import { HttpClient, HttpParams } from '@angular/common/http'; import { firstValueFrom } from 'rxjs'; import type { ProviderAdapter, ProviderSearchParams, SearchResult } from '../models'; import { VideoItem } from 'src/app/shared/models/video-item.model'; import { withFilterParams } from './filter-params'; import { readSearchGroup } from '../search-contract'; import { mapProvenance } from './provenance'; import { buildPeerTubeComposite, serverRefValue } from '../../shared/providers/channel-ref'; export class PtAdapter implements ProviderAdapter { key = 'pt' as const; label = 'PeerTube'; constructor(private http: HttpClient) {} async search(params: ProviderSearchParams, _signal: AbortSignal): Promise> { let httpParams = new HttpParams().set('q', params.q).set('providers', 'pt'); if (params.pageToken) httpParams = httpParams.set('page', params.pageToken); if (params.sort) httpParams = httpParams.set('sort', params.sort); // Filtres (type / durée / période) : appliqués côté serveur quand le provider // le sait, affinés en post-traitement sinon. httpParams = withFilterParams(httpParams, params.filters); const res = await firstValueFrom(this.http.get(`/api/search`, { params: httpParams })); const { items: rawItems, providerError } = readSearchGroup(res, 'pt'); const items: VideoItem[] = rawItems.map((it: any) => { // PeerTube identifie une chaîne par `instance|channel` : l'instance est // dérivée de l'URL vidéo, le nom de chaîne de `channelId`. // Phase 6 : si le serveur a envoyé `channelRef`, sa valeur canonique // prime — sinon on reconstruit avec la règle historique. let channelExternalId: string | undefined = serverRefValue(it?.channelRef, 'pt') ?? buildPeerTubeComposite(it?.url, it?.channelId); return { // Phase 7.3 — provenance (instant de collecte + voie), commune aux 6. ...mapProvenance(it), id: it.id, provider: 'peertube', title: it.title, channelName: it.uploaderName, durationSec: it.duration, thumbnailUrl: it.thumbnail, // `viewCount: undefined` en dur alors que le serveur envoie déjà le // nombre de vues : les compteurs PeerTube n'arrivaient jamais dans l'UI // (trouvé en gelant la sortie réelle, phase 8.4). On lit le champ // canonique `views` et on garde `viewCount` en repli. viewCount: typeof it.views === 'number' ? it.views : (typeof it.viewCount === 'number' ? it.viewCount : undefined), // Le serveur envoie `publishedAt` (ISO) quand l'instance l'expose. publishedAt: typeof it.publishedAt === 'string' ? it.publishedAt : undefined, channelExternalId, // URL canonnique : instance d'origine (vidéos fédérées). Sans elle, // /shorts reconstruit l'embed sur l'instance ACTIVE → « Sorry this // video does not exist ». url: typeof it.url === 'string' ? it.url : undefined, // Signaux de type pour le raffinement client des filtres. type: typeof it.type === 'string' ? it.type : undefined, isShort: it.isShort === true ? true : undefined, // Dimensions (signal d'orientation pour la détection Short). width: typeof it.width === 'number' ? it.width : undefined, height: typeof it.height === 'number' ? it.height : undefined, } as VideoItem; }); return { items, total: rawItems.length, providerError }; } }