你的浏览器不支持canvas

做你害怕做的事情,然后你会发现,不过如此。

Docker部署FunClip并提供API服务

时间: 作者: 黄运鑫

本文章属原创文章,未经作者许可,禁止转载,复制,下载,以及用作商业用途。原作者保留所有解释权。


下载源代码

  • 1.浏览器访问GitHub地址https://github.com/modelscope/FunClip,下载源代码
  • 2.将代码上传到服务器,我的代码根目录为/home/deploy/FunClip

构建镜像

  • /home/deploy下创建文件Dockerfile,内容如下:
FROM python:3.11

WORKDIR /app

RUN apt-get update && apt-get install -y \
    git \
    imagemagick \
    ffmpeg \
    wget \
    && rm -rf /var/lib/apt/lists/*

RUN sed -i '/<policy domain="path" rights="none" pattern="@\*"/d' /etc/ImageMagick-6/policy.xml

COPY FunClip ./

RUN chmod 777 ./

RUN pip install --no-cache-dir -r ./requirements.txt -i https://mirrors.aliyun.com/pypi/simple/

EXPOSE 7860

CMD ["python", "funclip/launch.py"]
  • /home/deploy目录下执行docker build -t fun-clip:1.0 .构建镜像

启动可视化页面

  • 镜像构建完成后启动容器,命令如下:
docker run --restart always -d \
--gpus=all \
-e TZ=Asia/Shanghai \
-p 7865:7860 \
-p 7866:5000 \
-v $(pwd)/volumes/modelscope:/root/.cache/modelscope \
-v $(pwd)/FunClip/funclip:/app/funclip \
--name fun-clip fun-clip:1.0 \
/bin/bash -c "python funclip/launch.py --listen"
  • 执行docker logs fun-clip -f命令查看容器日志,第一次启动会在modelscope目录下载模型文件,需要等待一段时间。
  • 等到出现WARNING:root:Initializing VideoClipper.后说明启动成功。
  • 浏览器访问http://ip:7865

提供API服务

  • /home/deploy/FunClip/funclip目录下创建api.py文件,实现将音视频转字幕的接口:
from flask import Flask, request, jsonify
import uuid
import os
import requests

app = Flask(__name__)

UPLOAD_FOLDER = './uploads'
OUTPUT_FOLDER = './output'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['OUTPUT_FOLDER'] = OUTPUT_FOLDER

os.makedirs(UPLOAD_FOLDER, exist_ok=True)
os.makedirs(OUTPUT_FOLDER, exist_ok=True)

@app.route('/process', methods=['POST'])
def process_file():
    # 使用form接收所有参数
    file_url = request.form.get('fileUrl')
    sd_switch = request.form.get('sdSwitch', 'no')  # 从form表单获取sd_switch参数 no或yes

    # 接收到参数
    print(f'接收到参数')
    print(f'fileUrl: {file_url}')
    print(f'sd_switch: {sd_switch}')

    # 检查是否有文件上传
    if 'file' in request.files:
        file = request.files['file']

        # 生成UUID标识本次处理任务
        job_id = str(uuid.uuid4())
        job_output_dir = os.path.join(app.config['OUTPUT_FOLDER'], job_id)
        os.makedirs(job_output_dir, exist_ok=True)

        # 保存上传的文件
        filename = f'{job_id}{os.path.splitext(file.filename)[1]}'
        file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
        file.save(file_path)

        file_to_process = file_path
        print(f'文件已保存到: {file_path}')  # 打印文件保存路径日志
    else:
        # 没有提供file参数,使用fileUrl下载文件
        if not file_url:
            return jsonify({'error': 'No file or fileUrl provided'}), 400

        # Generate UUID for this processing job
        job_id = str(uuid.uuid4())
        job_output_dir = os.path.join(app.config['OUTPUT_FOLDER'], job_id)
        os.makedirs(job_output_dir, exist_ok=True)

        # Handle URL download
        try:
            response = requests.get(file_url, stream=True)
            response.raise_for_status()

            # 从URL获取文件扩展名,默认为.tmp
            content_type = response.headers.get('content-type', '')
            extension = '.tmp'
            if 'video/' in content_type:
                extension = f'.{content_type.split("/")[-1]}'

            local_path = os.path.join(app.config['UPLOAD_FOLDER'], f'{job_id}{extension}')

            with open(local_path, 'wb') as f:
                for chunk in response.iter_content(chunk_size=8192):
                    if chunk:
                        f.write(chunk)

            file_to_process = local_path
            print(f'文件已下载到: {local_path}')  # 打印文件下载路径日志
        except Exception as e:
            return jsonify({'error': f'File download failed: {str(e)}'}), 500

    # 执行命令
    # Call videoclipper.py with stage 1 processing
    cmd = f'python /app/funclip/videoclipper.py --stage 1 --file "{file_to_process}" --output_dir "{job_output_dir}" --sd_switch "{sd_switch}"'

    try:
        # 执行命令调用videoclipper.py进行阶段1处理
        result = os.system(cmd)
        if result != 0:
            print(f'命令执行失败: {cmd}')  # 打印命令执行失败日志
            return jsonify({'error': 'Command execution failed'}), 500

        # 读取total.srt内容
        srt_path = os.path.join(job_output_dir, 'total.srt')
        print(f'识别结果保存在: {srt_path}')  # 打印识别结果保存路径日志
        with open(srt_path, 'r') as f:
            srt_content = f.read()

        # 返回成功响应
        return jsonify({
            'job_id': job_id,
            'status': 'success',
            'output_directory': job_output_dir,
            'srt_content': srt_content
        })

    except Exception as e:
        # 返回错误响应
        return jsonify({'error': f'Processing failed: {str(e)}'}), 500

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=5000)
  • 启动API服务
docker run --restart always -d \
--gpus=all \
-e TZ=Asia/Shanghai \
-p 7865:7860 \
-p 7866:5000 \
-v $(pwd)/volumes/modelscope:/root/.cache/modelscope \
-v $(pwd)/FunClip/funclip:/app/funclip \
--name fun-clip fun-clip:1.0 \
/bin/bash -c "python funclip/api.py"
  • 通过POST请求ip:7866/process接口,form-data中传入file参数,即可获取音视频的字幕结果。
  • 我这个版本的源代码sdSwitch参数传入yes后没有区分发音人,是因为videoclipper.py文件的代码有bug,修改如下:
源代码:
if sd_switch == 'Yes':
改为:
if sd_switch == 'yes':

对于本文内容有问题或建议的小伙伴,欢迎在文章底部留言交流讨论。