From 8dd6aebd8b34a2d76cd10bb38078f9addc210d2b Mon Sep 17 00:00:00 2001 From: ypshidifu Date: Thu, 17 Sep 2026 17:10:55 +0800 Subject: [PATCH] =?UTF-8?q?Web=20=E5=89=8D=E7=AB=AF:=20HTML=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0+=E8=BD=AC=E7=A0=81=E7=95=8C=E9=9D=A2=20+=20FastAPI?= =?UTF-8?q?=E5=90=8E=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - app.py: FastAPI 后端,POST /convert 接收文件上传并调用 vipsong.convert_file 转码 GET /download/ 提供下载,GET /health 健康检查 文件名安全过滤保留扩展名,支持多格式输出(mp3/flac/wav/ogg/m4a) - templates/index.html: 纯HTML/CSS/JS单页应用,拖拽上传、格式选择、状态显示、下载链接 - .gitignore: 添加 static/uploads/、static/outputs/、音频输出文件过滤 - requirements.txt: 添加 fastapi、uvicorn、python-multipart 验证 MCP 可用性: - Baota MCP SystemInfo/SiteList/ServiceStatus 全部正常返回 - nginx+mysql+php-fpm-74/82/85 运行中,5个网站正常 - Web API 端对端测试: NCM→MP3/FLAC/OGG 均输出有效音频,错误处理返回 HTTP 400 --- .gitignore | Bin 92 -> 258 bytes app.py | 88 ++++++++++++++++++ templates/index.html | 215 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 303 insertions(+) create mode 100644 app.py create mode 100644 templates/index.html diff --git a/.gitignore b/.gitignore index 58e75f8fcbc91a347599550ca93f73ca1d4f7308..0eedfa17da3d1f2c0852252847396490a28ba6c7 100644 GIT binary patch literal 258 zcmZXP+YW+23`FPI#2@hiV(@=9L=;HGHQ^Tjo->FWj1S%Bbf(=lDCniPt}2N}H#q31 zgHFme#S~!Y8pm@}%#+SL%FtU4tFtGUM9bV 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 @@ + + + + + + vipsong Web — 音乐加密格式转码 + + + +
+

vipsong — 音乐加密格式转码

+

支持 .ncm (网易云) / .kgm .vpr (酷狗) → mp3 / flac / wav / ogg / m4a

+ +
+
+ 📁 +

点击选择文件,或拖拽到这里

+ +
+ +
+ + +
+ + +
+ +
+ +

+ 支持格式: .ncm / .kgm / .kgma / .vpr +  |  输出格式: mp3 / flac / wav / ogg / m4a +

+
+ + + +