116 lines
3.2 KiB
JavaScript
116 lines
3.2 KiB
JavaScript
const https = require('https');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const crypto = require('crypto');
|
|
|
|
const CACHE_DIR = path.join(__dirname, '..', 'cache');
|
|
|
|
// Ensure cache directory exists
|
|
if (!fs.existsSync(CACHE_DIR)) {
|
|
fs.mkdirSync(CACHE_DIR, { recursive: true });
|
|
}
|
|
|
|
/**
|
|
* Generate a safe filename from a city query
|
|
* @param {string} cityQuery - City name
|
|
* @returns {string} Safe filename
|
|
*/
|
|
function getCacheFileName(cityQuery) {
|
|
const hash = crypto.createHash('md5').update(cityQuery.toLowerCase().trim()).digest('hex');
|
|
return `geocode_${hash}.json`;
|
|
}
|
|
|
|
/**
|
|
* Get cached geocode data if available
|
|
* @param {string} cityQuery - City name
|
|
* @returns {Object|null} Cached data or null
|
|
*/
|
|
function getCachedGeocode(cityQuery) {
|
|
try {
|
|
const cacheFile = path.join(CACHE_DIR, getCacheFileName(cityQuery));
|
|
if (fs.existsSync(cacheFile)) {
|
|
const data = fs.readFileSync(cacheFile, 'utf8');
|
|
const cached = JSON.parse(data);
|
|
// Verify the query matches
|
|
if (cached.query && cached.query.toLowerCase().trim() === cityQuery.toLowerCase().trim()) {
|
|
console.log(`Geocode: ${cityQuery} (cached)`);
|
|
return cached;
|
|
}
|
|
}
|
|
} catch (error) {
|
|
// Silent fail
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Save geocode data to cache
|
|
* @param {string} cityQuery - City name
|
|
* @param {Object} data - Geocode data to cache
|
|
*/
|
|
function saveCachedGeocode(cityQuery, data) {
|
|
try {
|
|
const cacheFile = path.join(CACHE_DIR, getCacheFileName(cityQuery));
|
|
fs.writeFileSync(cacheFile, JSON.stringify(data, null, 2), 'utf8');
|
|
} catch (error) {
|
|
// Silent fail
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Geocode city to lat/lon using Nominatim (OpenStreetMap)
|
|
* @param {string} cityQuery - City name to geocode
|
|
* @returns {Promise<{lat: number, lon: number, displayName: string}>}
|
|
*/
|
|
async function geocodeCity(cityQuery) {
|
|
// Check cache first
|
|
const cached = getCachedGeocode(cityQuery);
|
|
if (cached) {
|
|
return cached;
|
|
}
|
|
return new Promise((resolve, reject) => {
|
|
const encodedQuery = encodeURIComponent(cityQuery);
|
|
const url = `https://nominatim.openstreetmap.org/search?q=${encodedQuery}&format=json&limit=1`;
|
|
|
|
const options = {
|
|
headers: {
|
|
'User-Agent': 'webpage-to-hls-streaming-app/1.0'
|
|
}
|
|
};
|
|
|
|
https.get(url, options, (res) => {
|
|
let data = '';
|
|
|
|
res.on('data', (chunk) => {
|
|
data += chunk;
|
|
});
|
|
|
|
res.on('end', () => {
|
|
try {
|
|
const results = JSON.parse(data);
|
|
if (results && results.length > 0) {
|
|
const geocodeResult = {
|
|
query: cityQuery,
|
|
lat: parseFloat(results[0].lat),
|
|
lon: parseFloat(results[0].lon),
|
|
displayName: results[0].display_name
|
|
};
|
|
console.log(`Geocode: ${cityQuery} -> ${geocodeResult.displayName} (API)`);
|
|
// Save to cache
|
|
saveCachedGeocode(cityQuery, geocodeResult);
|
|
resolve(geocodeResult);
|
|
} else {
|
|
reject(new Error('No results found'));
|
|
}
|
|
} catch (error) {
|
|
reject(error);
|
|
}
|
|
});
|
|
}).on('error', (error) => {
|
|
reject(error);
|
|
});
|
|
});
|
|
}
|
|
|
|
module.exports = { geocodeCity };
|