-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtiktokTTS.py
More file actions
164 lines (140 loc) · 7.67 KB
/
tiktokTTS.py
File metadata and controls
164 lines (140 loc) · 7.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# tiktokTTS.py
import os
import sys
from pathlib import Path
import gradio as gr
import asyncio
import pysrt
from tqdm import tqdm
import shutil
# Importa funções utilitárias
from utils import remove_silence, timetoms, merge_audio_files, adjust_audio_speed
# --- Configuração e Imports da Biblioteca TikTok ---
try:
sys.path.append(str(Path(__file__).parent / "TikTok_TTS"))
from TikTok_TTS.tiktok_voice import Voice, tts
TIKTOK_TTS_AVAILABLE = True
print("Biblioteca TikTok TTS carregada com sucesso.")
except ImportError:
TIKTOK_TTS_AVAILABLE = False
print("Aviso: Biblioteca TikTok TTS não encontrada. A funcionalidade estará desabilitada.")
class Voice: pass
def tts(*args, **kwargs): pass
# --- DICIONÁRIO DE VOZES CATEGORIZADAS ---
TIKTOK_VOICES_CATEGORIZED = {
'Português (Brasil)': [
'BR_FEMALE_1', 'BR_FEMALE_2', 'BR_FEMALE_3', 'BR_MALE',
'BP_FEMALE_IVETE', 'BP_FEMALE_LUDMILLA', 'PT_FEMALE_LHAYS', 'PT_FEMALE_LAIZZA', 'PT_MALE_BUENO'
],
'Inglês (EUA)': [
'US_FEMALE_1', 'US_FEMALE_2', 'US_MALE_1', 'US_MALE_2', 'US_MALE_3', 'US_MALE_4'
],
'Inglês (Reino Unido)': ['UK_MALE_1', 'UK_MALE_2'],
'Inglês (Austrália)': ['AU_FEMALE_1', 'AU_MALE_1'],
'Inglês (Personagens Especiais)': [
'MALE_JOMBOY', 'MALE_CODY', 'FEMALE_SAMC', 'FEMALE_MAKEUP', 'FEMALE_RICHGIRL',
'MALE_ASHMAGIC', 'MALE_OLANTERKKERS', 'MALE_UKNEIGHBOR', 'MALE_UKBUTLER',
'FEMALE_SHENNA', 'FEMALE_PANSINO', 'MALE_TREVOR', 'FEMALE_BETTY', 'MALE_CUPID',
'FEMALE_GRANDMA', 'MALE_NARRATION', 'MALE_FUNNY', 'FEMALE_EMOTIONAL'
],
'Inglês Personagens (Filmes e Outros)': [
'GHOSTFACE', 'CHEWBACCA', 'C3PO', 'STITCH', 'STORMTROOPER', 'ROCKET',
'MADAME_LEOTA', 'GHOST_HOST', 'PIRATE', 'MALE_GRINCH', 'MALE_DEADPOOL', 'MALE_JARVIS'
],
'Inglês Personagens (Festivos)': [
'MALE_XMXS_CHRISTMAS', 'MALE_SANTA_NARRATION', 'MALE_SANTA_EFFECT',
'FEMALE_HT_NEYEAR', 'MALE_WIZARD', 'FEMALE_HT_HALLOWEEN'
],
'Inglês Cantores / Músicas': [
'MALE_SING_DEEP_JINGLE', 'SING_FEMALE_ALTO', 'SING_MALE_TENOR', 'SING_FEMALE_WARMY_BREEZE',
'SING_MALE_SUNSHINE_SOON', 'SING_FEMALE_GLORIOUS', 'SING_MALE_IT_GOES_UP',
'SING_MALE_CHIPMUNK', 'SING_FEMALE_WONDERFUL_WORLD', 'SING_MALE_FUNNY_THANKSGIVING'
],
'Japonês': [
'JP_FEMALE_1', 'JP_FEMALE_2', 'JP_FEMALE_3', 'JP_MALE', 'JP_FEMALE_FUJICOCHAN',
'JP_FEMALE_HASEGAWARIONA', 'JP_MALE_KEIICHINAKANO', 'JP_FEMALE_OOMAEAIIKA',
'JP_MALE_YUJINCHIGUSA', 'JP_FEMALE_SHIROU', 'JP_MALE_TAMAWAKAZUKI',
'JP_FEMALE_KAORISHOJI', 'JP_FEMALE_YAGISHAKI', 'JP_MALE_HIKAKIN', 'JP_FEMALE_REI',
'JP_MALE_SHUICHIRO', 'JP_MALE_MATSUDAKE', 'JP_FEMALE_MACHIKORIIITA',
'JP_MALE_MATSUO', 'JP_MALE_OSADA'
],
'Coreano': ['KR_MALE_1', 'KR_FEMALE', 'KR_MALE_2'],
'Espanhol': ['ES_MALE', 'ES_MX_MALE'],
'Francês': ['FR_MALE_1', 'FR_MALE_2'],
'Alemão': ['DE_FEMALE', 'DE_MALE'],
'Indonésio': ['ID_FEMALE']
}
def get_tiktok_voice_options(language):
return TIKTOK_VOICES_CATEGORIZED.get(language, [])
# --- Função Controladora de Texto/Arquivo ---
def controlador_generate_audio_tiktok(voice_str, text, text_file, cut_silence):
if not TIKTOK_TTS_AVAILABLE:
raise gr.Error("A biblioteca TikTok TTS não está instalada ou configurada corretamente.")
if not text and text_file is None:
raise gr.Error("Por favor, forneça um texto ou um arquivo .txt para gerar o áudio.")
output_dir = "output"; os.makedirs(output_dir, exist_ok=True)
output_file = os.path.join(output_dir, "tiktok_audio.mp3")
input_text = text if text else Path(text_file.name).read_text(encoding='utf-8')
try:
print(f"Gerando áudio com a voz TikTok: {voice_str}...")
tts(input_text, Voice[voice_str], output_file)
print("Áudio TikTok gerado com sucesso!")
if cut_silence:
print("Removendo silêncio do áudio TikTok..."); remove_silence(output_file, output_file); print("Silêncio removido.")
return output_file
except requests.exceptions.RequestException as e:
print(f"!!! TIKTOK TTS NETWORK ERROR DETECTED: {e}")
raise gr.Error(TIKTOK_CONNECTION_ERROR_MSG)
except KeyError:
raise gr.Error(f"A voz '{voice_str}' não foi encontrada.")
except Exception as e:
print(f"!!! TIKTOK TTS UNEXPECTED ERROR: {type(e).__name__} - {e}")
raise gr.Error(f"Ocorreu um erro inesperado no TikTok TTS, se tiver usando GRADIO, mude pra Google Colab: {e}")
# --- NOVA LÓGICA DE PROCESSAMENTO DE SRT PARA TIKTOK ---
async def process_srt_file_tiktok(srt_file_path, voice_str, output_dir_str, srt_temp_deleta, progress=None):
subs = pysrt.open(srt_file_path)
output_dir = Path(output_dir_str)
output_dir.mkdir(parents=True, exist_ok=True)
max_retries = 3 # Número de tentativas para cada legenda
with tqdm(total=len(subs), desc="Gerando e ajustando áudios com TikTok", unit="segmento") as pbar:
for sub in subs:
output_file = output_dir / f"{sub.index:02d}.mp3"
temp_file = output_dir / f"{sub.index:02d}_temp.mp3"
target_duration_ms = timetoms(sub.end) - timetoms(sub.start)
if not output_file.exists() or output_file.stat().st_size == 0:
success = False
for attempt in range(max_retries):
try:
await asyncio.to_thread(tts, sub.text, Voice[voice_str], str(temp_file))
if temp_file.exists() and temp_file.stat().st_size > 0:
await adjust_audio_speed(str(temp_file), str(output_file), target_duration_ms)
os.remove(temp_file)
success = True
break
else:
print(f"Aviso: Tentativa {attempt + 1} para o índice {sub.index} (TikTok) falhou. Retentando...")
except Exception as e:
print(f"Aviso: Tentativa {attempt + 1} para o índice {sub.index} (TikTok) falhou com erro: {e}. Retentando...")
await asyncio.sleep(1)
if not success:
print(f"ERRO: Todas as {max_retries} tentativas (TikTok) falharam para o índice {sub.index}. Gerando silêncio.")
silent_segment = AudioSegment.silent(duration=target_duration_ms)
silent_segment.export(str(output_file), format="mp3")
pbar.update(1)
final_audio = await merge_audio_files(output_dir, srt_file_path)
if srt_temp_deleta:
shutil.rmtree(output_dir, ignore_errors=True)
print(f"Pasta temporária {output_dir} apagada.")
return final_audio
def controlador_process_srt_file_tiktok(srt_file, voice_str, srt_temp_deleta, progress=None):
if not srt_file: return None
srt_filename_stem = Path(srt_file.name).stem
output_dir = f"output/srt_temp_{srt_filename_stem}"
try:
return asyncio.run(process_srt_file_tiktok(srt_file.name, voice_str, output_dir, srt_temp_deleta, progress=progress))
except requests.exceptions.RequestException as e:
print(f"!!! TIKTOK TTS NETWORK ERROR (SRT): {e}")
raise gr.Error(TIKTOK_CONNECTION_ERROR_MSG)
except Exception as e:
print(f"!!! TIKTOK TTS UNEXPECTED ERROR (SRT): {e}")
raise gr.Error(f"Ocorreu um erro inesperado no TikTok TTS, se tiver usando GRADIO, mude pra Google Colab: {e}")