import logging import os from flask import Flask, render_template, request, Response, abort, jsonify from werkzeug.exceptions import HTTPException import dlp from utils import is_valid_url, get_error_message app = Flask(__name__) LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO") logging.basicConfig( level=getattr(logging, LOG_LEVEL), format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" ) logger = logging.getLogger(__name__) PORT = int(os.getenv("PORT", 5000)) @app.route("/") def index(): return render_template("index.html") @app.route("/player") def player(): video_url = request.args.get("url") if not video_url: abort(400, description="Missing url parameter") if not is_valid_url(video_url): abort(400, description="Invalid or disallowed URL") try: stream_info = dlp.get_stream_info(video_url) from urllib.parse import quote encoded_url = quote(video_url, safe="") proxy_hls_url = f"/hls?url={encoded_url}&path=index.m3u8" return render_template( "player.html", video_url=video_url, proxy_hls_url=proxy_hls_url, title=stream_info.get("title", "Video"), thumbnail=stream_info.get("thumbnail") ) except Exception as e: logger.error(f"Error getting stream info: {e}") abort(500, description=str(e)) @app.route("/hls") def hls_proxy(): try: url_param = request.args.get("url", "") if not url_param: abort(400, description="Missing url parameter") from urllib.parse import unquote path = request.args.get("path", "") video_url = unquote(url_param) if not is_valid_url(video_url): abort(400, description="Invalid URL") # Main playlist request - get from yt-dlp and rewrite URLs if path == "index.m3u8" or path == "": playlist = dlp.get_hls_playlist(video_url) return Response(playlist, mimetype="application/vnd.apple.mpegurl") # Sub-playlist or segment request - path is the absolute URL segment_data = dlp.get_hls_segment(video_url, path) if segment_data is None: abort(500, description="Failed to fetch segment") if path.endswith(".m3u8"): return Response(segment_data, mimetype="application/vnd.apple.mpegurl") return Response(segment_data, mimetype="video/mp2t") except HTTPException: raise except ValueError as e: logger.warning(f"Validation error: {e}") abort(400, description=str(e)) except Exception as e: logger.error(f"HLS proxy error: {e}") abort(500, description="Error fetching stream") @app.errorhandler(Exception) def handle_error(e): if isinstance(e, HTTPException): return jsonify({"error": get_error_message(e.code), "message": str(e.description)}), e.code logger.error(f"Unexpected error: {e}") return jsonify({"error": "Internal Server Error", "message": str(e)}), 500 if __name__ == "__main__": app.run(host="0.0.0.0", port=PORT)