Gradio Agents & MCP 黑客松
获奖者Gradio Agents & MCP 黑客松
获奖者在这篇博客文章中,我们将通过创建一个使用 FastAPI 的端到端示例 Web 应用,来演示如何使用 gradio_client
Python 库。该库使开发人员能够以编程方式向 Gradio 应用发出请求。我们将构建的 Web 应用名为“Acapellify”,它将允许用户上传视频文件作为输入,并返回一个没有伴奏音乐的视频版本。它还将展示一个生成的视频画廊。
先决条件
在开始之前,请确保你正在运行 Python 3.9 或更高版本,并已安装以下库
gradio_client
fastapi
uvicorn
你可以从 pip
安装这些库
$ pip install gradio_client fastapi uvicorn
你还需要安装 ffmpeg。你可以通过在终端中运行以下命令来检查是否已安装 ffmpeg
$ ffmpeg version
否则,请按照这些说明安装 ffmpeg。
让我们从看似最复杂的部分开始——使用机器学习从视频中去除音乐。
幸运的是,我们有一个现有的 Space 可以用来简化这个过程:https://hugging-face.cn/spaces/abidlabs/music-separation。这个 Space 接收一个音频文件,并生成两个独立的音频文件:一个包含伴奏音乐,另一个包含原始剪辑中的所有其他声音。非常适合与我们的客户端一起使用!
打开一个新的 Python 文件,例如 main.py
,然后开始从 gradio_client
导入 Client
类并将其连接到此 Space
from gradio_client import Client, handle_file
client = Client("abidlabs/music-separation")
def acapellify(audio_path):
result = client.predict(handle_file(audio_path), api_name="/predict")
return result[0]
这就是所需的全部代码——请注意,API 端点会返回一个包含两个音频文件(一个无音乐,一个只有音乐)的列表,因此我们只需返回列表的第一个元素即可。
注意:由于这是一个公共 Space,可能还有其他用户也在使用此 Space,这可能会导致体验缓慢。你可以使用你自己的Hugging Face token复制此 Space,并创建一个只有你才能访问的私人 Space,从而绕过队列。为此,只需将上面的前两行替换为
from gradio_client import Client
client = Client.duplicate("abidlabs/music-separation", hf_token=YOUR_HF_TOKEN)
其他一切保持不变!
现在,当然,我们正在处理视频文件,所以我们首先需要从视频文件中提取音频。为此,我们将使用 ffmpeg
库,它在处理音频和视频文件时承担了大量的繁重工作。使用 ffmpeg
最常见的方式是通过命令行,我们将通过 Python 的 subprocess
模块来调用它
我们的视频处理工作流程将包括三个步骤
ffmpeg
提取音频。acapellify()
函数传入。以下是完整的 Python 代码,你可以将其添加到你的 main.py
文件中
import subprocess
def process_video(video_path):
old_audio = os.path.basename(video_path).split(".")[0] + ".m4a"
subprocess.run(['ffmpeg', '-y', '-i', video_path, '-vn', '-acodec', 'copy', old_audio])
new_audio = acapellify(old_audio)
new_video = f"acap_{video_path}"
subprocess.call(['ffmpeg', '-y', '-i', video_path, '-i', new_audio, '-map', '0:v', '-map', '1:a', '-c:v', 'copy', '-c:a', 'aac', '-strict', 'experimental', f"static/{new_video}"])
return new_video
如果你想了解所有命令行参数,可以查阅ffmpeg 文档,因为它们超出了本教程的范围。
接下来,我们将创建一个简单的 FastAPI 应用。如果你以前没有使用过 FastAPI,请查阅优秀的 FastAPI 文档。否则,我们会将这个基本模板添加到 main.py
中,它看起来会很熟悉
import os
from fastapi import FastAPI, File, UploadFile, Request
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
app = FastAPI()
os.makedirs("static", exist_ok=True)
app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")
videos = []
@app.get("/", response_class=HTMLResponse)
async def home(request: Request):
return templates.TemplateResponse(
"home.html", {"request": request, "videos": videos})
@app.post("/uploadvideo/")
async def upload_video(video: UploadFile = File(...)):
video_path = video.filename
with open(video_path, "wb+") as fp:
fp.write(video.file.read())
new_video = process_video(video.filename)
videos.append(new_video)
return RedirectResponse(url='/', status_code=303)
在这个示例中,FastAPI 应用有两个路由:/
和 /uploadvideo/
。
/
路由返回一个 HTML 模板,显示所有已上传视频的画廊。
/uploadvideo/
路由接受一个带有 UploadFile
对象的 POST
请求,该对象表示上传的视频文件。视频文件通过 process_video()
方法进行“无伴奏处理”,输出视频存储在一个列表中,该列表在内存中保存所有已上传的视频。
请注意,这是一个非常基本的示例,如果这是一个生产应用,你需要添加更多逻辑来处理文件存储、用户认证和安全方面的考虑。
最后,我们创建 Web 应用的前端。首先,我们在与 main.py
相同的目录中创建一个名为 templates
的文件夹。然后,我们在 templates
文件夹内创建一个模板文件 home.html
。以下是最终的文件结构
├── main.py
├── templates
│ └── home.html
将以下内容写入 home.html
文件
<!DOCTYPE html> <html> <head> <title>Video Gallery</title>
<style> body { font-family: sans-serif; margin: 0; padding: 0;
background-color: #f5f5f5; } h1 { text-align: center; margin-top: 30px;
margin-bottom: 20px; } .gallery { display: flex; flex-wrap: wrap;
justify-content: center; gap: 20px; padding: 20px; } .video { border: 2px solid
#ccc; box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2); border-radius: 5px; overflow:
hidden; width: 300px; margin-bottom: 20px; } .video video { width: 100%; height:
200px; } .video p { text-align: center; margin: 10px 0; } form { margin-top:
20px; text-align: center; } input[type="file"] { display: none; } .upload-btn {
display: inline-block; background-color: #3498db; color: #fff; padding: 10px
20px; font-size: 16px; border: none; border-radius: 5px; cursor: pointer; }
.upload-btn:hover { background-color: #2980b9; } .file-name { margin-left: 10px;
} </style> </head> <body> <h1>Video Gallery</h1> {% if videos %}
<div class="gallery"> {% for video in videos %} <div class="video">
<video controls> <source src="{{ url_for('static', path=video) }}"
type="video/mp4"> Your browser does not support the video tag. </video>
<p>{{ video }}</p> </div> {% endfor %} </div> {% else %} <p>No
videos uploaded yet.</p> {% endif %} <form action="/uploadvideo/"
method="post" enctype="multipart/form-data"> <label for="video-upload"
class="upload-btn">Choose video file</label> <input type="file"
name="video" id="video-upload"> <span class="file-name"></span> <button
type="submit" class="upload-btn">Upload</button> </form> <script> //
Display selected file name in the form const fileUpload =
document.getElementById("video-upload"); const fileName =
document.querySelector(".file-name"); fileUpload.addEventListener("change", (e)
=> { fileName.textContent = e.target.files[0].name; }); </script> </body>
</html>
最后,我们准备好运行我们的 FastAPI 应用了,它由 Gradio Python 客户端提供支持!
打开一个终端并导航到包含 main.py
的目录。然后在终端中运行以下命令
$ uvicorn main:app
你应该会看到类似以下的输出
Loaded as API: https://abidlabs-music-separation.hf.space ✔
INFO: Started server process [1360]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
就这样!开始上传视频,你将收到一些“无伴奏处理”后的视频作为响应(处理时间可能从几秒到几分钟不等,具体取决于视频长度)。以下是上传两个视频后 UI 的样子
如果你想了解更多关于如何在项目中Pro使用 Gradio Python 客户端的信息,请阅读专用指南。