Spaces:
Paused
Paused
| import os | |
| import json | |
| import time | |
| import sqlite3 | |
| import asyncio | |
| import threading | |
| import subprocess | |
| from datetime import datetime | |
| from flask import Flask, request, jsonify, send_from_directory | |
| from flask_socketio import SocketIO, emit | |
| from flask_cors import CORS | |
| from werkzeug.security import generate_password_hash | |
| import yt_dlp | |
| from providers import MegaProvider, FilenProvider, DrimeProvider | |
| from task_manager import TaskManager | |
| app = Flask(__name__, static_folder='frontend/build', static_url_path='') | |
| CORS(app) | |
| socketio = SocketIO(app, cors_allowed_origins="*", async_mode='eventlet') | |
| # Initialize task manager | |
| task_manager = TaskManager(socketio) | |
| # Database setup | |
| def init_db(): | |
| conn = sqlite3.connect('/tmp/vod-archiver/tasks.db') | |
| c = conn.cursor() | |
| c.execute(''' | |
| CREATE TABLE IF NOT EXISTS tasks ( | |
| id TEXT PRIMARY KEY, | |
| user_id TEXT, | |
| vod_url TEXT, | |
| provider TEXT, | |
| format_id TEXT, | |
| status TEXT, | |
| progress_data TEXT, | |
| created_at TIMESTAMP, | |
| updated_at TIMESTAMP, | |
| error TEXT, | |
| file_info TEXT | |
| ) | |
| ''') | |
| conn.commit() | |
| conn.close() | |
| init_db() | |
| def index(): | |
| return send_from_directory(app.static_folder, 'index.html') | |
| def get_providers(): | |
| """Get list of available providers""" | |
| return jsonify({ | |
| 'providers': [ | |
| {'id': 'mega', 'name': 'Mega.nz', 'maxSize': '20GB', 'features': ['streaming', 'encryption']}, | |
| {'id': 'filen', 'name': 'Filen.io', 'maxSize': '20GB', 'features': ['encryption', 'privacy']}, | |
| {'id': 'drime', 'name': 'Drime.cloud', 'maxSize': '30GB', 'features': ['streaming', 'sharing']} | |
| ] | |
| }) | |
| def get_formats(vod_url): | |
| """Get available formats for VOD""" | |
| try: | |
| ydl_opts = { | |
| 'quiet': True, | |
| 'no_warnings': True, | |
| 'extract_flat': False | |
| } | |
| with yt_dlp.YoutubeDL(ydl_opts) as ydl: | |
| info = ydl.extract_info(vod_url, download=False) | |
| formats = [] | |
| if 'formats' in info: | |
| # Group formats by quality | |
| quality_map = {} | |
| for f in info['formats']: | |
| if f.get('height'): | |
| quality = f"{f['height']}p" | |
| fps = f.get('fps', 30) | |
| quality_key = f"{quality}{fps}" | |
| if quality_key not in quality_map or (f.get('filesize', 0) > quality_map[quality_key].get('filesize', 0)): | |
| quality_map[quality_key] = f | |
| # Convert to list | |
| for key, f in quality_map.items(): | |
| formats.append({ | |
| 'format_id': f['format_id'], | |
| 'quality': f"{f.get('height', '?')}p", | |
| 'fps': f.get('fps', 30), | |
| 'ext': f.get('ext', 'mp4'), | |
| 'filesize': f.get('filesize', 0), | |
| 'label': f"{f.get('height', '?')}p{f.get('fps', '')}fps ({f.get('filesize', 0) / 1024 / 1024:.1f}MB)" if f.get('filesize') else f"{f.get('height', '?')}p{f.get('fps', '')}fps" | |
| }) | |
| # Always add best/worst | |
| formats.insert(0, {'format_id': 'best', 'label': 'Best Quality (Source)'}) | |
| formats.append({'format_id': 'worst', 'label': 'Lowest Quality (Smallest)'}) | |
| return jsonify({ | |
| 'formats': formats, | |
| 'title': info.get('title', 'Unknown'), | |
| 'duration': info.get('duration', 0), | |
| 'thumbnail': info.get('thumbnail', '') | |
| }) | |
| except Exception as e: | |
| return jsonify({'error': str(e)}), 500 | |
| def validate_credentials(): | |
| """Validate provider credentials""" | |
| data = request.json | |
| provider_id = data.get('provider') | |
| credentials = data.get('credentials', {}) | |
| try: | |
| if provider_id == 'mega': | |
| provider = MegaProvider() | |
| valid = provider.validate_credentials( | |
| credentials.get('email'), | |
| credentials.get('password') | |
| ) | |
| elif provider_id == 'filen': | |
| provider = FilenProvider() | |
| valid = provider.validate_credentials( | |
| credentials.get('email'), | |
| credentials.get('password') | |
| ) | |
| elif provider_id == 'drime': | |
| provider = DrimeProvider() | |
| valid = provider.validate_credentials( | |
| credentials.get('email'), | |
| credentials.get('password') | |
| ) | |
| else: | |
| return jsonify({'valid': False, 'message': 'Invalid provider'}), 400 | |
| return jsonify({'valid': valid, 'message': 'Valid' if valid else 'Invalid credentials'}) | |
| except Exception as e: | |
| return jsonify({'valid': False, 'message': str(e)}), 500 | |
| def get_tasks(): | |
| """Get all tasks for current session""" | |
| user_id = request.headers.get('X-User-ID', 'default') | |
| conn = sqlite3.connect('/tmp/vod-archiver/tasks.db') | |
| conn.row_factory = sqlite3.Row | |
| c = conn.cursor() | |
| tasks = c.execute( | |
| 'SELECT * FROM tasks WHERE user_id = ? ORDER BY created_at DESC', | |
| (user_id,) | |
| ).fetchall() | |
| conn.close() | |
| return jsonify({ | |
| 'tasks': [dict(task) for task in tasks] | |
| }) | |
| def create_task(): | |
| """Create a new download task""" | |
| data = request.json | |
| user_id = request.headers.get('X-User-ID', 'default') | |
| task_id = generate_password_hash(f"{user_id}{time.time()}")[:16] | |
| # Store task in database | |
| conn = sqlite3.connect('/tmp/vod-archiver/tasks.db') | |
| c = conn.cursor() | |
| c.execute(''' | |
| INSERT INTO tasks (id, user_id, vod_url, provider, format_id, status, progress_data, created_at, updated_at) | |
| VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) | |
| ''', ( | |
| task_id, | |
| user_id, | |
| data['vod_url'], | |
| data['provider'], | |
| data.get('format_id', 'best'), | |
| 'queued', | |
| json.dumps({'phase': 'queued', 'percent': 0}), | |
| datetime.now(), | |
| datetime.now() | |
| )) | |
| conn.commit() | |
| conn.close() | |
| # Start task in background | |
| task_manager.start_task(task_id, data) | |
| return jsonify({ | |
| 'task_id': task_id, | |
| 'status': 'queued' | |
| }) | |
| def cancel_task(task_id): | |
| """Cancel a running task""" | |
| task_manager.cancel_task(task_id) | |
| conn = sqlite3.connect('/tmp/vod-archiver/tasks.db') | |
| c = conn.cursor() | |
| c.execute('UPDATE tasks SET status = ? WHERE id = ?', ('cancelled', task_id)) | |
| conn.commit() | |
| conn.close() | |
| return jsonify({'status': 'cancelled'}) | |
| def handle_connect(): | |
| """Handle client connection""" | |
| emit('connected', {'data': 'Connected to server'}) | |
| def handle_subscribe(data): | |
| """Subscribe to task updates""" | |
| task_id = data.get('task_id') | |
| if task_id: | |
| # Join room for this task | |
| from flask_socketio import join_room | |
| join_room(task_id) | |
| emit('subscribed', {'task_id': task_id}) | |
| def handle_unsubscribe(data): | |
| """Unsubscribe from task updates""" | |
| task_id = data.get('task_id') | |
| if task_id: | |
| from flask_socketio import leave_room | |
| leave_room(task_id) | |
| if __name__ == '__main__': | |
| socketio.run(app, host='0.0.0.0', port=7860, debug=False) |