豆包Marscode体验官:不写一行代码创建一个srt字幕翻译工具

我正在参加「豆包MarsCode初体验」征文活动

当 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)

然而并不能直接用,存在几个错误。

存在的问题

  1. 没有使用我在提示词中指定的 url ,即 https://g3.pyvideotrans.com/translate_a/single,而是使用的占位符 url = 'URL_ADDRESS',需要手动修改
  2. 我已经在提示词中写明了返回的是json字符串,其实直接调用res=response.json()方法就行了,没必要多此一举的使用 res = json.loads(response.text)
  3. 可能是返回结果过于复杂,或我的提示词不够清晰,它没有按照我预想的方式获取翻译结果

生成的 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

  1. 出现了一个奇怪有趣的bug,在生成函数2完成后,继续回车,又提示生成函数3,和函数2功能完全一样,只是名字略不同,3完成后,又出现4。。。。如此直到10还没结束。不得不删掉,然后加了个 if开头行,然后按下空格,才开始正常提示 if __name__

如下图所示

  1. 在一行注释后回车,有一定几率生成的代码以 # 开头,也就是被注释掉了。如下图

总结

标题有点夸大了,不写代码或许可以,但不修改代码几乎是不可能的。

总体上体验还是挺不错的,尤其是在线IDE,使用流畅度不逊色于本地编辑器。

右侧还有更多功能待体验哦

相关推荐
FutureUniant1 小时前
GitHub每日最火火火项目(7.7)
python·计算机视觉·ai·github·视频
杰哥在此2 小时前
Java面试题:讨论持续集成/持续部署的重要性,并描述如何在项目中实施CI/CD流程
java·开发语言·python·面试·编程
PY1783 小时前
Python的上下文管理器
数据库·python·oracle
Struggle to dream3 小时前
Python编译器的选择
开发语言·python
爱看书的小沐4 小时前
ASCII码对照表(Matplotlib颜色对照表)
python·matplotlib·rgb·ascii·colormap·颜色对照表·颜色映射
算法金「全网同名」4 小时前
算法金 | 推导式、生成器、向量化、map、filter、reduce、itertools,再见 for 循环
python·机器学习·数据分析
nuclear20114 小时前
Python 如何批量压缩PDF文件或减小PDF文件大小
开发语言·python·压缩pdf·减小pdf文件大小
LensonYuan5 小时前
python项目常见使用的传参调试方法
开发语言·人工智能·python·语言模型
Struggle to dream5 小时前
opencv 处理图像去噪的几种方法
人工智能·python·opencv·计算机视觉
极客代码6 小时前
机器学习实现自然语言处理的背后技术详解
开发语言·人工智能·python·机器学习·自然语言处理·nlp