当 GitHub Copilot 还是免费的时候,用起来真是畅快淋漓,大呼666。不过,自从它开始收费,每月要10美元,虽然知道它的价值,但考虑到那总是鼓不起来的钱包,还是决定放弃了。又回到了古老编程时代,偶尔需要AI帮助时:打开浏览器某个AI对话框,输入问题复制答案,虽有挺多免费类Copilot工具,但总觉用的不顺手。
最近,试用了字节跳动推出的 Marscode,感觉挺不错。最让人惊喜的是,它还自带了一个在线IDE,不仅拥有智能补全、预测和问答等强大功能,而且操作起来流畅的就像使用本地编辑器一样,完全感觉不到延迟。更棒的是,它还提供了2核CPU、4G内存和10G硬盘的项目空间,真的很实用了!
使用方法
打开 www.marscode.cn/dashboard 注册登录,然后新建项目
支持 python、nodejs、go等多种模板,并可从GitHub导入项目,我需要一个python项目,自然选择python模板了,创建后就进入了编辑空间
可以愉快使用了,如果不是上面有个浏览器地址栏,简直以为是本地编辑器了。
最近在鼓捣字幕翻译,那就以此为契机搞个"字幕翻译项目吧"
项目概述
一个srt格式字幕翻译功能,将字幕从一种语言翻译到另一种语言,并保持字幕格式不变。
整体思路
AI虽然够聪明,但首先要让它理解你在指挥它做什么任务,你输入的指令越准确、逻辑越清晰,它完成的就越好,反之你不会得到想要的结果。
首先整理思路,然后根据该思路写提示词。
一. 写个函数1,用于读取srt字幕文件并转为字典对象列表
将每条字幕转为一个dict
对象,line
键对应行号、time
键对应字幕时间戳行、text
键对应字幕文本文字。如
原字幕
lua
1
00:00:01.000 --> 00:00:05.400
AI技术的发展超出了预期
转为dict对象后
json
{
"line":1,
"time":"00:00:01.000 --> 00:00:05.400",
"text":"AI技术的发展超出了预期"
}
为何这样处理呢?主要是方便批量获取多行字幕文本,然后将这多行文本以换行符\n
连接后,发给翻译引擎,再将翻译结果一一对应填充回dict。
干吗不直接带上 行号和时间戳行 的完整字幕格式进行翻译呢?
原因主要有二:
1. 传统翻译引擎,如百度,可能会将时间戳行中的英文标点转为中文标点,或者将时间戳也翻译一遍,导致格式出错。即便号称全世界最准确的DeepL也可能犯类似错误。
2. AI模型虽不会犯此错误,但会浪费很多token,并且不同厂家不同规模的模型智能程度不同,一些的模型仍然会犯格式错误,比如可能删除字幕之间的2个空白行。
或者出现合并2行内容为一行,尤其是下一行只有孤零零的一个或几个单词,而且该单词在语义上属于上一行时。
例如
sql
2
00:00:01,860 --> 00:00:04,660
you are my friend, i am
3
00:00:06,400 --> 00:00:07,580
from China
在一些不够庞大或智能的模型时,可能会遇到一个棘手的问题:有时会将"from China"这样的短语错误地合并到上一行,结果导致该行变成了空白。
尽管尝试通过使用严格的提示词和翻译示例来避免这种情况,但只要翻译量足够大时,比如成千上万的字幕条或数百个字幕文件,这种小概率的错误仍然难以完全避免。
为了应对这些偶发的格式问题,不得不编写大量的修正代码,还消耗了大量的token,从成本效益的角度来看,不划算。
特别是,考虑到我的字幕文件是通过语音识别生成的,其准确性和标点的正确性本身就无法保证
因此,经过深思熟虑,我决定采取一种更为简洁高效的方法:不再发送带有复杂格式的字幕,而只发送字幕中的文字行。这样既简化了处理流程,又避免了因格式问题带来的额外工作量,当出现行数不对应时,转而对此次任务采用逐行翻译。
如果一条字幕的文本行由两行或多行组成怎么办?这种情况下似乎带格式翻译更好,不过更简单的方式是直接将换行符替换为多个句号转为一行,最终字幕文件中再替换回来。
二:再写个函数2,用于组装发送翻译请求
在该函数中,调用函数1读取字幕文件返回字典对象列表,然后遍历该列表,取出text键
对应的字幕文本,发送翻译请求,得到翻译结果后再替换text键
对应的文本。
该处为加快翻译速度,同时在AI翻译时提供上下文以提升翻译质量,一次发送15行或更多行,具体方法是每15个
dict
对象的text值
用换行符连接为一个字符串,得到翻译结果后再按行拆分为15行,填充回对应的dict对象的text值中。如上文所述,这种情况下要求译文行数同原文行数严格一致,但聪明的AI往往会遵照语义合并行,尤其是孤零零一两个单词的行,即便已提供了严格的提示词限制。
虽然直接发送带行号和时间行格式的字幕能减少该情况出现的几率,但绝不会降到零,反而因此大幅增加了token消耗,得不偿失。
可在出现该情况时,对该次任务的15行字幕文本重新逐行翻译,通过容许一定的质量降低来保证行数绝对正确。
三:再写个函数3,将翻译后的字幕保存为新的字幕文件
遍历翻译后组装好的列表对象,组成srt格式的字幕字符串,重新保存回srt格式字幕文件。
css
dict["line"]
dict["time"]
dict["text"]
dict["line"]
dict["time"]
dict["text"]
写提示词
根据上文思路,编写提示词,可根据任务阶段,每完成一个任务后,再编写写一个提示词。也可以一次完成整个任务所有步骤,我是按后者执行。
在左侧点击新建图标创建一个start.py
文件
然后在 start.py
中输入提示词,在代码中,注释就是提示词。
以下是该项目的完整提示词或者说是注释
shell
# 创建一个srt格式的字幕翻译工具,实现从一种语言翻译为另一种语言
# 首先从命令行参数1中读取srt字幕路径,从参数2中读取要翻译到的目标语言代码
# 然后创建函数1,该函数读取字幕文件,并返回一个list列表,列表中每个元素都是一个dict字典对象,每个dict对象对应一条字幕,字典对象中的 line键值对应字幕行号数字、time键值对应时间戳行内容、text键值对应字幕的文本内容,注意文本可能有一行或多行。
# 再创建函数2,该函数中调用函数1,得到包含字典对象元素的列表list,然后遍历该list,取出dict中的text键对应的值
# 接着向 https://g3.pyvideotrans.com/translate_a/single 发起get请求。参数分别是 client=gtx,dt=t,sl=auto,tl=参数2,q=text键值
# 请求头headers中user-agnet模仿chrome104浏览器
# 请求返回的数据是一个json格式的字符串,转为json对象后是一个list列表,可命名为res,res[0]中存储翻译结果,它也是一个list,可能由1个或多个子元素组成,每个子元素也是一个list,将所有子元素的0索引元素拼接成一个字符串,即为翻译结果文本,将该结果替换text键对应的内容。
# 创建函数3,将函数2中替换text后的list对象进行遍历,组装为完整的srt格式字幕字符串,然后保存为srt文件,文件名是 原文件名+目标语言.srt
# 最后调用函数3,将函数2中替换text后的list对象保存为srt文件。
# 即可实现从一种语言翻译为另一种语言。
为简化示例,方便展示,没有采用一次15行文本进行翻译,而是一次只翻译一行
代码自动生成
如上,在最后一行提示词结束后,按回车
新起一行,无意外的情况下,即自动开始生成代码,以浅灰半透明显示。
可大致扫一眼,如果感觉合适,按Tab
键采用,然后不断循环 回车
+ Tab
直到完成。
有时按了一次回车后,并没有出现提示代码,这种情况时有发生,尤其是在一个函数完成或一段代码结束,需要开启新的代码片段时,可以继续再按一次或两次回车,稍作等待,提示代码应该就会出现了。
动图展示代码自动生成
从动图中也能看出,有时按一次回车后并没有显示可用的代码,此时需要再次按下回车。
以下是生成的全部代码,未经任何修改
scss
import sys
import json
import requests
import urllib.parse
def read_srt(srt_file):
with open(srt_file, 'r', encoding='utf-8') as f:
srt_data = f.read()
srt_lines = srt_data.split('\n')
srt_list = []
i = 0
while i < len(srt_lines):
line = srt_lines[i]
if line.isdigit():
line_dict = {}
line_dict['line'] = int(line)
i += 1
line_dict['time'] = srt_lines[i]
i += 1
text = []
while i < len(srt_lines) and srt_lines[i]!= '' and not srt_lines[i].isdigit():
text.append(srt_lines[i])
i += 1
line_dict['text'] = '\n'.join(text)
srt_list.append(line_dict)
else:
i+=1
return srt_list
def translate_srt(srt_list, target_lang):
for line_dict in srt_list:
text = line_dict['text']
url = 'URL_ADDRESS'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'
}
params = {
'client': 'gtx',
'dt': 't',
'sl': 'auto',
'tl': target_lang,
'q': text
}
response = requests.get(url, headers=headers, params=params)
res = json.loads(response.text)
line_dict['text'] = '\n'.join(res[0])
return srt_list
def save_srt(srt_list, srt_file, target_lang):
with open(srt_file, 'w', encoding='utf-8') as f:
for line_dict in srt_list:
f.write(str(line_dict['line']) + '\n')
f.write(line_dict['time'] + '\n')
f.write(line_dict['text'] + '\n')
f.write('\n')
if __name__ == '__main__':
srt_file = sys.argv[1]
target_lang = sys.argv[2]
srt_list = read_srt(srt_file)
srt_list = translate_srt(srt_list, target_lang)
save_srt(srt_list, srt_file + '.' + target_lang, target_lang)
然而并不能直接用,存在几个错误。
存在的问题
- 没有使用我在提示词中指定的 url ,即
https://g3.pyvideotrans.com/translate_a/single
,而是使用的占位符url = 'URL_ADDRESS'
,需要手动修改 - 我已经在提示词中写明了返回的是json字符串,其实直接调用
res=response.json()
方法就行了,没必要多此一举的使用res = json.loads(response.text)
- 可能是返回结果过于复杂,或我的提示词不够清晰,它没有按照我预想的方式获取翻译结果
生成的 line_dict['text'] = '\n'.join(res[0])
这种方式是错误的。
返回的json数据结构
css
[ [ ['朋友你好', 'hello,my friend', None, None, 10]
], None, 'en', None, None, None, 1, [], [['en'], None, [1], ['en']]]
...
预期的获取方式应该是
line_dict['text'] = ''.join([te[0] for te in res[0]])
修改后的代码: 为方便控制台观看实时结果,加了一行代码,print(line_dict)
scss
import sys
import json
import requests
import urllib.parse
def read_srt(srt_file):
with open(srt_file, 'r', encoding='utf-8') as f:
srt_data = f.read()
srt_lines = srt_data.split('\n')
srt_list = []
i = 0
while i < len(srt_lines):
line = srt_lines[i]
if line.isdigit():
line_dict = {}
line_dict['line'] = int(line)
i += 1
line_dict['time'] = srt_lines[i]
i += 1
text = []
while i < len(srt_lines) and srt_lines[i]!= '' and not srt_lines[i].isdigit():
text.append(srt_lines[i])
i += 1
line_dict['text'] = ''.join(text)
srt_list.append(line_dict)
else:
i+=1
return srt_list
def translate_srt(srt_list, target_lang):
for line_dict in srt_list:
text = line_dict['text']
url = 'https://g3.pyvideotrans.com/translate_a/single'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'
}
params = {
'client': 'gtx',
'dt': 't',
'sl': 'auto',
'tl': target_lang,
'q': text
}
response = requests.get(url, headers=headers, params=params)
res = response.json()
line_dict['text'] = ''.join([te[0] for te in res[0]])
print(line_dict)
return srt_list
def save_srt(srt_list, srt_file, target_lang):
with open(srt_file, 'w', encoding='utf-8') as f:
for line_dict in srt_list:
f.write(str(line_dict['line']) + '\n')
f.write(line_dict['time'] + '\n')
f.write(line_dict['text'] + '\n')
f.write('\n')
if __name__ == '__main__':
srt_file = sys.argv[1]
target_lang = sys.argv[2]
srt_list = read_srt(srt_file)
srt_list = translate_srt(srt_list, target_lang)
save_srt(srt_list, srt_file + '.' + target_lang, target_lang)
找个srt字幕文件,直接拖动到左侧文件区,我这里从本地拖动名为 1.srt
的英文字幕
控制台输入 python start.py 1.srt zh-cn
我需要翻译为简体中文,因此输入 zh-cn
没有意外发生的话,1.srt.zh-cn.srt
已经生成了。
这里使用的是Google翻译,质量还凑合
遇到的一些奇怪问题/Bug
- 出现了一个奇怪有趣的bug,在生成函数2完成后,继续回车,又提示生成函数3,和函数2功能完全一样,只是名字略不同,3完成后,又出现4。。。。如此直到10还没结束。不得不删掉,然后加了个
if
开头行,然后按下空格,才开始正常提示if __name__
如下图所示
- 在一行注释后回车,有一定几率生成的代码以
#
开头,也就是被注释掉了。如下图
总结
标题有点夸大了,不写代码或许可以,但不修改代码几乎是不可能的。
总体上体验还是挺不错的,尤其是在线IDE,使用流畅度不逊色于本地编辑器。
右侧还有更多功能待体验哦