yt-sub (5209B)
1 import sys 2 import subprocess 3 import glob 4 import re 5 import argparse 6 7 def clean_vtt(filepath, remove_time=True, words_per_paragraph=150): 8 """ 9 Limpa o arquivo VTT, remove duplicatas e junta o texto em blocos baseados 10 em uma quantidade limite de palavras por parágrafo. 11 """ 12 with open(filepath, "r", encoding="utf-8") as f: 13 lines = f.readlines() 14 15 cleaned_content = [] 16 last_text_line = "" 17 18 # Padrões Regex para identificar lixo e tempos 19 time_pattern = re.compile(r'-->') 20 header_pattern = re.compile(r'^WEBVTT|^Kind:|^Language:|^Align:|Style:') 21 number_pattern = re.compile(r'^\d+$') 22 tags_pattern = re.compile(r'<[^>]+>') # Remove tags como <00:00:01.000> ou <c> 23 24 for line in lines: 25 original_line = line.strip() 26 27 # Pula linhas vazias 28 if not original_line: 29 if not remove_time: 30 cleaned_content.append("\n") 31 continue 32 33 # Ignora cabeçalhos e números de indexação 34 if header_pattern.search(original_line) or number_pattern.match(original_line): 35 if not remove_time: 36 cleaned_content.append(original_line + "\n") 37 continue 38 39 # Lida com o tempo 40 if time_pattern.search(original_line): 41 if not remove_time: 42 cleaned_content.append(original_line + "\n") 43 continue 44 45 # Extrai apenas o texto limpo 46 text = tags_pattern.sub('', original_line).strip() 47 48 if not text: 49 continue 50 51 # Lógica para evitar as duplicações do YouTube (legendas rolantes) 52 if text != last_text_line: 53 if remove_time: 54 # Se for sem tempo, junta tudo na mesma linha separando por espaço 55 cleaned_content.append(text + " ") 56 else: 57 # Se for com tempo, mantém a quebra de linha do VTT 58 cleaned_content.append(text + "\n") 59 60 last_text_line = text 61 62 final_text = "".join(cleaned_content) 63 64 # Se estiver no modo texto (sem tempo), organiza em parágrafos por contagem de palavras 65 if remove_time: 66 # Remove espaços duplos que possam ter sobrado 67 final_text = re.sub(r'\s+', ' ', final_text).strip() 68 69 # Divide o texto todo em uma lista de palavras 70 palavras = final_text.split() 71 72 # Agrupa as palavras em blocos (parágrafos) do tamanho definido 73 paragrafos = [] 74 for i in range(0, len(palavras), words_per_paragraph): 75 bloco_palavras = palavras[i : i + words_per_paragraph] 76 paragrafo = " ".join(bloco_palavras) 77 paragrafos.append(paragrafo) 78 79 # Junta os parágrafos com duas quebras de linha entre eles 80 final_text = "\n\n".join(paragrafos) 81 82 # Sobrescreve o arquivo com a versão final 83 with open(filepath, "w", encoding="utf-8") as f: 84 f.write(final_text) 85 86 def process_url(url, keep_time, words_per_paragraph): 87 print(f"\n--- Processando: {url} ---") 88 89 # Lista arquivos antes para saber qual foi baixado agora 90 vtts_antes = set(glob.glob("*.vtt")) 91 92 # Comando base (silencioso) 93 comando_base = [ 94 "yt-dlp", 95 "--quiet", 96 "--no-warnings", 97 "--write-subs", 98 "--write-auto-subs", 99 "--sub-format", "vtt", 100 "--skip-download", 101 url 102 ] 103 104 # 1. TENTA BAIXAR EM PORTUGUÊS 105 print("Buscando legenda em PT...") 106 comando_pt = comando_base[:5] + ["--sub-langs", "pt"] + comando_base[5:] 107 subprocess.run(comando_pt) 108 109 vtts_depois = set(glob.glob("*.vtt")) 110 novos_vtts = vtts_depois - vtts_antes 111 112 # 2. SE NÃO ACHOU, TENTA EM INGLÊS 113 if not novos_vtts: 114 print("Legenda em PT não encontrada. Tentando EN...") 115 comando_en = comando_base[:5] + ["--sub-langs", "en"] + comando_base[5:] 116 subprocess.run(comando_en) 117 118 vtts_depois = set(glob.glob("*.vtt")) 119 novos_vtts = vtts_depois - vtts_antes 120 121 # SE CONTINUAR SEM ACHAR, ABORTA 122 if not novos_vtts: 123 print("Nenhuma legenda encontrada (nem PT, nem EN).") 124 return 125 126 # Limpa os arquivos recém-baixados 127 for arquivo in novos_vtts: 128 print(f"Limpando e formatando arquivo em blocos de {words_per_paragraph} palavras: {arquivo}") 129 clean_vtt(arquivo, remove_time=not keep_time, words_per_paragraph=words_per_paragraph) 130 131 print("Processo concluído com sucesso!") 132 133 def main(): 134 parser = argparse.ArgumentParser(description="Baixa legendas (PT ou EN) e formata em grandes blocos de texto.") 135 136 parser.add_argument("urls", nargs="+", help="Uma ou mais URLs do YouTube.") 137 parser.add_argument("-t", "--keep-time", action="store_true", 138 help="Mantém as tags de tempo (formato VTT original, sem juntar os blocos).") 139 parser.add_argument("-w", "--words", type=int, default=150, 140 help="Quantidade de palavras por parágrafo (Padrão: 150).") 141 142 args = parser.parse_args() 143 144 for url in args.urls: 145 process_url(url, keep_time=args.keep_time, words_per_paragraph=args.words) 146 147 if __name__ == "__main__": 148 main()