use huge uploader
All checks were successful
ci/woodpecker/push/build Pipeline was successful

This commit is contained in:
Jake Walker 2025-01-10 13:48:35 +00:00
parent 8f4f50ec89
commit a412932888
2 changed files with 65 additions and 38 deletions

56
main.py
View file

@ -3,11 +3,11 @@ from datetime import timedelta
from pathlib import Path from pathlib import Path
from typing import Annotated, Optional from typing import Annotated, Optional
import aiofiles
import podgen import podgen
import structlog import structlog
from fastapi import FastAPI, Form, HTTPException, Request, Response from fastapi import FastAPI, Form, HTTPException, Request, Response, UploadFile
from fastapi.responses import FileResponse, RedirectResponse from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse, JSONResponse, RedirectResponse
from fastapi.templating import Jinja2Templates from fastapi.templating import Jinja2Templates
import data import data
@ -17,6 +17,9 @@ from settings import settings
log = structlog.get_logger() log = structlog.get_logger()
app = FastAPI() app = FastAPI()
app.add_middleware(
CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"]
)
templates = Jinja2Templates(directory="templates") templates = Jinja2Templates(directory="templates")
audio_processor = AudioProcessor() audio_processor = AudioProcessor()
@ -71,22 +74,48 @@ def finish_processing(
@app.post("/admin/{feed_id}/upload") @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() repo = data.load_repository()
if feed_id not in repo.podcasts: if feed_id not in repo.podcasts:
raise HTTPException(status_code=404, detail="Podcast not found") raise HTTPException(status_code=404, detail="Podcast not found")
try: is_last = (chunk_number + 1) == chunks_total
filename = Path(urllib.parse.unquote(request.headers["filename"]))
episode = data.Episode(name=filename.stem, file_size=0, file_hash="") file_name = f"{file_id}_{chunk_number}"
settings.uploads_directory.mkdir(parents=True, exist_ok=True) 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 upload_path = settings.uploads_directory / episode.id
async with aiofiles.open(upload_path, "wb") as f: with open(upload_path, "wb") as buf:
async for chunk in request.stream(): chunk = 0
await f.write(chunk) 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( audio_processor.add_file(
upload_path, upload_path,
@ -95,11 +124,10 @@ async def admin_upload_episode(request: Request, feed_id: str):
feed_id, episode, duration, file_hash, file_size 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") @app.get("/admin/{feed_id}/{episode_id}/delete")

View file

@ -46,12 +46,14 @@
</tbody> </tbody>
</table> </table>
<script> <script type="module">
import HugeUploader from "https://cdn.skypack.dev/huge-uploader";
const resp = document.getElementById("response"); const resp = document.getElementById("response");
const fileInput = document.getElementById("fileInput"); const fileInput = document.getElementById("fileInput");
const submitButton = document.getElementById("submitButton"); const submitButton = document.getElementById("submitButton");
function reset() { window.reset = () => {
resp.innerHTML = ""; resp.innerHTML = "";
} }
@ -60,7 +62,7 @@
fileInput.disabled = !enabled; fileInput.disabled = !enabled;
} }
function go() { window.go = () => {
const file = fileInput.files[0]; const file = fileInput.files[0];
if (!file) { if (!file) {
return return
@ -68,34 +70,31 @@
setFormEnabled(false); setFormEnabled(false);
const xhr = new XMLHttpRequest(); const uploader = new HugeUploader({
endpoint: "/admin/{{ id }}/upload",
xhr.upload.addEventListener("progress", (event) => { file: file,
if (event.lengthComputable) { headers: {
const percentComplete = Math.round((event.loaded / event.total) * 100); "name": encodeURI(file.name)
resp.innerHTML = "Uploading: " + percentComplete + "%";
} }
}); });
xhr.addEventListener("load", () => { uploader.on("error", (err) => {
if (xhr.status === 200) { console.error("Upload error", err);
resp.innerHTML = "Upload complete, check back in a few minutes after the episode has processed."; resp.innerHTML = "Something has gone wrong!";
setFormEnabled(true);
});
uploader.on("progress", (progress) => {
if (progress.detail == 100) return;
resp.innerHTML = `Uploading ${progress.detail}%...`;
});
uploader.on("finish", (body) => {
console.log("Upload complete", body);
resp.innerHTML = "Upload complete! The episode will be processed in the background. This may take a few minutes but it's safe to navigate away.";
setFormEnabled(true);
fileInput.value = ""; fileInput.value = "";
} else {
resp.innerHTML = "Upload failed";
}
setFormEnabled(true);
}); });
xhr.addEventListener("error", () => {
resp.innerHTML = "Upload failed";
setFormEnabled(true);
});
xhr.open("POST", "/admin/{{ id }}/upload", true);
xhr.setRequestHeader("Content-Type", file.type);
xhr.setRequestHeader("filename", encodeURI(file.name));
xhr.send(file);
} }
</script> </script>
{% endblock %} {% endblock %}