Created
December 28, 2024 23:10
-
-
Save me-suzy/af81e7766002e5a33d2c4e61b5619d99 to your computer and use it in GitHub Desktop.
ChatGPT BUN tanar - adaugare text si voce video peste vechea voce
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
from moviepy.editor import VideoFileClip, AudioFileClip, concatenate_audioclips, AudioClip, TextClip, CompositeVideoClip | |
from moviepy.config import change_settings | |
from pydub import AudioSegment | |
import openai | |
import tempfile | |
import time | |
# Configurare pentru ffmpeg | |
os.environ["PATH"] += os.pathsep + r"D:\ffmpeg-master-latest-win64-gpl-shared\bin" | |
AudioSegment.converter = r"D:\ffmpeg-master-latest-win64-gpl-shared\bin\ffmpeg.exe" | |
AudioSegment.ffmpeg = r"D:\ffmpeg-master-latest-win64-gpl-shared\bin\ffmpeg.exe" | |
AudioSegment.ffprobe = r"D:\ffmpeg-master-latest-win64-gpl-shared\bin\ffprobe.exe" | |
# Configurare pentru ImageMagick | |
IMAGEMAGICK_BINARY = r"d:\Program Files\ImageMagick-7.1.1-Q16-HDRI\magick.exe" # Ajustează calea conform instalării tale | |
change_settings({"IMAGEMAGICK_BINARY": IMAGEMAGICK_BINARY}) | |
class VoiceConverter: | |
def __init__(self, api_key): | |
print("Inițializare client OpenAI...") | |
openai.api_key = api_key | |
def close_clips(self, *clips): | |
for clip in clips: | |
try: | |
if clip is not None: | |
clip.close() | |
except: | |
pass | |
def generate_speech_for_sentence(self, text, output_file, voice="alloy"): | |
"""Generează voce pentru o singură propoziție""" | |
try: | |
# Creează directorul 'voices' dacă nu există | |
os.makedirs('voices', exist_ok=True) | |
# Generează un nume de fișier bazat pe text | |
safe_filename = "".join(x for x in text[:30] if x.isalnum()) + ".mp3" | |
voice_path = os.path.join('voices', safe_filename) | |
# Verifică dacă fișierul există deja | |
if os.path.exists(voice_path): | |
print(f"Folosesc vocea existentă pentru: {text[:30]}...") | |
# Copiază fișierul existent în locația temporară | |
with open(voice_path, 'rb') as src, open(output_file, 'wb') as dst: | |
dst.write(src.read()) | |
else: | |
print(f"Generez voce nouă pentru: {text[:30]}...") | |
response = openai.audio.speech.create( | |
model="tts-1", | |
voice=voice, | |
input=text | |
) | |
# Salvează în ambele locații | |
response.stream_to_file(voice_path) | |
with open(voice_path, 'rb') as src, open(output_file, 'wb') as dst: | |
dst.write(src.read()) | |
return True | |
except Exception as e: | |
print(f"❌ Eroare la generarea vocii pentru propoziția: {text}") | |
print(f"Eroare: {str(e)}") | |
return False | |
def create_text_clip(self, text, duration, video_size): | |
"""Creează un TextClip cu formatare specifică""" | |
txt_clip = TextClip(text, | |
font='Arial', | |
fontsize=30, | |
color='white', | |
# bg_color='black', # aici daca vrei background la text | |
# stroke_color='black', | |
# stroke_width=2, | |
size=(video_size[0], None), | |
method='caption') | |
txt_clip = txt_clip.set_duration(duration) | |
# Poziționează textul în partea de jos a video-ului | |
txt_clip = txt_clip.set_position(('center', 'bottom')) | |
return txt_clip | |
def process_video(self, video_path, text, output_path, voice="alloy"): | |
print(f"\n=== Începe procesarea video ===") | |
video = None | |
final_audio = None | |
audio_clips = [] | |
text_clips = [] | |
temp_files = [] | |
try: | |
if not os.path.exists(video_path): | |
print(f"❌ Fișierul video nu există: {video_path}") | |
return False | |
# Încarcă video-ul | |
print("\n1. Încărcăm video-ul...") | |
video = VideoFileClip(video_path) | |
video_size = video.size | |
video_duration = video.duration | |
print(f"Durata video: {video_duration} secunde") | |
# Împarte textul în propoziții și generează audio pentru fiecare | |
sentences = [s.strip() for s in text.split('.') if s.strip()] | |
sentence_audios = [] | |
# Prima trecere: generează toate audio-urile și calculează durata totală | |
print("\n2. Generăm audio pentru fiecare propoziție...") | |
total_audio_duration = 0 | |
for i, sentence in enumerate(sentences): | |
temp_file = tempfile.NamedTemporaryFile(suffix='.mp3', delete=False) | |
temp_files.append(temp_file.name) | |
temp_file.close() | |
if self.generate_speech_for_sentence(sentence + ".", temp_file.name, voice): | |
audio = AudioFileClip(temp_file.name) | |
sentence_audios.append((sentence, audio)) | |
total_audio_duration += audio.duration | |
# Calculează timpul disponibil și pauzele necesare | |
available_time = video_duration - 5 # Rezervăm 5 secunde la final | |
total_pause_time = available_time - total_audio_duration | |
pauses_needed = len(sentences) - 1 | |
if pauses_needed > 0: | |
pause_duration = total_pause_time / pauses_needed | |
print(f"\nTimpul total pentru pauze: {total_pause_time:.2f} secunde") | |
print(f"Durata fiecărei pauze: {pause_duration:.2f} secunde") | |
else: | |
pause_duration = 0 | |
# A doua trecere: construiește audio-ul și textul sincronizat | |
print("\n3. Construim audio-ul și textul sincronizat...") | |
current_time = 0 | |
for i, (sentence, audio) in enumerate(sentence_audios): | |
# Adaugă audio | |
audio_clips.append(audio) | |
# Creează și adaugă text sincronizat | |
txt_clip = self.create_text_clip(sentence, audio.duration, video_size) | |
txt_clip = txt_clip.set_start(current_time) | |
text_clips.append(txt_clip) | |
current_time += audio.duration | |
# Adaugă pauză după fiecare propoziție (exceptând ultima) | |
if i < len(sentence_audios) - 1: | |
silence = AudioClip(lambda t: 0, duration=pause_duration) | |
audio_clips.append(silence) | |
current_time += pause_duration | |
# Adaugă pauza finală de 5 secunde | |
final_silence = AudioClip(lambda t: 0, duration=5) | |
audio_clips.append(final_silence) | |
# Concatenează toate clipurile audio | |
print("\n4. Combinăm toate clipurile audio...") | |
final_audio = concatenate_audioclips(audio_clips) | |
# Verifică durata audio finală | |
print(f"\nDurata audio finală: {final_audio.duration:.2f} secunde") | |
print(f"Durata video: {video_duration:.2f} secunde") | |
# Creează video-ul final cu text | |
print("\n5. Creăm video-ul final cu text...") | |
final_video = CompositeVideoClip([video] + text_clips) | |
final_video = final_video.set_audio(final_audio) | |
print("\n6. Exportăm rezultatul...") | |
final_video.write_videofile( | |
output_path, | |
codec='libx264', | |
audio_codec='aac', | |
temp_audiofile='temp-audio.m4a', | |
remove_temp=True | |
) | |
print("\n7. Export finalizat cu succes!") | |
return True | |
except Exception as e: | |
print(f"❌ Eroare la procesarea video: {str(e)}") | |
return False | |
finally: | |
print("\n8. Curățăm resursele...") | |
self.close_clips(video, final_audio) | |
for clip in audio_clips: | |
self.close_clips(clip) | |
for clip in text_clips: | |
self.close_clips(clip) | |
time.sleep(1) | |
for temp_file in temp_files: | |
try: | |
if os.path.exists(temp_file): | |
os.unlink(temp_file) | |
except Exception as e: | |
print(f"⚠️ Nu am putut șterge fișierul temporar {temp_file}: {str(e)}") | |
def main(): | |
OPENAI_API_KEY = "YOUR-API-KEY" | |
video_path = r"e:\De pus pe FTP 2\Cum se spala corect sosetele la mana, la chiuveta.mp4" | |
output_path = "video_cu_voce_si_text_sincronizat.mp4" | |
text = """ | |
How to wash socks by hand, under the tap. | |
First, turn on the tap with warm water and wet both socks well, rinsing them thoroughly. | |
Then, take the soap and, using circular movements, start rubbing the socks. | |
It is important to cover all surfaces with soap. | |
After lathering the socks well, continue rubbing them so that the foam penetrates each fiber. | |
When the foam starts to turn black, it is a sign that the dirt is coming out of the fabric. | |
After about two minutes of rubbing, rinse the socks with warm water until the water runs clear. | |
Finally, wring the socks well and place them on the radiator to dry. | |
Now the socks are clean and ready to dry. | |
""" | |
print("\n=== Începe procesul complet ===") | |
converter = VoiceConverter(OPENAI_API_KEY) | |
success = converter.process_video(video_path, text, output_path) | |
if success: | |
print(f"\n✅ Procesare completă! Video-ul a fost salvat ca: {output_path}") | |
else: | |
print("\n❌ Procesarea a eșuat!") | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment