@dsh-gis/dsh-video-studio-skill 0.7.2 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1109 -0
- package/README.md +76 -10
- package/THIRD_PARTY_NOTICES.md +1 -0
- package/docs/styles/sheet-8-styles.png +0 -0
- package/docs/styles/sheet-before-after.png +0 -0
- package/index.js +4 -1
- package/package.json +7 -3
- package/schemas/film.schema.json +329 -2
- package/skills/video-studio/SKILL.md +270 -13
- package/skills/video-studio/assets/golden_frames.json +65 -0
- package/skills/video-studio/assets/scenes.py +2162 -1190
- package/skills/video-studio/assets/web/engine.js +63 -2
- package/skills/video-studio/assets/web/film.js +224 -101
- package/skills/video-studio/assets/web/index.html +4 -2
- package/skills/video-studio/assets/web/qa_text.js +36 -8
- package/skills/video-studio/references/audio-variation.md +4 -3
- package/skills/video-studio/references/audio.md +2 -2
- package/skills/video-studio/references/change-impact.md +1 -1
- package/skills/video-studio/references/quality-gates.md +25 -3
- package/skills/video-studio/references/scene-grammar.md +306 -0
- package/skills/video-studio/references/style-packs.md +108 -3
- package/skills/video-studio/references/v08-quality.md +256 -0
- package/skills/video-studio/references/web-engine.md +19 -2
- package/skills/video-studio/references/word-timeline.md +147 -0
- package/skills/video-studio/requirements.txt +14 -0
- package/skills/video-studio/scripts/filmcheck.py +218 -0
- package/skills/video-studio/scripts/filmkit.py +521 -38
- package/skills/video-studio/scripts/gates.py +88 -4
- package/skills/video-studio/scripts/grammar.py +559 -0
- package/skills/video-studio/scripts/preflight.py +774 -0
- package/skills/video-studio/scripts/qa_frames.py +49 -8
- package/skills/video-studio/scripts/qaasr.py +1563 -0
- package/skills/video-studio/scripts/qavmaf.py +1442 -0
- package/skills/video-studio/scripts/render.py +764 -32
- package/skills/video-studio/scripts/scorekit.py +16 -30
- package/skills/video-studio/scripts/selfcheck.py +483 -0
- package/skills/video-studio/scripts/sfxkit.py +130 -14
- package/skills/video-studio/scripts/stagewatch.py +19 -6
- package/skills/video-studio/scripts/stylekit.py +242 -12
- package/skills/video-studio/scripts/voicekit.py +15 -7
- package/skills/video-studio/scripts/webengine.py +1119 -72
- package/tools/ci_check.py +173 -31
- package/tools/estimate.py +130 -19
- package/tools/golden_frames.py +664 -0
- package/tools/silhouette_qa.py +910 -0
|
@@ -773,11 +773,19 @@ def _cue_anchor(cue) -> float | None:
|
|
|
773
773
|
return None
|
|
774
774
|
|
|
775
775
|
|
|
776
|
-
def _sync_coverage(cues, events) -> dict:
|
|
776
|
+
def _sync_coverage(cues, events, tol=SYNC_TOL) -> dict:
|
|
777
777
|
"""统计"音画同步到底查了多少条":返回 {"cues","anchors","same","judged","src_skipped"}。
|
|
778
778
|
|
|
779
|
-
judged =
|
|
780
|
-
|
|
779
|
+
judged = **按命名约定真的判过 dt** 的 cue 数 = 自带视觉锚点的 cue + 同 kind 且落在
|
|
780
|
+
SYNC_SAME_WIN 搜索窗内、src 同基准的 cue。这是防"静默全过"的量化指标:
|
|
781
|
+
judged == 0 而 cues > 0 时,sync 为空没有意义。
|
|
782
|
+
⚠ 不能只数"存在同名 kind 的 cue":同名事件若都离 cue 超过 SYNC_SAME_WIN 的搜索窗,
|
|
783
|
+
sync_check 一条也不会判,judged 却会记成"查过了"——require_sync 的"没查"守卫
|
|
784
|
+
就被这个虚高计数骗过去(实测 cues=[impact@0.5] / events=[impact@100]
|
|
785
|
+
-> judged=1、errors=[],门禁根本没判)。所以这里用与 sync_check 同一个 _match_cue 判。
|
|
786
|
+
⚠ 也**不**把"异 kind 就近确认"(mode="near")算进 judged:它只在 |dt|<=tol 时认可,
|
|
787
|
+
是运气不是约定;把它算成"判过"会让命名约定失效的场景显示成"已判 1 条"。
|
|
788
|
+
另外记 judged_any(含异 kind 佐证)只作诊断,不进报告口径。
|
|
781
789
|
src_skipped = 因为"cue 与事件时间基准不同"(src 不同)而无法参与对照的 cue 数:
|
|
782
790
|
web 引擎三路合并(film.json/scenes.py 用 PIL 时间轴、页面事件用页面时间轴)时会有,
|
|
783
791
|
这种情况不能算"同步失败",但也不能静默跳过 —— 由 _load_cues 转成 warning 点名。
|
|
@@ -786,18 +794,29 @@ def _sync_coverage(cues, events) -> dict:
|
|
|
786
794
|
events = [e for e in (events or []) if isinstance(e, dict)]
|
|
787
795
|
anchors = sum(1 for c in cues if _cue_anchor(c) is not None)
|
|
788
796
|
same = 0
|
|
797
|
+
judged = 0
|
|
798
|
+
judged_any = 0
|
|
789
799
|
src_skipped = 0
|
|
790
800
|
for c in cues:
|
|
791
801
|
if _cue_anchor(c) is not None:
|
|
802
|
+
judged += 1
|
|
803
|
+
judged_any += 1
|
|
792
804
|
continue
|
|
793
805
|
compat = _compatible_events(c, events)
|
|
794
806
|
if not compat and events:
|
|
795
807
|
src_skipped += 1
|
|
796
808
|
continue
|
|
797
|
-
|
|
809
|
+
typ = str(c.get("type") or "").strip().lower()
|
|
810
|
+
if typ in {_event_kind(e) for e in compat}:
|
|
798
811
|
same += 1
|
|
812
|
+
matched = _match_cue(c, compat, tol)[1] is not None
|
|
813
|
+
if matched:
|
|
814
|
+
judged_any += 1
|
|
815
|
+
# 只有"同名 kind 配对成功"才算按约定判过(锚点上面已单独计入)
|
|
816
|
+
if matched and any(_event_kind(e) == typ for e in compat):
|
|
817
|
+
judged += 1
|
|
799
818
|
return {"cues": len(cues), "anchors": anchors, "same": same,
|
|
800
|
-
"judged":
|
|
819
|
+
"judged": judged, "judged_any": judged_any, "src_skipped": src_skipped}
|
|
801
820
|
|
|
802
821
|
|
|
803
822
|
def _load_cues(cues_json):
|
|
@@ -892,18 +911,40 @@ def _load_cues(cues_json):
|
|
|
892
911
|
warns.append(msg)
|
|
893
912
|
info["blockers"].append(msg)
|
|
894
913
|
elif cues and events:
|
|
895
|
-
#
|
|
896
|
-
#
|
|
897
|
-
#
|
|
914
|
+
# 门禁静默失效的最后两道保险,分别覆盖两种"零超差但一条没查":
|
|
915
|
+
# ① 命名约定失效:cue.type 与任何事件的 kind 都不同名(cov["same"] == 0);
|
|
916
|
+
# ② 同名事件全落在 SYNC_SAME_WIN 搜索窗之外,一条 dt 都算不出来(cov["judged"] == 0)。
|
|
917
|
+
# 两者都会让 sync_check 返回空列表——看起来干净,其实一条都没在查。
|
|
918
|
+
# 判据用 cov["judged"](按约定真的配对成功)而不是 cov["same"](只是同名)。
|
|
898
919
|
cue_types = {str(c.get("type") or "").strip().lower() for c in cues}
|
|
899
920
|
ev_kinds = {_event_kind(e) for e in events}
|
|
900
921
|
if cov["same"] == 0 and cov["anchors"] == 0:
|
|
922
|
+
# 命名约定失效:cue.type 与任何事件的 kind 都不同名。这时 sync_check 可能靠
|
|
923
|
+
# 异 kind 的"就近确认"蒙到几条(dt 恰好在容差内),但**没有任何一条是照约定
|
|
924
|
+
# 配出来的**——对齐了是巧合,错位了也不会报。所以这条与 judged 无关,永远报。
|
|
901
925
|
msg = ("音画同步检查可能没生效:%d 个 cue 的 type 与 %d 个事件的 kind 没有任何可配对的同名项"
|
|
902
926
|
"(同 kind 配对要求 events.kind == cue 的配方名,且双方 src 需属同一时间基准)。"
|
|
903
927
|
"sync 为空不代表同步正确——请按 references/frame-qa.md §2.5 的命名约定登记事件,"
|
|
904
928
|
"或把某个 cue 故意改错 0.3 s 重跑做反证。" % (len(cue_types), len(ev_kinds)))
|
|
905
929
|
warns.append(msg)
|
|
906
930
|
info["blockers"].append(msg)
|
|
931
|
+
elif cov["judged"] == 0 and cov.get("judged_any", 0) == 0:
|
|
932
|
+
# 同名 kind 有,但一条 dt 都算不出来:事件全落在 SYNC_SAME_WIN 搜索窗之外,
|
|
933
|
+
# 或全被 src 过滤掉了。sync_check 同样"零超差"通过——必须让人看见。
|
|
934
|
+
msg = ("音画同步检查可能没生效:%d 条 cue 里没有一条能判定 dt"
|
|
935
|
+
"(同名事件都不在 %.1f s 搜索窗内,或 src 时间基准不同)。"
|
|
936
|
+
"sync 为空不代表同步正确——请核对 events 的时间基准与时刻,"
|
|
937
|
+
"或给 cue 写数值 visual_at。" % (len(cues), SYNC_SAME_WIN))
|
|
938
|
+
warns.append(msg)
|
|
939
|
+
info["blockers"].append(msg)
|
|
940
|
+
else:
|
|
941
|
+
# 「有 cues.json 但里面既没有 cues 也没有 events」:文件解析成功、对照却无从做起。
|
|
942
|
+
# 不记 blocker 的话 require_sync 也拦不住:report() 里把"没查"升级成 error 的条件
|
|
943
|
+
# 要求 cues 非空,而这里 cues 为空 —— 于是空 cues.json 会静默通过整道同步门禁。
|
|
944
|
+
msg = ("cues.json 里既没有 cues 也没有 events:音画同步对照没有执行"
|
|
945
|
+
"(文件是空对象、只写了 version 字段?按 §4.2 补 cues / events)")
|
|
946
|
+
warns.append(msg)
|
|
947
|
+
info["blockers"].append(msg)
|
|
907
948
|
return cues, events, warns, info
|
|
908
949
|
|
|
909
950
|
|