Fix F1TV cover URLs: resolve relative pictureUrl via image-resizer.
F1 metadata returns paths like 1000…/id, not full HTTPS URLs — hub cards and hero stayed blank without this.
This commit is contained in:
parent
8685870358
commit
20a3cfb056
3 changed files with 35 additions and 6 deletions
|
|
@ -4,6 +4,32 @@ const F1TV_ORIGIN = "https://f1tv.formula1.com";
|
|||
const DEFAULT_UA =
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36";
|
||||
|
||||
/**
|
||||
* F1TV metadata levert `pictureUrl` als relatief pad
|
||||
* (`1000010377-…` of `…/landscape_web`), niet als http(s)-URL.
|
||||
* Covers/hero's gaan via image-resizer.
|
||||
*/
|
||||
export function resolveF1tvImageUrl(
|
||||
raw: string | null | undefined,
|
||||
opts?: { width?: number; height?: number; variant?: string }
|
||||
): string | null {
|
||||
const t = String(raw ?? "").trim();
|
||||
if (!t) return null;
|
||||
if (/^https?:\/\//i.test(t)) return t;
|
||||
const variant = opts?.variant || "landscape_web";
|
||||
const w = opts?.width ?? 768;
|
||||
const h = opts?.height ?? 427;
|
||||
let path = t.replace(/^\/+/, "");
|
||||
if (path.startsWith("image-resizer/")) {
|
||||
return `${F1TV_ORIGIN}/${path}${path.includes("?") ? "" : `?w=${w}&h=${h}`}`;
|
||||
}
|
||||
if (path.startsWith("image/")) path = path.slice("image/".length);
|
||||
// Already has variant suffix → keep; otherwise append landscape_web.
|
||||
const hasVariant = /\/(landscape_web|portrait_web|hero_web|square_web)\b/i.test(path);
|
||||
const withVariant = hasVariant ? path : `${path}/${variant}`;
|
||||
return `${F1TV_ORIGIN}/image-resizer/image/${withVariant}?w=${w}&h=${h}`;
|
||||
}
|
||||
|
||||
export type F1tvRequestAuth = {
|
||||
ascendontoken?: string | null;
|
||||
entitlementtoken?: string | null;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import type { ScheduleEventPublic } from "../viewer/schedule-events";
|
||||
import { resolveF1tvImageUrl } from "./client";
|
||||
import { loadF1tvConfigOnly } from "./settings";
|
||||
import type { F1tvMeetingSnapshot, F1tvSessionSnapshot } from "./tweaks";
|
||||
|
||||
|
|
@ -29,6 +30,7 @@ export function sessionToPublic(
|
|||
const stop = new Date(session.endMs).toISOString();
|
||||
const phase = phaseFor(session.startMs, session.endMs, session.state, nowMs);
|
||||
const playable = session.kind === "live_event" && phase === "live";
|
||||
const image = resolveF1tvImageUrl(session.imageUrl);
|
||||
|
||||
return {
|
||||
id: toEventId(session.contentId),
|
||||
|
|
@ -50,11 +52,11 @@ export function sessionToPublic(
|
|||
stopLocal: null,
|
||||
sportBucket: "racing",
|
||||
sportLabel: "Racing",
|
||||
imageLandscape: session.imageUrl,
|
||||
imageBackdrop: session.imageUrl,
|
||||
imageLandscape: image,
|
||||
imageBackdrop: image,
|
||||
imagePortrait: null,
|
||||
logoUrl: null,
|
||||
images: session.imageUrl ? [session.imageUrl] : [],
|
||||
images: image ? [image] : [],
|
||||
audioLanguages: [],
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { AppError } from "../security/errors";
|
||||
import type { F1tvContentOwners } from "./accounts";
|
||||
import { decryptAccount } from "./accounts";
|
||||
import { ensureSessionId, fetchPage } from "./client";
|
||||
import { ensureSessionId, fetchPage, resolveF1tvImageUrl } from "./client";
|
||||
import {
|
||||
loadF1tvSettings,
|
||||
saveF1tvSyncResult,
|
||||
|
|
@ -136,9 +136,10 @@ function extractSessions(
|
|||
state: String(emf.state ?? meta.state ?? "").trim() || null,
|
||||
startMs,
|
||||
endMs: Number.isFinite(endMs) && endMs > 0 ? endMs : startMs + 2 * 3600_000,
|
||||
imageUrl:
|
||||
imageUrl: resolveF1tvImageUrl(
|
||||
String(meta.pictureUrl ?? meta.imageUrl ?? meta.embargoImage ?? "").trim() ||
|
||||
null,
|
||||
null
|
||||
),
|
||||
defaultChannelId,
|
||||
accountIds: [accountId],
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue