update references to codey.lol -> codey.horse [new domain]

This commit is contained in:
2026-02-21 08:00:38 -05:00
parent d6689b9c38
commit 83285ff08c
6 changed files with 89 additions and 31 deletions

View File

@@ -4,6 +4,7 @@ import regex
import aiosqlite as sqlite3
from fastapi import FastAPI, HTTPException, Depends
from fastapi_throttle import RateLimiter
from fastapi.requests import Request
from fastapi.responses import JSONResponse
from typing import LiteralString, Optional, Union, Iterable
from regex import Pattern
@@ -128,7 +129,7 @@ class LyricSearch(FastAPI):
return JSONResponse(content=[])
return JSONResponse(content=typeahead)
async def lyric_search_handler(self, data: ValidLyricRequest) -> JSONResponse:
async def lyric_search_handler(self, data: ValidLyricRequest, request: Request) -> JSONResponse:
"""
Search for lyrics.
@@ -317,4 +318,13 @@ class LyricSearch(FastAPI):
if not data.extra:
result.pop("src")
# Check if the request is coming from the old domain
host = request.headers.get("host", "")
if "api.codey.lol" in host:
warning_message = "Warning: The API domain is moving to api.codey.horse. Please update your scripts to use the new domain."
if "lyrics" in result:
result["lyrics"] = f"{warning_message}<br>{result['lyrics']}"
elif "lrc" in result:
result["lrc"] = f"{warning_message}\n{result['lrc']}"
return JSONResponse(content=result)

View File

@@ -357,7 +357,7 @@ class Radio(FastAPI):
logging.debug("album_art_handler Exception: %s", str(e))
traceback.print_exc()
return RedirectResponse(
url="https://codey.lol/images/radio_art_default.jpg", status_code=302
url="https://codey.horse/images/radio_art_default.jpg", status_code=302
)
async def radio_now_playing(self, request: Request,

View File

@@ -686,7 +686,10 @@ class RIP(FastAPI):
self, video_id: str, request: Request, user=Depends(get_current_user)
) -> Response:
"""
Download a video file.
Download a video file via streaming.
Streams the video directly from the HLS source through ffmpeg
to the client without buffering the entire file to disk first.
Parameters:
- **video_id** (str): The Tidal video ID.
@@ -694,10 +697,11 @@ class RIP(FastAPI):
- **user**: Current user (dependency).
Returns:
- **Response**: The video file as a streaming response.
- **Response**: Streaming video response.
"""
from fastapi.responses import FileResponse
import os
from fastapi.responses import StreamingResponse
import asyncio
import re
if "trip" not in user.get("roles", []) and "admin" not in user.get("roles", []):
raise HTTPException(status_code=403, detail="Insufficient permissions")
@@ -710,32 +714,76 @@ class RIP(FastAPI):
status_code=400,
)
# Get video metadata for filename
metadata = await self.trip_util.get_video_metadata(vid_id)
# Get video metadata and stream URL concurrently
metadata, stream_url = await asyncio.gather(
self.trip_util.get_video_metadata(vid_id),
self.trip_util.get_video_stream_url(vid_id),
)
if not metadata:
return JSONResponse(
content={"error": "Video not found"},
status_code=404,
)
# Download the video
file_path = await self.trip_util.download_video(vid_id)
if not file_path or not os.path.exists(file_path):
if not stream_url:
return JSONResponse(
content={"error": "Failed to download video"},
content={"error": "Video stream not available"},
status_code=500,
)
# Generate a nice filename
# Build a safe filename
artist = metadata.get("artist", "Unknown")
title = metadata.get("title", f"video_{vid_id}")
# Sanitize filename
safe_filename = f"{artist} - {title}.mp4".replace("/", "-").replace("\\", "-")
safe_filename = re.sub(r'[<>:"|?*\x00-\x1F]', '', f"{artist} - {title}.mp4")
safe_filename = safe_filename.replace("/", "-").replace("\\", "-")
return FileResponse(
path=file_path,
filename=safe_filename,
async def stream_video():
"""Stream ffmpeg output directly to client."""
cmd = [
"ffmpeg",
"-nostdin",
"-hide_banner",
"-loglevel", "error",
"-analyzeduration", "10M",
"-probesize", "10M",
"-i", stream_url,
"-c:v", "copy",
"-c:a", "aac",
"-b:a", "256k",
"-af", "aresample=async=1:first_pts=0",
"-movflags", "frag_keyframe+empty_moov+faststart",
"-f", "mp4",
"pipe:1",
]
proc = await asyncio.create_subprocess_exec(
*cmd,
stdin=asyncio.subprocess.DEVNULL,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
try:
while True:
chunk = await proc.stdout.read(256 * 1024) # 256KB chunks
if not chunk:
break
yield chunk
finally:
if proc.returncode is None:
try:
proc.kill()
except Exception:
pass
await proc.wait()
return StreamingResponse(
stream_video(),
media_type="video/mp4",
headers={
"Content-Disposition": f'attachment; filename="{safe_filename}"',
},
)
async def video_bulk_fetch_handler(