diff --git a/app.py b/app.py index 80ce5e8..ac8f47e 100644 --- a/app.py +++ b/app.py @@ -3,8 +3,9 @@ import http.cookiejar import json import requests from bs4 import BeautifulSoup -from flask import Flask, Response, request, jsonify +from flask import Flask, Response, request, jsonify, send_file, make_response from diskcache import Cache +from io import BytesIO import logging import threading @@ -36,6 +37,17 @@ CACHE_EXPIRATION_HTML = 60 * 60 # 1 hour CACHE_EXPIRATION_CDN = 60 * 60 * 24 * 7 # 1 week CACHE_SIZE_LIMIT = 100 * 1024 * 1024 # 100 MB +placeholder_video = None +PLACEHOLDER_VIDEO_PATH = os.environ.get('NICONICOGAY_PLACEHOLDER_VIDEO', 'placeholder.mp4') +try: + with open(PLACEHOLDER_VIDEO_PATH, 'rb') as f: + placeholder_video = BytesIO(f.read()) + logger.debug("Loaded placeholder video") +except FileNotFoundError: + logger.warning(f"Placeholder video file '{PLACEHOLDER_VIDEO_PATH}' not found") +except Exception as e: + logger.error(f"Error loading placeholder video: {e}") + cache = None if os.environ.get('NICONICOGAY_DISABLE_CACHE', '') != '1': cache = Cache("disk_cache", size_limit=CACHE_SIZE_LIMIT) @@ -273,6 +285,29 @@ def get_oembed_url(params): return oembed_url +@app.route("/cdn/.mp4") +def cdn_redirect(video_id): + """ + Checks if a video exists in CDN and redirects accordingly. + Returns CDN URL if video exists, otherwise returns a placeholder video URL. + Only responds to requests from Discord bots. + """ + logger.info(f"{video_id}: CDN redirect request received") + request_user_agent = request.headers.get('User-Agent', '').lower() + if 'discordbot' not in request_user_agent: + logger.info(f"{video_id}: Video CDN redirect ignored due to user agent ({request_user_agent})") + return Response("Video not found", status=404) + + if placeholder_video is None or is_video_in_cdn(video_id): + cdn_url = get_cdn_url(video_id) + logger.info(f"{video_id}: Redirecting to CDN URL: {cdn_url}") + return Response("", status=302, headers={"Location": cdn_url}) + + logger.info(f"{video_id}: Video not found in CDN, returning placeholder") + response = make_response(send_file(placeholder_video, mimetype="video/mp4")) + response.headers['Content-Length'] = str(placeholder_video.getbuffer().nbytes) + return response + @app.route("/watch/") def proxy(video_id): @@ -329,7 +364,7 @@ def proxy(video_id): download_queue.append((video_id, real_url, video_quality)) logger.info(f"{video_id}: Queued for download") - cdn_video_url = get_cdn_url(video_id) + cdn_video_url = f"{HOST}/cdn/{video_id}.mp4" if placeholder_video else get_cdn_url(video_id) og_tags = soup.find_all("meta", attrs={"property": True}) for tag in og_tags: # Remove attribute(s) added by niconico @@ -372,6 +407,7 @@ if you want to download videos, please consider using a tool like nndownload: ht logger.info(f"{video_id}: Returning response") return Response(html_response, mimetype="text/html") + @app.route("/owoembed") def owoembed(): """