CoolFace
Apppublic

arcanus/koala2

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
app.py1064 linesDownload Raw Back to root
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