在本篇博文中,我们将演示如何使用 gradio_client Python 库,该库使开发人员能够以编程方式向 Gradio 应用程序发出请求,方法是使用 FastAPI 创建一个端到端示例 Web 应用程序。我们将要构建的 Web 应用程序名为“Acapellify”,它允许用户上传视频文件作为输入,并返回一个没有伴奏音乐的视频版本。它还将显示一个生成的视频画廊。
先决条件
在开始之前,请确保您正在运行 Python 3.9 或更高版本,并安装了以下库
gradio_clientfastapiuvicorn您可以通过 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 令牌复制这个 Space,并创建一个只有您有权访问的私有 Space,从而绕过队列。为此,只需将上面的前两行替换为
from gradio_client import Client
client = Client.duplicate("abidlabs/music-separation", 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>最后,我们准备好运行由 Gradio Python 客户端提供支持的 FastAPI 应用程序!
打开终端并导航到包含 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 的样子

如果您想了解有关如何在项目中使用 Gradio Python 客户端的更多信息,请阅读专用指南。