| const API_BASE = '/api' |
|
|
| |
| |
| |
| |
| |
| |
| export async function processVideo(urlOrFile, options = {}) { |
| const formData = new FormData() |
|
|
| if (typeof urlOrFile === 'string') { |
| formData.append('url', urlOrFile) |
| } else if (urlOrFile instanceof File) { |
| formData.append('file', urlOrFile) |
| } else { |
| throw new Error('urlOrFile must be a string (URL) or File object') |
| } |
|
|
| formData.append('num_clips', options.numClips || 5) |
| formData.append('language', options.language || 'en') |
| formData.append('aspect_ratio', options.aspectRatio || '9:16') |
| formData.append('rewrite_hooks', options.rewriteHooks ? 'true' : 'false') |
|
|
| |
| if (options.captionStyle) { |
| formData.append('caption_style', JSON.stringify(options.captionStyle)) |
| } |
| if (options.brandKit) { |
| formData.append('brand_kit', JSON.stringify(options.brandKit)) |
| } |
|
|
| |
| if (options.brandKit?.logoFile instanceof File) { |
| formData.append('logo_file', options.brandKit.logoFile) |
| } |
|
|
| |
| if (options.cookiesFile instanceof File) { |
| formData.append('cookies_file', options.cookiesFile) |
| } |
|
|
| const controller = new AbortController() |
| const timeout = setTimeout(() => controller.abort(), 300000) |
|
|
| try { |
| const response = await fetch(`${API_BASE}/process`, { |
| method: 'POST', |
| body: formData, |
| signal: controller.signal, |
| }) |
|
|
| if (!response.ok) { |
| const error = await response.json().catch(() => ({})) |
| throw new Error(error.detail || `HTTP ${response.status}`) |
| } |
|
|
| const data = await response.json() |
| |
| return { |
| jobId: data.job_id || data.jobId, |
| status: data.status || 'processing', |
| progress: data.progress || 0, |
| } |
| } finally { |
| clearTimeout(timeout) |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| export async function getJob(jobId) { |
| |
| const response = await fetch(`${API_BASE}/job/${jobId}`) |
| if (!response.ok) { |
| throw new Error(`Failed to fetch job: ${response.status}`) |
| } |
| return await response.json() |
| } |
|
|
| |
| |
| |
| |
| export async function getJobs() { |
| const response = await fetch(`${API_BASE}/jobs`) |
| if (!response.ok) { |
| throw new Error(`Failed to fetch jobs: ${response.status}`) |
| } |
| return await response.json() |
| } |
|
|
| |
| |
| |
| |
| |
| export async function deleteJob(jobId) { |
| const response = await fetch(`${API_BASE}/job/${jobId}`, { |
| method: 'DELETE', |
| }) |
| if (!response.ok) { |
| throw new Error(`Failed to delete job: ${response.status}`) |
| } |
| return await response.json() |
| } |
|
|
| |
| |
| |
| |
| |
| |
| export async function downloadClip(jobId, clipId) { |
| |
| const response = await fetch(`${API_BASE}/clips/${jobId}/${clipId}/download`, { |
| method: 'GET', |
| }) |
|
|
| if (!response.ok) { |
| throw new Error(`Failed to download clip: ${response.status}`) |
| } |
|
|
| return await response.blob() |
| } |
|
|
| |
| |
| |
| |
| |
| |
| export async function generateArticleReels(articleUrl, options = {}) { |
| const formData = new FormData() |
| formData.append('article_url', articleUrl) |
| formData.append('reel_type', options.reelType || 'Financial News Reel') |
| formData.append('num_points', options.numPoints || 7) |
| formData.append('tts_voice', options.ttsVoice || 'Guy - News Anchor (Male)') |
| formData.append('accent_hex', options.accentColor || '#E8A020') |
| formData.append('groq_api_key', options.groqApiKey || '') |
|
|
| const response = await fetch(`${API_BASE}/article-reels`, { |
| method: 'POST', |
| body: formData, |
| }) |
|
|
| if (!response.ok) { |
| const err = await response.json().catch(() => ({})) |
| throw new Error(err.detail || `HTTP ${response.status}`) |
| } |
|
|
| const data = await response.json() |
| return { |
| jobId: data.job_id || data.jobId, |
| status: data.status || 'processing', |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| export function createWebSocket(jobId, onMessage, onClose) { |
| const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:' |
| const wsUrl = `${protocol}//${window.location.host}/api/ws/${jobId}` |
|
|
| let ws = null |
| let reconnectAttempts = 0 |
| const maxReconnectAttempts = 5 |
| const reconnectDelay = 1000 |
|
|
| function connect() { |
| try { |
| ws = new WebSocket(wsUrl) |
|
|
| ws.onopen = () => { |
| reconnectAttempts = 0 |
| console.log('[WebSocket] Connected to', jobId) |
| } |
|
|
| ws.onmessage = (event) => { |
| try { |
| const data = JSON.parse(event.data) |
| if (onMessage) onMessage(data) |
| } catch (err) { |
| console.error('[WebSocket] Failed to parse message:', err) |
| } |
| } |
|
|
| ws.onerror = (error) => { |
| console.error('[WebSocket] Error:', error) |
| } |
|
|
| ws.onclose = () => { |
| console.log('[WebSocket] Connection closed') |
| if (reconnectAttempts < maxReconnectAttempts) { |
| reconnectAttempts++ |
| setTimeout(connect, reconnectDelay) |
| } else { |
| if (onClose) onClose() |
| } |
| } |
| } catch (err) { |
| console.error('[WebSocket] Connection failed:', err) |
| if (onClose) onClose() |
| } |
| } |
|
|
| connect() |
|
|
| return { |
| close: () => { |
| if (ws) ws.close() |
| }, |
| } |
| } |
|
|