可以在YouTube下载视频,下载字幕,以及需要文字转音频的一些代码,自己写的,目前也是能实现一点小需求~
是需要下载FFmpeg、yt-dlp.exe、chrome_cookies插件,需要下载的自行search,不再赘述
人机验证
需要在插件处点击,下载cookies文件,时间间隔较长会导致文件失效,把之前下载的文件删除再重新下载就可以了
下载视频
路径如上图
单独写了一个bat文件
也可以直接在终端写命令
(base) PS D:\tool\video> .\vdownlod.bat https://www.youtube.com/watch?v=b7ZC01afWPw(base) D:\tool\video>cd /d D:\tool\video\ (base) D:\tool\video>yt-dlp -f bv[ext=mp4]+ba[ext=m4a] --cookies d:\Downloads\www.youtube.com_cookies.txt -S codec:h264:m4a "https://www.youtube.com/watch?v=b7ZC01afWPw"
[youtube] Extracting URL: https://www.youtube.com/watch?v=b7ZC01afWPw
[youtube] b7ZC01afWPw: Downloading webpage
[youtube] b7ZC01afWPw: Downloading ios player API JSON
[youtube] b7ZC01afWPw: Downloading mweb player API JSON
[youtube] b7ZC01afWPw: Downloading m3u8 information
[info] b7ZC01afWPw: Downloading 1 format(s): 137+140
[download] Destination: All 60+ Adobe apps explained in 9 minutes [b7ZC01afWPw].f137.mp4
[download] 100% of 74.83MiB in 00:00:37 at 2.01MiB/s
[download] Destination: All 60+ Adobe apps explained in 9 minutes [b7ZC01afWPw].f140.m4a
[download] 100% of 9.56MiB in 00:00:03 at 2.73MiB/s
[Merger] Merging formats into "All 60+ Adobe apps explained in 9 minutes [b7ZC01afWPw].mp4"
Deleting original file All 60+ Adobe apps explained in 9 minutes [b7ZC01afWPw].f137.mp4 (pass -k to keep)
Deleting original file All 60+ Adobe apps explained in 9 minutes [b7ZC01afWPw].f140.m4a (pass -k to keep)
使用 yt-dlp
下载 YouTube 视频,并将视频和音频合并为一个 MP4 文件。下面是命令的具体操作步骤及输出信息的解释:
-
命令结构:
yt-dlp
是一个用于下载视频和音频的工具。-f bv[ext=mp4]+ba[ext=m4a]
指定下载视频格式为 MP4(bv
)和音频格式为 M4A(ba
)。--cookies d:\Downloads\www.youtube.com_cookies.txt
是用来提供 Cookie 文件,帮助绕过一些限制(如地理限制)。-S codec:h264:m4a
指定编码器。
-
下载过程:
- Extracting URL:提取给定的 YouTube 视频链接。
- Downloading webpage:下载视频网页的信息。
- Downloading m3u8 information:获取视频流的信息。
- Downloading format(s):下载指定格式的视频和音频流。
-
下载结果:
- 输出显示分别下载了视频(
f137.mp4
)和音频(f140.m4a
)文件。 - 下载完成后,
yt-dlp
会将这两个文件合并为一个完整的视频文件(All 60+ Adobe apps explained in 9 minutes.mp4
)。
- 输出显示分别下载了视频(
-
删除原始文件:
下载完成后,原始的视频和音频文件会被删除,除非你使用-k
参数来保留它们。
下载字幕
搜索字幕,先确定你要的字幕在不在列表
# 搜索某指定字幕,并打印相关信息import subprocessdef run_command_real_time(command):saveout = ''try:# 使用 Popen 执行命令process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)# 实时读取标准输出和标准错误while True:output = process.stdout.readline() # 逐行读取输出if output == '' and process.poll() is not None:breakif output:print(output.strip()) # 打印输出saveout += output.strip() + '\n'# 检查命令的返回值return_code = process.poll()if return_code != 0:error_output = process.stderr.read()print(f"命令执行失败,返回码: {return_code}")print(f"错误信息: {error_output.strip()}")except Exception as e:print(f"发生错误: {e}")return saveoutif __name__ == "__main__":command = ("yt-dlp --list-subs ""--proxy http://127.0.0.1:10809 ""--cookies D:\Downloads\www.youtube.com_cookies.txt ""https://www.youtube.com/watch?v=b7ZC01afWPw")# --list-subs 列出每个视频的可用字幕。 Simulate unless --no-simulate is used# --proxy 使用指定的HTTP/HTTPS/SOCKS代理。res = run_command_real_time(command)reslines = res.split('\n')ch = ''for i, v in enumerate(reslines):if 'Chinese (Simplified)' in v:ch = vprint('-'*10)print(ch)
下载能搜到的你想要的字幕格式
#下载字幕import subprocess # 导入 subprocess 模块以执行外部命令
import os # 导入 os 模块以处理文件和目录
import glob # 导入 glob 模块以查找符合特定规则的文件路径名# 配置部分
PROXY = "http://127.0.0.1:10809" # 设置代理
COOKIES_FILE = r"D:\Downloads\www.youtube.com_cookies.txt" # 指定 cookies 文件
VIDEO_URL = "https://www.youtube.com/watch?v=b7ZC01afWPw" # 视频链接
OUTPUT_FILE = 'output_zh-Hans.json' # 设置目标文件名def run_command_real_time(command):saveout = '' # 初始化保存输出的字符串try:process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) # 启动外部命令并允许获取标准输出和错误while True: # 持续读取输出output = process.stdout.readline() # 逐行读取输出if output == '' and process.poll() is not None: # 如果没有输出且进程已结束,则退出循环breakif output: # 如果有输出print(output.strip()) # 打印当前输出行saveout += output.strip() + '\n' # 将输出保存到字符串中return_code = process.poll() # 获取命令的返回值if return_code != 0: # 如果返回值不为0,表示命令执行失败error_output = process.stderr.read() # 读取标准错误输出print(f"命令执行失败,返回码: {return_code}") # 打印返回码print(f"错误信息: {error_output.strip()}") # 打印错误信息except Exception as e: # 捕获异常print(f"发生错误: {e}") # 打印错误信息return saveout # 返回保存的输出if __name__ == "__main__": # 如果是主程序list_command = [ # 定义列出字幕的命令"yt-dlp","--list-subs","--proxy", PROXY, # 使用配置的代理"--cookies", COOKIES_FILE, # 使用配置的 cookies 文件VIDEO_URL # 使用配置的视频链接]res = run_command_real_time(list_command) # 执行命令并获取输出reslines = res.split('\n') # 将输出按行分割成列表ch = '' # 初始化用于存储字幕信息的变量for i, v in enumerate(reslines): # 遍历每行输出if 'Chinese (Simplified)' in v: # 查找包含简体中文的行ch = v # 保存找到的行print('-' * 20) print(ch) # 打印找到的简体中文字幕信息print('-' * 20) if True: # 始终为真,执行以下代码download_command = [ # 定义下载字幕的命令"yt-dlp","--skip-download", # 跳过视频下载"--write-auto-subs", # 写入自动生成的字幕"--sub-langs", "zh-Hans", # 设置下载的字幕语言为简体中文"--sub-format", "json3", # 设置字幕格式为 json3"--proxy", PROXY, # 使用配置的代理"--cookies", COOKIES_FILE, # 使用配置的 cookies 文件VIDEO_URL # 使用配置的视频链接]download_result = run_command_real_time(download_command) # 执行下载命令并获取输出downloaded_files = glob.glob('*.zh-Hans.json3') # 查找当前目录下所有符合条件的文件if downloaded_files: # 如果找到了文件original_file = downloaded_files[0] # 假设只下载一个文件,取第一个os.rename(original_file, OUTPUT_FILE) # 重命名下载的文件print(f"下载并保存的文件名: {OUTPUT_FILE}") # 打印保存的文件名if os.path.exists(OUTPUT_FILE): # 检查文件是否存在print(f"文件已保存为: {OUTPUT_FILE}") # 打印保存的文件路径else:print("下载失败,未能保存文件。") # 打印失败信息else:print("没有找到下载的字幕文件。") # 如果没有找到文件,打印信息
以json格式为例
# json字幕解析import json # 导入 json 模块以处理 JSON 数据# 文件配置
input_filename = 'en.json3' # 输入 JSON 文件名
output_subtitles_file = 'subtitles_output.txt' # 输出字幕信息文件名
output_texts_file = 'Subtitlesn.txt' # 输出字幕文本文件名def getTxt(dat):out = '' # 初始化一个空字符串,用于存储所有字幕文本datlist = [] # 初始化一个空列表,用于存储时间偏移和字幕文本for i, v in enumerate(dat): # 遍历字幕片段列表tstr = v['utf8'] # 获取当前字幕文本out += tstr # 将字幕文本添加到总字符串中offset = 0 # 初始化时间偏移if i != 0:offset = v['tOffsetMs'] # 如果不是第一个字幕片段,获取其时间偏移datlist.append([offset, tstr]) # 将时间偏移和字幕文本添加到列表中return [out, datlist] # 返回包含总字幕文本和时间偏移信息的列表def decodeJosn(fname):jdict = None # 初始化字典with open(fname, 'rb') as f: # 以二进制模式打开文件jdict = json.load(f) # 读取并解析 JSON 数据subtxts = jdict['events'] # 提取字幕事件列表alltime = 0 # 初始化总时间dats = {} # 初始化字典以存储字幕事件信息outstrs = {} # 初始化字典以存储非空字幕文本for i, v in enumerate(subtxts): # 遍历每个字幕事件if i == 0:alltime = v['dDurationMs'] # 记录第一个事件的持续时间else:st = v['tStartMs'] # 获取开始时间dt = 0 # 初始化持续时间if 'dDurationMs' in v:dt = v['dDurationMs'] # 如果有持续时间,则获取if 'segs' in v: # 如果有字幕片段txtdat = getTxt(v['segs']) # 调用函数获取字幕文本和时间偏移dats[i] = [st, dt, txtdat[0], txtdat[1]] # 存储事件相关信息if txtdat[0] != '\n' and len(txtdat[0]) > 0: # 如果字幕文本不为空outstrs[i] = [st, dt, txtdat[0]] # 存储非空字幕文本return dats, outstrs # 返回两个字典dats, outstrs = decodeJosn(input_filename) # 读取指定 JSON 文件并获取字幕数据with open(output_subtitles_file, 'w', encoding='utf-8') as f: # 打开文件以写入字幕信息for k, v in outstrs.items(): # 遍历字幕字典f.write(f"{k}: {v}\n") # 将事件索引和信息写入文件
print(f"字幕信息已保存到 '{output_subtitles_file}' 文件中。")subtitle_texts = [v[2] for v in outstrs.values()] # 提取每个字幕文本
with open(output_texts_file, 'w', encoding='utf-8') as f: # 打开文件以写入字幕文本f.write('\n'.join(subtitle_texts)) # 将字幕文本拼接并写入文件
print(f"提取的字幕文本已保存到 '{output_texts_file}' 文件中。")
chattts文字转语音
长文本可能会失败
import requestsdef getAudio(savename,url):response = requests.get(url)# 检查请求是否成功if response.status_code == 200:# 打开本地文件并写入内容with open(savename, "wb") as file:file.write(response.content)print("音频文件已成功下载到本地。")return Trueelse:print(f"下载失败,状态码: {response.status_code}")return Falsedef chattts(msg,audioname):res = requests.post('http://192.168.88.251:9966/tts',data={"text": msg,"prompt": "","voice": "3333","temperature": 0.3,"top_p": 0.7,"top_k": 20,"skip_refine": 0,"custom_voice": 0,"is_split": 1})print(res.json())url = res.json()['url']print(url)getAudio(audioname,url)chattts('文本内容','1.wav')
逐行生成
# 读取输出字幕文本每行字幕,生成音频,集合在一个文件夹import requests # 导入 requests 模块以处理 HTTP 请求
import os # 导入 os 模块以处理文件和目录# 配置部分
INPUT_FILE = 'ch.txt' # 输入文件路径
OUTPUT_FOLDER = 'output_wav_files' # 输出文件夹def getAudio(savename, url): # 定义下载音频的函数response = requests.get(url) # 发起 GET 请求获取音频文件if response.status_code == 200: # 检查请求是否成功with open(savename, "wb") as file: # 以二进制写模式打开文件file.write(response.content) # 写入响应内容到文件print(f"音频文件 {savename} 已成功下载到本地。") # 打印成功信息return True # 返回成功状态else:print(f"下载失败,状态码: {response.status_code}") # 打印失败信息return False # 返回失败状态def chattts(msg, audioname): # 定义调用 TTS 服务的函数res = requests.post('http://192.168.88.251:9966/tts', data={ # 发起 POST 请求"text": msg, # 要转换的文本"prompt": "", # 提示文本(留空)"voice": "3333", # 指定语音类型"temperature": 0.3, # 控制生成音频的随机性"top_p": 0.7, # 采样范围"top_k": 20, # 采样数量"skip_refine": 0, # 是否跳过精炼"custom_voice": 0, # 是否使用自定义语音"is_split": 1 # 是否拆分音频})if res.status_code == 200: # 检查请求是否成功print(res.json()) # 打印响应的 JSON 数据url = res.json().get('url') # 获取音频文件的 URLif url: # 如果 URL 存在getAudio(audioname, url) # 下载音频文件else:print("未能获取音频文件的 URL。") # 打印错误信息else:print(f"请求失败,状态码: {res.status_code}") # 打印请求失败信息def process_subtitles(file_path, output_dir): # 定义处理字幕的函数os.makedirs(output_dir, exist_ok=True) # 创建输出目录,如果不存在with open(file_path, 'r', encoding='utf-8') as file: # 以 UTF-8 编码打开文件lines = file.readlines() # 读取所有行for index, line in enumerate(lines): # 遍历每行字幕line = line.strip() # 去除行首尾空白字符if line: # 确保行不为空audioname = f'{output_dir}/line_{index + 1}.wav' # 生成音频文件名chattts(line, audioname) # 调用 TTS 函数生成音频# 主函数
if __name__ == "__main__": # 如果是主程序process_subtitles(INPUT_FILE, OUTPUT_FOLDER) # 处理字幕文件并生成音频
多音频条合成,可能顺序会乱,需要自己调整代码,或者自己逐条剪辑
# 合成每一句wav为一条from pydub import AudioSegment
import os# 设置文件夹路径
folder_path = 'output_wav_files'
output_file = 'combined.wav'# 初始化一个空的音频段
combined = AudioSegment.empty()# 遍历文件夹中的所有 WAV 文件
for file in os.listdir(folder_path):if file.endswith('.wav'):file_path = os.path.join(folder_path, file)audio_segment = AudioSegment.from_wav(file_path)combined += audio_segment # 合成音频# 导出合成后的音频
combined.export(output_file, format='wav')
如果你要剪辑,第一列就是原文音频语句插入点,你可以用以下代码,转换为时分秒的时间格式,以便翻译英文音频
def ms_to_hms(ms):seconds = ms / 1000hours = int(seconds // 3600)minutes = int((seconds % 3600) // 60)seconds = int(seconds % 60)return f"{hours:02}:{minutes:02}:{seconds:02}"# 读取文件内容
with open('chtime.txt', 'r', encoding='utf-8') as file:lines = file.readlines()# 处理每一行
for line in lines:if line.strip(): # 确保行不为空# 提取序号和内容部分parts = line.split(': ', 1) # 按第一个冒号分割if len(parts) > 1:# 使用 eval 将字符串转换为列表list_content = eval(parts[1].strip())ms_value = list_content[0] # 获取第一列的毫秒数time_format = ms_to_hms(ms_value)text_content = list_content[2].strip("'") # 获取文本内容print(f"{time_format} - {text_content}")