"""Queue management API endpoints.""" import logging from flask import Blueprint, request from utils import make_error_response, make_response logger = logging.getLogger(__name__) queue_bp = Blueprint('queue', __name__, url_prefix='/api') @queue_bp.route('/queue', methods=['GET']) def get_queue(): """Get all queue items.""" from app import queue_store try: items = queue_store.get_all() items_data = [item.to_dict() for item in items] stats = queue_store.get_stats() return make_response({ "queue": items_data, "total": stats["total"], "pendingCount": stats["pending"], "downloadingCount": stats["downloading"], }) except Exception as e: return make_error_response(f"Failed to get queue: {str(e)}", 500) @queue_bp.route('/queue', methods=['POST']) def add_to_queue(): """Add a video to the download queue.""" from app import download_engine try: data = request.get_json() if not data: return make_error_response("Request body is required", 400) url = data.get('url', '').strip() title = data.get('title', 'Unknown Title') video_id = data.get('videoId', '') thumbnail = data.get('thumbnail', '') category = data.get('category', '') network_folder = data.get('network_folder') quality = data.get('quality') if not url: return make_error_response("Video URL is required", 400) logger.info(f"Queue add request: title={title}, videoId={video_id}, url={url}, category={category}") # Generate queue ID import uuid queue_id = str(uuid.uuid4()) # Create queue item from models import QueueItem queue_item = QueueItem( id=queue_id, video_id=video_id, title=title, url=url, thumbnail=thumbnail, category=category, network_folder=network_folder, quality=quality, ) # Enqueue item (will be processed in order by queue processor) download_engine.enqueue_download(queue_item) logger.info(f"Queue added successfully: queueId={queue_id}, title={title}") return make_response(queue_item.to_dict()), 202 except Exception as e: logger.error(f"Failed to add to queue: title={title}, videoId={video_id}, error={str(e)}") return make_error_response(f"Failed to add to queue: {str(e)}", 500) @queue_bp.route('/queue', methods=['DELETE']) def clear_queue(): """Clear the entire queue.""" from app import queue_store, socketio try: count = queue_store.clear_all() socketio.emit("queue:cleared") return make_response({ "message": "Queue cleared", "count": count }) except Exception as e: return make_error_response(f"Failed to clear queue: {str(e)}", 500) @queue_bp.route('/queue/', methods=['GET']) def get_queue_item(queue_id): """Get a specific queue item.""" from app import queue_store try: item = queue_store.get_item(queue_id) if not item: return make_error_response(f"Queue item {queue_id} not found", 404) return make_response(item.to_dict()) except Exception as e: return make_error_response(f"Failed to get queue item: {str(e)}", 500) @queue_bp.route('/queue/', methods=['DELETE']) def remove_from_queue(queue_id): """Remove an item from the queue.""" from app import queue_store, socketio try: removed = queue_store.remove_item(queue_id) if not removed: return make_error_response(f"Queue item {queue_id} not found", 404) socketio.emit("queue:removed", {"queueId": queue_id}) return make_response({"message": f"Item {queue_id} removed from queue"}) except Exception as e: return make_error_response(f"Failed to remove from queue: {str(e)}", 500) @queue_bp.route('/queue/', methods=['PUT']) def update_queue_item(queue_id): """Update a queue item's fields.""" from app import queue_store try: data = request.get_json() if not data: return make_error_response("Request body is required", 400) item = queue_store.update_item(queue_id, data) if not item: return make_error_response(f"Queue item {queue_id} not found", 404) return make_response(item.to_dict()) except Exception as e: return make_error_response(f"Failed to update queue item: {str(e)}", 500) @queue_bp.route('/queue//retry', methods=['POST']) def retry_download(queue_id): """Retry a failed download.""" from app import download_engine, queue_store, yt_cli try: item = queue_store.get_item(queue_id) if not item: return make_error_response(f"Queue item {queue_id} not found", 404) # Reset status queue_store.update_status(queue_id, "pending") queue_store.update_progress(queue_id, 0) # Re-download based on type config = yt_cli.config if item.item_type == "playlist": download_engine.download_playlist( queue_id=queue_id, url=item.url, config=config, category=item.category, network_folder=item.network_folder, quality=item.quality, ) else: download_engine.download_video( queue_id=queue_id, url=item.url, config=config, category=item.category, network_folder=item.network_folder, quality=item.quality, ) return make_response({ "queueId": queue_id, "status": "downloading", "message": "Download retry started" }) except Exception as e: return make_error_response(f"Retry failed: {str(e)}", 500) @queue_bp.route('/queue//cancel', methods=['POST']) def cancel_download(queue_id): """Cancel a download.""" from app import download_engine, queue_store try: item = queue_store.get_item(queue_id) if not item: return make_error_response(f"Queue item {queue_id} not found", 404) if item.status in ("completed", "failed"): return make_error_response(f"Cannot cancel {item.status} download", 400) # Try to cancel active download download_engine.cancel_download(queue_id) return make_response({ "queueId": queue_id, "status": "cancelled", "message": "Download cancelled" }) except Exception as e: return make_error_response(f"Failed to cancel download: {str(e)}", 500) @queue_bp.route('/queue//status', methods=['GET']) def get_queue_status(queue_id): """Get the status of a queue item.""" from app import queue_store try: item = queue_store.get_item(queue_id) if not item: return make_error_response(f"Queue item {queue_id} not found", 404) return make_response({ "queueId": queue_id, "status": item.status, "progress": item.progress, "speed": item.speed, "eta": item.eta, }) except Exception as e: return make_error_response(f"Failed to get queue status: {str(e)}", 500) @queue_bp.route('/queue//move', methods=['POST']) def move_queue_item(queue_id): """Reorder a queue item.""" from app import queue_store try: data = request.get_json() direction = data.get('direction', '').strip() if data else '' if direction not in ('up', 'down'): return make_error_response("Direction must be 'up' or 'down'", 400) moved = queue_store.reorder_item(queue_id, direction) if not moved: return make_error_response(f"Could not move item {queue_id} {direction}", 400) return make_response({"message": f"Item moved {direction}"}) except Exception as e: return make_error_response(f"Failed to move queue item: {str(e)}", 500) @queue_bp.route('/queue/clear/completed', methods=['POST']) def clear_completed(): """Clear completed items from the queue.""" from app import queue_store try: count = queue_store.clear_completed() return make_response({ "cleared": count, "count": count }) except Exception as e: return make_error_response(f"Failed to clear completed items: {str(e)}", 500) @queue_bp.route('/queue/clear/failed', methods=['POST']) def clear_failed(): """Clear failed items from the queue.""" from app import queue_store try: count = queue_store.clear_failed() return make_response({ "cleared": count, "count": count }) except Exception as e: return make_error_response(f"Failed to clear failed items: {str(e)}", 500) @queue_bp.route('/queue/stats', methods=['GET']) def get_queue_stats(): """Get queue statistics.""" from app import queue_store try: stats = queue_store.get_stats() return make_response(stats) except Exception as e: return make_error_response(f"Failed to get queue stats: {str(e)}", 500)