#!/usr/bin/env python3 import logging import os import socket import time import urllib.parse from datetime import date import mysql.connector from flask import Flask, jsonify, request from mysql.connector import Error # Set up basic logging logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" ) # Ensure Flask doesn't duplicate log messages log = logging.getLogger("werkzeug") log.setLevel(logging.ERROR) # Flask app app = Flask(__name__) # Flask logging configuration to use the same logger as the rest of the app app.logger.handlers = logging.getLogger().handlers app.logger.setLevel(logging.getLogger().level) SOCKET_RETRY_DELAY = 5 # Time in seconds between retries to connect to the socket MAX_RETRIES = 10 # Maximum number of retries to connect to the socket # Configuration MPV_SOCKET: str = os.getenv("MPV_SOCKET", "/tmp/mpvsocket") HOST_NAME: str = os.getenv("HOST_NAME", "0.0.0.0") PORT_NUMBER: int = int(os.getenv("PORT_NUMBER", "8080")) # MySQL Configuration MYSQL_HOST: str = os.getenv("MYSQL_HOST", "localhost") MYSQL_DATABASE: str = os.getenv("MYSQL_DATABASE", "your_database") MYSQL_USER: str = os.getenv("MYSQL_USER", "your_username") MYSQL_PASSWORD: str = os.getenv("MYSQL_PASSWORD", "your_password") MYSQL_PORT: int = int(os.getenv("MYSQL_PORT", "3306")) LOGLEVEL = os.getenv("LOGLEVEL", "INFO").strip().upper() if LOGLEVEL == "DEBUG": logging.getLogger().setLevel(logging.DEBUG) elif LOGLEVEL == "WARNING": logging.getLogger().setLevel(logging.WARNING) elif LOGLEVEL == "ERROR": logging.getLogger().setLevel(logging.ERROR) else: logging.getLogger().setLevel(logging.INFO) def get_mysql_connection(): """Get a MySQL database connection.""" try: logging.debug( f"Connection information: {MYSQL_HOST}, {MYSQL_USER}, {MYSQL_PORT}" ) connection = mysql.connector.connect( host=MYSQL_HOST, user=MYSQL_USER, password=MYSQL_PASSWORD, port=MYSQL_PORT, ) if connection.is_connected(): logging.info("Connected to MySQL database") return connection except Error as e: logging.error(f"Error while connecting to MySQL: {e}") return None def ensure_watch_history_table_exists(): """Ensure the watch_history table exists in the mpv schema, otherwise create it.""" connection = get_mysql_connection() if connection: try: cursor = connection.cursor() cursor.execute("CREATE DATABASE IF NOT EXISTS mpv") cursor.execute( """ CREATE TABLE IF NOT EXISTS mpv.watch_history ( whid INT AUTO_INCREMENT PRIMARY KEY, video_url VARCHAR(255) NOT NULL, video_name VARCHAR(255) NOT NULL, channel_url VARCHAR(255) NOT NULL, channel_name VARCHAR(255) NOT NULL, watch_date DATE NOT NULL ) """ ) connection.commit() logging.info("Ensured watch_history table exists") except Error as e: logging.error(f"Failed to ensure watch_history table exists: {e}") finally: cursor.close() connection.close() def send_to_mpv(command): """Send a command to the mpv socket, retrying up to MAX_RETRIES times if the socket is not available.""" attempts = 0 while attempts < MAX_RETRIES: try: with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as client_socket: client_socket.connect(MPV_SOCKET) client_socket.sendall(command.encode("utf-8")) logging.info("Command sent to mpv successfully.") return True except socket.error as e: attempts += 1 logging.error( f"Failed to connect to socket (attempt {attempts}/{MAX_RETRIES}): {e}. Retrying in {SOCKET_RETRY_DELAY} seconds..." ) time.sleep(SOCKET_RETRY_DELAY) logging.error(f"Exceeded maximum retries ({MAX_RETRIES}). Ignoring the request.") return False @app.route("/add_video", methods=["POST"]) def add_video(): data = request.get_json() if data: video_url: str = data.get("video_url") video_name: str = data.get("video_name") channel_url: str = data.get("channel_url") channel_name: str = data.get("channel_name") watch_date: date = date.today().strftime("%Y-%m-%d") if video_url and video_name and channel_url and channel_name and watch_date: logging.debug(f"Received data: {data}") logging.debug(f"Watch date: {watch_date}") # Insert the data into the MySQL database connection = get_mysql_connection() if connection: try: query = """ INSERT INTO mpv.watch_history (video_url, video_name, channel_url, channel_name, watch_date) VALUES (%s, %s, %s, %s, %s) """ cursor = connection.cursor() cursor.execute( query, ( video_url, video_name, channel_url, channel_name, watch_date, ), ) connection.commit() logging.info("Data inserted into MySQL database") return ( jsonify(message="Data added to mpv queue and database"), 200, ) except Error as e: logging.error(f"Failed to insert data into MySQL database: {e}") return jsonify(message="Failed to add data to database"), 500 finally: cursor.close() connection.close() else: return jsonify(message="Failed to connect to MySQL database"), 500 else: logging.error("Missing required data fields") return jsonify(message="Missing required data fields"), 400 else: logging.error("Invalid JSON data") return jsonify(message="Invalid JSON data"), 400 @app.route("/", methods=["GET"]) def handle_request(): video_url = request.args.get("url") if video_url: video_url = urllib.parse.unquote(video_url) # Decode the URL logging.info(f"Received URL: {video_url}") # Create the command to send to mpv command = f'{{"command": ["script-message", "add_to_youtube_queue", "{video_url}"]}}\n' # Try to send the command to mpv if send_to_mpv(command): return "URL added to mpv queue", 200 else: return "Failed to add URL to mpv queue after max retries", 500 else: logging.error("Missing 'url' parameter") return "Missing 'url' parameter", 400 if __name__ == "__main__": logging.info(f"Starting server on {HOST_NAME}:{PORT_NUMBER}...") ensure_watch_history_table_exists() try: app.run(host=HOST_NAME, port=PORT_NUMBER) except Exception as e: logging.exception(f"Error occurred: {e}") except KeyboardInterrupt: logging.info("Server is shutting down...") logging.info("Server stopped.")