diff --git a/.gitignore b/.gitignore index 58e75f8..0eedfa1 100644 Binary files a/.gitignore and b/.gitignore differ diff --git a/app.py b/app.py new file mode 100644 index 0000000..b88f231 --- /dev/null +++ b/app.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +"""vipsong Web — 音乐加密格式转码工具 (Web 版)。""" +import os +import shutil +from pathlib import Path +from fastapi import FastAPI, File, UploadFile, Form, HTTPException, BackgroundTasks +from fastapi.responses import HTMLResponse, FileResponse, JSONResponse +from fastapi.staticfiles import StaticFiles +import vipsong + +app = FastAPI(title="vipsong Web", description="音乐加密格式转码工具") + +BASE_DIR = Path(__file__).parent +UPLOAD_DIR = BASE_DIR / "static" / "uploads" +OUTPUT_DIR = BASE_DIR / "static" / "outputs" +UPLOAD_DIR.mkdir(parents=True, exist_ok=True) +OUTPUT_DIR.mkdir(parents=True, exist_ok=True) + +app.mount("/static", StaticFiles(directory=str(BASE_DIR / "static")), name="static") + +SUPPORTED_EXTENSIONS = [".ncm", ".kgm", ".kgma", ".vpr"] +SUPPORTED_FORMATS = ["mp3", "flac", "wav", "ogg", "m4a"] + + +def is_supported(filename: str) -> bool: + name = filename.lower() + return any(name.endswith(ext) for ext in SUPPORTED_EXTENSIONS) + + +@app.get("/", response_class=HTMLResponse) +async def index(): + html = (BASE_DIR / "templates" / "index.html").read_text(encoding="utf-8") + return HTMLResponse(html) + + +@app.post("/convert") +async def convert( + file: UploadFile = File(...), + format: str = Form("mp3"), +): + """接收单文件上传,转码后返回下载链接。""" + if not is_supported(file.filename): + raise HTTPException(status_code=400, detail=f"不支持的文件格式: {file.filename}") + + if format not in SUPPORTED_FORMATS: + raise HTTPException(status_code=400, detail=f"不支持的输出格式: {format}") + + # 清理文件名中的危险字符(保留扩展名中的点) + safe_name = "".join(c for c in file.filename if c not in "/\\<>:\"|?*" and ord(c) > 31) + upload_path = UPLOAD_DIR / safe_name + with open(upload_path, "wb") as f: + shutil.copyfileobj(file.file, f) + + output_name = Path(safe_name).stem + "." + format + output_path = OUTPUT_DIR / output_name + + try: + vipsong.convert_file( + str(upload_path), + format, + delete_flag=False, + output_path=str(output_path), + ) + except Exception as e: + raise HTTPException(status_code=500, detail=f"转码失败: {e}") + finally: + # 清理上传的临时文件 + if upload_path.exists(): + upload_path.unlink(missing_ok=True) + + return { + "original_name": file.filename, + "output_name": output_name, + "download_url": f"/download/{output_name}", + } + + +@app.get("/download/{filename}") +async def download(filename: str): + file_path = OUTPUT_DIR / filename + if not file_path.exists(): + raise HTTPException(status_code=404, detail="文件不存在") + return FileResponse(path=str(file_path), filename=filename) + + +@app.get("/health") +async def health(): + return {"status": "ok"} diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..b020597 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,215 @@ + + +
+ + +支持 .ncm (网易云) / .kgm .vpr (酷狗) → mp3 / flac / wav / ogg / m4a
+ + + + + ++ 支持格式: .ncm / .kgm / .kgma / .vpr + | 输出格式: mp3 / flac / wav / ogg / m4a +
+