Files
NewTube/server/providers/registry.mjs
T
bruno f443a470f9
CI / build-and-test (push) Successful in 14m6s
feat: détection short par orientation vidéo
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.
2026-09-29 12:17:10 -04:00

60 lines
2.4 KiB
JavaScript

// Server-side provider registry for search fan-out
// Each provider module should export an object with a `search(q, opts)` function returning Promise<Suggestion[]>
/**
* @typedef {Object} Suggestion
* @property {string} title
* @property {string} id
* @property {number=} duration
* @property {boolean=} isShort
* @property {string=} url
* @property {string=} thumbnail
* @property {string=} uploaderName
* @property {string=} type
* @property {number=} width (largeur vidéo en px, quand le provider l'expose)
* @property {number=} height (hauteur vidéo en px, quand le provider l'expose)
*/
/** @typedef {'yt'|'dm'|'tw'|'pt'|'od'|'ru'} ProviderId */
import channelRegistry from './channel-registry.mjs';
/**
* @typedef {Object} ProviderAdapter
* @property {ProviderId} id
* @property {string} label
* @property {(q: string, opts: { limit: number, page?: number, sort?: string }) => Promise<Suggestion[]>} search
* @property {(q: string, opts?: { limit?: number }) => Promise<string[]>} [suggest]
* @property {(externalId: string, ctx?: any) => Promise<any=} } [fetchChannelById]
*/
/** @type {Record<ProviderId, ProviderAdapter>} */
export const providerRegistry = {
/** @type {any} */ yt: (await import('./youtube.mjs')).default,
/** @type {any} */ dm: (await import('./dailymotion.mjs')).default,
/** @type {any} */ tw: (await import('./twitch.mjs')).default,
/** @type {any} */ pt: (await import('./peertube.mjs')).default,
/** @type {any} */ od: (await import('./odysee.mjs')).default,
/** @type {any} */ ru: (await import('./rumble.mjs')).default,
};
for (const [pid, adapter] of Object.entries(providerRegistry)) {
const channelAdapter = channelRegistry[/** @type {ProviderId} */(pid)];
if (channelAdapter && typeof channelAdapter.fetchChannelById === 'function' && !adapter.fetchChannelById) {
adapter.fetchChannelById = channelAdapter.fetchChannelById;
}
}
/**
* Validate and normalize a comma separated providers list
* @param {string} input
* @returns {ProviderId[]}
*/
export function validateProviders(input) {
const raw = String(input || '').split(',').map(s => s.trim()).filter(Boolean);
const allowed = /** @type {ProviderId[]} */(['yt','dm','tw','pt','od','ru']);
const filtered = raw.filter(p => allowed.includes(/** @type {any} */(p)));
if (filtered.length === 0) return allowed;
return /** @type {ProviderId[]} */(filtered);
}