mpv-youtube-queue-server/server.py

197 lines
6.8 KiB
Python
Raw Permalink Normal View History

2024-09-04 15:15:40 -07:00
#!/usr/bin/env python3
import logging
import os
import socket
import time
2024-09-06 12:32:09 -07:00
from urllib import parse
2024-09-04 15:15:40 -07:00
from flask import Flask, jsonify, request
from sqlalchemy import Column, DateTime, Integer, String, create_engine, exc
from sqlalchemy.orm import declarative_base, sessionmaker
from sqlalchemy.sql import func
2024-09-04 15:15:40 -07:00
# Set up basic logging
logging.basicConfig(
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
)
# 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)
2024-09-04 15:15:40 -07:00
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 from environment variables
LISTEN_ADDRESS = os.getenv("LISTEN_ADDRESS", "0.0.0.0")
LISTEN_PORT = int(os.getenv("LISTEN_PORT", "8080"))
DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///./mpv.db")
2024-09-04 15:15:40 -07:00
MPV_SOCKET: str = os.getenv("MPV_SOCKET", "/tmp/mpvsocket")
LOGLEVEL = os.getenv("LOGLEVEL", "INFO").strip().upper()
2024-09-04 15:15:40 -07:00
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)
# Set up SQLAlchemy
Base = declarative_base()
engine = create_engine(DATABASE_URL)
Session = sessionmaker(bind=engine)
session = Session()
2024-09-04 15:15:40 -07:00
class WatchHistory(Base):
__tablename__ = "watch_history"
whid = Column(Integer, primary_key=True, autoincrement=True)
video_url = Column(String(255), nullable=False)
video_name = Column(String(255), nullable=False)
channel_url = Column(String(255), nullable=False)
channel_name = Column(String(255), nullable=False)
watch_date = Column(DateTime, nullable=False, server_default=func.now())
created_by = Column(
String(100), nullable=False, server_default="mpv-youtube-queue-server"
)
2024-09-10 01:04:22 -07:00
class SavedQueue(Base):
__tablename__ = "saved_queue"
sqid = Column(Integer, primary_key=True, autoincrement=True)
video_url = Column(String(255), nullable=False)
created_date = Column(DateTime, nullable=False, server_default=func.now())
created_by = Column(
String(100), nullable=False, server_default="mpv-youtube-queue-server"
)
# Ensure tables exist
Base.metadata.create_all(engine)
2024-09-04 15:15:40 -07:00
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
2024-09-10 01:04:22 -07:00
@app.route("/save_queue", methods=["POST"])
def save_queue():
data = request.get_json()
if data is None or "urls" not in data:
logging.error("Invalid JSON data")
return jsonify(message="Invalid JSON data"), 400
logging.debug(f"Received data: {data}")
urls = data.get("urls")
logging.debug("Truncating saved queue")
session.query(SavedQueue).delete()
for url in urls:
logging.debug(f"Adding {url} to the saved queue")
new_entry = SavedQueue(video_url=url)
try:
session.add(new_entry)
except exc.SQLAlchemyError as e:
logging.error(f"Failed to insert data into database: {e}")
return jsonify(message="Failed to add data to database"), 500
session.commit()
return jsonify(message="Data added to saved queue"), 200
@app.route("/load_queue", methods=["GET"])
def load_queue():
logging.debug("Loading saved queue")
urls = [entry.video_url for entry in session.query(SavedQueue).all()]
logging.debug(f"Loaded {len(urls)} URLs from the saved queue")
return jsonify(urls), 200
2024-09-04 15:15:40 -07:00
@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")
if video_url and video_name and channel_url and channel_name:
logging.debug(f"Received data: {data}")
try:
new_entry = WatchHistory(
video_url=video_url,
video_name=video_name,
channel_url=channel_url,
channel_name=channel_name,
)
session.add(new_entry)
session.commit()
2024-09-06 02:55:10 -07:00
logging.debug(
f"{video_name} by {channel_name} inserted into the database successfully"
)
return jsonify(message="Data added to mpv queue and database"), 200
except exc.SQLAlchemyError as e:
session.rollback()
logging.error(f"Failed to insert data into database: {e}")
return jsonify(message="Failed to add data to database"), 500
2024-09-04 15:15:40 -07:00
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:
2024-09-06 12:32:09 -07:00
video_url = parse.unquote(video_url) # Decode the URL
2024-09-04 15:15:40 -07:00
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 {LISTEN_ADDRESS}:{LISTEN_PORT}...")
2024-09-04 15:15:40 -07:00
try:
app.run(host=LISTEN_ADDRESS, port=LISTEN_PORT)
2024-09-04 15:15:40 -07:00
except Exception as e:
logging.exception(f"Error occurred: {e}")
except KeyboardInterrupt:
logging.info("Server is shutting down...")
logging.info("Server stopped.")