aboutsummaryrefslogtreecommitdiff
path: root/services/stream/views.py
diff options
context:
space:
mode:
Diffstat (limited to 'services/stream/views.py')
-rw-r--r--services/stream/views.py106
1 files changed, 104 insertions, 2 deletions
diff --git a/services/stream/views.py b/services/stream/views.py
index 21e15dc1..a646d185 100644
--- a/services/stream/views.py
+++ b/services/stream/views.py
@@ -1,12 +1,18 @@
# views.py
import os
import random
-from urllib.parse import urlparse
+import re
+from urllib.parse import quote, urljoin, urlparse
from django.conf import settings
from services.stream.songs import MUSIC_FILES
import requests
-from django.http import HttpResponse, HttpResponseForbidden, JsonResponse
+from django.http import (
+ HttpResponse,
+ HttpResponseForbidden,
+ JsonResponse,
+ StreamingHttpResponse,
+)
CDN_URL = os.getenv("CDN_URL")
MUSIC_FILES_COUNT = len(MUSIC_FILES)
@@ -60,3 +66,99 @@ def stream_song(request, song_id: int) -> HttpResponse:
return HttpResponse(status=404)
except Exception:
return HttpResponse(status=500)
+
+
+def anime_stream(request):
+ if not request.COOKIES.get("csrftoken"):
+ return HttpResponseForbidden("Invalid request")
+
+ referrer = request.META.get("HTTP_REFERER")
+ if not referrer:
+ return HttpResponseForbidden("Direct access not allowed")
+
+ parsed_uri = urlparse(referrer)
+ referrer_host = parsed_uri.netloc.split(":")[0]
+
+ if referrer_host not in settings.ALLOWED_HOSTS:
+ return HttpResponseForbidden("Access not allowed")
+
+ url = request.GET.get("url")
+
+ if not url:
+ return HttpResponse("No URL provided", status=400)
+
+ try:
+ # Make the request to the target server
+ response = requests.get(
+ url,
+ stream=True,
+ headers={
+ "User-Agent": request.headers.get("User-Agent", ""),
+ "Referer": request.headers.get("Referer", ""),
+ "Origin": request.headers.get("Origin", ""),
+ },
+ )
+
+ content_type = response.headers.get("content-type", "")
+
+ # Handle M3U8 playlists
+ if "m3u8" in content_type or url.endswith(".m3u8"):
+ content = response.text
+ base_url = url.rsplit("/", 1)[0] + "/"
+
+ # Modify the playlist URLs to go through our proxy
+ modified_content = []
+ for line in content.splitlines():
+ if line.startswith("#"):
+ modified_content.append(line)
+ elif line.strip(): # Handle content lines (URLs)
+ # Handle both absolute and relative URLs
+ if not line.startswith("http"):
+ line = urljoin(base_url, line)
+ proxy_url = f"/services/stream/anime?url={quote(line)}"
+ modified_content.append(proxy_url)
+ else:
+ modified_content.append(line)
+
+ return HttpResponse(
+ "\n".join(modified_content),
+ content_type="application/vnd.apple.mpegurl",
+ )
+
+ # Handle VTT files
+ elif url.endswith(".vtt"):
+ content = response.text
+ base_url = url.rsplit("/", 1)[0] + "/"
+
+ # Pattern to match both full URLs and relative paths with optional #xywh fragment
+ sprite_pattern = r'((?:https?://[^\s<>"]+?|[\w-]+\.(?:jpg|jpeg|png|webp))(?:#xywh=[\d,]+)?)'
+
+ def replace_url(match):
+ sprite_url = match.group(1)
+ # If it's not a full URL, join it with the base URL
+ if not sprite_url.startswith("http"):
+ sprite_url = urljoin(base_url, sprite_url)
+ return f"/watch/stream?url={quote(sprite_url)}"
+
+ # Replace all image URLs with proxied versions
+ modified_content = re.sub(sprite_pattern, replace_url, content)
+
+ return HttpResponse(modified_content, content_type="text/vtt")
+
+ # Handle images (thumbnails)
+ elif any(ext in url.lower() for ext in [".jpg", ".jpeg", ".png", ".webp"]):
+ return StreamingHttpResponse(
+ response.iter_content(chunk_size=8192),
+ content_type=response.headers.get("content-type"),
+ status=response.status_code,
+ )
+
+ # For video segments and other content, stream directly
+ return StreamingHttpResponse(
+ response.iter_content(chunk_size=8192),
+ content_type=response.headers.get("content-type"),
+ status=response.status_code,
+ )
+
+ except requests.RequestException as e:
+ return HttpResponse(f"Error proxying request: {str(e)}", status=500)