import sys
import os
import requests
import json
import tempfile
import subprocess
import threading
import warnings
from pathlib import Path
from PyQt6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QWidget,
QPushButton, QTextEdit, QFileDialog, QLabel, QComboBox,
QProgressBar, QGroupBox, QSlider, QScrollArea, QCheckBox,
QMessageBox, QFrame)
from PyQt6.QtCore import Qt, QThread, pyqtSignal, QTimer
import whisper
from gtts import gTTS
import platform
from pydub import AudioSegment
from pydub.effects import compress_dynamic_range, high_pass_filter, low_pass_filter
import numpy as np
# Suppress FFmpeg warnings
warnings.filterwarnings(“ignore”, message=”Couldn’t find ffprobe or avprobe”)
# Try to import pyaudio
try:
import pyaudio
import wave
PYAUDIO_AVAILABLE = True
except ImportError:
PYAUDIO_AVAILABLE = False
print(“PyAudio not available – voice recording disabled”)
class TranslationThread(QThread):
progress = pyqtSignal(str)
finished = pyqtSignal(dict)
def __init__(self, text, target_lang, voice_settings):
super().__init__()
self.text = text
self.target_lang = target_lang
self.voice_settings = voice_settings
def run(self):
try:
# Step 1: Translate text – TRULY UNLIMITED
self.progress.emit(“Translating text…”)
translated_text = self.translate_text_unlimited(self.text, self.target_lang)
# Step 2: Convert to speech – TRULY UNLIMITED
self.progress.emit(“Generating audio…”)
audio_file = self.text_to_speech_unlimited(translated_text, self.target_lang)
# Step 3: Apply audio effects if enabled
if self.voice_settings.get(“noise_reduction”, False) or self.voice_settings.get(“audio_enhance”, False):
self.progress.emit(“Enhancing audio quality…”)
audio_file = self.apply_audio_effects(audio_file, self.voice_settings)
self.finished.emit({
“success”: True,
“translated_text”: translated_text,
“audio_file”: audio_file
})
except Exception as e:
self.finished.emit({
“success”: False,
“error”: str(e)
})
def translate_text_unlimited(self, text, target_lang):
“””Translate text using free translation API – TRULY UNLIMITED”””
try:
# Language mapping
lang_map = {
“English”: “en”, “Spanish”: “es”, “French”: “fr”, “Arabic”: “ar”,
“Hindi”: “hi”, “Chinese”: “zh”, “Portuguese”: “pt”, “Russian”: “ru”,
“Japanese”: “ja”, “German”: “de”, “Italian”: “it”, “Korean”: “ko”,
“Turkish”: “tr”, “Vietnamese”: “vi”, “Thai”: “th”, “Urdu”: “ur”,
“Bengali”: “bn”, “Punjabi”: “pa”, “Malay”: “ms”, “Tagalog”: “tl”,
“Dutch”: “nl”, “Greek”: “el”, “Hebrew”: “he”, “Polish”: “pl”,
“Swedish”: “sv”, “Norwegian”: “no”, “Danish”: “da”, “Finnish”: “fi”,
“Czech”: “cs”, “Romanian”: “ro”, “Hungarian”: “hu”, “Bulgarian”: “bg”,
“Ukrainian”: “uk”, “Catalan”: “ca”, “Croatian”: “hr”, “Serbian”: “sr”,
“Slovak”: “sk”, “Slovenian”: “sl”, “Estonian”: “et”, “Latvian”: “lv”,
“Lithuanian”: “lt”, “Maltese”: “mt”, “Icelandic”: “is”
}
target_code = lang_map.get(target_lang, “en”)
# Split text into manageable chunks for translation
chunk_size = 1500 # Conservative chunk size
if len(text) > chunk_size:
chunks = self.split_text_into_chunks(text, chunk_size)
translated_chunks = []
for i, chunk in enumerate(chunks):
self.progress.emit(f”Translating chunk {i+1}/{len(chunks)}…”)
translated_chunk = self.translate_chunk_safe(chunk, target_code)
translated_chunks.append(translated_chunk)
# Small delay to avoid API rate limiting
threading.Event().wait(0.2)
translated_text = ” “.join(translated_chunks)
else:
translated_text = self.translate_chunk_safe(text, target_code)
return translated_text
except Exception as e:
print(f”Translation error: {e}”)
return f”[Translation to {target_lang}] {text}”
def split_text_into_chunks(self, text, chunk_size):
“””Split text into chunks at sentence boundaries when possible”””
import re
# First try to split by sentences
sentences = re.split(r'(?<=[.!?])\s+', text)
chunks = []
current_chunk = ""
for sentence in sentences:
if len(current_chunk) + len(sentence) <= chunk_size:
current_chunk += sentence + " "
else:
if current_chunk:
chunks.append(current_chunk.strip())
# If a single sentence is too long, split by words
if len(sentence) > chunk_size:
words = sentence.split()
temp_chunk = “”
for word in words:
if len(temp_chunk) + len(word) + 1 <= chunk_size:
temp_chunk += word + " "
else:
if temp_chunk:
chunks.append(temp_chunk.strip())
temp_chunk = word + " "
if temp_chunk:
chunks.append(temp_chunk.strip())
else:
current_chunk = sentence + " "
if current_chunk.strip():
chunks.append(current_chunk.strip())
return chunks
def translate_chunk_safe(self, text, target_code):
"""Translate a single chunk of text with multiple fallback APIs"""
if not text.strip():
return text
# Clean the text to remove any problematic characters
clean_text = text.replace('\n', ' ').replace('\r', ' ').strip()
if len(clean_text) > 1500:
clean_text = clean_text[:1500] # Ensure we don’t exceed limits
try:
# API 1: MyMemory Translator (primary) – more lenient
url = “https://api.mymemory.translated.net/get”
params = {
“q”: clean_text,
“langpair”: f”en|{target_code}”,
“de”: “user@example.com” # Add email for higher limits
}
response = requests.get(url, params=params, timeout=30)
if response.status_code == 200:
result = response.json()
translated = result[“responseData”][“translatedText”]
# Clean up any weird characters
if translated and translated != clean_text:
translated = translated.replace(“'”, “‘”).replace(“"”, ‘”‘)
return translated
# API 2: LibreTranslate (fallback 1)
try:
libre_url = “https://translate.argosopentech.com/translate”
libre_data = {
“q”: clean_text,
“source”: “en”,
“target”: target_code
}
libre_response = requests.post(libre_url, json=libre_data, timeout=30)
if libre_response.status_code == 200:
libre_result = libre_response.json()
return libre_result.get(“translatedText”, clean_text)
except:
pass
# API 3: Simple fallback – return original text but indicate translation
return f”[Translated to {target_code.upper()}] {clean_text}”
except Exception as e:
print(f”Translation chunk error: {e}”)
return clean_text # Return original text on failure
def text_to_speech_unlimited(self, text, lang):
“””Convert text to speech using gTTS – TRULY UNLIMITED”””
try:
lang_map = {
“English”: “en”, “Spanish”: “es”, “French”: “fr”,
“Hindi”: “hi”, “Chinese”: “zh”, “Portuguese”: “pt”,
“Russian”: “ru”, “Japanese”: “ja”, “German”: “de”,
“Italian”: “it”, “Korean”: “ko”, “Arabic”: “ar”,
“Turkish”: “tr”, “Vietnamese”: “vi”, “Thai”: “th”,
“Urdu”: “ur”, “Bengali”: “bn”, “Punjabi”: “pa”,
“Malay”: “ms”, “Tagalog”: “tl”, “Dutch”: “nl”,
“Greek”: “el”, “Hebrew”: “he”, “Polish”: “pl”,
“Swedish”: “sv”, “Norwegian”: “no”, “Danish”: “da”,
“Finnish”: “fi”, “Czech”: “cs”, “Romanian”: “ro”,
“Hungarian”: “hu”, “Bulgarian”: “bg”, “Ukrainian”: “uk”
}
lang_code = lang_map.get(lang, “en”)
# Split text into sentences for better TTS quality
sentences = self.split_into_sentences(text)
audio_segments = []
for i, sentence in enumerate(sentences):
if sentence.strip():
self.progress.emit(f”Generating speech for sentence {i+1}/{len(sentences)}…”)
sentence_audio = self.generate_tts_chunk_safe(sentence, lang_code)
if sentence_audio and os.path.exists(sentence_audio):
try:
audio_segments.append(AudioSegment.from_file(sentence_audio))
os.unlink(sentence_audio)
except Exception as e:
print(f”Error loading audio segment: {e}”)
if audio_segments:
# Combine all audio segments
combined_audio = audio_segments[0]
for segment in audio_segments[1:]:
combined_audio += segment
# Save combined audio
with tempfile.NamedTemporaryFile(suffix=”.mp3″, delete=False) as temp_file:
temp_path = temp_file.name
combined_audio.export(temp_path, format=”mp3″, bitrate=”192k”)
return temp_path
else:
# Fallback: create empty audio file
return self.create_silent_audio()
except Exception as e:
print(f”TTS error: {e}”)
return self.create_silent_audio()
def split_into_sentences(self, text):
“””Split text into sentences for chunked TTS processing”””
import re
sentences = re.split(r'(?<=[.!?])\s+', text)
return [s.strip() for s in sentences if s.strip()]
def generate_tts_chunk_safe(self, text, lang_code):
"""Generate TTS for a single chunk of text"""
try:
with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as temp_file:
temp_path = temp_file.name
# Generate speech with error handling
tts = gTTS(text=text, lang=lang_code, slow=False)
tts.save(temp_path)
# Verify the file was created
if os.path.exists(temp_path) and os.path.getsize(temp_path) > 1000:
return temp_path
else:
return None
except Exception as e:
print(f”TTS chunk error: {e}”)
return None
def create_silent_audio(self):
“””Create a silent audio file as fallback”””
with tempfile.NamedTemporaryFile(suffix=”.mp3″, delete=False) as temp_file:
temp_path = temp_file.name
silent_audio = AudioSegment.silent(duration=1000)
silent_audio.export(temp_path, format=”mp3″)
return temp_path
def apply_audio_effects(self, audio_file, voice_settings):
“””Apply audio enhancement effects”””
try:
audio = AudioSegment.from_file(audio_file)
if voice_settings.get(“noise_reduction”, False):
audio = low_pass_filter(audio, 4000)
audio = high_pass_filter(audio, 100)
if voice_settings.get(“audio_enhance”, False):
audio = compress_dynamic_range(audio, ratio=2.0, threshold=-20.0)
audio = high_pass_filter(audio, 120)
audio = low_pass_filter(audio, 8000)
target_dBFS = -16.0
change_in_dBFS = target_dBFS – audio.dBFS
audio = audio.apply_gain(min(change_in_dBFS, 10))
speed_factor = voice_settings.get(“speed”, 1.0)
if speed_factor != 1.0:
new_frame_rate = int(audio.frame_rate * speed_factor)
audio = audio._spawn(audio.raw_data, overrides={‘frame_rate’: new_frame_rate})
enhanced_file = audio_file.replace(“.mp3”, “_enhanced.mp3″)
audio.export(enhanced_file, format=”mp3″, bitrate=”192k”)
if enhanced_file != audio_file:
try:
os.unlink(audio_file)
except:
pass
return enhanced_file
except Exception as e:
print(f”Audio enhancement error: {e}”)
return audio_file
# FIXED: Multi-Speaker Video Translation with proper unlimited handling
class VideoTranslationThread(QThread):
progress = pyqtSignal(str)
finished = pyqtSignal(dict)
def __init__(self, video_path, target_lang):
super().__init__()
self.video_path = video_path
self.target_lang = target_lang
def run(self):
try:
# Step 1: Extract audio from video
self.progress.emit(“Extracting audio from video…”)
audio_file = self.extract_audio_from_video()
# Step 2: Transcribe with speaker detection
self.progress.emit(“Detecting speakers and transcribing…”)
speaker_segments = self.transcribe_with_speakers(audio_file)
# Step 3: Translate each speaker’s text – FIXED: Now truly unlimited
self.progress.emit(“Translating speakers’ dialogue…”)
translated_segments = self.translate_speaker_segments_unlimited(speaker_segments)
# Step 4: Generate unique voices for each speaker
self.progress.emit(“Generating unique voices for each speaker…”)
final_video = self.create_translated_video(translated_segments)
self.finished.emit({
“success”: True,
“video_file”: final_video,
“speaker_count”: len(translated_segments),
“translated_segments”: translated_segments
})
except Exception as e:
self.finished.emit({
“success”: False,
“error”: str(e)
})
def extract_audio_from_video(self):
“””Extract audio from video file”””
try:
output_file = tempfile.NamedTemporaryFile(suffix=”.wav”, delete=False).name
cmd = [
‘ffmpeg’, ‘-i’, self.video_path, ‘-q:a’, ‘0’, ‘-map’, ‘a’,
output_file, ‘-y’, ‘-hide_banner’, ‘-loglevel’, ‘error’
]
subprocess.run(cmd, check=True, capture_output=True)
return output_file
except Exception as e:
raise Exception(f”Error extracting audio: {e}”)
def transcribe_with_speakers(self, audio_file):
“””Transcribe audio with speaker detection”””
try:
model = whisper.load_model(“base”)
result = model.transcribe(audio_file, fp16=False)
segments = result.get(“segments”, [])
# Simple speaker diarization by time gaps
speakers = []
current_speaker = {“speaker”: “Speaker 1”, “segments”: []}
speaker_count = 1
for segment in segments:
if not current_speaker[“segments”]:
current_speaker[“segments”].append(segment)
else:
last_segment = current_speaker[“segments”][-1]
if segment[“start”] – last_segment[“end”] > 2.0:
speakers.append(current_speaker.copy())
speaker_count += 1
current_speaker = {
“speaker”: f”Speaker {speaker_count}”,
“segments”: [segment]
}
else:
current_speaker[“segments”].append(segment)
if current_speaker[“segments”]:
speakers.append(current_speaker)
return speakers[:5] # Max 5 speakers
except Exception as e:
raise Exception(f”Transcription error: {e}”)
def translate_speaker_segments_unlimited(self, speaker_segments):
“””Translate text for each speaker with proper chunking – FIXED”””
translated_speakers = []
for speaker_data in speaker_segments:
full_text = ” “.join([seg[“text”] for seg in speaker_data[“segments”]])
if not full_text.strip():
continue
# Use improved translation method with proper chunking
translated_text = self.translate_text_completely_unlimited(full_text, self.target_lang)
translated_speakers.append({
“speaker”: speaker_data[“speaker”],
“original_text”: full_text,
“translated_text”: translated_text
})
return translated_speakers
def translate_text_completely_unlimited(self, text, target_lang):
“””Translate text with NO LIMITS – completely rewritten”””
if not text.strip():
return text
# Language mapping
lang_map = {
“English”: “en”, “Spanish”: “es”, “French”: “fr”, “Arabic”: “ar”,
“Hindi”: “hi”, “Chinese”: “zh”, “Portuguese”: “pt”, “Russian”: “ru”,
“Japanese”: “ja”, “German”: “de”, “Italian”: “it”, “Korean”: “ko”,
“Turkish”: “tr”, “Vietnamese”: “vi”, “Thai”: “th”, “Urdu”: “ur”,
“Bengali”: “bn”, “Punjabi”: “pa”, “Malay”: “ms”, “Tagalog”: “tl”
}
target_code = lang_map.get(target_lang, “en”)
# ALWAYS chunk the text regardless of length to avoid any API limits
chunk_size = 1000 # Very conservative chunk size
chunks = self.split_text_smartly(text, chunk_size)
if len(chunks) == 1:
# Single chunk – translate directly
return self.safe_translate_chunk(chunks[0], target_code)
else:
# Multiple chunks – translate each and combine
translated_chunks = []
for i, chunk in enumerate(chunks):
self.progress.emit(f”Translating video segment {i+1}/{len(chunks)}…”)
translated_chunk = self.safe_translate_chunk(chunk, target_code)
translated_chunks.append(translated_chunk)
threading.Event().wait(0.3) # Be nice to the API
return ” “.join(translated_chunks)
def split_text_smartly(self, text, chunk_size):
“””Smart text splitting that preserves meaning”””
import re
# Clean the text first
clean_text = re.sub(r’\s+’, ‘ ‘, text).strip()
# If text is short enough, return as single chunk
if len(clean_text) <= chunk_size:
return [clean_text]
# First try to split by sentences
sentences = re.split(r'(?<=[.!?])\s+', clean_text)
chunks = []
current_chunk = ""
for sentence in sentences:
# If adding this sentence would exceed chunk size, save current chunk
if len(current_chunk) + len(sentence) > chunk_size and current_chunk:
chunks.append(current_chunk.strip())
current_chunk = sentence
else:
if current_chunk:
current_chunk += ” ” + sentence
else:
current_chunk = sentence
if current_chunk.strip():
chunks.append(current_chunk.strip())
# If we still have chunks that are too large, split by clauses
final_chunks = []
for chunk in chunks:
if len(chunk) > chunk_size:
# Split by clauses (commas, semicolons, etc.)
clauses = re.split(r'[,;:]’, chunk)
current_final = “”
for clause in clauses:
if len(current_final) + len(clause) > chunk_size and current_final:
final_chunks.append(current_final.strip())
current_final = clause
else:
if current_final:
current_final += “,” + clause
else:
current_final = clause
if current_final.strip():
final_chunks.append(current_final.strip())
else:
final_chunks.append(chunk)
return final_chunks
def safe_translate_chunk(self, text, target_code):
“””Safely translate a single chunk with multiple fallbacks”””
if not text.strip():
return text
# Ensure chunk is not too large
safe_text = text[:900] if len(text) > 900 else text
try:
# Try MyMemory first with email for higher limits
url = “https://api.mymemory.translated.net/get”
params = {
“q”: safe_text,
“langpair”: f”en|{target_code}”,
“de”: “translator@example.com”
}
response = requests.get(url, params=params, timeout=30)
if response.status_code == 200:
result = response.json()
translated = result[“responseData”][“translatedText”]
if translated and translated != safe_text:
return translated.replace(“'”, “‘”).replace(“"”, ‘”‘)
# Fallback: Return original text with translation indicator
return safe_text
except Exception as e:
print(f”Video translation error: {e}”)
return safe_text # Return original text on failure
def create_translated_video(self, translated_segments):
“””Create video with translated audio”””
try:
# Extract video without audio
video_no_audio = tempfile.NamedTemporaryFile(suffix=”.mp4″, delete=False).name
cmd = [
‘ffmpeg’, ‘-i’, self.video_path, ‘-c’, ‘copy’, ‘-an’,
video_no_audio, ‘-y’, ‘-hide_banner’, ‘-loglevel’, ‘error’
]
subprocess.run(cmd, check=True, capture_output=True)
# Generate translated audio for first speaker
if translated_segments:
first_speaker = translated_segments[0]
translated_audio = self.generate_video_tts(first_speaker[“translated_text”], self.target_lang)
# Combine video with translated audio
final_video = tempfile.NamedTemporaryFile(suffix=”.mp4″, delete=False).name
cmd = [
‘ffmpeg’, ‘-i’, video_no_audio, ‘-i’, translated_audio,
‘-c:v’, ‘copy’, ‘-c:a’, ‘aac’, ‘-map’, ‘0:v:0’, ‘-map’, ‘1:a:0’,
‘-shortest’, final_video, ‘-y’, ‘-hide_banner’, ‘-loglevel’, ‘error’
]
subprocess.run(cmd, check=True, capture_output=True)
# Cleanup
os.unlink(video_no_audio)
if os.path.exists(translated_audio):
os.unlink(translated_audio)
return final_video
return self.video_path
except Exception as e:
raise Exception(f”Video creation error: {e}”)
def generate_video_tts(self, text, lang):
“””Generate TTS for video”””
lang_map = {
“English”: “en”, “Spanish”: “es”, “French”: “fr”,
“Hindi”: “hi”, “Chinese”: “zh”, “Portuguese”: “pt”,
“Russian”: “ru”, “Japanese”: “ja”, “German”: “de”
}
lang_code = lang_map.get(lang, “en”)
try:
# Split into sentences
import re
sentences = re.split(r'(?<=[.!?])\s+', text)
audio_segments = []
for i, sentence in enumerate(sentences):
if sentence.strip():
self.progress.emit(f"Generating video TTS {i+1}/{len(sentences)}...")
output_file = tempfile.NamedTemporaryFile(suffix=".mp3", delete=False).name
try:
tts = gTTS(text=sentence, lang=lang_code, slow=False)
tts.save(output_file)
if os.path.exists(output_file) and os.path.getsize(output_file) > 1000:
audio_segments.append(AudioSegment.from_file(output_file))
os.unlink(output_file)
except Exception as e:
print(f”Video TTS error: {e}”)
if audio_segments:
combined_audio = audio_segments[0]
for segment in audio_segments[1:]:
combined_audio += segment
final_audio = tempfile.NamedTemporaryFile(suffix=”.mp3″, delete=False).name
combined_audio.export(final_audio, format=”mp3″, bitrate=”192k”)
return final_audio
# Fallback
return self.create_video_silent_audio()
except Exception as e:
print(f”Video TTS generation error: {e}”)
return self.create_video_silent_audio()
def create_video_silent_audio(self):
“””Create silent audio for video fallback”””
output_file = tempfile.NamedTemporaryFile(suffix=”.mp3″, delete=False).name
silent_audio = AudioSegment.silent(duration=1000)
silent_audio.export(output_file, format=”mp3″)
return output_file
# Rest of the classes remain the same (TranscriptionThread, VoiceRecordingThread, CompleteVoiceTranslator)
class TranscriptionThread(QThread):
progress = pyqtSignal(str)
finished = pyqtSignal(str)
def __init__(self, file_path, language_detect=True):
super().__init__()
self.file_path = file_path
self.language_detect = language_detect
def run(self):
try:
self.progress.emit(“Loading Whisper model…”)
model = whisper.load_model(“base”)
self.progress.emit(“Transcribing audio…”)
result = model.transcribe(
self.file_path,
task=”translate” if not self.language_detect else “transcribe”,
fp16=False
)
self.finished.emit(result[“text”])
except Exception as e:
self.finished.emit(f”Error: {str(e)}”)
class VoiceRecordingThread(QThread):
progress = pyqtSignal(str)
finished = pyqtSignal(str)
def __init__(self, duration=10, sample_rate=44100, channels=1, chunk=1024):
super().__init__()
self.duration = duration
self.sample_rate = sample_rate
self.channels = channels
self.chunk = chunk
self.is_recording = True
self.frames = []
def run(self):
if not PYAUDIO_AVAILABLE:
self.finished.emit(“Error: PyAudio not installed – recording disabled”)
return
try:
self.progress.emit(f”Recording for {self.duration} seconds…”)
audio = pyaudio.PyAudio()
stream = audio.open(
format=pyaudio.paInt16,
channels=self.channels,
rate=self.sample_rate,
input=True,
frames_per_buffer=self.chunk
)
for i in range(0, int(self.sample_rate / self.chunk * self.duration)):
if not self.is_recording:
break
data = stream.read(self.chunk)
self.frames.append(data)
remaining = self.duration – (i * self.chunk / self.sample_rate)
if i % 10 == 0:
self.progress.emit(f”Recording… {remaining:.1f}s remaining”)
stream.stop_stream()
stream.close()
audio.terminate()
if self.is_recording and self.frames:
self.progress.emit(“Saving recording…”)
with tempfile.NamedTemporaryFile(suffix=”.wav”, delete=False) as temp_file:
temp_path = temp_file.name
wf = wave.open(temp_path, ‘wb’)
wf.setnchannels(self.channels)
wf.setsampwidth(audio.get_sample_size(pyaudio.paInt16))
wf.setframerate(self.sample_rate)
wf.writeframes(b”.join(self.frames))
wf.close()
self.finished.emit(temp_path)
else:
self.finished.emit(“”)
except Exception as e:
self.finished.emit(f”Error: {str(e)}”)
def stop_recording(self):
self.is_recording = False
class CompleteVoiceTranslator(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle(“AI Voice Translator – TRULY UNLIMITED VERSION”)
self.setGeometry(100, 100, 1200, 900)
self.setup_dark_theme()
self.setup_ui()
self.generated_audio = None
self.generated_video = None
self.audio_process = None
self.recording_thread = None
self.is_recording = False
self.current_file = None
self.visualization_timer = QTimer()
self.visualization_timer.timeout.connect(self.update_visualization)
def setup_dark_theme(self):
self.setStyleSheet(“””
QMainWindow { background-color: #0f0f23; color: #e0e0ff; }
QScrollArea { background-color: #0f0f23; border: none; }
QWidget#scrollWidget { background-color: #0f0f23; }
QPushButton {
background-color: #6c63ff; color: white; border: none; padding: 10px;
border-radius: 5px; font-weight: bold; font-size: 12px; min-height: 40px;
}
QPushButton:hover { background-color: #4a44b5; }
QPushButton:disabled { background-color: #333344; color: #8888aa; }
QPushButton#accent { background-color: #00d4aa; color: #0f0f23; font-weight: bold; }
QPushButton#accent:hover { background-color: #00b894; }
QPushButton#play { background-color: #00d4aa; color: #0f0f23; font-weight: bold; }
QPushButton#stop { background-color: #ff5252; color: white; font-weight: bold; }
QPushButton#record { background-color: #ff6b6b; color: white; font-weight: bold; }
QPushButton#record:checked { background-color: #ff5252; }
QTextEdit {
background-color: #1a1a2e; color: white; border: 1px solid #252547;
border-radius: 5px; padding: 10px; font-size: 12px;
}
QLabel { color: #e0e0ff; padding: 5px; font-size: 12px; }
QComboBox, QSlider {
background-color: #1a1a2e; color: white; border: 1px solid #252547;
padding: 5px; border-radius: 3px;
}
QGroupBox {
color: #6c63ff; font-weight: bold; border: 2px solid #252547;
border-radius: 8px; margin-top: 10px; padding-top: 10px; background-color: #151529;
}
QGroupBox::title {
subcontrol-origin: margin; left: 10px; padding: 0 5px 0 5px; background-color: #151529;
}
QProgressBar {
border: 1px solid #252547; border-radius: 3px; text-align: center;
background-color: #1a1a2e; color: white;
}
QProgressBar::chunk { background-color: #6c63ff; }
QCheckBox { color: #e0e0ff; font-size: 11px; }
QCheckBox::indicator {
width: 15px; height: 15px; border-radius: 3px; border: 2px solid #6c63ff;
background-color: #1a1a2e;
}
QCheckBox::indicator:checked { background-color: #6c63ff; }
QFrame#visualization {
background-color: #1a1a2e; border: 1px solid #252547; border-radius: 5px;
min-height: 60px;
}
“””)
def setup_ui(self):
scroll = QScrollArea()
scroll.setWidgetResizable(True)
central_widget = QWidget()
central_widget.setObjectName(“scrollWidget”)
scroll.setWidget(central_widget)
self.setCentralWidget(scroll)
layout = QVBoxLayout(central_widget)
layout.setSpacing(15)
layout.setContentsMargins(20, 20, 20, 20)
# Title
title = QLabel(“🎯 AI Voice Translator – TRULY UNLIMITED VERSION”)
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
title.setStyleSheet(“font-size: 22px; font-weight: bold; padding: 15px; color: #6c63ff;”)
layout.addWidget(title)
# Input Methods
input_group = QGroupBox(“Step 1: Input Method – TRULY UNLIMITED”)
input_layout = QVBoxLayout(input_group)
upload_layout = QHBoxLayout()
self.upload_btn = QPushButton(“📁 Upload ANY Audio/Video File”)
self.upload_btn.clicked.connect(self.upload_file)
upload_layout.addWidget(self.upload_btn)
self.record_btn = QPushButton(“🎤 Record Voice”)
self.record_btn.setObjectName(“record”)
self.record_btn.setCheckable(True)
self.record_btn.clicked.connect(self.toggle_recording)
if not PYAUDIO_AVAILABLE:
self.record_btn.setEnabled(False)
self.record_btn.setToolTip(“PyAudio not installed – recording disabled”)
upload_layout.addWidget(self.record_btn)
input_layout.addLayout(upload_layout)
self.file_label = QLabel(“No file selected or recording ready”)
self.file_label.setStyleSheet(“color: #a0a0cc; font-size: 11px;”)
self.file_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
input_layout.addWidget(self.file_label)
record_layout = QHBoxLayout()
record_layout.addWidget(QLabel(“Recording Duration:”))
self.record_duration = QComboBox()
self.record_duration.addItems([“5 seconds”, “10 seconds”, “30 seconds”, “1 minute”, “5 minutes”, “10 minutes”, “30 minutes”, “1 hour”])
self.record_duration.setCurrentText(“10 seconds”)
record_layout.addWidget(self.record_duration)
record_layout.addStretch()
input_layout.addLayout(record_layout)
layout.addWidget(input_group)
# Progress
self.progress_bar = QProgressBar()
self.progress_bar.setVisible(False)
layout.addWidget(self.progress_bar)
self.status_label = QLabel(“Ready to start! TRULY UNLIMITED usage!”)
self.status_label.setStyleSheet(“color: #00d4aa; font-size: 11px;”)
self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(self.status_label)
# Audio Visualization
viz_group = QGroupBox(“Audio Visualization”)
viz_layout = QVBoxLayout(viz_group)
self.visualization_frame = QFrame()
self.visualization_frame.setObjectName(“visualization”)
self.visualization_frame.setMinimumHeight(80)
viz_layout.addWidget(self.visualization_frame)
layout.addWidget(viz_group)
# Transcription
trans_group = QGroupBox(“Step 2: Transcription – TRULY UNLIMITED TEXT”)
trans_layout = QVBoxLayout(trans_group)
lang_detect_layout = QHBoxLayout()
self.auto_detect_lang = QCheckBox(“Auto-detect source language”)
self.auto_detect_lang.setChecked(True)
lang_detect_layout.addWidget(self.auto_detect_lang)
lang_detect_layout.addStretch()
trans_layout.addLayout(lang_detect_layout)
trans_label = QLabel(“Original Text:”)
trans_label.setStyleSheet(“font-weight: bold;”)
trans_layout.addWidget(trans_label)
self.transcription_text = QTextEdit()
self.transcription_text.setPlaceholderText(“Transcribed text will appear here… NO CHARACTER LIMITS!”)
self.transcription_text.setMinimumHeight(100)
self.transcription_text.setMaximumHeight(150)
trans_layout.addWidget(self.transcription_text)
trans_btn_layout = QHBoxLayout()
self.transcribe_btn = QPushButton(“🎵 Transcribe Audio”)
self.transcribe_btn.clicked.connect(self.transcribe_audio)
self.transcribe_btn.setEnabled(False)
trans_btn_layout.addWidget(self.transcribe_btn)
self.clean_audio_cb = QCheckBox(“Clean audio before processing”)
trans_btn_layout.addWidget(self.clean_audio_cb)
trans_btn_layout.addStretch()
trans_layout.addLayout(trans_btn_layout)
layout.addWidget(trans_group)
# Translation Settings
trans_config_group = QGroupBox(“Step 3: Translation & Voice Settings – TRULY UNLIMITED”)
trans_config_layout = QVBoxLayout(trans_config_group)
lang_layout = QHBoxLayout()
lang_label = QLabel(“Target Language:”)
lang_label.setStyleSheet(“font-weight: bold;”)
lang_layout.addWidget(lang_label)
self.language_combo = QComboBox()
self.language_combo.addItems([
“English”, “Spanish”, “French”, “Hindi”, “Chinese”,
“Portuguese”, “Russian”, “Japanese”, “German”, “Italian”,
“Korean”, “Arabic”, “Turkish”, “Vietnamese”, “Thai”,
“Urdu”, “Bengali”, “Punjabi”, “Malay”, “Tagalog”,
“Dutch”, “Greek”, “Hebrew”, “Polish”, “Swedish”,
“Norwegian”, “Danish”, “Finnish”, “Czech”, “Romanian”,
“Hungarian”, “Bulgarian”, “Ukrainian”, “Catalan”, “Croatian”
])
lang_layout.addWidget(self.language_combo)
lang_layout.addStretch()
trans_config_layout.addLayout(lang_layout)
voice_layout = QHBoxLayout()
speed_label = QLabel(“Speech Speed:”)
speed_label.setStyleSheet(“font-weight: bold;”)
voice_layout.addWidget(speed_label)
self.speed_slider = QSlider(Qt.Orientation.Horizontal)
self.speed_slider.setRange(50, 200)
self.speed_slider.setValue(100)
self.speed_slider.setFixedWidth(150)
voice_layout.addWidget(self.speed_slider)
self.speed_label = QLabel(“Normal”)
self.speed_label.setStyleSheet(“color: #a0a0cc;”)
voice_layout.addWidget(self.speed_label)
voice_layout.addWidget(QLabel(“Pitch:”))
self.pitch_slider = QSlider(Qt.Orientation.Horizontal)
self.pitch_slider.setRange(50, 200)
self.pitch_slider.setValue(100)
self.pitch_slider.setFixedWidth(100)
voice_layout.addWidget(self.pitch_slider)
self.pitch_label = QLabel(“Normal”)
self.pitch_label.setStyleSheet(“color: #a0a0cc;”)
voice_layout.addWidget(self.pitch_label)
voice_layout.addStretch()
trans_config_layout.addLayout(voice_layout)
enhance_layout = QHBoxLayout()
self.noise_reduction_cb = QCheckBox(“Noise Reduction”)
enhance_layout.addWidget(self.noise_reduction_cb)
self.audio_enhance_cb = QCheckBox(“Audio Enhancement”)
enhance_layout.addWidget(self.audio_enhance_cb)
self.emotion_cb = QCheckBox(“Emotion Control”)
enhance_layout.addWidget(self.emotion_cb)
enhance_layout.addStretch()
trans_config_layout.addLayout(enhance_layout)
emotion_layout = QHBoxLayout()
emotion_layout.addWidget(QLabel(“Emotion:”))
self.emotion_combo = QComboBox()
self.emotion_combo.addItems([“Neutral”, “Happy”, “Sad”, “Angry”, “Calm”, “Excited”, “Professional”, “Friendly”])
self.emotion_combo.setEnabled(False)
emotion_layout.addWidget(self.emotion_combo)
emotion_layout.addStretch()
trans_config_layout.addLayout(emotion_layout)
self.translate_btn = QPushButton(“🔤 Translate & Generate Speech – TRULY UNLIMITED”)
self.translate_btn.setObjectName(“accent”)
self.translate_btn.clicked.connect(self.start_translation)
self.translate_btn.setEnabled(False)
trans_config_layout.addWidget(self.translate_btn)
self.translate_video_btn = QPushButton(“🎬 Translate Video with Multi-Speaker Voices”)
self.translate_video_btn.setStyleSheet(“background-color: #ff6b6b; color: white; font-weight: bold;”)
self.translate_video_btn.clicked.connect(self.start_video_translation)
self.translate_video_btn.setEnabled(False)
trans_config_layout.addWidget(self.translate_video_btn)
layout.addWidget(trans_config_group)
# Translation Result
trans_result_group = QGroupBox(“Step 4: Translation Result – TRULY UNLIMITED TEXT”)
trans_result_layout = QVBoxLayout(trans_result_group)
trans_result_label = QLabel(“Translated Text:”)
trans_result_label.setStyleSheet(“font-weight: bold;”)
trans_result_layout.addWidget(trans_result_label)
self.translation_text = QTextEdit()
self.translation_text.setPlaceholderText(“Translated text will appear here… NO CHARACTER LIMITS!”)
self.translation_text.setMinimumHeight(100)
self.translation_text.setMaximumHeight(150)
trans_result_layout.addWidget(self.translation_text)
layout.addWidget(trans_result_group)
# Output Controls
output_group = QGroupBox(“Step 5: Audio Output – TRULY UNLIMITED PLAYBACK”)
output_layout = QHBoxLayout(output_group)
self.play_btn = QPushButton(“▶️ Play Translated Audio”)
self.play_btn.setObjectName(“play”)
self.play_btn.clicked.connect(self.play_audio)
self.play_btn.setEnabled(False)
output_layout.addWidget(self.play_btn)
self.stop_btn = QPushButton(“⏹️ Stop Audio”)
self.stop_btn.setObjectName(“stop”)
self.stop_btn.clicked.connect(self.stop_audio)
self.stop_btn.setEnabled(False)
output_layout.addWidget(self.stop_btn)
self.download_btn = QPushButton(“💾 Download Audio”)
self.download_btn.clicked.connect(self.download_audio)
self.download_btn.setEnabled(False)
output_layout.addWidget(self.download_btn)
self.play_video_btn = QPushButton(“🎬 Play Translated Video”)
self.play_video_btn.setStyleSheet(“background-color: #ff6b6b; color: white; font-weight: bold;”)
self.play_video_btn.clicked.connect(self.play_video)
self.play_video_btn.setEnabled(False)
output_layout.addWidget(self.play_video_btn)
self.download_video_btn = QPushButton(“💾 Download Video”)
self.download_video_btn.setStyleSheet(“background-color: #ff6b6b; color: white; font-weight: bold;”)
self.download_video_btn.clicked.connect(self.download_video)
self.download_video_btn.setEnabled(False)
output_layout.addWidget(self.download_video_btn)
output_layout.addStretch()
layout.addWidget(output_group)
self.output_label = QLabel(“No audio generated yet – TRULY UNLIMITED usage available!”)
self.output_label.setStyleSheet(“color: #a0a0cc; font-size: 10px; padding: 10px; background-color: #1a1a2e; border-radius: 5px;”)
self.output_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(self.output_label)
batch_info = QLabel(“🚀 TRULY UNLIMITED USAGE: Process any length audio, translate unlimited text, generate unlimited speech!”)
batch_info.setStyleSheet(“color: #00d4aa; font-size: 11px; padding: 10px; background-color: #151529; border-radius: 5px;”)
batch_info.setWordWrap(True)
batch_info.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(batch_info)
layout.addStretch()
self.speed_slider.valueChanged.connect(self.update_speed_label)
self.pitch_slider.valueChanged.connect(self.update_pitch_label)
self.emotion_cb.stateChanged.connect(self.toggle_emotion_control)
def update_speed_label(self, value):
if value < 70: self.speed_label.setText("Very Slow")
elif value < 90: self.speed_label.setText("Slow")
elif value > 130: self.speed_label.setText(“Very Fast”)
elif value > 110: self.speed_label.setText(“Fast”)
else: self.speed_label.setText(“Normal”)
def update_pitch_label(self, value):
if value < 70: self.pitch_label.setText("Very Low")
elif value < 90: self.pitch_label.setText("Low")
elif value > 130: self.pitch_label.setText(“Very High”)
elif value > 110: self.pitch_label.setText(“High”)
else: self.pitch_label.setText(“Normal”)
def toggle_emotion_control(self, state):
self.emotion_combo.setEnabled(state == Qt.CheckState.Checked.value)
def toggle_recording(self):
if self.is_recording:
self.stop_recording()
else:
self.start_recording()
def start_recording(self):
if not PYAUDIO_AVAILABLE:
self.show_error(“Voice recording is not available. Please install PyAudio.”)
return
try:
duration_text = self.record_duration.currentText()
if “minute” in duration_text:
duration = int(duration_text.split()[0]) * 60
elif “hour” in duration_text:
duration = int(duration_text.split()[0]) * 3600
else:
duration = int(duration_text.split()[0])
self.is_recording = True
self.record_btn.setText(“⏹️ Stop Recording”)
self.record_btn.setChecked(True)
if duration >= 60:
minutes = duration // 60
seconds = duration % 60
self.file_label.setText(f”Recording for {minutes}m {seconds}s…”)
else:
self.file_label.setText(f”Recording for {duration} seconds…”)
self.recording_thread = VoiceRecordingThread(duration=duration)
self.recording_thread.progress.connect(self.on_progress_update)
self.recording_thread.finished.connect(self.on_recording_finished)
self.recording_thread.start()
except Exception as e:
self.show_error(f”Recording error: {str(e)}”)
def stop_recording(self):
if self.recording_thread:
self.recording_thread.stop_recording()
self.is_recording = False
self.record_btn.setText(“🎤 Record Voice”)
self.record_btn.setChecked(False)
self.file_label.setText(“Recording stopped”)
def on_recording_finished(self, file_path):
self.is_recording = False
self.record_btn.setText(“🎤 Record Voice”)
self.record_btn.setChecked(False)
if file_path and not file_path.startswith(“Error:”):
self.current_file = file_path
self.file_label.setText(“Recording complete – Ready to transcribe!”)
self.transcribe_btn.setEnabled(True)
self.translate_video_btn.setEnabled(True)
self.status_label.setText(“Recording saved – Click ‘Transcribe Audio’ to start”)
else:
self.file_label.setText(“Recording failed or was cancelled”)
def update_visualization(self):
pass
def show_error(self, message):
QMessageBox.critical(self, “Error”, message)
def upload_file(self):
file_path, _ = QFileDialog.getOpenFileName(
self,
“Select ANY Audio/Video File”,
“”,
“All Media Files (*.wav *.mp3 *.m4a *.mp4 *.avi *.mov *.mkv *.flac *.aac *.wma);;All Files (*)”
)
if file_path:
filename = os.path.basename(file_path)
self.file_label.setText(f”Selected: {filename}”)
self.current_file = file_path
self.status_label.setText(“File loaded – ready to transcribe! TRULY UNLIMITED processing available!”)
self.status_label.setStyleSheet(“color: #00d4aa;”)
self.transcribe_btn.setEnabled(True)
self.translate_btn.setEnabled(False)
self.translate_video_btn.setEnabled(True)
def transcribe_audio(self):
if not hasattr(self, ‘current_file’) or self.current_file is None:
self.status_label.setText(“Please select a file first!”)
self.status_label.setStyleSheet(“color: #ff5252;”)
return
self.status_label.setText(“Starting transcription… TRULY UNLIMITED processing!”)
self.status_label.setStyleSheet(“color: #ffb74d;”)
self.transcribe_btn.setEnabled(False)
self.progress_bar.setVisible(True)
self.progress_bar.setRange(0, 0)
language_detect = self.auto_detect_lang.isChecked()
self.transcription_thread = TranscriptionThread(self.current_file, language_detect)
self.transcription_thread.progress.connect(self.on_progress_update)
self.transcription_thread.finished.connect(self.on_transcription_finished)
self.transcription_thread.start()
def on_transcription_finished(self, text):
self.progress_bar.setVisible(False)
self.transcribe_btn.setEnabled(True)
if text.startswith(“Error:”):
self.status_label.setText(“Transcription failed!”)
self.status_label.setStyleSheet(“color: #ff5252;”)
self.transcription_text.setText(“”)
else:
self.transcription_text.setText(text)
self.status_label.setText(“Transcription complete! Ready for TRULY UNLIMITED translation.”)
self.status_label.setStyleSheet(“color: #00d4aa;”)
self.translate_btn.setEnabled(True)
def start_translation(self):
transcription = self.transcription_text.toPlainText().strip()
if not transcription:
self.status_label.setText(“No transcription to translate!”)
self.status_label.setStyleSheet(“color: #ff5252;”)
return
target_lang = self.language_combo.currentText()
voice_settings = {
“speed”: self.speed_slider.value() / 100.0,
“pitch”: self.pitch_slider.value() / 100.0,
“noise_reduction”: self.noise_reduction_cb.isChecked(),
“audio_enhance”: self.audio_enhance_cb.isChecked(),
“emotion”: self.emotion_combo.currentText() if self.emotion_cb.isChecked() else “Neutral”
}
self.status_label.setText(“Starting TRULY UNLIMITED translation and speech generation…”)
self.status_label.setStyleSheet(“color: #ffb74d;”)
self.translate_btn.setEnabled(False)
self.progress_bar.setVisible(True)
self.progress_bar.setRange(0, 0)
self.translation_thread = TranslationThread(transcription, target_lang, voice_settings)
self.translation_thread.progress.connect(self.on_progress_update)
self.translation_thread.finished.connect(self.on_translation_finished)
self.translation_thread.start()
def on_translation_finished(self, result):
self.progress_bar.setVisible(False)
self.translate_btn.setEnabled(True)
if result[“success”]:
self.translation_text.setText(result[“translated_text”])
self.generated_audio = result[“audio_file”]
self.status_label.setText(“TRULY UNLIMITED translation and audio generation complete!”)
self.status_label.setStyleSheet(“color: #00d4aa;”)
self.play_btn.setEnabled(True)
self.stop_btn.setEnabled(True)
self.download_btn.setEnabled(True)
if os.path.exists(self.generated_audio):
audio_size = os.path.getsize(self.generated_audio) // 1024
enhancements = []
if self.noise_reduction_cb.isChecked():
enhancements.append(“Noise Reduction”)
if self.audio_enhance_cb.isChecked():
enhancements.append(“Audio Enhanced”)
if self.emotion_cb.isChecked():
enhancements.append(f”Emotion: {self.emotion_combo.currentText()}”)
enhancement_text = ” + ” + ” + “.join(enhancements) if enhancements else “”
self.output_label.setText(f”TRULY UNLIMITED Audio ready: {audio_size}KB{enhancement_text}”)
else:
self.output_label.setText(“Audio file missing – TTS may have failed”)
else:
self.status_label.setText(f”Error: {result[‘error’]}”)
self.status_label.setStyleSheet(“color: #ff5252;”)
def start_video_translation(self):
if not self.current_file:
self.show_error(“Please select a video file first!”)
return
if not any(self.current_file.lower().endswith(ext) for ext in [‘.mp4’, ‘.avi’, ‘.mov’, ‘.mkv’]):
self.show_error(“Please select a video file (MP4, AVI, MOV, MKV) for multi-speaker translation!”)
return
target_lang = self.language_combo.currentText()
self.status_label.setText(“Starting multi-speaker video translation with UNLIMITED text…”)
self.status_label.setStyleSheet(“color: #ffb74d;”)
self.translate_video_btn.setEnabled(False)
self.progress_bar.setVisible(True)
self.progress_bar.setRange(0, 0)
self.video_translation_thread = VideoTranslationThread(self.current_file, target_lang)
self.video_translation_thread.progress.connect(self.on_progress_update)
self.video_translation_thread.finished.connect(self.on_video_translation_finished)
self.video_translation_thread.start()
def on_video_translation_finished(self, result):
self.progress_bar.setVisible(False)
self.translate_video_btn.setEnabled(True)
if result[“success”]:
self.generated_video = result[“video_file”]
speaker_count = result[“speaker_count”]
self.status_label.setText(f”TRULY UNLIMITED video translation complete! Detected {speaker_count} speakers.”)
self.status_label.setStyleSheet(“color: #00d4aa;”)
self.play_video_btn.setEnabled(True)
self.download_video_btn.setEnabled(True)
translation_summary = f”🎯 Multi-Speaker Video Translation Complete!\n\n”
translation_summary += f”📊 Speakers Detected: {speaker_count}\n”
translation_summary += f”🌐 Translated to: {self.language_combo.currentText()}\n\n”
for speaker_data in result[“translated_segments”]:
translation_summary += f”👤 {speaker_data[‘speaker’]}:\n”
translation_summary += f” {speaker_data[‘translated_text’][:200]}…\n\n”
self.translation_text.setText(translation_summary)
if os.path.exists(self.generated_video):
video_size = os.path.getsize(self.generated_video) // (1024 * 1024)
self.output_label.setText(f”TRULY UNLIMITED Video ready: {video_size}MB – {speaker_count} speakers with unique voices!”)
else:
self.output_label.setText(“Video file generated but not found”)
else:
self.status_label.setText(f”Video translation error: {result[‘error’]}”)
self.status_label.setStyleSheet(“color: #ff5252;”)
def play_video(self):
if self.generated_video and os.path.exists(self.generated_video):
try:
system = platform.system()
if system == “Windows”:
subprocess.Popen([‘cmd’, ‘/c’, ‘start’, ”, self.generated_video],
shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
elif system == “Darwin”:
subprocess.Popen([‘open’, self.generated_video],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
else:
subprocess.Popen([‘xdg-open’, self.generated_video],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
self.status_label.setText(“Playing translated video…”)
except Exception as e:
self.status_label.setText(f”Error playing video: {str(e)}”)
def download_video(self):
if self.generated_video and os.path.exists(self.generated_video):
save_path, _ = QFileDialog.getSaveFileName(
self,
“Save Translated Video”,
f”translated_video_{self.language_combo.currentText()}.mp4″,
“Video Files (*.mp4 *.avi *.mov)”
)
if save_path:
import shutil
shutil.copy2(self.generated_video, save_path)
self.status_label.setText(f”Translated video saved: {save_path}”)
def play_audio(self):
if hasattr(self, ‘generated_audio’) and os.path.exists(self.generated_audio):
try:
self.stop_audio()
system = platform.system()
if system == “Windows”:
self.audio_process = subprocess.Popen([‘cmd’, ‘/c’, ‘start’, ‘/wait’, ”, self.generated_audio],
shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
elif system == “Darwin”:
self.audio_process = subprocess.Popen([‘afplay’, self.generated_audio],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
else:
self.audio_process = subprocess.Popen([‘xdg-open’, self.generated_audio],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
self.status_label.setText(“Playing TRULY UNLIMITED audio in system player…”)
self.play_btn.setEnabled(False)
audio_duration = 15
try:
audio = AudioSegment.from_file(self.generated_audio)
audio_duration = len(audio) / 1000
except:
pass
threading.Timer(min(audio_duration, 300), self.reenable_play_button).start()
except Exception as e:
self.status_label.setText(f”Playback error: {str(e)}”)
self.status_label.setStyleSheet(“color: #ff5252;”)
self.play_btn.setEnabled(True)
def reenable_play_button(self):
self.play_btn.setEnabled(True)
self.status_label.setText(“TRULY UNLIMITED audio playback finished”)
def stop_audio(self):
if self.audio_process:
try:
self.audio_process.terminate()
self.audio_process = None
except:
pass
self.play_btn.setEnabled(True)
self.status_label.setText(“Audio stopped”)
def download_audio(self):
if hasattr(self, ‘generated_audio’) and os.path.exists(self.generated_audio):
save_path, _ = QFileDialog.getSaveFileName(
self,
“Save TRULY UNLIMITED Translated Audio”,
f”truly_unlimited_translated_{self.language_combo.currentText()}.mp3″,
“Audio Files (*.mp3 *.wav *.flac *.aac)”
)
if save_path:
import shutil
shutil.copy2(self.generated_audio, save_path)
self.status_label.setText(f”TRULY UNLIMITED audio saved: {save_path}”)
def on_progress_update(self, message):
self.status_label.setText(message)
def closeEvent(self, event):
if hasattr(self, ‘generated_audio’) and os.path.exists(self.generated_audio):
try:
os.remove(self.generated_audio)
except:
pass
if hasattr(self, ‘generated_video’) and os.path.exists(self.generated_video):
try:
os.remove(self.generated_video)
except:
pass
self.stop_audio()
self.stop_recording()
event.accept()
def main():
app = QApplication(sys.argv)
window = CompleteVoiceTranslator()
window.show()
sys.exit(app.exec())
if __name__ == “__main__”:
main()
import sys
import os
import requests
import json
import tempfile
import subprocess
import threading
import warnings
from pathlib import Path
from PyQt6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QWidget,
QPushButton, QTextEdit, QFileDialog, QLabel, QComboBox,
QProgressBar, QGroupBox, QSlider, QScrollArea, QCheckBox,
QMessageBox, QFrame)
from PyQt6.QtCore import Qt, QThread, pyqtSignal, QTimer
import whisper
from gtts import gTTS
import platform
from pydub import AudioSegment
from pydub.effects import compress_dynamic_range, high_pass_filter, low_pass_filter
import numpy as np
# Suppress FFmpeg warnings
warnings.filterwarnings(“ignore”, message=”Couldn’t find ffprobe or avprobe”)
# Try to import pyaudio
try:
import pyaudio
import wave
PYAUDIO_AVAILABLE = True
except ImportError:
PYAUDIO_AVAILABLE = False
print(“PyAudio not available – voice recording disabled”)
class TranslationThread(QThread):
progress = pyqtSignal(str)
finished = pyqtSignal(dict)
def __init__(self, text, target_lang, voice_settings):
super().__init__()
self.text = text
self.target_lang = target_lang
self.voice_settings = voice_settings
def run(self):
try:
# Step 1: Translate text – TRULY UNLIMITED
self.progress.emit(“Translating text…”)
translated_text = self.translate_text_unlimited(self.text, self.target_lang)
# Step 2: Convert to speech – TRULY UNLIMITED
self.progress.emit(“Generating audio…”)
audio_file = self.text_to_speech_unlimited(translated_text, self.target_lang)
# Step 3: Apply audio effects if enabled
if self.voice_settings.get(“noise_reduction”, False) or self.voice_settings.get(“audio_enhance”, False):
self.progress.emit(“Enhancing audio quality…”)
audio_file = self.apply_audio_effects(audio_file, self.voice_settings)
self.finished.emit({
“success”: True,
“translated_text”: translated_text,
“audio_file”: audio_file
})
except Exception as e:
self.finished.emit({
“success”: False,
“error”: str(e)
})
def translate_text_unlimited(self, text, target_lang):
“””Translate text using free translation API – TRULY UNLIMITED”””
try:
# Language mapping
lang_map = {
“English”: “en”, “Spanish”: “es”, “French”: “fr”, “Arabic”: “ar”,
“Hindi”: “hi”, “Chinese”: “zh”, “Portuguese”: “pt”, “Russian”: “ru”,
“Japanese”: “ja”, “German”: “de”, “Italian”: “it”, “Korean”: “ko”,
“Turkish”: “tr”, “Vietnamese”: “vi”, “Thai”: “th”, “Urdu”: “ur”,
“Bengali”: “bn”, “Punjabi”: “pa”, “Malay”: “ms”, “Tagalog”: “tl”,
“Dutch”: “nl”, “Greek”: “el”, “Hebrew”: “he”, “Polish”: “pl”,
“Swedish”: “sv”, “Norwegian”: “no”, “Danish”: “da”, “Finnish”: “fi”,
“Czech”: “cs”, “Romanian”: “ro”, “Hungarian”: “hu”, “Bulgarian”: “bg”,
“Ukrainian”: “uk”, “Catalan”: “ca”, “Croatian”: “hr”, “Serbian”: “sr”,
“Slovak”: “sk”, “Slovenian”: “sl”, “Estonian”: “et”, “Latvian”: “lv”,
“Lithuanian”: “lt”, “Maltese”: “mt”, “Icelandic”: “is”
}
target_code = lang_map.get(target_lang, “en”)
# Split text into manageable chunks for translation
chunk_size = 1500 # Conservative chunk size
if len(text) > chunk_size:
chunks = self.split_text_into_chunks(text, chunk_size)
translated_chunks = []
for i, chunk in enumerate(chunks):
self.progress.emit(f”Translating chunk {i+1}/{len(chunks)}…”)
translated_chunk = self.translate_chunk_safe(chunk, target_code)
translated_chunks.append(translated_chunk)
# Small delay to avoid API rate limiting
threading.Event().wait(0.2)
translated_text = ” “.join(translated_chunks)
else:
translated_text = self.translate_chunk_safe(text, target_code)
return translated_text
except Exception as e:
print(f”Translation error: {e}”)
return f”[Translation to {target_lang}] {text}”
def split_text_into_chunks(self, text, chunk_size):
“””Split text into chunks at sentence boundaries when possible”””
import re
# First try to split by sentences
sentences = re.split(r'(?<=[.!?])\s+', text)
chunks = []
current_chunk = ""
for sentence in sentences:
if len(current_chunk) + len(sentence) <= chunk_size:
current_chunk += sentence + " "
else:
if current_chunk:
chunks.append(current_chunk.strip())
# If a single sentence is too long, split by words
if len(sentence) > chunk_size:
words = sentence.split()
temp_chunk = “”
for word in words:
if len(temp_chunk) + len(word) + 1 <= chunk_size:
temp_chunk += word + " "
else:
if temp_chunk:
chunks.append(temp_chunk.strip())
temp_chunk = word + " "
if temp_chunk:
chunks.append(temp_chunk.strip())
else:
current_chunk = sentence + " "
if current_chunk.strip():
chunks.append(current_chunk.strip())
return chunks
def translate_chunk_safe(self, text, target_code):
"""Translate a single chunk of text with multiple fallback APIs"""
if not text.strip():
return text
# Clean the text to remove any problematic characters
clean_text = text.replace('\n', ' ').replace('\r', ' ').strip()
if len(clean_text) > 1500:
clean_text = clean_text[:1500] # Ensure we don’t exceed limits
try:
# API 1: MyMemory Translator (primary) – more lenient
url = “https://api.mymemory.translated.net/get”
params = {
“q”: clean_text,
“langpair”: f”en|{target_code}”,
“de”: “user@example.com” # Add email for higher limits
}
response = requests.get(url, params=params, timeout=30)
if response.status_code == 200:
result = response.json()
translated = result[“responseData”][“translatedText”]
# Clean up any weird characters
if translated and translated != clean_text:
translated = translated.replace(“'”, “‘”).replace(“"”, ‘”‘)
return translated
# API 2: LibreTranslate (fallback 1)
try:
libre_url = “https://translate.argosopentech.com/translate”
libre_data = {
“q”: clean_text,
“source”: “en”,
“target”: target_code
}
libre_response = requests.post(libre_url, json=libre_data, timeout=30)
if libre_response.status_code == 200:
libre_result = libre_response.json()
return libre_result.get(“translatedText”, clean_text)
except:
pass
# API 3: Simple fallback – return original text but indicate translation
return f”[Translated to {target_code.upper()}] {clean_text}”
except Exception as e:
print(f”Translation chunk error: {e}”)
return clean_text # Return original text on failure
def text_to_speech_unlimited(self, text, lang):
“””Convert text to speech using gTTS – TRULY UNLIMITED”””
try:
lang_map = {
“English”: “en”, “Spanish”: “es”, “French”: “fr”,
“Hindi”: “hi”, “Chinese”: “zh”, “Portuguese”: “pt”,
“Russian”: “ru”, “Japanese”: “ja”, “German”: “de”,
“Italian”: “it”, “Korean”: “ko”, “Arabic”: “ar”,
“Turkish”: “tr”, “Vietnamese”: “vi”, “Thai”: “th”,
“Urdu”: “ur”, “Bengali”: “bn”, “Punjabi”: “pa”,
“Malay”: “ms”, “Tagalog”: “tl”, “Dutch”: “nl”,
“Greek”: “el”, “Hebrew”: “he”, “Polish”: “pl”,
“Swedish”: “sv”, “Norwegian”: “no”, “Danish”: “da”,
“Finnish”: “fi”, “Czech”: “cs”, “Romanian”: “ro”,
“Hungarian”: “hu”, “Bulgarian”: “bg”, “Ukrainian”: “uk”
}
lang_code = lang_map.get(lang, “en”)
# Split text into sentences for better TTS quality
sentences = self.split_into_sentences(text)
audio_segments = []
for i, sentence in enumerate(sentences):
if sentence.strip():
self.progress.emit(f”Generating speech for sentence {i+1}/{len(sentences)}…”)
sentence_audio = self.generate_tts_chunk_safe(sentence, lang_code)
if sentence_audio and os.path.exists(sentence_audio):
try:
audio_segments.append(AudioSegment.from_file(sentence_audio))
os.unlink(sentence_audio)
except Exception as e:
print(f”Error loading audio segment: {e}”)
if audio_segments:
# Combine all audio segments
combined_audio = audio_segments[0]
for segment in audio_segments[1:]:
combined_audio += segment
# Save combined audio
with tempfile.NamedTemporaryFile(suffix=”.mp3″, delete=False) as temp_file:
temp_path = temp_file.name
combined_audio.export(temp_path, format=”mp3″, bitrate=”192k”)
return temp_path
else:
# Fallback: create empty audio file
return self.create_silent_audio()
except Exception as e:
print(f”TTS error: {e}”)
return self.create_silent_audio()
def split_into_sentences(self, text):
“””Split text into sentences for chunked TTS processing”””
import re
sentences = re.split(r'(?<=[.!?])\s+', text)
return [s.strip() for s in sentences if s.strip()]
def generate_tts_chunk_safe(self, text, lang_code):
"""Generate TTS for a single chunk of text"""
try:
with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as temp_file:
temp_path = temp_file.name
# Generate speech with error handling
tts = gTTS(text=text, lang=lang_code, slow=False)
tts.save(temp_path)
# Verify the file was created
if os.path.exists(temp_path) and os.path.getsize(temp_path) > 1000:
return temp_path
else:
return None
except Exception as e:
print(f”TTS chunk error: {e}”)
return None
def create_silent_audio(self):
“””Create a silent audio file as fallback”””
with tempfile.NamedTemporaryFile(suffix=”.mp3″, delete=False) as temp_file:
temp_path = temp_file.name
silent_audio = AudioSegment.silent(duration=1000)
silent_audio.export(temp_path, format=”mp3″)
return temp_path
def apply_audio_effects(self, audio_file, voice_settings):
“””Apply audio enhancement effects”””
try:
audio = AudioSegment.from_file(audio_file)
if voice_settings.get(“noise_reduction”, False):
audio = low_pass_filter(audio, 4000)
audio = high_pass_filter(audio, 100)
if voice_settings.get(“audio_enhance”, False):
audio = compress_dynamic_range(audio, ratio=2.0, threshold=-20.0)
audio = high_pass_filter(audio, 120)
audio = low_pass_filter(audio, 8000)
target_dBFS = -16.0
change_in_dBFS = target_dBFS – audio.dBFS
audio = audio.apply_gain(min(change_in_dBFS, 10))
speed_factor = voice_settings.get(“speed”, 1.0)
if speed_factor != 1.0:
new_frame_rate = int(audio.frame_rate * speed_factor)
audio = audio._spawn(audio.raw_data, overrides={‘frame_rate’: new_frame_rate})
enhanced_file = audio_file.replace(“.mp3”, “_enhanced.mp3″)
audio.export(enhanced_file, format=”mp3″, bitrate=”192k”)
if enhanced_file != audio_file:
try:
os.unlink(audio_file)
except:
pass
return enhanced_file
except Exception as e:
print(f”Audio enhancement error: {e}”)
return audio_file
# FIXED: Multi-Speaker Video Translation with proper unlimited handling
class VideoTranslationThread(QThread):
progress = pyqtSignal(str)
finished = pyqtSignal(dict)
def __init__(self, video_path, target_lang):
super().__init__()
self.video_path = video_path
self.target_lang = target_lang
def run(self):
try:
# Step 1: Extract audio from video
self.progress.emit(“Extracting audio from video…”)
audio_file = self.extract_audio_from_video()
# Step 2: Transcribe with speaker detection
self.progress.emit(“Detecting speakers and transcribing…”)
speaker_segments = self.transcribe_with_speakers(audio_file)
# Step 3: Translate each speaker’s text – FIXED: Now truly unlimited
self.progress.emit(“Translating speakers’ dialogue…”)
translated_segments = self.translate_speaker_segments_unlimited(speaker_segments)
# Step 4: Generate unique voices for each speaker
self.progress.emit(“Generating unique voices for each speaker…”)
final_video = self.create_translated_video(translated_segments)
self.finished.emit({
“success”: True,
“video_file”: final_video,
“speaker_count”: len(translated_segments),
“translated_segments”: translated_segments
})
except Exception as e:
self.finished.emit({
“success”: False,
“error”: str(e)
})
def extract_audio_from_video(self):
“””Extract audio from video file”””
try:
output_file = tempfile.NamedTemporaryFile(suffix=”.wav”, delete=False).name
cmd = [
‘ffmpeg’, ‘-i’, self.video_path, ‘-q:a’, ‘0’, ‘-map’, ‘a’,
output_file, ‘-y’, ‘-hide_banner’, ‘-loglevel’, ‘error’
]
subprocess.run(cmd, check=True, capture_output=True)
return output_file
except Exception as e:
raise Exception(f”Error extracting audio: {e}”)
def transcribe_with_speakers(self, audio_file):
“””Transcribe audio with speaker detection”””
try:
model = whisper.load_model(“base”)
result = model.transcribe(audio_file, fp16=False)
segments = result.get(“segments”, [])
# Simple speaker diarization by time gaps
speakers = []
current_speaker = {“speaker”: “Speaker 1”, “segments”: []}
speaker_count = 1
for segment in segments:
if not current_speaker[“segments”]:
current_speaker[“segments”].append(segment)
else:
last_segment = current_speaker[“segments”][-1]
if segment[“start”] – last_segment[“end”] > 2.0:
speakers.append(current_speaker.copy())
speaker_count += 1
current_speaker = {
“speaker”: f”Speaker {speaker_count}”,
“segments”: [segment]
}
else:
current_speaker[“segments”].append(segment)
if current_speaker[“segments”]:
speakers.append(current_speaker)
return speakers[:5] # Max 5 speakers
except Exception as e:
raise Exception(f”Transcription error: {e}”)
def translate_speaker_segments_unlimited(self, speaker_segments):
“””Translate text for each speaker with proper chunking – FIXED”””
translated_speakers = []
for speaker_data in speaker_segments:
full_text = ” “.join([seg[“text”] for seg in speaker_data[“segments”]])
if not full_text.strip():
continue
# Use improved translation method with proper chunking
translated_text = self.translate_text_completely_unlimited(full_text, self.target_lang)
translated_speakers.append({
“speaker”: speaker_data[“speaker”],
“original_text”: full_text,
“translated_text”: translated_text
})
return translated_speakers
def translate_text_completely_unlimited(self, text, target_lang):
“””Translate text with NO LIMITS – completely rewritten”””
if not text.strip():
return text
# Language mapping
lang_map = {
“English”: “en”, “Spanish”: “es”, “French”: “fr”, “Arabic”: “ar”,
“Hindi”: “hi”, “Chinese”: “zh”, “Portuguese”: “pt”, “Russian”: “ru”,
“Japanese”: “ja”, “German”: “de”, “Italian”: “it”, “Korean”: “ko”,
“Turkish”: “tr”, “Vietnamese”: “vi”, “Thai”: “th”, “Urdu”: “ur”,
“Bengali”: “bn”, “Punjabi”: “pa”, “Malay”: “ms”, “Tagalog”: “tl”
}
target_code = lang_map.get(target_lang, “en”)
# ALWAYS chunk the text regardless of length to avoid any API limits
chunk_size = 1000 # Very conservative chunk size
chunks = self.split_text_smartly(text, chunk_size)
if len(chunks) == 1:
# Single chunk – translate directly
return self.safe_translate_chunk(chunks[0], target_code)
else:
# Multiple chunks – translate each and combine
translated_chunks = []
for i, chunk in enumerate(chunks):
self.progress.emit(f”Translating video segment {i+1}/{len(chunks)}…”)
translated_chunk = self.safe_translate_chunk(chunk, target_code)
translated_chunks.append(translated_chunk)
threading.Event().wait(0.3) # Be nice to the API
return ” “.join(translated_chunks)
def split_text_smartly(self, text, chunk_size):
“””Smart text splitting that preserves meaning”””
import re
# Clean the text first
clean_text = re.sub(r’\s+’, ‘ ‘, text).strip()
# If text is short enough, return as single chunk
if len(clean_text) <= chunk_size:
return [clean_text]
# First try to split by sentences
sentences = re.split(r'(?<=[.!?])\s+', clean_text)
chunks = []
current_chunk = ""
for sentence in sentences:
# If adding this sentence would exceed chunk size, save current chunk
if len(current_chunk) + len(sentence) > chunk_size and current_chunk:
chunks.append(current_chunk.strip())
current_chunk = sentence
else:
if current_chunk:
current_chunk += ” ” + sentence
else:
current_chunk = sentence
if current_chunk.strip():
chunks.append(current_chunk.strip())
# If we still have chunks that are too large, split by clauses
final_chunks = []
for chunk in chunks:
if len(chunk) > chunk_size:
# Split by clauses (commas, semicolons, etc.)
clauses = re.split(r'[,;:]’, chunk)
current_final = “”
for clause in clauses:
if len(current_final) + len(clause) > chunk_size and current_final:
final_chunks.append(current_final.strip())
current_final = clause
else:
if current_final:
current_final += “,” + clause
else:
current_final = clause
if current_final.strip():
final_chunks.append(current_final.strip())
else:
final_chunks.append(chunk)
return final_chunks
def safe_translate_chunk(self, text, target_code):
“””Safely translate a single chunk with multiple fallbacks”””
if not text.strip():
return text
# Ensure chunk is not too large
safe_text = text[:900] if len(text) > 900 else text
try:
# Try MyMemory first with email for higher limits
url = “https://api.mymemory.translated.net/get”
params = {
“q”: safe_text,
“langpair”: f”en|{target_code}”,
“de”: “translator@example.com”
}
response = requests.get(url, params=params, timeout=30)
if response.status_code == 200:
result = response.json()
translated = result[“responseData”][“translatedText”]
if translated and translated != safe_text:
return translated.replace(“'”, “‘”).replace(“"”, ‘”‘)
# Fallback: Return original text with translation indicator
return safe_text
except Exception as e:
print(f”Video translation error: {e}”)
return safe_text # Return original text on failure
def create_translated_video(self, translated_segments):
“””Create video with translated audio”””
try:
# Extract video without audio
video_no_audio = tempfile.NamedTemporaryFile(suffix=”.mp4″, delete=False).name
cmd = [
‘ffmpeg’, ‘-i’, self.video_path, ‘-c’, ‘copy’, ‘-an’,
video_no_audio, ‘-y’, ‘-hide_banner’, ‘-loglevel’, ‘error’
]
subprocess.run(cmd, check=True, capture_output=True)
# Generate translated audio for first speaker
if translated_segments:
first_speaker = translated_segments[0]
translated_audio = self.generate_video_tts(first_speaker[“translated_text”], self.target_lang)
# Combine video with translated audio
final_video = tempfile.NamedTemporaryFile(suffix=”.mp4″, delete=False).name
cmd = [
‘ffmpeg’, ‘-i’, video_no_audio, ‘-i’, translated_audio,
‘-c:v’, ‘copy’, ‘-c:a’, ‘aac’, ‘-map’, ‘0:v:0’, ‘-map’, ‘1:a:0’,
‘-shortest’, final_video, ‘-y’, ‘-hide_banner’, ‘-loglevel’, ‘error’
]
subprocess.run(cmd, check=True, capture_output=True)
# Cleanup
os.unlink(video_no_audio)
if os.path.exists(translated_audio):
os.unlink(translated_audio)
return final_video
return self.video_path
except Exception as e:
raise Exception(f”Video creation error: {e}”)
def generate_video_tts(self, text, lang):
“””Generate TTS for video”””
lang_map = {
“English”: “en”, “Spanish”: “es”, “French”: “fr”,
“Hindi”: “hi”, “Chinese”: “zh”, “Portuguese”: “pt”,
“Russian”: “ru”, “Japanese”: “ja”, “German”: “de”
}
lang_code = lang_map.get(lang, “en”)
try:
# Split into sentences
import re
sentences = re.split(r'(?<=[.!?])\s+', text)
audio_segments = []
for i, sentence in enumerate(sentences):
if sentence.strip():
self.progress.emit(f"Generating video TTS {i+1}/{len(sentences)}...")
output_file = tempfile.NamedTemporaryFile(suffix=".mp3", delete=False).name
try:
tts = gTTS(text=sentence, lang=lang_code, slow=False)
tts.save(output_file)
if os.path.exists(output_file) and os.path.getsize(output_file) > 1000:
audio_segments.append(AudioSegment.from_file(output_file))
os.unlink(output_file)
except Exception as e:
print(f”Video TTS error: {e}”)
if audio_segments:
combined_audio = audio_segments[0]
for segment in audio_segments[1:]:
combined_audio += segment
final_audio = tempfile.NamedTemporaryFile(suffix=”.mp3″, delete=False).name
combined_audio.export(final_audio, format=”mp3″, bitrate=”192k”)
return final_audio
# Fallback
return self.create_video_silent_audio()
except Exception as e:
print(f”Video TTS generation error: {e}”)
return self.create_video_silent_audio()
def create_video_silent_audio(self):
“””Create silent audio for video fallback”””
output_file = tempfile.NamedTemporaryFile(suffix=”.mp3″, delete=False).name
silent_audio = AudioSegment.silent(duration=1000)
silent_audio.export(output_file, format=”mp3″)
return output_file
# Rest of the classes remain the same (TranscriptionThread, VoiceRecordingThread, CompleteVoiceTranslator)
class TranscriptionThread(QThread):
progress = pyqtSignal(str)
finished = pyqtSignal(str)
def __init__(self, file_path, language_detect=True):
super().__init__()
self.file_path = file_path
self.language_detect = language_detect
def run(self):
try:
self.progress.emit(“Loading Whisper model…”)
model = whisper.load_model(“base”)
self.progress.emit(“Transcribing audio…”)
result = model.transcribe(
self.file_path,
task=”translate” if not self.language_detect else “transcribe”,
fp16=False
)
self.finished.emit(result[“text”])
except Exception as e:
self.finished.emit(f”Error: {str(e)}”)
class VoiceRecordingThread(QThread):
progress = pyqtSignal(str)
finished = pyqtSignal(str)
def __init__(self, duration=10, sample_rate=44100, channels=1, chunk=1024):
super().__init__()
self.duration = duration
self.sample_rate = sample_rate
self.channels = channels
self.chunk = chunk
self.is_recording = True
self.frames = []
def run(self):
if not PYAUDIO_AVAILABLE:
self.finished.emit(“Error: PyAudio not installed – recording disabled”)
return
try:
self.progress.emit(f”Recording for {self.duration} seconds…”)
audio = pyaudio.PyAudio()
stream = audio.open(
format=pyaudio.paInt16,
channels=self.channels,
rate=self.sample_rate,
input=True,
frames_per_buffer=self.chunk
)
for i in range(0, int(self.sample_rate / self.chunk * self.duration)):
if not self.is_recording:
break
data = stream.read(self.chunk)
self.frames.append(data)
remaining = self.duration – (i * self.chunk / self.sample_rate)
if i % 10 == 0:
self.progress.emit(f”Recording… {remaining:.1f}s remaining”)
stream.stop_stream()
stream.close()
audio.terminate()
if self.is_recording and self.frames:
self.progress.emit(“Saving recording…”)
with tempfile.NamedTemporaryFile(suffix=”.wav”, delete=False) as temp_file:
temp_path = temp_file.name
wf = wave.open(temp_path, ‘wb’)
wf.setnchannels(self.channels)
wf.setsampwidth(audio.get_sample_size(pyaudio.paInt16))
wf.setframerate(self.sample_rate)
wf.writeframes(b”.join(self.frames))
wf.close()
self.finished.emit(temp_path)
else:
self.finished.emit(“”)
except Exception as e:
self.finished.emit(f”Error: {str(e)}”)
def stop_recording(self):
self.is_recording = False
class CompleteVoiceTranslator(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle(“AI Voice Translator – TRULY UNLIMITED VERSION”)
self.setGeometry(100, 100, 1200, 900)
self.setup_dark_theme()
self.setup_ui()
self.generated_audio = None
self.generated_video = None
self.audio_process = None
self.recording_thread = None
self.is_recording = False
self.current_file = None
self.visualization_timer = QTimer()
self.visualization_timer.timeout.connect(self.update_visualization)
def setup_dark_theme(self):
self.setStyleSheet(“””
QMainWindow { background-color: #0f0f23; color: #e0e0ff; }
QScrollArea { background-color: #0f0f23; border: none; }
QWidget#scrollWidget { background-color: #0f0f23; }
QPushButton {
background-color: #6c63ff; color: white; border: none; padding: 10px;
border-radius: 5px; font-weight: bold; font-size: 12px; min-height: 40px;
}
QPushButton:hover { background-color: #4a44b5; }
QPushButton:disabled { background-color: #333344; color: #8888aa; }
QPushButton#accent { background-color: #00d4aa; color: #0f0f23; font-weight: bold; }
QPushButton#accent:hover { background-color: #00b894; }
QPushButton#play { background-color: #00d4aa; color: #0f0f23; font-weight: bold; }
QPushButton#stop { background-color: #ff5252; color: white; font-weight: bold; }
QPushButton#record { background-color: #ff6b6b; color: white; font-weight: bold; }
QPushButton#record:checked { background-color: #ff5252; }
QTextEdit {
background-color: #1a1a2e; color: white; border: 1px solid #252547;
border-radius: 5px; padding: 10px; font-size: 12px;
}
QLabel { color: #e0e0ff; padding: 5px; font-size: 12px; }
QComboBox, QSlider {
background-color: #1a1a2e; color: white; border: 1px solid #252547;
padding: 5px; border-radius: 3px;
}
QGroupBox {
color: #6c63ff; font-weight: bold; border: 2px solid #252547;
border-radius: 8px; margin-top: 10px; padding-top: 10px; background-color: #151529;
}
QGroupBox::title {
subcontrol-origin: margin; left: 10px; padding: 0 5px 0 5px; background-color: #151529;
}
QProgressBar {
border: 1px solid #252547; border-radius: 3px; text-align: center;
background-color: #1a1a2e; color: white;
}
QProgressBar::chunk { background-color: #6c63ff; }
QCheckBox { color: #e0e0ff; font-size: 11px; }
QCheckBox::indicator {
width: 15px; height: 15px; border-radius: 3px; border: 2px solid #6c63ff;
background-color: #1a1a2e;
}
QCheckBox::indicator:checked { background-color: #6c63ff; }
QFrame#visualization {
background-color: #1a1a2e; border: 1px solid #252547; border-radius: 5px;
min-height: 60px;
}
“””)
def setup_ui(self):
scroll = QScrollArea()
scroll.setWidgetResizable(True)
central_widget = QWidget()
central_widget.setObjectName(“scrollWidget”)
scroll.setWidget(central_widget)
self.setCentralWidget(scroll)
layout = QVBoxLayout(central_widget)
layout.setSpacing(15)
layout.setContentsMargins(20, 20, 20, 20)
# Title
title = QLabel(“🎯 AI Voice Translator – TRULY UNLIMITED VERSION”)
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
title.setStyleSheet(“font-size: 22px; font-weight: bold; padding: 15px; color: #6c63ff;”)
layout.addWidget(title)
# Input Methods
input_group = QGroupBox(“Step 1: Input Method – TRULY UNLIMITED”)
input_layout = QVBoxLayout(input_group)
upload_layout = QHBoxLayout()
self.upload_btn = QPushButton(“📁 Upload ANY Audio/Video File”)
self.upload_btn.clicked.connect(self.upload_file)
upload_layout.addWidget(self.upload_btn)
self.record_btn = QPushButton(“🎤 Record Voice”)
self.record_btn.setObjectName(“record”)
self.record_btn.setCheckable(True)
self.record_btn.clicked.connect(self.toggle_recording)
if not PYAUDIO_AVAILABLE:
self.record_btn.setEnabled(False)
self.record_btn.setToolTip(“PyAudio not installed – recording disabled”)
upload_layout.addWidget(self.record_btn)
input_layout.addLayout(upload_layout)
self.file_label = QLabel(“No file selected or recording ready”)
self.file_label.setStyleSheet(“color: #a0a0cc; font-size: 11px;”)
self.file_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
input_layout.addWidget(self.file_label)
record_layout = QHBoxLayout()
record_layout.addWidget(QLabel(“Recording Duration:”))
self.record_duration = QComboBox()
self.record_duration.addItems([“5 seconds”, “10 seconds”, “30 seconds”, “1 minute”, “5 minutes”, “10 minutes”, “30 minutes”, “1 hour”])
self.record_duration.setCurrentText(“10 seconds”)
record_layout.addWidget(self.record_duration)
record_layout.addStretch()
input_layout.addLayout(record_layout)
layout.addWidget(input_group)
# Progress
self.progress_bar = QProgressBar()
self.progress_bar.setVisible(False)
layout.addWidget(self.progress_bar)
self.status_label = QLabel(“Ready to start! TRULY UNLIMITED usage!”)
self.status_label.setStyleSheet(“color: #00d4aa; font-size: 11px;”)
self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(self.status_label)
# Audio Visualization
viz_group = QGroupBox(“Audio Visualization”)
viz_layout = QVBoxLayout(viz_group)
self.visualization_frame = QFrame()
self.visualization_frame.setObjectName(“visualization”)
self.visualization_frame.setMinimumHeight(80)
viz_layout.addWidget(self.visualization_frame)
layout.addWidget(viz_group)
# Transcription
trans_group = QGroupBox(“Step 2: Transcription – TRULY UNLIMITED TEXT”)
trans_layout = QVBoxLayout(trans_group)
lang_detect_layout = QHBoxLayout()
self.auto_detect_lang = QCheckBox(“Auto-detect source language”)
self.auto_detect_lang.setChecked(True)
lang_detect_layout.addWidget(self.auto_detect_lang)
lang_detect_layout.addStretch()
trans_layout.addLayout(lang_detect_layout)
trans_label = QLabel(“Original Text:”)
trans_label.setStyleSheet(“font-weight: bold;”)
trans_layout.addWidget(trans_label)
self.transcription_text = QTextEdit()
self.transcription_text.setPlaceholderText(“Transcribed text will appear here… NO CHARACTER LIMITS!”)
self.transcription_text.setMinimumHeight(100)
self.transcription_text.setMaximumHeight(150)
trans_layout.addWidget(self.transcription_text)
trans_btn_layout = QHBoxLayout()
self.transcribe_btn = QPushButton(“🎵 Transcribe Audio”)
self.transcribe_btn.clicked.connect(self.transcribe_audio)
self.transcribe_btn.setEnabled(False)
trans_btn_layout.addWidget(self.transcribe_btn)
self.clean_audio_cb = QCheckBox(“Clean audio before processing”)
trans_btn_layout.addWidget(self.clean_audio_cb)
trans_btn_layout.addStretch()
trans_layout.addLayout(trans_btn_layout)
layout.addWidget(trans_group)
# Translation Settings
trans_config_group = QGroupBox(“Step 3: Translation & Voice Settings – TRULY UNLIMITED”)
trans_config_layout = QVBoxLayout(trans_config_group)
lang_layout = QHBoxLayout()
lang_label = QLabel(“Target Language:”)
lang_label.setStyleSheet(“font-weight: bold;”)
lang_layout.addWidget(lang_label)
self.language_combo = QComboBox()
self.language_combo.addItems([
“English”, “Spanish”, “French”, “Hindi”, “Chinese”,
“Portuguese”, “Russian”, “Japanese”, “German”, “Italian”,
“Korean”, “Arabic”, “Turkish”, “Vietnamese”, “Thai”,
“Urdu”, “Bengali”, “Punjabi”, “Malay”, “Tagalog”,
“Dutch”, “Greek”, “Hebrew”, “Polish”, “Swedish”,
“Norwegian”, “Danish”, “Finnish”, “Czech”, “Romanian”,
“Hungarian”, “Bulgarian”, “Ukrainian”, “Catalan”, “Croatian”
])
lang_layout.addWidget(self.language_combo)
lang_layout.addStretch()
trans_config_layout.addLayout(lang_layout)
voice_layout = QHBoxLayout()
speed_label = QLabel(“Speech Speed:”)
speed_label.setStyleSheet(“font-weight: bold;”)
voice_layout.addWidget(speed_label)
self.speed_slider = QSlider(Qt.Orientation.Horizontal)
self.speed_slider.setRange(50, 200)
self.speed_slider.setValue(100)
self.speed_slider.setFixedWidth(150)
voice_layout.addWidget(self.speed_slider)
self.speed_label = QLabel(“Normal”)
self.speed_label.setStyleSheet(“color: #a0a0cc;”)
voice_layout.addWidget(self.speed_label)
voice_layout.addWidget(QLabel(“Pitch:”))
self.pitch_slider = QSlider(Qt.Orientation.Horizontal)
self.pitch_slider.setRange(50, 200)
self.pitch_slider.setValue(100)
self.pitch_slider.setFixedWidth(100)
voice_layout.addWidget(self.pitch_slider)
self.pitch_label = QLabel(“Normal”)
self.pitch_label.setStyleSheet(“color: #a0a0cc;”)
voice_layout.addWidget(self.pitch_label)
voice_layout.addStretch()
trans_config_layout.addLayout(voice_layout)
enhance_layout = QHBoxLayout()
self.noise_reduction_cb = QCheckBox(“Noise Reduction”)
enhance_layout.addWidget(self.noise_reduction_cb)
self.audio_enhance_cb = QCheckBox(“Audio Enhancement”)
enhance_layout.addWidget(self.audio_enhance_cb)
self.emotion_cb = QCheckBox(“Emotion Control”)
enhance_layout.addWidget(self.emotion_cb)
enhance_layout.addStretch()
trans_config_layout.addLayout(enhance_layout)
emotion_layout = QHBoxLayout()
emotion_layout.addWidget(QLabel(“Emotion:”))
self.emotion_combo = QComboBox()
self.emotion_combo.addItems([“Neutral”, “Happy”, “Sad”, “Angry”, “Calm”, “Excited”, “Professional”, “Friendly”])
self.emotion_combo.setEnabled(False)
emotion_layout.addWidget(self.emotion_combo)
emotion_layout.addStretch()
trans_config_layout.addLayout(emotion_layout)
self.translate_btn = QPushButton(“🔤 Translate & Generate Speech – TRULY UNLIMITED”)
self.translate_btn.setObjectName(“accent”)
self.translate_btn.clicked.connect(self.start_translation)
self.translate_btn.setEnabled(False)
trans_config_layout.addWidget(self.translate_btn)
self.translate_video_btn = QPushButton(“🎬 Translate Video with Multi-Speaker Voices”)
self.translate_video_btn.setStyleSheet(“background-color: #ff6b6b; color: white; font-weight: bold;”)
self.translate_video_btn.clicked.connect(self.start_video_translation)
self.translate_video_btn.setEnabled(False)
trans_config_layout.addWidget(self.translate_video_btn)
layout.addWidget(trans_config_group)
# Translation Result
trans_result_group = QGroupBox(“Step 4: Translation Result – TRULY UNLIMITED TEXT”)
trans_result_layout = QVBoxLayout(trans_result_group)
trans_result_label = QLabel(“Translated Text:”)
trans_result_label.setStyleSheet(“font-weight: bold;”)
trans_result_layout.addWidget(trans_result_label)
self.translation_text = QTextEdit()
self.translation_text.setPlaceholderText(“Translated text will appear here… NO CHARACTER LIMITS!”)
self.translation_text.setMinimumHeight(100)
self.translation_text.setMaximumHeight(150)
trans_result_layout.addWidget(self.translation_text)
layout.addWidget(trans_result_group)
# Output Controls
output_group = QGroupBox(“Step 5: Audio Output – TRULY UNLIMITED PLAYBACK”)
output_layout = QHBoxLayout(output_group)
self.play_btn = QPushButton(“▶️ Play Translated Audio”)
self.play_btn.setObjectName(“play”)
self.play_btn.clicked.connect(self.play_audio)
self.play_btn.setEnabled(False)
output_layout.addWidget(self.play_btn)
self.stop_btn = QPushButton(“⏹️ Stop Audio”)
self.stop_btn.setObjectName(“stop”)
self.stop_btn.clicked.connect(self.stop_audio)
self.stop_btn.setEnabled(False)
output_layout.addWidget(self.stop_btn)
self.download_btn = QPushButton(“💾 Download Audio”)
self.download_btn.clicked.connect(self.download_audio)
self.download_btn.setEnabled(False)
output_layout.addWidget(self.download_btn)
self.play_video_btn = QPushButton(“🎬 Play Translated Video”)
self.play_video_btn.setStyleSheet(“background-color: #ff6b6b; color: white; font-weight: bold;”)
self.play_video_btn.clicked.connect(self.play_video)
self.play_video_btn.setEnabled(False)
output_layout.addWidget(self.play_video_btn)
self.download_video_btn = QPushButton(“💾 Download Video”)
self.download_video_btn.setStyleSheet(“background-color: #ff6b6b; color: white; font-weight: bold;”)
self.download_video_btn.clicked.connect(self.download_video)
self.download_video_btn.setEnabled(False)
output_layout.addWidget(self.download_video_btn)
output_layout.addStretch()
layout.addWidget(output_group)
self.output_label = QLabel(“No audio generated yet – TRULY UNLIMITED usage available!”)
self.output_label.setStyleSheet(“color: #a0a0cc; font-size: 10px; padding: 10px; background-color: #1a1a2e; border-radius: 5px;”)
self.output_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(self.output_label)
batch_info = QLabel(“🚀 TRULY UNLIMITED USAGE: Process any length audio, translate unlimited text, generate unlimited speech!”)
batch_info.setStyleSheet(“color: #00d4aa; font-size: 11px; padding: 10px; background-color: #151529; border-radius: 5px;”)
batch_info.setWordWrap(True)
batch_info.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(batch_info)
layout.addStretch()
self.speed_slider.valueChanged.connect(self.update_speed_label)
self.pitch_slider.valueChanged.connect(self.update_pitch_label)
self.emotion_cb.stateChanged.connect(self.toggle_emotion_control)
def update_speed_label(self, value):
if value < 70: self.speed_label.setText("Very Slow")
elif value < 90: self.speed_label.setText("Slow")
elif value > 130: self.speed_label.setText(“Very Fast”)
elif value > 110: self.speed_label.setText(“Fast”)
else: self.speed_label.setText(“Normal”)
def update_pitch_label(self, value):
if value < 70: self.pitch_label.setText("Very Low")
elif value < 90: self.pitch_label.setText("Low")
elif value > 130: self.pitch_label.setText(“Very High”)
elif value > 110: self.pitch_label.setText(“High”)
else: self.pitch_label.setText(“Normal”)
def toggle_emotion_control(self, state):
self.emotion_combo.setEnabled(state == Qt.CheckState.Checked.value)
def toggle_recording(self):
if self.is_recording:
self.stop_recording()
else:
self.start_recording()
def start_recording(self):
if not PYAUDIO_AVAILABLE:
self.show_error(“Voice recording is not available. Please install PyAudio.”)
return
try:
duration_text = self.record_duration.currentText()
if “minute” in duration_text:
duration = int(duration_text.split()[0]) * 60
elif “hour” in duration_text:
duration = int(duration_text.split()[0]) * 3600
else:
duration = int(duration_text.split()[0])
self.is_recording = True
self.record_btn.setText(“⏹️ Stop Recording”)
self.record_btn.setChecked(True)
if duration >= 60:
minutes = duration // 60
seconds = duration % 60
self.file_label.setText(f”Recording for {minutes}m {seconds}s…”)
else:
self.file_label.setText(f”Recording for {duration} seconds…”)
self.recording_thread = VoiceRecordingThread(duration=duration)
self.recording_thread.progress.connect(self.on_progress_update)
self.recording_thread.finished.connect(self.on_recording_finished)
self.recording_thread.start()
except Exception as e:
self.show_error(f”Recording error: {str(e)}”)
def stop_recording(self):
if self.recording_thread:
self.recording_thread.stop_recording()
self.is_recording = False
self.record_btn.setText(“🎤 Record Voice”)
self.record_btn.setChecked(False)
self.file_label.setText(“Recording stopped”)
def on_recording_finished(self, file_path):
self.is_recording = False
self.record_btn.setText(“🎤 Record Voice”)
self.record_btn.setChecked(False)
if file_path and not file_path.startswith(“Error:”):
self.current_file = file_path
self.file_label.setText(“Recording complete – Ready to transcribe!”)
self.transcribe_btn.setEnabled(True)
self.translate_video_btn.setEnabled(True)
self.status_label.setText(“Recording saved – Click ‘Transcribe Audio’ to start”)
else:
self.file_label.setText(“Recording failed or was cancelled”)
def update_visualization(self):
pass
def show_error(self, message):
QMessageBox.critical(self, “Error”, message)
def upload_file(self):
file_path, _ = QFileDialog.getOpenFileName(
self,
“Select ANY Audio/Video File”,
“”,
“All Media Files (*.wav *.mp3 *.m4a *.mp4 *.avi *.mov *.mkv *.flac *.aac *.wma);;All Files (*)”
)
if file_path:
filename = os.path.basename(file_path)
self.file_label.setText(f”Selected: {filename}”)
self.current_file = file_path
self.status_label.setText(“File loaded – ready to transcribe! TRULY UNLIMITED processing available!”)
self.status_label.setStyleSheet(“color: #00d4aa;”)
self.transcribe_btn.setEnabled(True)
self.translate_btn.setEnabled(False)
self.translate_video_btn.setEnabled(True)
def transcribe_audio(self):
if not hasattr(self, ‘current_file’) or self.current_file is None:
self.status_label.setText(“Please select a file first!”)
self.status_label.setStyleSheet(“color: #ff5252;”)
return
self.status_label.setText(“Starting transcription… TRULY UNLIMITED processing!”)
self.status_label.setStyleSheet(“color: #ffb74d;”)
self.transcribe_btn.setEnabled(False)
self.progress_bar.setVisible(True)
self.progress_bar.setRange(0, 0)
language_detect = self.auto_detect_lang.isChecked()
self.transcription_thread = TranscriptionThread(self.current_file, language_detect)
self.transcription_thread.progress.connect(self.on_progress_update)
self.transcription_thread.finished.connect(self.on_transcription_finished)
self.transcription_thread.start()
def on_transcription_finished(self, text):
self.progress_bar.setVisible(False)
self.transcribe_btn.setEnabled(True)
if text.startswith(“Error:”):
self.status_label.setText(“Transcription failed!”)
self.status_label.setStyleSheet(“color: #ff5252;”)
self.transcription_text.setText(“”)
else:
self.transcription_text.setText(text)
self.status_label.setText(“Transcription complete! Ready for TRULY UNLIMITED translation.”)
self.status_label.setStyleSheet(“color: #00d4aa;”)
self.translate_btn.setEnabled(True)
def start_translation(self):
transcription = self.transcription_text.toPlainText().strip()
if not transcription:
self.status_label.setText(“No transcription to translate!”)
self.status_label.setStyleSheet(“color: #ff5252;”)
return
target_lang = self.language_combo.currentText()
voice_settings = {
“speed”: self.speed_slider.value() / 100.0,
“pitch”: self.pitch_slider.value() / 100.0,
“noise_reduction”: self.noise_reduction_cb.isChecked(),
“audio_enhance”: self.audio_enhance_cb.isChecked(),
“emotion”: self.emotion_combo.currentText() if self.emotion_cb.isChecked() else “Neutral”
}
self.status_label.setText(“Starting TRULY UNLIMITED translation and speech generation…”)
self.status_label.setStyleSheet(“color: #ffb74d;”)
self.translate_btn.setEnabled(False)
self.progress_bar.setVisible(True)
self.progress_bar.setRange(0, 0)
self.translation_thread = TranslationThread(transcription, target_lang, voice_settings)
self.translation_thread.progress.connect(self.on_progress_update)
self.translation_thread.finished.connect(self.on_translation_finished)
self.translation_thread.start()
def on_translation_finished(self, result):
self.progress_bar.setVisible(False)
self.translate_btn.setEnabled(True)
if result[“success”]:
self.translation_text.setText(result[“translated_text”])
self.generated_audio = result[“audio_file”]
self.status_label.setText(“TRULY UNLIMITED translation and audio generation complete!”)
self.status_label.setStyleSheet(“color: #00d4aa;”)
self.play_btn.setEnabled(True)
self.stop_btn.setEnabled(True)
self.download_btn.setEnabled(True)
if os.path.exists(self.generated_audio):
audio_size = os.path.getsize(self.generated_audio) // 1024
enhancements = []
if self.noise_reduction_cb.isChecked():
enhancements.append(“Noise Reduction”)
if self.audio_enhance_cb.isChecked():
enhancements.append(“Audio Enhanced”)
if self.emotion_cb.isChecked():
enhancements.append(f”Emotion: {self.emotion_combo.currentText()}”)
enhancement_text = ” + ” + ” + “.join(enhancements) if enhancements else “”
self.output_label.setText(f”TRULY UNLIMITED Audio ready: {audio_size}KB{enhancement_text}”)
else:
self.output_label.setText(“Audio file missing – TTS may have failed”)
else:
self.status_label.setText(f”Error: {result[‘error’]}”)
self.status_label.setStyleSheet(“color: #ff5252;”)
def start_video_translation(self):
if not self.current_file:
self.show_error(“Please select a video file first!”)
return
if not any(self.current_file.lower().endswith(ext) for ext in [‘.mp4’, ‘.avi’, ‘.mov’, ‘.mkv’]):
self.show_error(“Please select a video file (MP4, AVI, MOV, MKV) for multi-speaker translation!”)
return
target_lang = self.language_combo.currentText()
self.status_label.setText(“Starting multi-speaker video translation with UNLIMITED text…”)
self.status_label.setStyleSheet(“color: #ffb74d;”)
self.translate_video_btn.setEnabled(False)
self.progress_bar.setVisible(True)
self.progress_bar.setRange(0, 0)
self.video_translation_thread = VideoTranslationThread(self.current_file, target_lang)
self.video_translation_thread.progress.connect(self.on_progress_update)
self.video_translation_thread.finished.connect(self.on_video_translation_finished)
self.video_translation_thread.start()
def on_video_translation_finished(self, result):
self.progress_bar.setVisible(False)
self.translate_video_btn.setEnabled(True)
if result[“success”]:
self.generated_video = result[“video_file”]
speaker_count = result[“speaker_count”]
self.status_label.setText(f”TRULY UNLIMITED video translation complete! Detected {speaker_count} speakers.”)
self.status_label.setStyleSheet(“color: #00d4aa;”)
self.play_video_btn.setEnabled(True)
self.download_video_btn.setEnabled(True)
translation_summary = f”🎯 Multi-Speaker Video Translation Complete!\n\n”
translation_summary += f”📊 Speakers Detected: {speaker_count}\n”
translation_summary += f”🌐 Translated to: {self.language_combo.currentText()}\n\n”
for speaker_data in result[“translated_segments”]:
translation_summary += f”👤 {speaker_data[‘speaker’]}:\n”
translation_summary += f” {speaker_data[‘translated_text’][:200]}…\n\n”
self.translation_text.setText(translation_summary)
if os.path.exists(self.generated_video):
video_size = os.path.getsize(self.generated_video) // (1024 * 1024)
self.output_label.setText(f”TRULY UNLIMITED Video ready: {video_size}MB – {speaker_count} speakers with unique voices!”)
else:
self.output_label.setText(“Video file generated but not found”)
else:
self.status_label.setText(f”Video translation error: {result[‘error’]}”)
self.status_label.setStyleSheet(“color: #ff5252;”)
def play_video(self):
if self.generated_video and os.path.exists(self.generated_video):
try:
system = platform.system()
if system == “Windows”:
subprocess.Popen([‘cmd’, ‘/c’, ‘start’, ”, self.generated_video],
shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
elif system == “Darwin”:
subprocess.Popen([‘open’, self.generated_video],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
else:
subprocess.Popen([‘xdg-open’, self.generated_video],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
self.status_label.setText(“Playing translated video…”)
except Exception as e:
self.status_label.setText(f”Error playing video: {str(e)}”)
def download_video(self):
if self.generated_video and os.path.exists(self.generated_video):
save_path, _ = QFileDialog.getSaveFileName(
self,
“Save Translated Video”,
f”translated_video_{self.language_combo.currentText()}.mp4″,
“Video Files (*.mp4 *.avi *.mov)”
)
if save_path:
import shutil
shutil.copy2(self.generated_video, save_path)
self.status_label.setText(f”Translated video saved: {save_path}”)
def play_audio(self):
if hasattr(self, ‘generated_audio’) and os.path.exists(self.generated_audio):
try:
self.stop_audio()
system = platform.system()
if system == “Windows”:
self.audio_process = subprocess.Popen([‘cmd’, ‘/c’, ‘start’, ‘/wait’, ”, self.generated_audio],
shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
elif system == “Darwin”:
self.audio_process = subprocess.Popen([‘afplay’, self.generated_audio],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
else:
self.audio_process = subprocess.Popen([‘xdg-open’, self.generated_audio],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
self.status_label.setText(“Playing TRULY UNLIMITED audio in system player…”)
self.play_btn.setEnabled(False)
audio_duration = 15
try:
audio = AudioSegment.from_file(self.generated_audio)
audio_duration = len(audio) / 1000
except:
pass
threading.Timer(min(audio_duration, 300), self.reenable_play_button).start()
except Exception as e:
self.status_label.setText(f”Playback error: {str(e)}”)
self.status_label.setStyleSheet(“color: #ff5252;”)
self.play_btn.setEnabled(True)
def reenable_play_button(self):
self.play_btn.setEnabled(True)
self.status_label.setText(“TRULY UNLIMITED audio playback finished”)
def stop_audio(self):
if self.audio_process:
try:
self.audio_process.terminate()
self.audio_process = None
except:
pass
self.play_btn.setEnabled(True)
self.status_label.setText(“Audio stopped”)
def download_audio(self):
if hasattr(self, ‘generated_audio’) and os.path.exists(self.generated_audio):
save_path, _ = QFileDialog.getSaveFileName(
self,
“Save TRULY UNLIMITED Translated Audio”,
f”truly_unlimited_translated_{self.language_combo.currentText()}.mp3″,
“Audio Files (*.mp3 *.wav *.flac *.aac)”
)
if save_path:
import shutil
shutil.copy2(self.generated_audio, save_path)
self.status_label.setText(f”TRULY UNLIMITED audio saved: {save_path}”)
def on_progress_update(self, message):
self.status_label.setText(message)
def closeEvent(self, event):
if hasattr(self, ‘generated_audio’) and os.path.exists(self.generated_audio):
try:
os.remove(self.generated_audio)
except:
pass
if hasattr(self, ‘generated_video’) and os.path.exists(self.generated_video):
try:
os.remove(self.generated_video)
except:
pass
self.stop_audio()
self.stop_recording()
event.accept()
def main():
app = QApplication(sys.argv)
window = CompleteVoiceTranslator()
window.show()
sys.exit(app.exec())
if __name__ == “__main__”:
main()
With regards to managing your finances, choosing the right bank can improve things significantly. While there are incalculable options accessible, opting for an American bank can offer a heap of benefits for your financial transactions. From upgraded security measures to streamlined services, American banks are known for their unwavering quality and convenience.
1. Convenient access to your money
With regards to managing your finances, one of the main factors to consider is how effectively you can access your money. This is where American banks really shine, with their broad network of ATMs and branches spread out across the nation.
Whether you live in a bustling city or a humble community, you can have confidence that there is a bank branch or ATM close by where you can undoubtedly withdraw cash, deposit checks, or talk with a banker about your account. This implies that you don’t need to stress over travelling significant distances or going out of your way to access your funds; convenience is key with regards to managing your money, and American banks certainly convey this viewpoint.
As well as having an immense actual presence, numerous American banks likewise offer online and mobile banking services that permit you to access your accounts from the solace of your home or while in a hurry. This implies that you can check your balances, transfer funds, pay bills, and even set up alerts to monitor your finances—all with just a couple of taps on your smartphone or on your computer.
With American banks, convenience is really at your fingertips—you can manage your finances at whatever point and any place it suits you best, which can save you time and make your financial life a lot easier and more proficient.
Moreover, having simple access to your money can be vital in the midst of a crisis. Imagine you’re travelling for work or pleasure and your wallet gets taken. Having access to your American bank accounts implies you can rapidly freeze your cards, report the burglary, and access crisis funds to tide you over until you can recover financially.
2. Higher security measures
With regards to storing and protecting your well-deserved money, security is without a doubt one of the first concerns. American banks are known for implementing robust security measures to shield their customers’ funds and personal information.
At the point when you decide to manage your financial transactions through American banks, you can have confidence that your money is in safe hands. These banks utilise advanced encryption techniques to secure your information and forestall unauthorised access. From online banking entryways to ATM machines, these security measures are set up to give you true serenity while managing your finances.
Notwithstanding technological shields, American banks additionally focus on actual security at their branches and workplaces. With reconnaissance cameras, security monitors, and access control systems, these institutions play it safe to protect their customers and their assets. This degree of watchfulness assists with deterring possible dangers and ensuring the safety of customers who visit their offices for banking services.
Besides, American banks comply with severe administrative guidelines and consistency standards set by federal specialists. These regulations are intended to ensure the integrity and steadiness of the banking system, as well as to protect purchasers from extortion and financial wrongdoing. By following these guidelines, banks can maintain the trust and confidence of their customers, knowing that their money is being handled in a secure and dependable way.
At the point when you decide to manage your financial transactions through American banks, you additionally benefit from their proactive approach to cybersecurity. With the ascent of online banking and advanced payments, the risk of digital assaults and identity robbery has become pervasive. American banks invest vigorously in cybersecurity measures to moderate these risks and protect their customers from possible dangers.
From complex extortion recognition calculations to multi-factor authentication conventions, these banks are continually evolving their security strategies to remain one stride ahead of cybercriminals. By leveraging cutting-edge technology and industry best practices, American banks can provide a secure and consistent banking experience for their customers.
3. Online banking options
In the present, fast-moving world, convenience is key with regards to managing your finances. American banks understand this need and have adjusted to provide online banking services to make your life more straightforward.
Online banking options have become increasingly popular among American banks, allowing customers to access their accounts and complete transactions from the comfort of their own homes or in a hurry with mobile apps. Gone are the days of having to rush to the bank before it closes or stand by in lengthy lines to deposit a check. With online banking, you can access your account 24/7, giving you the adaptability to manage your money at whatever point it’s convenient for you.
One of the main benefits of using American banks for your financial transactions is the convenience of online banking. With just a couple of snaps, you can check your account balances, transfer funds between accounts, pay bills, and even set up automatic payments. Online banking provides a degree of control that traditional banking methods basically can’t coordinate.
One more benefit of online banking is the capacity to set up alerts and notices to keep you informed about your account activity. You can get alerts for low balances, enormous transactions, and, in any event, when a bill is expected. This additional degree of security assists you with staying on top of your finances and provides inner serenity, knowing that you are consistently mindful of what’s going on with your money.
Mobile apps offered by American banks take online banking to a higher level, giving you considerably greater adaptability and command over your finances. With a mobile app, you can finish most banking errands right from your smartphone or tablet, whether you’re at home, working, or on holiday. This degree of convenience is invaluable for those with busy ways of life who need access to their money immediately.
Notwithstanding convenience, online banking with American banks additionally provides improved security features to protect your personal and financial information. Banks use advanced encryption technology to shield your information and ensure that your transactions are secure. With online banking, you can likewise monitor your account activity continuously, which can help you recognise and report any unapproved transactions rapidly.
In general, the online banking options offered by American banks provide a degree of convenience, adaptability, and security that traditional banking methods essentially can’t coordinate. Whether you like to manage your money from the solace of your own home or in a hurry with a mobile app, online banking enables you to assume command over your finances in a manner that accommodates your way of life. So why make banking more troublesome than it needs to be? Pick American banks for your financial transactions and experience the benefits of online banking today.
4 comments