youtube-web/server/routes/download.py

215 lines
7.0 KiB
Python

"""Download-related API endpoints."""
import uuid
from flask import Blueprint, request
from models import QueueItem
from utils import make_error_response, make_response
download_bp = Blueprint('download', __name__, url_prefix='/api')
@download_bp.route('/download', methods=['POST'])
def download_video():
"""Queue a video for download or start an existing queue item."""
from app import download_engine, queue_store, yt_cli
try:
data = request.get_json()
url = data.get('url', '').strip() if data else ''
category = data.get('category') if data else None
network_folder = data.get('network_folder') if data else None
quality = data.get('quality') if data else None
queue_id = data.get('queueId') if data else None
# If queueId provided, use existing queue item
if queue_id:
item = queue_store.get_item(queue_id)
if not item:
return make_error_response("Queue item not found", 404)
if item.status != "pending":
return make_error_response(f"Cannot download item with status '{item.status}'", 400)
# Check if already downloaded
if yt_cli.is_video_downloaded(item.video_id):
return make_error_response(f"Video {item.video_id} has already been downloaded", 409)
# Enqueue for sequential processing
download_engine.enqueue_download(item)
return make_response({
"queueId": queue_id,
"status": "pending",
"message": "Added to download queue",
}), 202
# No queueId — create new queue item
if not url:
return make_error_response("Video URL is required", 400)
# Extract video ID from URL
import re
video_id = None
id_match = re.search(r"(?:v=|\/)([0-9A-Za-z_-]{11})", url)
if id_match:
video_id = id_match.group(1)
# Check if already downloaded
if video_id and yt_cli.is_video_downloaded(video_id):
return make_error_response(f"Video {video_id} has already been downloaded", 409)
# Generate queue ID
queue_id = str(uuid.uuid4())
# Get video info for title/thumbnail
title = "Unknown Title"
thumbnail = ""
try:
import yt_dlp
ydl_opts = {
'dump_single_json': True,
'no_warnings': True,
'quiet': True,
'no_progress': True,
'write_thumbnail': False,
}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(url, download=False)
if info:
title = info.get("title", "Unknown Title")
thumbnail = info.get("thumbnail", "")
except Exception:
pass
# Create queue item
queue_item = QueueItem(
id=queue_id,
video_id=video_id or "",
title=title,
url=url,
thumbnail=thumbnail,
category=category or "",
network_folder=network_folder,
quality=quality,
)
# Enqueue for sequential processing
download_engine.enqueue_download(queue_item)
return make_response({
"queueId": queue_id,
"status": "pending",
"message": "Added to download queue",
}), 202
except Exception as e:
return make_error_response(f"Failed to queue download: {str(e)}", 500)
@download_bp.route('/download/playlist', methods=['POST'])
def download_playlist():
"""Queue a playlist for download."""
from app import download_engine
try:
data = request.get_json()
url = data.get('url', '').strip() if data else ''
category = data.get('category') if data else None
network_folder = data.get('network_folder') if data else None
quality = data.get('quality') if data else None
if not url:
return make_error_response("Playlist URL is required", 400)
# Generate queue ID
queue_id = str(uuid.uuid4())
# Get playlist title
title = "Unknown Playlist"
try:
import yt_dlp
ydl_opts = {
'flat_playlist': True,
'dump_single_json': True,
'no_warnings': True,
'quiet': True,
'no_progress': True,
}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(url, download=False)
if info:
title = info.get("title", "Unknown Playlist")
except Exception:
pass
# Create queue item
queue_item = QueueItem(
id=queue_id,
video_id="",
title=title,
url=url,
category=category or "",
network_folder=network_folder,
item_type="playlist",
quality=quality,
)
# Enqueue for sequential processing
download_engine.enqueue_download(queue_item)
return make_response({
"queueId": queue_id,
"status": "pending",
"message": "Added to download queue",
}), 202
except Exception as e:
return make_error_response(f"Failed to queue playlist: {str(e)}", 500)
@download_bp.route('/download/direct', methods=['POST'])
def download_video_direct():
"""Download a video directly (legacy endpoint, synchronous)."""
from app import download_engine, yt_cli
try:
data = request.get_json()
url = data.get('url', '').strip() if data else ''
category = data.get('category') if data else None
network_folder = data.get('network_folder') if data else None
quality = data.get('quality') if data else None
if not url:
return make_error_response("Video URL is required", 400)
# Extract video ID from URL
import re
video_id = None
id_match = re.search(r"(?:v=|\/)([0-9A-Za-z_-]{11})", url)
if id_match:
video_id = id_match.group(1)
# Check if already downloaded
if video_id and yt_cli.is_video_downloaded(video_id):
return make_error_response(f"Video {video_id} has already been downloaded", 409)
# Generate queue ID
queue_id = str(uuid.uuid4())
# Create queue item
queue_item = QueueItem(
id=queue_id,
video_id=video_id or "",
title="Unknown Title",
url=url,
category=category or "",
network_folder=network_folder,
quality=quality,
)
# Enqueue for sequential processing
download_engine.enqueue_download(queue_item)
return make_response({
"queueId": queue_id,
"status": "pending",
"message": "Added to download queue",
}), 202
except Exception as e:
return make_error_response(f"Failed to queue direct download: {str(e)}", 500)