Fixed icao images
This commit is contained in:
@@ -9,6 +9,7 @@ import * as cheerio from 'cheerio';
|
|||||||
|
|
||||||
const DIR = dirname(fileURLToPath(import.meta.url));
|
const DIR = dirname(fileURLToPath(import.meta.url));
|
||||||
const DATA = resolve(DIR, '../data');
|
const DATA = resolve(DIR, '../data');
|
||||||
|
const IMAGES_DIR = resolve(DATA, 'aircraft');
|
||||||
const DB_PATH = resolve(DATA, 'aircraft.db');
|
const DB_PATH = resolve(DATA, 'aircraft.db');
|
||||||
const CSV_CACHE = resolve(DATA, 'aircraft_db.csv');
|
const CSV_CACHE = resolve(DATA, 'aircraft_db.csv');
|
||||||
const MAX_HISTORY = 500;
|
const MAX_HISTORY = 500;
|
||||||
@@ -329,50 +330,85 @@ export async function getADSBRange() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getADSBImage(icao) {
|
export async function getADSBImage(icao) {
|
||||||
|
function randomUA() {
|
||||||
|
const v = 36 + Math.floor(Math.random() * 40);
|
||||||
|
const builds = [
|
||||||
|
`Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.${v} (KHTML, like Gecko) Chrome/1${10 + ~~(Math.random() * 16)}.0.0.0 Safari/537.${v}`,
|
||||||
|
`Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.${v} (KHTML, like Gecko) Chrome/1${10 + ~~(Math.random() * 16)}.0.0.0 Safari/537.${v}`,
|
||||||
|
`Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.${v} (KHTML, like Gecko) Chrome/1${10 + ~~(Math.random() * 16)}.0.0.0 Safari/537.${v}`,
|
||||||
|
];
|
||||||
|
return builds[~~(Math.random() * builds.length)];
|
||||||
|
}
|
||||||
|
|
||||||
async function duckduckgo(query) {
|
async function duckduckgo(query) {
|
||||||
async function getVqd(query) {
|
async function getVqd(query) {
|
||||||
const res = await fetch(`https://duckduckgo.com/?q=${encodeURIComponent(query)}`, {
|
const res = await fetch(`https://duckduckgo.com/?q=${encodeURIComponent(query)}`, {
|
||||||
headers: { 'User-Agent': 'Mozilla/5.0' }
|
headers: { 'User-Agent': randomUA() }
|
||||||
})
|
});
|
||||||
const html = await res.text()
|
const html = await res.text();
|
||||||
const match = html.match(/vqd=['"]?([\d-]+)['"]?/)
|
const match = html.match(/vqd=['"]?([\d-]+)['"]?/);
|
||||||
return match?.[1]
|
return match?.[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
const vqd = await getVqd(query)
|
const vqd = await getVqd(query);
|
||||||
if (!vqd) throw new Error('Could not get vqd token')
|
if (!vqd) throw new Error('Could not get vqd token');
|
||||||
|
|
||||||
const url = `https://duckduckgo.com/i.js?q=${encodeURIComponent(query)}&o=json&vqd=${vqd}&f=,,,,,&p=1`
|
const url = `https://duckduckgo.com/i.js?q=${encodeURIComponent(query)}&o=json&vqd=${vqd}&f=,,,,,&p=1`;
|
||||||
const data = await fetch(url,
|
const data = await fetch(url, {
|
||||||
{headers: {'User-Agent': 'Mozilla/5.0', 'Referer': 'https://duckduckgo.com/'}}
|
headers: { 'User-Agent': randomUA(), 'Referer': 'https://duckduckgo.com/' }
|
||||||
).then(resp => resp.json());
|
}).then(resp => resp.json());
|
||||||
console.log(query, data.results)
|
|
||||||
|
|
||||||
// Sequentially try each image until we find a valid one
|
|
||||||
if (data?.results?.length) {
|
if (data?.results?.length) {
|
||||||
for (const result of data.results) {
|
for (const result of data.results) {
|
||||||
if (!result.image) continue;
|
if (!result.image) continue;
|
||||||
try {
|
try {
|
||||||
const imgRes = await fetch(result.image);
|
const imgRes = await fetch(result.image);
|
||||||
if (imgRes.ok) return result.image;
|
if (imgRes.ok) return imgRes.blob();
|
||||||
} catch (e) {
|
} catch { }
|
||||||
continue; // Try next image
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const aircraft = await this.get(icao);
|
async function genericImage(modelType) {
|
||||||
const generic = duckduckgo(`${aircraft.type || aircraft.shape} Aircraft`);
|
if (!modelType) return null;
|
||||||
const specific = await fetch(`https://api.planespotters.net/pub/photos/hex/${icao.toLowerCase()}`,
|
const filePath = resolve(IMAGES_DIR, 'generic', `${modelType}.jpg`);
|
||||||
{headers: {'User-Agent': `open-sight.net (zaktimson@gmail.com)`}}
|
if (fs.existsSync(filePath)) return fs.readFileSync(filePath);
|
||||||
).then(resp => resp.ok ? resp.json() : null);
|
|
||||||
|
|
||||||
let src;
|
const blob = await duckduckgo(`${modelType} Aircraft`);
|
||||||
if(specific?.photos?.[0]) src = specific.photos[0].thumbnail_large?.src || specific.photos[0].thumbnail?.src;
|
if (!blob) return null;
|
||||||
else src = await generic;
|
|
||||||
|
|
||||||
if(!src) return null;
|
fs.mkdirSync(dirname(filePath), { recursive: true });
|
||||||
return fetch(src).then(resp => resp.blob());
|
const buffer = Buffer.from(await blob.arrayBuffer());
|
||||||
|
fs.writeFileSync(filePath, buffer);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function icaoImage(icao) {
|
||||||
|
const filePath = resolve(IMAGES_DIR, `${icao}.jpg`);
|
||||||
|
if (fs.existsSync(filePath)) return fs.readFileSync(filePath);
|
||||||
|
|
||||||
|
const planeSpotters = await fetch(`https://api.planespotters.net/pub/photos/hex/${icao}`, {
|
||||||
|
headers: { 'User-Agent': 'open-sight.net (zaktimson@gmail.com)' }
|
||||||
|
}).then(resp => resp.ok ? resp.json() : null).catch(() => null);
|
||||||
|
|
||||||
|
const src = planeSpotters?.photos?.[0]?.thumbnail_large?.src || planeSpotters?.photos?.[0]?.thumbnail?.src;
|
||||||
|
if (!src) return null;
|
||||||
|
|
||||||
|
const resp = await fetch(src, { headers: { 'Accept': '*/*', 'User-Agent': randomUA() } });
|
||||||
|
if (!resp.ok) return null;
|
||||||
|
|
||||||
|
fs.mkdirSync(IMAGES_DIR, { recursive: true });
|
||||||
|
const buffer = Buffer.from(await resp.arrayBuffer());
|
||||||
|
fs.writeFileSync(filePath, buffer);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
icao = icao.toLowerCase();
|
||||||
|
const aircraft = await enrichAircraft({ hex: icao });
|
||||||
|
const modelType = aircraft.aircraft || aircraft.model || aircraft.type;
|
||||||
|
|
||||||
|
const generic = genericImage(modelType);
|
||||||
|
const specific = await icaoImage(icao);
|
||||||
|
return specific || await generic;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,10 +156,8 @@ app.get('/api/space', async (req, res) => {
|
|||||||
|
|
||||||
app.get('/api/adsb', asyncHandler(async (req, res) => res.json(await getADSB())));
|
app.get('/api/adsb', asyncHandler(async (req, res) => res.json(await getADSB())));
|
||||||
app.get('/api/adsb/:icao/image', asyncHandler(async (req, res) => {
|
app.get('/api/adsb/:icao/image', asyncHandler(async (req, res) => {
|
||||||
const blob = await getADSBImage(req.params.icao);
|
const buffer = await getADSBImage(req.params.icao);
|
||||||
if (!blob) return res.status(404).send('No image found');
|
res.contentType('image/jpeg').send(buffer)
|
||||||
const buffer = Buffer.from(await blob.arrayBuffer());
|
|
||||||
res.contentType(blob.type).send(buffer);
|
|
||||||
}));
|
}));
|
||||||
app.get('/api/adsb/:icao', asyncHandler(async (req, res) => res.json(await getADSBHistory(req.params.icao))));
|
app.get('/api/adsb/:icao', asyncHandler(async (req, res) => res.json(await getADSBHistory(req.params.icao))));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user