4242)
4343from .prompt_builder import build_audio_prompt , build_prompt
4444from .runner import merge_subprocess_output , resolve_binary , run_cmd
45- from .templates import GAME_YAML , MANIFEST_CSV
45+ from .templates import GAME_YAML , MANIFEST_YAML
4646
4747console = Console ()
4848
@@ -167,6 +167,46 @@ def _append_text2sound_profile_args(ts: Text2SoundProfile, argv: list[str]) -> N
167167 argv .append ("--no-half" )
168168
169169
170+ def _text2sound_args_for_row (
171+ profile : Text2SoundProfile ,
172+ row : ManifestRow ,
173+ argv : list [str ],
174+ ) -> None :
175+ """Append text2sound args: per-row overrides take precedence over global profile."""
176+ duration = row .audio_duration if row .audio_duration is not None else profile .duration
177+ steps = row .audio_steps if row .audio_steps is not None else profile .steps
178+ cfg = row .audio_cfg_scale if row .audio_cfg_scale is not None else profile .cfg_scale
179+ trim = row .audio_trim if row .audio_trim is not None else profile .trim
180+ preset = row .audio_preset if row .audio_preset is not None else profile .preset
181+
182+ if duration is not None :
183+ argv .extend (["-d" , str (duration )])
184+ if steps is not None :
185+ argv .extend (["-s" , str (steps )])
186+ if cfg is not None :
187+ argv .extend (["-c" , str (cfg )])
188+ fmt = (profile .audio_format or "wav" ).lower ().strip ().lstrip ("." )
189+ argv .extend (["-f" , fmt ])
190+ if preset and preset .lower () != "none" :
191+ argv .extend (["-p" , preset ])
192+ if profile .sigma_min is not None :
193+ argv .extend (["--sigma-min" , str (profile .sigma_min )])
194+ if profile .sigma_max is not None :
195+ argv .extend (["--sigma-max" , str (profile .sigma_max )])
196+ if profile .sampler :
197+ argv .extend (["--sampler" , profile .sampler ])
198+ if trim is not None :
199+ argv .append ("--trim" if trim else "--no-trim" )
200+ if row .audio_profile :
201+ argv .extend (["--profile" , row .audio_profile ])
202+ elif profile .model_id :
203+ argv .extend (["-m" , profile .model_id ])
204+ if profile .half_precision is True :
205+ argv .append ("--half" )
206+ elif profile .half_precision is False :
207+ argv .append ("--no-half" )
208+
209+
170210def _texture2d_profile_effective (profile : GameProfile ) -> Texture2DProfile :
171211 """Opções Texture2D do perfil ou defaults (para linhas CSV texture2d sem bloco no YAML)."""
172212 return profile .texture2d or Texture2DProfile ()
@@ -770,8 +810,11 @@ def _build_context(
770810 manifest_path : Path ,
771811 presets_local : Path | None ,
772812) -> tuple [GameProfile , list [ManifestRow ], dict [str , Any ], dict [str , Any ]]:
813+ resolved = _resolve_manifest_path (manifest_path )
814+ if not resolved .is_file ():
815+ raise click .ClickException (f"Manifest não encontrado: { manifest_path } (tentado { resolved } )" )
773816 profile = load_profile (profile_path )
774- rows = load_manifest (manifest_path )
817+ rows = load_manifest (resolved )
775818 bundle = load_presets_bundle (presets_local )
776819 preset = get_preset (bundle , profile .style_preset )
777820 return profile , rows , bundle , preset
@@ -871,30 +914,42 @@ def mesh_reorigin_feet_cmd(path: Path, recursive: bool, dry_run: bool, excludes:
871914 console .print (Panel (f"[bold green]{ ok } [/bold green] GLB(s) actualizados." , border_style = "green" ))
872915
873916
917+ def _resolve_manifest_path (raw : str | Path ) -> Path :
918+ """Resolve manifest path: if no extension, try .yaml, .yml, then .csv."""
919+ p = Path (raw )
920+ if p .suffix .lower () in (".csv" , ".yaml" , ".yml" ):
921+ return p
922+ for ext in (".yaml" , ".yml" , ".csv" ):
923+ candidate = p .with_suffix (ext )
924+ if candidate .is_file ():
925+ return candidate
926+ return p .with_suffix (".yaml" )
927+
928+
874929@main .command ("init" )
875930@click .option (
876931 "--path" ,
877932 "target_dir" ,
878933 type = click .Path (file_okay = False , writable = True , path_type = Path ),
879934 default = "." ,
880- help = "Diretório onde criar game.yaml e manifest.csv " ,
935+ help = "Diretório onde criar game.yaml e manifest.yaml " ,
881936)
882937@click .option ("--force" , is_flag = True , help = "Sobrescrever ficheiros existentes" )
883938def init_cmd (target_dir : Path , force : bool ) -> None :
884- """Cria game.yaml e manifest.csv de exemplo."""
939+ """Cria game.yaml e manifest.yaml de exemplo."""
885940 target_dir = target_dir .resolve ()
886941 target_dir .mkdir (parents = True , exist_ok = True )
887942 gy = target_dir / "game.yaml"
888- mc = target_dir / "manifest.csv "
943+ my = target_dir / "manifest.yaml "
889944 if gy .exists () and not force :
890945 raise click .ClickException (f"Já existe { gy } (usa --force para sobrescrever)" )
891- if mc .exists () and not force :
892- raise click .ClickException (f"Já existe { mc } (usa --force para sobrescrever)" )
946+ if my .exists () and not force :
947+ raise click .ClickException (f"Já existe { my } (usa --force para sobrescrever)" )
893948 gy .write_text (GAME_YAML , encoding = "utf-8" )
894- mc .write_text (MANIFEST_CSV , encoding = "utf-8" )
949+ my .write_text (MANIFEST_YAML , encoding = "utf-8" )
895950 console .print (
896951 Panel (
897- f"Criados [bold cyan]{ gy } [/bold cyan] e [bold cyan]{ mc } [/bold cyan].\n \n "
952+ f"Criados [bold cyan]{ gy } [/bold cyan] e [bold cyan]{ my } [/bold cyan].\n \n "
898953 "Seguinte: edita o perfil, preenche o manifest, depois "
899954 "[bold]gameassets prompts[/bold] ou [bold]gameassets batch[/bold]." ,
900955 title = "[bold green]init[/bold green]" ,
@@ -958,9 +1013,9 @@ def row(name: str, env: str, exe: str) -> None:
9581013@click .option (
9591014 "--manifest" ,
9601015 "manifest_path" ,
961- type = click .Path (exists = True , dir_okay = False , path_type = Path ),
962- default = "manifest.csv " ,
963- help = "CSV com id, idea e colunas opcionais" ,
1016+ type = click .Path (dir_okay = False , path_type = Path ),
1017+ default = "manifest" ,
1018+ help = "CSV/YAML com id, idea e colunas opcionais" ,
9641019)
9651020@click .option (
9661021 "--presets-local" ,
@@ -1052,8 +1107,8 @@ def prompts_cmd(
10521107@click .option (
10531108 "--manifest" ,
10541109 "manifest_path" ,
1055- type = click .Path (exists = True , dir_okay = False , path_type = Path ),
1056- default = "manifest.csv " ,
1110+ type = click .Path (dir_okay = False , path_type = Path ),
1111+ default = "manifest" ,
10571112)
10581113@click .option (
10591114 "--presets-local" ,
@@ -1117,9 +1172,13 @@ def handoff_cmd(
11171172 """Copia GLB/áudio do ``output_dir`` do perfil para ``public/assets`` e grava ``gameassets_handoff.json``."""
11181173 from .handoff_export import handoff_command_impl
11191174
1175+ resolved = _resolve_manifest_path (manifest_path )
1176+ if not resolved .is_file ():
1177+ raise click .ClickException (f"Manifest não encontrado: { manifest_path } (tentado { resolved } )" )
1178+
11201179 handoff_command_impl (
11211180 profile_path ,
1122- manifest_path ,
1181+ resolved ,
11231182 presets_local ,
11241183 public_dir ,
11251184 copy = use_copy ,
@@ -1141,8 +1200,8 @@ def handoff_cmd(
11411200@click .option (
11421201 "--manifest" ,
11431202 "manifest_path" ,
1144- type = click .Path (exists = True , dir_okay = False , path_type = Path ),
1145- default = "manifest.csv " ,
1203+ type = click .Path (dir_okay = False , path_type = Path ),
1204+ default = "manifest" ,
11461205)
11471206@click .option (
11481207 "--presets-local" ,
@@ -1268,6 +1327,7 @@ def batch_cmd(
12681327) -> None :
12691328 """Gera imagens (e opcionalmente meshes) para cada linha do manifest."""
12701329 profile , rows , _bundle , preset = _build_context (profile_path , manifest_path , presets_local )
1330+ manifest_path = _resolve_manifest_path (manifest_path )
12711331
12721332 has_rigging_profile = profile .rigging3d is not None
12731333 has_parts_profile = profile .part3d is not None
@@ -1550,7 +1610,7 @@ def append_log(rec: dict[str, Any]) -> None:
15501610 seed_a = _seed_for_row (profile , f"{ row .id } :audio" )
15511611 if seed_a is not None :
15521612 argv_au .extend (["--seed" , str (seed_a )])
1553- _append_text2sound_profile_args (ts_line , argv_au )
1613+ _text2sound_args_for_row (ts_line , row , argv_au )
15541614 if profile .text3d and profile .text3d .low_vram :
15551615 argv_au .append ("--low-vram" )
15561616 _dry_run_emit (dry_plan , phase = p1_title + " text2sound" , row_id = row .id , argv = argv_au )
@@ -1577,7 +1637,7 @@ def append_log(rec: dict[str, Any]) -> None:
15771637 seed_a = _seed_for_row (profile , f"{ row .id } :audio" )
15781638 if seed_a is not None :
15791639 argv_au .extend (["--seed" , str (seed_a )])
1580- _append_text2sound_profile_args (ts_line , argv_au )
1640+ _text2sound_args_for_row (ts_line , row , argv_au )
15811641 if profile .text3d and profile .text3d .low_vram :
15821642 argv_au .append ("--low-vram" )
15831643 _dry_run_emit (
@@ -2035,7 +2095,7 @@ def append_log(rec: dict[str, Any]) -> None:
20352095 seed_a = _seed_for_row (profile , f"{ row .id } :audio" )
20362096 if seed_a is not None :
20372097 argv_au .extend (["--seed" , str (seed_a )])
2038- _append_text2sound_profile_args (ts_line , argv_au )
2098+ _text2sound_args_for_row (ts_line , row , argv_au )
20392099 if profile .text3d and profile .text3d .low_vram :
20402100 argv_au .append ("--low-vram" )
20412101 t_au = time .perf_counter ()
@@ -2592,8 +2652,8 @@ def _text3d_argv(
25922652@click .option (
25932653 "--manifest" ,
25942654 "manifest_path" ,
2595- type = click .Path (exists = True , dir_okay = False , path_type = Path ),
2596- default = "manifest.csv " ,
2655+ type = click .Path (dir_okay = False , path_type = Path ),
2656+ default = "manifest" ,
25972657)
25982658@click .option (
25992659 "--presets-local" ,
@@ -2648,6 +2708,7 @@ def resume_cmd(
26482708 raise click .ClickException ("--gpu-ids deve ser lista separada por vírgulas (ex.: '0,1')" ) from _err
26492709
26502710 profile , rows , _bundle , preset = _build_context (profile_path , manifest_path , presets_local )
2711+ manifest_path = _resolve_manifest_path (manifest_path )
26512712 manifest_dir = manifest_path .resolve ().parent
26522713 t3_opts = profile .text3d
26532714
0 commit comments