This commit is contained in:
parent
8f4f50ec89
commit
a412932888
2 changed files with 65 additions and 38 deletions
58
main.py
58
main.py
|
@ -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="")
|
|
||||||
|
|
||||||
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
|
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")
|
||||||
|
|
|
@ -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!";
|
||||||
fileInput.value = "";
|
|
||||||
} else {
|
|
||||||
resp.innerHTML = "Upload failed";
|
|
||||||
}
|
|
||||||
setFormEnabled(true);
|
setFormEnabled(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
xhr.addEventListener("error", () => {
|
uploader.on("progress", (progress) => {
|
||||||
resp.innerHTML = "Upload failed";
|
if (progress.detail == 100) return;
|
||||||
setFormEnabled(true);
|
resp.innerHTML = `Uploading ${progress.detail}%...`;
|
||||||
});
|
});
|
||||||
|
|
||||||
xhr.open("POST", "/admin/{{ id }}/upload", true);
|
uploader.on("finish", (body) => {
|
||||||
xhr.setRequestHeader("Content-Type", file.type);
|
console.log("Upload complete", body);
|
||||||
xhr.setRequestHeader("filename", encodeURI(file.name));
|
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.";
|
||||||
xhr.send(file);
|
setFormEnabled(true);
|
||||||
|
fileInput.value = "";
|
||||||
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
Loading…
Reference in a new issue