diff --git a/main.py b/main.py index 351191a..c5641a1 100644 --- a/main.py +++ b/main.py @@ -3,11 +3,11 @@ from datetime import timedelta from pathlib import Path from typing import Annotated, Optional -import aiofiles import podgen import structlog -from fastapi import FastAPI, Form, HTTPException, Request, Response -from fastapi.responses import FileResponse, RedirectResponse +from fastapi import FastAPI, Form, HTTPException, Request, Response, UploadFile +from fastapi.middleware.cors import CORSMiddleware +from fastapi.responses import FileResponse, JSONResponse, RedirectResponse from fastapi.templating import Jinja2Templates import data @@ -17,6 +17,9 @@ from settings import settings log = structlog.get_logger() app = FastAPI() +app.add_middleware( + CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"] +) templates = Jinja2Templates(directory="templates") audio_processor = AudioProcessor() @@ -71,22 +74,48 @@ def finish_processing( @app.post("/admin/{feed_id}/upload") -async def admin_upload_episode(request: Request, feed_id: str): +async def admin_upload_episode(request: Request, feed_id: str, file: UploadFile): + file_id = request.headers.get("uploader-file-id") + chunks_total = int(request.headers.get("uploader-chunks-total")) + chunk_number = int(request.headers.get("uploader-chunk-number")) + episode_name = request.headers.get("name") + + if file_id is None or episode_name is None: + raise HTTPException(400, "Invalid request") + + file_id = "".join(c for c in file_id if c.isalnum()).strip() + repo = data.load_repository() if feed_id not in repo.podcasts: raise HTTPException(status_code=404, detail="Podcast not found") - try: - filename = Path(urllib.parse.unquote(request.headers["filename"])) - episode = data.Episode(name=filename.stem, file_size=0, file_hash="") + is_last = (chunk_number + 1) == chunks_total - settings.uploads_directory.mkdir(parents=True, exist_ok=True) + file_name = f"{file_id}_{chunk_number}" + settings.uploads_directory.mkdir(parents=True, exist_ok=True) + + with open(settings.uploads_directory / file_name, "wb") as buf: + buf.write(await file.read()) + + if is_last: + episode = data.Episode( + name=Path(urllib.parse.unquote(episode_name)).stem, + file_size=0, + file_hash="", + ) upload_path = settings.uploads_directory / episode.id - async with aiofiles.open(upload_path, "wb") as f: - async for chunk in request.stream(): - await f.write(chunk) + with open(upload_path, "wb") as buf: + chunk = 0 + while chunk < chunks_total: + chunk_path = settings.uploads_directory / f"{file_id}_{chunk}" + with open(chunk_path, "rb") as infile: + buf.write(infile.read()) + infile.close() + + chunk_path.unlink() + chunk += 1 audio_processor.add_file( upload_path, @@ -95,11 +124,10 @@ async def admin_upload_episode(request: Request, feed_id: str): feed_id, episode, duration, file_hash, file_size ), ) - except Exception as e: - log.error("Failed to upload file", error=e) - raise HTTPException(status_code=500, detail="Something went wrong") - return {"ok": True} + return JSONResponse({"message": "File Uploaded"}, status_code=200) + + return JSONResponse({"message": "Chunk Uploaded"}, status_code=200) @app.get("/admin/{feed_id}/{episode_id}/delete") diff --git a/templates/admin_feed.html.j2 b/templates/admin_feed.html.j2 index 8d375b8..88f0c3f 100644 --- a/templates/admin_feed.html.j2 +++ b/templates/admin_feed.html.j2 @@ -46,12 +46,14 @@ - {% endblock %}