CI / build-and-test (push) Successful in 13m34s
- Strategy Pattern front: ChannelProviderInterface + 6 providers (yt/dm/tw/pt/od/ru) + factory (ids longs/courts) - ChannelContentService: cache par onglet/tri/recherche (TTL 5min), pagination nextPage - ChannelPage: header (banniere/avatar/subs/description ...plus), tabs dynamiques par capabilities, grilles videos/shorts 9:16/playlists/live, recherche debounced, tri recent/populaire, infinite scroll IntersectionObserver, skeletons/empty/error - Backend: GET /api/channels/:provider/:id/content (videos/shorts/playlists/live) natif par provider
57 lines
2.1 KiB
JavaScript
57 lines
2.1 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
|
|
*/
|
|
|
|
/** @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 {(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);
|
|
}
|