arcanus/koala2
0
1from flask import Flask, render_template, request, jsonify, send_file, session, redirect, url_for, send_from_directory2from functools import wraps3from functools import update_wrapper4import os5from app_rvc2 import SoniTranslate, TTS_Info6from madkoala.language_configuration import LANGUAGES_LIST, LANGUAGES, LANGUAGES_UNIDIRECTIONAL7from datetime import datetime8import re9import shutil10import browser_cookie311import json12import time13import sys14import logging15from flask import Response16import queue17import threading18from werkzeug.utils import secure_filename19from flask_sqlalchemy import SQLAlchemy20from sqlalchemy.orm import DeclarativeBase21import requests22import signal23 24 25app = Flask(__name__)26 27# Create TTS_Info instance to get XTTS status28tts_info = TTS_Info(piper_enabled=True, xtts_enabled=True) # Adjust these values based on your setup29logger = logging.getLogger()30logger.info(f"XTTS is {'enabled' if tts_info.xtts_enabled else 'disabled'}")31app.secret_key = os.environ.get('SECRET_KEY', 'default_secret_key')32APP_PASSWORD = os.environ.get('APP_PASSWORD', 'default_password')33YOUTUBE_USERNAME = os.environ.get('YOUTUBE_USERNAME', 'default_ysecret_key')34YOUTUBE_PASSWORD = os.environ.get('YOUTUBE_PASSWORD', 'default_ypassword')35# Konstanty pro proxy nastavení36PROXY_URL = "" # Změňte na vaši proxy37PROXY_USER = "" # Změňte na vaše uživatelské jméno38PROXY_PASS = "" # Změňte na vaše heslo39 40 41 42# Middleware pro kontrolu přihlášení43def login_required(f):44 @wraps(f)45 def decorated_function(*args, **kwargs):46 if 'logged_in' not in session:47 return redirect(url_for('login'))48 return f(*args, **kwargs)49 return decorated_function50 51@app.route('/login', methods=['GET', 'POST'])52def login():53 if request.method == 'POST':54 if request.form['password'] == APP_PASSWORD:55 session['logged_in'] = True56 return redirect(url_for('home'))57 return render_template('login.html', error='Nesprávné heslo')58 return render_template('login.html')59 60 61@app.route('/logout')62def logout():63 session.pop('logged_in', None)64 return redirect(url_for('login'))65 66soni = SoniTranslate()67 68# Přidání cesty pro servírování výsledných videí69@app.route('/outputs/<path:filename>')70def serve_output(filename):71 return send_file('outputs/' + filename)72 73@app.route('/uploads/<path:filename>')74def serve_upload(filename):75 return send_file('uploads/' + filename)76 77@app.route('/exports/<path:filename>')78def serve_export(filename):79 return send_file('exports/' + filename)80 81@app.route('/')82@login_required83def home():84 return render_template('index_new.html', 85 languages=LANGUAGES_LIST,86 tts_voices=soni.tts_info.tts_list())87 88def create_project_folder(project_name):89 # Vytvoření názvu složky ve formátu DD-MM-YYYY-H:M-project_name90 current_time = datetime.now()91 sanitized_name = re.sub(r'[<>:"/\\|?*]', '_', project_name) # Nahrazení neplatných znaků92 folder_name = current_time.strftime("%d-%m-%Y-%H-%M-") + sanitized_name93 94 # Vytvoření složky v exports95 project_path = os.path.join('exports', folder_name)96 os.makedirs(project_path, exist_ok=True)97 return project_path98 99@app.route('/translate', methods=['POST'])100@login_required101def translate():102 try:103 data = request.form104 files = request.files105 106 # Kontrola manuální korekce107 manual_correction = data.get('translation_correction', 'off') == 'on'108 109 # Vytvoření projektové složky110 project_name = data.get('project_name', 'untitled')111 project_path = create_project_folder(project_name)112 113 # Handle file upload or YouTube URL114 media_file = None115 if 'video' in files and files['video'].filename:116 # Handle file upload117 media_filer = files['video']118 custom_filename = "video.mp4"119 upload_paths = os.path.join(project_path, custom_filename)120 os.makedirs(project_path, exist_ok=True)121 media_filer.save(upload_paths)122 media_file = upload_paths123 print("Video saved to project:", media_file)124 elif 'downloaded_video_path' in data and data['downloaded_video_path'].strip():125 # Použít stažené video z YouTube126 downloaded_path = data['downloaded_video_path'].strip()127 if os.path.exists(downloaded_path):128 media_file = downloaded_path129 print("Using downloaded YouTube video:", media_file)130 else:131 return jsonify({'success': False, 'error': 'Stažené video nebylo nalezeno'})132 elif 'url' in data and data['url'].strip():133 # Handle YouTube URL134 url = data['url'].strip()135 if 'youtube.com' in url or 'youtu.be' in url:136 # Check if we already have this video downloaded137 if 'video_path' in session and os.path.exists(session['video_path']):138 media_file = session['video_path']139 print("Using already downloaded video:", media_file)140 else:141 try:142 media_file, thumbnail_url = download_youtube_video(url, project_path ) #PROXY_URL, PROXY_USER, PROXY_PASS143 # Copy downloaded file to video.mp4144 final_path = os.path.join(project_path, "video.mp4")145 shutil.copy(media_file, final_path)146 media_file = final_path147 print("YouTube video downloaded to:", media_file)148 except Exception as e:149 return jsonify({'success': False, 'error': f'YouTube download failed: {str(e)}'})150 else:151 return jsonify({'success': False, 'error': 'Invalid YouTube URL'})152 153 # Pokud je zapnuta manuální korekce, uložit cestu k videu do session154 if manual_correction and media_file:155 session['current_video_path'] = media_file156 print(f"Uloženo do session: {media_file}")157 158 if not media_file:159 return jsonify({'success': False, 'error': 'No video file or valid YouTube URL provided'})160 161 # Get parameters162 source_lang = data.get('source_language', 'Automatic detection')163 target_lang = data.get('target_language', 'English (en)')164 max_speakers = int(data.get('max_speakers', 1))165 166 # Get edited subtitles if available167 edited_subtitles = data.get('edited_subtitles')168 169 # If edited subtitles are available, save them to a temporary file170 subtitle_file = None171 logger.info(f"edited_subtitles: {edited_subtitles}")172 if edited_subtitles:173 logger.info(f"Získány edited_subtitles: {edited_subtitles}")174 # If edited subtitles are provided, use target language as source language175 # since the subtitles are already in the target language176 source_lang = target_lang177 get_translated_text=False178 get_video_from_text_json=True179 text_json=edited_subtitles180 os.makedirs('uploads', exist_ok=True)181 subtitle_file = os.path.join('uploads', 'edited_subtitles.srt')182 with open(subtitle_file, 'w', encoding='utf-8') as f:183 f.write(edited_subtitles)184 else:185 get_translated_text=False186 get_video_from_text_json=False187 text_json=""188 tts_voices = {}189 for i in range(max_speakers):190 voice_key = f'tts_voice{i:02d}'191 if voice_key in data:192 tts_voices[voice_key] = data[voice_key] 193 194 # Process the translation195 result = soni.multilingual_media_conversion(196 media_file=media_file,197 link_media="",198 directory_input="",199 origin_language=source_lang,200 target_language=target_lang,201 max_speakers=max_speakers,202 get_translated_text=get_translated_text,203 get_video_from_text_json=get_video_from_text_json,204 text_json=text_json,205 max_accelerate_audio=1.0,206 acceleration_rate_regulation=False,207 **tts_voices,208 is_gui=True209 )210 211 if isinstance(result, list):212 # Přesun výstupních souborů do projektové složky213 new_paths = []214 for file_path in result:215 if os.path.exists(file_path):216 new_path = os.path.join(project_path, os.path.basename(file_path))217 shutil.move(file_path, new_path)218 # Převedení na relativní cestu pro frontend219 relative_path = os.path.relpath(new_path, 'exports')220 new_paths.append(f'/exports/{relative_path.replace(os.sep, "/")}')221 222 # Převedení originálního videa na relativní cestu223 original_video_path = None224 if media_file and os.path.exists(media_file):225 original_video_path = f'/exports/{os.path.relpath(media_file, "exports").replace(os.sep, "/")}'226 227 return jsonify({228 'success': True,229 'video': new_paths[0] if new_paths else None,230 'original_video': original_video_path,231 'files': new_paths232 })233 else:234 return jsonify({'success': False, 'error': str(result)})235 236 except Exception as e:237 # Clean up temporary subtitle file in case of error238 if 'subtitle_file' in locals() and subtitle_file and os.path.exists(subtitle_file):239 os.remove(subtitle_file)240 return jsonify({'success': False, 'error': str(e)})241 242def get_youtube_cookies():243 print("Získávám cookies pomocí automatického přihlášení...")244 logger.info("Získávám cookies pomocí automatického přihlášení...")245 options = webdriver.ChromeOptions()246 # Removed headless mode as it often causes issues with Google login247 options.add_argument('--no-sandbox')248 options.add_argument('--disable-dev-shm-usage')249 options.add_argument('--disable-gpu')250 options.add_argument('--disable-features=TranslateUI')251 options.add_argument('--disable-translate')252 options.add_argument('--lang=en')253 options.add_argument('--disable-blink-features=AutomationControlled')254 255 try:256 driver_manager = ChromeDriverManager()257 driver_path = driver_manager.install()258 service = Service(driver_path)259 260 print(f"Používám ChromeDriver z: {driver_path}")261 logger.info(f"Používám ChromeDriver z: {driver_path}")262 driver = webdriver.Chrome(service=service, options=options)263 264 # Add undetected characteristics265 driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")266 267 wait = WebDriverWait(driver, 30) # Increased timeout268 269 print("Přihlašuji se do Google účtu...")270 logger.info("*YOUTUBE*Přihlašuji se do Google účtu...")271 driver.get('https://accounts.google.com/signin/v2/identifier?service=youtube')272 273 # Wait for and interact with email field274 email_input = wait.until(EC.presence_of_element_located((By.NAME, "identifier")))275 email_input.send_keys("")276 email_input.send_keys(Keys.RETURN)277 278 # Wait for and interact with password field279 try:280 password_input = wait.until(EC.presence_of_element_located((By.NAME, "Passwd")))281 password_input.send_keys("")282 password_input.send_keys(Keys.RETURN)283 except TimeoutException:284 print("⚠ Timeout při čekání na pole pro heslo. Možná je vyžadováno ruční ověření.")285 logger.error("⚠ Timeout při čekání na pole pro heslo. Možná je vyžadováno ruční ověření.")286 driver.quit()287 return None288 289 # Wait for successful login and redirect to YouTube290 print("*YOUTUBE*Přecházím na YouTube...")291 logger.info("*YOUTUBE*Přecházím na YouTube...")292 try:293 wait.until(lambda driver: "youtube.com" in driver.current_url)294 except TimeoutException:295 pass # Continue anyway296 297 # Get and save cookies298 cookies = driver.get_cookies()299 if not cookies:300 print("⚠ Nepodařilo se získat cookies. Přihlášení možná selhalo.")301 logger.error("⚠ Nepodařilo se získat cookies. Přihlášení možná selhalo.")302 driver.quit()303 return None304 305 # Save cookies to file306 cookies_dir = "cookies"307 os.makedirs(cookies_dir, exist_ok=True)308 cookies_file = os.path.join(cookies_dir, "youtube.txt")309 310 with open(cookies_file, 'w', encoding='utf-8') as f:311 for cookie in cookies:312 domain = cookie['domain']313 flag = "TRUE"314 path = cookie['path']315 secure = str(cookie.get('secure', False)).upper()316 expires = str(int(cookie.get('expiry', 0)) if cookie.get('expiry') else 0)317 f.write(f"{domain}\tTRUE\t{path}\t{secure}\t{expires}\t{cookie['name']}\t{cookie['value']}\n")318 319 print(f"*YOUTUBE*✓ Cookies úspěšně uloženy do: {cookies_file}")320 logger.info(f"*YOUTUBE*✓ Cookies úspěšně uloženy do: {cookies_file}")321 driver.quit()322 return cookies_file323 324 except Exception as e:325 print(f"⚠ Chyba při získávání cookies: {str(e)}")326 logger.error(f"⚠ Chyba při získávání cookies: {str(e)}")327 if 'driver' in locals():328 driver.quit()329 return None330 331def download_youtube_video(url, project_path, proxy=None, proxy_user=None, proxy_pass=None):332 """Stáhne video z YouTube"""333 try:334 print("\nZahajuji stahování videa...")335 logger.info("*YOUTUBE-zahajeni*Zahajuji stahování videa...")336 337 # Prepare the API request338 api_url = 'https://hound-patient-honestly.ngrok-free.app/download-video'339 headers = {340 'Accept': '*/*', # Accept any content type341 'Content-Type': 'application/json'342 }343 payload = {344 'url': url,345 'api_key': '5as4d4f12sxdf45sfg46vawd74879ad5sd5AF4g6d8f4hfgb5'346 }347 348 print(f"Odesílám požadavek na API: {api_url}")349 print(f"Payload: {payload}")350 logger.info(f"*YOUTUBE*Odesílám požadavek na API: {api_url}")351 logger.info(f"*YOUTUBE*Payload: {payload}")352 353 # Make the API request with proper JSON encoding354 response = requests.post(355 api_url,356 headers=headers,357 json=payload,358 stream=True359 )360 361 print(f"Status code: {response.status_code}")362 print(f"Response headers: {dict(response.headers)}")363 logger.info(f"*YOUTUBE*Status code: {response.status_code}")364 logger.info(f"Response headers: {dict(response.headers)}")365 366 # First check if the response is JSON (error message)367 content_type = response.headers.get('Content-Type', '').lower()368 if 'application/json' in content_type:369 try:370 error_data = response.json()371 error_message = error_data.get('error', 'Unknown API error')372 raise Exception(f"API returned error: {error_message}")373 except json.JSONDecodeError:374 pass # Not JSON, continue with file download375 376 if response.status_code != 200:377 error_message = f"API request failed with status code {response.status_code}"378 try:379 error_message += f": {response.text[:200]}"380 except Exception as e:381 error_message += f" (Error reading response: {str(e)})"382 raise Exception(error_message)383 384 # Get content type and suggested filename from headers385 content_type = response.headers.get('Content-Type', '').lower()386 content_disp = response.headers.get('Content-Disposition', '')387 388 print(f"Content-Type: {content_type}")389 print(f"Content-Disposition: {content_disp}")390 logger.info(f"Content-Type: {content_type}")391 logger.info(f"Content-Disposition: {content_disp}")392 393 video_path = os.path.join(project_path, "video.mp4")394 total_size = 0395 396 print("Začínám stahovat video po částech...")397 logger.info("*YOUTUBE-download*Začínám stahovat video po částech...")398 with open(video_path, 'wb') as f:399 for chunk in response.iter_content(chunk_size=8192):400 if chunk:401 chunk_size = len(chunk)402 total_size += chunk_size403 f.write(chunk)404 print(f"\rStaženo: {total_size / (1024*1024):.2f} MB", end='', flush=True)405 if total_size % (1024*1024) == 0: # Log every 1MB406 logger.info(f"*YOUTUBE-download*Staženo: {total_size / (1024*1024):.2f} MB")407 408 print("\nKontroluji stažený soubor...")409 logger.info("Kontroluji stažený soubor...")410 if os.path.exists(video_path):411 final_size = os.path.getsize(video_path)412 print(f"Velikost souboru: {final_size / (1024*1024):.2f} MB")413 logger.info(f"*YOUTUBE-finish*Velikost souboru: {final_size / (1024*1024):.2f} MB")414 415 if final_size > 0:416 print(f"✓ Video bylo úspěšně staženo do: {video_path}")417 logger.info(f"*YOUTUBE*✓ Video bylo úspěšně staženo do: {video_path}")418 return video_path419 else:420 os.remove(video_path)421 raise Exception("Stažený soubor je prázdný")422 else:423 raise Exception("Soubor nebyl vytvořen")424 425 except Exception as e:426 print(f"Chyba při stahování videa: {str(e)}")427 logger.error(f"Chyba při stahování videa: {str(e)}")428 if 'video_path' in locals() and os.path.exists(video_path):429 os.remove(video_path)430 return None431 432@app.route('/download_youtube', methods=['POST'])433@login_required434def download_yt():435 try:436 # Get data from form submission437 url = request.form.get('url')438 project_name = request.form.get('project_name', '')439 440 if not url:441 return jsonify({'success': False, 'error': 'URL není zadána'})442 443 if not any(x in url.lower() for x in ['youtube.com', 'youtu.be']):444 return jsonify({'success': False, 'error': 'Není platná YouTube URL'})445 446 project_path = create_project_folder(project_name)447 if not project_path:448 return jsonify({'success': False, 'error': 'Nepodařilo se vytvořit složku projektu'})449 450 # Get thumbnail from API451 api_url = 'https://hound-patient-honestly.ngrok-free.app/download-thumbnail'452 headers = {453 'accept': 'application/json',454 'Content-Type': 'application/json'455 }456 payload = {457 'url': url,458 'api_key': '5as4d4f12sxdf45sfg46vawd74879ad5sd5AF4g6d8f4hfgb5'459 }460 461 # Get thumbnail URL with error handling462 thumbnail_url = None # Initialize as None463 try:464 print(f"Sending request to thumbnail API with payload: {payload}")465 thumbnail_response = requests.post(api_url, headers=headers, json=payload)466 print(f"Thumbnail API response status: {thumbnail_response.status_code}")467 print(f"Thumbnail API response content: {thumbnail_response.text}")468 469 if thumbnail_response.status_code == 200:470 try:471 thumbnail_data = thumbnail_response.json()472 if isinstance(thumbnail_data, dict):473 thumbnail_url = thumbnail_data.get('thumbnail_url')474 if not thumbnail_url:475 # Extract video ID and use YouTube thumbnail URL directly476 video_id = None477 if 'youtube.com/watch?v=' in url:478 video_id = url.split('watch?v=')[1].split('&')[0]479 elif 'youtu.be/' in url:480 video_id = url.split('youtu.be/')[1].split('?')[0]481 482 if video_id:483 thumbnail_url = f'https://img.youtube.com/vi/{video_id}/maxresdefault.jpg'484 485 print(f"Final thumbnail URL: {thumbnail_url}")486 except ValueError as e:487 print(f"Error parsing thumbnail JSON: {str(e)}")488 # Fallback to direct YouTube thumbnail489 if 'youtube.com/watch?v=' in url:490 video_id = url.split('watch?v=')[1].split('&')[0]491 thumbnail_url = f'https://img.youtube.com/vi/{video_id}/maxresdefault.jpg'492 except Exception as e:493 print(f"Error getting thumbnail: {str(e)}")494 495 video_path = download_youtube_video(url, project_path)496 497 if video_path and os.path.exists(video_path):498 session['video_path'] = video_path499 session['project_path'] = project_path500 return jsonify({501 'success': True,502 'file_path': video_path,503 'filename': os.path.basename(video_path),504 'thumbnail_url': thumbnail_url505 })506 else:507 return jsonify({'success': False, 'error': 'Nepodařilo se stáhnout video'})508 509 except Exception as e:510 print(f"Route error: {str(e)}")511 return jsonify({'success': False, 'error': f'Chyba: {str(e)}'})512 513@app.route('/get_subtitles', methods=['POST'])514@login_required515def get_subtitles():516 try:517 data = request.form518 files = request.files519 520 # Vytvoření projektové složky521 project_name = data.get('project_name', 'untitled')522 project_path = create_project_folder(project_name)523 524 # Get file or URL or directory525 media_file = None526 link_media = ""527 directory_input = ""528 529 # Handle file upload530 if 'video' in files and files['video'].filename:531 # Handle file upload532 media_filer = files['video']533 custom_filename = "video.mp4"534 upload_paths = os.path.join(project_path, custom_filename)535 os.makedirs(project_path, exist_ok=True)536 media_filer.save(upload_paths)537 media_file = upload_paths538 print("Video saved to project:", media_file)539 540 # Also save to uploads folder541 upload_path = os.path.join('uploads', custom_filename)542 os.makedirs('uploads', exist_ok=True)543 shutil.copy(media_file, upload_path)544 print("Video copied to uploads:", upload_path)545 546 # Save video path to session547 session['video_path'] = media_file548 549 # Handle URL550 elif 'url' in data and data['url'].strip():551 link_media = data['url'].strip()552 print("URL provided:", link_media)553 session['video_url'] = link_media554 555 # Handle directory556 elif 'directory' in data and data['directory'].strip():557 directory_input = data['directory'].strip()558 print("Directory provided:", directory_input)559 session['directory_path'] = directory_input560 561 # Get parameters562 source_lang = data.get('source_language', 'Automatic detection')563 target_lang = data.get('target_language', 'English (en)')564 565 # Process to get subtitles566 result = soni.multilingual_media_conversion(567 media_file=media_file,568 link_media=link_media,569 directory_input=directory_input,570 origin_language=source_lang,571 target_language=target_lang,572 get_translated_text=True,573 is_gui=True574 )575 576 # Save subtitles to session577 if result:578 session['subtitles'] = result579 print("Subtitles saved to session")580 581 print("Subtitles result:", result)582 return jsonify({583 'success': True, 584 'subtitles': result,585 'video_path': session.get('video_path'),586 'video_url': session.get('video_url'),587 'directory_path': session.get('directory_path')588 })589 except Exception as e:590 print("Error in get_subtitles:", str(e))591 return jsonify({'success': False, 'error': str(e)})592 593@app.route('/edit_subtitles', methods=['POST'])594@login_required595def edit_subtitles():596 try:597 data = request.json598 subtitle_text = data.get('subtitle_text', '')599 600 # Process the edited subtitles601 # This would integrate with your existing subtitle processing logic602 return jsonify({'success': True, 'message': 'Subtitles updated successfully'})603 except Exception as e:604 return jsonify({'success': False, 'error': str(e)})605 606@app.route('/voice_imitation', methods=['POST'])607@login_required608def voice_imitation():609 try:610 data = request.form611 files = request.files612 613 voice_imitation_enabled = data.get('voice_imitation', 'false').lower() == 'true'614 voice_imitation_method = data.get('voice_imitation_method', 'freevc')615 voice_imitation_max_segments = int(data.get('voice_imitation_max_segments', 3))616 voice_imitation_vocals_dereverb = data.get('voice_imitation_vocals_dereverb', 'false').lower() == 'true'617 voice_imitation_remove_previous = data.get('voice_imitation_remove_previous', 'true').lower() == 'true'618 619 return jsonify({'success': True})620 except Exception as e:621 return jsonify({'success': False, 'error': str(e)})622 623@app.route('/get_voice_models', methods=['GET'])624@login_required625def get_voice_models():626 try:627 method = request.args.get('method', 'RVC')628 models_dir = 'weights'629 630 if not os.path.exists(models_dir):631 return jsonify({'success': False, 'error': 'Models directory not found'})632 633 # Get all .pth files from the weights directory634 models = [f for f in os.listdir(models_dir) if f.endswith('.pth')]635 636 return jsonify({'success': True, 'models': models})637 except Exception as e:638 return jsonify({'success': False, 'error': str(e)})639 640@app.route('/subtitle_settings', methods=['POST'])641@login_required642def subtitle_settings():643 try:644 data = request.form645 646 # Get subtitle settings647 output_format = data.get('subtitle_format', 'srt')648 soft_subtitles = data.get('soft_subtitles', 'false').lower() == 'true'649 burn_subtitles = data.get('burn_subtitles', 'false').lower() == 'true'650 651 # Get Whisper settings652 literalize_numbers = data.get('literalize_numbers', 'true').lower() == 'true'653 vocal_refinement = data.get('vocal_refinement', 'false').lower() == 'true'654 segment_duration = int(data.get('segment_duration', 15))655 whisper_model = data.get('whisper_model', 'large-v3')656 compute_type = data.get('compute_type', 'float16')657 batch_size = int(data.get('batch_size', 8))658 659 # Get text segmentation settings660 text_segmentation = data.get('text_segmentation', 'sentence')661 divide_text_by = data.get('divide_text_by', '')662 663 # Get diarization and translation settings664 diarization_model = data.get('diarization_model', 'pyannote_2.1')665 translation_process = data.get('translation_process', 'google_translator_batch')666 667 return jsonify({'success': True})668 except Exception as e:669 return jsonify({'success': False, 'error': str(e)})670 671@app.route('/output_settings', methods=['POST'])672@login_required673def output_settings():674 try:675 data = request.form676 677 # Get output settings678 output_type = data.get('output_type', 'video (mp4)')679 output_name = data.get('output_name', '')680 play_sound = data.get('play_sound', 'true').lower() == 'true'681 enable_cache = data.get('enable_cache', 'true').lower() == 'false'682 preview = data.get('preview', 'false').lower() == 'true'683 684 return jsonify({'success': True})685 except Exception as e:686 return jsonify({'success': False, 'error': str(e)})687 688@app.route('/save_srt', methods=['POST'])689@login_required690def save_srt():691 try:692 data = request.json693 srt_content = data.get('srt_content')694 project_name = data.get('project_name')695 696 if not srt_content or not project_name:697 return jsonify({'success': False, 'error': 'Missing required data'})698 video_path = session.get('video_path')699 700 # Create project directory with timestamp like in /translate701 #timestamp = datetime.now().strftime("%d-%m-%Y-%H-%M")702 #project_path = os.path.join('exports', f"{timestamp}-{project_name}")703 #os.makedirs(project_path, exist_ok=True)704 if video_path:705 # Úprava řetězce pomocí regulárních výrazů706 #cleaned_path = re.sub(r'^exports\\', '', video_path) # Odstraní 'exports\' na začátku707 #cleaned_path = re.sub(r'\\video\.mp4$', '', cleaned_path) # Odstraní '\video.mp4' na konci708 cleaned_path = re.sub(r'^exports[\\/]', '', video_path)709 cleaned_path = re.sub(r'[\\/]+video\.mp4$', '', cleaned_path)710 print("===================cleaned")711 print(cleaned_path)712 else:713 print("Hodnota video_path není v session.")714 715 project_path = os.path.join('exports', cleaned_path)716 print("===================project")717 print(project_path)718 719 # Save SRT file in the same directory as video720 srt_path = os.path.join(project_path, 'titulky.srt')721 print("===================srt")722 print(srt_path)723 with open(srt_path, 'w', encoding='utf-8') as f:724 f.write(srt_content)725 726 return jsonify({'success': True})727 except Exception as e:728 return jsonify({'success': False, 'error': str(e)})729 730@app.route('/purge', methods=['POST'])731@login_required732def purge_folders():733 try:734 # Seznam složek k vymazání735 folders = ['outputs', 'audio']736 737 for folder in folders:738 if os.path.exists(folder):739 # Vymaže obsah složky740 for filename in os.listdir(folder):741 file_path = os.path.join(folder, filename)742 try:743 if os.path.isfile(file_path) or os.path.islink(file_path):744 os.unlink(file_path)745 elif os.path.isdir(file_path):746 shutil.rmtree(file_path)747 except Exception as e:748 print(f'Failed to delete {file_path}. Reason: {e}')749 750 return jsonify({'success': True, 'message': 'Složky byly úspěšně vymazány'})751 except Exception as e:752 return jsonify({'success': False, 'error': str(e)})753 754@app.route('/get_current_video_path', methods=['GET'])755@login_required756def get_current_video_path():757 video_path = session.get('current_video_path')758 if video_path:759 return jsonify({760 'success': True, 761 'video_path': video_path,762 'filename': os.path.basename(video_path)763 })764 return jsonify({'success': False, 'error': 'No video path in session'})765 766@app.route('/get_status')767def get_status():768 # Check if ngrok server is online769 try:770 response = requests.get(771 'https://hound-patient-honestly.ngrok-free.app/',772 params={'api_key': '5as4d4f12sxdf45sfg46vawd74879ad5sd5AF4g6d8f4hfgb5'},773 headers={'accept': 'application/json'},774 timeout=5775 )776 ngrok_online = response.status_code == 200777 logger.info(f"Ngrok server status check: {response.status_code}")778 except Exception as e:779 logger.error(f"Error checking ngrok server: {str(e)}")780 ngrok_online = False781 782 return jsonify({783 'xtts_enabled': tts_info.xtts_enabled,784 'piper_enabled': tts_info.piper_enabled,785 'ngrok_server_online': ngrok_online786 })787 788#@app.route('/reset_sessions', methods=['POST'])789#def reset_sessions():790# session.clear()791# return jsonify({'status': 'success'})792 793# Create a queue for log messages794log_queue = queue.Queue()795 796# Create a custom handler that puts messages into the queue797class QueueHandler(logging.Handler):798 def emit(self, record):799 log_queue.put({800 'level': record.levelname,801 'message': self.format(record)802 })803 804# Add the queue handler to the logger805logger = logging.getLogger()806queue_handler = QueueHandler()807logger.addHandler(queue_handler)808 809@app.route('/logs')810def logs():811 def generate():812 while True:813 try:814 # Get message from queue815 message = log_queue.get(timeout=20) # 20 second timeout816 yield f"data: {json.dumps(message)}\n\n"817 except queue.Empty:818 # Send keepalive every 20 seconds819 yield f"data: {json.dumps({'level': 'INFO', 'message': 'keepalive'})}\n\n"820 821 return Response(generate(), mimetype='text/event-stream')822 823 824 825logging.basicConfig(level=logging.DEBUG)826 827 828@app.route('/editace', methods=['GET', 'POST'])829def edit_page():830 app.logger.info("-----------EDITACE-----------")831 #if request.method == 'POST':832 # folder_id = request.form.get('project_name')833 #else:834 # folder_id = request.args.get('project_name')835 session['editovano'] = True836 app.logger.info("Nastavena session hodnota 'editovano' na True")837 838 839 840 841 folder_id = request.args.get('folder_id')842 843 app.logger.info(f"Request parameters: folder_id={folder_id}")844 video_path = None845 subtitle_content = None846 847 if folder_id:848 app.logger.info("Processing request with folder_id")849 app.logger.info(f"Processing request for folder_id: {folder_id}")850 851 852 853 854 855 856 857 current_directory = os.getcwd()858 three_levels_up = os.path.abspath(os.path.join(current_directory, ".."))859 base_path = os.path.abspath(os.path.join(three_levels_up, "exports", folder_id))860 861 862 upload_dir = os.path.join('static', 'uploads')863 folder_dir = os.path.join(base_path, folder_id)864 video_file = os.path.join(base_path, 'video.mp4')865 subtitle_file = os.path.join("exports", folder_id, 'titulky.srt')866 video_fullfile = os.path.join("exports", folder_id, 'video.mp4')867 868 app.logger.info(869 f"Full subtitle file path: {os.path.abspath(subtitle_file)}"870 f"Full video file path: {os.path.abspath(video_fullfile)}")871 # Create directories if they don't exist872 os.makedirs(base_path, exist_ok=True)873 874 # Check file permissions875 if os.path.exists(video_file):876 try:877 with open(video_file, 'rb') as f:878 app.logger.info("Successfully opened video file")879 video_path = f'{folder_id}/video.mp4'880 app.logger.info(f"Set video path to: {video_path}")881 except Exception as e:882 app.logger.error(f"Error accessing video file: {e}")883 else:884 app.logger.error(f"Video file does not exist at: {video_file}")885 886 if os.path.exists(subtitle_file):887 try:888 with open(subtitle_file, 'r', encoding='utf-8') as f:889 subtitle_content = f.read()890 app.logger.info(891 f"Successfully read subtitle content {subtitle_content}")892 except Exception as e:893 app.logger.error(f"Error reading subtitle file: {e}")894 else:895 app.logger.error(896 f"Subtitle file does not exist at: {subtitle_file}")897 app.logger.info(f"base path: {base_path}")898 app.logger.info(f"video file: {video_file}")899 # Ensure subtitle content is properly escaped and formatted900 if subtitle_content:901 # Replace HTML entities with their actual characters902 import html903 subtitle_content = html.unescape(subtitle_content.strip())904 app.logger.info(f"Sending subtitle content with length: {len(subtitle_content)}")905 app.logger.debug(f"Subtitle content sample: {subtitle_content[:200]}")906 return render_template('index.html',907 video_path=f"/serve_video/{folder_id}",908 subtitle_content=subtitle_content,909 languages=LANGUAGES_LIST,910 tts_voices=soni.tts_info.tts_list())911 912@app.route('/upload_video', methods=['POST'])913def upload_video():914 """915 Upload a video file either through form-data or raw binary content916 917 curl examples:918 Form-data:919 curl -X POST -F "video=@/path/to/video.mp4" http://localhost:5000/upload_video920 921 Raw binary:922 curl -X POST --data-binary @/path/to/video.mp4 -H "Content-Type: video/mp4" http://localhost:5000/upload_video?filename=video.mp4923 """924 upload_dir = os.path.join('static', 'uploads')925 os.makedirs(upload_dir, exist_ok=True)926 927 if request.files and 'video' in request.files:928 # Handle form-data upload929 video = request.files['video']930 if video.filename == '':931 return jsonify({'error': 'No selected file'}), 400932 filename = secure_filename(video.filename)933 content = video.read()934 else:935 # Handle raw binary upload936 content = request.get_data()937 if not content:938 return jsonify({'error': 'No content received'}), 400939 filename = secure_filename(request.args.get('filename', 'video.mp4'))940 941 if content:942 filepath = os.path.join(upload_dir, filename)943 with open(filepath, 'wb') as f:944 f.write(content)945 946 return jsonify({947 'success': True,948 'filename': filename,949 'filepath': f'/static/uploads/{filename}'950 })951 952 return jsonify({'error': 'No valid content received'}), 400953 954 955@app.route('/generate_subtitles', methods=['POST'])956def generate_subtitles():957 """958 Generate subtitles from video file using Whisper959 """960 try:961 import whisper962 model = whisper.load_model("base")963 964 video_file = request.files.get('video')965 if not video_file:966 return jsonify({'error': 'No video file provided'}), 400967 968 # Save video temporarily969 temp_path = os.path.join('static', 'uploads', 'temp_' + secure_filename(video_file.filename))970 video_file.save(temp_path)971 972 try:973 # Transcribe the audio974 result = model.transcribe(temp_path)975 subtitles = []976 977 # Convert Whisper segments to SRT format978 for i, segment in enumerate(result["segments"]):979 subtitle = {980 'start': segment['start'],981 'end': segment['end'],982 'text': segment['text'].strip()983 }984 subtitles.append(subtitle)985 986 return jsonify({987 'success': True,988 'subtitles': subtitles989 })990 finally:991 # Clean up temporary file992 if os.path.exists(temp_path):993 os.remove(temp_path)994 995 except Exception as e:996 app.logger.error(f"Error generating subtitles: {str(e)}")997 return jsonify({'error': str(e)}), 500998 999 1000@app.route('/serve_video/<folder_id>')1001def serve_video(folder_id):1002 """1003 Endpoint pro servírování video souboru z adresáře exports.1004 """1005 try:1006 # Vytvoření cesty k souboru1007 current_directory = os.getcwd()1008 three_levels_up = os.path.abspath(os.path.join(current_directory, ".."))1009 base_path = os.path.abspath(os.path.join("exports", folder_id))1010 app.logger.error(f"Error serving video file: {base_path}/video.mp4")1011 # Vrátí soubor video.mp41012 return send_from_directory(base_path, "video.mp4")1013 except Exception as e:1014 app.logger.error(f"Error serving video file: {e}")1015 return jsonify({'success': False, 'error': str(e)}), 4041016 1017 1018@app.route('/upload_subtitle', methods=['POST'])1019def upload_subtitle():1020 """1021 Upload subtitle file either through form-data or raw text content1022 1023 curl examples:1024 Form-data:1025 curl -X POST -F "subtitle=@/path/to/subtitles.srt" http://localhost:5000/upload_subtitle1026 1027 Raw text:1028 curl -X POST --data-binary @/path/to/subtitles.srt -H "Content-Type: text/plain" http://localhost:5000/upload_subtitle1029 """1030 if request.files and 'subtitle' in request.files:1031 # Handle form-data upload1032 subtitle = request.files['subtitle']1033 if subtitle.filename == '':1034 return jsonify({'error': 'No selected file'}), 4001035 content = subtitle.read()1036 else:1037 # Handle raw text upload1038 content = request.get_data()1039 if not content:1040 return jsonify({'error': 'No content received'}), 4001041 1042 try:1043 # Decode content to string1044 if isinstance(content, bytes):1045 content = content.decode('utf-8')1046 return jsonify({'success': True, 'content': content})1047 except UnicodeDecodeError:1048 return jsonify(1049 {'error': 'Invalid subtitle file encoding, must be UTF-8'}), 4001050 1051 1052with app.app_context():1053 1054 1055 # Create upload directory if it doesn't exist1056 os.makedirs(os.path.join('static', 'uploads'), exist_ok=True)1057 1058 1059 1060# Pokud je skript spuštěn přímo, exportujeme cookies1061if __name__ == "__main__":1062 app.run(host="0.0.0.0", port=7860, debug=True)1063 #get_youtube_cookies()1064 