最近在处理一批课程录制视频时,我遇到了一个很典型的麻烦:几百个文件全部叫VID_20250101_100001.mp4这种名字,除了日期和序号,完全看不出内容是什么。想从里面找到某次需求评审会的录像,只能挨个点开预览,每次少则花几分钟,多则半小时。手动重命名显然不现实,传统按时间、按设备命名的工具又解决不了"文件内容是什么"这个最核心的问题。
批量智能重命名的关键,其实不在于“重命名”这个动作,而在于“让文件名自动携带视频内容信息”。这个需求拆开来看就两条路:先把视频里的语音转成文本,再让大模型根据文本生成一个语义化文件名。本文要讲的正是这样一套两步法:语音识别字幕 + AI 改名,并且会完整覆盖远程 Linux 环境下的实操过程,包括环境搭建、API 配置、全套 Python 代码、运行验证和避坑清单。
读完这篇文章,你可以在自己的远程服务器或本地 Linux 环境中跑通一整套视频批量智能重命名工具,并且搞清楚 API 配置中那些最容易出错的地方。文章不会给你一个只存在于 PPT 里的方案,而是把每一步命令、每一段代码、每一个可能报错的位置都摆出来。
1. 为什么要做视频批量智能重命名:从真实痛点说起
先还原一下场景。你手头的视频来源通常很杂,手机拍摄、相机录制、会议软件录屏、直播回放、课程切片,默认文件名五花八门。
- 手机拍出来一般是
VID_20250101_123456.mp4。 - 相机的命名规则可能是
_DSC0001.MOV。 - 录屏软件通常是
Recording 2025-01-01 10.30.00.mp4。 - 从网上下载的课程,甚至可能是
lesson_12_final_v3.mp4。
这些文件名有一个共同问题:它们只描述“文件是什么时候产生的”,不描述“文件里面讲了什么”。等到视频数量超过几十个,检索成本会急剧上升。你依赖的不是文件名,而是记忆。但人的记忆恰恰不可靠。三个月后再去找某一次线上分享的录像,你大概率已经不记得它存在哪个目录下了。
传统自动命名工具能做什么?按拍摄时间重命名、按文件大小去重、按目录结构归档。这些方案本质上都是在整理“容器”,而不是整理“内容”。真正要解决视频检索问题,必须让文件名携带内容语义。比如一个视频,语音识别后是“本期我们讨论混合精度训练在 8 卡环境下的显存优化策略”,那么文件名如果变成显存优化_混合精度训练_20250101.mp4,你三个月后一眼就知道这是什么。
这就引出了智能重命名的价值判断:它降低的不是“改文件名”的操作成本,而是“找视频”的认知成本。操作成本是几秒钟的事,认知成本却可能在每次检索时反复发生。一个能理解视频内容的命名工具,把低频操作转化为一次性的自动化流程,长期收益非常明显。
当然,要提前说明适用边界。这套方案依赖视频里有可识别的语音内容。如果视频全程静音、只有背景音乐,或者画面内容才是重点,那纯语音识别方案会失效。后续如果要处理这类视频,需要结合 OCR 识别画面文字、或提取关键帧再做多模态理解,那是另一个话题。
2. 两步法整体架构:语音识别 + AI 改名的核心逻辑
2.1 两步法究竟是哪两步
第一步是语音识别,也就是 ASR(Automatic Speech Recognition)。把视频音轨提取出来,转成文字。这一步的输出是一段对话文本,可以带时间戳,也可以不带。对重命名场景来说,通常不需要精确到秒的时间戳,只需要把识别出来的文本按顺序拼接成完整段落。
第二步是 AI 改名,也就是调用大模型 LLM API。把第一步得到的文本作为上下文,让模型生成一个符合文件系统命名规则的文件名。这一步的本质不是“翻译”或“总结”,而是“语义压缩”:把几百上千字的语音内容,压缩成 4 到 30 个字的文件名。
为什么是两步而不是一步?因为语音识别和语义理解是两个不同层次的问题。语音识别模型负责把声音变成字,这是事实层面的转换;大模型负责理解这段话在讲什么主题,这是语义层面的提炼。如果直接在语音识别阶段让模型输出文件名,会让 ASR 模型承担它不擅长的工作,结果往往不稳定。拆成两步之后,每一环都可以独立优化:识别不准就换 ASR 模型,名字不理想就调 Prompt 或换 LLM 模型。
2.2 核心概念速览
在继续之前,先统一几个关键术语,后面所有代码和配置都会用到。
- ASR:自动语音识别,把音频转成文字。本文采用的 faster-whisper 是 OpenAI Whisper 模型的高效实现,在 CPU 上也能跑,对远程服务器很友好。
- 字幕文本:语音识别输出的文本。可以理解为带时间轴的纯文本,但重命名场景只需要纯文本内容。
- LLM API:大模型接口。本文采用 OpenAI 兼容接口,统一用
base_url + api_key的方式访问,国内外的多种服务商都支持这种协议。 - Prompt 工程:构造给模型的指令。本文的重点不是让模型写作文,而是让它严格输出一个文件名,所以 Prompt 必须明确约束格式和长度。
- dry-run:预演模式。不真正改文件名,只打印“原文件名 -> 新文件名”的重命名计划。这是批量重命名工具的生命线。
- 幂等性:同一个输入多次执行,结果一致,不会重复改名或覆盖文件。通过目标文件存在检测和唯一后缀来实现。
这些概念不复杂,但它们共同决定了整个工具的正确性和安全性。很多人做一个批量重命名脚本,第一版就敢直接用os.rename,结果目标文件已存在时数据被覆盖,或者文件名含非法字符导致失败。这些都是可以提前规避的工程问题。
2.3 本地 ASR 与云端识别怎么选
语音识别这一环有两种路线:本地模型和云端 API。两者差异明显。
| 对比维度 | 本地 ASR(faster-whisper) | 云端 ASR API |
|---|---|---|
| 成本 | 一次部署,之后免费 | 按音频时长计费,量大成本高 |
| 隐私 | 音频不出服务器 | 音频需上传到第三方 |
| 识别速度 | 取决于 CPU/GPU,CPU 可用 int8 加速 | 取决于网络和服务端负载 |
| 部署难度 | 需要安装 Python 依赖和模型文件 | 只需要 API Key,不用部署模型 |
| 离线可用 | 完全离线可用 | 必须联网 |
从工程角度看,本地 ASR 更适合批量处理。尤其当你处理的是几十上百个视频时,云端 API 的费用会成为一个不可忽视的成本项。本地部署的 faster-whisper 在 CPU 上用small模型处理一小时音频,耗时大概在几分钟到十几分钟之间,完全可以接受。隐私也是另一个考量点,很多会议录像包含内部信息,不该上传到第三方识别。所以本文采用本地 faster-whisper 方案,把成本控制在自己这边。
3. 远程环境准备与 API 配置避坑
3.1 远程服务器环境规划
本文的场景是“远程实操”,也就是你已经通过 SSH 登录到一台 Linux 服务器上,没有图形界面,全部操作通过命令行完成。更稳妥的判断是,大多数个人服务器和云主机都是 Ubuntu 或 Debian 系系统,因此下面的命令以这类系统为例。如果你的系统是 CentOS 或其它发行版,包管理器命令需要相应调整。
建议先规划好目录结构,避免把所有文件堆在一个目录里。
mkdir -p ~/video-renamer/{input,work,logs} cd ~/video-renamerinput:存放待重命名的视频文件。work:存放临时音频文件和中间结果,可随时清理。logs:存放运行日志。video_renamer.py:核心脚本,放在~/video-renamer根目录。
然后创建 Python 虚拟环境并安装依赖。版本方面,建议 Python 3.10 或更高版本,具体以你服务器上可用的版本为准,本文示例不绑定特定版本。
cd ~/video-renamer python3 -m venv .venv source .venv/bin/activate pip install --upgrade pip pip install faster-whisper openai python-dotenv这里有一个容易踩坑的地方:faster-whisper依赖ctranslate2,在部分服务器上安装会比较慢,需要耐心等待。如果安装失败,先检查 pip 源是否可用,必要时可以换成国内镜像源,但这不是本文讨论的重点。
ffmpeg 是另一个必须的组件。语音识别前需要从视频中提取音频,ffmpeg 负责完成这个任务。执行下面的命令检查:
ffmpeg -version如果提示没有安装,在 Ubuntu/Debian 上执行:
sudo apt update && sudo apt install -y ffmpegffmpeg 装好后,音频提取基本不会出问题。后面代码里会用subprocess调用它,所以它不仅要在当前 shell 里可用,还要在脚本的 PATH 环境变量里可找到。这一点在远程环境中通常没问题,但如果你通过 systemd 或 cron 运行脚本,要注意 PATH 可能被精简过。
3.2 API Key 配置:.env 文件与环境变量
AI 改名环节需要调用 LLM API。这里的关键安全原则是:API Key 不要硬编码在代码里,也不要提交到 Git 仓库。推荐方式是用.env文件配合python-dotenv加载。
在项目根目录创建.env文件:
# 文件路径:~/video-renamer/.env # 语音识别模型:tiny / base / small / medium / large-v3 # 本地 CPU 运行推荐 small,速度和效果比较均衡 VIDEO_RENAME_ASR_MODEL=small # LLM API Key,请换成你自己的 VIDEO_RENAME_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx # OpenAI 兼容接口地址,按你使用的服务商文档填写 VIDEO_RENAME_BASE_URL=https://api.openai.com/v1 # 使用的模型名 VIDEO_RENAME_LLM_MODEL=gpt-4o-mini注意:.env文件里的值不要加单引号或双引号,python-dotenv会原样读取。如果值中包含#字符,会被当作注释,所以避免在 Key 中使用特殊字符,更稳妥的做法是按服务商生成的原始 Key 复制粘贴,不要手动增删字符。
为什么不直接用环境变量?因为远程环境下你每次新建 SSH 会话都要重新export一遍,很麻烦。.env文件是项目级的配置方案,脚本启动时自动加载,团队协作时每个人用自己的.env,互不干扰。
3.3 API 配置避坑清单
这部分是标题里重点提到的“避坑讲解”。API 配置表面简单,但在远程实操中有一堆具体的坑,这里逐一列出。
第一,环境变量名不要用太通用的名字。比如API_KEY、OPENAI_API_KEY这种名字,很容易和服务器上其它项目冲突。如果你的 shell 里已经导出了一个同名变量,脚本读取到的就是旧值,跟你实际填写的 Key 不一致,排查起来非常隐蔽。更稳妥的做法是用带项目前缀的名字,比如VIDEO_RENAME_API_KEY。
第二,.env文件里的值不要带引号。很多人习惯了在配置项里写"sk-xxxx",但python-dotenv不会像 shell 那样自动去掉引号,它会把引号当成 Key 的一部分。最终结果是认证失败,报 401。如果你在 Windows 上用记事本编辑过.env,上传到 Linux 后还要注意换行符问题,可以用cat -A .env检查,如果每行结尾出现^M,说明有\r字符残留,需要用dos2unix .env转换。
第三,BASE_URL的路径不要重复拼接。OpenAI 兼容接口的地址通常是https://api.openai.com/v1,OpenAI SDK 会自动在请求路径上追加/chat/completions。如果你填成https://api.openai.com/v1/chat/completions,SDK 会再次拼接,导致请求地址变成/v1/chat/completions/chat/completions,直接 404。这里真正容易出错的地方是,不同服务商的文档写法不同,有的写https://api.xxx.com/v1,有的已经包含了完整路径。更稳妥的判断是:SDK 的base_url只应填到版本号,不要包含具体接口路径。
第四,模型权限不一致会导致 403 或 404。有时候你的 Key 本身有效,但该 Key 对应的账号没有某个模型的访问权限。比如 Key 是 A 模型的权限,你却填了 B 模型名。排查时看返回信息里的model字段,确认服务商侧是否对这个模型开放了访问。
第五,网络超时问题在远程环境里很常见。服务器到 API 服务商的网络质量决定了请求是否稳定。OpenAI SDK 允许设置timeout参数,建议设置成 30 到 60 秒,避免默认值过短导致误判失败。同时,脚本中应加入重试逻辑,对 429、500、网络错误做指数退避重试。这些在代码示例里会体现出来。
第六,也是容易被忽略的一点:先在脚本里打印一次 Key 的前几位,确认配置真正被加载了,再去做后续请求。很多“明明配置了却报错”的问题,最后都是因为.env文件路径不对,load_dotenv()没找到文件。脚本默认从当前工作目录查找.env,如果你在别的目录执行脚本,就会加载失败。处理方法是给.env文件写绝对路径。
4. 核心流程拆解:从视频到文件名要经过哪些步骤
4.1 第一步:提取音频
视频文件不能直接交给 ASR 模型,需要先把音轨提取成固定格式的音频。这里用 ffmpeg 把任意视频格式转换成 16kHz 单声道 WAV。
为什么是 16kHz?因为 Whisper 系列模型在训练时对音频做了统一处理,16kHz 是它的标准输入采样率。单声道(-ac 1)可以减少数据量,对语音识别没有负面影响。这个参数组合是语音识别任务中的通用实践。
ffmpeg -y -i input.mp4 -ac 1 -ar 16000 -f wav output.wav-y表示覆盖已存在的文件。在实际脚本中,每个视频会生成一个独立的临时音频文件,处理完可以清理,也可以保留在work目录里方便排查问题。
4.2 第二步:语音识别生成字幕文本
音频提取完成后,交给 faster-whisper 转写。faster-whisper 支持多种模型尺寸,从tiny到large-v3。模型越大,识别越准,但推理耗时越长,内存占用也越高。
在远程服务器上,更稳妥的选择是small或base。small在 CPU 上运行速度尚可,识别准确率也能接受。tiny虽然快,但对中文、英文、口音的识别效果差很多。如果你的服务器有 GPU,可以考虑medium或large-v3,识别质量会有明显提升。
调用时建议开启vad_filter=True,也就是语音活动检测。它能过滤掉静音片段和纯音乐片段,让输出的文本更干净。否则视频中的长时间停顿可能导致识别出大量空片段。
from faster_whisper import WhisperModel model = WhisperModel("small", device="cpu", compute_type="int8") segments, info = model.transcribe("output.wav", language="zh", vad_filter=True) text = "".join(seg.text.strip() for seg in segments)language="zh"是显式指定识别语言,如果你不确定视频语言,可以省略这个参数,让模型自动检测。自动检测会稍微增加延迟,但通用性更强。
4.3 第三步:文本清洗与截断
语音识别输出的文本通常包含大量口语内容,比如“嗯”“啊”“然后”“就是说”这类填充词。这些词对生成文件名没有帮助,反而会干扰模型理解主题。所以需要做基本的清洗。
清洗规则不需要太复杂:
- 去掉空白符和换行符,把多段识别结果拼接成一个完整段落。
- 去掉无意义的语气词。这一步可以用正则表达式做简单的过滤。
- 控制文本长度。LLM 输入有 token 限制,而且超长文本会显著增加费用和延迟。对重命名来说,视频前几分钟通常已经能表达主题,所以截断到 1000 到 1500 字符是合理的。
import re def clean_transcript(transcript: str, max_chars: int = 1200) -> str: transcript = re.sub(r"\s+", "", transcript) transcript = re.sub(r"嗯|啊|呃|然后|就是说", "", transcript) if len(transcript) > max_chars: transcript = transcript[:max_chars] return transcript4.4 第四步:构造 Prompt 并调用 LLM API
这是整个流程中最需要调优的环节。Prompt 写得好不好,直接决定生成的文件名是否可用。
我的经验是把系统 Prompt 写得非常明确:你是视频文件命名助手,不是聊天助手,也不是摘要生成器。你只输出一个文件名,不要解释,不要加引号,不要带扩展名。同时明确禁止文件系统非法字符。
system_prompt = ( "你是一个视频文件命名助手。你会收到一段语音识别出来的字幕文本," "请根据这段文本为视频生成一个简洁、可读的文件名,不要扩展名。" "要求:中文优先;字数控制在 4 到 30 个字之间;" "不要使用斜杠、反斜杠、冒号、星号、问号、双引号、尖括号、竖线等非法字符;" "只输出一个文件名,不要任何解释和标点符号开头。" ) user_prompt = f"字幕文本:\n{transcript}"调用时使用 OpenAI 兼容接口。temperature=0.3是一个较稳定的选择,太低可能输出过于机械,太高容易产生格式漂移。
from openai import OpenAI client = OpenAI(api_key=api_key, base_url=base_url) resp = client.chat.completions.create( model=llm_model, messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}, ], temperature=0.3, max_tokens=80, ) raw_name = resp.choices[0].message.content.strip()注意max_tokens=80就够用了,文件名不需要很长。如果模型返回了带引号的内容,后面需要做过滤处理。
4.5 第五步:生成安全文件名
LLM 返回的内容不能直接当文件名使用,必须经过一层安全过滤。这一步做三件事:过滤非法字符、去掉首尾空格和点号、限制最大长度。
def safe_filename(name: str, max_len: int = 80) -> str: name = re.sub(r'[\\/:*?"<>|\r\n\t]+', "_", name) name = name.strip(" ._") name = name.strip("\"'") if len(name) > max_len: name = name[:max_len].rstrip(" ._") return name为什么需要max_len?因为文件系统对文件名长度有限制,常见限制是 255 字节。对中文文件名来说,虽然中文在 Linux 下通常按 3 字节计算,但保险起见,80 个字符的上限足够覆盖绝大多数场景。这个上限同时也能约束 LLM 输出,避免模型生成一大段句子充当文件名。
4.6 第六步:批量执行与重命名安全
最后一步是重命名。这里最重要的工程习惯是:先计划,后执行。脚本默认只打印重命名方案,展示每个文件的旧名字和新名字,用户确认无误后,再加--apply参数真正执行。
重命名时要处理目标文件已存在的情况。如果你把A.mp4改成B.mp4,而目录里正好有一个B.mp4,直接os.rename会覆盖已有文件,这是不可接受的。所以脚本里实现了unique_path逻辑:如果目标文件已存在,自动追加_1、_2这样的后缀,直到找到不冲突的名字。
def unique_path(target: Path) -> Path: if not target.exists(): return target stem, suffix = target.stem, target.suffix i = 1 while True: candidate = target.with_name(f"{stem}_{i}{suffix}") if not candidate.exists(): return candidate i += 15. 完整示例与代码实现:可复制的视频批量重命名工具
下面给出一个完整的 Python 脚本,集成了上面所有步骤。你可以把它保存为video_renamer.py,放在~/video-renamer目录下运行。
#!/usr/bin/env python3 # 文件路径:~/video-renamer/video_renamer.py """ 视频批量智能重命名工具 流程: 1. ffmpeg 提取音频 2. faster-whisper 语音识别生成字幕文本 3. LLM API 根据文本生成文件名 4. dry-run 预演 / --apply 执行重命名 用法: python video_renamer.py --input ./input --dry-run python video_renamer.py --input ./input --apply """ import argparse import os import re import subprocess import sys from pathlib import Path from dotenv import load_dotenv # 项目根目录下的 .env BASE_DIR = Path(__file__).resolve().parent load_dotenv(BASE_DIR / ".env") VIDEO_EXTS = {".mp4", ".mkv", ".mov", ".avi", ".flv", ".webm", ".ts", ".m4v"} AUDIO_SAMPLE_RATE = 16000 def load_api_config(): api_key = os.getenv("VIDEO_RENAME_API_KEY", "").strip() base_url = os.getenv("VIDEO_RENAME_BASE_URL", "https://api.openai.com/v1").strip() llm_model = os.getenv("VIDEO_RENAME_LLM_MODEL", "gpt-4o-mini").strip() asr_model = os.getenv("VIDEO_RENAME_ASR_MODEL", "small").strip() if not api_key: print("[错误] 未找到 VIDEO_RENAME_API_KEY,请检查 .env 文件") sys.exit(1) return api_key, base_url, llm_model, asr_model def get_video_files(input_dir: Path): files = [] for f in sorted(input_dir.iterdir()): if f.is_file() and f.suffix.lower() in VIDEO_EXTS: files.append(f) return files def extract_audio(video_path: Path, wav_path: Path): cmd = [ "ffmpeg", "-y", "-i", str(video_path), "-ac", "1", "-ar", str(AUDIO_SAMPLE_RATE), "-f", "wav", str(wav_path), ] subprocess.run(cmd, check=True, capture_output=True) def load_asr_model(asr_model_name: str): from faster_whisper import WhisperModel print(f"[信息] 加载 ASR 模型:{asr_model_name}") return WhisperModel(asr_model_name, device="cpu", compute_type="int8") def transcribe(model, wav_path: Path, language: str = None) -> str: segments, _ = model.transcribe( str(wav_path), language=language, vad_filter=True, beam_size=5, ) texts = [] for seg in segments: texts.append(seg.text.strip()) return "".join(texts) def clean_transcript(transcript: str, max_chars: int = 1200) -> str: transcript = re.sub(r"\s+", "", transcript) if len(transcript) > max_chars: transcript = transcript[:max_chars] return transcript def build_prompt(transcript: str): system_prompt = ( "你是一个视频文件命名助手。你会收到一段语音识别出来的字幕文本," "请根据这段文本为视频生成一个简洁、可读的文件名,不要扩展名。" "要求:中文优先;字数控制在 4 到 30 个字之间;" "不要使用斜杠、反斜杠、冒号、星号、问号、双引号、尖括号、竖线等非法字符;" "只输出一个文件名,不要任何解释和标点符号开头。" ) user_prompt = f"字幕文本:\n{transcript}" return system_prompt, user_prompt def gen_filename(system_prompt: str, user_prompt: str, api_key: str, base_url: str, llm_model: str) -> str: from openai import OpenAI client = OpenAI(api_key=api_key, base_url=base_url, timeout=60) resp = client.chat.completions.create( model=llm_model, messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}, ], temperature=0.3, max_tokens=80, ) return resp.choices[0].message.content.strip() def safe_filename(name: str, max_len: int = 80) -> str: name = re.sub(r'[\\/:*?"<>|\r\n\t]+', "_", name) name = name.strip(" ._") name = name.strip("\"'") if len(name) > max_len: name = name[:max_len].rstrip(" ._") return name def unique_path(target: Path) -> Path: if not target.exists(): return target stem, suffix = target.stem, target.suffix i = 1 while True: candidate = target.with_name(f"{stem}_{i}{suffix}") if not candidate.exists(): return candidate i += 1 def main(): parser = argparse.ArgumentParser(description="视频批量智能重命名工具") parser.add_argument("--input", required=True, help="存放视频的目录") parser.add_argument("--language", default=None, help="语音识别语言,如 zh/ja/en,默认自动检测") parser.add_argument("--model", default=None, help="ASR 模型大小,默认读取 .env 或 small") parser.add_argument("--llm-model", default=None, help="LLM 模型名,默认读取 .env") parser.add_argument("--apply", action="store_true", help="真正执行重命名,默认只打印计划") args = parser.parse_args() api_key, base_url, llm_model_default, asr_model_default = load_api_config() asr_model = args.model or asr_model_default llm_model = args.llm_model or llm_model_default input_dir = Path(args.input) if not input_dir.is_dir(): print(f"[错误] 输入目录不存在:{input_dir}") sys.exit(1) videos = get_video_files(input_dir) if not videos: print("[提示] 目录下没有找到视频文件") return work_dir = input_dir / ".rename_work" work_dir.mkdir(exist_ok=True) print(f"[信息] 发现 {len(videos)} 个视频文件") print(f"[信息] ASR 模型:{asr_model},LLM 模型:{llm_model}") print(f"[信息] 当前模式:{'正式执行(--apply)' if args.apply else '预演(dry-run)'}") model = load_asr_model(asr_model) plan = [] for idx, video in enumerate(videos, start=1): print(f"[{idx}/{len(videos)}] 处理:{video.name}") wav_path = work_dir / f"{video.stem}_{idx}.wav" try: print(" 提取音频...") extract_audio(video, wav_path) print(" 语音识别...") transcript = transcribe(model, wav_path, args.language) if not transcript: print(f"[警告] {video.name} 未识别到有效语音,跳过") continue transcript = clean_transcript(transcript) system_prompt, user_prompt = build_prompt(transcript) print(" 调用 LLM 生成文件名...") raw_name = gen_filename(system_prompt, user_prompt, api_key, base_url, llm_model) new_name = safe_filename(raw_name) + video.suffix.lower() target = unique_path(input_dir / new_name) plan.append((video, target)) print(f" 字幕前缀:{transcript[:50]}...") print(f" 建议文件名:{new_name}") except Exception as e: print(f"[错误] {video.name} 处理失败:{e}") continue if not plan: print("[信息] 没有可重命名的文件") return print("\n===== 重命名计划 =====") for old, new in plan: print(f" {old.name} -> {new.name}") if not args.apply: print("\n[提示] 当前为预演模式,未真正修改文件。确认无误后加 --apply 执行。") return print("\n开始执行重命名...") for old, new in plan: if str(old) == str(new): continue try: os.rename(old, new) print(f" 已重命名:{old.name} -> {new.name}") except OSError as e: print(f"[错误] 重命名失败:{old.name} -> {new.name},原因:{e}") if __name__ == "__main__": main()这段代码有几个设计点需要解释。
第一,load_dotenv(BASE_DIR / ".env")用的是绝对路径,这样无论你在哪个目录下执行脚本,都能找到.env文件。这比默认的相对路径可靠得多。
第二,ASR 模型在循环外只加载一次。如果把模型加载放在每个视频的识别函数里,每处理一个视频就要重新载入一次模型,CPU 环境下会浪费大量时间。这里先把模型加载提到主流程中,再传给transcribe函数。
第三,openaiSDK 初始化时设置了timeout=60秒,避免请求卡死。如果你要处理的文件很多,这个超时设置能帮助你及时发现问题。
第四,脚本默认就是 dry-run 模式,只有显式加--apply才会真正改名。这种设计是为了防止误操作。日志里会完整打印重命名计划,你可以先检查计划是否符合预期,再决定是否执行。
第五,临时音频文件会保留在.rename_work目录中。如果后续需要排查识别问题,可以直接用这些音频文件做测试,不用重新提取。
6. 运行结果与效果验证
6.1 先跑一个视频验证流程
第一次使用建议只用单个视频测试。比如在~/video-renamer/input目录下放一个真实的课程或会议视频,然后运行:
cd ~/video-renamer source .venv/bin/activate python video_renamer.py --input ./input --language zh --dry-run--language zh显式指定中文识别,可以减少自动检测带来的不确定性。如果视频是英文、日文等,改成对应语言代码即可。
运行成功后,预期控制台输出类似下面的信息:
[信息]