Este artigo apresenta como usar Python para chamar ffmpeg e Gemini para traduzir legendas de filmes. O efeito pode ser encontrado na seção "Exibição de efeitos".
fundo
Saí da minha última empresa há pouco tempo e tornei-me independente novamente. Alternei entre os empregos anteriores quase sem problemas, sem pensar muito. Desta vez decidi pensar bem e relaxar e trabalhar em algo que considero divertido. Comprei um NAS e descobri que minhas habilidades de TI no trabalho finalmente foram utilizadas em minha vida, a primeira delas foi sobre legendas de filmes em chinês.
O primeiro passo para obter um NAS é começar a baixar freneticamente filmes em 4K. Todos esses filmes vêm com legendas, mas alguns não têm legendas em chinês ou não são bem traduzidos. Além disso, o software NAS que comprei não está totalmente funcional e o download de legendas em chinês é problemático, por isso espero ter uma solução automatizada. Após avaliação, acho que podemos usar a IA atual, como ChatGPT e Gemini, para traduzir legendas em inglês, o que deve ter bons resultados.
Use Poesia para gerenciar projetos
Não fiz muitos projetos em Python nos últimos anos, mas vi alguns projetos usando poesia , então decidi usá-lo neste projeto. A experiência de teste é muito boa, muito melhor do que o pipenv que usei antes.
O conteúdo do meu arquivo pyproject.toml é o seguinte:
[tool.poetry]
name = "upbox"
version = "0.1.0"
description = ""
authors = ["rocksun <[email protected]>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.10"
ffmpeg-python = "^0.2.0"
llama-index = "^0.10.25"
llama-index-llms-gemini = "^0.1.6"
pysubs2 = "^1.6.1"
# yt-dlp = "^2024.4.9"
# typer = "^0.12.3"
# faster-whisper = "^1.0.1"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Não vou entrar em detalhes sobre o uso da poesia aqui, você pode aprender sozinho. A biblioteca de empacotamento do ffmpeg é citada aqui (o comando ffmpeg é necessário no caminho); então há o llama-index e a biblioteca Gemini correspondente. Na verdade, não há muita diferença entre usar o llama-index e não. não use muitas funções do índice de lhama; finalmente, é a biblioteca de processamento de legendas pysubs2. Certa vez, considerei se deveria analisar as legendas diretamente, mas depois descobri que usar pysubs2 ainda pode economizar muito tempo.
Extração de legenda em inglês
É fácil extrair legendas incorporadas em um vídeo através do ffmpeg. Basta executar o seguinte comando:
ffmpeg -i my_file.mkv outfile.vtt
Mas, na verdade, haverá várias legendas em um vídeo, o que não é exato, então você ainda precisa confirmar. Ainda considero usar uma biblioteca ffmpeg, ou seja, ffmpeg-python . O código para usar esta biblioteca para extrair legendas em inglês é o seguinte:
def _guess_eng_subtitle_index(video_path):
probe = ffmpeg.probe(video_path)
streams = probe['streams']
for index, stream in enumerate(streams):
if stream.get('codec_type') == 'subtitle' and stream.get('tags', {}).get('language') == 'eng':
return index
for index, stream in enumerate(streams):
if stream['codec_type'] == 'subtitle' and stream.get('tags', {}).get('title', "").lower().find("english")!=-1 :
return index
return -1
def _extract_subtitle_by_index(video_path, output_path, index):
return ffmpeg.input(video_path).output(output_path, map='0:'+str(index)).run()
def extract_subtitle(video_path, en_subtitle_path):
# get the streams from video with ffprobe
index = _guess_eng_subtitle_index(video_path)
if index == -1:
return -1
return _extract_subtitle_by_index(video_path, en_subtitle_path, index)
Foi adicionado um método _guess_eng_subtitle_index
para determinar o índice de legendas em inglês. Isso ocorre porque, embora as tags de legenda da maioria dos vídeos sejam relativamente padronizadas, existem de fato alguns vídeos cujas legendas não possuem tags, então só posso adivinhar. ainda existem algumas na prática. Outras situações só podem ser tratadas com base na situação real.
Processamento de legendas em inglês
A princípio pensei que bastaria apenas jogar as legendas para o Gemini e salvar os resultados, mas na verdade não funcionou.
- Existem muitas tags em muitas legendas em inglês, o que afetará o efeito durante a tradução
- Se a legenda for muito grande, o Gemini não consegue lidar com ela e, se o contexto for muito longo, podem surgir problemas.
- O carimbo de data/hora nas legendas é muito longo, tornando o prompt muito longo.
Por esse motivo, tive que adicionar uma classe de legenda UpSubs para lidar com os problemas acima:
class UpSubs:
def __init__(self, subs_path):
self.subs = pysubs2.load(subs_path)
def get_subtitle_text(self):
text = ""
for sub in self.subs:
text += sub.text + "\n\n"
return text
def get_subtitle_text_with_index(self):
text = ""
for i, sub in enumerate(self.subs):
text += "chunk-"+str(i) + ":\n" + sub.text.replace("\\N", " ") + "\n\n"
return text
def save(self, output_path):
self.subs.save(output_path)
def clean(self):
indexes = []
for i, sub in enumerate(self.subs):
# remove xml tag and line change in sub text
sub.text = re.sub(r"<[^>]+>", "", sub.text)
sub.text = sub.text.replace("\\N", " ")
def fill(self, text):
text = text.strip()
pattern = r"\n\s*\n"
paragraphs = re.split(pattern, text)
for para in paragraphs:
try:
firtline = para.split("\n")[0]
countstr = firtline[6:len(firtline)-1]
# print(countstr)
index = int(countstr)
p = "\n".join(para.split("\n")[1:])
self.subs[index].text = p
except Exception as e:
print(f"Error merge paragraph : \n {para} \n with exception: \n {e}")
raise(e)
def merge_dual(self, subspath):
second_subs = pysubs2.load(subspath)
merged_subs = SSAFile()
if len(self.subs.events) == len(second_subs.events):
for i, first_event in enumerate(self.subs.events):
second_event = second_subs[i]
if first_event.text == second_event.text:
merged_event = SSAEvent(first_event.start, first_event.end, first_event.text)
else:
merged_event = SSAEvent(first_event.start, first_event.end, first_event.text + '\n' + second_event.text)
merged_subs.append(merged_event)
return merged_subs
return None
clean
O método pode simplesmente limpar legendas; o método save pode ser usado para salvar legendas merge_dual
; Eles são relativamente simples e nos concentraremos no processamento do texto das legendas posteriormente.
O formato do arquivo srt original é o seguinte:
12
00:02:30,776 --> 00:02:34,780
Not even the great Dragon Warrior.
13
00:02:43,830 --> 00:02:45,749
Oh, where is Po?
14
00:02:45,749 --> 00:02:48,502
He was supposed to be here hours ago.
O método se tornará get_subtitle_text_with_index
:
chunk-12
Not even the great Dragon Warrior.
chunk-13
Oh, where is Po?
chunk-14
He was supposed to be here hours ago.
Isso é feito para reduzir o número de palavras e pedaços. Além disso, o número de cada legenda ainda pode ser rastreado. Através fill
do método podemos restaurar as legendas do texto traduzido.
Ligue para Gêmeos
Existem vários problemas ao ligar para Gemini:
- chave de acesso necessária
- As visitas domésticas requerem um agente adequado
- Deve ter certa tolerância a falhas
- Há também a necessidade de contornar o mecanismo de segurança do Gemini.
Portanto, um complete
método especial foi escrito para resolver estes problemas:
def complete(prompt, max_tokens=32760):
prompt = prompt.strip()
if not prompt:
return ""
safety_settings = [
{
"category": "HARM_CATEGORY_HARASSMENT",
"threshold": "BLOCK_NONE"
},
{
"category": "HARM_CATEGORY_HATE_SPEECH",
"threshold": "BLOCK_NONE"
},
{
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
"threshold": "BLOCK_NONE"
},
{
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
"threshold": "BLOCK_NONE"
},
]
retries = 3
for _ in range(retries):
try:
return Gemini(max_tokens=max_tokens, safety_settings=safety_settings, temperature = 0.01).complete(prompt).text
except Exception as e:
print(f"Error completing prompt: {prompt} \n with error: \n ")
traceback.print_exc()
return ""
safety_settings
É muito importante que alguma linguagem particularmente sensível apareça frequentemente nas legendas dos filmes, e Gêmeos deve ser informado para tolerá-la tanto quanto possível. Embora de acordo com a documentação, apenas contas pagas possam fazer isso BLOCK_NONE
, parece que não encontrei muitos problemas com a configuração acima ao traduzir filmes. Ocasionalmente, encontrei alguns, mas eles desapareceriam se eu tentasse novamente.
Em seguida, são adicionadas três tentativas. A chamada falhará ocasionalmente.
Por fim, a chave API pode ser obtida através do Google AI Studio . Em seguida, adicione um arquivo .env ao projeto:
http_proxy=http://192.168.0.107:7890
https_proxy=http://192.168.0.107:7890
GOOGLE_API_KEY=[your-api-key]
O programa pode ler a chave API e as configurações de proxy.
Processo de chamada
Vejamos primeiro o tran_subtitles
método mais externo
def tran_subtitles(fixed_subtitle, zh_subtitle=None, cncf = False, chunk_size=3000):
subtitle_base = os.path.splitext(fixed_subtitle)[0]
video_base = os.path.splitext(subtitle_base)[0]
if zh_subtitle is None:
zh_subtitle = video_base + ".zh-fixed.vtt"
if os.path.exists(zh_subtitle):
print(f"zh subtitle {zh_subtitle} already translated, skip to translate.")
return 1
prompt_tpl = MOVIE_TRAN_PROMPT_TPL
opts = { }
srtp = UpSubs(fixed_subtitle)
text = srtp.get_subtitle_text_with_index()
process_text(srtp, text, prompt_tpl, opts, chunk_size = chunk_size)
srtp.save(zh_subtitle)
return zh_subtitle
A lógica é relativamente simples. Leia as legendas em inglês, use get_subtitle_text_with_index
o método para convertê-las em texto a ser traduzido e, em seguida, execute o método process_text para concluir a tradução. O modelo de palavra de prompt prompt_tpl faz referência direta a MOVIE_TRAN_PROMPT_TPL, que contém:
MOVIE_TRAN_PROMPT_TPL = """你是个专业电影字幕翻译,你需要将一份英文字幕翻译成中文。
[需要翻译的英文字幕]:
{content}
# [中文字幕]:"""
Você pode ver que este prompt é bastante simples.
Então você pode prestar atenção aos seguintes process_text
métodos:
def process_text(subs, text, prompt_tpl, opts, chunk_size=2500):
# ret = ""
chunks = _split_subtitles(text, chunk_size)
for(i, chunk) in enumerate(chunks):
print("process chunk ({}/{})".format(i+1,len(chunks)))
# if i==4:
# break
# format string with all the field in a dict
opts["content"] = chunk
prompt = prompt_tpl.format(**opts)
print(prompt)
out = complete(prompt, max_tokens=32760)
subs.fill(out)
print(out)
Divida o texto da legenda em vários pedaços por meio _split_subtitles
do método e, em seguida, jogue-os no método mencionado acima complete
.
Mostrar resultados
No início não tinha muitas expectativas para a tradução das legendas, mas o efeito final foi surpreendentemente bom Tomando Kung Fu Panda 4 como exemplo, esta é uma comparação de algumas traduções:
Legendas em inglês:
10
00:02:22,184 --> 00:02:27,606
Let it be known from the highest mountain
to the lowest valley that Tai Lung lives,
11
00:02:27,606 --> 00:02:30,776
and no one will stand in his way.
12
00:02:30,776 --> 00:02:34,780
Not even the great Dragon Warrior.
13
00:02:43,830 --> 00:02:45,749
Oh, where is Po?
Legenda chinesa:
10
00:02:22,184 --> 00:02:27,606
让最高的山峰和最低的山谷都知道,泰隆还活着,
11
00:02:27,606 --> 00:02:30,776
没人能阻挡他。
12
00:02:30,776 --> 00:02:34,780
即使是伟大的神龙大侠也不行。
13
00:02:43,830 --> 00:02:45,749
哦,阿宝在哪儿?
Os resultados foram surpreendentemente bons. Meu projeto não forneceu mais contexto, mas Gemini forneceu uma tradução autêntica.
Resumir
Para filmes, o código acima é executado de forma relativamente estável. Mas quando nos deparamos com algumas legendas que não são muito boas por si só, os resultados da tradução também não são muito bons e existem muitas anomalias, por isso muitas melhorias precisam ser feitas. Recentemente, minha conta de vídeo (Yunyunzhongshengs) compartilhou alguns vídeos técnicos, que foram implementados com código aprimorado, e os compartilharei com vocês mais tarde.
Sinto que é ótimo poder usar a tecnologia para mudar vidas. Espero que haja mais oportunidades de melhoria. Todos são bem-vindos para prestar mais atenção e se comunicar.
Decidi desistir do código aberto Hongmeng Wang Chenglu, o pai do código aberto Hongmeng: Hongmeng de código aberto é o único evento de software industrial de inovação arquitetônica na área de software básico na China - o OGG 1.0 é lançado, a Huawei contribui com todo o código-fonte. Google Reader é morto pela "montanha de merda de código" Ubuntu 24.04 LTS é lançado oficialmente Antes do lançamento oficial do Fedora Linux 40, desenvolvedores da Microsoft: o desempenho do Windows 11 é "ridiculamente ruim", Ma Huateng e Zhou Hongyi apertam as mãos, "eliminando rancores" Empresas de jogos conhecidas emitiram novos regulamentos: os presentes de casamento dos funcionários não devem exceder 100.000 yuans Pinduoduo foi condenado por concorrência desleal Compensação de 5 milhões de yuansEste artigo foi publicado pela primeira vez em Yunyunzhongsheng ( https://yylives.cc/ ), todos são bem-vindos para visitar.