import os, whisper, asyncio, edge_tts, re, torch
from googletrans import Translator
from moviepy.editor import VideoFileClip, AudioFileClip, CompositeAudioClip
import moviepy.video.fx.all as vfx
# --- ⚙️ ការកំណត់ (ម្ចាស់គ្រូកែឈ្មោះ File នៅទីនេះ) ---
Video_Name = "movie.mp4"
VIDEO_PATH = f"input_video/{Video_Name}"
OUTPUT_PATH = f"final_result/BM7_ANGKOR_PRO_{Video_Name}"
translator = Translator()
# --- 🧠 មុខងារបែងចែកភេទ និងចម្រោះពាក្យឡប់ ---
def detect_gender(text):
female_keywords = ['នាង', 'ស្រី', 'អូន', 'អ្នកនាង', 'កញ្ញា', 'ម៉ាក់', 'យាយ', 'ព្រះនាង']
if any(word in text for word in female_keywords):
return "km-KH-SreymomNeural"
return "km-KH-PisethNeural"
def clean_repetition(text):
# លុបពាក្យដែលលោតដដែលៗ (ឧទាហរណ៍៖ "នៅទីនោះ នៅទីនោះ" -> "នៅទីនោះ")
return re.sub(r'\b(\w+)( \1\b)+', r'\1', text)
def professional_khmer_filter(text):
try:
raw_kh = translator.translate(text, src='zh-cn', dest='km').text
corrections = {
"អ្នក": "ឯង", "ខ្ញុំ": "យើង", "សួស្តី": "ជម្រាបសួរ",
"អរគុណ": "អរគុណច្រើន", "មែនទេ": "មែនអត់?",
"តើមានរឿងអ្វី": "មានរឿងអី?", "ពិតជា": "ពិតមែនហើយ"
}
for old, new in corrections.items():
raw_kh = raw_kh.replace(old, new)
# ចម្រោះពាក្យដដែលៗ និងនិមិត្តសញ្ញា
clean_text = clean_repetition(raw_kh)
return re.sub(r'[.,!?]', ' ', clean_text).strip()
except:
return ""
async def generate_voice(text, start, duration, index):
try:
kh_text = professional_khmer_filter(text)
if not kh_text or len(kh_text) < 2: return None, None
selected_voice = detect_gender(kh_text)
tmp = f"tmp_{index}.mp3"
# កែសម្រួលល្បឿន និងសម្លេងឱ្យមានជីវិត
communicate = edge_tts.Communicate(kh_text, selected_voice, rate="+8%", pitch="-1Hz")
await communicate.save(tmp)
audio = AudioFileClip(tmp).set_start(start).volumex(5.0)
if audio.duration > duration:
return vfx.speedx(audio, factor=audio.duration/duration).set_duration(duration), tmp
return audio, tmp
except:
return None, None
async def start_dubbing():
if not os.path.exists(VIDEO_PATH):
print(f"❌ រកមិនឃើញ File: {VIDEO_PATH}")
return
print("🚀 កំពុងដាស់ម៉ាស៊ីន AI (Whisper Medium)...")
device = "cuda" if torch.cuda.is_available() else "cpu"
model = whisper.load_model("medium").to(device)
# បន្ថែម Parameters ការពារការនិយាយឡប់ដដែលៗ
print("🔍 កំពុងវិភាគសាច់រឿង (ទប់ស្កាត់ Hallucination)...")
transcribe = model.transcribe(
VIDEO_PATH,
task="transcribe",
language="zh",
temperature=0, # កាត់បន្ថយការទាយខុស
beam_size=5, # បង្កើនភាពហ្មត់ចត់
compression_ratio_threshold=2.4, # លុបពាក្យដដែលៗ
no_speech_threshold=0.6 # មិនបកប្រែបើលឺតែភ្លេង
)
segments = transcribe['segments']
video = VideoFileClip(VIDEO_PATH)
audio_tracks = [video.audio.volumex(0.12)]
temp_files = []
print(f"🎙️ ចាប់ផ្ដើមផលិតសម្លេងតួអង្គចំនួន {len(segments)} ឃ្លា...")
for i, s in enumerate(segments):
aud, path = await generate_voice(s['text'], s['start'], s['end']-s['start'], i)
if aud:
audio_tracks.append(aud)
temp_files.append(path)
if i % 25 == 0:
print(f"⏳ ដំណើរការបាន {round((i/len(segments))*100)}% ...")
print("🎬 កំពុងបូកបញ្ចូលគ្នា និង Export វីដេអូ (Threads=4)...")
final_audio = CompositeAudioClip(audio_tracks)
final_video = video.set_audio(final_audio)
final_video.write_videofile(
OUTPUT_PATH,
codec="libx264",
audio_codec="aac",
fps=video.fps,
threads=4,
logger=None # បិទការបង្ហាញ log រញ៉េរញ៉ៃ
)
video.close()
for f in temp_files:
try: os.remove(f)
except: pass
print(f"✅ សម្រេចមហាជោគជ័យ! ម្ចាស់គ្រូអាចមើលវីដេអូបាននៅ៖ {OUTPUT_PATH}")
# រត់ដំណើរការ
if __name__ == "__main__":
await start_dubbing()
China Movies Speak Khmer.
May 07, 2026
China Movies Speak Khmer.
May 07, 2026
import os, whisper, asyncio, edge_tts, re
from googletrans import Translator
from moviepy.editor import VideoFileClip, AudioFileClip, CompositeAudioClip
import moviepy.video.fx.all as vfx
# --- ⚙️ កំណត់រចនាសម្ព័ន្ធ ---
Video_Name = "movie.mp4"
VIDEO_PATH = f"input_video/{Video_Name}"
OUTPUT_PATH = f"final_result/LONG_DUB_{Video_Name}"
translator = Translator()
def professional_khmer_filter(text):
try:
raw_kh = translator.translate(text, src='zh-cn', dest='km').text
corrections = {
"អ្នក": "ឯង", "ខ្ញុំ": "យើង", "របស់ខ្ញុំ": "របស់បង",
"តើអ្នកកំពុងធ្វើអ្វី": "ឯងធ្វើស្អីហ្នឹង?", "សួស្តី": "ជម្រាបសួរ",
"អរគុណ": "អរគុណច្រើន", "បាទ": "បាទ/ចា៎",
"មែនទេ": "មែនអត់?", "តើមានរឿងអ្វី": "មានរឿងអី?",
"ពិតជា": "ពិតមែនហើយ", "មិនអាច": "មិនបានទេ",
}
for old, new in corrections.items():
raw_kh = raw_kh.replace(old, new)
return re.sub(r'[.,!?]', ' ', raw_kh).strip()
except:
return ""
async def generate_smooth_audio(text, start, duration, index):
try:
kh_text = professional_khmer_filter(text)
if not kh_text: return None, None
voice = "km-KH-PisethNeural" # ម្ចាស់គ្រូអាចកែជា Sreymom បើជាតួស្រី
tmp = f"tmp_{index}_{start}.mp3"
communicate = edge_tts.Communicate(kh_text, voice, rate="+7%")
await communicate.save(tmp)
audio = AudioFileClip(tmp).set_start(start).volumex(4.5)
if audio.duration > duration:
return vfx.speedx(audio, factor=audio.duration/duration).set_duration(duration), tmp
return audio, tmp
except:
return None, None
async def start_dubbing():
if not os.path.exists(VIDEO_PATH):
print("❌ រកមិនឃើញ File វីដេអូទេម្ចាស់គ្រូ!")
return
# ១. ប្រើ Whisper ស្កែន (សម្រាប់រឿងវែង គួរប្រើ 'base' បើ RAM តិច ឬ 'medium' បើមាន GPU ខ្លាំង)
print("🔍 កំពុងវិភាគសាច់រឿងវែង... (សូមរង់ចាំបន្តិច)")
model = whisper.load_model("medium")
transcribe = model.transcribe(VIDEO_PATH, task="transcribe", language="zh")
# ២. រៀបចំ Segment
merged_segments = transcribe['segments']
video = VideoFileClip(VIDEO_PATH)
audio_tracks = [video.audio.volumex(0.15)]
temp_files = []
print(f"🎙️ ចាប់ផ្ដើម Dubbing ចំនួន {len(merged_segments)} ឃ្លា...")
# ៣. ប្រើ Loop ធម្មតាជំនួសឱ្យ asyncio.gather ដើម្បីការពារការគាំង RAM
for i, s in enumerate(merged_segments):
# បង្កើតសំឡេងម្ដងមួយៗ
aud, path = await generate_smooth_audio(s['text'], s['start'], s['end']-s['start'], i)
if aud:
audio_tracks.append(aud)
temp_files.append(path)
# បង្ហាញ Progress ឱ្យម្ចាស់គ្រូដឹង
if i % 10 == 0:
print(f"⏳ ធ្វើបាន {i}/{len(merged_segments)} ឃ្លាហើយ...")
# ៤. រួមបញ្ចូលគ្នា
print("🎬 កំពុងបូកបញ្ចូលសំឡេង និង Export វីដេអូកម្រិតច្បាស់...")
final_audio = CompositeAudioClip(audio_tracks)
final_video = video.set_audio(final_audio)
# ប្រើ threads=4 ដើម្បីឱ្យវា Export លឿន
final_video.write_videofile(OUTPUT_PATH, codec="libx264", audio_codec="aac", fps=video.fps, threads=4)
# ៥. សម្អាត File
video.close() # បិទ File ដើម្បីឱ្យលុប Temp បាន
for f in temp_files:
try: os.remove(f)
except: pass
print(f"✅ រួចរាល់ហើយម្ចាស់គ្រូ! វីដេអូរឿងវែងស្ថិតនៅ៖ {OUTPUT_PATH}")
# រត់កម្មវិធី
await start_dubbing()
Clone voice
China Movies Speak Khmer.
May 06, 2026
# ១. ដំឡើងបណ្ណាល័យចាំបាច់ (រង់ចាំប្រហែល ២-៣ នាទី)
!pip install coqui-tts openai-whisper moviepy==1.0.3 googletrans==4.0.0-rc1 torch torchvision torchaudio
import os, whisper, asyncio, torch
from TTS.api import TTS
from googletrans import Translator
from moviepy.editor import VideoFileClip, AudioFileClip, CompositeAudioClip
import moviepy.video.fx.all as vfx
# ២. កំណត់ផ្លូវ File (ម្ចាស់គ្រូអាចប្តូរឈ្មោះតាមចិត្ត)
VIDEO_IN = "jade_video.mp4"
MALE_VOICE = "male_ref.wav"
FEMALE_VOICE = "female_ref.wav"
OUTPUT_NAME = "BM7_CLONED_COLAB.mp4"
# ៣. ដាស់ម៉ាស៊ីន AI (ប្រើ GPU ឥតគិតថ្លៃរបស់ Google)
device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"🚀 កំពុងប្រើអំណាច GPU: {device}")
tts = TTS("tts_models/multilingual/multi-dataset/xtts_v2").to(device)
translator = Translator()
model = whisper.load_model("medium")
# ៤. មុខងារក្លូនសំឡេង
def clone_voice(text, start, gender):
ref_path = MALE_VOICE if gender == "MALE" else FEMALE_VOICE
out_path = f"tmp_{start}.wav"
tts.tts_to_file(text=text, speaker_wav=ref_path, language="kh", file_path=out_path)
return out_path
# ៥. ចាប់ផ្ដើមដំណើរការ Dubbing
print("🔍 កំពុងវិភាគសាច់រឿង និងបកប្រែ...")
transcribe = model.transcribe(VIDEO_IN, language="zh")
video = VideoFileClip(VIDEO_IN)
audio_tracks = [video.audio.volumex(0.15)]
for s in transcribe['segments']:
kh_text = translator.translate(s['text'], src='zh-cn', dest='km').text
# បែងចែកភេទតួអង្គ (Logic BM7 ម្ចាស់គ្រូ)
gender = "FEMALE" if any(w in kh_text for w in ['នាង', 'ស្រី', 'អូន', 'អ្នកនាង']) else "MALE"
print(f"🎙️ [{s['start']}s] កំពុងក្លូន៖ {kh_text}")
wave_file = clone_voice(kh_text, s['start'], gender)
if wave_file:
duration = s['end'] - s['start']
clip = AudioFileClip(wave_file).set_start(s['start']).volumex(4.5)
# សម្រួលល្បឿនឱ្យត្រូវមាត់ (Lip-sync)
if clip.duration > duration:
clip = vfx.speedx(clip, factor=clip.duration/duration).set_duration(duration)
audio_tracks.append(clip)
# ៦. រួមបញ្ចូល និងទាញយកវីដេអូ
print("🎬 កំពុងផលិតវីដេអូចុងក្រោយ...")
final_video = video.set_audio(CompositeAudioClip(audio_tracks))
final_video.write_videofile(OUTPUT_NAME, codec="libx264", audio_codec="aac")
print(f"✅ រួចរាល់! ម្ចាស់គ្រូអាច Download File {OUTPUT_NAME} បានហើយ!")
China Movies Speak Khmer.
May 05, 2026
import os, whisper, asyncio, re
import edge_tts
from googletrans import Translator
from moviepy.editor import VideoFileClip, AudioFileClip, CompositeAudioClip
import moviepy.video.fx.all as vfx
# --- ⚙️ ការកំណត់ (Configuration) ---
VIDEO_INPUT = "input_video/your_video.mp4" # ដាក់ឈ្មោះ File វីដេអូម្ចាស់គ្រូ
OUTPUT_NAME = "final_result/BM7_DUBBED_V3.mp4"
translator = Translator()
# --- 🎙️ មុខងារបែងចែកភេទតួអង្គ និងកែសម្រួលភាសា ---
def filter_and_improve_khmer(text):
# ១. បកប្រែពីចិន មកខ្មែរ
translation = translator.translate(text, src='zh-cn', dest='km').text
# ២. កំណត់ភេទតួអង្គតាមពាក្យគន្លឹះ (Keywords)
female_words = ['នាង', 'ស្រី', 'ព្រះនាង', 'អ្នកនាង', 'ម៉ាក់', 'បងស្រី', 'អូន', 'ម្ចាស់ក្សត្រី']
gender = "FEMALE" if any(word in translation for word in female_words) else "MALE"
# ៣. កែសម្រួលពាក្យឱ្យសមជារឿងភាគ (Drama Style)
replacements = {
"អ្នក": "ឯង",
"ខ្ញុំ": "យើង",
"តើអ្នកសុខសប្បាយទេ": "ឯងសុខសប្បាយទេ?",
"តើមានរឿងអ្វី": "មានរឿងអីហ្នឹង?",
"ពិតជា": "ពិតមែនហើយ",
"អរគុណ": "អរគុណហើយ"
}
for old, new in replacements.items():
translation = translation.replace(old, new)
return translation.strip(), gender
# --- 🔊 មុខងារបង្កើតសំឡេង AI ---
async def generate_voice(text, start, duration):
try:
kh_text, gender = filter_and_improve_khmer(text)
if not kh_text: return None, None
# ជ្រើសរើសសំឡេង (Piseth សម្រាប់ប្រុស, Sreymom សម្រាប់ស្រី)
voice = "km-KH-PisethNeural" if gender == "MALE" else "km-KH-SreymomNeural"
tmp_path = f"temp_audio_{start}.mp3"
communicate = edge_tts.Communicate(kh_text, voice, rate="+5%") # បន្ថែមល្បឿន ៥% ឱ្យសមស្រប
await communicate.save(tmp_path)
audio = AudioFileClip(tmp_path).set_start(start).volumex(5.0) # បង្កើនកម្រិតសំឡេងឱ្យច្បាស់
# ធ្វើឱ្យសំឡេងត្រូវនឹងរូបភាព (Lip-sync Adjustment)
if audio.duration > duration:
return vfx.speedx(audio, factor=audio.duration/duration).set_duration(duration), tmp_path
return audio, tmp_path
except Exception as e:
print(f"Error creating voice: {e}")
return None, None
async def run_dubbing_process():
# ១. វិភាគវីដេអូជាមួយ Whisper (ប្រើភាសាចិន)
print("🔍 កំពុងស្កែនសាច់រឿង... (Whisper Medium)")
model = whisper.load_model("medium")
result = model.transcribe(VIDEO_INPUT, task="transcribe", language="zh")
video = VideoFileClip(VIDEO_INPUT)
audio_tracks = [video.audio.volumex(0.2)] # បន្ថយសំឡេងដើម ២០% ដើម្បីឱ្យឮសំឡេង AI ច្បាស់
# ២. បង្កើតសំឡេងខ្មែរដាក់ចូលវីដេអូ
print("🎬 កំពុងបញ្ចូលសំឡេងខ្មែរដោយ AI...")
temp_files = []
for segment in result['segments']:
aud, path = await generate_voice(segment['text'], segment['start'], segment['end'] - segment['start'])
if aud:
audio_tracks.append(aud)
temp_files.append(path)
# ៣. រួមបញ្ចូល និង Export វីដេអូចុងក្រោយ
final_audio = CompositeAudioClip(audio_tracks)
final_video = video.set_audio(final_audio)
final_video.write_videofile(OUTPUT_NAME, codec="libx264", audio_codec="aac")
# លុប File បណ្តោះអាសន្ន
for f in temp_files:
if os.path.exists(f): os.remove(f)
print(f"✅ រួចរាល់ហើយម្ចាស់គ្រូ! វីដេអូស្ថិតនៅ៖ {OUTPUT_NAME}")
if __name__ == "__main__":
asyncio.run(run_dubbing_process())
China Movies Speak Khmer.
May 04, 2026
import os, whisper, asyncio, edge_tts, re
from googletrans import Translator
from moviepy.editor import VideoFileClip, AudioFileClip, CompositeAudioClip
import moviepy.video.fx.all as vfx
# --- ⚙️ កំណត់រចនាសម្ព័ន្ធ ---
Video_Name = "PRO_AI_DUB_BM7.mp4"
VIDEO_PATH = f"input_video/{Video_Name}"
OUTPUT_PATH = f"final_result/CHARACTER_DUB_SLOW_{Video_Name}"
translator = Translator()
# --- 🎙️ បញ្ជីសំឡេងតួអង្គ ---
VOICES = {
"MALE": "km-KH-PisethNeural",
"FEMALE": "km-KH-SreymomNeural"
}
# --- 🧠 ម៉ាស៊ីនវិភាគតួអង្គ និងកែសម្រួលភាសា ---
def identify_and_filter(text):
raw_kh = translator.translate(text, src='zh-cn', dest='km').text
female_keywords = ['នាង', 'ស្រី', 'ព្រះនាង', 'អ្នកនាង', 'ម៉ាក់', 'បងស្រី', 'អូន']
gender = "MALE"
if any(word in raw_kh for word in female_keywords):
gender = "FEMALE"
corrections = {
"អ្នក": "ឯង", "ខ្ញុំ": "យើង",
"តើអ្នកកំពុងធ្វើអ្វី": "ឯងកំពុងធ្វើស្អី?",
"ពិតជា": "ពិតមែនហើយ", "មិនអាច": "មិនបានទេ",
"តើមានរឿងអ្វី": "មានរឿងអី?"
}
for old, new in corrections.items():
raw_kh = raw_kh.replace(old, new)
return raw_kh.strip(), gender
async def generate_character_audio(text, start, duration):
try:
kh_text, gender = identify_and_filter(text)
if not kh_text: return None, None
selected_voice = VOICES[gender]
# បន្ថយល្បឿនមកត្រឹម +0% (ល្បឿនដើម) ដើម្បីឱ្យស្ដាប់បានច្បាស់ល្អ
# ម្ចាស់គ្រូអាចប្តូរជា "+3%" បើចង់ឱ្យលឿនជាងនេះបន្តិច
rate_setting = "+0%"
tmp = f"tmp_{start}.mp3"
communicate = edge_tts.Communicate(kh_text, selected_voice, rate=rate_setting)
await communicate.save(tmp)
audio = AudioFileClip(tmp).set_start(start).volumex(4.5)
# ប្រសិនបើសំឡេងវែងជាងវីដេអូខ្លាំង ទើបយើងបង្កើនល្បឿនតាមរូបភាព
if audio.duration > duration:
return vfx.speedx(audio, factor=audio.duration/duration).set_duration(duration), tmp
return audio, tmp
except Exception as e:
print(f"Error: {e}")
return None, None
async def start_multi_dubbing():
model = whisper.load_model("medium")
print("🔍 កំពុងវិភាគសាច់រឿង (ល្បឿនធម្មជាតិ)...")
transcribe = model.transcribe(VIDEO_PATH, task="transcribe", language="zh")
segments = transcribe['segments']
video = VideoFileClip(VIDEO_PATH)
audio_tracks = [video.audio.volumex(0.15)]
print(f"🎬 កំពុងបញ្ចូលសំឡេងតួអង្គក្នុងល្បឿនសមល្មម...")
tasks = [generate_character_audio(s['text'], s['start'], s['end']-s['start']) for s in segments]
results = await asyncio.gather(*tasks)
temp_files = []
for aud, path in results:
if aud:
audio_tracks.append(aud)
temp_files.append(path)
final_audio = CompositeAudioClip(audio_tracks)
final_video = video.set_audio(final_audio)
final_video.write_videofile(OUTPUT_PATH, codec="libx264", audio_codec="aac")
for f in temp_files:
if os.path.exists(f): os.remove(f)
print(f"✅ រួចរាល់! វីដេអូសំឡេងរលូន៖ {OUTPUT_PATH}")
if __name__ == "__main__":
await start_multi_dubbing()
China Movies Speak Khmer.
May 03, 2026
import os, whisper, asyncio, edge_tts, re
from googletrans import Translator
from moviepy.editor import VideoFileClip, AudioFileClip, CompositeAudioClip
import moviepy.video.fx.all as vfx
# --- ⚙️ កំណត់រចនាសម្ព័ន្ធខ្ពស់ ---
Video_Name = "movie.mp4"
VIDEO_PATH = f"input_video/{Video_Name}"
OUTPUT_PATH = f"final_result/SMOOTH_AI_DUB_{Video_Name}"
translator = Translator()
# --- 🧠 ម៉ាស៊ីនប៉ូឡាបកប្រែ (Fluency Engine) ---
def professional_khmer_filter(text):
# បកប្រែដោយផ្ដោតលើអត្ថន័យ
raw_kh = translator.translate(text, src='zh-cn', dest='km').text
# ជំនួសពាក្យរអាក់រអួល និងកែសម្រួលពាក្យសព្វនាម
corrections = {
"អ្នក": "ឯង", "ខ្ញុំ": "យើង", "របស់ខ្ញុំ": "របស់បង",
"តើអ្នកកំពុងធ្វើអ្វី": "ឯងធ្វើស្អីហ្នឹង?", "សួស្តី": "ជម្រាបសួរ",
"អរគុណ": "អរគុណច្រើន", "បាទ": "បាទ/ចា៎",
"មែនទេ": "មែនអត់?", "តើមានរឿងអ្វី": "មានរឿងអី?",
"ពិតជា": "ពិតមែនហើយ", "មិនអាច": "មិនបានទេ",
}
for old, new in corrections.items():
raw_kh = raw_kh.replace(old, new)
# បច្ចេកទេស Joiner: លុបដកឃ្លាដែលមិនចាំបាច់ ដើម្បីកុំឱ្យ AI អានដាច់ៗ
final_text = re.sub(r'[.,!?]', ' ', raw_kh) # ប្តូរសញ្ញាខណ្ឌមកជាដកឃ្លាខ្លីវិញ
return final_text.strip()
async def generate_smooth_audio(text, start, duration):
try:
kh_text = professional_khmer_filter(text)
if not kh_text: return None, None
# កំណត់កម្រិតសំឡេងឱ្យស្រទន់ និងល្បឿនថេរ
# ប្រើ "km-KH-SreymomNeural" សម្រាប់ស្រី ឬ "km-KH-PisethNeural" សម្រាប់ប្រុស
voice = "km-KH-PisethNeural"
tmp = f"tmp_{start}.mp3"
communicate = edge_tts.Communicate(kh_text, voice, rate="+5%", pitch="-1Hz")
await communicate.save(tmp)
audio = AudioFileClip(tmp).set_start(start).volumex(4.0)
# រក្សាល្បឿននិយាយឱ្យត្រូវនឹងពេលវេលា (Time-Stretching)
if audio.duration > duration:
return vfx.speedx(audio, factor=audio.duration/duration).set_duration(duration), tmp
return audio, tmp
except: return None, None
async def start_dubbing():
# ១. ស្ដាប់ និងបំប្លែងសំឡេងចិន (Whisper Medium - ខ្លាំងជាងមុន)
model = whisper.load_model("medium")
transcribe = model.transcribe(VIDEO_PATH, task="transcribe", language="zh")
# ២. ចងក្រងឃ្លាដែលនៅជិតគ្នា (Smart Merging)
segments = transcribe['segments']
merged_segments = []
if segments:
curr = segments[0]
for next_seg in segments[1:]:
# បើឃ្លាពីរនៅឆ្ងាយគ្នាលើសពី ០.៥ វិនាទី ចាំផ្ដាច់ឃ្លា
if next_seg['start'] - curr['end'] < 0.5:
curr['text'] += " " + next_seg['text']
curr['end'] = next_seg['end']
else:
merged_segments.append(curr)
curr = next_seg
merged_segments.append(curr)
# ៣. បង្កើតសំឡេងខ្មែរ
video = VideoFileClip(VIDEO_PATH)
audio_tracks = [video.audio.volumex(0.15)] # បន្ថយសំឡេងដើម
print(f"🎙️ កំពុងដំណើរការ Dubbing លើ {len(merged_segments)} ឃ្លាធំៗ...")
tasks = [generate_smooth_audio(s['text'], s['start'], s['end']-s['start']) for s in merged_segments]
results = await asyncio.gather(*tasks)
temp_files = []
for aud, path in results:
if aud:
audio_tracks.append(aud)
temp_files.append(path)
# ៤. រួមបញ្ចូលគ្នា និង Export
final_audio = CompositeAudioClip(audio_tracks)
final_video = video.set_audio(final_audio)
final_video.write_videofile(OUTPUT_PATH, codec="libx264", audio_codec="aac")
# សម្អាត File បណ្ដោះអាសន្ន
for f in temp_files:
if os.path.exists(f): os.remove(f)
print(f"✅ ជោគជ័យ! វីដេអូបានកែសម្រួលឱ្យរលូនជាងមុនរួចរាល់៖ {OUTPUT_PATH}")
await start_dubbing()
China Movies Speak Khmer.
May 03, 2026
1
# ដំឡើងគ្រឿងបន្លាស់ដែលបាត់ (ម្ចាស់គ្រូត្រូវរត់អាហ្នឹងមុនគេ)
!pip install openai-whisper edge-tts googletrans==4.0.0-rc1 moviepy==1.0.3
2
#@title 🪄 ម៉ាស៊ីនបកប្រែវីដេអូ - គ្រូអាគម AI
import os
# --- ផ្នែកបង្កើតប៊ូតុងសម្រាប់បញ្ចូលឈ្មោះ (Forms) ---
Video_Name = "movie.mp4" #@param {type:"string"}
Language = "Khmer" #@param ["Khmer", "English", "Chinese"]
def create_project_folders():
folders = ['input_video', 'output_audio', 'final_result']
for folder in folders:
if not os.path.exists(folder):
os.makedirs(folder)
print(f"✅ បង្កើត Folder: {folder} រួចរាល់!")
else:
print(f"📂 Folder: {folder} មានរួចហើយ។")
if __name__ == "__main__":
print(f"--- 🧙♂️ ចាប់ផ្ដើមរៀបចំសម្រាប់វីដេអូ: {Video_Name} ---")
create_project_folders()
# បង្ហាញផ្លូវដែលត្រូវដាក់វីដេអូឱ្យមិត្តភក្តិឃើញច្បាស់ៗ
path = os.path.join('input_video', Video_Name)
print(f"--- 👉 friend ត្រូវយកវីដេអូទៅដាក់ក្នុង: {path} ---")
3
import os, whisper, asyncio, edge_tts, re
from googletrans import Translator
from moviepy.editor import VideoFileClip, AudioFileClip, CompositeAudioClip
import moviepy.video.fx.all as vfx
# --- ⚙️ កំណត់រចនាសម្ព័ន្ធ ---
Video_Name = "PRO_AI_DUB_BM7.mp4"
VIDEO_PATH = f"input_video/{Video_Name}"
OUTPUT_PATH = f"final_result/CHARACTER_DUB_{Video_Name}"
translator = Translator()
# --- 🎙️ បញ្ជីសំឡេងតួអង្គ ---
VOICES = {
"MALE": "km-KH-PisethNeural",
"FEMALE": "km-KH-SreymomNeural"
}
# --- 🧠 ម៉ាស៊ីនវិភាគតួអង្គ និងកែសម្រួលភាសា ---
def identify_and_filter(text):
# ១. បកប្រែជាមុនសិន
raw_kh = translator.translate(text, src='zh-cn', dest='km').text
# ២. កំណត់ភេទតួអង្គតាមរយៈពាក្យគន្លឹះ (Heuristic Detection)
# បើមានពាក្យ "នាង" "ស្រី" "ព្រះនាង" យើងនឹងប្រើសំឡេងស្រី
female_keywords = ['នាង', 'ស្រី', 'ព្រះនាង', 'អ្នកនាង', 'ម៉ាក់', 'បងស្រី', 'អូន']
gender = "MALE"
if any(word in raw_kh for word in female_keywords):
gender = "FEMALE"
# ៣. កែសម្រួលពាក្យឱ្យសមជាតួអង្គក្នុងរឿង (Drama Style)
corrections = {
"អ្នក": "ឯង", "ខ្ញុំ": "យើង",
"តើអ្នកកំពុងធ្វើអ្វី": "ឯងកំពុងធ្វើស្អី?",
"ពិតជា": "ពិតមែនហើយ", "មិនអាច": "មិនបានទេ",
"តើមានរឿងអ្វី": "មានរឿងអី?"
}
for old, new in corrections.items():
raw_kh = raw_kh.replace(old, new)
return raw_kh.strip(), gender
async def generate_character_audio(text, start, duration):
try:
kh_text, gender = identify_and_filter(text)
if not kh_text: return None, None
selected_voice = VOICES[gender]
# កំណត់ Pitch ឱ្យខុសគ្នាបន្តិចៗតាមឃ្លា ដើម្បីកុំឱ្យសំឡេងដូចគ្នាច្រើនពេក
pitch = "-1Hz" if gender == "MALE" else "+1Hz"
tmp = f"tmp_{start}.mp3"
communicate = edge_tts.Communicate(kh_text, selected_voice, rate="+7%", pitch=pitch)
await communicate.save(tmp)
audio = AudioFileClip(tmp).set_start(start).volumex(4.5)
if audio.duration > duration:
return vfx.speedx(audio, factor=audio.duration/duration).set_duration(duration), tmp
return audio, tmp
except Exception as e:
print(f"Error: {e}")
return None, None
async def start_multi_dubbing():
# ១. ស្កែនវីដេអូ
model = whisper.load_model("medium")
print("🔍 កំពុងវិភាគសាច់រឿង និងតួអង្គ...")
transcribe = model.transcribe(VIDEO_PATH, task="transcribe", language="zh")
# ២. រៀបចំ Segment
segments = transcribe['segments']
video = VideoFileClip(VIDEO_PATH)
audio_tracks = [video.audio.volumex(0.15)]
# ៣. បង្កើតសំឡេងតាមតួអង្គ
print(f"🎬 កំពុងបញ្ចូលសំឡេងតួអង្គក្នុង {Video_Name}...")
tasks = [generate_character_audio(s['text'], s['start'], s['end']-s['start']) for s in segments]
results = await asyncio.gather(*tasks)
temp_files = []
for aud, path in results:
if aud:
audio_tracks.append(aud)
temp_files.append(path)
# ៤. បញ្ចប់ការងារ
final_audio = CompositeAudioClip(audio_tracks)
final_video = video.set_audio(final_audio)
final_video.write_videofile(OUTPUT_PATH, codec="libx264", audio_codec="aac")
for f in temp_files:
if os.path.exists(f): os.remove(f)
print(f"✅ រួចរាល់! វីដេអូបំបែកសំឡេងតួអង្គ៖ {OUTPUT_PATH}")
if __name__ == "__main__":
await start_multi_dubbing()
