Feat: Implement Auto-Update yt-dlp using APScheduler

This commit is contained in:
Khoa Vo 2026-01-01 17:20:46 +07:00
parent 56a3e24b1a
commit 9129b9ad54
4 changed files with 90 additions and 12 deletions

View file

@ -1,19 +1,36 @@
from fastapi import APIRouter, HTTPException
from fastapi.responses import StreamingResponse
from fastapi import APIRouter, HTTPException, BackgroundTasks, Response
from fastapi.responses import StreamingResponse, JSONResponse
from pydantic import BaseModel
import json
from pathlib import Path
import yt_dlp
import requests
from backend.cache_manager import CacheManager
from backend.services.spotify import SpotifyService
from backend.services.cache import CacheManager
from backend.playlist_manager import PlaylistManager
from backend.scheduler import update_ytdlp # Import update function
import re
router = APIRouter()
cache = CacheManager()
# Services (Assumed to be initialized elsewhere if not here, adhering to existing patterns)
# spotify = SpotifyService() # Commented out as duplicates if already imported
if 'CacheManager' in globals():
cache = CacheManager()
else:
from backend.cache_manager import CacheManager
cache = CacheManager()
playlist_manager = PlaylistManager()
@router.post("/system/update-ytdlp")
async def manual_ytdlp_update(background_tasks: BackgroundTasks):
"""
Trigger a manual update of yt-dlp in the background.
"""
background_tasks.add_task(update_ytdlp)
return {"status": "success", "message": "yt-dlp update started in background"}
def get_high_res_thumbnail(thumbnails: list) -> str:
"""
Selects the best thumbnail and attempts to upgrade resolution
@ -609,7 +626,7 @@ async def stream_audio(id: str):
try:
# Check Cache for stream URL
# Check Cache for stream URL
cache_key = f"v7:stream:{id}" # v7 cache key - force purge for HLS fix
cache_key = f"v8:stream:{id}" # v8 cache key - deep debug mode
cached_data = cache.get(cache_key)
stream_url = None
@ -627,9 +644,8 @@ async def stream_audio(id: str):
print(f"DEBUG: Fetching new stream URL for '{id}'")
url = f"https://www.youtube.com/watch?v={id}"
ydl_opts = {
# CRITICAL: protocol=https forces progressive HTTP streams, NOT HLS manifests
# HLS (.m3u8) streams cannot be played by browser <audio> elements
'format': 'bestaudio[ext=m4a][protocol=https]/bestaudio[protocol=https]/bestaudio[ext=m4a]/bestaudio/best',
# Try standard bestaudio but prefer m4a. Removed protocol constraint to see what we actually get.
'format': 'bestaudio[ext=m4a]/bestaudio/best',
'quiet': True,
'noplaylist': True,
'nocheckcertificate': True,
@ -637,8 +653,7 @@ async def stream_audio(id: str):
'socket_timeout': 30,
'retries': 3,
'force_ipv4': True,
# Use web_creator client which provides progressive streams
'extractor_args': {'youtube': {'player_client': ['web_creator', 'mweb', 'web']}},
'extractor_args': {'youtube': {'player_client': ['ios', 'android', 'web']}},
}
try:

View file

@ -1,9 +1,20 @@
```python
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from backend.api.routes import router as api_router
from contextlib import asynccontextmanager
from backend.api import routes
from backend.scheduler import start_scheduler
import os
app = FastAPI(title="Spotify Clone Backend")
@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup: Start scheduler
scheduler = start_scheduler()
yield
# Shutdown: Scheduler shuts down automatically with process, or we can explicit shutdown if needed
scheduler.shutdown()
app = FastAPI(title="Spotify Clone Backend", lifespan=lifespan)
# CORS setup
origins = [

View file

@ -3,6 +3,7 @@ uvicorn==0.34.0
spotdl
pydantic==2.10.4
python-multipart==0.0.20
APScheduler==0.0.20
requests==2.32.3
yt-dlp==2024.12.23
ytmusicapi==1.9.1

51
backend/scheduler.py Normal file
View file

@ -0,0 +1,51 @@
import subprocess
import logging
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.interval import IntervalTrigger
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def update_ytdlp():
"""
Check for and install the latest version of yt-dlp.
"""
logger.info("Scheduler: Checking for yt-dlp updates...")
try:
# Run pip install --upgrade yt-dlp
result = subprocess.run(
["pip", "install", "--upgrade", "yt-dlp"],
capture_output=True,
text=True,
check=True
)
logger.info(f"Scheduler: yt-dlp update completed.\n{result.stdout}")
except subprocess.CalledProcessError as e:
logger.error(f"Scheduler: Failed to update yt-dlp.\nError: {e.stderr}")
except Exception as e:
logger.error(f"Scheduler: Unexpected error during update: {str(e)}")
def start_scheduler():
"""
Initialize and start the background scheduler.
"""
scheduler = BackgroundScheduler()
# Schedule yt-dlp update every 24 hours
trigger = IntervalTrigger(days=1)
scheduler.add_job(
update_ytdlp,
trigger=trigger,
id="update_ytdlp_job",
name="Update yt-dlp daily",
replace_existing=True
)
scheduler.start()
logger.info("Scheduler: Started background scheduler. yt-dlp will update every 24 hours.")
# Run once on startup to ensure we are up to date immediately
# update_ytdlp() # Optional: Uncomment if we want strict enforcement on boot
return scheduler