iobroker.jetframe 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +357 -0
- package/admin/SF-Pro.ttf +0 -0
- package/admin/admin.d.ts +65 -0
- package/admin/frame.html +982 -0
- package/admin/frame.html.bak-aircraft-card-real-row-20260518-1608 +1236 -0
- package/admin/frame.html.bak-aircraft-card-structure-20260518-1517 +1236 -0
- package/admin/frame.html.bak-aircraft-logo-id-fix-20260518-1639 +1239 -0
- package/admin/frame.html.bak-shortcut-test +1236 -0
- package/admin/frame.html.bak-tablet-class-20260518-1729 +1239 -0
- package/admin/heatmap.html +216 -0
- package/admin/index.html +268 -0
- package/admin/index_m.html +1749 -0
- package/admin/jetframe.css +1260 -0
- package/admin/jetframe.css.bak-airbus-landscape-fix +4630 -0
- package/admin/jetframe.css.bak-aircraft-card-clean-equal-20260518-1438 +4899 -0
- package/admin/jetframe.css.bak-aircraft-card-real-row-20260518-1608 +4814 -0
- package/admin/jetframe.css.bak-aircraft-card-row-left-20260518-1525 +4604 -0
- package/admin/jetframe.css.bak-aircraft-card-slim-equal-20260518-1446 +4647 -0
- package/admin/jetframe.css.bak-aircraft-card-structure-20260518-1517 +4646 -0
- package/admin/jetframe.css.bak-aircraft-inline-final-20260518-1527 +4654 -0
- package/admin/jetframe.css.bak-aircraft-row-compact-fix-20260518-1639 +4763 -0
- package/admin/jetframe.css.bak-before-aircrafttype-purge +4818 -0
- package/admin/jetframe.css.bak-before-cleanup +4670 -0
- package/admin/jetframe.css.bak-before-remove-tablet-only-20260518-1711 +4896 -0
- package/admin/jetframe.css.bak-before-tablet-layout-rework-20260518-1650 +4914 -0
- package/admin/jetframe.css.bak-clean-duplicate-fonts-20260518-1340 +4975 -0
- package/admin/jetframe.css.bak-clean-old-index-fix-20260518-1937 +5167 -0
- package/admin/jetframe.css.bak-hardleft-airbus +4751 -0
- package/admin/jetframe.css.bak-index-iphone-landscape-20260518-1931 +5030 -0
- package/admin/jetframe.css.bak-index-landscape-final-20260518-1941 +5167 -0
- package/admin/jetframe.css.bak-index-landscape-real-20260518-1936 +5186 -0
- package/admin/jetframe.css.bak-landscape-compact-jumbo-bold-20260518-1343 +4802 -0
- package/admin/jetframe.css.bak-logo-align-final +4551 -0
- package/admin/jetframe.css.bak-logo-final2 +4551 -0
- package/admin/jetframe.css.bak-narrowbody-font-fix +4992 -0
- package/admin/jetframe.css.bak-nuke-airbus-align +4790 -0
- package/admin/jetframe.css.bak-pill-balance-20260518-1603 +4773 -0
- package/admin/jetframe.css.bak-pill-balance-fix +4910 -0
- package/admin/jetframe.css.bak-radar-fix-fonts +4710 -0
- package/admin/jetframe.css.bak-shortcut-test +4899 -0
- package/admin/jetframe.css.bak-smaller-aircraft-card-fonts-20260518-1345 +4897 -0
- package/admin/jetframe.css.bak-tablet-fix-real-20260518-1748 +4945 -0
- package/admin/jetframe.css.bak-tablet-fullscreen-fix-20260518-1804 +4972 -0
- package/admin/jetframe.css.bak-tablet-landscape-layout-20260518-1645 +4802 -0
- package/admin/jetframe.css.bak-tablet-layout-final-20260518-1839 +4802 -0
- package/admin/jetframe.css.bak-tablet-layout-v3-20260518-1729 +4802 -0
- package/admin/jetframe.css.bak-tablet-layout-v4-20260518-1801 +4957 -0
- package/admin/jetframe.css.bak-tablet-layout-v5-20260518-1843 +4970 -0
- package/admin/jetframe.css.bak-tablet-layout-v6-20260518-1848 +4958 -0
- package/admin/jetframe.css.bak-tablet-layout-v7-20260518-1909 +4985 -0
- package/admin/jetframe.css.bak-tablet-only-landscape-v2-20260518-1707 +4802 -0
- package/admin/jetframe.css.bak-tablet-pages-final-20260519-1857 +5188 -0
- package/admin/jetframe.css.bak-tablet-pages-final-20260519-1859 +5347 -0
- package/admin/jetframe.css.bak-tablet-pages-v2-20260519-190807 +5349 -0
- package/admin/jetframe.css.bak-typography-align-final +4818 -0
- package/admin/jetframe.png +0 -0
- package/admin/manifest.webmanifest +15 -0
- package/admin/src/app.tsx +58 -0
- package/admin/src/components/settings.tsx +97 -0
- package/admin/src/i18n/de.json +11 -0
- package/admin/src/i18n/en.json +11 -0
- package/admin/src/i18n/es.json +11 -0
- package/admin/src/i18n/fr.json +11 -0
- package/admin/src/i18n/i18n.d.ts +28 -0
- package/admin/src/i18n/it.json +11 -0
- package/admin/src/i18n/nl.json +11 -0
- package/admin/src/i18n/pl.json +11 -0
- package/admin/src/i18n/pt.json +11 -0
- package/admin/src/i18n/ru.json +11 -0
- package/admin/src/i18n/uk.json +11 -0
- package/admin/src/i18n/zh-cn.json +11 -0
- package/admin/src/index.tsx +25 -0
- package/admin/stats.html +228 -0
- package/admin/style.css +32 -0
- package/admin/tsconfig.json +11 -0
- package/admin/words.js +46 -0
- package/build/lib/adsb.js +218 -0
- package/build/lib/adsb.js.map +7 -0
- package/build/lib/airportNamesDe.js +131 -0
- package/build/lib/airportNamesDe.js.map +7 -0
- package/build/lib/airports.js +281 -0
- package/build/lib/airports.js.map +7 -0
- package/build/lib/classify.js +339 -0
- package/build/lib/classify.js.map +7 -0
- package/build/lib/config.js +103 -0
- package/build/lib/config.js.map +7 -0
- package/build/lib/flightInfo.js +1409 -0
- package/build/lib/flightInfo.js.map +7 -0
- package/build/lib/geo.js +84 -0
- package/build/lib/geo.js.map +7 -0
- package/build/lib/images.js +422 -0
- package/build/lib/images.js.map +7 -0
- package/build/lib/specialLiveries.js +342 -0
- package/build/lib/specialLiveries.js.map +7 -0
- package/build/lib/states.js +971 -0
- package/build/lib/states.js.map +7 -0
- package/build/lib/staticFiles.js +73 -0
- package/build/lib/staticFiles.js.map +7 -0
- package/build/lib/types.js +17 -0
- package/build/lib/types.js.map +7 -0
- package/build/lib/visConfig.js +52 -0
- package/build/lib/visConfig.js.map +7 -0
- package/build/main.js +1454 -0
- package/build/main.js.map +7 -0
- package/io-package.json +169 -0
- package/package.json +82 -0
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var images_exports = {};
|
|
30
|
+
__export(images_exports, {
|
|
31
|
+
MANUFACTURER_LOGO_CACHE_DIR: () => MANUFACTURER_LOGO_CACHE_DIR,
|
|
32
|
+
cacheExternalLogoUrl: () => cacheExternalLogoUrl,
|
|
33
|
+
clearImageCache: () => clearImageCache,
|
|
34
|
+
ensureImageDirs: () => ensureImageDirs,
|
|
35
|
+
saveImages: () => saveImages
|
|
36
|
+
});
|
|
37
|
+
module.exports = __toCommonJS(images_exports);
|
|
38
|
+
var https = __toESM(require("https"));
|
|
39
|
+
var http = __toESM(require("http"));
|
|
40
|
+
const IMAGE_CACHE = {
|
|
41
|
+
jetDir: "img/jet",
|
|
42
|
+
logoDir: "img/logos",
|
|
43
|
+
manufacturerDir: "img/manufacturer"
|
|
44
|
+
};
|
|
45
|
+
const NEGATIVE_IMAGE_CACHE = /* @__PURE__ */ new Map();
|
|
46
|
+
const NEGATIVE_CACHE_TTL_MS = 1e3 * 60 * 60 * 6;
|
|
47
|
+
const NEGATIVE_CACHE_SPECIAL_TTL_MS = 1e3 * 60 * 30;
|
|
48
|
+
const MANUFACTURER_CACHE_LOGGED = {};
|
|
49
|
+
async function ensureImageDirs(adapter, logDebug, logWarn) {
|
|
50
|
+
try {
|
|
51
|
+
await adapter.writeFileAsync("jetframe.admin", ".keep", Buffer.from(""));
|
|
52
|
+
logDebug("jetframe.admin Datei-Storage bereit");
|
|
53
|
+
} catch (e) {
|
|
54
|
+
logWarn(`jetframe.admin Storage Fehler: ${errorText(e)}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async function saveImages(adapter, config, a, logDebug, logWarn) {
|
|
58
|
+
let logoUrl = "";
|
|
59
|
+
let jetUrl = "";
|
|
60
|
+
if (config.cacheExternalImages) {
|
|
61
|
+
logoUrl = await cacheLogoIfNeeded(adapter, a, logDebug, logWarn);
|
|
62
|
+
jetUrl = await cacheJetIfNeeded(adapter, a, logDebug, logWarn);
|
|
63
|
+
} else {
|
|
64
|
+
logoUrl = a.logoUrl || "";
|
|
65
|
+
jetUrl = String(a.fr24ImageUrl || "").trim() || String(a.jetphotosImageUrl || "").trim();
|
|
66
|
+
if (!jetUrl) {
|
|
67
|
+
jetUrl = await resolveFr24AircraftImageFromPage(a, logDebug, logWarn);
|
|
68
|
+
}
|
|
69
|
+
if (!jetUrl) {
|
|
70
|
+
jetUrl = buildHexDbImageUrl(a);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
a.localLogoUrl = logoUrl;
|
|
74
|
+
a.localImageUrl = jetUrl;
|
|
75
|
+
a.finalImageUrl = jetUrl || "";
|
|
76
|
+
const bases = [`${config.dpRoot}.current`];
|
|
77
|
+
if (a.mode === "OVERFLIGHT") {
|
|
78
|
+
bases.push(`${config.dpRoot}.overflight`);
|
|
79
|
+
} else {
|
|
80
|
+
bases.push(`${config.dpRoot}.airport`);
|
|
81
|
+
}
|
|
82
|
+
for (const base of bases) {
|
|
83
|
+
await adapter.setForeignStateAsync(`${base}.localLogoUrl`, logoUrl, true);
|
|
84
|
+
await adapter.setForeignStateAsync(`${base}.localImageUrl`, jetUrl, true);
|
|
85
|
+
await adapter.setForeignStateAsync(`${base}.finalImageUrl`, jetUrl || "", true);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async function clearImageCache(adapter, logDebug, logWarn) {
|
|
89
|
+
const dirs = [IMAGE_CACHE.jetDir, IMAGE_CACHE.logoDir, "img/manufacturer"];
|
|
90
|
+
for (const dir of dirs) {
|
|
91
|
+
try {
|
|
92
|
+
await deleteFolderFiles(adapter, dir, logDebug);
|
|
93
|
+
} catch (e) {
|
|
94
|
+
logWarn(`Cache-Ordner konnte nicht geleert werden: ${dir} | ${errorText(e)}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
logDebug("Bild-/Logo-Cache geleert");
|
|
98
|
+
}
|
|
99
|
+
async function deleteFolderFiles(adapter, dir, logDebug) {
|
|
100
|
+
try {
|
|
101
|
+
const files = await adapter.readDirAsync("jetframe.admin", dir);
|
|
102
|
+
for (const file of files || []) {
|
|
103
|
+
if (!(file == null ? void 0 : file.file)) {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
const relPath = `${dir}/${file.file}`;
|
|
107
|
+
try {
|
|
108
|
+
await adapter.unlinkAsync("jetframe.admin", relPath);
|
|
109
|
+
logDebug(`Cache gel\xF6scht: ${relPath}`);
|
|
110
|
+
} catch {
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
} catch {
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async function cacheExternalLogoUrl(adapter, url, key, relDir, logDebug, logWarn) {
|
|
117
|
+
const cleanUrl = String(url || "").trim();
|
|
118
|
+
if (!cleanUrl) {
|
|
119
|
+
return "";
|
|
120
|
+
}
|
|
121
|
+
const fileBase = safeFileName(key || "logo");
|
|
122
|
+
const existing = await findExistingImage(adapter, relDir, fileBase);
|
|
123
|
+
if (existing) {
|
|
124
|
+
if (!MANUFACTURER_CACHE_LOGGED[existing.url]) {
|
|
125
|
+
MANUFACTURER_CACHE_LOGGED[existing.url] = true;
|
|
126
|
+
logDebug(`Logo Cache hit: ${existing.url}`);
|
|
127
|
+
}
|
|
128
|
+
return existing.url;
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
logDebug(`Logo Download: ${cleanUrl}`);
|
|
132
|
+
const buffer = await downloadImageBuffer(cleanUrl, false);
|
|
133
|
+
const ext = detectImageExt(buffer);
|
|
134
|
+
const relPath = `${relDir}/${fileBase}.${ext}`;
|
|
135
|
+
await adapter.writeFileAsync("jetframe.admin", relPath, buffer);
|
|
136
|
+
const cachedUrl = publicUrl(relPath);
|
|
137
|
+
logDebug(`Logo gespeichert: ${cachedUrl}`);
|
|
138
|
+
return cachedUrl;
|
|
139
|
+
} catch (e) {
|
|
140
|
+
logWarn(`Logo Download/Speichern Fehler: ${errorText(e)}`);
|
|
141
|
+
return cleanUrl;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const MANUFACTURER_LOGO_CACHE_DIR = IMAGE_CACHE.manufacturerDir;
|
|
145
|
+
async function cacheLogoIfNeeded(adapter, a, logDebug, logWarn) {
|
|
146
|
+
if (!a.logoUrl) {
|
|
147
|
+
return "";
|
|
148
|
+
}
|
|
149
|
+
const logoKey = String(a.airlineIcao || a.airlineIata || a.callsign || "logo").toUpperCase().replace(/[^A-Z0-9]/g, "").substring(0, 8);
|
|
150
|
+
const fileBase = safeFileName(logoKey);
|
|
151
|
+
const existing = await findExistingImage(adapter, IMAGE_CACHE.logoDir, fileBase);
|
|
152
|
+
if (existing) {
|
|
153
|
+
logDebug(`Airline Logo Cache hit: ${existing.url}`);
|
|
154
|
+
return existing.url;
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
logDebug(`Logo Download: ${a.logoUrl}`);
|
|
158
|
+
const buffer = await downloadImageBuffer(a.logoUrl, false);
|
|
159
|
+
const ext = detectImageExt(buffer);
|
|
160
|
+
const relPath = `${IMAGE_CACHE.logoDir}/${fileBase}.${ext}`;
|
|
161
|
+
await adapter.writeFileAsync("jetframe.admin", relPath, buffer);
|
|
162
|
+
const url = publicUrl(relPath);
|
|
163
|
+
logDebug(`Logo gespeichert: ${url}`);
|
|
164
|
+
return url;
|
|
165
|
+
} catch (e) {
|
|
166
|
+
logWarn(`Logo Download/Speichern Fehler: ${errorText(e)}`);
|
|
167
|
+
return "";
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function buildHexDbImageUrl(a) {
|
|
171
|
+
const hex = String(a.hex || "").trim().toLowerCase().replace(/[^a-f0-9]/g, "");
|
|
172
|
+
if (hex) {
|
|
173
|
+
return `https://hexdb.io/hex-image?hex=${hex}`;
|
|
174
|
+
}
|
|
175
|
+
const reg = String(a.registration || "").trim().toUpperCase().replace(/[^A-Z0-9-]/g, "");
|
|
176
|
+
if (reg) {
|
|
177
|
+
return `https://hexdb.io/static/aircraft-images/${reg}.jpg`;
|
|
178
|
+
}
|
|
179
|
+
return "";
|
|
180
|
+
}
|
|
181
|
+
async function cacheJetIfNeeded(adapter, a, logDebug, logWarn) {
|
|
182
|
+
const key = a.registration || a.callsign || a.hex || "unknown";
|
|
183
|
+
const fileBase = safeFileName(key);
|
|
184
|
+
const negativeKey = String(fileBase).toUpperCase();
|
|
185
|
+
const isSpecial = String(a.specialLivery || "").trim() || String(a.specialLiveryVisText || "").trim() || String(a.emergency || "").trim() || String(a.aircraftSize || "").toLowerCase().includes("heavy");
|
|
186
|
+
const ttl = isSpecial ? NEGATIVE_CACHE_SPECIAL_TTL_MS : NEGATIVE_CACHE_TTL_MS;
|
|
187
|
+
const negativeTs = NEGATIVE_IMAGE_CACHE.get(negativeKey);
|
|
188
|
+
if (negativeTs && Date.now() - negativeTs < ttl) {
|
|
189
|
+
logDebug(`Jet Negativ-Cache hit: ${negativeKey}`);
|
|
190
|
+
return "";
|
|
191
|
+
}
|
|
192
|
+
const existing = await findExistingImage(adapter, IMAGE_CACHE.jetDir, fileBase);
|
|
193
|
+
if (existing) {
|
|
194
|
+
logDebug(`Aircraft Bild Cache hit: ${existing.url}`);
|
|
195
|
+
return existing.url;
|
|
196
|
+
}
|
|
197
|
+
const hexUrl = buildHexDbImageUrl(a);
|
|
198
|
+
if (hexUrl) {
|
|
199
|
+
try {
|
|
200
|
+
logDebug(`Jet Bild Download (HexDB): ${hexUrl}`);
|
|
201
|
+
const buffer = await downloadImageBuffer(hexUrl, false);
|
|
202
|
+
const ext = detectImageExt(buffer);
|
|
203
|
+
const relPath = `${IMAGE_CACHE.jetDir}/${fileBase}.${ext}`;
|
|
204
|
+
await adapter.writeFileAsync("jetframe.admin", relPath, buffer);
|
|
205
|
+
const url = publicUrl(relPath);
|
|
206
|
+
logDebug(`Jet gespeichert (HexDB): ${url}`);
|
|
207
|
+
return url;
|
|
208
|
+
} catch (e) {
|
|
209
|
+
logDebug(`Jet Bild HexDB nicht nutzbar: ${errorText(e)}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
let fr24Url = String(a.fr24ImageUrl || a.jetphotosImageUrl || "").trim();
|
|
213
|
+
if (!fr24Url) {
|
|
214
|
+
fr24Url = await resolveFr24AircraftImageFromPage(a, logDebug, logWarn);
|
|
215
|
+
}
|
|
216
|
+
if (!fr24Url) {
|
|
217
|
+
NEGATIVE_IMAGE_CACHE.set(negativeKey, Date.now());
|
|
218
|
+
logDebug(`Jet Bild negativ gecached: ${negativeKey}`);
|
|
219
|
+
return "";
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
logDebug(`Jet Bild Download (FR24 Fallback): ${fr24Url}`);
|
|
223
|
+
const buffer = await downloadImageBuffer(fr24Url, true);
|
|
224
|
+
const ext = detectImageExt(buffer);
|
|
225
|
+
const relPath = `${IMAGE_CACHE.jetDir}/${fileBase}.${ext}`;
|
|
226
|
+
await adapter.writeFileAsync("jetframe.admin", relPath, buffer);
|
|
227
|
+
const url = publicUrl(relPath);
|
|
228
|
+
logDebug(`Jet gespeichert (FR24 Fallback): ${url}`);
|
|
229
|
+
return url;
|
|
230
|
+
} catch (e) {
|
|
231
|
+
logWarn(`FR24 Bild Download/Speichern Fehler: ${errorText(e)}`);
|
|
232
|
+
NEGATIVE_IMAGE_CACHE.set(negativeKey, Date.now());
|
|
233
|
+
logDebug(`Jet Bild negativ gecached: ${negativeKey}`);
|
|
234
|
+
return "";
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
async function findExistingImage(adapter, relDir, fileBase) {
|
|
238
|
+
const exts = ["jpg", "jpeg", "png", "webp", "avif"];
|
|
239
|
+
for (const ext of exts) {
|
|
240
|
+
const relPath = `${relDir}/${fileBase}.${ext}`;
|
|
241
|
+
try {
|
|
242
|
+
const file = await adapter.readFileAsync("jetframe.admin", relPath);
|
|
243
|
+
if (file == null ? void 0 : file.file) {
|
|
244
|
+
return {
|
|
245
|
+
url: publicUrl(relPath)
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
} catch {
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
async function resolveFr24AircraftImageFromPage(a, logDebug, logWarn) {
|
|
254
|
+
const reg = String(a.registration || "").trim().toLowerCase();
|
|
255
|
+
if (!reg) {
|
|
256
|
+
return "";
|
|
257
|
+
}
|
|
258
|
+
const url = `https://www.flightradar24.com/data/aircraft/${encodeURIComponent(reg)}`;
|
|
259
|
+
try {
|
|
260
|
+
logDebug(`FR24 Aircraft Page Anfrage: ${url}`);
|
|
261
|
+
const html = await new Promise((resolve, reject) => {
|
|
262
|
+
const req = https.get(
|
|
263
|
+
url,
|
|
264
|
+
{
|
|
265
|
+
headers: {
|
|
266
|
+
"User-Agent": "Mozilla/5.0 AppleWebKit/605.1.15 Safari/604.1"
|
|
267
|
+
},
|
|
268
|
+
timeout: 2e4
|
|
269
|
+
},
|
|
270
|
+
(res) => {
|
|
271
|
+
if (res.statusCode !== 200) {
|
|
272
|
+
reject(new Error(`HTTP ${res.statusCode}`));
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const chunks = [];
|
|
276
|
+
res.on("data", (chunk) => {
|
|
277
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
278
|
+
});
|
|
279
|
+
res.on("end", () => {
|
|
280
|
+
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
);
|
|
284
|
+
req.on("timeout", () => {
|
|
285
|
+
req.destroy(new Error("FR24 Timeout"));
|
|
286
|
+
});
|
|
287
|
+
req.on("error", reject);
|
|
288
|
+
});
|
|
289
|
+
const matches = [...html.matchAll(/https:\/\/cdn\.jetphotos\.com\/[^"' ]+\.(jpg|jpeg|png|webp)/gi)];
|
|
290
|
+
if (!matches.length) {
|
|
291
|
+
logDebug(`FR24 Aircraft kein Bild gefunden: ${reg}`);
|
|
292
|
+
return "";
|
|
293
|
+
}
|
|
294
|
+
const imageUrl = String(matches[0][0] || "").trim();
|
|
295
|
+
logDebug(`FR24 Aircraft Bild gefunden: ${imageUrl}`);
|
|
296
|
+
return imageUrl;
|
|
297
|
+
} catch (e) {
|
|
298
|
+
logWarn(`FR24 Aircraft Fehler: ${errorText(e)}`);
|
|
299
|
+
return "";
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function looksLikeImageBuffer(buf) {
|
|
303
|
+
if (buf.length >= 3 && buf[0] === 255 && buf[1] === 216 && buf[2] === 255) {
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
if (buf.length >= 8 && buf[0] === 137 && buf[1] === 80 && buf[2] === 78 && buf[3] === 71) {
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
if (buf.length >= 12 && buf.subarray(0, 4).toString("ascii") === "RIFF" && buf.subarray(8, 12).toString("ascii") === "WEBP") {
|
|
310
|
+
return true;
|
|
311
|
+
}
|
|
312
|
+
if (buf.includes(Buffer.from("ftypavif"))) {
|
|
313
|
+
return true;
|
|
314
|
+
}
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
function detectImageExt(buf) {
|
|
318
|
+
if (buf.length >= 3 && buf[0] === 255 && buf[1] === 216 && buf[2] === 255) {
|
|
319
|
+
return "jpg";
|
|
320
|
+
}
|
|
321
|
+
if (buf.length >= 8 && buf[0] === 137 && buf[1] === 80 && buf[2] === 78 && buf[3] === 71) {
|
|
322
|
+
return "png";
|
|
323
|
+
}
|
|
324
|
+
if (buf.length >= 12 && buf.subarray(0).toString("ascii") === "RIFF" && buf.subarray(8, 12).toString("ascii") === "WEBP") {
|
|
325
|
+
return "webp";
|
|
326
|
+
}
|
|
327
|
+
if (buf.includes(Buffer.from("ftypavif"))) {
|
|
328
|
+
return "avif";
|
|
329
|
+
}
|
|
330
|
+
return "jpg";
|
|
331
|
+
}
|
|
332
|
+
function publicUrl(relPath) {
|
|
333
|
+
return `/jetframe.admin/${relPath}`;
|
|
334
|
+
}
|
|
335
|
+
function safeFileName(name) {
|
|
336
|
+
return String(name || "unknown").trim().replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
337
|
+
}
|
|
338
|
+
function downloadImageBuffer(url, useReferer, redirects = 0) {
|
|
339
|
+
return new Promise((resolve, reject) => {
|
|
340
|
+
if (redirects > 5) {
|
|
341
|
+
reject(new Error("Zu viele Redirects beim Bilddownload"));
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
const client = url.startsWith("https") ? https : http;
|
|
345
|
+
const headers = {
|
|
346
|
+
"User-Agent": "Mozilla/5.0 AppleWebKit/605.1.15 Safari/604.1",
|
|
347
|
+
Accept: "image/avif,image/webp,image/apng,image/png,image/jpeg,image/*,*/*;q=0.8"
|
|
348
|
+
};
|
|
349
|
+
if (useReferer) {
|
|
350
|
+
headers.Referer = "https://www.flightradar24.com/";
|
|
351
|
+
}
|
|
352
|
+
const req = client.get(
|
|
353
|
+
url,
|
|
354
|
+
{
|
|
355
|
+
headers,
|
|
356
|
+
timeout: 2e4
|
|
357
|
+
},
|
|
358
|
+
(res) => {
|
|
359
|
+
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
360
|
+
const nextUrl = res.headers.location.startsWith("http") ? res.headers.location : new URL(res.headers.location, url).toString();
|
|
361
|
+
downloadImageBuffer(nextUrl, useReferer, redirects + 1).then(resolve).catch(reject);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
if (res.statusCode !== 200) {
|
|
365
|
+
reject(new Error(`HTTP ${res.statusCode}`));
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
const chunks = [];
|
|
369
|
+
res.on("data", (chunk) => {
|
|
370
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
371
|
+
});
|
|
372
|
+
res.on("end", () => {
|
|
373
|
+
const buffer = Buffer.concat(chunks);
|
|
374
|
+
if (!buffer.length) {
|
|
375
|
+
reject(new Error("Leeres Bild erhalten"));
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const contentType = String(res.headers["content-type"] || "").toLowerCase();
|
|
379
|
+
const text = buffer.toString("utf8").trim();
|
|
380
|
+
if (!contentType.startsWith("image/") && /^https?:\/\//i.test(text)) {
|
|
381
|
+
downloadImageBuffer(text, useReferer, redirects + 1).then(resolve).catch(reject);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
if (!looksLikeImageBuffer(buffer)) {
|
|
385
|
+
reject(new Error("Antwort ist kein Bild"));
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
resolve(buffer);
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
);
|
|
392
|
+
req.on("timeout", () => {
|
|
393
|
+
req.destroy(new Error("Bild Download Timeout"));
|
|
394
|
+
});
|
|
395
|
+
req.on("error", reject);
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
function errorText(e) {
|
|
399
|
+
if (!e) {
|
|
400
|
+
return "unbekannter Fehler";
|
|
401
|
+
}
|
|
402
|
+
if (typeof e === "string") {
|
|
403
|
+
return e;
|
|
404
|
+
}
|
|
405
|
+
if (e instanceof Error) {
|
|
406
|
+
return e.message;
|
|
407
|
+
}
|
|
408
|
+
try {
|
|
409
|
+
return JSON.stringify(e);
|
|
410
|
+
} catch {
|
|
411
|
+
return String(e);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
415
|
+
0 && (module.exports = {
|
|
416
|
+
MANUFACTURER_LOGO_CACHE_DIR,
|
|
417
|
+
cacheExternalLogoUrl,
|
|
418
|
+
clearImageCache,
|
|
419
|
+
ensureImageDirs,
|
|
420
|
+
saveImages
|
|
421
|
+
});
|
|
422
|
+
//# sourceMappingURL=images.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/lib/images.ts"],
|
|
4
|
+
"sourcesContent": ["import * as fs from 'fs';\nimport * as path from 'path';\nimport * as https from 'https';\nimport * as http from 'http';\n\nimport type { Aircraft, JetFrameConfig, AdapterLike } from './types';\n\nconst IMAGE_CACHE = {\n\tjetDir: 'img/jet',\n\tlogoDir: 'img/logos',\n\tmanufacturerDir: 'img/manufacturer',\n};\n\nconst NEGATIVE_IMAGE_CACHE = new Map<string, number>();\n\nconst NEGATIVE_CACHE_TTL_MS = 1000 * 60 * 60 * 6; // 6h\nconst NEGATIVE_CACHE_SPECIAL_TTL_MS = 1000 * 60 * 30; // 30min\n\nconst MANUFACTURER_CACHE_LOGGED: Record<string, boolean> = {};\n\n/**\n *\n */\nexport async function ensureImageDirs(\n\tadapter: AdapterLike,\n\tlogDebug: (msg: string, level?: number) => void,\n\tlogWarn: (msg: string) => void,\n): Promise<void> {\n\ttry {\n\t\tawait adapter.writeFileAsync('jetframe.admin', '.keep', Buffer.from(''));\n\n\t\tlogDebug('jetframe.admin Datei-Storage bereit');\n\t} catch (e) {\n\t\tlogWarn(`jetframe.admin Storage Fehler: ${errorText(e)}`);\n\t}\n}\n\n/**\n *\n */\nexport async function saveImages(\n\tadapter: AdapterLike,\n\tconfig: JetFrameConfig,\n\ta: Aircraft,\n\tlogDebug: (msg: string, level?: number) => void,\n\tlogWarn: (msg: string) => void,\n): Promise<void> {\n\tlet logoUrl = '';\n\tlet jetUrl = '';\n\n\tif (config.cacheExternalImages) {\n\t\tlogoUrl = await cacheLogoIfNeeded(adapter, a, logDebug, logWarn);\n\t\tjetUrl = await cacheJetIfNeeded(adapter, a, logDebug, logWarn);\n\t} else {\n\t\tlogoUrl = a.logoUrl || '';\n\n\t\t// Cache aus:\n\t\t// Kein lokales Speichern, aber trotzdem bestes externes Bild ermitteln.\n\t\tjetUrl = String((a as any).fr24ImageUrl || '').trim() || String(a.jetphotosImageUrl || '').trim();\n\n\t\tif (!jetUrl) {\n\t\t\tjetUrl = await resolveFr24AircraftImageFromPage(a, logDebug, logWarn);\n\t\t}\n\n\t\tif (!jetUrl) {\n\t\t\tjetUrl = buildHexDbImageUrl(a);\n\t\t}\n\t}\n\n\ta.localLogoUrl = logoUrl;\n\ta.localImageUrl = jetUrl;\n\ta.finalImageUrl = jetUrl || '';\n\n\tconst bases = [`${config.dpRoot}.current`];\n\n\tif (a.mode === 'OVERFLIGHT') {\n\t\tbases.push(`${config.dpRoot}.overflight`);\n\t} else {\n\t\tbases.push(`${config.dpRoot}.airport`);\n\t}\n\n\tfor (const base of bases) {\n\t\tawait adapter.setForeignStateAsync(`${base}.localLogoUrl`, logoUrl, true);\n\n\t\tawait adapter.setForeignStateAsync(`${base}.localImageUrl`, jetUrl, true);\n\n\t\tawait adapter.setForeignStateAsync(`${base}.finalImageUrl`, jetUrl || '', true);\n\t}\n}\n\n/**\n *\n */\nexport async function clearImageCache(\n\tadapter: AdapterLike,\n\tlogDebug: (msg: string, level?: number) => void,\n\tlogWarn: (msg: string) => void,\n): Promise<void> {\n\tconst dirs = [IMAGE_CACHE.jetDir, IMAGE_CACHE.logoDir, 'img/manufacturer'];\n\n\tfor (const dir of dirs) {\n\t\ttry {\n\t\t\tawait deleteFolderFiles(adapter, dir, logDebug);\n\t\t} catch (e) {\n\t\t\tlogWarn(`Cache-Ordner konnte nicht geleert werden: ${dir} | ${errorText(e)}`);\n\t\t}\n\t}\n\n\tlogDebug('Bild-/Logo-Cache geleert');\n}\n\nasync function deleteFolderFiles(\n\tadapter: AdapterLike,\n\tdir: string,\n\tlogDebug: (msg: string, level?: number) => void,\n): Promise<void> {\n\ttry {\n\t\tconst files = await adapter.readDirAsync('jetframe.admin', dir);\n\n\t\tfor (const file of files || []) {\n\t\t\tif (!file?.file) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst relPath = `${dir}/${file.file}`;\n\n\t\t\ttry {\n\t\t\t\tawait adapter.unlinkAsync('jetframe.admin', relPath);\n\t\t\t\tlogDebug(`Cache gel\u00F6scht: ${relPath}`);\n\t\t\t} catch {\n\t\t\t\t// ignore single file\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// Ordner existiert nicht\n\t}\n}\n\n/**\n *\n */\nexport async function cacheExternalLogoUrl(\n\tadapter: AdapterLike,\n\turl: string,\n\tkey: string,\n\trelDir: string,\n\tlogDebug: (msg: string, level?: number) => void,\n\tlogWarn: (msg: string) => void,\n): Promise<string> {\n\tconst cleanUrl = String(url || '').trim();\n\n\tif (!cleanUrl) {\n\t\treturn '';\n\t}\n\n\tconst fileBase = safeFileName(key || 'logo');\n\n\tconst existing = await findExistingImage(adapter, relDir, fileBase);\n\n\tif (existing) {\n\t\tif (!MANUFACTURER_CACHE_LOGGED[existing.url]) {\n\t\t\tMANUFACTURER_CACHE_LOGGED[existing.url] = true;\n\t\t\tlogDebug(`Logo Cache hit: ${existing.url}`);\n\t\t}\n\n\t\treturn existing.url;\n\t}\n\n\ttry {\n\t\tlogDebug(`Logo Download: ${cleanUrl}`);\n\n\t\tconst buffer = await downloadImageBuffer(cleanUrl, false);\n\n\t\tconst ext = detectImageExt(buffer);\n\n\t\tconst relPath = `${relDir}/${fileBase}.${ext}`;\n\n\t\tawait adapter.writeFileAsync('jetframe.admin', relPath, buffer);\n\n\t\tconst cachedUrl = publicUrl(relPath);\n\n\t\tlogDebug(`Logo gespeichert: ${cachedUrl}`);\n\n\t\treturn cachedUrl;\n\t} catch (e) {\n\t\tlogWarn(`Logo Download/Speichern Fehler: ${errorText(e)}`);\n\n\t\treturn cleanUrl;\n\t}\n}\n\nexport const MANUFACTURER_LOGO_CACHE_DIR = IMAGE_CACHE.manufacturerDir;\n\nasync function cacheLogoIfNeeded(\n\tadapter: AdapterLike,\n\ta: Aircraft,\n\tlogDebug: (msg: string, level?: number) => void,\n\tlogWarn: (msg: string) => void,\n): Promise<string> {\n\tif (!a.logoUrl) {\n\t\treturn '';\n\t}\n\n\tconst logoKey = String(a.airlineIcao || a.airlineIata || a.callsign || 'logo')\n\t\t.toUpperCase()\n\t\t.replace(/[^A-Z0-9]/g, '')\n\t\t.substring(0, 8);\n\n\tconst fileBase = safeFileName(logoKey);\n\n\tconst existing = await findExistingImage(adapter, IMAGE_CACHE.logoDir, fileBase);\n\n\tif (existing) {\n\t\tlogDebug(`Airline Logo Cache hit: ${existing.url}`);\n\t\treturn existing.url;\n\t}\n\n\ttry {\n\t\tlogDebug(`Logo Download: ${a.logoUrl}`);\n\n\t\tconst buffer = await downloadImageBuffer(a.logoUrl, false);\n\n\t\tconst ext = detectImageExt(buffer);\n\n\t\tconst relPath = `${IMAGE_CACHE.logoDir}/${fileBase}.${ext}`;\n\n\t\tawait adapter.writeFileAsync('jetframe.admin', relPath, buffer);\n\n\t\tconst url = publicUrl(relPath);\n\n\t\tlogDebug(`Logo gespeichert: ${url}`);\n\n\t\treturn url;\n\t} catch (e) {\n\t\tlogWarn(`Logo Download/Speichern Fehler: ${errorText(e)}`);\n\n\t\treturn '';\n\t}\n}\n\nfunction buildHexDbImageUrl(a: Aircraft): string {\n\tconst hex = String(a.hex || '')\n\t\t.trim()\n\t\t.toLowerCase()\n\t\t.replace(/[^a-f0-9]/g, '');\n\n\tif (hex) {\n\t\treturn `https://hexdb.io/hex-image?hex=${hex}`;\n\t}\n\n\tconst reg = String(a.registration || '')\n\t\t.trim()\n\t\t.toUpperCase()\n\t\t.replace(/[^A-Z0-9-]/g, '');\n\n\tif (reg) {\n\t\treturn `https://hexdb.io/static/aircraft-images/${reg}.jpg`;\n\t}\n\n\treturn '';\n}\n\nasync function cacheJetIfNeeded(\n\tadapter: AdapterLike,\n\ta: Aircraft,\n\tlogDebug: (msg: string, level?: number) => void,\n\tlogWarn: (msg: string) => void,\n): Promise<string> {\n\tconst key = a.registration || a.callsign || a.hex || 'unknown';\n\n\tconst fileBase = safeFileName(key);\n\n\t// --------------------------------------------------\n\t// Smarter Negative Cache\n\t// --------------------------------------------------\n\n\tconst negativeKey = String(fileBase).toUpperCase();\n\n\tconst isSpecial =\n\t\tString((a as any).specialLivery || '').trim() ||\n\t\tString((a as any).specialLiveryVisText || '').trim() ||\n\t\tString((a as any).emergency || '').trim() ||\n\t\tString((a as any).aircraftSize || '')\n\t\t\t.toLowerCase()\n\t\t\t.includes('heavy');\n\n\tconst ttl = isSpecial ? NEGATIVE_CACHE_SPECIAL_TTL_MS : NEGATIVE_CACHE_TTL_MS;\n\n\tconst negativeTs = NEGATIVE_IMAGE_CACHE.get(negativeKey);\n\n\tif (negativeTs && Date.now() - negativeTs < ttl) {\n\t\tlogDebug(`Jet Negativ-Cache hit: ${negativeKey}`);\n\n\t\treturn '';\n\t}\n\n\tconst existing = await findExistingImage(adapter, IMAGE_CACHE.jetDir, fileBase);\n\n\tif (existing) {\n\t\tlogDebug(`Aircraft Bild Cache hit: ${existing.url}`);\n\t\treturn existing.url;\n\t}\n\n\tconst hexUrl = buildHexDbImageUrl(a);\n\n\tif (hexUrl) {\n\t\ttry {\n\t\t\tlogDebug(`Jet Bild Download (HexDB): ${hexUrl}`);\n\n\t\t\tconst buffer = await downloadImageBuffer(hexUrl, false);\n\n\t\t\tconst ext = detectImageExt(buffer);\n\n\t\t\tconst relPath = `${IMAGE_CACHE.jetDir}/${fileBase}.${ext}`;\n\n\t\t\tawait adapter.writeFileAsync('jetframe.admin', relPath, buffer);\n\n\t\t\tconst url = publicUrl(relPath);\n\n\t\t\tlogDebug(`Jet gespeichert (HexDB): ${url}`);\n\n\t\t\treturn url;\n\t\t} catch (e) {\n\t\t\tlogDebug(`Jet Bild HexDB nicht nutzbar: ${errorText(e)}`);\n\t\t}\n\t}\n\n\tlet fr24Url = String((a as any).fr24ImageUrl || a.jetphotosImageUrl || '').trim();\n\n\tif (!fr24Url) {\n\t\tfr24Url = await resolveFr24AircraftImageFromPage(a, logDebug, logWarn);\n\t}\n\n\tif (!fr24Url) {\n\t\tNEGATIVE_IMAGE_CACHE.set(negativeKey, Date.now());\n\n\t\tlogDebug(`Jet Bild negativ gecached: ${negativeKey}`);\n\n\t\treturn '';\n\t}\n\n\ttry {\n\t\tlogDebug(`Jet Bild Download (FR24 Fallback): ${fr24Url}`);\n\n\t\tconst buffer = await downloadImageBuffer(fr24Url, true);\n\n\t\tconst ext = detectImageExt(buffer);\n\n\t\tconst relPath = `${IMAGE_CACHE.jetDir}/${fileBase}.${ext}`;\n\n\t\tawait adapter.writeFileAsync('jetframe.admin', relPath, buffer);\n\n\t\tconst url = publicUrl(relPath);\n\n\t\tlogDebug(`Jet gespeichert (FR24 Fallback): ${url}`);\n\n\t\treturn url;\n\t} catch (e) {\n\t\tlogWarn(`FR24 Bild Download/Speichern Fehler: ${errorText(e)}`);\n\n\t\tNEGATIVE_IMAGE_CACHE.set(negativeKey, Date.now());\n\n\t\tlogDebug(`Jet Bild negativ gecached: ${negativeKey}`);\n\n\t\treturn '';\n\t}\n}\n\nasync function findExistingImage(\n\tadapter: AdapterLike,\n\trelDir: string,\n\tfileBase: string,\n): Promise<{ url: string } | null> {\n\tconst exts = ['jpg', 'jpeg', 'png', 'webp', 'avif'];\n\n\tfor (const ext of exts) {\n\t\tconst relPath = `${relDir}/${fileBase}.${ext}`;\n\n\t\ttry {\n\t\t\tconst file = await adapter.readFileAsync('jetframe.admin', relPath);\n\n\t\t\tif (file?.file) {\n\t\t\t\treturn {\n\t\t\t\t\turl: publicUrl(relPath),\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t}\n\n\treturn null;\n}\n\nasync function resolveFr24AircraftImageFromPage(\n\ta: Aircraft,\n\tlogDebug: (msg: string, level?: number) => void,\n\tlogWarn: (msg: string) => void,\n): Promise<string> {\n\tconst reg = String(a.registration || '')\n\t\t.trim()\n\t\t.toLowerCase();\n\n\tif (!reg) {\n\t\treturn '';\n\t}\n\n\tconst url = `https://www.flightradar24.com/data/aircraft/${encodeURIComponent(reg)}`;\n\n\ttry {\n\t\tlogDebug(`FR24 Aircraft Page Anfrage: ${url}`);\n\n\t\tconst html = await new Promise<string>((resolve, reject) => {\n\t\t\tconst req = https.get(\n\t\t\t\turl,\n\t\t\t\t{\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t'User-Agent': 'Mozilla/5.0 AppleWebKit/605.1.15 Safari/604.1',\n\t\t\t\t\t},\n\t\t\t\t\ttimeout: 20000,\n\t\t\t\t},\n\t\t\t\tres => {\n\t\t\t\t\tif (res.statusCode !== 200) {\n\t\t\t\t\t\treject(new Error(`HTTP ${res.statusCode}`));\n\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst chunks: Buffer[] = [];\n\n\t\t\t\t\tres.on('data', chunk => {\n\t\t\t\t\t\tchunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n\t\t\t\t\t});\n\n\t\t\t\t\tres.on('end', () => {\n\t\t\t\t\t\tresolve(Buffer.concat(chunks).toString('utf8'));\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t);\n\n\t\t\treq.on('timeout', () => {\n\t\t\t\treq.destroy(new Error('FR24 Timeout'));\n\t\t\t});\n\n\t\t\treq.on('error', reject);\n\t\t});\n\n\t\tconst matches = [...html.matchAll(/https:\\/\\/cdn\\.jetphotos\\.com\\/[^\"' ]+\\.(jpg|jpeg|png|webp)/gi)];\n\n\t\tif (!matches.length) {\n\t\t\tlogDebug(`FR24 Aircraft kein Bild gefunden: ${reg}`);\n\n\t\t\treturn '';\n\t\t}\n\n\t\tconst imageUrl = String(matches[0][0] || '').trim();\n\n\t\tlogDebug(`FR24 Aircraft Bild gefunden: ${imageUrl}`);\n\n\t\treturn imageUrl;\n\t} catch (e) {\n\t\tlogWarn(`FR24 Aircraft Fehler: ${errorText(e)}`);\n\n\t\treturn '';\n\t}\n}\n\nfunction looksLikeImageBuffer(buf: Buffer): boolean {\n\tif (buf.length >= 3 && buf[0] === 0xff && buf[1] === 0xd8 && buf[2] === 0xff) {\n\t\treturn true;\n\t}\n\n\tif (buf.length >= 8 && buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4e && buf[3] === 0x47) {\n\t\treturn true;\n\t}\n\n\tif (\n\t\tbuf.length >= 12 &&\n\t\tbuf.subarray(0, 4).toString('ascii') === 'RIFF' &&\n\t\tbuf.subarray(8, 12).toString('ascii') === 'WEBP'\n\t) {\n\t\treturn true;\n\t}\n\n\tif (buf.includes(Buffer.from('ftypavif'))) {\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nfunction detectImageExt(buf: Buffer): string {\n\t// JPG\n\tif (buf.length >= 3 && buf[0] === 0xff && buf[1] === 0xd8 && buf[2] === 0xff) {\n\t\treturn 'jpg';\n\t}\n\n\t// PNG\n\tif (buf.length >= 8 && buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4e && buf[3] === 0x47) {\n\t\treturn 'png';\n\t}\n\n\t// WEBP\n\tif (\n\t\tbuf.length >= 12 &&\n\t\tbuf.subarray(0).toString('ascii') === 'RIFF' &&\n\t\tbuf.subarray(8, 12).toString('ascii') === 'WEBP'\n\t) {\n\t\treturn 'webp';\n\t}\n\n\t// AVIF\n\tif (buf.includes(Buffer.from('ftypavif'))) {\n\t\treturn 'avif';\n\t}\n\n\treturn 'jpg';\n}\n\nfunction publicUrl(relPath: string): string {\n\treturn `/jetframe.admin/${relPath}`;\n}\n\nfunction safeFileName(name: string): string {\n\treturn String(name || 'unknown')\n\t\t.trim()\n\t\t.replace(/[^a-zA-Z0-9_-]/g, '_');\n}\n\nfunction downloadImageBuffer(url: string, useReferer: boolean, redirects = 0): Promise<Buffer> {\n\treturn new Promise((resolve, reject) => {\n\t\tif (redirects > 5) {\n\t\t\treject(new Error('Zu viele Redirects beim Bilddownload'));\n\n\t\t\treturn;\n\t\t}\n\n\t\tconst client = url.startsWith('https') ? https : http;\n\n\t\tconst headers: Record<string, string> = {\n\t\t\t'User-Agent': 'Mozilla/5.0 AppleWebKit/605.1.15 Safari/604.1',\n\n\t\t\tAccept: 'image/avif,image/webp,image/apng,image/png,image/jpeg,image/*,*/*;q=0.8',\n\t\t};\n\n\t\tif (useReferer) {\n\t\t\theaders.Referer = 'https://www.flightradar24.com/';\n\t\t}\n\n\t\tconst req = client.get(\n\t\t\turl,\n\t\t\t{\n\t\t\t\theaders,\n\t\t\t\ttimeout: 20000,\n\t\t\t},\n\t\t\tres => {\n\t\t\t\tif (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {\n\t\t\t\t\tconst nextUrl = res.headers.location.startsWith('http')\n\t\t\t\t\t\t? res.headers.location\n\t\t\t\t\t\t: new URL(res.headers.location, url).toString();\n\n\t\t\t\t\tdownloadImageBuffer(nextUrl, useReferer, redirects + 1)\n\t\t\t\t\t\t.then(resolve)\n\t\t\t\t\t\t.catch(reject);\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (res.statusCode !== 200) {\n\t\t\t\t\treject(new Error(`HTTP ${res.statusCode}`));\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst chunks: Buffer[] = [];\n\n\t\t\t\tres.on('data', chunk => {\n\t\t\t\t\tchunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n\t\t\t\t});\n\n\t\t\t\tres.on('end', () => {\n\t\t\t\t\tconst buffer = Buffer.concat(chunks);\n\n\t\t\t\t\tif (!buffer.length) {\n\t\t\t\t\t\treject(new Error('Leeres Bild erhalten'));\n\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst contentType = String(res.headers['content-type'] || '').toLowerCase();\n\n\t\t\t\t\tconst text = buffer.toString('utf8').trim();\n\n\t\t\t\t\tif (!contentType.startsWith('image/') && /^https?:\\/\\//i.test(text)) {\n\t\t\t\t\t\tdownloadImageBuffer(text, useReferer, redirects + 1)\n\t\t\t\t\t\t\t.then(resolve)\n\t\t\t\t\t\t\t.catch(reject);\n\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!looksLikeImageBuffer(buffer)) {\n\t\t\t\t\t\treject(new Error('Antwort ist kein Bild'));\n\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tresolve(buffer);\n\t\t\t\t});\n\t\t\t},\n\t\t);\n\n\t\treq.on('timeout', () => {\n\t\t\treq.destroy(new Error('Bild Download Timeout'));\n\t\t});\n\n\t\treq.on('error', reject);\n\t});\n}\n\nfunction errorText(e: unknown): string {\n\tif (!e) {\n\t\treturn 'unbekannter Fehler';\n\t}\n\n\tif (typeof e === 'string') {\n\t\treturn e;\n\t}\n\n\tif (e instanceof Error) {\n\t\treturn e.message;\n\t}\n\n\ttry {\n\t\treturn JSON.stringify(e);\n\t} catch {\n\t\treturn String(e);\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|