import React, { useState, useEffect, useRef } from 'react'; import { Container, Paper, TextField, Button, Select, MenuItem, FormControl, InputLabel, Typography, Box, Grid, Card, CardContent, LinearProgress, Chip, IconButton, Alert, Snackbar, Dialog, DialogTitle, DialogContent, DialogActions, List, ListItem, ListItemText, ListItemSecondaryAction, Tabs, Tab, Switch, FormControlLabel } from '@mui/material'; import { CloudUpload, Cancel, CheckCircle, Error, Refresh, Settings, Visibility, VisibilityOff } from '@mui/icons-material'; import axios from 'axios'; import io from 'socket.io-client'; import { v4 as uuidv4 } from 'uuid'; const API_URL = process.env.REACT_APP_API_URL || ''; const SOCKET_URL = process.env.REACT_APP_SOCKET_URL || ''; function App() { const [vodUrl, setVodUrl] = useState(''); const [selectedProvider, setSelectedProvider] = useState('mega'); const [selectedFormat, setSelectedFormat] = useState('best'); const [providers, setProviders] = useState([]); const [formats, setFormats] = useState([]); const [credentials, setCredentials] = useState({ mega: { email: '', password: '' }, filen: { email: '', password: '' }, drime: { email: '', password: '' } }); const [tasks, setTasks] = useState([]); const [activeTab, setActiveTab] = useState(0); const [showPassword, setShowPassword] = useState(false); const [alert, setAlert] = useState({ open: false, message: '', severity: 'info' }); const [loading, setLoading] = useState(false); const [autoScroll, setAutoScroll] = useState(true); const socketRef = useRef(null); const userIdRef = useRef(localStorage.getItem('userId') || uuidv4()); useEffect(() => { // Save user ID localStorage.setItem('userId', userIdRef.current); // Load providers loadProviders(); // Load tasks loadTasks(); // Initialize socket connection socketRef.current = io(SOCKET_URL); socketRef.current.on('connect', () => { console.log('Connected to server'); }); socketRef.current.on('task_update', (data) => { updateTask(data.task_id, data); }); return () => { if (socketRef.current) { socketRef.current.disconnect(); } }; }, []); const loadProviders = async () => { try { const response = await axios.get(`${API_URL}/api/providers`); setProviders(response.data.providers); } catch (error) { showAlert('Failed to load providers', 'error'); } }; const loadTasks = async () => { try { const response = await axios.get(`${API_URL}/api/tasks`, { headers: { 'X-User-ID': userIdRef.current } }); setTasks(response.data.tasks); // Subscribe to active tasks response.data.tasks.forEach(task => { if (task.status === 'processing' || task.status === 'queued') { socketRef.current.emit('subscribe_task', { task_id: task.id }); } }); } catch (error) { showAlert('Failed to load tasks', 'error'); } }; const loadFormats = async () => { if (!vodUrl) return; setLoading(true); try { const response = await axios.get(`${API_URL}/api/formats/${encodeURIComponent(vodUrl)}`); setFormats(response.data.formats); showAlert(`Found ${response.data.formats.length} quality options`, 'success'); } catch (error) { showAlert('Failed to load formats', 'error'); } finally { setLoading(false); } }; const validateCredentials = async () => { const creds = credentials[selectedProvider]; if (!creds.email || !creds.password) { showAlert('Please enter credentials', 'warning'); return; } setLoading(true); try { const response = await axios.post(`${API_URL}/api/validate-credentials`, { provider: selectedProvider, credentials: creds }); showAlert(response.data.message, response.data.valid ? 'success' : 'error'); } catch (error) { showAlert('Validation failed', 'error'); } finally { setLoading(false); } }; const startArchiving = async () => { const creds = credentials[selectedProvider]; if (!vodUrl || !creds.email || !creds.password) { showAlert('Please fill all required fields', 'warning'); return; } setLoading(true); try { const response = await axios.post(`${API_URL}/api/tasks`, { vod_url: vodUrl, provider: selectedProvider, format_id: selectedFormat, credentials: creds }, { headers: { 'X-User-ID': userIdRef.current } }); const newTask = { id: response.data.task_id, vod_url: vodUrl, provider: selectedProvider, status: 'queued', progress_data: { phase: 'queued', percent: 0 }, created_at: new Date().toISOString() }; setTasks([newTask, ...tasks]); // Subscribe to task updates socketRef.current.emit('subscribe_task', { task_id: response.data.task_id }); showAlert('Task started successfully', 'success'); // Clear form setVodUrl(''); setSelectedFormat('best'); } catch (error) { showAlert('Failed to start task', 'error'); } finally { setLoading(false); } }; const cancelTask = async (taskId) => { try { await axios.delete(`${API_URL}/api/tasks/${taskId}`); updateTask(taskId, { status: 'cancelled' }); showAlert('Task cancelled', 'info'); } catch (error) { showAlert('Failed to cancel task', 'error'); } }; const updateTask = (taskId, updates) => { setTasks(prevTasks => prevTasks.map(task => task.id === taskId ? { ...task, ...updates } : task ) ); }; const showAlert = (message, severity = 'info') => { setAlert({ open: true, message, severity }); }; const getStatusColor = (status) => { switch (status) { case 'completed': return 'success'; case 'failed': return 'error'; case 'processing': return 'primary'; case 'cancelled': return 'default'; default: return 'default'; } }; const getStatusIcon = (status) => { switch (status) { case 'completed': return ; case 'failed': return ; case 'cancelled': return ; default: return null; } }; return ( VOD Archiver Pro Download and archive Twitch VODs to multiple cloud providers setActiveTab(v)}> {activeTab === 0 && ( setVodUrl(e.target.value)} placeholder="https://www.twitch.tv/videos/123456789" InputProps={{ endAdornment: ( ) }} /> Provider Quality setCredentials({ ...credentials, [selectedProvider]: { ...credentials[selectedProvider], email: e.target.value } })} /> setCredentials({ ...credentials, [selectedProvider]: { ...credentials[selectedProvider], password: e.target.value } })} InputProps={{ endAdornment: ( setShowPassword(!showPassword)}> {showPassword ? : } ) }} /> )} {(activeTab === 1 || activeTab === 2) && ( setAutoScroll(e.target.checked)} />} label="Auto-scroll logs" /> {tasks .filter(task => activeTab === 1 ? ['queued', 'processing'].includes(task.status) : ['completed', 'failed', 'cancelled'].includes(task.status) ) .map(task => ( {task.vod_url} {new Date(task.created_at).toLocaleString()} {task.status === 'processing' && task.progress_data && ( {task.progress_data.phase === 'downloading' ? 'Downloading' : 'Uploading'} {Math.round(task.progress_data.percent)}% )} {task.error && ( {task.error} )} {task.status === 'processing' && ( )} ))} )} setAlert({ ...alert, open: false })} > setAlert({ ...alert, open: false })}> {alert.message} ); } export default App;