update add_video route and change queue method
All checks were successful
Build Docker Image / build (push) Successful in 14m39s
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:
116
app/views.py
116
app/views.py
@@ -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"])
|
||||
|
||||
Reference in New Issue
Block a user