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 =
|
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";
|
"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 = {
|
export type F1tvRequestAuth = {
|
||||||
ascendontoken?: string | null;
|
ascendontoken?: string | null;
|
||||||
entitlementtoken?: string | null;
|
entitlementtoken?: string | null;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import type { ScheduleEventPublic } from "../viewer/schedule-events";
|
import type { ScheduleEventPublic } from "../viewer/schedule-events";
|
||||||
|
import { resolveF1tvImageUrl } from "./client";
|
||||||
import { loadF1tvConfigOnly } from "./settings";
|
import { loadF1tvConfigOnly } from "./settings";
|
||||||
import type { F1tvMeetingSnapshot, F1tvSessionSnapshot } from "./tweaks";
|
import type { F1tvMeetingSnapshot, F1tvSessionSnapshot } from "./tweaks";
|
||||||
|
|
||||||
|
|
@ -29,6 +30,7 @@ export function sessionToPublic(
|
||||||
const stop = new Date(session.endMs).toISOString();
|
const stop = new Date(session.endMs).toISOString();
|
||||||
const phase = phaseFor(session.startMs, session.endMs, session.state, nowMs);
|
const phase = phaseFor(session.startMs, session.endMs, session.state, nowMs);
|
||||||
const playable = session.kind === "live_event" && phase === "live";
|
const playable = session.kind === "live_event" && phase === "live";
|
||||||
|
const image = resolveF1tvImageUrl(session.imageUrl);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: toEventId(session.contentId),
|
id: toEventId(session.contentId),
|
||||||
|
|
@ -50,11 +52,11 @@ export function sessionToPublic(
|
||||||
stopLocal: null,
|
stopLocal: null,
|
||||||
sportBucket: "racing",
|
sportBucket: "racing",
|
||||||
sportLabel: "Racing",
|
sportLabel: "Racing",
|
||||||
imageLandscape: session.imageUrl,
|
imageLandscape: image,
|
||||||
imageBackdrop: session.imageUrl,
|
imageBackdrop: image,
|
||||||
imagePortrait: null,
|
imagePortrait: null,
|
||||||
logoUrl: null,
|
logoUrl: null,
|
||||||
images: session.imageUrl ? [session.imageUrl] : [],
|
images: image ? [image] : [],
|
||||||
audioLanguages: [],
|
audioLanguages: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { AppError } from "../security/errors";
|
import { AppError } from "../security/errors";
|
||||||
import type { F1tvContentOwners } from "./accounts";
|
import type { F1tvContentOwners } from "./accounts";
|
||||||
import { decryptAccount } from "./accounts";
|
import { decryptAccount } from "./accounts";
|
||||||
import { ensureSessionId, fetchPage } from "./client";
|
import { ensureSessionId, fetchPage, resolveF1tvImageUrl } from "./client";
|
||||||
import {
|
import {
|
||||||
loadF1tvSettings,
|
loadF1tvSettings,
|
||||||
saveF1tvSyncResult,
|
saveF1tvSyncResult,
|
||||||
|
|
@ -136,9 +136,10 @@ function extractSessions(
|
||||||
state: String(emf.state ?? meta.state ?? "").trim() || null,
|
state: String(emf.state ?? meta.state ?? "").trim() || null,
|
||||||
startMs,
|
startMs,
|
||||||
endMs: Number.isFinite(endMs) && endMs > 0 ? endMs : startMs + 2 * 3600_000,
|
endMs: Number.isFinite(endMs) && endMs > 0 ? endMs : startMs + 2 * 3600_000,
|
||||||
imageUrl:
|
imageUrl: resolveF1tvImageUrl(
|
||||||
String(meta.pictureUrl ?? meta.imageUrl ?? meta.embargoImage ?? "").trim() ||
|
String(meta.pictureUrl ?? meta.imageUrl ?? meta.embargoImage ?? "").trim() ||
|
||||||
null,
|
null
|
||||||
|
),
|
||||||
defaultChannelId,
|
defaultChannelId,
|
||||||
accountIds: [accountId],
|
accountIds: [accountId],
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue