ADSB/AIS image proxy
This commit is contained in:
@@ -327,3 +327,38 @@ export async function getADSBRange() {
|
|||||||
const j = await r.json();
|
const j = await r.json();
|
||||||
return j?.actualRange?.last24h?.points || [];
|
return j?.actualRange?.last24h?.points || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getADSBImage(icao) {
|
||||||
|
async function duckduckgo(query) {
|
||||||
|
async function getVqd(query) {
|
||||||
|
const res = await fetch(`https://duckduckgo.com/?q=${encodeURIComponent(query)}`, {
|
||||||
|
headers: { 'User-Agent': 'Mozilla/5.0' }
|
||||||
|
})
|
||||||
|
const html = await res.text()
|
||||||
|
const match = html.match(/vqd=['"]?([\d-]+)['"]?/)
|
||||||
|
return match?.[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
const vqd = await getVqd(query)
|
||||||
|
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 data = await fetch(url, {headers: {'User-Agent': 'Mozilla/5.0', 'Referer': 'https://duckduckgo.com/'}}).then(resp => resp.json());
|
||||||
|
return data?.results?.[0]?.image || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let aircraft = (await adsbCache).find(a => a.icao?.toLowerCase() === icao?.toLowerCase());
|
||||||
|
if(!aircraft) aircraft = enrichAircraft({hex: icao});
|
||||||
|
if(!aircraft) return null;
|
||||||
|
|
||||||
|
const generic = duckduckgo(aircraft.model ? `${aircraft.manufacturer} ${aircraft.model}` : `${aircraft.aircraft} Aircraft`);
|
||||||
|
const specific = await fetch(`https://api.planespotters.net/pub/photos/hex/${icao.toLowerCase()}`).then(resp => resp.ok ? resp.json() : null);
|
||||||
|
|
||||||
|
let src;
|
||||||
|
if(specific?.photos?.[0]) src = specific?.photos?.[0];
|
||||||
|
else src = await generic;
|
||||||
|
|
||||||
|
if(!src) return null;
|
||||||
|
return fetch(src).then(resp => resp.blob());
|
||||||
|
}
|
||||||
|
|||||||
@@ -87,3 +87,8 @@ export async function getAIS() {
|
|||||||
aisCacheTs = Date.now();
|
aisCacheTs = Date.now();
|
||||||
return aisCache;
|
return aisCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getAISImage(mmsi) {
|
||||||
|
return fetch(`https://www.marinetraffic.com/getAssetDefaultPhoto/?photo_size=800&asset_type_id=0&asset_id=${mmsi}`)
|
||||||
|
.then(resp => resp.ok ? resp.blob() : null);
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import {getCelestialCurrent, getCelestialForecast} from './celestial.mjs';
|
|||||||
import {apiReference} from '@scalar/express-api-reference';
|
import {apiReference} from '@scalar/express-api-reference';
|
||||||
import {spec} from './spec.mjs';
|
import {spec} from './spec.mjs';
|
||||||
import {existsSync} from 'fs';
|
import {existsSync} from 'fs';
|
||||||
import {getAIS} from './ais.mjs';
|
import {getAIS, getAISImage} from './ais.mjs';
|
||||||
import {getADSB, getADSBHistory, getADSBRange, initAircraftDb} from './adsb.mjs';
|
import {getADSBImage, getADSB, getADSBHistory, getADSBRange, initAircraftDb} from './adsb.mjs';
|
||||||
import {fetchIcon} from './openweather.mjs';
|
import {fetchIcon} from './openweather.mjs';
|
||||||
import {getForecast, getWeatherCondition, forecastTTL} from './forecast.mjs';
|
import {getForecast, getWeatherCondition, forecastTTL} from './forecast.mjs';
|
||||||
import {dailyWeather, hourlyWeather} from './openmeteo.mjs';
|
import {dailyWeather, hourlyWeather} from './openmeteo.mjs';
|
||||||
@@ -155,8 +155,18 @@ app.get('/api/icon/:icon', asyncHandler(async (req, res, next) => {
|
|||||||
// ── ADSB Proxy ────────────────────────────────────────────────────────────────
|
// ── ADSB Proxy ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
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-image/:icao', asyncHandler(async (req, res) => {
|
||||||
|
const blob = await getADSBImage(req.params.icao);
|
||||||
|
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))));
|
||||||
app.get('/api/ais', asyncHandler(async (req, res) => res.json(await getAIS())));
|
app.get('/api/ais', asyncHandler(async (req, res) => res.json(await getAIS())));
|
||||||
|
app.get('/api/ais-image/:mmsi', asyncHandler(async (req, res) => {
|
||||||
|
const blob = await getAISImage(req.params.mmsi);
|
||||||
|
const buffer = Buffer.from(await blob.arrayBuffer());
|
||||||
|
res.contentType(blob.type).send(buffer);
|
||||||
|
}));
|
||||||
app.get('/api/range', asyncHandler(async (req, res) => res.json(await getADSBRange())));
|
app.get('/api/range', asyncHandler(async (req, res) => res.json(await getADSBRange())));
|
||||||
|
|
||||||
// ── DOCS ──────────────────────────────────────────────────────────────────────
|
// ── DOCS ──────────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user