-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
icon.js
251 lines (204 loc) · 5.61 KB
/
icon.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
const axios = require("axios");
const join = require("path").join;
const fs = require("fs").promises;
const cheerio = require("cheerio");
const opts = require("minimist")(process.argv.slice(2), {
string: ["icons-dir"],
boolean: ["proxy-icons"],
default: {
"proxy-icons": true,
"icons-dir": join(require("os").tmpdir(), "mitto-icons")
}
});
let icon = {
// this is the order in which `icon.get()` will go
priorities: [
"fdroid",
"appstore",
"playstore",
"izzydroid"
]
}
icon.fdroid = async (app_id) => {
let endpoint = "https://f-droid.org" +
"/en/packages/" + app_id;
let page;
// attempt to request page, and start `cheerio()` on the HTML
try {
page = (await axios.get(endpoint)).data;
page = cheerio.load(page);
}catch(err) {
return false;
}
// attempt to get `src` property of icon element
let icon = page(".package-icon")["0"].attribs.src;
// was the `src` property found?
if (icon) {
// we likely received the default icon, which is a relative path
if (! icon.startsWith("http")) {
return false;
}
return icon;
}
return false;
}
icon.izzydroid = async (app_id) => {
let endpoint = "https://apt.izzysoft.de" +
"/fdroid/repo/" + app_id + "/en-US/icon.png";
// attempt to request headers for icon
try {
(await axios.head(endpoint)).data;
}catch(err) { // icon doesn't exist
return false;
}
return endpoint;
}
icon.playstore = async (app_id) => {
let endpoint = "https://play.google.com" +
"/store/apps/details?id=" + app_id;
let page;
// attempt to request page, and start `cheerio()` on the HTML
try {
page = (await axios.get(endpoint)).data;
page = cheerio.load(page);
}catch(err) {
return false;
}
// attempt to get `src` property of icon element
let icon_src = page("[alt='Icon image']")["0"].attribs.src;
// if `src` property was found, return it
if (icon_src) {
return icon_src;
}
// no icon was found
return false;
}
// attempts to use Apple's iTunes API to search for apps, then we'll
// attempt to match them to the requested `app_id` and if found, get the
// icon and return that, otherwise return `false`
icon.appstore = async (app_id) => {
let endpoint = "https://itunes.apple.com" +
"/search?limit=10&media=software&term=" + app_id;
let results;
// attempt to get search results from API
try {
results = (await axios.get(endpoint)).data.results;
}catch(err) {
// something went wrong!
return false;
}
// normalize `app_id`
app_id = app_id.toLowerCase();
// run through search results
for (let i = 0; i < results.length; i++) {
// if a result's bundle ID matches `app_id` when normalized,
// return it's icon
if (results[i].bundleId.toLowerCase() == app_id) {
return results[i].artworkUrl512;
}
}
// no icon was found
return false;
}
// attempts to return an icon by going through all the values in
// `icon.priorities[]`, if no icon is found `false` is returned
icon.get = async (app_id) => {
let cached_file = await icon.get_file(app_id);
// have we already downloaded this and is proxying icons enabled?
// then we simply return the path
if (cached_file && opts["proxy-icons"]) {
return "local://" + app_id;
}
// check if a file with `app_id` exists in `icon_dir`, and if so,
// return an API endpoint that'll let you download that
try {
// attempt to `.stat()` the file with a normalized `app_id`
let is_file = (
await fs.stat(join(icon_dir, app_id.toLowerCase()))
).isFile();
// it's a file! return the URL
if (is_file) {
return app_id;
}
}catch(err) {
// this is a fine, this just means it's not a file or similar
}
// run through `icon.priorities[]` and attempt to run the respective
// function for the value in the priority, if an icon is resolved,
// return that
for (let i = 0; i < icon.priorities.length; i++) {
let icon_url;
// attempt to run icon function
try {
icon_url = await icon[icon.priorities[i]](app_id);
}catch(err) {
// skip to next function if an error happened
continue;
}
// make sure we actually got back a string, and if so return it
if (typeof icon_url == "string") {
let proxy_url;
// if proxying icons is enabled, we'll download the icon
// first, then return the endpoint for getting the proxied
// icon, instead of the direct icon
if (opts["proxy-icons"]) {
let download = await icon.download(app_id, icon_url);
if (download) {
proxy_url = "local://" + app_id;
}
}
return proxy_url || icon_url;
}
}
// no icon could be found
return false;
}
icon.get_file = async (app_id) => {
let icon_path = join(icon_dir, app_id.toLowerCase());
try {
// attempt to `.stat()` the file with a normalized `app_id`
let is_file = (
await fs.stat(icon_path)
).isFile();
// it's a file! return the URL
if (is_file) {
return icon_path;
}
}catch(err) { // icon likely doesn't exist
return false;
}
return icon_path;
}
let icon_dir = opts["icons-dir"];
icon.download = async (app_id, url) => {
let cached_file = await icon.get_file(app_id);
// have we already downloaded this? simply return the path
if (cached_file) {
return cached_file;
}
// normalize `app_id`
app_id = app_id.toLowerCase();
// attempt to create folder to store icons
await fs.mkdir(icon_dir, {
recursive: true
})
let image;
// attempt to download image
try {
image = (await axios.get(url, {
responseType: "arraybuffer"
})).data;
}catch(err) { // download failed or alike
return false;
}
let icon_path = join(icon_dir, app_id);
// attempt to write image to file
try {
fs.writeFile(icon_path, image);
}catch(err) { // writing to file failed
return false;
}
// everything worked!
return icon_path;
}
module.exports = icon;