CI / build-and-test (push) Successful in 14m6s
Règle partagée front/back : flag natif, verticale stricte (ratio ≤ 0.8, ≤ 90 s), verticale sans durée jamais short, repli durée (70 YT / 75 autres). Dimensions alimentées par Dailymotion/Odysee/PeerTube, flag maison Rumble supprimé, condition innertube morte simplifiée. Shorts : langues utilisateur (UI + transcripts) via relevanceLanguage/languageOneOf/Twitch language, son activé par défaut.
69 lines
2.6 KiB
JavaScript
69 lines
2.6 KiB
JavaScript
/**
|
|
* Minimal Odysee provider handler
|
|
*/
|
|
const handler = {
|
|
id: 'od',
|
|
label: 'Odysee',
|
|
/**
|
|
* @param {string} q
|
|
* @param {{ limit: number, page?: number }} opts
|
|
* @returns {Promise<Array<any>>}
|
|
*/
|
|
async search(q, opts) {
|
|
const { limit = 10, page = 1 } = opts || {};
|
|
try {
|
|
const perPage = Math.min(Math.max(1, Number(limit || 10)), 50);
|
|
const pageNum = Math.max(1, Number(page || 1));
|
|
const params = new URLSearchParams({
|
|
s: q,
|
|
size: perPage.toString(),
|
|
from: ((pageNum - 1) * perPage).toString(),
|
|
include: 'channel,thumbnail_url,title,description,duration,release_time,claimId,name',
|
|
mediaType: 'video'
|
|
});
|
|
|
|
const response = await fetch(`https://lighthouse.odysee.tv/search?${params.toString()}`);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`Odysee API error: ${response.status}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
|
|
return (Array.isArray(data) ? data : []).map(item => {
|
|
const rawThumb = item.thumbnail_url || '';
|
|
const thumbnail = rawThumb
|
|
? rawThumb.startsWith('http')
|
|
? rawThumb
|
|
: `https://thumbnails.odycdn.com/optimize/s:390:0/quality:85/plain/${rawThumb.replace(/^\//, '')}`
|
|
: undefined;
|
|
const name = item.name || '';
|
|
const claimId = item.claimId || item.claim_id || '';
|
|
const urlSegment = claimId ? `${encodeURIComponent(name)}:${claimId}` : encodeURIComponent(name);
|
|
// Dimensions vidéo (signal d'orientation pour isShortItem) quand
|
|
// lighthouse les expose — lecture défensive, aucun appel en plus.
|
|
const vw = Number(item.video?.width ?? item.video?.video_width ?? item.width);
|
|
const vh = Number(item.video?.height ?? item.video?.video_height ?? item.height);
|
|
|
|
return {
|
|
title: item.title || name,
|
|
id: claimId || name,
|
|
url: claimId ? `https://odysee.com/${urlSegment}` : `https://odysee.com/${encodeURIComponent(name)}`,
|
|
thumbnail,
|
|
uploaderName: item.channel || undefined,
|
|
type: 'video',
|
|
duration: typeof item.duration === 'number' && item.duration > 0
|
|
? Math.round(item.duration)
|
|
: (typeof item.video?.duration === 'number' && item.video.duration > 0 ? Math.round(item.video.duration) : undefined),
|
|
...(Number.isFinite(vw) && vw > 0 ? { width: vw } : {}),
|
|
...(Number.isFinite(vh) && vh > 0 ? { height: vh } : {}),
|
|
};
|
|
});
|
|
} catch (error) {
|
|
console.error('Odysee search error:', error);
|
|
return [];
|
|
}
|
|
}
|
|
};
|
|
export default handler;
|