| | from fastapi.responses import JSONResponse, FileResponse, Response |
| | from fastapi import FastAPI, HTTPException, Request |
| | import mimetypes |
| | from threading import Thread |
| | from Instance import Instance |
| | from api import LoadBalancerAPI |
| | import os |
| | import aiofiles |
| | from urllib.parse import unquote |
| |
|
| | |
| | CACHE_DIR = os.getenv("CACHE_DIR") |
| | TOKEN = os.getenv("TOKEN") |
| | REPO = os.getenv("REPO") |
| | ID = os.getenv("ID") |
| | URL = os.getenv("URL") |
| | LOAD_BALANCER_URL = os.getenv("LOAD_BALANCER_URL") |
| |
|
| | load_balancer_api = LoadBalancerAPI(base_url=LOAD_BALANCER_URL) |
| | instance = Instance(id=ID, url=URL, cache_dir=CACHE_DIR, token=TOKEN, repo=REPO, load_balancer_api=load_balancer_api) |
| |
|
| | app = FastAPI() |
| |
|
| | from fastapi import HTTPException, Request |
| | from fastapi.responses import Response, FileResponse |
| | import os |
| | import mimetypes |
| | import aiofiles |
| |
|
| | async def serve_media(file_path: str, request: Request): |
| | """Serve any media file with support for range requests.""" |
| | if not os.path.isfile(file_path): |
| | raise HTTPException(status_code=404, detail="Media file not found") |
| | |
| | file_size = os.path.getsize(file_path) |
| | range_header = request.headers.get('range', None) |
| |
|
| | |
| | mime_type, _ = mimetypes.guess_type(file_path) |
| | if mime_type is None: |
| | mime_type = 'application/octet-stream' |
| |
|
| | |
| | if range_header: |
| | range_specifier = range_header.replace('bytes=', '').strip() |
| | start, end = (None, None) |
| |
|
| | if '-' in range_specifier: |
| | start_str, end_str = range_specifier.split('-') |
| | start = int(start_str) |
| | end = int(end_str) if end_str else file_size - 1 |
| |
|
| | |
| | if start is None or start >= file_size or (end is not None and end >= file_size) or (end is not None and start > end): |
| | raise HTTPException(status_code=416, detail="Requested range not satisfiable") |
| |
|
| | headers = { |
| | 'Content-Range': f'bytes {start}-{end or file_size - 1}/{file_size}', |
| | 'Accept-Ranges': 'bytes', |
| | 'Content-Length': str((end - start + 1) if end is not None else file_size - start), |
| | 'Content-Type': mime_type |
| | } |
| |
|
| | async with aiofiles.open(file_path, 'rb') as f: |
| | await f.seek(start) |
| | chunk_size = 8192 |
| | data = bytearray() |
| |
|
| | while start <= (end or file_size - 1): |
| | remaining = (end or file_size - 1) - start + 1 |
| | read_size = min(chunk_size, remaining) |
| | chunk = await f.read(read_size) |
| |
|
| | if not chunk: |
| | break |
| | |
| | data.extend(chunk) |
| | start += read_size |
| |
|
| | return Response(content=bytes(data), status_code=206, headers=headers) |
| |
|
| | |
| | return FileResponse(file_path, media_type=mime_type) |
| |
|
| | @app.get("/") |
| | async def index(): |
| | return instance.version |
| |
|
| | @app.get("/api/get/report") |
| | async def get_report(): |
| | report = instance.compile_report() |
| | return JSONResponse(report) |
| |
|
| | @app.get('/api/get/music/store') |
| | async def get_media_store_api(): |
| | """Endpoint to get the music store JSON.""" |
| | return JSONResponse(instance.MUSIC_STORE) |
| |
|
| | @app.get("/api/get/music/{filename}") |
| | async def get_media_api(request: Request, filename: str): |
| | """Endpoint to get the music file (audio or video) by filename with support for range requests.""" |
| | filename = unquote(filename) |
| | if not filename: |
| | raise HTTPException(status_code=400, detail="filename parameter is required") |
| | |
| | if filename in instance.MUSIC_STORE: |
| | cache_path = instance.MUSIC_STORE[filename] |
| | if os.path.exists(cache_path): |
| | return await serve_media(cache_path, request) |
| | |
| | media_path = instance.find_music_path(filename) |
| | |
| | if not media_path: |
| | raise HTTPException(status_code=404, detail="Media not found") |
| | |
| | cache_path = os.path.join(CACHE_DIR, media_path) |
| | file_url = f"https://huggingface.co/{REPO}/resolve/main/{media_path}" |
| | music_id = instance.get_music_id(filename) |
| | |
| | if music_id not in instance.download_threads or not instance.download_threads[music_id].is_alive(): |
| | thread = Thread(target=instance.download_music, args=(file_url, TOKEN, cache_path, music_id, filename)) |
| | instance.download_threads[music_id] = thread |
| | thread.start() |
| | |
| | return JSONResponse({"status": "Download started", "music_id": music_id}) |
| |
|
| | @app.get("/api/get/progress/{id}") |
| | async def get_progress_api(id: str): |
| | """Endpoint to get the download progress of a media file.""" |
| | progress = instance.get_download_progress(id) |
| | return JSONResponse({"id": id, "progress": progress}) |
| |
|