Spaces:
Running
Running
import os | |
import gradio as gr | |
from google import genai | |
from google.genai import types | |
import mimetypes | |
from pydub import AudioSegment | |
import asyncio | |
import logging | |
# Configure logging | |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') | |
logger = logging.getLogger(__name__) | |
# Атрыманне ключоў і мадэляў з пераменных асяроддзя | |
GEMINI_API_KEY = os.getenv("gemini") | |
MODEL_NAME_TH = os.getenv("modTH") # Мадэль для транскрыпцыі | |
MODEL_NAME = os.getenv("mod") # Мадэль для выпраўлення фармату і перакладу | |
PROMPT_TRANSCRIBE = os.getenv("p") # Промпт для транскрыпцыі | |
# Стварэнне кліента | |
client = genai.Client(api_key=GEMINI_API_KEY) | |
def transcribe_audio(audio_file: str) -> str: | |
"""Транскрыбуе аўдыяфайл з дапамогай Google GenAI.""" | |
try: | |
if not audio_file or not os.path.exists(audio_file): | |
return "Памылка: Файл не знойдзены або не указаны." | |
mime_type, _ = mimetypes.guess_type(audio_file) | |
if not mime_type or not mime_type.startswith("audio"): | |
return ( | |
"Немагчыма вызначыць тып файла або файл не з'яўляецца аўдыяфайлам. " | |
"Падтрымліваюцца толькі аўдыяфайлы." | |
) | |
with open(audio_file, "rb") as f: | |
audio_data = f.read() | |
# Стварэнне contents для перадачы ў API | |
contents = [ | |
{"text": PROMPT_TRANSCRIBE}, | |
{"mime_type": mime_type, "data": audio_data}, | |
] | |
# Выклік API | |
response = client.models.generate_content( | |
model=MODEL_NAME_TH, | |
contents=contents, | |
config={"temperature": 0.2} | |
) | |
# Атрыманне адказу | |
return response.text.strip() | |
except FileNotFoundError: | |
logger.error(f"Файл не знойдзены: {audio_file}") | |
return "Памылка: Файл не знойдзены." | |
except Exception as e: | |
logger.error(f"Памылка пры транскрыпцыі: {str(e)}", exc_info=True) | |
return f"Нечаканая памылка: {str(e)}" | |
def fix_subtitles_format(transcript: str) -> str: | |
"""Выпраўляе фармат часу ў субцітрах.""" | |
if not transcript: | |
return "" | |
prompt_fix = ( | |
"Не змяняй тэксты, выправі толькі часовы фармат у субцітрах на правільны, " | |
"вось прыклад 00:00:01,589. \nУ адказ напішы толькі субцітры:\n" | |
f"{transcript}" | |
) | |
try: | |
response_fix = client.models.generate_content( | |
model=MODEL_NAME, | |
contents=[{"text": prompt_fix}], | |
config={"temperature": 0.2} | |
) | |
return response_fix.text.strip() | |
except Exception as e: | |
logger.error(f"Памылка пры выпраўленні субцітраў: {str(e)}", exc_info=True) | |
return transcript | |
def create_srt(transcript: str, filename: str = "subtitles.srt") -> tuple[str, str]: | |
"""Стварае SRT-файл з транскрыпцыі.""" | |
if not transcript: | |
return "", "" | |
try: | |
with open(filename, "w", encoding="utf-8") as f: | |
f.write(transcript) | |
return transcript, filename | |
except Exception as e: | |
logger.error(f"Памылка пры запісе SRT-файла: {str(e)}", exc_info=True) | |
return f"Памылка пры запісе SRT-файла: {str(e)}", "" | |
def process_audio(audio_path: str) -> tuple[str, str]: | |
"""Апрацоўвае аўдыёфайл: транскрыбуе і стварае SRT.""" | |
if not audio_path: | |
return "Не указаны шлях да аўдыёфайла.", "" | |
transcript = transcribe_audio(audio_path) | |
if transcript.startswith("Памылка") or transcript.startswith("Немагчыма") or transcript.startswith("Нечаканая"): | |
return transcript, "" | |
fixed_transcript = fix_subtitles_format(transcript) | |
text, srt_file = create_srt(fixed_transcript) | |
return text, srt_file | |
def extract_audio_from_video(video_file: str) -> tuple[str, str]: | |
"""Выдзяляе аўдыёдарожку з відэафайла.""" | |
if not video_file or not os.path.exists(video_file): | |
return "", "Памылка: Відэафайл не знойдзены або не указаны." | |
try: | |
audio = AudioSegment.from_file(video_file) | |
audio_path = "extracted_audio.mp3" | |
audio.export(audio_path, format="mp3") | |
return audio_path, "" | |
except Exception as e: | |
logger.error(f"Памылка пры выдзяленні аўдыё з відэафайла: {str(e)}", exc_info=True) | |
return "", f"Памылка пры выдзяленні аўдыё з відэафайла: {str(e)}" | |
def process_video(video_path: str) -> tuple[str, str]: | |
"""Апрацоўвае відэафайл: выдзяляе аўдыё, транскрыбуе і стварае SRT.""" | |
if not video_path: | |
return "Не указаны шлях да відэафайла.", "" | |
audio_path, error = extract_audio_from_video(video_path) | |
if error: | |
return error, "" | |
return process_audio(audio_path) | |
def process_file(audio_path: str | None, video_path: str | None) -> tuple[str, str]: | |
"""Апрацоўвае файл (аўдыё або відэа).""" | |
try: | |
if audio_path: | |
return process_audio(audio_path) | |
elif video_path: | |
return process_video(video_path) | |
else: | |
return "Няма файла для апрацоўкі.", "" | |
except Exception as e: | |
logger.error(f"Памылка пры апрацоўцы файла: {str(e)}", exc_info=True) | |
return f"Памылка пры апрацоўцы файла: {str(e)}", "" | |
def update_on_audio_change(audio_path: str | None) -> gr.update: | |
"""Абнаўляе інтэрфейс пры змене аўдыёфайла.""" | |
try: | |
return gr.update(value=None, interactive=not bool(audio_path)) | |
except Exception as e: | |
logger.error(f"Памылка пры абнаўленні інтэрфейса: {str(e)}", exc_info=True) | |
return gr.update(value=None, interactive=True) | |
def update_on_video_change(video_path: str | None) -> gr.update: | |
"""Абнаўляе інтэрфейс пры змене відэафайла.""" | |
try: | |
return gr.update(value=None, interactive=not bool(video_path)) | |
except Exception as e: | |
logger.error(f"Памылка пры абнаўленні інтэрфейса: {str(e)}", exc_info=True) | |
return gr.update(value=None, interactive=True) | |
def translate_transcript(transcript: str, target_language: str) -> tuple[str, str]: | |
"""Перакладае транскрыпцыю на іншую мову і стварае SRT.""" | |
if not transcript: | |
return "Няма тэксту для перакладу.", "" | |
prompt_text = ( | |
f"Перакладзі толькі тэксты субцітраў на {target_language} мову. " | |
"Астатняе пакінь як ёсць.\nТэкст:\n{transcript}" | |
) | |
try: | |
response = client.models.generate_content( | |
model=MODEL_NAME, | |
contents=[{"text": prompt_text}], | |
config={"temperature": 0.2} | |
) | |
translated = response.text.strip() | |
translated_srt_filename = "translated_subtitles.srt" | |
return create_srt(translated, translated_srt_filename) | |
except Exception as e: | |
logger.error(f"Памылка пры перакладзе: {str(e)}", exc_info=True) | |
return f"Памылка пры перакладзе: {str(e)}", "" | |
try: | |
with gr.Blocks() as demo: | |
gr.Markdown("# Транскрыпцыя аўдыя для беларускай мовы") | |
gr.Markdown( | |
""" | |
## Загрузіце аўдыёфайл або відэафайл да 15 хвілін. Калі загружаны аўдыёфайл, відэа неактыўна, і наадварот. | |
Субцітры будуць аўтаматычна згенераваны разам з SRT-файлам. | |
[Далучайцеся да беларускаймоўнай суполкі ў ТГ](https://t.me/belarusai) | |
**Падтрымаць праект:** [Buy me a coffee](https://buymeacoffee.com/tuteishygpt) | |
""" | |
) | |
with gr.Row(): | |
audio_input = gr.Audio(type="filepath", label="Аўдыёфайл") | |
video_input = gr.Video(label="Відэафайл") | |
audio_input.change( | |
fn=update_on_audio_change, inputs=audio_input, outputs=video_input | |
) | |
video_input.change( | |
fn=update_on_video_change, inputs=video_input, outputs=audio_input | |
) | |
btn = gr.Button("Апрацаваць") | |
transcript_output = gr.Textbox(label="Транскрыпцыя", lines=10) | |
file_output = gr.File(label="SRT-файл") | |
btn.click( | |
fn=process_file, inputs=[audio_input, video_input], outputs=[transcript_output, file_output] | |
) | |
gr.Markdown("## Пераклад субцітраў") | |
with gr.Row(): | |
language_dropdown = gr.Dropdown( | |
choices=["English", "Руcкая", "Польская", "Літоўская", "Нямецкая"], | |
label="Выберы мову перакладу", | |
value="English", | |
) | |
translate_btn = gr.Button("Пераклад") | |
translation_output = gr.Textbox(label="Пераклад", lines=10) | |
translation_file_output = gr.File(label="Translated SRT-файл") | |
translate_btn.click( | |
fn=translate_transcript, | |
inputs=[transcript_output, language_dropdown], | |
outputs=[translation_output, translation_file_output], | |
) | |
# Запуск з дадатковымі параметрамі для стабільнасці | |
demo.launch( | |
server_name="0.0.0.0", | |
server_port=7860, | |
share=False, | |
debug=False, | |
show_error=True, | |
prevent_thread_lock=True | |
) | |
except Exception as e: | |
logger.critical(f"Крытычная памылка пры запуску праграмы: {str(e)}", exc_info=True) | |
print(f"Крытычная памылка: {str(e)}") | |