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 { serverRefValue } from '../../shared/providers/channel-ref'; // TODO: Make SearchResult generic export class DmAdapter implements ProviderAdapter { key = 'dm' as const; label = 'Dailymotion'; constructor(private http: HttpClient) {} async search(params: ProviderSearchParams, _signal: AbortSignal): Promise> { let httpParams = new HttpParams().set('q', params.q).set('providers', 'dm'); 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, 'dm'); const items: VideoItem[] = rawItems.map((it: any) => ({ // Phase 7.3 — provenance (instant de collecte + voie) : propriété de la // donnée, pas d'un provider — mapping commun aux 6 adaptateurs. ...mapProvenance(it), id: it.id, provider: 'dailymotion', title: it.title, channelName: it.uploaderName, durationSec: it.duration, thumbnailUrl: it.thumbnail, viewCount: undefined, // Dailymotion API does not provide view count in search results publishedAt: undefined, // Dailymotion API does not provide published date in search results channelExternalId: serverRefValue(it.channelRef, 'dm') || it.channelId || undefined, channelAvatarUrl: it.uploaderAvatar || undefined, // Jouabilité : `allow_embed=false` (propriétaire) => le player répond // "Forbidden". Propagé tel quel : `accepts()` l'exclut via le garde // `raw.allowEmbed` (même garde que le chemin historique direct). // Champ absent = on ne sait pas (jamais de `false` inventé). embeddable: typeof it.embeddable === 'boolean' ? it.embeddable : 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 }; } }