euiia commited on
Commit
f5c517f
·
verified ·
1 Parent(s): d198ecb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +333 -117
app.py CHANGED
@@ -1,31 +1,43 @@
1
- #Uma implementação aberta e funcional da arquitetura ADUC-SDR para geração de vídeo coerente.
2
- # Copyright (C) 4 de Agosto de 2025 Carlos Rodrigues dos Santos
3
  #
4
- #Versao: 1.5.1
5
  #
6
- # Contato:
 
 
7
  # Carlos Rodrigues dos Santos
8
  # carlex22@gmail.com
9
  #
10
- # Repositórios e Projetos Relacionados:
11
  # GitHub: https://github.com/carlex22/Aduc-sdr
12
- # YouTube (Resultados): https://m.youtube.com/channel/UC3EgoJi_Fv7yuDpvfYNtoIQ
13
  #
14
- # Este programa é software livre: você pode redistribuí-lo e/ou modificá-lo
15
- # sob os termos da Licença Pública Geral Affero da GNU como publicada pela
16
- # Free Software Foundation, seja a versão 3 da Licença, ou
17
- # (a seu critério) qualquer versão posterior.
18
  #
19
- # Este programa é distribuído na esperança de que seja útil,
20
- # mas SEM QUALQUER GARANTIA; sem mesmo a garantia implícita de
21
- # COMERCIALIZAÇÃO ou ADEQUAÇÃO A UM DETERMINADO FIM. Consulte a
22
- # Licença Pública Geral Affero da GNU para mais detalhes.
23
  #
24
- # Você deve ter recebido uma cópia da Licença Pública Geral Affero da GNU
25
- # junto com este programa. Se não, veja <https://www.gnu.org/licenses/>.
26
  #
27
- # AVISO DE PATENTE PENDENTE: O método e sistema ADUC implementado neste
28
- # software está em processo de patenteamento. Consulte NOTICE.md.
 
 
 
 
 
 
 
 
 
 
 
29
 
30
  import gradio as gr
31
  import yaml
@@ -38,7 +50,9 @@ import json
38
 
39
  from aduc_orchestrator import AducOrchestrator
40
 
41
- # --- 1. CONFIGURAÇÃO E INICIALIZAÇÃO ---
 
 
42
 
43
  LOG_FILE_PATH = "aduc_log.txt"
44
  if os.path.exists(LOG_FILE_PATH):
@@ -61,43 +75,54 @@ root_logger.addHandler(file_handler)
61
 
62
  logger = logging.getLogger(__name__)
63
 
 
64
  i18n = {}
65
  try:
66
  with open("i18n.json", "r", encoding="utf-8") as f:
67
  i18n = json.load(f)
68
  except Exception as e:
69
- logger.error(f"Erro ao carregar i18n.json: {e}")
70
  i18n = {"pt": {}, "en": {}, "zh": {}}
71
 
 
72
  if 'pt' not in i18n: i18n['pt'] = i18n.get('en', {})
73
  if 'en' not in i18n: i18n['en'] = {}
74
  if 'zh' not in i18n: i18n['zh'] = i18n.get('en', {})
75
 
 
76
  try:
77
  with open("config.yaml", 'r') as f: config = yaml.safe_load(f)
78
  WORKSPACE_DIR = config['application']['workspace_dir']
79
  aduc = AducOrchestrator(workspace_dir=WORKSPACE_DIR)
80
- logger.info("Orquestrador ADUC e Especialistas inicializados com sucesso.")
81
  except Exception as e:
82
- logger.error(f"ERRO CRÍTICO ao inicializar: {e}", exc_info=True)
83
  exit()
84
 
85
- # --- 2. WRAPPERS DA UI ---
 
 
 
 
 
 
 
 
 
 
 
86
 
87
- def run_mode_a_wrapper(prompt, num_keyframes, ref_files, resolution_str, duration_per_fragment, progress=gr.Progress()):
88
- if not ref_files:
89
- raise gr.Error("Por favor, forneça pelo menos uma imagem de referência.")
90
-
91
  ref_paths = [aduc.process_image_for_story(f.name, 480, f"ref_processed_{i}.png") for i, f in enumerate(ref_files)]
92
-
93
- progress(0.1, desc="Gerando roteiro...")
94
  storyboard, initial_ref_path, _ = aduc.task_generate_storyboard(prompt, num_keyframes, ref_paths, progress)
95
-
96
  resolution = int(resolution_str.split('x')[0])
97
 
 
98
  def cb_factory(scene_index, total_scenes):
99
  start_time = time.time()
100
- total_steps = 12
101
  def callback(pipe_self, step, timestep, callback_kwargs):
102
  elapsed = time.time() - start_time
103
  current_step = step + 1
@@ -110,65 +135,164 @@ def run_mode_a_wrapper(prompt, num_keyframes, ref_files, resolution_str, duratio
110
  progress(base_progress + step_progress, desc=desc)
111
  return {}
112
  return callback
113
-
114
  final_keyframes = aduc.task_generate_keyframes(storyboard, initial_ref_path, prompt, resolution, cb_factory)
115
-
 
116
  return gr.update(value=storyboard), gr.update(value=final_keyframes), gr.update(visible=True, open=True)
117
 
118
- def run_mode_b_wrapper(prompt, num_keyframes, ref_files, progress=gr.Progress()):
119
- if not ref_files or len(ref_files) < 2:
120
- raise gr.Error("Modo Fotógrafo requer pelo menos 2 imagens: uma base e uma para o banco de cenas.")
 
 
 
 
121
 
122
  base_ref_paths = [aduc.process_image_for_story(ref_files[0].name, 480, "base_ref_processed_0.png")]
123
  pool_ref_paths = [aduc.process_image_for_story(f.name, 480, f"pool_ref_{i+1}.png") for i, f in enumerate(ref_files[1:])]
124
 
125
- progress(0.1, desc="Gerando roteiro...")
126
  storyboard, _, _ = aduc.task_generate_storyboard(prompt, num_keyframes, base_ref_paths, progress)
127
-
128
- progress(0.5, desc="IA (Fotógrafo) está selecionando as melhores cenas...")
129
  selected_keyframes = aduc.task_select_keyframes(storyboard, base_ref_paths, pool_ref_paths)
130
-
131
  return gr.update(value=storyboard), gr.update(value=selected_keyframes), gr.update(visible=True, open=True)
132
 
133
- def run_video_production_wrapper(keyframes, prompt, duration,
134
- trim_percent, handler_strength, destination_convergence_strength,
135
- use_upscaler, use_refiner, use_hd, use_audio,
136
- video_resolution,
137
- progress=gr.Progress()):
 
 
 
 
138
  yield {
139
- final_video_output: gr.update(value=None, visible=True, label="🎬 Produzindo seu filme... Por favor, aguarde.")
 
 
140
  }
141
-
142
  resolution = int(video_resolution.split('x')[0])
143
- final_movie_path = None
144
 
145
- for update in aduc.task_produce_final_movie_with_feedback(
146
- keyframes, prompt, duration,
 
147
  int(trim_percent), handler_strength, destination_convergence_strength,
148
- use_upscaler, use_refiner, use_hd, use_audio,
149
  resolution, use_continuity_director=True, progress=progress
150
- ):
151
- if "final_path" in update and update["final_path"]:
152
- final_movie_path = update["final_path"]
153
- break
154
 
 
 
 
155
  yield {
156
- final_video_output: gr.update(value=final_movie_path, label="🎉 FILME COMPLETO 🎉")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  }
158
 
159
  def get_log_content():
 
 
 
160
  try:
161
  with open(LOG_FILE_PATH, "r", encoding="utf-8") as f:
162
  return f.read()
163
  except FileNotFoundError:
164
- return "Arquivo de log ainda não criado. Inicie uma geração."
165
 
166
  def update_ui_language(lang_code):
 
 
 
 
167
  lang_map = i18n.get(lang_code, i18n.get('en', {}))
 
168
  return {
 
169
  title_md: gr.update(value=f"# {lang_map.get('app_title')}"),
170
  subtitle_md: gr.update(value=lang_map.get('app_subtitle')),
171
  lang_selector: gr.update(label=lang_map.get('lang_selector_label')),
 
 
172
  step1_accordion: gr.update(label=lang_map.get('step1_accordion')),
173
  prompt_input: gr.update(label=lang_map.get('prompt_label')),
174
  ref_image_input: gr.update(label=lang_map.get('ref_images_label')),
@@ -179,36 +303,68 @@ def update_ui_language(lang_code):
179
  step1_mode_b_info_md: gr.update(value=f"*{lang_map.get('step1_mode_b_info')}*"),
180
  storyboard_output: gr.update(label=lang_map.get('storyboard_output_label')),
181
  keyframe_gallery: gr.update(label=lang_map.get('keyframes_gallery_label')),
182
- step2_accordion: gr.update(label=lang_map.get('step3_accordion')),
183
- step2_description_md: gr.update(value=lang_map.get('step3_description')),
184
- produce_button: gr.update(value=lang_map.get('produce_button')),
185
- final_video_output: gr.update(label=lang_map.get('final_movie_with_audio_label')),
186
- log_accordion: gr.update(label=lang_map.get('log_accordion_label')),
187
- log_display: gr.update(label=lang_map.get('log_display_label')),
188
- update_log_button: gr.update(value=lang_map.get('update_log_button')),
189
- advanced_options_accordion: gr.update(label=lang_map.get('advanced_options_accordion')),
190
- causality_controls_title_md: gr.update(value=f"**{lang_map.get('causality_controls_title')}**"),
191
  trim_percent_slider: gr.update(label=lang_map.get('trim_percent_label'), info=lang_map.get('trim_percent_info')),
192
  forca_guia_slider: gr.update(label=lang_map.get('forca_guia_label'), info=lang_map.get('forca_guia_info')),
193
  convergencia_destino_slider: gr.update(label=lang_map.get('convergencia_final_label'), info=lang_map.get('convergencia_final_info')),
194
- post_production_controls_title_md: gr.update(value=f"**{lang_map.get('post_production_controls_title')}**"),
195
- use_upscaler_checkbox: gr.update(label=lang_map.get('use_upscaler_label')),
196
- use_refiner_checkbox: gr.update(label=lang_map.get('use_refiner_label')),
197
- use_hd_checkbox: gr.update(label=lang_map.get('use_hd_label')),
198
- use_audio_checkbox: gr.update(label=lang_map.get('use_audio_label')),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  }
200
 
201
- # --- 3. DEFINIÇÃO DA UI ---
202
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
 
203
  default_lang = i18n.get('pt', {})
204
-
 
 
 
 
 
 
 
 
205
  title_md = gr.Markdown(f"# {default_lang.get('app_title')}")
206
  subtitle_md = gr.Markdown(default_lang.get('app_subtitle'))
207
-
208
  with gr.Row():
209
  lang_selector = gr.Radio(["pt", "en", "zh"], value="pt", label=default_lang.get('lang_selector_label'))
210
- resolution_selector = gr.Radio(["480x480"], value="480x480", label="Resolução Base")
211
 
 
212
  with gr.Accordion(default_lang.get('step1_accordion'), open=True) as step1_accordion:
213
  prompt_input = gr.Textbox(label=default_lang.get('prompt_label'), value="A majestic lion walks across the savanna, sits down, and then roars at the setting sun.")
214
  ref_image_input = gr.File(label=default_lang.get('ref_images_label'), file_count="multiple", file_types=["image"])
@@ -221,69 +377,129 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
221
  step1_mode_b_info_md = gr.Markdown(f"*{default_lang.get('step1_mode_b_info')}*")
222
  storyboard_output = gr.JSON(label=default_lang.get('storyboard_output_label'))
223
  keyframe_gallery = gr.Gallery(label=default_lang.get('keyframes_gallery_label'), visible=True, object_fit="contain", height="auto", type="filepath")
224
-
225
- with gr.Accordion(default_lang.get('step3_accordion'), open=False, visible=False) as step2_accordion:
226
- step2_description_md = gr.Markdown(default_lang.get('step3_description'))
227
-
228
- with gr.Accordion(default_lang.get('advanced_options_accordion'), open=True) as advanced_options_accordion:
229
- causality_controls_title_md = gr.Markdown(f"**{default_lang.get('causality_controls_title')}**")
230
- trim_percent_slider = gr.Slider(minimum=10, maximum=90, value=50, step=5,
231
- label=default_lang.get('trim_percent_label'),
232
- info=default_lang.get('trim_percent_info'))
233
- with gr.Row():
234
- forca_guia_slider = gr.Slider(label=default_lang.get('forca_guia_label'), minimum=0.0, maximum=1.0, value=0.5, step=0.05, info=default_lang.get('forca_guia_info'))
235
- convergencia_destino_slider = gr.Slider(label=default_lang.get('convergencia_final_label'), minimum=0.0, maximum=1.0, value=0.75, step=0.05, info=default_lang.get('convergencia_final_info'))
236
-
237
- gr.Markdown("---")
238
- post_production_controls_title_md = gr.Markdown(f"**{default_lang.get('post_production_controls_title')}**")
239
- with gr.Row():
240
- use_upscaler_checkbox = gr.Checkbox(label=default_lang.get('use_upscaler_label'), value=False)
241
- use_refiner_checkbox = gr.Checkbox(label=default_lang.get('use_refiner_label'), value=False)
242
- use_hd_checkbox = gr.Checkbox(label=default_lang.get('use_hd_label'), value=False)
243
- use_audio_checkbox = gr.Checkbox(label=default_lang.get('use_audio_label'), value=False)
244
-
245
- produce_button = gr.Button(default_lang.get('produce_button'), variant="primary")
246
-
247
- final_video_output = gr.Video(label=default_lang.get('final_movie_with_audio_label'), visible=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
 
249
  with gr.Accordion(default_lang.get('log_accordion_label'), open=False) as log_accordion:
250
  log_display = gr.Textbox(label=default_lang.get('log_display_label'), lines=20, interactive=False, autoscroll=True)
251
  update_log_button = gr.Button(default_lang.get('update_log_button'))
252
 
253
- # --- 4. CONEXÕES DA UI ---
 
254
  all_ui_components = list(update_ui_language('pt').keys())
255
  lang_selector.change(fn=update_ui_language, inputs=lang_selector, outputs=all_ui_components)
256
-
 
257
  storyboard_and_keyframes_button.click(
258
- fn=run_mode_a_wrapper,
259
- inputs=[prompt_input, num_keyframes_slider, ref_image_input, resolution_selector, duration_per_fragment_slider],
260
- outputs=[storyboard_output, keyframe_gallery, step2_accordion]
261
  )
262
-
263
  storyboard_from_photos_button.click(
264
- fn=run_mode_b_wrapper,
265
  inputs=[prompt_input, num_keyframes_slider, ref_image_input],
266
- outputs=[storyboard_output, keyframe_gallery, step2_accordion]
267
  )
268
-
269
- produce_button.click(
270
- fn=run_video_production_wrapper,
 
271
  inputs=[
272
- keyframe_gallery, prompt_input, duration_per_fragment_slider,
273
  trim_percent_slider, forca_guia_slider, convergencia_destino_slider,
274
- use_upscaler_checkbox, use_refiner_checkbox, use_hd_checkbox, use_audio_checkbox,
275
  resolution_selector
276
  ],
277
- outputs=[final_video_output]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
  )
279
 
 
 
 
 
 
 
 
280
  update_log_button.click(fn=get_log_content, inputs=[], outputs=[log_display])
281
 
282
- # --- 5. INICIALIZAÇÃO DA APLICAÇÃO ---
283
  if __name__ == "__main__":
284
- if os.path.exists(WORKSPACE_DIR):
285
- logger.info(f"Limpando o workspace anterior em: {WORKSPACE_DIR}")
286
  shutil.rmtree(WORKSPACE_DIR)
287
  os.makedirs(WORKSPACE_DIR)
288
- logger.info(f"Aplicação iniciada. Lançando interface Gradio...")
289
  demo.queue().launch()
 
1
+ # app.py
 
2
  #
3
+ # Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
4
  #
5
+ # Version: 2.0.0
6
+ #
7
+ # Contact:
8
  # Carlos Rodrigues dos Santos
9
  # carlex22@gmail.com
10
  #
11
+ # Related Repositories and Projects:
12
  # GitHub: https://github.com/carlex22/Aduc-sdr
13
+ # YouTube (Results): https://m.youtube.com/channel/UC3EgoJi_Fv7yuDpvfYNtoIQ
14
  #
15
+ # This program is free software: you can redistribute it and/or modify
16
+ # it under the terms of the GNU Affero General Public License as published by the
17
+ # Free Software Foundation, either version 3 of the License, or
18
+ # (at your option) any later version.
19
  #
20
+ # This program is distributed in the hope that it will be useful,
21
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
22
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
+ # GNU Affero General Public License for more details.
24
  #
25
+ # You should have received a copy of the GNU Affero General Public License
26
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
27
  #
28
+ # PENDING PATENT NOTICE: The ADUC method and system implemented in this
29
+ # software is in the process of being patented. Please see NOTICE.md for details.
30
+
31
+ """
32
+ This file serves as the main entry point for the ADUC-SDR Gradio user interface.
33
+ It orchestrates the multi-step workflow for AI-driven film creation, from
34
+ pre-production (storyboarding, keyframing) to production (original video rendering)
35
+ and post-production (upscaling, HD mastering, audio generation).
36
+
37
+ The UI is structured using Accordion blocks to guide the user through a logical
38
+ sequence of operations, while `gr.State` components manage the flow of data
39
+ (file paths of generated artifacts) between these independent steps.
40
+ """
41
 
42
  import gradio as gr
43
  import yaml
 
50
 
51
  from aduc_orchestrator import AducOrchestrator
52
 
53
+ # --- 1. CONFIGURATION AND INITIALIZATION ---
54
+ # This section sets up logging, loads internationalization strings, and initializes
55
+ # the core AducOrchestrator which manages all AI specialist models.
56
 
57
  LOG_FILE_PATH = "aduc_log.txt"
58
  if os.path.exists(LOG_FILE_PATH):
 
75
 
76
  logger = logging.getLogger(__name__)
77
 
78
+ # Load translation strings for the UI
79
  i18n = {}
80
  try:
81
  with open("i18n.json", "r", encoding="utf-8") as f:
82
  i18n = json.load(f)
83
  except Exception as e:
84
+ logger.error(f"Error loading i18n.json: {e}")
85
  i18n = {"pt": {}, "en": {}, "zh": {}}
86
 
87
+ # Fallback for missing languages
88
  if 'pt' not in i18n: i18n['pt'] = i18n.get('en', {})
89
  if 'en' not in i18n: i18n['en'] = {}
90
  if 'zh' not in i18n: i18n['zh'] = i18n.get('en', {})
91
 
92
+ # Initialize the main orchestrator from the configuration file
93
  try:
94
  with open("config.yaml", 'r') as f: config = yaml.safe_load(f)
95
  WORKSPACE_DIR = config['application']['workspace_dir']
96
  aduc = AducOrchestrator(workspace_dir=WORKSPACE_DIR)
97
+ logger.info("ADUC Orchestrator and Specialists initialized successfully.")
98
  except Exception as e:
99
+ logger.error(f"CRITICAL ERROR during initialization: {e}", exc_info=True)
100
  exit()
101
 
102
+ # --- 2. UI WRAPPER FUNCTIONS ---
103
+ # These functions act as intermediaries between the Gradio UI components and the
104
+ # AducOrchestrator. They handle input validation, progress tracking, and updating
105
+ # the UI state after each operation.
106
+
107
+ def run_pre_production_wrapper(prompt, num_keyframes, ref_files, resolution_str, duration_per_fragment, progress=gr.Progress()):
108
+ """
109
+ Wrapper for Pre-Production (Steps 1 & 2): Generates storyboard and keyframes.
110
+ This corresponds to the "Art Director Mode".
111
+ """
112
+ if not ref_files:
113
+ raise gr.Error("Please provide at least one reference image.")
114
 
 
 
 
 
115
  ref_paths = [aduc.process_image_for_story(f.name, 480, f"ref_processed_{i}.png") for i, f in enumerate(ref_files)]
116
+
117
+ progress(0.1, desc="Generating storyboard...")
118
  storyboard, initial_ref_path, _ = aduc.task_generate_storyboard(prompt, num_keyframes, ref_paths, progress)
119
+
120
  resolution = int(resolution_str.split('x')[0])
121
 
122
+ # Callback factory to create progress updates for keyframe generation
123
  def cb_factory(scene_index, total_scenes):
124
  start_time = time.time()
125
+ total_steps = 12 # Standard steps for Flux model
126
  def callback(pipe_self, step, timestep, callback_kwargs):
127
  elapsed = time.time() - start_time
128
  current_step = step + 1
 
135
  progress(base_progress + step_progress, desc=desc)
136
  return {}
137
  return callback
138
+
139
  final_keyframes = aduc.task_generate_keyframes(storyboard, initial_ref_path, prompt, resolution, cb_factory)
140
+
141
+ # Make the next step (Production) visible
142
  return gr.update(value=storyboard), gr.update(value=final_keyframes), gr.update(visible=True, open=True)
143
 
144
+ def run_pre_production_photo_wrapper(prompt, num_keyframes, ref_files, progress=gr.Progress()):
145
+ """
146
+ Wrapper for Pre-Production (Steps 1 & 2) in "Photographer Mode".
147
+ Generates a storyboard and selects the best matching keyframes from a user-provided pool.
148
+ """
149
+ if not ref_files or len(ref_files) < 2:
150
+ raise gr.Error("Photographer Mode requires at least 2 images: one base and one for the scene pool.")
151
 
152
  base_ref_paths = [aduc.process_image_for_story(ref_files[0].name, 480, "base_ref_processed_0.png")]
153
  pool_ref_paths = [aduc.process_image_for_story(f.name, 480, f"pool_ref_{i+1}.png") for i, f in enumerate(ref_files[1:])]
154
 
155
+ progress(0.1, desc="Generating storyboard...")
156
  storyboard, _, _ = aduc.task_generate_storyboard(prompt, num_keyframes, base_ref_paths, progress)
157
+
158
+ progress(0.5, desc="AI Photographer is selecting the best scenes...")
159
  selected_keyframes = aduc.task_select_keyframes(storyboard, base_ref_paths, pool_ref_paths)
160
+
161
  return gr.update(value=storyboard), gr.update(value=selected_keyframes), gr.update(visible=True, open=True)
162
 
163
+ def run_original_production_wrapper(keyframes, prompt, duration,
164
+ trim_percent, handler_strength, destination_convergence_strength,
165
+ guidance_scale, stg_scale, inference_steps,
166
+ video_resolution,
167
+ progress=gr.Progress()):
168
+ """
169
+ Wrapper for Step 3: Production. Generates the original master video using LTX.
170
+ Yields UI updates to show progress and final output.
171
+ """
172
  yield {
173
+ original_video_output: gr.update(value=None, visible=True, label="🎬 Producing your original master video... Please wait."),
174
+ final_video_output: gr.update(value=None, visible=True, label="🎬 Production in progress..."),
175
+ step4_accordion: gr.update(visible=False) # Hide post-production until this is done
176
  }
177
+
178
  resolution = int(video_resolution.split('x')[0])
 
179
 
180
+ # The orchestrator now returns the paths to the generated artifacts
181
+ result = aduc.task_produce_original_movie(
182
+ keyframes, prompt, duration,
183
  int(trim_percent), handler_strength, destination_convergence_strength,
184
+ guidance_scale, stg_scale, int(inference_steps),
185
  resolution, use_continuity_director=True, progress=progress
186
+ )
 
 
 
187
 
188
+ original_latents = result["latent_paths"]
189
+ original_video = result["final_path"]
190
+
191
  yield {
192
+ original_video_output: gr.update(value=original_video, label=" Original Master Video"),
193
+ final_video_output: gr.update(value=original_video, label="Final Film (Result of the Last Step)"),
194
+ step4_accordion: gr.update(visible=True, open=True), # Show post-production tools
195
+ # Update state for the next steps
196
+ original_latents_paths_state: original_latents,
197
+ original_video_path_state: original_video,
198
+ current_source_video_state: original_video,
199
+ }
200
+
201
+ def run_upscaler_wrapper(latent_paths, chunk_size, progress=gr.Progress()):
202
+ """
203
+ Wrapper for Post-Production Step 4A: Latent Upscaler.
204
+ """
205
+ if not latent_paths:
206
+ raise gr.Error("Cannot run Upscaler. No original latents found. Please complete Step 3 first.")
207
+
208
+ yield {
209
+ upscaler_video_output: gr.update(value=None, visible=True, label="Upscaling latents and decoding video..."),
210
+ final_video_output: gr.update(label="Post-Production in progress: Latent Upscaling...")
211
+ }
212
+
213
+ upscaled_video_path = aduc.task_run_latent_upscaler(
214
+ latent_paths, int(chunk_size), progress=progress
215
+ )
216
+
217
+ yield {
218
+ upscaler_video_output: gr.update(value=upscaled_video_path, label="✅ Latent Upscale Complete"),
219
+ final_video_output: gr.update(value=upscaled_video_path),
220
+ # Update states for subsequent steps
221
+ upscaled_video_path_state: upscaled_video_path,
222
+ current_source_video_state: upscaled_video_path,
223
+ }
224
+
225
+ def run_hd_wrapper(source_video, model_version, steps, progress=gr.Progress()):
226
+ """
227
+ Wrapper for Post-Production Step 4B: HD Mastering.
228
+ """
229
+ if not source_video:
230
+ raise gr.Error("Cannot run HD Mastering. No source video found. Please complete a previous step first.")
231
+
232
+ yield {
233
+ hd_video_output: gr.update(value=None, visible=True, label="Applying HD mastering... This may take a while."),
234
+ final_video_output: gr.update(label="Post-Production in progress: HD Mastering...")
235
+ }
236
+
237
+ hd_video_path = aduc.task_run_hd_mastering(
238
+ source_video, model_version, int(steps), progress=progress
239
+ )
240
+
241
+ yield {
242
+ hd_video_output: gr.update(value=hd_video_path, label="✅ HD Mastering Complete"),
243
+ final_video_output: gr.update(value=hd_video_path),
244
+ hd_video_path_state: hd_video_path,
245
+ current_source_video_state: hd_video_path,
246
+ }
247
+
248
+ def run_audio_wrapper(source_video, audio_prompt, global_prompt, progress=gr.Progress()):
249
+ """
250
+ Wrapper for Post-Production Step 4C: Audio Generation.
251
+ """
252
+ if not source_video:
253
+ raise gr.Error("Cannot run Audio Generation. No source video found. Please complete a previous step first.")
254
+
255
+ yield {
256
+ audio_video_output: gr.update(value=None, visible=True, label="Generating audio and muxing..."),
257
+ final_video_output: gr.update(label="Post-Production in progress: Audio Generation...")
258
+ }
259
+
260
+ # Use the specific audio prompt if provided, otherwise fall back to the global prompt
261
+ final_audio_prompt = audio_prompt if audio_prompt and audio_prompt.strip() else global_prompt
262
+
263
+ video_with_audio_path = aduc.task_run_audio_generation(
264
+ source_video, final_audio_prompt, progress=progress
265
+ )
266
+
267
+ yield {
268
+ audio_video_output: gr.update(value=video_with_audio_path, label="✅ Audio Generation Complete"),
269
+ final_video_output: gr.update(value=video_with_audio_path),
270
  }
271
 
272
  def get_log_content():
273
+ """
274
+ Reads and returns the content of the log file for display in the UI.
275
+ """
276
  try:
277
  with open(LOG_FILE_PATH, "r", encoding="utf-8") as f:
278
  return f.read()
279
  except FileNotFoundError:
280
+ return "Log file not yet created. Start a generation."
281
 
282
  def update_ui_language(lang_code):
283
+ """
284
+ Updates all text components in the UI to the selected language.
285
+ It fetches the translation map from the `i18n` dictionary.
286
+ """
287
  lang_map = i18n.get(lang_code, i18n.get('en', {}))
288
+ # This dictionary maps each UI component variable to its new value from the language map.
289
  return {
290
+ # General
291
  title_md: gr.update(value=f"# {lang_map.get('app_title')}"),
292
  subtitle_md: gr.update(value=lang_map.get('app_subtitle')),
293
  lang_selector: gr.update(label=lang_map.get('lang_selector_label')),
294
+
295
+ # Step 1: Pre-Production
296
  step1_accordion: gr.update(label=lang_map.get('step1_accordion')),
297
  prompt_input: gr.update(label=lang_map.get('prompt_label')),
298
  ref_image_input: gr.update(label=lang_map.get('ref_images_label')),
 
303
  step1_mode_b_info_md: gr.update(value=f"*{lang_map.get('step1_mode_b_info')}*"),
304
  storyboard_output: gr.update(label=lang_map.get('storyboard_output_label')),
305
  keyframe_gallery: gr.update(label=lang_map.get('keyframes_gallery_label')),
306
+
307
+ # Step 3: Production
308
+ step3_accordion: gr.update(label=lang_map.get('step3_accordion')),
309
+ step3_description_md: gr.update(value=lang_map.get('step3_description')),
310
+ produce_original_button: gr.update(value=lang_map.get('produce_original_button')),
311
+ causality_accordion: gr.update(label=lang_map.get('causality_controls_title')),
 
 
 
312
  trim_percent_slider: gr.update(label=lang_map.get('trim_percent_label'), info=lang_map.get('trim_percent_info')),
313
  forca_guia_slider: gr.update(label=lang_map.get('forca_guia_label'), info=lang_map.get('forca_guia_info')),
314
  convergencia_destino_slider: gr.update(label=lang_map.get('convergencia_final_label'), info=lang_map.get('convergencia_final_info')),
315
+ ltx_pipeline_accordion: gr.update(label=lang_map.get('ltx_pipeline_options')),
316
+ guidance_scale_slider: gr.update(label=lang_map.get('guidance_scale_label'), info=lang_map.get('guidance_scale_info')),
317
+ stg_scale_slider: gr.update(label=lang_map.get('stg_scale_label'), info=lang_map.get('stg_scale_info')),
318
+ inference_steps_slider: gr.update(label=lang_map.get('steps_label'), info=lang_map.get('steps_info')),
319
+
320
+ # Step 4: Post-Production
321
+ step4_accordion: gr.update(label=lang_map.get('step4_accordion')),
322
+ step4_description_md: gr.update(value=lang_map.get('step4_description')),
323
+ sub_step_a_accordion: gr.update(label=lang_map.get('sub_step_a_upscaler')),
324
+ upscaler_description_md: gr.update(value=lang_map.get('upscaler_description')),
325
+ upscaler_options_accordion: gr.update(label=lang_map.get('upscaler_options')),
326
+ upscaler_chunk_size_slider: gr.update(label=lang_map.get('upscaler_chunk_size_label'), info=lang_map.get('upscaler_chunk_size_info')),
327
+ run_upscaler_button: gr.update(value=lang_map.get('run_upscaler_button')),
328
+ sub_step_b_accordion: gr.update(label=lang_map.get('sub_step_b_hd')),
329
+ hd_description_md: gr.update(value=lang_map.get('hd_description')),
330
+ hd_options_accordion: gr.update(label=lang_map.get('hd_options')),
331
+ hd_model_radio: gr.update(label=lang_map.get('hd_model_label')),
332
+ hd_steps_slider: gr.update(label=lang_map.get('hd_steps_label'), info=lang_map.get('hd_steps_info')),
333
+ run_hd_button: gr.update(value=lang_map.get('run_hd_button')),
334
+ sub_step_c_accordion: gr.update(label=lang_map.get('sub_step_c_audio')),
335
+ audio_description_md: gr.update(value=lang_map.get('audio_description')),
336
+ audio_options_accordion: gr.update(label=lang_map.get('audio_options')),
337
+ audio_prompt_input: gr.update(label=lang_map.get('audio_prompt_label'), info=lang_map.get('audio_prompt_info')),
338
+ run_audio_button: gr.update(value=lang_map.get('run_audio_button')),
339
+
340
+ # Final Outputs & Logs
341
+ final_video_output: gr.update(label=lang_map.get('final_video_label')),
342
+ log_accordion: gr.update(label=lang_map.get('log_accordion_label')),
343
+ log_display: gr.update(label=lang_map.get('log_display_label')),
344
+ update_log_button: gr.update(value=lang_map.get('update_log_button')),
345
  }
346
 
347
+ # --- 3. GRADIO UI DEFINITION ---
348
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
349
+ # Initialize UI with default language (Portuguese)
350
  default_lang = i18n.get('pt', {})
351
+
352
+ # State components to manage the pipeline artifacts
353
+ original_latents_paths_state = gr.State(value=None)
354
+ original_video_path_state = gr.State(value=None)
355
+ upscaled_video_path_state = gr.State(value=None)
356
+ hd_video_path_state = gr.State(value=None)
357
+ current_source_video_state = gr.State(value=None) # Tracks the latest video for post-production steps
358
+
359
+ # --- UI Header ---
360
  title_md = gr.Markdown(f"# {default_lang.get('app_title')}")
361
  subtitle_md = gr.Markdown(default_lang.get('app_subtitle'))
362
+
363
  with gr.Row():
364
  lang_selector = gr.Radio(["pt", "en", "zh"], value="pt", label=default_lang.get('lang_selector_label'))
365
+ resolution_selector = gr.Radio(["480x480", "720x720", "960x960"], value="480x480", label="Base Resolution")
366
 
367
+ # --- Step 1 & 2: Pre-Production ---
368
  with gr.Accordion(default_lang.get('step1_accordion'), open=True) as step1_accordion:
369
  prompt_input = gr.Textbox(label=default_lang.get('prompt_label'), value="A majestic lion walks across the savanna, sits down, and then roars at the setting sun.")
370
  ref_image_input = gr.File(label=default_lang.get('ref_images_label'), file_count="multiple", file_types=["image"])
 
377
  step1_mode_b_info_md = gr.Markdown(f"*{default_lang.get('step1_mode_b_info')}*")
378
  storyboard_output = gr.JSON(label=default_lang.get('storyboard_output_label'))
379
  keyframe_gallery = gr.Gallery(label=default_lang.get('keyframes_gallery_label'), visible=True, object_fit="contain", height="auto", type="filepath")
380
+
381
+ # --- Step 3: Production ---
382
+ with gr.Accordion(default_lang.get('step3_accordion'), open=False, visible=False) as step3_accordion:
383
+ step3_description_md = gr.Markdown(default_lang.get('step3_description'))
384
+
385
+ with gr.Accordion(default_lang.get('ltx_advanced_options'), open=False) as ltx_advanced_options_accordion:
386
+ with gr.Accordion(default_lang.get('causality_controls_title'), open=True) as causality_accordion:
387
+ trim_percent_slider = gr.Slider(minimum=10, maximum=90, value=50, step=5, label=default_lang.get('trim_percent_label'), info=default_lang.get('trim_percent_info'))
388
+ with gr.Row():
389
+ forca_guia_slider = gr.Slider(label=default_lang.get('forca_guia_label'), minimum=0.0, maximum=1.0, value=0.5, step=0.05, info=default_lang.get('forca_guia_info'))
390
+ convergencia_destino_slider = gr.Slider(label=default_lang.get('convergencia_final_label'), minimum=0.0, maximum=1.0, value=0.75, step=0.05, info=default_lang.get('convergencia_final_info'))
391
+
392
+ with gr.Accordion(default_lang.get('ltx_pipeline_options'), open=True) as ltx_pipeline_accordion:
393
+ with gr.Row():
394
+ guidance_scale_slider = gr.Slider(minimum=1.0, maximum=10.0, value=2.0, step=0.1, label=default_lang.get('guidance_scale_label'), info=default_lang.get('guidance_scale_info'))
395
+ stg_scale_slider = gr.Slider(minimum=0.0, maximum=1.0, value=0.025, step=0.005, label=default_lang.get('stg_scale_label'), info=default_lang.get('stg_scale_info'))
396
+ inference_steps_slider = gr.Slider(minimum=10, maximum=50, value=20, step=1, label=default_lang.get('steps_label'), info=default_lang.get('steps_info'))
397
+
398
+ produce_original_button = gr.Button(default_lang.get('produce_original_button'), variant="primary")
399
+ original_video_output = gr.Video(label="Original Master Video", visible=False)
400
+
401
+ # --- Step 4: Post-Production ---
402
+ with gr.Accordion(default_lang.get('step4_accordion'), open=False, visible=False) as step4_accordion:
403
+ step4_description_md = gr.Markdown(default_lang.get('step4_description'))
404
+
405
+ # Sub-Step 4A: Latent Upscaler
406
+ with gr.Accordion(default_lang.get('sub_step_a_upscaler'), open=True) as sub_step_a_accordion:
407
+ upscaler_description_md = gr.Markdown(default_lang.get('upscaler_description'))
408
+ with gr.Accordion(default_lang.get('upscaler_options'), open=False) as upscaler_options_accordion:
409
+ upscaler_chunk_size_slider = gr.Slider(minimum=1, maximum=10, value=4, step=1, label=default_lang.get('upscaler_chunk_size_label'), info=default_lang.get('upscaler_chunk_size_info'))
410
+ run_upscaler_button = gr.Button(default_lang.get('run_upscaler_button'), variant="secondary")
411
+ upscaler_video_output = gr.Video(label="Upscaled Video", visible=False)
412
+
413
+ # Sub-Step 4B: HD Mastering
414
+ with gr.Accordion(default_lang.get('sub_step_b_hd'), open=True) as sub_step_b_accordion:
415
+ hd_description_md = gr.Markdown(default_lang.get('hd_description'))
416
+ with gr.Accordion(default_lang.get('hd_options'), open=False) as hd_options_accordion:
417
+ hd_model_radio = gr.Radio(["3B", "7B"], value="3B", label=default_lang.get('hd_model_label'))
418
+ hd_steps_slider = gr.Slider(minimum=20, maximum=150, value=50, step=5, label=default_lang.get('hd_steps_label'), info=default_lang.get('hd_steps_info'))
419
+ run_hd_button = gr.Button(default_lang.get('run_hd_button'), variant="secondary")
420
+ hd_video_output = gr.Video(label="HD Mastered Video", visible=False)
421
+
422
+ # Sub-Step 4C: Audio Generation
423
+ with gr.Accordion(default_lang.get('sub_step_c_audio'), open=True) as sub_step_c_accordion:
424
+ audio_description_md = gr.Markdown(default_lang.get('audio_description'))
425
+ with gr.Accordion(default_lang.get('audio_options'), open=False) as audio_options_accordion:
426
+ audio_prompt_input = gr.Textbox(label=default_lang.get('audio_prompt_label'), info=default_lang.get('audio_prompt_info'), lines=3)
427
+ run_audio_button = gr.Button(default_lang.get('run_audio_button'), variant="secondary")
428
+ audio_video_output = gr.Video(label="Video with Audio", visible=False)
429
+
430
+ # --- Final Output & Logs ---
431
+ final_video_output = gr.Video(label=default_lang.get('final_video_label'), visible=False)
432
 
433
  with gr.Accordion(default_lang.get('log_accordion_label'), open=False) as log_accordion:
434
  log_display = gr.Textbox(label=default_lang.get('log_display_label'), lines=20, interactive=False, autoscroll=True)
435
  update_log_button = gr.Button(default_lang.get('update_log_button'))
436
 
437
+ # --- 4. UI EVENT CONNECTIONS ---
438
+ # Collect all UI components that need language updates
439
  all_ui_components = list(update_ui_language('pt').keys())
440
  lang_selector.change(fn=update_ui_language, inputs=lang_selector, outputs=all_ui_components)
441
+
442
+ # Pre-Production Button Clicks
443
  storyboard_and_keyframes_button.click(
444
+ fn=run_pre_production_wrapper,
445
+ inputs=[prompt_input, num_keyframes_slider, ref_image_input, resolution_selector, duration_per_fragment_slider],
446
+ outputs=[storyboard_output, keyframe_gallery, step3_accordion]
447
  )
448
+
449
  storyboard_from_photos_button.click(
450
+ fn=run_pre_production_photo_wrapper,
451
  inputs=[prompt_input, num_keyframes_slider, ref_image_input],
452
+ outputs=[storyboard_output, keyframe_gallery, step3_accordion]
453
  )
454
+
455
+ # Production Button Click
456
+ produce_original_button.click(
457
+ fn=run_original_production_wrapper,
458
  inputs=[
459
+ keyframe_gallery, prompt_input, duration_per_fragment_slider,
460
  trim_percent_slider, forca_guia_slider, convergencia_destino_slider,
461
+ guidance_scale_slider, stg_scale_slider, inference_steps_slider,
462
  resolution_selector
463
  ],
464
+ outputs=[
465
+ original_video_output, final_video_output, step4_accordion,
466
+ original_latents_paths_state, original_video_path_state, current_source_video_state
467
+ ]
468
+ )
469
+
470
+ # Post-Production Button Clicks
471
+ run_upscaler_button.click(
472
+ fn=run_upscaler_wrapper,
473
+ inputs=[original_latents_paths_state, upscaler_chunk_size_slider],
474
+ outputs=[
475
+ upscaler_video_output, final_video_output,
476
+ upscaled_video_path_state, current_source_video_state
477
+ ]
478
+ )
479
+
480
+ run_hd_button.click(
481
+ fn=run_hd_wrapper,
482
+ inputs=[current_source_video_state, hd_model_radio, hd_steps_slider],
483
+ outputs=[
484
+ hd_video_output, final_video_output,
485
+ hd_video_path_state, current_source_video_state
486
+ ]
487
  )
488
 
489
+ run_audio_button.click(
490
+ fn=run_audio_wrapper,
491
+ inputs=[current_source_video_state, audio_prompt_input, prompt_input],
492
+ outputs=[audio_video_output, final_video_output]
493
+ )
494
+
495
+ # Log Button Click
496
  update_log_button.click(fn=get_log_content, inputs=[], outputs=[log_display])
497
 
498
+ # --- 5. APPLICATION LAUNCH ---
499
  if __name__ == "__main__":
500
+ if os.path.exists(WORKSPACE_DIR):
501
+ logger.info(f"Clearing previous workspace at: {WORKSPACE_DIR}")
502
  shutil.rmtree(WORKSPACE_DIR)
503
  os.makedirs(WORKSPACE_DIR)
504
+ logger.info(f"Application started. Launching Gradio interface...")
505
  demo.queue().launch()