arcacolab commited on
Commit
6503586
ยท
verified ยท
1 Parent(s): 3d3ce25

Upload run_audio_generator.py

Browse files
Files changed (1) hide show
  1. run_audio_generator.py +319 -0
run_audio_generator.py ADDED
@@ -0,0 +1,319 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # V2A (Video-to-Audio) ๋ฐฑ์—”๋“œ ์Šคํฌ๋ฆฝํŠธ (VRAM ์ตœ์ ํ™”)
2
+
3
+ import sys
4
+ import os
5
+ import time
6
+ import glob
7
+ import gc
8
+ import torch
9
+ import subprocess
10
+ import random
11
+ import argparse
12
+ import shutil
13
+ from typing import Sequence, Mapping, Any, Union
14
+
15
+ # --- 0. ๊ธฐ๋ณธ ํ—ฌํผ ํ•จ์ˆ˜ (I2V ์Šคํฌ๋ฆฝํŠธ์™€ ๋™์ผ) ---
16
+
17
+ def to_bool(s: str) -> bool:
18
+ return s.lower() in ['true', '1', 't', 'y', 'yes', 'on']
19
+
20
+ def clear_memory():
21
+ """VRAM ๋ฐ RAM ์บ์‹œ๋ฅผ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค."""
22
+ if torch.cuda.is_available():
23
+ torch.cuda.empty_cache()
24
+ torch.cuda.ipc_collect()
25
+ gc.collect()
26
+
27
+ COMFYUI_BASE_PATH = '/content/ComfyUI'
28
+
29
+ def get_value_at_index(obj: Union[Sequence, Mapping], index: int) -> Any:
30
+ """ ComfyUI ๋…ธ๋“œ ์ถœ๋ ฅ์—์„œ ๊ฐ’์„ ์•ˆ์ „ํ•˜๊ฒŒ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. """
31
+ try:
32
+ return obj[index]
33
+ except (KeyError, TypeError):
34
+ if isinstance(obj, dict) and "result" in obj:
35
+ return obj["result"][index]
36
+ raise
37
+
38
+ def add_comfyui_directory_to_sys_path() -> None:
39
+ """ ComfyUI ๊ฒฝ๋กœ๋ฅผ sys.path์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. """
40
+ if os.path.isdir(COMFYUI_BASE_PATH) and COMFYUI_BASE_PATH not in sys.path:
41
+ sys.path.append(COMFYUI_BASE_PATH)
42
+ print(f"'{COMFYUI_BASE_PATH}' added to sys.path")
43
+
44
+ def import_custom_nodes() -> None:
45
+ """
46
+ ComfyUI ์ปค์Šคํ…€ ๋…ธ๋“œ๋ฅผ ๋กœ๋“œํ•˜๊ธฐ ์œ„ํ•ด ๋น„๋™๊ธฐ ํ™˜๊ฒฝ์„ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.
47
+ (I2V ์Šคํฌ๋ฆฝํŠธ์˜ import_custom_nodes์™€ ๋™์ผ)
48
+ """
49
+ try:
50
+ import nest_asyncio
51
+ nest_asyncio.apply()
52
+ except ImportError:
53
+ print("nest_asyncio not found, installing...")
54
+ try:
55
+ subprocess.run([sys.executable, "-m", "pip", "install", "-q", "nest_asyncio"], check=True)
56
+ import nest_asyncio
57
+ nest_asyncio.apply()
58
+ print("nest_asyncio installed and applied.")
59
+ except Exception as e:
60
+ print(f"Failed to install or apply nest_asyncio: {e}")
61
+
62
+ import asyncio, execution, server
63
+ from nodes import init_extra_nodes
64
+ try:
65
+ loop = asyncio.get_event_loop()
66
+ if loop.is_closed():
67
+ loop = asyncio.new_event_loop()
68
+ asyncio.set_event_loop(loop)
69
+ except RuntimeError:
70
+ loop = asyncio.new_event_loop()
71
+ asyncio.set_event_loop(loop)
72
+
73
+ server_instance = server.PromptServer(loop)
74
+ execution.PromptQueue(server_instance)
75
+
76
+ if not loop.is_running():
77
+ try:
78
+ loop.run_until_complete(init_extra_nodes())
79
+ except RuntimeError as e:
80
+ print(f"Note: Could not run init_extra_nodes synchronously: {e}")
81
+ try: asyncio.ensure_future(init_extra_nodes())
82
+ except Exception as fut_e: print(f"Error trying async init_extra_nodes: {fut_e}")
83
+ else:
84
+ try: asyncio.ensure_future(init_extra_nodes())
85
+ except Exception as fut_e: print(f"Error trying async init_extra_nodes on running loop: {fut_e}")
86
+
87
+
88
+ # --- 1. Gradio UI์—์„œ ๋ชจ๋“  ์ธ์ˆ˜๋ฅผ ๋ฐ›๊ธฐ ์œ„ํ•œ ArgParser ---
89
+
90
+ def parse_args():
91
+ parser = argparse.ArgumentParser(description="ComfyUI V2A (Video-to-Audio) Generation Script")
92
+
93
+ # 1. ์ผ๋ฐ˜ ์„ค์ • (Input/Prompt)
94
+ parser.add_argument("--input_video_path", type=str, required=True, help="์˜ค๋””์˜ค๋ฅผ ์ƒ์„ฑํ•  ์ž…๋ ฅ ๋น„๋””์˜ค ํŒŒ์ผ ๊ฒฝ๋กœ")
95
+ parser.add_argument("--prompt", type=str, default="")
96
+ parser.add_argument("--negative_prompt", type=str, default="")
97
+
98
+ # 2. ๊ณ ๊ธ‰ ์„ค์ • - ์ƒ˜ํ”Œ๋ง (MMAudio Sampler)
99
+ parser.add_argument("--steps", type=int, default=25)
100
+ parser.add_argument("--cfg", type=float, default=4.5)
101
+ parser.add_argument("--seed", type=int, default=-1)
102
+ parser.add_argument("--mask_away_clip", type=str, default="off") # bool
103
+ parser.add_argument("--force_offload", type=str, default="off") # bool
104
+
105
+ # 3. ๊ณ ๊ธ‰ ์„ค์ • - ๋ชจ๋ธ (Loaders)
106
+ parser.add_argument("--mmaudio_model", type=str, default="mmaudio_large_44k_v2_fp16.safetensors")
107
+ parser.add_argument("--base_precision", type=str, default="fp16")
108
+ parser.add_argument("--vae_model", type=str, default="mmaudio_vae_44k_fp16.safetensors")
109
+ parser.add_argument("--synchformer_model", type=str, default="mmaudio_synchformer_fp16.safetensors")
110
+ parser.add_argument("--clip_model", type=str, default="apple_DFN5B-CLIP-ViT-H-14-384_fp16.safetensors")
111
+ parser.add_argument("--mode", type=str, default="44k")
112
+ parser.add_argument("--precision", type=str, default="fp16", help="Feature Utils Precision")
113
+
114
+ # 4. ๊ณ ๊ธ‰ ์„ค์ • - ๋น„๋””์˜ค ๋กœ๋”ฉ (VHS LoadVideo)
115
+ parser.add_argument("--force_rate", type=int, default=0)
116
+ parser.add_argument("--custom_width", type=int, default=0)
117
+ parser.add_argument("--custom_height", type=int, default=0)
118
+ parser.add_argument("--frame_load_cap", type=int, default=0)
119
+ parser.add_argument("--skip_first_frames", type=int, default=0)
120
+ parser.add_argument("--select_every_nth", type=int, default=1)
121
+ parser.add_argument("--load_format", type=str, default="AnimateDiff")
122
+
123
+ # 5. ๊ณ ๊ธ‰ ์„ค์ • - ๋น„๋””์˜ค ๊ฒฐํ•ฉ (VHS VideoCombine)
124
+ parser.add_argument("--loop_count", type=int, default=0)
125
+ parser.add_argument("--filename_prefix", type=str, default="MMaudio")
126
+ parser.add_argument("--combine_format", type=str, default="video/h264-mp4")
127
+ parser.add_argument("--pix_fmt", type=str, default="yuv420p")
128
+ parser.add_argument("--crf", type=int, default=19)
129
+ parser.add_argument("--save_metadata", type=str, default="on") # bool
130
+ parser.add_argument("--trim_to_audio", type=str, default="off") # bool
131
+ parser.add_argument("--pingpong", type=str, default="off") # bool
132
+
133
+ return parser.parse_args()
134
+
135
+
136
+ # --- 2. VRAM ์ตœ์ ํ™”๋œ ๋ฉ”์ธ ์‹คํ–‰ ํ•จ์ˆ˜ ---
137
+
138
+ # --- 2. VRAM ์ตœ์ ํ™”๋œ ๋ฉ”์ธ ์‹คํ–‰ ํ•จ์ˆ˜ ---
139
+
140
+ def main():
141
+ args = parse_args()
142
+ print("๐Ÿš€ V2A ์˜ค๋””์˜ค ์ƒ์„ฑ์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค (VRAM Optimized)...")
143
+
144
+ # --- ํ™˜๊ฒฝ ์„ค์ • ---
145
+ add_comfyui_directory_to_sys_path()
146
+
147
+ try:
148
+ from utils.extra_config import load_extra_path_config
149
+ except ImportError:
150
+ print("โš ๏ธ ComfyUI์˜ extra_model_paths.yaml ๋กœ๋”ฉ ์‹คํŒจ (๋ฌด์‹œํ•˜๊ณ  ์ง„ํ–‰)")
151
+ load_extra_path_config = lambda x: None
152
+
153
+ extra_model_paths_file = os.path.join(COMFYUI_BASE_PATH, "extra_model_paths.yaml")
154
+ if os.path.exists(extra_model_paths_file):
155
+ load_extra_path_config(extra_model_paths_file)
156
+
157
+ print("ComfyUI ์ปค์Šคํ…€ ๋…ธ๋“œ ์ดˆ๊ธฐํ™” ์ค‘...")
158
+ import_custom_nodes()
159
+ from nodes import NODE_CLASS_MAPPINGS
160
+ print("์ปค์Šคํ…€ ๋…ธ๋“œ ์ดˆ๊ธฐํ™” ์™„๋ฃŒ.")
161
+
162
+ # --- ๋…ธ๋“œ ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šคํ™” ---
163
+ mmaudiomodelloader = NODE_CLASS_MAPPINGS["MMAudioModelLoader"]()
164
+ vhs_loadvideo = NODE_CLASS_MAPPINGS["VHS_LoadVideo"]()
165
+ mmaudiofeatureutilsloader = NODE_CLASS_MAPPINGS["MMAudioFeatureUtilsLoader"]()
166
+ vhs_videoinfo = NODE_CLASS_MAPPINGS["VHS_VideoInfo"]()
167
+ mmaudiosampler = NODE_CLASS_MAPPINGS["MMAudioSampler"]()
168
+ vhs_videocombine = NODE_CLASS_MAPPINGS["VHS_VideoCombine"]()
169
+
170
+ # --- ์‹œ๋“œ ์„ค์ • ---
171
+ if args.seed == -1:
172
+ final_seed = random.randint(1, 2**64)
173
+ print(f" - ๋žœ๋ค ์‹œ๋“œ ์ƒ์„ฑ: {final_seed}")
174
+ else:
175
+ final_seed = args.seed
176
+ print(f" - ๊ณ ์ • ์‹œ๋“œ ์‚ฌ์šฉ: {final_seed}")
177
+
178
+ # --- VRAM ์ตœ์ ํ™” ํŒŒ์ดํ”„๋ผ์ธ ---
179
+ with torch.inference_mode():
180
+
181
+ # โœจ [์ˆ˜์ •๋จ] 1๋‹จ๊ณ„: ์˜ค๋””์˜ค ์ƒ์„ฑ์„ ์œ„ํ•œ ๋น„๋””์˜ค ๋กœ๋“œ (25 FPS ๊ฐ•์ œ)
182
+ print(f"\n1๋‹จ๊ณ„: ์˜ค๋””์˜ค ์ƒ์„ฑ์„ ์œ„ํ•œ ๋น„๋””์˜ค ๋กœ๋“œ (25 FPS ๊ฐ•์ œ)... ({args.input_video_path})")
183
+ vhs_loadvideo_91_audio = vhs_loadvideo.load_video(
184
+ video=args.input_video_path,
185
+ force_rate=25, # โœจ ์˜ค๋””์˜ค์šฉ 25 FPS ๊ฐ•์ œ
186
+ custom_width=args.custom_width,
187
+ custom_height=args.custom_height,
188
+ frame_load_cap=args.frame_load_cap,
189
+ skip_first_frames=args.skip_first_frames,
190
+ select_every_nth=args.select_every_nth,
191
+ format=args.load_format,
192
+ unique_id=random.randint(1, 2**64)
193
+ )
194
+ images_for_audio = get_value_at_index(vhs_loadvideo_91_audio, 0) # โœจ ์˜ค๋””์˜ค์šฉ ์ด๋ฏธ์ง€
195
+
196
+ # ์›๋ณธ ๋น„๋””์˜ค ์ •๋ณด ์ถ”์ถœ (์žฌ์ƒ ์‹œ๊ฐ„, ์›๋ณธ FPS ๋“ฑ)
197
+ vhs_videoinfo_105 = vhs_videoinfo.get_video_info(
198
+ video_info=get_value_at_index(vhs_loadvideo_91_audio, 3)
199
+ )
200
+ del vhs_loadvideo_91_audio # ํ…์„œ ๋กœ๋”๋Š” ์ฆ‰์‹œ ์‚ญ์ œ
201
+
202
+ duration = get_value_at_index(vhs_videoinfo_105, 7)
203
+ original_frame_rate = get_value_at_index(vhs_videoinfo_105, 0) # โœจ ์ตœ์ข… ๋น„๋””์˜ค์— ์‚ฌ์šฉํ•  ์›๋ณธ FPS
204
+ print(f" - ๋น„๋””์˜ค ์ •๋ณด: {duration}์ดˆ, ์›๋ณธ {original_frame_rate} FPS")
205
+ clear_memory()
206
+
207
+ # 2๋‹จ๊ณ„: ์˜ค๋””์˜ค ๋ชจ๋ธ ๋กœ๋“œ
208
+ print(f"\n2๋‹จ๊ณ„: ์˜ค๋””์˜ค ๋ชจ๋ธ ๋กœ๋”ฉ ์ค‘...")
209
+ print(f" - MMAudio ๋ชจ๋ธ: {args.mmaudio_model} ({args.base_precision})")
210
+ mmaudiomodelloader_85 = mmaudiomodelloader.loadmodel(
211
+ mmaudio_model=args.mmaudio_model,
212
+ base_precision=args.base_precision
213
+ )
214
+ mmaudio_model = get_value_at_index(mmaudiomodelloader_85, 0)
215
+
216
+ print(f" - ์œ ํ‹ธ๋ฆฌํ‹ฐ ๋ชจ๋ธ: (Mode: {args.mode}, Precision: {args.precision})")
217
+ mmaudiofeatureutilsloader_102 = mmaudiofeatureutilsloader.loadmodel(
218
+ vae_model=args.vae_model,
219
+ synchformer_model=args.synchformer_model,
220
+ clip_model=args.clip_model,
221
+ mode=args.mode,
222
+ precision=args.precision
223
+ )
224
+ feature_utils = get_value_at_index(mmaudiofeatureutilsloader_102, 0)
225
+
226
+ # 3๋‹จ๊ณ„: ์˜ค๋””์˜ค ์ƒ์„ฑ (์ƒ˜ํ”Œ๋ง)
227
+ print(f"\n3๋‹จ๊ณ„: ์˜ค๋””์˜ค ์ƒ์„ฑ ์ค‘... (Steps: {args.steps}, CFG: {args.cfg})")
228
+ mmaudiosampler_92 = mmaudiosampler.sample(
229
+ duration=duration,
230
+ steps=args.steps,
231
+ cfg=args.cfg,
232
+ seed=final_seed,
233
+ prompt=args.prompt,
234
+ negative_prompt=args.negative_prompt,
235
+ mask_away_clip=to_bool(args.mask_away_clip),
236
+ force_offload=to_bool(args.force_offload),
237
+ mmaudio_model=mmaudio_model,
238
+ feature_utils=feature_utils,
239
+ images=images_for_audio # โœจ ์˜ค๋””์˜ค์šฉ ์ด๋ฏธ์ง€ ํ…์„œ ์‚ฌ์šฉ
240
+ )
241
+ generated_audio = get_value_at_index(mmaudiosampler_92, 0)
242
+
243
+ # โœจ [์ˆ˜์ •๋จ] 4๋‹จ๊ณ„: ๋ชจ๋ธ ๋ฐ ์˜ค๋””์˜ค์šฉ ์ด๋ฏธ์ง€ ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ
244
+ print(f"\n4๋‹จ๊ณ„: ๋ชจ๋ธ ๋ฐ ์˜ค๋””์˜ค์šฉ ์ด๋ฏธ์ง€ ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ ์ค‘...")
245
+ del mmaudiomodelloader_85, mmaudio_model, mmaudiofeatureutilsloader_102, feature_utils
246
+ del images_for_audio # โœจ ์˜ค๋””์˜ค์šฉ ์ด๋ฏธ์ง€ ํ…์„œ ์‚ญ์ œ (VRAM ํ™•๋ณด)
247
+ clear_memory()
248
+
249
+ # โœจ [์ˆ˜์ •๋จ] 5๋‹จ๊ณ„: ๋น„๋””์˜ค ๊ฒฐํ•ฉ์„ ์œ„ํ•œ ์›๋ณธ ๋น„๋””์˜ค ๋กœ๋“œ
250
+ print(f"\n5๋‹จ๊ณ„: ๋น„๋””์˜ค ๊ฒฐํ•ฉ์„ ์œ„ํ•œ ์›๋ณธ ๋น„๋””์˜ค ๋กœ๋“œ (์‚ฌ์šฉ์ž ์„ค์ • FPS: {args.force_rate})...")
251
+ vhs_loadvideo_91_combine = vhs_loadvideo.load_video(
252
+ video=args.input_video_path,
253
+ force_rate=args.force_rate, # โœจ ์‚ฌ์šฉ์ž์˜ ์›๋ณธ FPS ์„ค์ • ์‚ฌ์šฉ (๋ณดํ†ต 0)
254
+ custom_width=args.custom_width,
255
+ custom_height=args.custom_height,
256
+ frame_load_cap=args.frame_load_cap,
257
+ skip_first_frames=args.skip_first_frames,
258
+ select_every_nth=args.select_every_nth,
259
+ format=args.load_format,
260
+ unique_id=random.randint(1, 2**64) # ๋‹ค๋ฅธ ID ์‚ฌ์šฉ
261
+ )
262
+ images_for_combine = get_value_at_index(vhs_loadvideo_91_combine, 0) # โœจ ๊ฒฐํ•ฉ์šฉ ์ด๋ฏธ์ง€
263
+ del vhs_loadvideo_91_combine
264
+ clear_memory()
265
+
266
+ # โœจ [์ˆ˜์ •๋จ] 6๋‹จ๊ณ„: ๋น„๋””์˜ค์™€ ์˜ค๋””์˜ค ๊ฒฐํ•ฉ ๋ฐ ์ €์žฅ
267
+ print(f"\n6๋‹จ๊ณ„: ๋น„๋””์˜ค + ์˜ค๋””์˜ค ๊ฒฐํ•ฉ ๋ฐ ์ €์žฅ ์ค‘...")
268
+ timestamp = time.strftime("%Y%m%d-%H%M%S")
269
+ final_filename_prefix = f"{args.filename_prefix}_{timestamp}"
270
+
271
+ vhs_videocombine_97 = vhs_videocombine.combine_video(
272
+ frame_rate=original_frame_rate, # โœจ 1๋‹จ๊ณ„์—์„œ ์ถ”์ถœํ•œ "์›๋ณธ" FPS ์‚ฌ์šฉ
273
+ loop_count=args.loop_count,
274
+ filename_prefix=final_filename_prefix,
275
+ format=args.combine_format,
276
+ pix_fmt=args.pix_fmt,
277
+ crf=args.crf,
278
+ save_metadata=to_bool(args.save_metadata),
279
+ trim_to_audio=to_bool(args.trim_to_audio),
280
+ pingpong=to_bool(args.pingpong),
281
+ save_output=True, # <<< ํŒŒ์ผ ์ €์žฅ์„ ์œ„ํ•ด True๋กœ ์„ค์ •
282
+ images=images_for_combine, # โœจ ๊ฒฐํ•ฉ์šฉ ์ด๋ฏธ์ง€ ํ…์„œ ์‚ฌ์šฉ
283
+ audio=generated_audio,
284
+ unique_id=random.randint(1, 2**64)
285
+ )
286
+
287
+ # โœจ [์ถ”๊ฐ€] ๊ฒฐํ•ฉ ํ›„ ์ฆ‰์‹œ ํ…์„œ ์‚ญ์ œ
288
+ del images_for_combine, generated_audio
289
+ clear_memory()
290
+
291
+ # 7๋‹จ๊ณ„: Gradio UI๋กœ ๋ฐ˜ํ™˜ํ•  ์ตœ์ข… ํŒŒ์ผ ๊ฒฝ๋กœ ์ถœ๋ ฅ
292
+ try:
293
+ # ๋””๋ฒ„๊ทธ ๊ฒฐ๊ณผ ๊ธฐ๋ฐ˜: ๋ฐ˜ํ™˜๊ฐ’ {'result': ((True, [workflow.png, video.mp4, video-audio.mp4]),)}
294
+ # ์šฐ๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ๋กœ๋Š” ๋ฆฌ์ŠคํŠธ์˜ 3๋ฒˆ์งธ ํ•ญ๋ชฉ(์ธ๋ฑ์Šค 2)์ž…๋‹ˆ๋‹ค.
295
+ file_path_list = vhs_videocombine_97['result'][0][1]
296
+ final_video_path = file_path_list[2] # ์˜ค๋””์˜ค๊ฐ€ ํฌํ•จ๋œ ์ตœ์ข… ๋น„๋””์˜ค ๊ฒฝ๋กœ
297
+ except Exception as e:
298
+ print(f"โŒ [์˜ค๋ฅ˜] ์ตœ์ข… ํŒŒ์ผ ๊ฒฝ๋กœ๋ฅผ ์ถ”์ถœํ•˜๋Š” ๋ฐ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค: {e}")
299
+ print(f" - ์ „์ฒด ๋ฐ˜ํ™˜๊ฐ’: {vhs_videocombine_97}")
300
+ final_video_path = None
301
+
302
+ if final_video_path and os.path.exists(final_video_path):
303
+ print(f"โœ… ์˜ค๋””์˜ค ์ƒ์„ฑ ๋ฐ ๊ฒฐํ•ฉ ์™„๋ฃŒ!")
304
+ print(f"LATEST_VIDEO_PATH:{final_video_path}")
305
+
306
+ # I2V ์Šคํฌ๋ฆฝํŠธ์™€ ๋™์ผํ•œ ์ถœ๋ ฅ์„ ์œ„ํ•ด ์›๋ณธ ๋ณต์‚ฌ๋ณธ ์ƒ์„ฑ
307
+ base, ext = os.path.splitext(final_video_path)
308
+ original_copy_path = f"{base}_original{ext}"
309
+ try:
310
+ shutil.copy2(final_video_path, original_copy_path)
311
+ print(f"โœ… ์›๋ณธ ๋ณต์‚ฌ๋ณธ ์ƒ์„ฑ ์™„๋ฃŒ: {original_copy_path}")
312
+ print(f"ORIGINAL_COPY_PATH:{original_copy_path}")
313
+ except Exception as e:
314
+ print(f"โŒ ์›๋ณธ ๋ณต์‚ฌ๋ณธ ์ƒ์„ฑ ์‹คํŒจ: {e}")
315
+ else:
316
+ print(f"โŒ ์ตœ์ข… ๋น„๋””์˜ค ํŒŒ์ผ ๊ฒฝ๋กœ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
317
+
318
+ if __name__ == "__main__":
319
+ main()