import gradio as gr import edge_tts import asyncio import tempfile import os import time import base64 from datetime import datetime from pydub import AudioSegment VOICE = "en-US-AndrewMultilingualNeural" CHUNK_SIZE = 5000 HF_USER = "Cracked-User" HF_REPO = "tts-audiobooks" # ── Exact same logic as Colab ───────────────────────────────────────────────── def run_tts(text, custom_name=''): chunks = [text[i:i+CHUNK_SIZE] for i in range(0, len(text), CHUNK_SIZE)] total = len(chunks) parts = [] chunk_times = [] batch_start = time.time() tmp_dir = tempfile.mkdtemp() yield None, '⏳ Total chunks: ' + str(total), make_player(None), '' for i, chunk in enumerate(chunks): path = tmp_dir + '/chunk_' + str(i).zfill(5) + '.mp3' if chunk_times: avg = sum(chunk_times) / len(chunk_times) eta = int(avg * (total - i)) elapsed = int(time.time() - batch_start) status = ( 'Chunk ' + str(i+1) + '/' + str(total) + ' ETA: ' + str(eta//60) + 'm ' + str(eta%60) + 's' + ' Elapsed: ' + str(elapsed//60) + 'm ' + str(elapsed%60) + 's' ) else: status = 'Chunk ' + str(i+1) + '/' + str(total) + ' ETA: calculating...' yield None, status, make_player(None), '' try: async def save(chunk=chunk, path=path): comm = edge_tts.Communicate(chunk, VOICE) await comm.save(path) asyncio.run(save()) parts.append(path) except Exception as e: try: time.sleep(5) asyncio.run(save()) parts.append(path) except Exception as e2: yield None, '❌ Failed chunk ' + str(i+1) + ': ' + str(e2), make_player(None), '' return chunk_times.append(time.time() - batch_start - sum(chunk_times)) # Log progress to HF so you can check after refresh avg2 = sum(chunk_times) / len(chunk_times) eta2 = int(avg2 * (total - i - 1)) elapsed2 = int(time.time() - batch_start) pct2 = int(((i+1) / total) * 100) log_msg = ( '🔊 Chunk ' + str(i+1) + ' / ' + str(total) + ' (' + str(pct2) + '%)\n' + '⏱️ Elapsed: ' + str(elapsed2//60) + 'm ' + str(elapsed2%60) + 's\n' + '⏳ ETA: ' + str(eta2//60) + 'm ' + str(eta2%60) + 's\n' + '🕐 Updated: ' + datetime.now().strftime('%H:%M:%S') ) import threading threading.Thread(target=log_progress, args=(log_msg, os.environ.get('HF_TOKEN', '')), daemon=True).start() yield None, 'Merging all chunks...', make_player(None), '' combined = AudioSegment.empty() for p in sorted(parts): if os.path.exists(p): combined += AudioSegment.from_file(p) + AudioSegment.silent(200) timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') filename = (custom_name.strip().replace(' ', '_') + '.mp3') if custom_name and custom_name.strip() else ('andrew_' + timestamp + '.mp3') out_path = tempfile.mktemp(suffix='.mp3') combined.export(out_path, format='mp3', bitrate='192k') # Cleanup chunks for p in parts: try: os.remove(p) except: pass total_time = int(time.time() - batch_start) done_msg = 'DONE! Time: ' + str(total_time//60) + 'm ' + str(total_time%60) + 's' log_progress('✅ DONE! Uploading audio file...\n🕐 ' + datetime.now().strftime('%H:%M:%S'), os.environ.get('HF_TOKEN', '')) yield None, done_msg + '\nUploading to HF...', make_player(None), '' hf_msg = upload_to_hf(out_path, filename) yield out_path, done_msg, make_player(out_path), hf_msg # ── Upload to HF Dataset ────────────────────────────────────────────────────── def upload_to_hf(file_path, filename): try: from huggingface_hub import HfApi token = os.environ.get('HF_TOKEN', '') if not token: return '⚠️ No HF_TOKEN secret found.' api = HfApi() # Create repo if it doesn't exist try: api.create_repo( repo_id=HF_USER + '/' + HF_REPO, repo_type='dataset', private=True, token=token, exist_ok=True ) except: pass api.upload_file( path_or_fileobj=file_path, path_in_repo='audiobooks/' + filename, repo_id=HF_USER + '/' + HF_REPO, repo_type='dataset', token=token ) url = 'https://huggingface.co/datasets/' + HF_USER + '/' + HF_REPO return '✅ Saved!\n🔗 ' + url except Exception as e: return '❌ Upload error: ' + str(e) # ── Log progress to HF Dataset ─────────────────────────────────────────────── def log_progress(msg, token): try: from huggingface_hub import HfApi if not token: return api = HfApi() log_path = '/tmp/progress.txt' with open(log_path, 'w') as f: f.write(msg) api.upload_file( path_or_fileobj=log_path, path_in_repo='progress.txt', repo_id=HF_USER + '/' + HF_REPO, repo_type='dataset', token=token ) except: pass # ── Custom HTML player ──────────────────────────────────────────────────────── def make_player(file_path): if not file_path or not os.path.exists(file_path): return '
Microsoft Andrew voice · Exact Colab quality · Auto saves to HF Dataset
') with gr.Tabs(): with gr.Tab('📝 Paste Text'): with gr.Row(): with gr.Column(scale=2): text_input = gr.Textbox( label='Your Text', placeholder='Paste your novel chapter or any text here — no limit!', lines=12 ) name1 = gr.Textbox(label='File Name (optional)', placeholder='e.g. chapter_102 — leave blank for auto name') btn1 = gr.Button('🎙️ Generate Audio', elem_classes='btn-gen') with gr.Column(scale=1): gr.HTML('