@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,32 @@
|
|
|
1
|
+
type LogLevel = "I" | "W" | "E";
|
|
2
|
+
/** Default path: `~/.gg/ggeditor.log`. */
|
|
3
|
+
export declare function defaultLogPath(): string;
|
|
4
|
+
/**
|
|
5
|
+
* Initialize logger. Idempotent. Writes a session HEADER line with the
|
|
6
|
+
* fields that don't change across the run, so per-line overhead stays low.
|
|
7
|
+
*/
|
|
8
|
+
export declare function initLogger(filePath?: string, meta?: {
|
|
9
|
+
version?: string;
|
|
10
|
+
provider?: string;
|
|
11
|
+
model?: string;
|
|
12
|
+
host?: string;
|
|
13
|
+
}): void;
|
|
14
|
+
/** Session ID — useful for filtering one run out of an interleaved log. */
|
|
15
|
+
export declare function getSessionId(): string;
|
|
16
|
+
/** Hot-path skip flag for callers that want to avoid building log payloads. */
|
|
17
|
+
export declare function isLoggerActive(): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Write a log line. Format: `HH:MM:SS.mmm L cat msg k=v k=v`.
|
|
20
|
+
*
|
|
21
|
+
* Values that contain spaces or `=` are JSON-quoted; bare otherwise. Long
|
|
22
|
+
* fields are truncated at MAX_FIELD_CHARS with `…(+N)` so the LLM sees the
|
|
23
|
+
* prefix plus a count of elided chars (rather than a silent cliff).
|
|
24
|
+
*/
|
|
25
|
+
export declare function log(level: LogLevel, category: string, message: string, data?: Record<string, unknown>): void;
|
|
26
|
+
export declare const logInfo: (cat: string, msg: string, data?: Record<string, unknown>) => void;
|
|
27
|
+
export declare const logWarn: (cat: string, msg: string, data?: Record<string, unknown>) => void;
|
|
28
|
+
export declare const logError: (cat: string, msg: string, data?: Record<string, unknown>) => void;
|
|
29
|
+
/** Close the log fd. Safe to call multiple times. */
|
|
30
|
+
export declare function closeLogger(): void;
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/core/logger.ts"],"names":[],"mappings":"AAoCA,KAAK,QAAQ,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAQhC,0CAA0C;AAC1C,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AA4BD;;;GAGG;AACH,wBAAgB,UAAU,CACxB,QAAQ,GAAE,MAAyB,EACnC,IAAI,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5E,IAAI,CA8BN;AAED,2EAA2E;AAC3E,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAED;;;;;;GAMG;AACH,wBAAgB,GAAG,CACjB,KAAK,EAAE,QAAQ,EACf,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,IAAI,CAeN;AA0BD,eAAO,MAAM,OAAO,GAAI,KAAK,MAAM,EAAE,KAAK,MAAM,EAAE,OAAO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,SACtD,CAAC;AAC3B,eAAO,MAAM,OAAO,GAAI,KAAK,MAAM,EAAE,KAAK,MAAM,EAAE,OAAO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,SACtD,CAAC;AAC3B,eAAO,MAAM,QAAQ,GAAI,KAAK,MAAM,EAAE,KAAK,MAAM,EAAE,OAAO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,SACvD,CAAC;AAE3B,qDAAqD;AACrD,wBAAgB,WAAW,IAAI,IAAI,CASlC"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gg-editor debug logger — optimized for LLM consumption.
|
|
3
|
+
*
|
|
4
|
+
* Writes to `~/.gg/ggeditor.log`. Append mode across sessions, rotated at
|
|
5
|
+
* MAX_BYTES, one generation kept (`ggeditor.log.1`).
|
|
6
|
+
*
|
|
7
|
+
* Format choices (every byte fed to an LLM matters):
|
|
8
|
+
* - Per-session HEADER carries the rarely-changing fields (date, sid,
|
|
9
|
+
* version, provider, model, pid, platform). Lines below reference them
|
|
10
|
+
* implicitly so we don't repeat ~80 bytes on every line.
|
|
11
|
+
* - Each line: `HH:MM:SS.mmm L cat msg k=v k=v`
|
|
12
|
+
* L — single-letter level (I/W/E). Saves 3-4 bytes/line vs. INFO/WARN/ERROR.
|
|
13
|
+
* cat — short stable category (tool, bridge, bridge.call, host, …).
|
|
14
|
+
* msg — short imperative phrase, no decorative arrows.
|
|
15
|
+
* - Field values JSON-encoded only when they need quoting; bare otherwise.
|
|
16
|
+
* - Args/results truncated at MAX_FIELD_CHARS with `…(+N)` overflow marker
|
|
17
|
+
* so the LLM sees the prefix + how much was elided.
|
|
18
|
+
*
|
|
19
|
+
* Categories:
|
|
20
|
+
* startup CLI launch line + capability probe result
|
|
21
|
+
* bridge Resolve Python bridge spawn/handshake/exit
|
|
22
|
+
* bridge.call Per-method round trips (one line per call: name + dur + ok/err)
|
|
23
|
+
* tool Agent tool calls (one line per call: name + dur + args + result)
|
|
24
|
+
* host Lazy host detection transitions
|
|
25
|
+
* shutdown Clean exit
|
|
26
|
+
* fatal Unhandled errors
|
|
27
|
+
*
|
|
28
|
+
* Filter a single run: `grep " sid=<id>" ~/.gg/ggeditor.log` once you've
|
|
29
|
+
* read the header. Multi-run greps key off the category column.
|
|
30
|
+
*/
|
|
31
|
+
import fs from "node:fs";
|
|
32
|
+
import { mkdirSync } from "node:fs";
|
|
33
|
+
import { homedir } from "node:os";
|
|
34
|
+
import { dirname, join } from "node:path";
|
|
35
|
+
import { randomBytes } from "node:crypto";
|
|
36
|
+
const MAX_BYTES = 10 * 1024 * 1024; // 10 MB
|
|
37
|
+
const MAX_FIELD_CHARS = 200; // Per k=v value cap. Args/results truncated above this.
|
|
38
|
+
let fd = null;
|
|
39
|
+
let sessionId = "";
|
|
40
|
+
/** Default path: `~/.gg/ggeditor.log`. */
|
|
41
|
+
export function defaultLogPath() {
|
|
42
|
+
return join(homedir(), ".gg", "ggeditor.log");
|
|
43
|
+
}
|
|
44
|
+
function rotateIfNeeded(filePath) {
|
|
45
|
+
try {
|
|
46
|
+
const st = fs.statSync(filePath);
|
|
47
|
+
if (st.size < MAX_BYTES)
|
|
48
|
+
return;
|
|
49
|
+
const rotated = `${filePath}.1`;
|
|
50
|
+
try {
|
|
51
|
+
fs.unlinkSync(rotated);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// No prior rotation
|
|
55
|
+
}
|
|
56
|
+
fs.renameSync(filePath, rotated);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Log file doesn't exist yet
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** Pad to 2/3 chars with leading zeros for stable column widths. */
|
|
63
|
+
function pad(n, w) {
|
|
64
|
+
return String(n).padStart(w, "0");
|
|
65
|
+
}
|
|
66
|
+
function timestamp() {
|
|
67
|
+
const d = new Date();
|
|
68
|
+
return `${pad(d.getHours(), 2)}:${pad(d.getMinutes(), 2)}:${pad(d.getSeconds(), 2)}.${pad(d.getMilliseconds(), 3)}`;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Initialize logger. Idempotent. Writes a session HEADER line with the
|
|
72
|
+
* fields that don't change across the run, so per-line overhead stays low.
|
|
73
|
+
*/
|
|
74
|
+
export function initLogger(filePath = defaultLogPath(), meta) {
|
|
75
|
+
if (fd !== null)
|
|
76
|
+
return;
|
|
77
|
+
try {
|
|
78
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// openSync below will surface real failures
|
|
82
|
+
}
|
|
83
|
+
rotateIfNeeded(filePath);
|
|
84
|
+
try {
|
|
85
|
+
fd = fs.openSync(filePath, "a");
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
sessionId = randomBytes(4).toString("hex");
|
|
91
|
+
// Session header — all per-run constants in one line. Bash/awk friendly,
|
|
92
|
+
// human-readable, LLM-parseable. Subsequent lines don't repeat any of this.
|
|
93
|
+
const headerParts = [
|
|
94
|
+
`=== ${new Date().toISOString()}`,
|
|
95
|
+
`sid=${sessionId}`,
|
|
96
|
+
`ggeditor=${meta?.version ?? "?"}`,
|
|
97
|
+
];
|
|
98
|
+
if (meta?.provider)
|
|
99
|
+
headerParts.push(`provider=${meta.provider}`);
|
|
100
|
+
if (meta?.model)
|
|
101
|
+
headerParts.push(`model=${meta.model}`);
|
|
102
|
+
if (meta?.host)
|
|
103
|
+
headerParts.push(`host=${meta.host}`);
|
|
104
|
+
headerParts.push(`pid=${process.pid}`, `platform=${process.platform}`, "===");
|
|
105
|
+
try {
|
|
106
|
+
fs.writeSync(fd, "\n" + headerParts.join(" ") + "\n");
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// Header write failed — proceed; subsequent lines may still land
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/** Session ID — useful for filtering one run out of an interleaved log. */
|
|
113
|
+
export function getSessionId() {
|
|
114
|
+
return sessionId;
|
|
115
|
+
}
|
|
116
|
+
/** Hot-path skip flag for callers that want to avoid building log payloads. */
|
|
117
|
+
export function isLoggerActive() {
|
|
118
|
+
return fd !== null;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Write a log line. Format: `HH:MM:SS.mmm L cat msg k=v k=v`.
|
|
122
|
+
*
|
|
123
|
+
* Values that contain spaces or `=` are JSON-quoted; bare otherwise. Long
|
|
124
|
+
* fields are truncated at MAX_FIELD_CHARS with `…(+N)` so the LLM sees the
|
|
125
|
+
* prefix plus a count of elided chars (rather than a silent cliff).
|
|
126
|
+
*/
|
|
127
|
+
export function log(level, category, message, data) {
|
|
128
|
+
if (fd === null)
|
|
129
|
+
return;
|
|
130
|
+
let line = `${timestamp()} ${level} ${category} ${message}`;
|
|
131
|
+
if (data) {
|
|
132
|
+
for (const [k, v] of Object.entries(data)) {
|
|
133
|
+
if (v === undefined || v === null)
|
|
134
|
+
continue;
|
|
135
|
+
line += ` ${k}=${formatValue(v)}`;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
line += "\n";
|
|
139
|
+
try {
|
|
140
|
+
fs.writeSync(fd, line);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// Don't crash the CLI on a write failure
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function formatValue(v) {
|
|
147
|
+
let s;
|
|
148
|
+
if (typeof v === "string") {
|
|
149
|
+
s = v;
|
|
150
|
+
}
|
|
151
|
+
else if (typeof v === "number" || typeof v === "boolean") {
|
|
152
|
+
return String(v); // numerics/booleans never need quoting and never overflow
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
try {
|
|
156
|
+
s = JSON.stringify(v);
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
s = String(v);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (s.length > MAX_FIELD_CHARS) {
|
|
163
|
+
s = s.slice(0, MAX_FIELD_CHARS) + `…(+${s.length - MAX_FIELD_CHARS})`;
|
|
164
|
+
}
|
|
165
|
+
// Quote when the value has whitespace, `=`, or quotes — so grep/awk on
|
|
166
|
+
// ` k=` boundaries works reliably. Bare otherwise (saves 2 bytes).
|
|
167
|
+
if (/[\s="]/.test(s)) {
|
|
168
|
+
return JSON.stringify(s);
|
|
169
|
+
}
|
|
170
|
+
return s;
|
|
171
|
+
}
|
|
172
|
+
export const logInfo = (cat, msg, data) => log("I", cat, msg, data);
|
|
173
|
+
export const logWarn = (cat, msg, data) => log("W", cat, msg, data);
|
|
174
|
+
export const logError = (cat, msg, data) => log("E", cat, msg, data);
|
|
175
|
+
/** Close the log fd. Safe to call multiple times. */
|
|
176
|
+
export function closeLogger() {
|
|
177
|
+
if (fd === null)
|
|
178
|
+
return;
|
|
179
|
+
log("I", "shutdown", "exit");
|
|
180
|
+
try {
|
|
181
|
+
fs.closeSync(fd);
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
// Ignore
|
|
185
|
+
}
|
|
186
|
+
fd = null;
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/core/logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loop-match filter builder for Shorts.
|
|
3
|
+
*
|
|
4
|
+
* YouTube Shorts auto-replays from the start when the clip ends. A
|
|
5
|
+
* crossfade between the last N ms and the first N ms makes the
|
|
6
|
+
* re-loop feel seamless. Loop rate is a confirmed Shorts ranking
|
|
7
|
+
* signal — see the `youtube-algorithm-primer` skill.
|
|
8
|
+
*
|
|
9
|
+
* This module produces the `-filter_complex` string and the matching
|
|
10
|
+
* `-map` directives. The wrapper tool then runs ffmpeg with libx264 +
|
|
11
|
+
* aac at standard delivery settings.
|
|
12
|
+
*
|
|
13
|
+
* Two modes:
|
|
14
|
+
* - "crossfade" (default): xfade fade between the tail and a copy of
|
|
15
|
+
* the head. Audio: acrossfade.
|
|
16
|
+
* - "jumpcut": no crossfade — simply hard-cut the last cf seconds off
|
|
17
|
+
* so the loop boundary lands right before the head's first frame.
|
|
18
|
+
* Cheaper; safe for very short clips where any crossfade would eat
|
|
19
|
+
* too much of the visible content.
|
|
20
|
+
*/
|
|
21
|
+
export type LoopMatchMethod = "crossfade" | "jumpcut";
|
|
22
|
+
export interface LoopMatchPlan {
|
|
23
|
+
/** Effective crossfade duration after clamping. */
|
|
24
|
+
crossfadeSec: number;
|
|
25
|
+
/** Final output duration after the filter applies. */
|
|
26
|
+
outDurationSec: number;
|
|
27
|
+
method: LoopMatchMethod;
|
|
28
|
+
/** ffmpeg -filter_complex value. */
|
|
29
|
+
filter: string;
|
|
30
|
+
/** -map directives in order. */
|
|
31
|
+
maps: string[];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Build the filter graph for a loop-match pass.
|
|
35
|
+
*
|
|
36
|
+
* crossfade mode plan:
|
|
37
|
+
* [0:v] split=2 [main][tail_src];
|
|
38
|
+
* [main] trim=0:T-cf, setpts=PTS-STARTPTS [vmain];
|
|
39
|
+
* [tail_src] trim=T-cf:T, setpts=PTS-STARTPTS [tail];
|
|
40
|
+
* [0:v] trim=0:cf, setpts=PTS-STARTPTS [head];
|
|
41
|
+
* [tail][head] xfade=transition=fade:duration=cf:offset=0 [xfade];
|
|
42
|
+
* [vmain][xfade] concat=n=2:v=1:a=0 [vout]
|
|
43
|
+
* ── audio ──
|
|
44
|
+
* [0:a] asplit=2 [amain][atail_src];
|
|
45
|
+
* [amain] atrim=0:T-cf, asetpts=PTS-STARTPTS [aout1];
|
|
46
|
+
* [atail_src] atrim=T-cf:T, asetpts=PTS-STARTPTS [atail];
|
|
47
|
+
* [0:a] atrim=0:cf, asetpts=PTS-STARTPTS [ahead];
|
|
48
|
+
* [atail][ahead] acrossfade=d=cf [axfade];
|
|
49
|
+
* [aout1][axfade] concat=n=2:v=0:a=1 [aout]
|
|
50
|
+
*
|
|
51
|
+
* Final length = (T - cf) + cf = T. Duration unchanged; only the last
|
|
52
|
+
* cf seconds become a crossfade into the head.
|
|
53
|
+
*
|
|
54
|
+
* Pure — easy to unit-test the filter string. Throws on invalid input.
|
|
55
|
+
*/
|
|
56
|
+
export declare function buildLoopMatchFilter(durationSec: number, crossfadeSec: number, method?: LoopMatchMethod): LoopMatchPlan;
|
|
57
|
+
//# sourceMappingURL=loop-match.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loop-match.d.ts","sourceRoot":"","sources":["../../src/core/loop-match.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,SAAS,CAAC;AAEtD,MAAM,WAAW,aAAa;IAC5B,mDAAmD;IACnD,YAAY,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,eAAe,CAAC;IACxB,oCAAoC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,MAAM,GAAE,eAA6B,GACpC,aAAa,CAqDf"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loop-match filter builder for Shorts.
|
|
3
|
+
*
|
|
4
|
+
* YouTube Shorts auto-replays from the start when the clip ends. A
|
|
5
|
+
* crossfade between the last N ms and the first N ms makes the
|
|
6
|
+
* re-loop feel seamless. Loop rate is a confirmed Shorts ranking
|
|
7
|
+
* signal — see the `youtube-algorithm-primer` skill.
|
|
8
|
+
*
|
|
9
|
+
* This module produces the `-filter_complex` string and the matching
|
|
10
|
+
* `-map` directives. The wrapper tool then runs ffmpeg with libx264 +
|
|
11
|
+
* aac at standard delivery settings.
|
|
12
|
+
*
|
|
13
|
+
* Two modes:
|
|
14
|
+
* - "crossfade" (default): xfade fade between the tail and a copy of
|
|
15
|
+
* the head. Audio: acrossfade.
|
|
16
|
+
* - "jumpcut": no crossfade — simply hard-cut the last cf seconds off
|
|
17
|
+
* so the loop boundary lands right before the head's first frame.
|
|
18
|
+
* Cheaper; safe for very short clips where any crossfade would eat
|
|
19
|
+
* too much of the visible content.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Build the filter graph for a loop-match pass.
|
|
23
|
+
*
|
|
24
|
+
* crossfade mode plan:
|
|
25
|
+
* [0:v] split=2 [main][tail_src];
|
|
26
|
+
* [main] trim=0:T-cf, setpts=PTS-STARTPTS [vmain];
|
|
27
|
+
* [tail_src] trim=T-cf:T, setpts=PTS-STARTPTS [tail];
|
|
28
|
+
* [0:v] trim=0:cf, setpts=PTS-STARTPTS [head];
|
|
29
|
+
* [tail][head] xfade=transition=fade:duration=cf:offset=0 [xfade];
|
|
30
|
+
* [vmain][xfade] concat=n=2:v=1:a=0 [vout]
|
|
31
|
+
* ── audio ──
|
|
32
|
+
* [0:a] asplit=2 [amain][atail_src];
|
|
33
|
+
* [amain] atrim=0:T-cf, asetpts=PTS-STARTPTS [aout1];
|
|
34
|
+
* [atail_src] atrim=T-cf:T, asetpts=PTS-STARTPTS [atail];
|
|
35
|
+
* [0:a] atrim=0:cf, asetpts=PTS-STARTPTS [ahead];
|
|
36
|
+
* [atail][ahead] acrossfade=d=cf [axfade];
|
|
37
|
+
* [aout1][axfade] concat=n=2:v=0:a=1 [aout]
|
|
38
|
+
*
|
|
39
|
+
* Final length = (T - cf) + cf = T. Duration unchanged; only the last
|
|
40
|
+
* cf seconds become a crossfade into the head.
|
|
41
|
+
*
|
|
42
|
+
* Pure — easy to unit-test the filter string. Throws on invalid input.
|
|
43
|
+
*/
|
|
44
|
+
export function buildLoopMatchFilter(durationSec, crossfadeSec, method = "crossfade") {
|
|
45
|
+
if (!Number.isFinite(durationSec) || durationSec <= 0) {
|
|
46
|
+
throw new Error("durationSec must be > 0");
|
|
47
|
+
}
|
|
48
|
+
if (!Number.isFinite(crossfadeSec) || crossfadeSec <= 0) {
|
|
49
|
+
throw new Error("crossfadeSec must be > 0");
|
|
50
|
+
}
|
|
51
|
+
// Crossfade can't be ≥ half the clip — there'd be no "main" body left.
|
|
52
|
+
const maxCf = durationSec / 2;
|
|
53
|
+
const cf = Math.min(crossfadeSec, +(maxCf - 0.05).toFixed(3));
|
|
54
|
+
if (cf <= 0) {
|
|
55
|
+
throw new Error(`clip too short (${durationSec.toFixed(2)}s) for any crossfade; pass crossfadeSec ≤ ${maxCf.toFixed(2)}`);
|
|
56
|
+
}
|
|
57
|
+
const T = +durationSec.toFixed(3);
|
|
58
|
+
const tailStart = +(T - cf).toFixed(3);
|
|
59
|
+
if (method === "jumpcut") {
|
|
60
|
+
// Just trim the last cf seconds. No xfade. Output is shorter.
|
|
61
|
+
const filter = `[0:v]trim=0:${tailStart},setpts=PTS-STARTPTS[vout];` +
|
|
62
|
+
`[0:a]atrim=0:${tailStart},asetpts=PTS-STARTPTS[aout]`;
|
|
63
|
+
return {
|
|
64
|
+
crossfadeSec: cf,
|
|
65
|
+
outDurationSec: tailStart,
|
|
66
|
+
method,
|
|
67
|
+
filter,
|
|
68
|
+
maps: ["[vout]", "[aout]"],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
const filter = `[0:v]split=2[main][tail_src];` +
|
|
72
|
+
`[main]trim=0:${tailStart},setpts=PTS-STARTPTS[vmain];` +
|
|
73
|
+
`[tail_src]trim=${tailStart}:${T},setpts=PTS-STARTPTS[tail];` +
|
|
74
|
+
`[0:v]trim=0:${cf},setpts=PTS-STARTPTS[head];` +
|
|
75
|
+
`[tail][head]xfade=transition=fade:duration=${cf}:offset=0[xf];` +
|
|
76
|
+
`[vmain][xf]concat=n=2:v=1:a=0[vout];` +
|
|
77
|
+
`[0:a]asplit=2[amain][atail_src];` +
|
|
78
|
+
`[amain]atrim=0:${tailStart},asetpts=PTS-STARTPTS[aout1];` +
|
|
79
|
+
`[atail_src]atrim=${tailStart}:${T},asetpts=PTS-STARTPTS[atail];` +
|
|
80
|
+
`[0:a]atrim=0:${cf},asetpts=PTS-STARTPTS[ahead];` +
|
|
81
|
+
`[atail][ahead]acrossfade=d=${cf}[axf];` +
|
|
82
|
+
`[aout1][axf]concat=n=2:v=0:a=1[aout]`;
|
|
83
|
+
return {
|
|
84
|
+
crossfadeSec: cf,
|
|
85
|
+
outDurationSec: T,
|
|
86
|
+
method,
|
|
87
|
+
filter,
|
|
88
|
+
maps: ["[vout]", "[aout]"],
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=loop-match.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loop-match.js","sourceRoot":"","sources":["../../src/core/loop-match.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAgBH;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,oBAAoB,CAClC,WAAmB,EACnB,YAAoB,EACpB,SAA0B,WAAW;IAErC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,WAAW,IAAI,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,YAAY,IAAI,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAC9C,CAAC;IACD,uEAAuE;IACvE,MAAM,KAAK,GAAG,WAAW,GAAG,CAAC,CAAC;IAC9B,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9D,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,mBAAmB,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,6CAA6C,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CACzG,CAAC;IACJ,CAAC;IACD,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAClC,MAAM,SAAS,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAEvC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,8DAA8D;QAC9D,MAAM,MAAM,GACV,eAAe,SAAS,6BAA6B;YACrD,gBAAgB,SAAS,6BAA6B,CAAC;QACzD,OAAO;YACL,YAAY,EAAE,EAAE;YAChB,cAAc,EAAE,SAAS;YACzB,MAAM;YACN,MAAM;YACN,IAAI,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC;SAC3B,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GACV,+BAA+B;QAC/B,gBAAgB,SAAS,8BAA8B;QACvD,kBAAkB,SAAS,IAAI,CAAC,6BAA6B;QAC7D,eAAe,EAAE,6BAA6B;QAC9C,8CAA8C,EAAE,gBAAgB;QAChE,sCAAsC;QACtC,kCAAkC;QAClC,kBAAkB,SAAS,+BAA+B;QAC1D,oBAAoB,SAAS,IAAI,CAAC,+BAA+B;QACjE,gBAAgB,EAAE,+BAA+B;QACjD,8BAA8B,EAAE,QAAQ;QACxC,sCAAsC,CAAC;IAEzC,OAAO;QACL,YAAY,EAAE,EAAE;QAChB,cAAc,EAAE,CAAC;QACjB,MAAM;QACN,MAAM;QACN,IAAI,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC;KAC3B,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ffmpeg.d.ts","sourceRoot":"","sources":["../../../src/core/media/ffmpeg.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ffmpeg.d.ts","sourceRoot":"","sources":["../../../src/core/media/ffmpeg.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AAEH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,WAAW,IAAI,OAAO,CAGrC;AAED,wBAAgB,YAAY,IAAI,OAAO,CAGtC;AAED,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,WAAW,CAAA;CAAO,GAClC,OAAO,CAAC,YAAY,CAAC,CAuBvB;AAED,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CA4C9D"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn, spawnSync } from "node:child_process";
|
|
2
|
+
import { wireChildAbort } from "../child-abort.js";
|
|
2
3
|
export function checkFfmpeg() {
|
|
3
4
|
const r = spawnSync("ffmpeg", ["-version"], { encoding: "utf8" });
|
|
4
5
|
return r.status === 0;
|
|
@@ -16,9 +17,19 @@ export function runFfmpeg(args, opts = {}) {
|
|
|
16
17
|
let stderr = "";
|
|
17
18
|
child.stdout.on("data", (d) => (stdout += d.toString()));
|
|
18
19
|
child.stderr.on("data", (d) => (stderr += d.toString()));
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
// Robust abort: SIGTERM → SIGKILL after 1.5s. ffmpeg respects SIGTERM
|
|
21
|
+
// quickly on its read loop; the SIGKILL backstop covers wedge cases.
|
|
22
|
+
// We resolve with a non-zero code on abort instead of rejecting because
|
|
23
|
+
// ffmpeg callers check `r.code !== 0` and surface a tailored error.
|
|
24
|
+
const cleanup = wireChildAbort(opts.signal, child);
|
|
25
|
+
child.on("error", (e) => {
|
|
26
|
+
cleanup();
|
|
27
|
+
reject(e);
|
|
28
|
+
});
|
|
29
|
+
child.on("close", (code) => {
|
|
30
|
+
cleanup();
|
|
31
|
+
resolve({ code: code ?? 1, stdout, stderr });
|
|
32
|
+
});
|
|
22
33
|
});
|
|
23
34
|
}
|
|
24
35
|
export function probeMedia(filePath) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ffmpeg.js","sourceRoot":"","sources":["../../../src/core/media/ffmpeg.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"ffmpeg.js","sourceRoot":"","sources":["../../../src/core/media/ffmpeg.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-format render presets — the "render every platform at once" matrix.
|
|
3
|
+
*
|
|
4
|
+
* Pure dispatcher for `render_multi_format`: given a source resolution and a
|
|
5
|
+
* preset name, produces the ffmpeg `-vf` filter graph plus the target W×H.
|
|
6
|
+
* Spawning ffmpeg lives in the tool layer so this module stays trivially
|
|
7
|
+
* unit-testable.
|
|
8
|
+
*
|
|
9
|
+
* Two transform modes:
|
|
10
|
+
*
|
|
11
|
+
* - "scale-pad" — letterbox/pillarbox the source into the target frame
|
|
12
|
+
* without cropping. Used for 16:9-into-16:9 (different
|
|
13
|
+
* resolution) and any time we want to preserve every
|
|
14
|
+
* pixel of the source.
|
|
15
|
+
*
|
|
16
|
+
* - "centre-crop" — crop the source to the target aspect, then scale to
|
|
17
|
+
* target resolution. The dumb / mechanical version of
|
|
18
|
+
* content-aware reframing — for face-tracked re-cropping
|
|
19
|
+
* the agent should pipe the source through `face_reframe`
|
|
20
|
+
* or Resolve `smart_reframe` BEFORE calling this tool.
|
|
21
|
+
*/
|
|
22
|
+
export type MultiFormat = "youtube-1080p" | "shorts-9x16" | "reels-9x16" | "tiktok-9x16" | "square-1x1" | "instagram-4x5" | "twitter-16x9";
|
|
23
|
+
export declare const MULTI_FORMATS: readonly MultiFormat[];
|
|
24
|
+
export type MultiFormatTransform = "scale-pad" | "centre-crop";
|
|
25
|
+
interface PresetSpec {
|
|
26
|
+
width: number;
|
|
27
|
+
height: number;
|
|
28
|
+
transform: MultiFormatTransform;
|
|
29
|
+
}
|
|
30
|
+
export declare function multiFormatSpec(format: MultiFormat): PresetSpec;
|
|
31
|
+
export interface BuildRenderFilterResult {
|
|
32
|
+
targetW: number;
|
|
33
|
+
targetH: number;
|
|
34
|
+
vf: string;
|
|
35
|
+
transform: MultiFormatTransform;
|
|
36
|
+
}
|
|
37
|
+
export interface BuildRenderFilterOptions {
|
|
38
|
+
/**
|
|
39
|
+
* If true, treat the source as already cropped to the target aspect (e.g.
|
|
40
|
+
* via face_reframe / smart_reframe upstream). Forces a non-cropping
|
|
41
|
+
* scale-pad regardless of the preset's default transform.
|
|
42
|
+
*/
|
|
43
|
+
faceTracked?: boolean;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Build the ffmpeg `-vf` filter for a single (source, preset) pair.
|
|
47
|
+
*
|
|
48
|
+
* Pure function — no side effects, no I/O. Used by `render_multi_format`
|
|
49
|
+
* to compose ffmpeg args, and by the unit tests to lock the filter math.
|
|
50
|
+
*
|
|
51
|
+
* Math notes for centre-crop:
|
|
52
|
+
*
|
|
53
|
+
* srcAR = srcW/srcH, tgtAR = Wt/Ht. The crop keeps the larger axis full.
|
|
54
|
+
*
|
|
55
|
+
* - srcAR > tgtAR ⇒ source is wider than target. Crop width.
|
|
56
|
+
* cropW = ih * Wt/Ht ; cropH = ih ; x = (iw - cropW)/2 ; y = 0
|
|
57
|
+
* - srcAR < tgtAR ⇒ source is taller than target. Crop height.
|
|
58
|
+
* cropW = iw ; cropH = iw * Ht/Wt ; x = 0 ; y = (ih - cropH)/2
|
|
59
|
+
* - srcAR == tgtAR ⇒ no crop needed; emit `scale=Wt:Ht` only.
|
|
60
|
+
*
|
|
61
|
+
* Float crop dimensions are passed through to ffmpeg (which accepts them
|
|
62
|
+
* and rounds internally); the trailing `scale=Wt:Ht` enforces the final
|
|
63
|
+
* even-dimension target so x264 stays happy.
|
|
64
|
+
*/
|
|
65
|
+
export declare function buildRenderFilter(srcW: number, srcH: number, format: MultiFormat, opts?: BuildRenderFilterOptions): BuildRenderFilterResult;
|
|
66
|
+
export {};
|
|
67
|
+
//# sourceMappingURL=multi-format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multi-format.d.ts","sourceRoot":"","sources":["../../src/core/multi-format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,MAAM,WAAW,GACnB,eAAe,GACf,aAAa,GACb,YAAY,GACZ,aAAa,GACb,YAAY,GACZ,eAAe,GACf,cAAc,CAAC;AAEnB,eAAO,MAAM,aAAa,EAAE,SAAS,WAAW,EAQtC,CAAC;AAEX,MAAM,MAAM,oBAAoB,GAAG,WAAW,GAAG,aAAa,CAAC;AAE/D,UAAU,UAAU;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,oBAAoB,CAAC;CACjC;AAmBD,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,UAAU,CAQ/D;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,oBAAoB,CAAC;CACjC;AAED,MAAM,WAAW,wBAAwB;IACvC;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,WAAW,EACnB,IAAI,GAAE,wBAA6B,GAClC,uBAAuB,CA0CzB"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-format render presets — the "render every platform at once" matrix.
|
|
3
|
+
*
|
|
4
|
+
* Pure dispatcher for `render_multi_format`: given a source resolution and a
|
|
5
|
+
* preset name, produces the ffmpeg `-vf` filter graph plus the target W×H.
|
|
6
|
+
* Spawning ffmpeg lives in the tool layer so this module stays trivially
|
|
7
|
+
* unit-testable.
|
|
8
|
+
*
|
|
9
|
+
* Two transform modes:
|
|
10
|
+
*
|
|
11
|
+
* - "scale-pad" — letterbox/pillarbox the source into the target frame
|
|
12
|
+
* without cropping. Used for 16:9-into-16:9 (different
|
|
13
|
+
* resolution) and any time we want to preserve every
|
|
14
|
+
* pixel of the source.
|
|
15
|
+
*
|
|
16
|
+
* - "centre-crop" — crop the source to the target aspect, then scale to
|
|
17
|
+
* target resolution. The dumb / mechanical version of
|
|
18
|
+
* content-aware reframing — for face-tracked re-cropping
|
|
19
|
+
* the agent should pipe the source through `face_reframe`
|
|
20
|
+
* or Resolve `smart_reframe` BEFORE calling this tool.
|
|
21
|
+
*/
|
|
22
|
+
export const MULTI_FORMATS = [
|
|
23
|
+
"youtube-1080p",
|
|
24
|
+
"shorts-9x16",
|
|
25
|
+
"reels-9x16",
|
|
26
|
+
"tiktok-9x16",
|
|
27
|
+
"square-1x1",
|
|
28
|
+
"instagram-4x5",
|
|
29
|
+
"twitter-16x9",
|
|
30
|
+
];
|
|
31
|
+
/**
|
|
32
|
+
* Per-preset target resolution + default transform.
|
|
33
|
+
*
|
|
34
|
+
* Resolutions are platform-canonical: 1080p horizontal, 1080×1920 vertical,
|
|
35
|
+
* 1080-square IG, 1080×1350 IG-portrait, 720p Twitter (their feed downsamples
|
|
36
|
+
* anything taller anyway).
|
|
37
|
+
*/
|
|
38
|
+
const PRESETS = {
|
|
39
|
+
"youtube-1080p": { width: 1920, height: 1080, transform: "scale-pad" },
|
|
40
|
+
"shorts-9x16": { width: 1080, height: 1920, transform: "centre-crop" },
|
|
41
|
+
"reels-9x16": { width: 1080, height: 1920, transform: "centre-crop" },
|
|
42
|
+
"tiktok-9x16": { width: 1080, height: 1920, transform: "centre-crop" },
|
|
43
|
+
"square-1x1": { width: 1080, height: 1080, transform: "centre-crop" },
|
|
44
|
+
"instagram-4x5": { width: 1080, height: 1350, transform: "centre-crop" },
|
|
45
|
+
"twitter-16x9": { width: 1280, height: 720, transform: "scale-pad" },
|
|
46
|
+
};
|
|
47
|
+
export function multiFormatSpec(format) {
|
|
48
|
+
const spec = PRESETS[format];
|
|
49
|
+
if (!spec) {
|
|
50
|
+
throw new Error(`unknown multi-format preset: ${format}; valid: ${Object.keys(PRESETS).join(", ")}`);
|
|
51
|
+
}
|
|
52
|
+
return spec;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Build the ffmpeg `-vf` filter for a single (source, preset) pair.
|
|
56
|
+
*
|
|
57
|
+
* Pure function — no side effects, no I/O. Used by `render_multi_format`
|
|
58
|
+
* to compose ffmpeg args, and by the unit tests to lock the filter math.
|
|
59
|
+
*
|
|
60
|
+
* Math notes for centre-crop:
|
|
61
|
+
*
|
|
62
|
+
* srcAR = srcW/srcH, tgtAR = Wt/Ht. The crop keeps the larger axis full.
|
|
63
|
+
*
|
|
64
|
+
* - srcAR > tgtAR ⇒ source is wider than target. Crop width.
|
|
65
|
+
* cropW = ih * Wt/Ht ; cropH = ih ; x = (iw - cropW)/2 ; y = 0
|
|
66
|
+
* - srcAR < tgtAR ⇒ source is taller than target. Crop height.
|
|
67
|
+
* cropW = iw ; cropH = iw * Ht/Wt ; x = 0 ; y = (ih - cropH)/2
|
|
68
|
+
* - srcAR == tgtAR ⇒ no crop needed; emit `scale=Wt:Ht` only.
|
|
69
|
+
*
|
|
70
|
+
* Float crop dimensions are passed through to ffmpeg (which accepts them
|
|
71
|
+
* and rounds internally); the trailing `scale=Wt:Ht` enforces the final
|
|
72
|
+
* even-dimension target so x264 stays happy.
|
|
73
|
+
*/
|
|
74
|
+
export function buildRenderFilter(srcW, srcH, format, opts = {}) {
|
|
75
|
+
if (!Number.isFinite(srcW) || srcW <= 0 || !Number.isFinite(srcH) || srcH <= 0) {
|
|
76
|
+
throw new Error(`invalid source dimensions: ${srcW}x${srcH}`);
|
|
77
|
+
}
|
|
78
|
+
const spec = multiFormatSpec(format);
|
|
79
|
+
const targetW = spec.width;
|
|
80
|
+
const targetH = spec.height;
|
|
81
|
+
const transform = opts.faceTracked ? "scale-pad" : spec.transform;
|
|
82
|
+
if (transform === "scale-pad") {
|
|
83
|
+
const vf = `scale=${targetW}:${targetH}:force_original_aspect_ratio=decrease,` +
|
|
84
|
+
`pad=${targetW}:${targetH}:(ow-iw)/2:(oh-ih)/2:color=black`;
|
|
85
|
+
return { targetW, targetH, vf, transform };
|
|
86
|
+
}
|
|
87
|
+
// centre-crop
|
|
88
|
+
const srcAR = srcW / srcH;
|
|
89
|
+
const tgtAR = targetW / targetH;
|
|
90
|
+
// Use a tiny tolerance so 1920x1080 vs perfectly-equal 16:9 sources round
|
|
91
|
+
// to the no-crop branch; prevents emitting `crop=iw:ih:0:0`.
|
|
92
|
+
const eps = 1e-6;
|
|
93
|
+
if (Math.abs(srcAR - tgtAR) < eps) {
|
|
94
|
+
return {
|
|
95
|
+
targetW,
|
|
96
|
+
targetH,
|
|
97
|
+
vf: `scale=${targetW}:${targetH}`,
|
|
98
|
+
transform,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (srcAR > tgtAR) {
|
|
102
|
+
// crop width
|
|
103
|
+
const cropW = formatNum((srcH * targetW) / targetH);
|
|
104
|
+
const x = formatNum((srcW - (srcH * targetW) / targetH) / 2);
|
|
105
|
+
const vf = `crop=${cropW}:${srcH}:${x}:0,scale=${targetW}:${targetH}`;
|
|
106
|
+
return { targetW, targetH, vf, transform };
|
|
107
|
+
}
|
|
108
|
+
// srcAR < tgtAR — crop height
|
|
109
|
+
const cropH = formatNum((srcW * targetH) / targetW);
|
|
110
|
+
const y = formatNum((srcH - (srcW * targetH) / targetW) / 2);
|
|
111
|
+
const vf = `crop=${srcW}:${cropH}:0:${y},scale=${targetW}:${targetH}`;
|
|
112
|
+
return { targetW, targetH, vf, transform };
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Format a number for inclusion in an ffmpeg filter expression. Trims any
|
|
116
|
+
* `.0` tail (e.g. 1080.0 → "1080") so integer paths remain integer-looking
|
|
117
|
+
* in the filter graph; otherwise emits up to 2 decimals.
|
|
118
|
+
*/
|
|
119
|
+
function formatNum(n) {
|
|
120
|
+
if (Number.isInteger(n))
|
|
121
|
+
return String(n);
|
|
122
|
+
// Two decimals is enough for any practical (W, H) from canonical sources;
|
|
123
|
+
// the trailing `scale` rounds to the final integer target.
|
|
124
|
+
const rounded = Math.round(n * 100) / 100;
|
|
125
|
+
return Number.isInteger(rounded) ? String(rounded) : String(rounded);
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=multi-format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multi-format.js","sourceRoot":"","sources":["../../src/core/multi-format.ts"],"names":[],"mappings":"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"}
|