fix(shorts): autoplay muet, langues FR/EN, proxy prod et Dailymotion embeds
CI / build-and-test (push) Successful in 14m9s
CI / build-and-test (push) Successful in 14m9s
- autoplay: les Shorts demarrent muets (comme Watch) pour garantir l'autoplay des iframes; le son se debloque a la premiere interaction (unlockSound) et reste controlable
- proxy prod: ajoute les alias /proxy/twitch-api, /proxy/twitch-auth, /proxy/dm, /proxy/odysee (evite le fallback SPA -> parse JSON 'status 200 ok:false'); forwardJson accepte les deux prefixes. Corrige aussi la validation des clips Twitch morts
- Dailymotion: allow_embed demande dans les flux shorts/trending -> les videos non-embeddables ('Forbidden') sont exclues
- langues: mapping language (Dailymotion/PeerTube/Odysee) + shortsLangOk applique a tous les fournisseurs; en+fr toujours acceptes
- PeerTube shorts: durationMin/Max cote serveur (plus de videos longues au hasard)
- Twitch: validation helix/clips de nouveau fonctionnelle + autoplay muet
This commit is contained in:
@@ -52,6 +52,7 @@ export class YoutubeApiService {
|
||||
const base = String(v || '').trim().toLowerCase().split('-')[0];
|
||||
if (/^[a-z]{2,3}$/.test(base) && !out.includes(base)) out.push(base);
|
||||
};
|
||||
// 1) Préférences d'abord (l'UI guide la langue primaire des recherches).
|
||||
try { push(this.i18n.lang()); } catch {}
|
||||
try {
|
||||
const prefs = this.users.preferences() as { language?: unknown; downloadLanguages?: unknown } | null;
|
||||
@@ -59,7 +60,11 @@ export class YoutubeApiService {
|
||||
const dl = (prefs as any)?.downloadLanguages;
|
||||
if (Array.isArray(dl)) for (const l of dl) push(l);
|
||||
} catch {}
|
||||
return out.length ? out : ['en'];
|
||||
// 2) L'app ne propose que FR/EN : ces deux langues sont TOUJOURS acceptées
|
||||
// (on filtre les autres uniquement quand le provider expose `language`).
|
||||
push('en');
|
||||
push('fr');
|
||||
return out;
|
||||
}
|
||||
|
||||
/** Langue primaire (2 lettres) pour les APIs qui n'acceptent qu'un code ISO 639-1. */
|
||||
@@ -589,7 +594,7 @@ export class YoutubeApiService {
|
||||
limit: String(limit),
|
||||
page: String(page),
|
||||
country: String(region).toLowerCase(),
|
||||
fields: 'id,title,thumbnail_url,thumbnail_480_url,thumbnail_720_url,duration,views_total,allow_embed,owner.id,owner.screenname,owner.avatar_80_url,created_time',
|
||||
fields: 'id,title,thumbnail_url,thumbnail_480_url,thumbnail_720_url,duration,views_total,language,allow_embed,owner.id,owner.screenname,owner.avatar_80_url,created_time',
|
||||
});
|
||||
return this.fetchDailymotion('/videos', params).pipe(
|
||||
map(res => {
|
||||
@@ -1104,7 +1109,9 @@ export class YoutubeApiService {
|
||||
// Dailymotion compte en minutes : 2 = ≤ 2 min, resserré à ≤75 s côté client.
|
||||
shorter_than: '2',
|
||||
country: String(region).toLowerCase(),
|
||||
fields: 'id,title,thumbnail_url,thumbnail_480_url,thumbnail_720_url,duration,views_total,owner.screenname,owner.avatar_80_url,created_time',
|
||||
// `allow_embed` indispensable : sans lui, les vidéos non-embeddables
|
||||
// passent le filtre et le player répond "Forbidden".
|
||||
fields: 'id,title,thumbnail_url,thumbnail_480_url,thumbnail_720_url,duration,views_total,language,allow_embed,owner.screenname,owner.avatar_80_url,created_time',
|
||||
search: q,
|
||||
});
|
||||
return this.fetchDailymotion('/videos', params).pipe(
|
||||
@@ -1158,6 +1165,10 @@ export class YoutubeApiService {
|
||||
sort: '-views',
|
||||
isLocal: 'true',
|
||||
nsfw: 'false',
|
||||
// Bornes de durée côté serveur : sans elles, le feed rendait des vidéos
|
||||
// longues (rejetées ensuite) et pouvait se retrouver quasi vide.
|
||||
durationMin: '1',
|
||||
durationMax: '90',
|
||||
});
|
||||
// Langues de l'utilisateur (filtre natif PeerTube, ignoré si inconnu).
|
||||
try {
|
||||
@@ -1237,6 +1248,7 @@ export class YoutubeApiService {
|
||||
duration: Number(i.duration || 0),
|
||||
views: Number(i.views_total || 0),
|
||||
uploaded: i.created_time ? i.created_time * 1000 : 0,
|
||||
language: i?.language ? String(i.language).toLowerCase().split('-')[0] : undefined,
|
||||
raw: { allowEmbed: i?.allow_embed !== false },
|
||||
});
|
||||
|
||||
@@ -1269,6 +1281,8 @@ export class YoutubeApiService {
|
||||
duration: Number(i.duration || 0),
|
||||
views: Number(i.views || 0),
|
||||
uploaded: i.publishedAt ? Date.parse(i.publishedAt) : 0,
|
||||
// PeerTube expose `language` sous forme { id, label } ou chaîne ISO.
|
||||
language: (i?.language && String(i.language.id || i.language).toLowerCase().split('-')[0]) || undefined,
|
||||
} as Video;
|
||||
};
|
||||
|
||||
@@ -1289,6 +1303,9 @@ export class YoutubeApiService {
|
||||
?? (typeof i?.value?.duration !== 'undefined' ? i.value.duration : undefined);
|
||||
const duration = this.normalizeDuration(rawDuration);
|
||||
const views = (i?.meta?.view_count != null) ? Number(i.meta.view_count) : 0;
|
||||
// Odysee : `value.languages` (tableau ISO) ou `value.language` (chaîne).
|
||||
const langRaw = Array.isArray(val?.languages) ? val.languages[0] : (val?.language ?? i?.language);
|
||||
const language = langRaw ? String(langRaw).toLowerCase().split('-')[0] : undefined;
|
||||
const uploaderName = channelVal?.title || channel?.name || '';
|
||||
const channelClaimId = channel?.claim_id || channel?.claimId || '';
|
||||
const channelHandle = channel?.name || '';
|
||||
@@ -1312,6 +1329,7 @@ export class YoutubeApiService {
|
||||
duration,
|
||||
views: views,
|
||||
uploaded: publishedAt ? Date.parse(publishedAt) : 0,
|
||||
language,
|
||||
} as Video;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user