Skip to content

Commit 4808248

Browse files
feat: HuggingFace model mirror, LaMa cache fix, and mask points chaining
Mirror AI models to AEmotionStudio HuggingFace repos (MMAudio, Whisper, LaMa). Mirror-first download with upstream fallback. Fix LaMa torch.hub cache detection. Add mask_points chaining through SaveVideo/LoadVideoPath nodes. LoadImagePath node styling.
1 parent f5a54a2 commit 4808248

6 files changed

Lines changed: 66 additions & 22 deletions

File tree

core/mmaudio_synthesizer.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -669,7 +669,6 @@ def _generate_chunk(
669669
start_sec=0.0,
670670
):
671671
"""Generate audio for a single chunk (≤ 8s)."""
672-
import torch
673672

674673
if video_path is not None:
675674
video_info = load_video_fn(Path(video_path), duration)
@@ -714,7 +713,6 @@ def _generate_chunked(
714713
generate_fn,
715714
):
716715
"""Generate audio for a long video by chunking into segments."""
717-
import torch
718716

719717
sampling_rate = seq_cfg.sampling_rate
720718
step = _CHUNK_DURATION - _OVERLAP_SECS

js/ffmpega_ui.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1532,7 +1532,11 @@ app.registerExtension({
15321532
if (!Object.prototype.hasOwnProperty.call(uploadBtn, '_originalBorder')) {
15331533
uploadBtn._originalBorder = uploadBtn.style.border;
15341534
}
1535+
if (!Object.prototype.hasOwnProperty.call(uploadBtn, '_originalAriaLabel')) {
1536+
uploadBtn._originalAriaLabel = uploadBtn.getAttribute("aria-label");
1537+
}
15351538
uploadBtn.textContent = "📂 Drop to Upload";
1539+
uploadBtn.setAttribute("aria-label", "Drop to Upload");
15361540
uploadBtn.style.border = "1px dashed #4a6a8a";
15371541
uploadBtn.style.backgroundColor = "#333";
15381542

@@ -1547,6 +1551,14 @@ app.registerExtension({
15471551
uploadBtn.style.border = uploadBtn._originalBorder;
15481552
delete uploadBtn._originalBorder;
15491553
}
1554+
if (Object.prototype.hasOwnProperty.call(uploadBtn, '_originalAriaLabel')) {
1555+
if (uploadBtn._originalAriaLabel) {
1556+
uploadBtn.setAttribute("aria-label", uploadBtn._originalAriaLabel);
1557+
} else {
1558+
uploadBtn.removeAttribute("aria-label");
1559+
}
1560+
delete uploadBtn._originalAriaLabel;
1561+
}
15501562
updateUploadBtn();
15511563
}
15521564
}, 100);
@@ -2122,8 +2134,12 @@ app.registerExtension({
21222134
if (!Object.prototype.hasOwnProperty.call(uploadBtn, '_originalBorder')) {
21232135
uploadBtn._originalBorder = uploadBtn.style.border;
21242136
}
2137+
if (!Object.prototype.hasOwnProperty.call(uploadBtn, '_originalAriaLabel')) {
2138+
uploadBtn._originalAriaLabel = uploadBtn.getAttribute("aria-label");
2139+
}
21252140

21262141
uploadBtn.textContent = "📂 Drop to Upload";
2142+
uploadBtn.setAttribute("aria-label", "Drop to Upload");
21272143
uploadBtn.style.border = "1px dashed #4a6a8a";
21282144
uploadBtn.style.backgroundColor = "#333";
21292145

@@ -2139,6 +2155,14 @@ app.registerExtension({
21392155
uploadBtn.style.border = uploadBtn._originalBorder;
21402156
delete uploadBtn._originalBorder;
21412157
}
2158+
if (Object.prototype.hasOwnProperty.call(uploadBtn, '_originalAriaLabel')) {
2159+
if (uploadBtn._originalAriaLabel) {
2160+
uploadBtn.setAttribute("aria-label", uploadBtn._originalAriaLabel);
2161+
} else {
2162+
uploadBtn.removeAttribute("aria-label");
2163+
}
2164+
delete uploadBtn._originalAriaLabel;
2165+
}
21422166
updateBtn();
21432167
}
21442168
}, 100);

skills/composer.py

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -975,16 +975,30 @@ def _skill_to_filters(
975975
# If skill has a template, use it
976976
if skill.ffmpeg_template:
977977
template = skill.ffmpeg_template
978-
for key, value in params.items():
979-
val_str = str(value)
980-
if isinstance(value, str):
981-
val_str = sanitize_text_param(val_str)
982-
template = template.replace(f"{{{key}}}", val_str)
983978

984-
# Resolve any remaining {placeholder} with skill defaults
985-
for sp in (skill.parameters or []):
986-
if sp.default is not None:
987-
template = template.replace(f"{{{sp.name}}}", str(sp.default))
979+
# Optimization: Skip replacements entirely if there are no placeholders
980+
if "{" in template:
981+
for key, value in params.items():
982+
val_str = str(value)
983+
if isinstance(value, str):
984+
val_str = sanitize_text_param(val_str)
985+
986+
# Unconditional replace is fast here because user params are few
987+
# and likely present in the template.
988+
template = template.replace(f"{{{key}}}", val_str)
989+
990+
# Resolve any remaining {placeholder} with skill defaults
991+
if "{" in template:
992+
for sp in (skill.parameters or []):
993+
if sp.default is not None:
994+
# Optimization: "Check First" pattern. Skills often have
995+
# many default parameters that aren't in the template.
996+
placeholder = f"{{{sp.name}}}"
997+
if placeholder in template:
998+
template = template.replace(placeholder, str(sp.default))
999+
# Optimization: Early break if all placeholders are resolved
1000+
if "{" not in template:
1001+
break
9881002

9891003
# Determine if it's a video filter, audio filter, or output option
9901004
if template.startswith("-"):
@@ -1005,14 +1019,23 @@ def _skill_to_filters(
10051019
_defaults[sp.name] = str(sp.default)
10061020

10071021
for step_str in skill.pipeline:
1008-
# Substitute {placeholder} values from parent params
1009-
for key, value in params.items():
1010-
step_str = step_str.replace(f"{{{key}}}", str(value))
1011-
1012-
# Resolve any remaining {placeholder} with skill defaults
1013-
# to prevent literal "{ratio}" from reaching handlers.
1014-
for key, default_val in _defaults.items():
1015-
step_str = step_str.replace(f"{{{key}}}", default_val)
1022+
# Optimization: Skip replacements entirely if there are no placeholders
1023+
if "{" in step_str:
1024+
# Substitute {placeholder} values from parent params
1025+
for key, value in params.items():
1026+
step_str = step_str.replace(f"{{{key}}}", str(value))
1027+
1028+
# Resolve any remaining {placeholder} with skill defaults
1029+
# to prevent literal "{ratio}" from reaching handlers.
1030+
if "{" in step_str:
1031+
for key, default_val in _defaults.items():
1032+
# Optimization: "Check First" pattern
1033+
placeholder = f"{{{key}}}"
1034+
if placeholder in step_str:
1035+
step_str = step_str.replace(placeholder, default_val)
1036+
# Optimization: Early break if all placeholders are resolved
1037+
if "{" not in step_str:
1038+
break
10161039

10171040
# Parse step string (format: "skill_name:param1=val1,param2=val2")
10181041
if ":" in step_str:

skills/handlers/generate_audio.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def _f_generate_audio(p):
114114
f"[0:v]null[_vpass]"
115115
)
116116
if mode == "mix" and p.get("_has_embedded_audio"):
117-
fc += f";[0:a][_gen_audio]amix=inputs=2:duration=shortest[_aout]"
117+
fc += ";[0:a][_gen_audio]amix=inputs=2:duration=shortest[_aout]"
118118
return _mr(
119119
fc=fc,
120120
opts=["-map", "[_vpass]", "-map", "[_aout]"],

tests/test_generate_audio.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"""
66

77
import pytest
8-
from unittest.mock import patch, MagicMock
8+
from unittest.mock import patch
99

1010
from skills.handler_contract import HandlerResult
1111
from skills.registry import get_registry, SkillCategory

tests/test_node_chaining.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import sys
1010
import types
1111

12-
import pytest
1312

1413
# Ensure conftest.py has run for mocking, then add additional mocks
1514
# that these specific node imports need.

0 commit comments

Comments
 (0)