import os os.environ["PYTUBE_CACHE_DIR"] = "/app/cache" from pytubefix import YouTube from pytubefix.cli import on_progress from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware import logging as log import subprocess import json from typing import Tuple # --- Logging Setup --- log.basicConfig( level=log.INFO, format="%(asctime)s [%(levelname)s] %(message)s", ) # --- FastAPI Setup --- app = FastAPI() app.add_middleware( CORSMiddleware, allow_origins=["*"], # Adjust in production allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.get("/") def index(): return {"message": "Hello, World!"} # --- Shell Command Runner --- def cmd(command, check=True, shell=True, capture_output=True, text=True): log.info(f"Executing command: {command}") try: result = subprocess.run( command, check=check, shell=shell, capture_output=capture_output, text=text ) log.info("Command executed successfully.") return result except subprocess.CalledProcessError as error: log.error(f"Command failed: {error}") raise HTTPException(status_code=500, detail="Token generation failed.") # --- Token Generator --- def generate_youtube_token() -> dict: result = cmd("node test.js") if not result or not result.stdout: log.error("No output received from test.js") raise HTTPException(status_code=500, detail="Invalid token response") try: data = json.loads(result.stdout) log.info(f"Token data received: {data}") return data except json.JSONDecodeError as e: log.error(f"Failed to decode token JSON: {e}") raise HTTPException(status_code=500, detail="Invalid JSON from token generator") # --- Token Verifier Function --- def po_token_verifier() -> Tuple[str, str]: token_object = generate_youtube_token() return token_object["visitorData"], token_object["poToken"] # --- Video Fetch Endpoint --- @app.get("/api/video/{id}") def video_id(id: str): url = f"https://www.youtube.com/watch?v={id}" log.info(f"Fetching YouTube video for ID: {id}") try: yt = YouTube( url, use_po_token=True, po_token_verifier=po_token_verifier ) ys = yt.streams.get_highest_resolution() if not ys: log.warning("No suitable video stream found.") raise HTTPException(status_code=404, detail="No suitable video stream found") log.info(f"Video fetched: {yt.title}") return { "title": yt.title, "url": ys.url, "thumbnail": yt.thumbnail_url, "description": yt.description } except Exception as e: log.error(f"Failed to fetch video info: {e}") raise HTTPException(status_code=500, detail=str(e))