kopia lustrzana https://github.com/FacilMap/facilmap
112 wiersze
3.1 KiB
TypeScript
112 wiersze
3.1 KiB
TypeScript
import { load, type CheerioAPI } from "cheerio";
|
|
import compressjs from "compressjs";
|
|
import zlib from "zlib";
|
|
import util from "util";
|
|
import type { SearchResult } from "facilmap-types";
|
|
import stripBomBuf from "strip-bom-buf";
|
|
import config from "./config.js";
|
|
import { find as findSearch, parseUrlQuery } from "facilmap-utils";
|
|
import { getDomainLang, getI18n } from "./i18n.js";
|
|
|
|
export async function find(query: string, loadUrls = false): Promise<Array<SearchResult> | string> {
|
|
if (loadUrls) {
|
|
const url = parseUrlQuery(query);
|
|
if (url) {
|
|
return await _loadUrl(url);
|
|
}
|
|
}
|
|
|
|
const lang = getDomainLang();
|
|
|
|
return await findSearch(query, {
|
|
lang: lang?.isExplicit ? lang.i18n.languages.join(",") : lang?.acceptLanguage
|
|
});
|
|
}
|
|
|
|
async function _loadUrl(url: string): Promise<string> {
|
|
const res = await fetch(
|
|
url,
|
|
{
|
|
headers: {
|
|
"User-Agent": config.userAgent
|
|
}
|
|
}
|
|
);
|
|
|
|
if (!res.ok) {
|
|
throw new Error(getI18n().t("search.url-request-error", { url, status: res.status }));
|
|
}
|
|
|
|
let bodyBuf = new Uint8Array(await res.arrayBuffer());
|
|
|
|
if(!bodyBuf)
|
|
throw new Error(getI18n().t("search.url-response-error"));
|
|
|
|
if(bodyBuf[0] == 0x42 && bodyBuf[1] == 0x5a && bodyBuf[2] == 0x68) {// bzip2
|
|
bodyBuf = Buffer.from(compressjs.Bzip2.decompressFile(Buffer.from(bodyBuf)));
|
|
}
|
|
else if(bodyBuf[0] == 0x1f && bodyBuf[1] == 0x8b && bodyBuf[2] == 0x08) // gzip
|
|
bodyBuf = await util.promisify(zlib.gunzip.bind(zlib))(bodyBuf);
|
|
|
|
const body = new TextDecoder().decode(stripBomBuf(bodyBuf));
|
|
|
|
if(url.match(/^https?:\/\/www\.freietonne\.de\/seekarte\/getOpenLayerPois\.php\?/))
|
|
return body;
|
|
else if(body.match(/^\s*</)) {
|
|
const $ = load(body, { xmlMode: true });
|
|
const rootEl = $.root().children();
|
|
|
|
if(rootEl.is("osm") && url.startsWith("https://api.openstreetmap.org/api/")) {
|
|
await _loadSubRelations($);
|
|
return $.xml();
|
|
} else if(rootEl.is("gpx,kml,osm"))
|
|
return body;
|
|
else
|
|
throw new Error(getI18n().t("search.url-unknown-format-error"));
|
|
} else if(body.match(/^\s*\{/)) {
|
|
const content = JSON.parse(body);
|
|
if(content.type)
|
|
return body;
|
|
else
|
|
throw new Error(getI18n().t("search.url-unknown-format-error"));
|
|
} else {
|
|
throw new Error(getI18n().t("search.url-unknown-format-error"));
|
|
}
|
|
}
|
|
|
|
async function _loadSubRelations($: CheerioAPI) {
|
|
const loadedIds = new Set<string>();
|
|
|
|
// eslint-disable-next-line no-constant-condition
|
|
while (true) {
|
|
const promises: Array<Promise<string>> = [ ];
|
|
|
|
$("member[type='relation']").each(function() {
|
|
const relId = $(this).attr("ref")!;
|
|
if(!loadedIds.has(relId)) {
|
|
$(this).remove(); // Remove relation from result, as it will be returned again as part of the sub request
|
|
promises.push(fetch(
|
|
"https://api.openstreetmap.org/api/0.6/relation/" + relId + "/full",
|
|
{
|
|
headers: {
|
|
"User-Agent": config.userAgent
|
|
}
|
|
}
|
|
).then((res) => res.text()));
|
|
loadedIds.add(relId);
|
|
}
|
|
});
|
|
|
|
if (promises.length == 0)
|
|
return;
|
|
|
|
if(promises.length > 0) {
|
|
const relations = await Promise.all(promises);
|
|
|
|
for (const relation of relations) {
|
|
$.root().children().append(load(relation, { xmlMode: true }).root().children().children());
|
|
}
|
|
}
|
|
}
|
|
}
|