Files
NewTube/src/app/shared/models/channel-detail.model.ts
T
bruno 97e5de74f5
CI / build-and-test (push) Successful in 13m34s
feat(channel): page details chaine multi-providers avec onglets et infinite scroll
- 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
2026-09-25 10:15:12 -04:00

65 lines
2.2 KiB
TypeScript

import type { ChannelMeta, ProviderId } from './channel.model';
import type { VideoItem } from './video-item.model';
export type ChannelTabId = 'videos' | 'shorts' | 'playlists' | 'live' | 'about';
export type ChannelSort = 'recent' | 'popular';
export type ChannelContentType = 'videos' | 'shorts' | 'playlists' | 'live';
export interface ChannelCapabilities {
videos: boolean;
shorts: boolean;
playlists: boolean;
live: boolean;
about: boolean;
searchInChannel: boolean;
}
export interface ChannelDetail extends ChannelMeta {
bannerUrl?: string | null;
description?: string | null;
videoCount?: number | null;
viewCount?: number | null;
createdAt?: string | null;
links?: { label: string; url: string }[];
capabilities: ChannelCapabilities;
}
export interface ChannelContentPage<T> {
items: T[];
nextPage: number | null;
total?: number | null;
}
export interface ChannelPlaylist {
id: string;
provider: ProviderId;
title: string;
thumbnailUrl?: string | null;
videoCount?: number | null;
updatedAt?: string | null;
}
export const DEFAULT_CAPABILITIES: Record<ProviderId, ChannelCapabilities> = {
yt: { videos: true, shorts: true, playlists: true, live: true, about: true, searchInChannel: true },
dm: { videos: true, shorts: false, playlists: true, live: false, about: true, searchInChannel: true },
tw: { videos: true, shorts: false, playlists: false, live: true, about: true, searchInChannel: true },
pt: { videos: true, shorts: false, playlists: true, live: false, about: true, searchInChannel: true },
od: { videos: true, shorts: false, playlists: false, live: false, about: true, searchInChannel: true },
ru: { videos: true, shorts: false, playlists: false, live: false, about: true, searchInChannel: true },
};
export function buildChannelDetail(meta: ChannelMeta, extra?: Partial<ChannelDetail>): ChannelDetail {
return {
...meta,
bannerUrl: extra?.bannerUrl ?? null,
description: extra?.description ?? null,
videoCount: extra?.videoCount ?? null,
viewCount: extra?.viewCount ?? null,
createdAt: extra?.createdAt ?? null,
links: extra?.links ?? [],
capabilities: extra?.capabilities ?? DEFAULT_CAPABILITIES[meta.provider],
};
}
export type { VideoItem };