Fix : Stream Preview
All checks were successful
release / build (push) Successful in 27s
release / verify-windows (push) Successful in 1m26s

This commit is contained in:
jeanotx32
2026-08-12 16:37:04 +02:00
parent 58aadee97d
commit 8c82807368
3 changed files with 39 additions and 24 deletions

View File

@@ -603,11 +603,16 @@ flux — pas une vidéo : le serveur n'a que l'API REST publique de la plateform
pas l'agent qui pilote un navigateur ici. Au survol, l'image grandit et passe au premier pas l'agent qui pilote un navigateur ici. Au survol, l'image grandit et passe au premier
plan, quitte à déborder sur les cartes voisines. plan, quitte à déborder sur les cartes voisines.
Source : le même appel que le statut (`/api/front/v2/models/username/{pseudo}/cam`) inclut Source : le même appel que le statut (`/api/front/v2/models/username/{pseudo}/cam`), sans
déjà `previewUrlThumbBig` et `snapshotTimestamp` — aucune requête supplémentaire. L'URL de requête supplémentaire — mais pas son champ `previewUrlThumbBig` / `previewUrlThumbSmall` :
la vignette ne change pas d'une image à l'autre (le CDN la sert avec un cache de 30 jours), malgré leur nom, ce sont ceux de la photo de couverture du profil, fixe, choisie par le
donc le client y ajoute `?t={snapshotTimestamp}` pour forcer un frame à jour à chaque sonde modèle, pas une image de la diffusion en cours (repéré à l'usage : l'aperçu ne changeait
plutôt que de garder indéfiniment la toute première image vue. jamais). La vraie vignette caméra se reconstruit à partir de deux autres champs de la même
réponse, `id` et `snapshotTimestamp`, en `https://img.doppiocdn.net/thumbs/{snapshotTimestamp}/{id}`
— confirmé en comparant les deux : contenu différent, cache CDN de 30 minutes au lieu de
30 jours, `Last-Modified` à quelques secondes de la requête. Cet horodatage étant dans le
chemin de l'URL et non dans un paramètre, chaque nouvelle vignette porte déjà sa propre URL —
inutile de forcer l'invalidation du cache navigateur en plus.
Se rafraîchit donc au rythme de la veille (`WATCHLIST_INTERVAL_MS`, 30 s par défaut) — un Se rafraîchit donc au rythme de la veille (`WATCHLIST_INTERVAL_MS`, 30 s par défaut) — un
aperçu, pas un flux temps réel. aperçu, pas un flux temps réel.

View File

@@ -1206,17 +1206,26 @@ export interface StripchatStatus {
*/ */
statusChangedAt: number | null; statusChangedAt: number | null;
/** /**
* Vignette du flux (`previewUrlThumbBig`) — une image, pas un flux vidéo : * Image du flux — pas le flux lui-même : l'agent seul pilote un navigateur,
* l'agent seul pilote un navigateur, le serveur n'a que cette API REST. * le serveur n'a que cette API REST.
* Absente si le modèle n'a jamais diffusé. *
* Reconstruite depuis `img.doppiocdn.net/thumbs/{snapshotTimestamp}/{id}` et
* non lue telle quelle sur `previewUrlThumbBig` / `previewUrlThumbSmall` :
* ces deux champs, malgré leur nom, pointent vers la photo de couverture du
* profil — fixe, choisie par le modèle — et non vers une image de la
* diffusion en cours. `img.doppiocdn.net` est la vraie vignette caméra,
* confirmée en comparant les deux : contenu différent, cache CDN de 30 min
* au lieu de 30 jours, `Last-Modified` à quelques secondes de la requête.
*
* Absente si le modèle n'a jamais diffusé (pas d'identifiant numérique ou
* d'horodatage exploitable côté plateforme).
*/ */
previewUrl: string | null; previewUrl: string | null;
/** /**
* Horodatage de cette vignette côté plateforme, pour invalider le cache du * Horodatage de cette vignette côté plateforme — déjà encodé dans
* navigateur : l'URL, elle, ne change pas d'une image à l'autre — le CDN la * `previewUrl`, donc sans utilité pour le cache : une nouvelle vignette porte
* sert avec un `max-age` de 30 jours. Sans un paramètre qui varie à chaque * de toute façon une URL différente. Exposé pour l'affichage seulement
* rafraîchissement, le navigateur garderait indéfiniment la toute première * (« vignette vieille de X »).
* image vue.
*/ */
snapshotAt: number | null; snapshotAt: number | null;
} }
@@ -1254,10 +1263,10 @@ export async function fetchStripchatStatus(
const payload = (await response.json()) as { const payload = (await response.json()) as {
user?: { user?: {
user?: { user?: {
id?: number;
status?: string; status?: string;
avatarUrl?: string; avatarUrl?: string;
statusChangedAt?: string; statusChangedAt?: string;
previewUrlThumbBig?: string;
snapshotTimestamp?: number; snapshotTimestamp?: number;
}; };
}; };
@@ -1273,6 +1282,7 @@ export async function fetchStripchatStatus(
: Number.NaN; : Number.NaN;
// Secondes côté API, comme tous les timestamps Unix bruts de cette plateforme. // Secondes côté API, comme tous les timestamps Unix bruts de cette plateforme.
const snapshotAt = Number(profile?.snapshotTimestamp) * 1000; const snapshotAt = Number(profile?.snapshotTimestamp) * 1000;
const modelId = Number(profile?.id);
return { return {
raw, raw,
@@ -1280,8 +1290,8 @@ export async function fetchStripchatStatus(
avatarUrl: typeof profile?.avatarUrl === 'string' && profile.avatarUrl ? profile.avatarUrl : null, avatarUrl: typeof profile?.avatarUrl === 'string' && profile.avatarUrl ? profile.avatarUrl : null,
statusChangedAt: Number.isFinite(changedAt) ? changedAt : null, statusChangedAt: Number.isFinite(changedAt) ? changedAt : null,
previewUrl: previewUrl:
typeof profile?.previewUrlThumbBig === 'string' && profile.previewUrlThumbBig Number.isFinite(modelId) && modelId > 0 && Number.isFinite(snapshotAt) && snapshotAt > 0
? profile.previewUrlThumbBig ? `https://img.doppiocdn.net/thumbs/${Math.round(snapshotAt / 1000)}/${modelId}`
: null, : null,
snapshotAt: Number.isFinite(snapshotAt) && snapshotAt > 0 ? snapshotAt : null, snapshotAt: Number.isFinite(snapshotAt) && snapshotAt > 0 ? snapshotAt : null,
}; };

View File

@@ -122,16 +122,16 @@ export function autoRecordHint(
} }
/** /**
* URL de la vignette du flux, invalidée par un paramètre de cache. * URL de la vignette du flux.
* *
* `previewUrl` ne change pas d'une image à l'autre — c'est le CDN qui la sert * Un simple passe-plat aujourd'hui — `previewUrl` porte déjà l'horodatage de
* avec un `max-age` de 30 jours — donc sans `snapshotAt` en paramètre, le * la vignette dans son chemin, donc chaque nouvelle image a naturellement sa
* navigateur garderait indéfiniment la toute première image vue au lieu de la * propre URL, sans paramètre de cache à ajouter. Gardé comme fonction plutôt
* rafraîchir à chaque sonde. * qu'un accès direct au champ : c'est le seul point à changer si la source
* de la vignette évolue encore une fois.
*/ */
export function previewSrc(target: Pick<WatchTarget, 'previewUrl' | 'snapshotAt'>): string | null { export function previewSrc(target: Pick<WatchTarget, 'previewUrl'>): string | null {
if (!target.previewUrl) return null; return target.previewUrl;
return target.snapshotAt ? `${target.previewUrl}?t=${target.snapshotAt}` : target.previewUrl;
} }
/** Fenêtres proposées sous les frises. */ /** Fenêtres proposées sous les frises. */