import { Injectable } from '@angular/core'; import { HttpClient, HttpParams } from '@angular/common/http'; import { Observable } from 'rxjs'; export interface DownloadFormat { id: string; resolution?: string; fps?: number; ext?: string; vcodec?: string; acodec?: string; sizeEstimate?: number; label: string; } export type DownloadState = 'queued' | 'running' | 'merging' | 'completed' | 'failed'; export interface DownloadJob { id: string; userId: string; provider: string; videoId: string; state: DownloadState; progress: number; createdAt: string; updatedAt: string; filePath?: string | null; fileExt?: string | null; fileSize?: number | null; error?: string | null; url?: string; } @Injectable({ providedIn: 'root' }) export class DownloadService { constructor(private http: HttpClient) {} listFormats( provider: string, videoId: string, opts?: { instance?: string; slug?: string; sourceUrl?: string } ): Observable<{ url: string; formats: DownloadFormat[]; title: string; duration: number }> { let params = new HttpParams(); if (opts?.instance) params = params.set('instance', opts.instance); if (opts?.slug) params = params.set('slug', opts.slug); if (opts?.sourceUrl) params = params.set('sourceUrl', opts.sourceUrl); return this.http.get<{ url: string; formats: DownloadFormat[]; title: string; duration: number }>( `/proxy/api/download/${encodeURIComponent(provider)}/${encodeURIComponent(videoId)}/formats`, { params, withCredentials: true } ); } startDownload( provider: string, videoId: string, body?: { formatId?: string; audioOnly?: boolean; sourceUrl?: string }, opts?: { instance?: string; slug?: string } ): Observable<{ jobId: string }> { let params = new HttpParams(); if (opts?.instance) params = params.set('instance', opts.instance); if (opts?.slug) params = params.set('slug', opts.slug); return this.http.post<{ jobId: string }>( `/proxy/api/download/${encodeURIComponent(provider)}/${encodeURIComponent(videoId)}`, body || {}, { params, withCredentials: true } ); } getJob(id: string): Observable { return this.http.get(`/proxy/api/download/jobs/${encodeURIComponent(id)}`, { withCredentials: true }); } cancelJob(id: string): Observable { return this.http.delete(`/proxy/api/download/jobs/${encodeURIComponent(id)}`, { withCredentials: true }); } fileUrl(id: string): string { return `/proxy/api/download/jobs/${encodeURIComponent(id)}/file`; } }