update add_video route and change queue method
All checks were successful
Build Docker Image / build (push) Successful in 14m39s

- change add to queue method from `GET` on `/` to `POST` on `/queue`
- update `/add_video` route to accept single video url
This commit is contained in:
2025-02-25 02:02:54 -08:00
parent b7aa64935f
commit ce605006f5
5 changed files with 219 additions and 27 deletions

View File

@@ -1,12 +1,18 @@
"""Views for the Flask app."""
from flask import Blueprint, current_app, g, jsonify, request
import re
from urllib.parse import urlparse
from flask import Blueprint, abort, current_app, g, jsonify, request
from sqlalchemy import inspect
from sqlalchemy.exc import SQLAlchemyError
from yt_dlp import YoutubeDL
from app.database import get_db_session
from app.models import SavedQueue, WatchHistory
from app.mpv import send_to_mpv
from app.utils import (fetch_video_info, is_valid_url, sanitize_video_data,
validate_video_data)
bp = Blueprint("views", __name__)
@@ -59,35 +65,66 @@ def load_queue():
@bp.route("/add_video", methods=["POST"])
def add_video():
data = request.get_json()
if not data or "video_url" not in data:
current_app.logger.error("Missing video_url field")
return jsonify(message="Missing video_url field"), 400
# Validate video URL format and allowed domains
if not is_valid_url(
data.get("video_url"), allowed_domains=["youtube.com", "youtu.be"]
):
current_app.logger.error("Invalid video URL format or domain")
return jsonify(message="Invalid video URL"), 400
# Check if we only have video_url
if all(key == "video_url" for key in data.keys()):
current_app.logger.info(
"Only video_url provided. Fetching additional information..."
)
video_info = fetch_video_info(data["video_url"])
if not video_info:
return jsonify(message="Failed to fetch video information"), 400
# Replace the data with our fetched info
data = video_info
# Validate all required fields exist
if not all(
k in data for k in ["video_url", "video_name", "channel_url", "channel_name"]
):
current_app.logger.error("Missing required fields")
current_app.logger.error(
"Required fields: video_url, video_name, channel_url, channel_name"
)
current_app.logger.error("Missing required fields after fetching")
return jsonify(message="Missing required fields"), 400
# Validate and sanitize all inputs
validation_errors = validate_video_data(data)
if validation_errors:
current_app.logger.error(f"Validation errors: {validation_errors}")
return jsonify(message="Invalid input data", errors=validation_errors), 400
# Sanitize string inputs
sanitized_data = sanitize_video_data(data)
new_entry = WatchHistory(
video_url=data["video_url"],
video_name=data["video_name"],
channel_url=data["channel_url"],
channel_name=data["channel_name"],
category=data.get("category") if data.get("category") else None,
view_count=data.get("view_count") if data.get("view_count") else None,
subscriber_count=data.get("subscribers") if data.get("subscribers") else None,
thumbnail_url=data.get("thumbnail_url") if data.get("thumbnail_url") else None,
upload_date=data.get("upload_date") if data.get("upload_date") else None,
video_url=sanitized_data["video_url"],
video_name=sanitized_data["video_name"],
channel_url=sanitized_data["channel_url"],
channel_name=sanitized_data["channel_name"],
category=sanitized_data.get("category"),
view_count=sanitized_data.get("view_count"),
subscriber_count=sanitized_data.get("subscribers"),
thumbnail_url=sanitized_data.get("thumbnail_url"),
upload_date=sanitized_data.get("upload_date"),
)
db_session = g.db_session
try:
current_app.logger.debug("Adding video to watch history")
current_app.logger.debug(f"Data: {sanitized_data}")
db_session.add(new_entry)
db_session.commit()
current_app.logger.debug("Video added to watch history")
current_app.logger.debug("Data: %s", data)
return jsonify(message="Video added"), 200
except SQLAlchemyError as e:
db_session.rollback()
@@ -95,18 +132,47 @@ def add_video():
return jsonify(message="Failed to add video"), 500
@bp.route("/", methods=["GET"])
def handle_request():
video_url = request.args.get("url")
if not video_url:
return "Missing 'url' parameter", 400
@bp.route("/queue", methods=["POST"])
def handle_queue():
data = request.get_json()
if not data or "url" not in data:
current_app.logger.warning("Request missing 'url' parameter")
return jsonify(message="Missing 'url' parameter"), 400
command = (
f'{{"command": ["script-message", "add_to_youtube_queue", "{video_url}"]}}\n'
)
video_url = data["url"]
# Basic URL validation
if not isinstance(video_url, str) or not video_url.strip():
current_app.logger.warning(f"Invalid URL format: {repr(video_url)}")
return jsonify(message="Invalid URL format"), 400
# URL validation to check for http/https protocol
if not (video_url.startswith("http://") or video_url.startswith("https://")):
current_app.logger.warning(f"URL missing protocol: {repr(video_url)}")
return jsonify(message="URL must start with http:// or https://"), 400
# Validate URL structure
try:
parsed_url = urlparse(video_url)
if not all([parsed_url.scheme, parsed_url.netloc]):
current_app.logger.warning(f"Invalid URL structure: {repr(video_url)}")
return jsonify(message="Invalid URL structure"), 400
except Exception as e:
current_app.logger.error(f"Failed to parse URL {repr(video_url)}: {str(e)}")
return jsonify(message="Failed to parse URL"), 400
# Sanitize the URL to prevent command injection
sanitized_url = video_url.replace('"', '\\"')
command = f'{{"command": ["script-message", "add_to_youtube_queue", "{sanitized_url}"]}}\n'
current_app.logger.debug(f"Sending URL to MPV: {repr(sanitized_url)}")
if send_to_mpv(command):
return "URL added to mpv queue", 200
return "Failed to add URL to mpv queue", 500
current_app.logger.info("Successfully added URL to MPV queue")
return jsonify(message="URL added to mpv queue"), 200
current_app.logger.error("Failed to add URL to MPV queue")
return jsonify(message="Failed to add URL to mpv queue"), 500
@bp.route("/migrate_watch_history", methods=["POST"])