Update src/libpoketube/init/pages-static.js

This commit is contained in:
ashley 2025-08-24 12:14:06 +02:00
parent 256112ec03
commit 56c02e58b2

View File

@ -65,6 +65,128 @@ app.get("/143", (req, res) => {
return res.redirect(`/?number=${numberEasterEgg}`);
});
// GET /weather — SSR + hydrates the same EJS for no-JS users.
// Query options:
// ?q=Izmir (free text place)
// ?lat=38.42&lon=27.14 (coordinates)
// ?units=metric|imperial
app.get("/weather", async (req, res) => {
try {
const q = (req.query.q || "").trim();
const lat = req.query.lat ? Number(req.query.lat) : null;
const lon = req.query.lon ? Number(req.query.lon) : null;
const units = (req.query.units === "imperial") ? "imperial" : "metric";
const tempUnit = units === "metric" ? "celsius" : "fahrenheit";
const windUnit = units === "metric" ? "kmh" : "mph";
// Resolve coordinates
let place = { name: null, lat: null, lon: null };
if (lat != null && lon != null) {
place = { name: `${lat.toFixed(3)}, ${lon.toFixed(3)}`, lat, lon };
// reverse geocode (best effort)
try {
const r = await fetch(`https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${lat}&lon=${lon}`);
if (r.ok) {
const j = await r.json();
place.name = (j.display_name || "").split(",").slice(0,2).join(", ") || place.name;
}
} catch {}
} else if (q) {
const r = await fetch(`https://nominatim.openstreetmap.org/search?format=json&limit=1&addressdetails=1&q=${encodeURIComponent(q)}`, { headers: { "Accept-Language": req.headers["accept-language"] || "en" }});
const arr = r.ok ? await r.json() : [];
if (!arr[0]) return renderTemplate(res, req, "weather.ejs", { ssr: { forceNoJS: true, name: "Not found" }});
place = {
name: (arr[0].display_name || "").split(",").slice(0,2).join(", "),
lat: Number(arr[0].lat),
lon: Number(arr[0].lon)
};
} else {
// default: try to render minimal page with no data
return renderTemplate(res, req, "weather.ejs", { ssr: { forceNoJS: true, name: "Choose a location" }});
}
// Fetch Open-Meteo
const url = new URL("https://api.open-meteo.com/v1/forecast");
url.searchParams.set("latitude", place.lat);
url.searchParams.set("longitude", place.lon);
url.searchParams.set("current","temperature_2m,relative_humidity_2m,apparent_temperature,is_day,precipitation,weather_code,wind_speed_10m,wind_direction_10m,pressure_msl");
url.searchParams.set("hourly","temperature_2m,apparent_temperature,precipitation_probability,precipitation,weather_code,wind_speed_10m");
url.searchParams.set("daily","weather_code,temperature_2m_max,temperature_2m_min,sunrise,sunset,uv_index_max");
url.searchParams.set("timezone","auto");
url.searchParams.set("forecast_days","7");
url.searchParams.set("temperature_unit", tempUnit);
url.searchParams.set("windspeed_unit", windUnit);
const wr = await fetch(url.toString());
if (!wr.ok) throw new Error("weather fetch failed");
const data = await wr.json();
// Helpers to map WMO codes for SSR
const codeText = (c)=>{
if ([0].includes(c)) return "Clear sky";
if ([1].includes(c)) return "Mostly clear";
if ([2].includes(c)) return "Partly cloudy";
if ([3].includes(c)) return "Overcast";
if ([45,48].includes(c)) return "Fog";
if ([51,53,55].includes(c)) return "Drizzle";
if ([56,57].includes(c)) return "Freezing drizzle";
if ([61,63,65].includes(c)) return "Rain";
if ([66,67].includes(c)) return "Freezing rain";
if ([71,73,75].includes(c)) return "Snow";
if ([77].includes(c)) return "Snow grains";
if ([80,81,82].includes(c)) return "Showers";
if ([85,86].includes(c)) return "Snow showers";
if ([95].includes(c)) return "Thunderstorm";
if ([96,99].includes(c)) return "Storm & hail";
return "—";
};
const codeIcon = (c,isDay)=>{
if (c===0) return isDay ? "☀️" : "🌙";
if ([1,2].includes(c)) return isDay ? "🌤️" : "☁️";
if ([3].includes(c)) return "☁️";
if ([45,48].includes(c)) return "🌫️";
if ([51,53,55,80,81,82].includes(c)) return "🌦️";
if ([61,63,65].includes(c)) return "🌧️";
if ([66,67].includes(c)) return "🌧️❄️";
if ([71,73,75,77,85,86].includes(c)) return "❄️";
if ([95,96,99].includes(c)) return "⛈️";
return "☁️";
};
const cur = data.current || {};
const daily = data.daily || { time:[] };
// Next-hour precip probability best-effort
let popNext = null;
try {
const idx = (data.hourly?.time||[]).findIndex(t => Date.parse(t) > Date.now());
popNext = data.hourly?.precipitation_probability?.[idx>=0?idx:0] ?? null;
} catch {}
// Prepare SSR payload
const ssr = {
forceNoJS: Boolean(req.query.nojs),
name: place.name,
lat: place.lat,
lon: place.lon,
windUnit,
current: cur,
daily: daily,
icon: codeIcon(cur.weather_code, cur.is_day),
desc: codeText(cur.weather_code),
sunriseLocal: daily.sunrise ? new Date(daily.sunrise[0]).toLocaleTimeString([], {hour:'2-digit',minute:'2-digit'}) : "—",
sunsetLocal: daily.sunset ? new Date(daily.sunset[0]).toLocaleTimeString([], {hour:'2-digit',minute:'2-digit'}) : "—",
popNext,
dailyIcons: (daily.weather_code||[]).map(c => codeIcon(c,1)),
dailyTexts: (daily.weather_code||[]).map(c => codeText(c)),
dailyLabels: (daily.time||[]).map(t => new Date(t).toLocaleDateString([], {weekday:'short',month:'short',day:'numeric'}))
};
// Render
return renderTemplate(res, req, "weather.ejs", { ssr });
} catch (err) {
return renderTemplate(res, req, "weather.ejs", { ssr:{ forceNoJS:true, name:"Error loading weather" } });
}
});
app.get("/rewind", function (req, res) {