@kenkaiiii/gg-editor 0.6.7 → 0.7.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/dist/cli.js +64 -1
- package/dist/cli.js.map +1 -1
- package/dist/core/audio-mix.d.ts.map +1 -1
- package/dist/core/audio-mix.js +8 -1
- package/dist/core/audio-mix.js.map +1 -1
- package/dist/core/audio-mix.test.js +1 -1
- package/dist/core/audio-mix.test.js.map +1 -1
- package/dist/core/auth/api-keys.d.ts +1 -1
- package/dist/core/auth/api-keys.d.ts.map +1 -1
- package/dist/core/auth/api-keys.js +2 -1
- package/dist/core/auth/api-keys.js.map +1 -1
- package/dist/core/auth/index.d.ts +18 -5
- package/dist/core/auth/index.d.ts.map +1 -1
- package/dist/core/auth/index.js +16 -4
- package/dist/core/auth/index.js.map +1 -1
- package/dist/core/auth/login.d.ts.map +1 -1
- package/dist/core/auth/login.js +4 -31
- package/dist/core/auth/login.js.map +1 -1
- package/dist/core/beats.d.ts +59 -0
- package/dist/core/beats.d.ts.map +1 -0
- package/dist/core/beats.js +122 -0
- package/dist/core/beats.js.map +1 -0
- package/dist/core/beats.test.d.ts +2 -0
- package/dist/core/beats.test.d.ts.map +1 -0
- package/dist/core/beats.test.js +86 -0
- package/dist/core/beats.test.js.map +1 -0
- package/dist/core/brand-kit.d.ts +80 -0
- package/dist/core/brand-kit.d.ts.map +1 -0
- package/dist/core/brand-kit.js +96 -0
- package/dist/core/brand-kit.js.map +1 -0
- package/dist/core/brand-kit.test.d.ts +2 -0
- package/dist/core/brand-kit.test.d.ts.map +1 -0
- package/dist/core/brand-kit.test.js +76 -0
- package/dist/core/brand-kit.test.js.map +1 -0
- package/dist/core/bundled-sfx.d.ts +64 -0
- package/dist/core/bundled-sfx.d.ts.map +1 -0
- package/dist/core/bundled-sfx.js +218 -0
- package/dist/core/bundled-sfx.js.map +1 -0
- package/dist/core/bundled-sfx.test.d.ts +2 -0
- package/dist/core/bundled-sfx.test.d.ts.map +1 -0
- package/dist/core/bundled-sfx.test.js +81 -0
- package/dist/core/bundled-sfx.test.js.map +1 -0
- package/dist/core/child-abort.d.ts +57 -0
- package/dist/core/child-abort.d.ts.map +1 -0
- package/dist/core/child-abort.js +95 -0
- package/dist/core/child-abort.js.map +1 -0
- package/dist/core/child-abort.test.d.ts +2 -0
- package/dist/core/child-abort.test.d.ts.map +1 -0
- package/dist/core/child-abort.test.js +88 -0
- package/dist/core/child-abort.test.js.map +1 -0
- package/dist/core/clip-scoring.d.ts +44 -0
- package/dist/core/clip-scoring.d.ts.map +1 -0
- package/dist/core/clip-scoring.js +165 -0
- package/dist/core/clip-scoring.js.map +1 -0
- package/dist/core/clip-scoring.test.d.ts +2 -0
- package/dist/core/clip-scoring.test.d.ts.map +1 -0
- package/dist/core/clip-scoring.test.js +113 -0
- package/dist/core/clip-scoring.test.js.map +1 -0
- package/dist/core/emoji-captions.d.ts +45 -0
- package/dist/core/emoji-captions.d.ts.map +1 -0
- package/dist/core/emoji-captions.js +121 -0
- package/dist/core/emoji-captions.js.map +1 -0
- package/dist/core/face-reframe.d.ts +91 -0
- package/dist/core/face-reframe.d.ts.map +1 -0
- package/dist/core/face-reframe.js +141 -0
- package/dist/core/face-reframe.js.map +1 -0
- package/dist/core/face-reframe.test.d.ts +2 -0
- package/dist/core/face-reframe.test.d.ts.map +1 -0
- package/dist/core/face-reframe.test.js +171 -0
- package/dist/core/face-reframe.test.js.map +1 -0
- package/dist/core/filler-words.d.ts +57 -9
- package/dist/core/filler-words.d.ts.map +1 -1
- package/dist/core/filler-words.js +61 -9
- package/dist/core/filler-words.js.map +1 -1
- package/dist/core/filler-words.test.js +91 -17
- package/dist/core/filler-words.test.js.map +1 -1
- package/dist/core/hook-rewrite.d.ts +48 -0
- package/dist/core/hook-rewrite.d.ts.map +1 -0
- package/dist/core/hook-rewrite.js +151 -0
- package/dist/core/hook-rewrite.js.map +1 -0
- package/dist/core/hook-rewrite.test.d.ts +2 -0
- package/dist/core/hook-rewrite.test.d.ts.map +1 -0
- package/dist/core/hook-rewrite.test.js +58 -0
- package/dist/core/hook-rewrite.test.js.map +1 -0
- package/dist/core/hosts/lazy.d.ts.map +1 -1
- package/dist/core/hosts/lazy.js +2 -0
- package/dist/core/hosts/lazy.js.map +1 -1
- package/dist/core/hosts/premiere/adapter.d.ts +1 -0
- package/dist/core/hosts/premiere/adapter.d.ts.map +1 -1
- package/dist/core/hosts/premiere/adapter.js.map +1 -1
- package/dist/core/hosts/premiere/bridge-source.d.ts.map +1 -1
- package/dist/core/hosts/premiere/bridge-source.js +6 -3
- package/dist/core/hosts/premiere/bridge-source.js.map +1 -1
- package/dist/core/hosts/resolve/adapter.d.ts +1 -0
- package/dist/core/hosts/resolve/adapter.d.ts.map +1 -1
- package/dist/core/hosts/resolve/adapter.js.map +1 -1
- package/dist/core/hosts/resolve/bridge-source.d.ts.map +1 -1
- package/dist/core/hosts/resolve/bridge-source.js +31 -4
- package/dist/core/hosts/resolve/bridge-source.js.map +1 -1
- package/dist/core/hosts/resolve/bridge.d.ts +2 -19
- package/dist/core/hosts/resolve/bridge.d.ts.map +1 -1
- package/dist/core/hosts/resolve/bridge.js +70 -41
- package/dist/core/hosts/resolve/bridge.js.map +1 -1
- package/dist/core/hosts/resolve/bridge.test.js +130 -0
- package/dist/core/hosts/resolve/bridge.test.js.map +1 -1
- package/dist/core/hosts/types.d.ts +6 -0
- package/dist/core/hosts/types.d.ts.map +1 -1
- package/dist/core/hosts/types.js.map +1 -1
- package/dist/core/logger.d.ts +32 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +188 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/loop-match.d.ts +57 -0
- package/dist/core/loop-match.d.ts.map +1 -0
- package/dist/core/loop-match.js +91 -0
- package/dist/core/loop-match.js.map +1 -0
- package/dist/core/media/ffmpeg.d.ts.map +1 -1
- package/dist/core/media/ffmpeg.js +14 -3
- package/dist/core/media/ffmpeg.js.map +1 -1
- package/dist/core/multi-format.d.ts +67 -0
- package/dist/core/multi-format.d.ts.map +1 -0
- package/dist/core/multi-format.js +127 -0
- package/dist/core/multi-format.js.map +1 -0
- package/dist/core/multi-format.test.d.ts +2 -0
- package/dist/core/multi-format.test.d.ts.map +1 -0
- package/dist/core/multi-format.test.js +151 -0
- package/dist/core/multi-format.test.js.map +1 -0
- package/dist/core/python/beats.py +61 -0
- package/dist/core/python/face_reframe.py +163 -0
- package/dist/core/python/sidecar-path.d.ts +13 -0
- package/dist/core/python/sidecar-path.d.ts.map +1 -0
- package/dist/core/python/sidecar-path.js +24 -0
- package/dist/core/python/sidecar-path.js.map +1 -0
- package/dist/core/python.d.ts +57 -0
- package/dist/core/python.d.ts.map +1 -0
- package/dist/core/python.js +107 -0
- package/dist/core/python.js.map +1 -0
- package/dist/core/python.test.d.ts +2 -0
- package/dist/core/python.test.d.ts.map +1 -0
- package/dist/core/python.test.js +129 -0
- package/dist/core/python.test.js.map +1 -0
- package/dist/core/retention-structure.d.ts +81 -0
- package/dist/core/retention-structure.d.ts.map +1 -0
- package/dist/core/retention-structure.js +206 -0
- package/dist/core/retention-structure.js.map +1 -0
- package/dist/core/retention-structure.test.d.ts +2 -0
- package/dist/core/retention-structure.test.d.ts.map +1 -0
- package/dist/core/retention-structure.test.js +88 -0
- package/dist/core/retention-structure.test.js.map +1 -0
- package/dist/core/review.d.ts +17 -0
- package/dist/core/review.d.ts.map +1 -1
- package/dist/core/review.js +20 -24
- package/dist/core/review.js.map +1 -1
- package/dist/core/safe-paths.d.ts +11 -0
- package/dist/core/safe-paths.d.ts.map +1 -1
- package/dist/core/safe-paths.js +26 -10
- package/dist/core/safe-paths.js.map +1 -1
- package/dist/core/safe-paths.test.js +16 -0
- package/dist/core/safe-paths.test.js.map +1 -1
- package/dist/core/skills-loader.d.ts +48 -2
- package/dist/core/skills-loader.d.ts.map +1 -1
- package/dist/core/skills-loader.js +97 -19
- package/dist/core/skills-loader.js.map +1 -1
- package/dist/core/skills-loader.test.js +63 -1
- package/dist/core/skills-loader.test.js.map +1 -1
- package/dist/core/srt.d.ts +42 -7
- package/dist/core/srt.d.ts.map +1 -1
- package/dist/core/srt.js +101 -32
- package/dist/core/srt.js.map +1 -1
- package/dist/core/srt.test.js +54 -1
- package/dist/core/srt.test.js.map +1 -1
- package/dist/core/thumbnail-compose.d.ts +58 -0
- package/dist/core/thumbnail-compose.d.ts.map +1 -0
- package/dist/core/thumbnail-compose.js +101 -0
- package/dist/core/thumbnail-compose.js.map +1 -0
- package/dist/core/thumbnail-promise.d.ts +46 -0
- package/dist/core/thumbnail-promise.d.ts.map +1 -0
- package/dist/core/thumbnail-promise.js +133 -0
- package/dist/core/thumbnail-promise.js.map +1 -0
- package/dist/core/thumbnail-promise.test.d.ts +2 -0
- package/dist/core/thumbnail-promise.test.d.ts.map +1 -0
- package/dist/core/thumbnail-promise.test.js +52 -0
- package/dist/core/thumbnail-promise.test.js.map +1 -0
- package/dist/core/viral-moments.d.ts +70 -0
- package/dist/core/viral-moments.d.ts.map +1 -0
- package/dist/core/viral-moments.js +192 -0
- package/dist/core/viral-moments.js.map +1 -0
- package/dist/core/viral-moments.test.d.ts +2 -0
- package/dist/core/viral-moments.test.d.ts.map +1 -0
- package/dist/core/viral-moments.test.js +153 -0
- package/dist/core/viral-moments.test.js.map +1 -0
- package/dist/core/whisper.d.ts +16 -0
- package/dist/core/whisper.d.ts.map +1 -1
- package/dist/core/whisper.js +72 -5
- package/dist/core/whisper.js.map +1 -1
- package/dist/core/whisper.test.js +111 -1
- package/dist/core/whisper.test.js.map +1 -1
- package/dist/core/youtube-metadata.d.ts +44 -0
- package/dist/core/youtube-metadata.d.ts.map +1 -0
- package/dist/core/youtube-metadata.js +168 -0
- package/dist/core/youtube-metadata.js.map +1 -0
- package/dist/core/youtube-metadata.test.d.ts +2 -0
- package/dist/core/youtube-metadata.test.d.ts.map +1 -0
- package/dist/core/youtube-metadata.test.js +132 -0
- package/dist/core/youtube-metadata.test.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/prompt-commands.d.ts +24 -0
- package/dist/prompt-commands.d.ts.map +1 -0
- package/dist/prompt-commands.js +243 -0
- package/dist/prompt-commands.js.map +1 -0
- package/dist/prompt-commands.test.d.ts +2 -0
- package/dist/prompt-commands.test.d.ts.map +1 -0
- package/dist/prompt-commands.test.js +46 -0
- package/dist/prompt-commands.test.js.map +1 -0
- package/dist/skills.d.ts +6 -6
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +1426 -445
- package/dist/skills.js.map +1 -1
- package/dist/system-prompt.d.ts.map +1 -1
- package/dist/system-prompt.js +108 -0
- package/dist/system-prompt.js.map +1 -1
- package/dist/tools/add-fades.d.ts.map +1 -1
- package/dist/tools/add-fades.js +2 -1
- package/dist/tools/add-fades.js.map +1 -1
- package/dist/tools/add-sfx-at-cuts.d.ts.map +1 -1
- package/dist/tools/add-sfx-at-cuts.js +36 -11
- package/dist/tools/add-sfx-at-cuts.js.map +1 -1
- package/dist/tools/add-sfx-to-timeline.d.ts +34 -0
- package/dist/tools/add-sfx-to-timeline.d.ts.map +1 -0
- package/dist/tools/add-sfx-to-timeline.js +169 -0
- package/dist/tools/add-sfx-to-timeline.js.map +1 -0
- package/dist/tools/add-sfx-to-timeline.test.d.ts +2 -0
- package/dist/tools/add-sfx-to-timeline.test.d.ts.map +1 -0
- package/dist/tools/add-sfx-to-timeline.test.js +181 -0
- package/dist/tools/add-sfx-to-timeline.test.js.map +1 -0
- package/dist/tools/audit-first-frame.d.ts +36 -0
- package/dist/tools/audit-first-frame.d.ts.map +1 -0
- package/dist/tools/audit-first-frame.js +181 -0
- package/dist/tools/audit-first-frame.js.map +1 -0
- package/dist/tools/audit-retention-structure.d.ts +20 -0
- package/dist/tools/audit-retention-structure.d.ts.map +1 -0
- package/dist/tools/audit-retention-structure.js +95 -0
- package/dist/tools/audit-retention-structure.js.map +1 -0
- package/dist/tools/audit-retention-structure.test.d.ts +2 -0
- package/dist/tools/audit-retention-structure.test.d.ts.map +1 -0
- package/dist/tools/audit-retention-structure.test.js +93 -0
- package/dist/tools/audit-retention-structure.test.js.map +1 -0
- package/dist/tools/bleep-words.d.ts +59 -0
- package/dist/tools/bleep-words.d.ts.map +1 -0
- package/dist/tools/bleep-words.js +211 -0
- package/dist/tools/bleep-words.js.map +1 -0
- package/dist/tools/bleep-words.test.d.ts +2 -0
- package/dist/tools/bleep-words.test.d.ts.map +1 -0
- package/dist/tools/bleep-words.test.js +96 -0
- package/dist/tools/bleep-words.test.js.map +1 -0
- package/dist/tools/burn-subtitles.d.ts.map +1 -1
- package/dist/tools/burn-subtitles.js +10 -5
- package/dist/tools/burn-subtitles.js.map +1 -1
- package/dist/tools/clean-audio.d.ts.map +1 -1
- package/dist/tools/clean-audio.js +2 -1
- package/dist/tools/clean-audio.js.map +1 -1
- package/dist/tools/cluster-takes.js +2 -1
- package/dist/tools/cluster-takes.js.map +1 -1
- package/dist/tools/compose-thumbnail-variants.d.ts +70 -0
- package/dist/tools/compose-thumbnail-variants.d.ts.map +1 -0
- package/dist/tools/compose-thumbnail-variants.js +274 -0
- package/dist/tools/compose-thumbnail-variants.js.map +1 -0
- package/dist/tools/compose-thumbnail.d.ts +6 -13
- package/dist/tools/compose-thumbnail.d.ts.map +1 -1
- package/dist/tools/compose-thumbnail.js +44 -81
- package/dist/tools/compose-thumbnail.js.map +1 -1
- package/dist/tools/concat-videos.d.ts.map +1 -1
- package/dist/tools/concat-videos.js +12 -5
- package/dist/tools/concat-videos.js.map +1 -1
- package/dist/tools/concat-videos.test.d.ts +2 -0
- package/dist/tools/concat-videos.test.d.ts.map +1 -0
- package/dist/tools/concat-videos.test.js +103 -0
- package/dist/tools/concat-videos.test.js.map +1 -0
- package/dist/tools/crossfade-videos.d.ts.map +1 -1
- package/dist/tools/crossfade-videos.js +2 -1
- package/dist/tools/crossfade-videos.js.map +1 -1
- package/dist/tools/cut-filler-words.d.ts.map +1 -1
- package/dist/tools/cut-filler-words.js +24 -8
- package/dist/tools/cut-filler-words.js.map +1 -1
- package/dist/tools/detect-speaker-changes.js +2 -1
- package/dist/tools/detect-speaker-changes.js.map +1 -1
- package/dist/tools/extract-audio.d.ts.map +1 -1
- package/dist/tools/extract-audio.js +13 -7
- package/dist/tools/extract-audio.js.map +1 -1
- package/dist/tools/face-reframe.d.ts +30 -0
- package/dist/tools/face-reframe.d.ts.map +1 -0
- package/dist/tools/face-reframe.js +143 -0
- package/dist/tools/face-reframe.js.map +1 -0
- package/dist/tools/face-reframe.test.d.ts +2 -0
- package/dist/tools/face-reframe.test.d.ts.map +1 -0
- package/dist/tools/face-reframe.test.js +139 -0
- package/dist/tools/face-reframe.test.js.map +1 -0
- package/dist/tools/find-viral-moments.d.ts +23 -0
- package/dist/tools/find-viral-moments.d.ts.map +1 -0
- package/dist/tools/find-viral-moments.js +176 -0
- package/dist/tools/find-viral-moments.js.map +1 -0
- package/dist/tools/find-viral-moments.test.d.ts +2 -0
- package/dist/tools/find-viral-moments.test.d.ts.map +1 -0
- package/dist/tools/find-viral-moments.test.js +144 -0
- package/dist/tools/find-viral-moments.test.js.map +1 -0
- package/dist/tools/generate-gif.d.ts.map +1 -1
- package/dist/tools/generate-gif.js +47 -40
- package/dist/tools/generate-gif.js.map +1 -1
- package/dist/tools/generate-gif.test.d.ts +2 -0
- package/dist/tools/generate-gif.test.d.ts.map +1 -0
- package/dist/tools/generate-gif.test.js +115 -0
- package/dist/tools/generate-gif.test.js.map +1 -0
- package/dist/tools/generate-outro.d.ts +18 -0
- package/dist/tools/generate-outro.d.ts.map +1 -0
- package/dist/tools/generate-outro.js +175 -0
- package/dist/tools/generate-outro.js.map +1 -0
- package/dist/tools/generate-youtube-metadata.d.ts +23 -0
- package/dist/tools/generate-youtube-metadata.d.ts.map +1 -0
- package/dist/tools/generate-youtube-metadata.js +103 -0
- package/dist/tools/generate-youtube-metadata.js.map +1 -0
- package/dist/tools/generate-youtube-metadata.test.d.ts +2 -0
- package/dist/tools/generate-youtube-metadata.test.d.ts.map +1 -0
- package/dist/tools/generate-youtube-metadata.test.js +118 -0
- package/dist/tools/generate-youtube-metadata.test.js.map +1 -0
- package/dist/tools/index.d.ts +14 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +130 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/index.test.js +27 -1
- package/dist/tools/index.test.js.map +1 -1
- package/dist/tools/ken-burns.d.ts.map +1 -1
- package/dist/tools/ken-burns.js +2 -1
- package/dist/tools/ken-burns.js.map +1 -1
- package/dist/tools/loop-match-short.d.ts +22 -0
- package/dist/tools/loop-match-short.d.ts.map +1 -0
- package/dist/tools/loop-match-short.js +107 -0
- package/dist/tools/loop-match-short.js.map +1 -0
- package/dist/tools/mix-audio.d.ts.map +1 -1
- package/dist/tools/mix-audio.js +2 -1
- package/dist/tools/mix-audio.js.map +1 -1
- package/dist/tools/normalize-loudness.d.ts.map +1 -1
- package/dist/tools/normalize-loudness.js +2 -1
- package/dist/tools/normalize-loudness.js.map +1 -1
- package/dist/tools/path-traversal.test.d.ts +15 -0
- package/dist/tools/path-traversal.test.d.ts.map +1 -0
- package/dist/tools/path-traversal.test.js +223 -0
- package/dist/tools/path-traversal.test.js.map +1 -0
- package/dist/tools/pick-best-takes.js +2 -1
- package/dist/tools/pick-best-takes.js.map +1 -1
- package/dist/tools/punch-in.d.ts.map +1 -1
- package/dist/tools/punch-in.js +2 -1
- package/dist/tools/punch-in.js.map +1 -1
- package/dist/tools/read-transcript.js +2 -1
- package/dist/tools/read-transcript.js.map +1 -1
- package/dist/tools/render-multi-format.d.ts +35 -0
- package/dist/tools/render-multi-format.d.ts.map +1 -0
- package/dist/tools/render-multi-format.js +206 -0
- package/dist/tools/render-multi-format.js.map +1 -0
- package/dist/tools/render-multi-format.test.d.ts +2 -0
- package/dist/tools/render-multi-format.test.d.ts.map +1 -0
- package/dist/tools/render-multi-format.test.js +312 -0
- package/dist/tools/render-multi-format.test.js.map +1 -0
- package/dist/tools/render.d.ts.map +1 -1
- package/dist/tools/render.js +2 -2
- package/dist/tools/render.js.map +1 -1
- package/dist/tools/rewrite-hook.d.ts +32 -0
- package/dist/tools/rewrite-hook.d.ts.map +1 -0
- package/dist/tools/rewrite-hook.js +65 -0
- package/dist/tools/rewrite-hook.js.map +1 -0
- package/dist/tools/score-clip.d.ts +30 -0
- package/dist/tools/score-clip.d.ts.map +1 -0
- package/dist/tools/score-clip.js +109 -0
- package/dist/tools/score-clip.js.map +1 -0
- package/dist/tools/score-clip.test.d.ts +2 -0
- package/dist/tools/score-clip.test.d.ts.map +1 -0
- package/dist/tools/score-clip.test.js +110 -0
- package/dist/tools/score-clip.test.js.map +1 -0
- package/dist/tools/search-tools.d.ts +34 -0
- package/dist/tools/search-tools.d.ts.map +1 -0
- package/dist/tools/search-tools.js +86 -0
- package/dist/tools/search-tools.js.map +1 -0
- package/dist/tools/search-tools.test.d.ts +2 -0
- package/dist/tools/search-tools.test.d.ts.map +1 -0
- package/dist/tools/search-tools.test.js +60 -0
- package/dist/tools/search-tools.test.js.map +1 -0
- package/dist/tools/snap-cuts-to-beats.d.ts +18 -0
- package/dist/tools/snap-cuts-to-beats.d.ts.map +1 -0
- package/dist/tools/snap-cuts-to-beats.js +110 -0
- package/dist/tools/snap-cuts-to-beats.js.map +1 -0
- package/dist/tools/snap-cuts-to-beats.test.d.ts +2 -0
- package/dist/tools/snap-cuts-to-beats.test.d.ts.map +1 -0
- package/dist/tools/snap-cuts-to-beats.test.js +99 -0
- package/dist/tools/snap-cuts-to-beats.test.js.map +1 -0
- package/dist/tools/speed-ramp.d.ts.map +1 -1
- package/dist/tools/speed-ramp.js +2 -1
- package/dist/tools/speed-ramp.js.map +1 -1
- package/dist/tools/stabilize-video.d.ts.map +1 -1
- package/dist/tools/stabilize-video.js +2 -1
- package/dist/tools/stabilize-video.js.map +1 -1
- package/dist/tools/suggest-broll.d.ts +34 -0
- package/dist/tools/suggest-broll.d.ts.map +1 -0
- package/dist/tools/suggest-broll.js +367 -0
- package/dist/tools/suggest-broll.js.map +1 -0
- package/dist/tools/suggest-broll.test.d.ts +2 -0
- package/dist/tools/suggest-broll.test.d.ts.map +1 -0
- package/dist/tools/suggest-broll.test.js +217 -0
- package/dist/tools/suggest-broll.test.js.map +1 -0
- package/dist/tools/text-based-cut.d.ts +33 -0
- package/dist/tools/text-based-cut.d.ts.map +1 -0
- package/dist/tools/text-based-cut.js +172 -0
- package/dist/tools/text-based-cut.js.map +1 -0
- package/dist/tools/text-based-cut.test.d.ts +2 -0
- package/dist/tools/text-based-cut.test.d.ts.map +1 -0
- package/dist/tools/text-based-cut.test.js +32 -0
- package/dist/tools/text-based-cut.test.js.map +1 -0
- package/dist/tools/transcribe.d.ts +1 -1
- package/dist/tools/transition-videos.d.ts +1 -1
- package/dist/tools/transition-videos.d.ts.map +1 -1
- package/dist/tools/transition-videos.js +2 -1
- package/dist/tools/transition-videos.js.map +1 -1
- package/dist/tools/trim-dead-air.d.ts +59 -0
- package/dist/tools/trim-dead-air.d.ts.map +1 -0
- package/dist/tools/trim-dead-air.js +215 -0
- package/dist/tools/trim-dead-air.js.map +1 -0
- package/dist/tools/trim-dead-air.test.d.ts +2 -0
- package/dist/tools/trim-dead-air.test.d.ts.map +1 -0
- package/dist/tools/trim-dead-air.test.js +75 -0
- package/dist/tools/trim-dead-air.test.js.map +1 -0
- package/dist/tools/verify-thumbnail-promise.d.ts +33 -0
- package/dist/tools/verify-thumbnail-promise.d.ts.map +1 -0
- package/dist/tools/verify-thumbnail-promise.js +112 -0
- package/dist/tools/verify-thumbnail-promise.js.map +1 -0
- package/dist/tools/verify-thumbnail-promise.test.d.ts +2 -0
- package/dist/tools/verify-thumbnail-promise.test.d.ts.map +1 -0
- package/dist/tools/verify-thumbnail-promise.test.js +38 -0
- package/dist/tools/verify-thumbnail-promise.test.js.map +1 -0
- package/dist/tools/write-keyword-captions.d.ts +7 -0
- package/dist/tools/write-keyword-captions.d.ts.map +1 -1
- package/dist/tools/write-keyword-captions.js +35 -4
- package/dist/tools/write-keyword-captions.js.map +1 -1
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +75 -11
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/tool-formatters.d.ts +30 -0
- package/dist/ui/tool-formatters.d.ts.map +1 -0
- package/dist/ui/tool-formatters.js +461 -0
- package/dist/ui/tool-formatters.js.map +1 -0
- package/dist/ui/tool-formatters.test.d.ts +2 -0
- package/dist/ui/tool-formatters.test.d.ts.map +1 -0
- package/dist/ui/tool-formatters.test.js +143 -0
- package/dist/ui/tool-formatters.test.js.map +1 -0
- package/package.json +10 -9
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { runPython } from "./python.js";
|
|
2
|
+
import { sidecarPath } from "./python/sidecar-path.js";
|
|
3
|
+
export function parseAspect(aspect) {
|
|
4
|
+
const [w, h] = aspect.split(":").map((n) => Number(n));
|
|
5
|
+
if (!Number.isFinite(w) || !Number.isFinite(h) || w <= 0 || h <= 0) {
|
|
6
|
+
throw new Error(`invalid aspect: ${aspect}`);
|
|
7
|
+
}
|
|
8
|
+
return { w, h };
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Spawn the face-reframe sidecar; return the parsed shot plan.
|
|
12
|
+
* Throws on sidecar failure with the structured error embedded.
|
|
13
|
+
*/
|
|
14
|
+
export async function analyzeReframe(videoPath, opts = {}) {
|
|
15
|
+
const script = sidecarPath("face_reframe.py");
|
|
16
|
+
const stdin = JSON.stringify({
|
|
17
|
+
videoPath,
|
|
18
|
+
sampleFps: opts.sampleFps,
|
|
19
|
+
minDetectionConfidence: opts.minDetectionConfidence,
|
|
20
|
+
smoothingWindowSec: opts.smoothingWindowSec,
|
|
21
|
+
});
|
|
22
|
+
const { code, stdout, stderr } = await runPython(script, [], {
|
|
23
|
+
signal: opts.signal,
|
|
24
|
+
stdin,
|
|
25
|
+
});
|
|
26
|
+
const trimmed = stdout.trim();
|
|
27
|
+
if (!trimmed) {
|
|
28
|
+
throw new Error(`face-reframe sidecar returned empty stdout (exit ${code}): ${tail(stderr)}`);
|
|
29
|
+
}
|
|
30
|
+
let parsed;
|
|
31
|
+
try {
|
|
32
|
+
parsed = JSON.parse(trimmed);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
throw new Error(`face-reframe sidecar returned malformed output: ${trimmed.slice(-200)} | stderr: ${tail(stderr)}`);
|
|
36
|
+
}
|
|
37
|
+
if (parsed && typeof parsed === "object" && "error" in parsed) {
|
|
38
|
+
const err = parsed;
|
|
39
|
+
throw new Error(err.error ?? "unknown face-reframe sidecar error");
|
|
40
|
+
}
|
|
41
|
+
if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.shots)) {
|
|
42
|
+
throw new Error(`face-reframe sidecar returned unexpected shape: ${trimmed.slice(-200)}`);
|
|
43
|
+
}
|
|
44
|
+
return parsed;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Compute the crop dimensions for a target aspect when cropping FROM a
|
|
48
|
+
* source frame. The crop is sized to fit ENTIRELY inside the source — i.e.
|
|
49
|
+
* we letterbox neither dimension. The returned `outW` × `outH` matches the
|
|
50
|
+
* target aspect exactly.
|
|
51
|
+
*
|
|
52
|
+
* Pure / unit-testable.
|
|
53
|
+
*/
|
|
54
|
+
export function reframeCropSize(srcW, srcH, aspect) {
|
|
55
|
+
if (srcW <= 0 || srcH <= 0) {
|
|
56
|
+
throw new Error("source dimensions must be positive");
|
|
57
|
+
}
|
|
58
|
+
const { w: aw, h: ah } = parseAspect(aspect);
|
|
59
|
+
// Two candidate crops:
|
|
60
|
+
// width-bound: outW = srcW, outH = srcW * ah / aw
|
|
61
|
+
// height-bound: outH = srcH, outW = srcH * aw / ah
|
|
62
|
+
// Pick whichever fits inside the source frame.
|
|
63
|
+
const heightFromW = (srcW * ah) / aw;
|
|
64
|
+
let outW;
|
|
65
|
+
let outH;
|
|
66
|
+
if (heightFromW <= srcH) {
|
|
67
|
+
// Width-bound (e.g. 16:9 source → 1:1 crop is height-bound; 16:9 source → 9:16 crop is height-bound;
|
|
68
|
+
// a square source → 9:16 crop is width-bound).
|
|
69
|
+
outW = srcW;
|
|
70
|
+
outH = heightFromW;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
outH = srcH;
|
|
74
|
+
outW = (srcH * aw) / ah;
|
|
75
|
+
}
|
|
76
|
+
// Round DOWN to even integers. ffmpeg's libx264 needs even crop dims;
|
|
77
|
+
// rounding down avoids exceeding the source.
|
|
78
|
+
outW = Math.floor(outW / 2) * 2;
|
|
79
|
+
outH = Math.floor(outH / 2) * 2;
|
|
80
|
+
if (outW <= 0 || outH <= 0) {
|
|
81
|
+
throw new Error("computed crop is degenerate");
|
|
82
|
+
}
|
|
83
|
+
return { outW, outH };
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Compile a per-shot smoothed-face plan into one ffmpeg `crop` expression
|
|
87
|
+
* that switches X position by timestamp. Each shot contributes one
|
|
88
|
+
* `if(between(t, a, b), x_i, ...)` tier; the outermost ELSE is a centre
|
|
89
|
+
* crop so the filter is a no-op outside any defined shot.
|
|
90
|
+
*
|
|
91
|
+
* The crop's Y is left static (we don't currently track vertical motion;
|
|
92
|
+
* faces tend to dominate the upper third on portrait crops anyway).
|
|
93
|
+
*
|
|
94
|
+
* Pure / unit-testable. No ffmpeg execution.
|
|
95
|
+
*/
|
|
96
|
+
export function buildReframeFilter(plan, aspect) {
|
|
97
|
+
const { sourceWidth: srcW, sourceHeight: srcH } = plan;
|
|
98
|
+
const { outW, outH } = reframeCropSize(srcW, srcH, aspect);
|
|
99
|
+
// For each shot compute the desired top-left X from the smoothed face
|
|
100
|
+
// centre, then clamp into the legal range so the crop never escapes the
|
|
101
|
+
// frame. Y is centred — vertical face position varies less for talking
|
|
102
|
+
// heads, and exposing both axes invites jitter.
|
|
103
|
+
const yCentre = Math.floor((srcH - outH) / 2);
|
|
104
|
+
const shots = plan.shots
|
|
105
|
+
.filter((s) => s.endSec > s.startSec)
|
|
106
|
+
.sort((a, b) => a.startSec - b.startSec);
|
|
107
|
+
const shotXs = [];
|
|
108
|
+
// Build `xExpr` from the LAST shot backwards so each `if` wraps the next.
|
|
109
|
+
// Default (outside all shots): centre crop.
|
|
110
|
+
const centreX = Math.floor((srcW - outW) / 2);
|
|
111
|
+
let xExpr = String(centreX);
|
|
112
|
+
for (let i = shots.length - 1; i >= 0; i--) {
|
|
113
|
+
const s = shots[i];
|
|
114
|
+
const desiredCx = Math.round(s.smoothedX * srcW);
|
|
115
|
+
let x = desiredCx - Math.floor(outW / 2);
|
|
116
|
+
// Clamp into [0, srcW - outW].
|
|
117
|
+
if (x < 0)
|
|
118
|
+
x = 0;
|
|
119
|
+
const maxX = srcW - outW;
|
|
120
|
+
if (x > maxX)
|
|
121
|
+
x = maxX;
|
|
122
|
+
shotXs[i] = x;
|
|
123
|
+
const a = fmt(s.startSec);
|
|
124
|
+
const b = fmt(s.endSec);
|
|
125
|
+
xExpr = `if(between(t\\,${a}\\,${b})\\,${x}\\,${xExpr})`;
|
|
126
|
+
}
|
|
127
|
+
// crop=W:H:x:y — single filter, single pass. We pin Y to a constant
|
|
128
|
+
// (centre) for now; if we extend to vertical tracking later this becomes
|
|
129
|
+
// a parallel piecewise expression.
|
|
130
|
+
const cropFilter = `crop=${outW}:${outH}:x='${xExpr}':y=${yCentre}`;
|
|
131
|
+
return { filter: cropFilter, outWidth: outW, outHeight: outH, shotXs };
|
|
132
|
+
}
|
|
133
|
+
function fmt(n) {
|
|
134
|
+
if (!Number.isFinite(n))
|
|
135
|
+
return "0";
|
|
136
|
+
return Number(n.toFixed(4)).toString();
|
|
137
|
+
}
|
|
138
|
+
function tail(s) {
|
|
139
|
+
return s.split("\n").filter(Boolean).slice(-3).join(" | ").slice(-300);
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=face-reframe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"face-reframe.js","sourceRoot":"","sources":["../../src/core/face-reframe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAqBvD,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACvD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,KAAK,CAAC,mBAAmB,MAAM,EAAE,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;AAClB,CAAC;AAyCD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,SAAiB,EACjB,OAA8B,EAAE;IAEhC,MAAM,MAAM,GAAG,WAAW,CAAC,iBAAiB,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC;QAC3B,SAAS;QACT,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,sBAAsB,EAAE,IAAI,CAAC,sBAAsB;QACnD,kBAAkB,EAAE,IAAI,CAAC,kBAAkB;KAC5C,CAAC,CAAC;IACH,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,MAAM,EAAE,EAAE,EAAE;QAC3D,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,KAAK;KACN,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,oDAAoD,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAChG,CAAC;IACD,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,mDAAmD,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,cAAc,IAAI,CAAC,MAAM,CAAC,EAAE,CACnG,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,OAAO,IAAI,MAAM,EAAE,CAAC;QAC9D,MAAM,GAAG,GAAG,MAA4B,CAAC;QACzC,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,oCAAoC,CAAC,CAAC;IACrE,CAAC;IACD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAE,MAAsB,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3F,MAAM,IAAI,KAAK,CAAC,mDAAmD,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC5F,CAAC;IACD,OAAO,MAAqB,CAAC;AAC/B,CAAC;AAaD;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAY,EACZ,IAAY,EACZ,MAAc;IAEd,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACxD,CAAC;IACD,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IAC7C,uBAAuB;IACvB,oDAAoD;IACpD,qDAAqD;IACrD,+CAA+C;IAC/C,MAAM,WAAW,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC;IACrC,IAAI,IAAY,CAAC;IACjB,IAAI,IAAY,CAAC;IACjB,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;QACxB,qGAAqG;QACrG,+CAA+C;QAC/C,IAAI,GAAG,IAAI,CAAC;QACZ,IAAI,GAAG,WAAW,CAAC;IACrB,CAAC;SAAM,CAAC;QACN,IAAI,GAAG,IAAI,CAAC;QACZ,IAAI,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC;IAC1B,CAAC;IACD,sEAAsE;IACtE,6CAA6C;IAC7C,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACxB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAiB,EAAE,MAAc;IAClE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;IACvD,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IAE3D,sEAAsE;IACtE,wEAAwE;IACxE,uEAAuE;IACvE,gDAAgD;IAChD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAE9C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK;SACrB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,QAAQ,CAAC;SACpC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;IAE3C,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,0EAA0E;IAC1E,4CAA4C;IAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9C,IAAI,KAAK,GAAW,MAAM,CAAC,OAAO,CAAC,CAAC;IACpC,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3C,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACnB,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;QACjD,IAAI,CAAC,GAAG,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;QACzC,+BAA+B;QAC/B,IAAI,CAAC,GAAG,CAAC;YAAE,CAAC,GAAG,CAAC,CAAC;QACjB,MAAM,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,GAAG,IAAI;YAAE,CAAC,GAAG,IAAI,CAAC;QACvB,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACd,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC1B,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QACxB,KAAK,GAAG,kBAAkB,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,GAAG,CAAC;IAC3D,CAAC;IAED,oEAAoE;IACpE,yEAAyE;IACzE,mCAAmC;IACnC,MAAM,UAAU,GAAG,QAAQ,IAAI,IAAI,IAAI,OAAO,KAAK,OAAO,OAAO,EAAE,CAAC;IACpE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AACzE,CAAC;AAED,SAAS,GAAG,CAAC,CAAS;IACpB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,GAAG,CAAC;IACpC,OAAO,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;AACzC,CAAC;AAED,SAAS,IAAI,CAAC,CAAS;IACrB,OAAO,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC;AACzE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"face-reframe.test.d.ts","sourceRoot":"","sources":["../../src/core/face-reframe.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { buildReframeFilter, parseAspect, reframeCropSize, } from "./face-reframe.js";
|
|
3
|
+
function plan(overrides = {}) {
|
|
4
|
+
return {
|
|
5
|
+
shots: [],
|
|
6
|
+
totalSec: 10,
|
|
7
|
+
fps: 30,
|
|
8
|
+
sourceWidth: 1920,
|
|
9
|
+
sourceHeight: 1080,
|
|
10
|
+
...overrides,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
describe("parseAspect", () => {
|
|
14
|
+
it("parses each supported ratio", () => {
|
|
15
|
+
expect(parseAspect("9:16")).toEqual({ w: 9, h: 16 });
|
|
16
|
+
expect(parseAspect("1:1")).toEqual({ w: 1, h: 1 });
|
|
17
|
+
expect(parseAspect("4:5")).toEqual({ w: 4, h: 5 });
|
|
18
|
+
expect(parseAspect("16:9")).toEqual({ w: 16, h: 9 });
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
describe("reframeCropSize", () => {
|
|
22
|
+
it("9:16 from 1920x1080 → height-bound; outW = 1080 * 9/16 = 607 → even 606", () => {
|
|
23
|
+
const { outW, outH } = reframeCropSize(1920, 1080, "9:16");
|
|
24
|
+
expect(outH).toBe(1080);
|
|
25
|
+
expect(outW).toBe(606); // floor(607.5) -> 607 -> even-floor 606
|
|
26
|
+
});
|
|
27
|
+
it("1:1 from 1920x1080 → height-bound; outW = outH = 1080", () => {
|
|
28
|
+
const { outW, outH } = reframeCropSize(1920, 1080, "1:1");
|
|
29
|
+
expect(outW).toBe(1080);
|
|
30
|
+
expect(outH).toBe(1080);
|
|
31
|
+
});
|
|
32
|
+
it("4:5 from 1920x1080 → height-bound; outW = 1080 * 4/5 = 864", () => {
|
|
33
|
+
const { outW, outH } = reframeCropSize(1920, 1080, "4:5");
|
|
34
|
+
expect(outH).toBe(1080);
|
|
35
|
+
expect(outW).toBe(864);
|
|
36
|
+
});
|
|
37
|
+
it("16:9 from 1920x1080 → width-bound (full source)", () => {
|
|
38
|
+
const { outW, outH } = reframeCropSize(1920, 1080, "16:9");
|
|
39
|
+
expect(outW).toBe(1920);
|
|
40
|
+
expect(outH).toBe(1080);
|
|
41
|
+
});
|
|
42
|
+
it("9:16 from a square 1080x1080 source → width-bound; outH > srcH? no — width-bound", () => {
|
|
43
|
+
// height-from-w = 1080 * 16/9 = 1920 > srcH 1080 → fall to height-bound:
|
|
44
|
+
// outH = 1080, outW = 1080 * 9 / 16 = 607.5 → 606
|
|
45
|
+
const { outW, outH } = reframeCropSize(1080, 1080, "9:16");
|
|
46
|
+
expect(outH).toBe(1080);
|
|
47
|
+
expect(outW).toBe(606);
|
|
48
|
+
});
|
|
49
|
+
it("rejects non-positive source dimensions", () => {
|
|
50
|
+
expect(() => reframeCropSize(0, 1080, "9:16")).toThrow();
|
|
51
|
+
expect(() => reframeCropSize(1920, -1, "9:16")).toThrow();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe("buildReframeFilter", () => {
|
|
55
|
+
it("emits a centre-crop filter when there are no shots (filter falls through to centreX)", () => {
|
|
56
|
+
const r = buildReframeFilter(plan({ shots: [] }), "9:16");
|
|
57
|
+
// outW from 1920x1080 9:16 = 606; centreX = floor((1920-606)/2) = 657
|
|
58
|
+
expect(r.outWidth).toBe(606);
|
|
59
|
+
expect(r.outHeight).toBe(1080);
|
|
60
|
+
// No shots → no `if(...)` wrapping; filter = "crop=606:1080:x='657':y=0"
|
|
61
|
+
expect(r.filter).toBe("crop=606:1080:x='657':y=0");
|
|
62
|
+
expect(r.shotXs).toEqual([]);
|
|
63
|
+
});
|
|
64
|
+
it("clamps a face at the right edge to the maximum legal X", () => {
|
|
65
|
+
const p = plan({
|
|
66
|
+
shots: [
|
|
67
|
+
{
|
|
68
|
+
startSec: 0,
|
|
69
|
+
endSec: 5,
|
|
70
|
+
frames: [],
|
|
71
|
+
smoothedX: 0.99, // pushes crop way past the right edge
|
|
72
|
+
smoothedY: 0.5,
|
|
73
|
+
mode: "face",
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
});
|
|
77
|
+
const r = buildReframeFilter(p, "9:16");
|
|
78
|
+
// maxX = 1920 - 606 = 1314
|
|
79
|
+
expect(r.shotXs[0]).toBe(1314);
|
|
80
|
+
expect(r.filter).toContain("between(t\\,0\\,5)");
|
|
81
|
+
expect(r.filter).toContain("\\,1314\\,");
|
|
82
|
+
});
|
|
83
|
+
it("clamps a face at the left edge to X=0", () => {
|
|
84
|
+
const p = plan({
|
|
85
|
+
shots: [
|
|
86
|
+
{
|
|
87
|
+
startSec: 1,
|
|
88
|
+
endSec: 2,
|
|
89
|
+
frames: [],
|
|
90
|
+
smoothedX: 0.01,
|
|
91
|
+
smoothedY: 0.5,
|
|
92
|
+
mode: "face",
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
});
|
|
96
|
+
const r = buildReframeFilter(p, "9:16");
|
|
97
|
+
expect(r.shotXs[0]).toBe(0);
|
|
98
|
+
expect(r.filter).toContain("between(t\\,1\\,2)");
|
|
99
|
+
expect(r.filter).toContain("\\,0\\,");
|
|
100
|
+
});
|
|
101
|
+
it("uses centred X for a face at exact 0.5", () => {
|
|
102
|
+
const p = plan({
|
|
103
|
+
shots: [
|
|
104
|
+
{
|
|
105
|
+
startSec: 0,
|
|
106
|
+
endSec: 4,
|
|
107
|
+
frames: [],
|
|
108
|
+
smoothedX: 0.5,
|
|
109
|
+
smoothedY: 0.5,
|
|
110
|
+
mode: "face",
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
});
|
|
114
|
+
const r = buildReframeFilter(p, "9:16");
|
|
115
|
+
// desiredCx = round(0.5 * 1920) = 960; x = 960 - floor(606/2) = 960 - 303 = 657
|
|
116
|
+
expect(r.shotXs[0]).toBe(657);
|
|
117
|
+
});
|
|
118
|
+
it("nests two shots into a single piecewise X expression", () => {
|
|
119
|
+
const p = plan({
|
|
120
|
+
shots: [
|
|
121
|
+
{ startSec: 0, endSec: 3, frames: [], smoothedX: 0.3, smoothedY: 0.5, mode: "face" },
|
|
122
|
+
{ startSec: 3, endSec: 6, frames: [], smoothedX: 0.7, smoothedY: 0.5, mode: "face" },
|
|
123
|
+
],
|
|
124
|
+
});
|
|
125
|
+
const r = buildReframeFilter(p, "9:16");
|
|
126
|
+
expect(r.shotXs).toHaveLength(2);
|
|
127
|
+
expect(r.filter).toContain("between(t\\,0\\,3)");
|
|
128
|
+
expect(r.filter).toContain("between(t\\,3\\,6)");
|
|
129
|
+
// Outer if() should be the FIRST shot (sorted by start); inner the SECOND.
|
|
130
|
+
const idx0 = r.filter.indexOf("between(t\\,0\\,3)");
|
|
131
|
+
const idx3 = r.filter.indexOf("between(t\\,3\\,6)");
|
|
132
|
+
expect(idx0).toBeGreaterThanOrEqual(0);
|
|
133
|
+
expect(idx3).toBeGreaterThan(idx0);
|
|
134
|
+
// Final fallback (centre): 657 appears as the innermost else of x='...'.
|
|
135
|
+
// x is wrapped in single quotes; closing the y arg follows.
|
|
136
|
+
expect(r.filter).toMatch(/657\)\)':y=0$/);
|
|
137
|
+
});
|
|
138
|
+
it("produces an even outW × outH for libx264 compatibility", () => {
|
|
139
|
+
// Test a few odd source resolutions.
|
|
140
|
+
for (const [w, h] of [
|
|
141
|
+
[1280, 720],
|
|
142
|
+
[3840, 2160],
|
|
143
|
+
[1281, 721],
|
|
144
|
+
]) {
|
|
145
|
+
const r = buildReframeFilter(plan({ sourceWidth: w, sourceHeight: h }), "9:16");
|
|
146
|
+
expect(r.outWidth % 2).toBe(0);
|
|
147
|
+
expect(r.outHeight % 2).toBe(0);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
it("sorts shots by startSec even when caller passes them out of order", () => {
|
|
151
|
+
const p = plan({
|
|
152
|
+
shots: [
|
|
153
|
+
{ startSec: 5, endSec: 7, frames: [], smoothedX: 0.7, smoothedY: 0.5, mode: "face" },
|
|
154
|
+
{ startSec: 0, endSec: 2, frames: [], smoothedX: 0.3, smoothedY: 0.5, mode: "face" },
|
|
155
|
+
],
|
|
156
|
+
});
|
|
157
|
+
const r = buildReframeFilter(p, "9:16");
|
|
158
|
+
const idx0 = r.filter.indexOf("between(t\\,0\\,2)");
|
|
159
|
+
const idx5 = r.filter.indexOf("between(t\\,5\\,7)");
|
|
160
|
+
// Outer wraps inner; the FIRST sorted shot must appear first in the string.
|
|
161
|
+
expect(idx0).toBeGreaterThanOrEqual(0);
|
|
162
|
+
expect(idx5).toBeGreaterThan(idx0);
|
|
163
|
+
});
|
|
164
|
+
it("includes scale-back? — nope; we only emit a single crop filter (no scale)", () => {
|
|
165
|
+
// The tool layer chooses whether to upscale; the filter we emit is a
|
|
166
|
+
// pure crop. Verify there's no `scale=` in the output.
|
|
167
|
+
const r = buildReframeFilter(plan({ shots: [] }), "9:16");
|
|
168
|
+
expect(r.filter).not.toContain("scale=");
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
//# sourceMappingURL=face-reframe.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"face-reframe.test.js","sourceRoot":"","sources":["../../src/core/face-reframe.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -41,13 +41,23 @@ export interface DetectFillersOptions {
|
|
|
41
41
|
/** Filler vocabulary; defaults to DEFAULT_FILLERS. Case-insensitive. */
|
|
42
42
|
fillers?: readonly string[];
|
|
43
43
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
44
|
+
* Trim the filler-cut start by this many ms so the trailing audio of
|
|
45
|
+
* the PREVIOUS word isn't clipped. Positive = keep more of the previous
|
|
46
|
+
* word (cut starts later than whisper's word boundary). Default 0 ms.
|
|
47
|
+
*
|
|
48
|
+
* Note: this used to default to 20 ms with the OPPOSITE sign — the cut
|
|
49
|
+
* would EXPAND outward by 20 ms on each side, eating ~5 s of speech
|
|
50
|
+
* across a typical podcast cut. Reverted to a 0 default in commit
|
|
51
|
+
* `keep more of the speaker's actual audio` (May 2026); set to a small
|
|
52
|
+
* positive value (e.g. 15 ms) only if whisper is over-reporting word
|
|
53
|
+
* end times in your specific dataset.
|
|
46
54
|
*/
|
|
47
55
|
paddingStartMs?: number;
|
|
48
56
|
/**
|
|
49
|
-
*
|
|
50
|
-
* word isn't clipped.
|
|
57
|
+
* Trim the filler-cut end by this many ms so the leading audio of the
|
|
58
|
+
* NEXT word isn't clipped. Positive = keep more of the next word (cut
|
|
59
|
+
* ends earlier than whisper's word boundary). Default 0 ms. See note on
|
|
60
|
+
* paddingStartMs above for the bug-fix history.
|
|
51
61
|
*/
|
|
52
62
|
paddingEndMs?: number;
|
|
53
63
|
/**
|
|
@@ -59,9 +69,17 @@ export interface DetectFillersOptions {
|
|
|
59
69
|
* Aggressive single words like "like" / "so" / "actually" / "right" /
|
|
60
70
|
* "honestly" / "literally" / "basically" are filler ONLY when they
|
|
61
71
|
* appear as standalone disfluencies. When `aggressiveSingleWords` is
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
72
|
+
* true, these are included; when false (the default), only the safe-list
|
|
73
|
+
* (um/uh/er/hmm/you know/i mean/kind of/sort of) is applied.
|
|
74
|
+
*
|
|
75
|
+
* Default is **false** — the conservative path — because the tool
|
|
76
|
+
* permanently removes audio and words like "like"/"so" are frequently
|
|
77
|
+
* substantive. Pass `true` for ruthless cuts on monologue-style content
|
|
78
|
+
* where the speaker's verbal tics are clearly fillers.
|
|
79
|
+
*
|
|
80
|
+
* Changed from `true` default (May 2026): the old default caused
|
|
81
|
+
* unintended removal of substantive speech for callers who didn't
|
|
82
|
+
* explicitly configure this option.
|
|
65
83
|
*/
|
|
66
84
|
aggressiveSingleWords?: boolean;
|
|
67
85
|
}
|
|
@@ -82,8 +100,38 @@ export declare function detectFillerRanges(transcript: Transcript, opts?: Detect
|
|
|
82
100
|
*/
|
|
83
101
|
export declare function keepRangesFromFillers(fillers: FillerRange[], totalSec: number, minKeepDurSec?: number): KeepRange[];
|
|
84
102
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
103
|
+
* From a sorted keep-range list, compute the TIMELINE-space cut points.
|
|
104
|
+
*
|
|
105
|
+
* After `import_edl` lands the keep ranges concatenated onto the timeline,
|
|
106
|
+
* each junction between adjacent keeps becomes a visible cut at the
|
|
107
|
+
* timeline-relative timestamp = sum of all earlier keep durations.
|
|
108
|
+
*
|
|
109
|
+
* Example: keeps = [[0,10], [12,25], [28,40]]
|
|
110
|
+
* timeline = [0..10] + [10..23] + [23..35]
|
|
111
|
+
* cuts at = 10s, 23s (two junctions)
|
|
112
|
+
*
|
|
113
|
+
* Why this matters: tools like `add_sfx_to_timeline` and `add_sfx_at_cuts`
|
|
114
|
+
* place SFX at TIMELINE timestamps. If callers pass SOURCE timestamps from
|
|
115
|
+
* the transcript, the drift compounds with every cut and the SFX lands
|
|
116
|
+
* progressively further out of sync (fine for the first ~30s, off by 20+s
|
|
117
|
+
* by the end of a typical podcast). This helper exists to remove that
|
|
118
|
+
* footgun.
|
|
119
|
+
*/
|
|
120
|
+
export declare function keepRangesToTimelineCuts(keeps: KeepRange[]): number[];
|
|
121
|
+
/**
|
|
122
|
+
* Frame-align keep ranges. Rounds OUTWARD (start floors, end ceils) so
|
|
123
|
+
* we never DROP speech that lives in a partial frame at the keep's edge.
|
|
124
|
+
*
|
|
125
|
+
* Was: rounds inward (start ceils, end floors). Inward rounding never
|
|
126
|
+
* extends INTO an adjacent filler, but it ALSO loses up to one frame at
|
|
127
|
+
* each end of every keep — ~33 ms at 30 fps, ~17 ms at 60 fps. Across
|
|
128
|
+
* a 73-cut podcast that's 5+ seconds of speech disappearing into
|
|
129
|
+
* sub-frame rounding error.
|
|
130
|
+
*
|
|
131
|
+
* Trade-off: outward rounding can include up to one frame of an adjacent
|
|
132
|
+
* filler at each junction. That's ~33 ms at 30 fps — well within whisper
|
|
133
|
+
* timing noise (typically ±50 ms) and inaudible compared to losing 5 s
|
|
134
|
+
* of actual speech. Worth it.
|
|
87
135
|
*/
|
|
88
136
|
export declare function keepRangesToFrameRanges(keeps: KeepRange[], fps: number): Array<{
|
|
89
137
|
startFrame: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filler-words.d.ts","sourceRoot":"","sources":["../../src/core/filler-words.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,6CAA6C;AAC7C,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,mDAAmD;AACnD,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,oLAoBlB,CAAC;AAEX,MAAM,WAAW,oBAAoB;IACnC,wEAAwE;IACxE,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B
|
|
1
|
+
{"version":3,"file":"filler-words.d.ts","sourceRoot":"","sources":["../../src/core/filler-words.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,6CAA6C;AAC7C,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,mDAAmD;AACnD,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,oLAoBlB,CAAC;AAEX,MAAM,WAAW,oBAAoB;IACnC,wEAAwE;IACxE,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B;;;;;;;;;;;OAWG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;;;;;;;;;OAeG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAiBD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,UAAU,EACtB,IAAI,GAAE,oBAAyB,GAC9B,WAAW,EAAE,CAyDf;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,WAAW,EAAE,EACtB,QAAQ,EAAE,MAAM,EAChB,aAAa,SAAO,GACnB,SAAS,EAAE,CAuBb;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,MAAM,EAAE,CASrE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,SAAS,EAAE,EAClB,GAAG,EAAE,MAAM,GACV,KAAK,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAQjD;AA6DD;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,+CAA+C;IAC/C,UAAU,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpD;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,WAAW,CAmBpE"}
|
|
@@ -63,10 +63,13 @@ const NON_AGGRESSIVE_SAFE_LIST = new Set([
|
|
|
63
63
|
* within `mergeGapMs` are merged into a single cut.
|
|
64
64
|
*/
|
|
65
65
|
export function detectFillerRanges(transcript, opts = {}) {
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
// Default 0 ms — trust whisper's word timings. Previously defaulted to
|
|
67
|
+
// 20 ms with the wrong sign, producing 5+ s of unintended speech loss
|
|
68
|
+
// across a 73-cut podcast. See DetectFillersOptions docs.
|
|
69
|
+
const padStart = (opts.paddingStartMs ?? 0) / 1000;
|
|
70
|
+
const padEnd = (opts.paddingEndMs ?? 0) / 1000;
|
|
68
71
|
const mergeGap = (opts.mergeGapMs ?? 150) / 1000;
|
|
69
|
-
const aggressive = opts.aggressiveSingleWords ??
|
|
72
|
+
const aggressive = opts.aggressiveSingleWords ?? false;
|
|
70
73
|
const vocab = (opts.fillers ?? DEFAULT_FILLERS)
|
|
71
74
|
.map((f) => f.trim().toLowerCase())
|
|
72
75
|
.filter((f) => f.length > 0)
|
|
@@ -89,8 +92,17 @@ export function detectFillerRanges(transcript, opts = {}) {
|
|
|
89
92
|
}
|
|
90
93
|
const startWord = words[i];
|
|
91
94
|
const endWord = words[i + match.length - 1];
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
// Padding now SHRINKS the filler range — positive padStart means
|
|
96
|
+
// the cut starts LATER (preserves more of the previous word's tail);
|
|
97
|
+
// positive padEnd means the cut ends EARLIER (preserves more of the
|
|
98
|
+
// next word's head). Old code used `- padStart` / `+ padEnd` which
|
|
99
|
+
// EXPANDED the cut outward, eating ~40 ms per filler.
|
|
100
|
+
const rawStart = startWord.start + padStart;
|
|
101
|
+
const rawEnd = endWord.end - padEnd;
|
|
102
|
+
// Clamp: padding can't make the filler range negative-duration; if
|
|
103
|
+
// it would, keep the original word boundaries unpadded.
|
|
104
|
+
const startSec = rawEnd > rawStart ? Math.max(0, rawStart) : Math.max(0, startWord.start);
|
|
105
|
+
const endSec = rawEnd > rawStart ? rawEnd : endWord.end;
|
|
94
106
|
out.push({
|
|
95
107
|
startSec,
|
|
96
108
|
endSec,
|
|
@@ -135,14 +147,54 @@ export function keepRangesFromFillers(fillers, totalSec, minKeepDurSec = 0.05) {
|
|
|
135
147
|
return keeps;
|
|
136
148
|
}
|
|
137
149
|
/**
|
|
138
|
-
*
|
|
139
|
-
*
|
|
150
|
+
* From a sorted keep-range list, compute the TIMELINE-space cut points.
|
|
151
|
+
*
|
|
152
|
+
* After `import_edl` lands the keep ranges concatenated onto the timeline,
|
|
153
|
+
* each junction between adjacent keeps becomes a visible cut at the
|
|
154
|
+
* timeline-relative timestamp = sum of all earlier keep durations.
|
|
155
|
+
*
|
|
156
|
+
* Example: keeps = [[0,10], [12,25], [28,40]]
|
|
157
|
+
* timeline = [0..10] + [10..23] + [23..35]
|
|
158
|
+
* cuts at = 10s, 23s (two junctions)
|
|
159
|
+
*
|
|
160
|
+
* Why this matters: tools like `add_sfx_to_timeline` and `add_sfx_at_cuts`
|
|
161
|
+
* place SFX at TIMELINE timestamps. If callers pass SOURCE timestamps from
|
|
162
|
+
* the transcript, the drift compounds with every cut and the SFX lands
|
|
163
|
+
* progressively further out of sync (fine for the first ~30s, off by 20+s
|
|
164
|
+
* by the end of a typical podcast). This helper exists to remove that
|
|
165
|
+
* footgun.
|
|
166
|
+
*/
|
|
167
|
+
export function keepRangesToTimelineCuts(keeps) {
|
|
168
|
+
if (keeps.length <= 1)
|
|
169
|
+
return [];
|
|
170
|
+
const cuts = [];
|
|
171
|
+
let acc = 0;
|
|
172
|
+
for (let i = 0; i < keeps.length - 1; i++) {
|
|
173
|
+
acc += keeps[i].endSec - keeps[i].startSec;
|
|
174
|
+
cuts.push(+acc.toFixed(3));
|
|
175
|
+
}
|
|
176
|
+
return cuts;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Frame-align keep ranges. Rounds OUTWARD (start floors, end ceils) so
|
|
180
|
+
* we never DROP speech that lives in a partial frame at the keep's edge.
|
|
181
|
+
*
|
|
182
|
+
* Was: rounds inward (start ceils, end floors). Inward rounding never
|
|
183
|
+
* extends INTO an adjacent filler, but it ALSO loses up to one frame at
|
|
184
|
+
* each end of every keep — ~33 ms at 30 fps, ~17 ms at 60 fps. Across
|
|
185
|
+
* a 73-cut podcast that's 5+ seconds of speech disappearing into
|
|
186
|
+
* sub-frame rounding error.
|
|
187
|
+
*
|
|
188
|
+
* Trade-off: outward rounding can include up to one frame of an adjacent
|
|
189
|
+
* filler at each junction. That's ~33 ms at 30 fps — well within whisper
|
|
190
|
+
* timing noise (typically ±50 ms) and inaudible compared to losing 5 s
|
|
191
|
+
* of actual speech. Worth it.
|
|
140
192
|
*/
|
|
141
193
|
export function keepRangesToFrameRanges(keeps, fps) {
|
|
142
194
|
const out = [];
|
|
143
195
|
for (const k of keeps) {
|
|
144
|
-
const sf = Math.
|
|
145
|
-
const ef = Math.
|
|
196
|
+
const sf = Math.floor(k.startSec * fps);
|
|
197
|
+
const ef = Math.ceil(k.endSec * fps);
|
|
146
198
|
if (ef > sf)
|
|
147
199
|
out.push({ startFrame: sf, endFrame: ef });
|
|
148
200
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filler-words.js","sourceRoot":"","sources":["../../src/core/filler-words.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAqBH;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,IAAI;IACJ,IAAI;IACJ,KAAK;IACL,KAAK;IACL,KAAK;IACL,IAAI;IACJ,IAAI;IACJ,KAAK;IACL,UAAU;IACV,QAAQ;IACR,SAAS;IACT,SAAS;IACT,MAAM;IACN,UAAU;IACV,WAAW;IACX,WAAW;IACX,UAAU;IACV,OAAO;IACP,IAAI;CACI,CAAC;
|
|
1
|
+
{"version":3,"file":"filler-words.js","sourceRoot":"","sources":["../../src/core/filler-words.ts"],"names":[],"mappings":"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"}
|