Files
Video-Processing-Scripts/video-processing-scripts/SKILL.md
T

218 lines
19 KiB
Markdown

---
name: video-processing-scripts
description: Operates a collection of PowerShell 7 scripts that process video and image files with ffmpeg, ffprobe, rife-ncnn-vulkan and upscayl-bin. Covers clipping frame ranges, scene-based clip extraction, frame extraction to PNG, frame counts and video metadata, cropping and resizing to standard, LTX-2, Kling and Wan resolutions, pillar-boxing, side-by-side comparison, turnaround sheets, subtitle merging and subtitle extraction, audio extraction, AI image upscaling and RIFE frame interpolation. Use this skill when the user asks about video clipping, frame extraction, scene detection, cropping, upscaling, frame interpolation, subtitles, ffmpeg or RIFE workflows, the PowerShell video scripts, or preparing clips for Wan2GP or LTX models.
---
# Video Processing PowerShell Scripts
A collection of PowerShell 7 scripts that wrap ffmpeg and ffprobe for video and image processing. Some scripts also call rife-ncnn-vulkan (frame interpolation) and upscayl-bin (AI upscaling).
The per-script pages sit in the same folder as this file. Load a page only when you need its details, using the links in the catalog below.
## Prerequisites
- **Platform**: Windows 11 only. The scripts are tested on Windows 11 and nowhere else. Linux and WSL are not supported. Run them with PowerShell 7 for Windows (`pwsh.exe`).
- **PowerShell 7+** (`pwsh`). Required by every script.
- **Core tools**: **ffmpeg** and **ffprobe** on `PATH`. Most scripts need both. [Stitch-InBetween.ps1](Stitch-InBetween.md) needs only ffmpeg. [Get-FramesCount.ps1](Get-FramesCount.md), [Get-VideoInfo.ps1](Get-VideoInfo.md) and [Create-FullSubtitle.ps1](Create-FullSubtitle.md) need only ffprobe.
- **Optional tools**, needed only by specific scripts:
- **rife-ncnn-vulkan** on `PATH`, with `RIFE_HOME` set. Required by [Stitch-InBetween.ps1](Stitch-InBetween.md). See the [RIFE installation guide](RIFE.md).
- **upscayl-bin** on `PATH`, with `UPSCAYL_HOME` set. Required by [Clone-Image.ps1](Clone-Image.md) when `-Upscale` is used. See the [Upscalers installation guide](Upscalers.md).
- **Tested with** ffmpeg n8.0.1-40 (GPL build, Windows 11). Do not claim that other versions work.
### Encoders and filters required
These encoders are used by the scripts. Check the ones a task needs before running it; see [Check the Environment](#check-the-environment).
| Encoder or filter | Used by | Needed for |
| --- | --- | --- |
| `libx264` | [Clone-Video.ps1](Clone-Video.md), [Compare-Videos.ps1](Compare-Videos.md), [Create-PillarBox.ps1](Create-PillarBox.md), [Create-ShortClip.ps1](Create-ShortClip.md), [Crop-Clips.ps1](Crop-Clips.md) | H.264 video in MP4 and MKV output |
| `libx265` | [Create-ShortClip.ps1](Create-ShortClip.md) | Output of HEVC (H.265) sources |
| `libvpx-vp9` | [Clone-Video.ps1](Clone-Video.md) | Video in WebM output |
| `libopus` | [Clone-Video.ps1](Clone-Video.md) | Audio in WebM output |
| `libmp3lame` | [Get-VideoAudio.ps1](Get-VideoAudio.md) | MP3 extraction (`-MP3`) |
| `libaom-av1` | [Clone-Image.ps1](Clone-Image.md), [Create-Turnaround.ps1](Create-Turnaround.md) | AVIF images |
| `libwebp` | [Clone-Image.ps1](Clone-Image.md), [Create-Turnaround.ps1](Create-Turnaround.md) | WebP images |
The filters used (for example `scale`, `crop`, `pad`, `hstack`, `vstack`, `select`, `concat`) are built into ffmpeg. No script needs `libass` or `libfreetype`.
## Environment Variables
| Variable | Purpose | Accepted values | Required by |
| --- | --- | --- | --- |
| `RIFE_HOME` | Install folder of rife-ncnn-vulkan (the same folder as its `PATH` entry). Models are read from `%RIFE_HOME%\rife-v4.26`. | Folder path, e.g. `C:\tools\rife-ncnn-vulkan\` | [Stitch-InBetween.ps1](Stitch-InBetween.md) |
| `UPSCAYL_HOME` | Install folder of upscayl-bin (the same folder as its `PATH` entry). Models are read from `%UPSCAYL_HOME%\models`. | Folder path | [Clone-Image.ps1](Clone-Image.md) with `-Upscale` |
| `NCNN_GPU_ID` | GPU device passed to the `-g` flag of rife-ncnn-vulkan and upscayl-bin. | `0`, `1`, `2`... (GPU index), `-1` (CPU for rife-ncnn-vulkan; upscayl-bin gets no `-g` flag and uses its default device) | [Stitch-InBetween.ps1](Stitch-InBetween.md), [Clone-Image.ps1](Clone-Image.md) with `-Upscale` |
These must be persistent user or system variables. An agent shell may not inherit variables set only in an interactive session.
## Check the Environment
Run the core checks once before the first use in a session:
```powershell
pwsh --version
ffmpeg -version
ffprobe -version
```
Run the task-specific checks only when the task uses the script that needs them:
- Stitch-InBetween.ps1: `Get-Command rife-ncnn-vulkan`, `$env:RIFE_HOME` and `$env:NCNN_GPU_ID`.
- Clone-Image.ps1 with `-Upscale`: `Get-Command upscayl-bin`, `$env:UPSCAYL_HOME` and `$env:NCNN_GPU_ID`.
- Encoder checks, only for the task that needs the encoder (see the table above). For example, before an MP3 extraction: `ffmpeg -hide_banner -encoders | Select-String libmp3lame`. A missing encoder stops only the task that needs it.
If a core check fails, or a check needed by the current task fails, **stop**. Tell the user exactly what is missing and point to [index.md](index.md) (prerequisites), [RIFE.md](RIFE.md) or [Upscalers.md](Upscalers.md). Do not install software or change environment variables without the user's approval. A missing optional tool must not block unrelated tasks such as clipping.
## Installation and Location
- Git: `git clone https://git.gurusushi.dyndns.org/guru/Video-Processing-Scripts.git` (repository `guru/Video-Processing-Scripts`).
- SVN: `public-svn`, project `Video Processing Scripts`.
- The script folder should be on `PATH` so that `Get-Command` finds the scripts and the `.cmd` wrappers work. This does not make `pwsh -File <bare name>` work. See [How to Run the Scripts](#how-to-run-the-scripts).
- `Update-Video-Processing-Scripts.cmd` updates a Git copy to the latest version. Run it only with the user's approval.
## How to Run the Scripts
- Always call a script as `pwsh -NoProfile -File <full path to script>.ps1 <parameters>`. `pwsh -File` does not search `PATH`, so a bare name fails from any other folder. Find the script folder once with `Split-Path (Get-Command Create-Clip.ps1).Source`.
- In the examples below, `.\` means the script folder. From any other folder, replace `.\` with the full script path.
- The `.cmd` wrappers are Windows-only convenience files. Do not use them in agent commands.
- Quote every path that contains spaces.
- Paths can be absolute or relative to the current directory.
```powershell
pwsh -NoProfile -File .\Create-Clip.ps1 -Video "My Video.mp4" -Frames "100-200"
```
From any other folder, use the full script path and an absolute video path:
```powershell
pwsh -NoProfile -File "C:\path\to\Video Processing Scripts\Create-Clip.ps1" -Video "D:\clips\P01.mp4" -Frames "100-200"
```
## Conventions
- Frame indices are **0-based**: the first frame is `0`.
- **`-Frames` syntax** for [Clone-Video.ps1](Clone-Video.md), [Create-Clip.ps1](Create-Clip.md) and [Get-FramesRange.ps1](Get-FramesRange.md):
| Form | Meaning |
| --- | --- |
| `start-end` | Frames from `start` to `end`, e.g. `100-200`. Both ends must be inside the video. A reversed range or an end past the last frame is rejected with exit code `1`. |
| `N` | Frames 0 to `N`, not only frame `N`. A number past the last frame is clamped to the last frame. |
| `-N` | The last `N` frames. `-1` is the last frame. |
| `%` | The last frame index. Alone it selects only the last frame. In a range, e.g. `10-%`, it is the end of the range. |
| `-%` | Not supported. Rejected with exit code `1`. |
| `all` | All frames, same as `0-%` |
- [Create-Scenes.ps1](Create-Scenes.md) takes no frame range. It finds scene boundaries itself.
- **`-Frame` syntax** for [Get-Frame.ps1](Get-Frame.md) and [Get-LastFrame.ps1](Get-LastFrame.md): a single frame index. Negative values count from the end (`-1` is the last frame). [Get-Frame.ps1](Get-Frame.md) clamps values to the valid range: a positive value past the last frame gives the last frame, and a negative value before the first frame gives frame 0. [Get-LastFrame.ps1](Get-LastFrame.md) clamps the same way.
- If the extension is omitted, `.mp4`, `.webm` and `.mkv` are tried in that order by [Clone-Video.ps1](Clone-Video.md), [Create-Clip.ps1](Create-Clip.md) and [Create-Scenes.ps1](Create-Scenes.md).
- **`.skip` marker**: a file named `<name>.skip.<ext>` is excluded from batch processing, and the marker stays last in derived names.
- [Create-Clip.ps1](Create-Clip.md) accepts `.mp4`, `.webm` and `.mkv`, case-insensitive. `P01.skip.mp4` gives `P01-Clip.skip.mp4`.
- [Crop-Clips.ps1](Crop-Clips.md) processes only `.mp4` files and skips `*.skip.mp4` and `*.bak.mp4`.
- [Create-Scenes.ps1](Create-Scenes.md) gives `P01-Scene-01.skip.mp4` for `P01.skip.mp4`.
- On failure, scripts exit with `1`, print an error message to the error stream, and create no files. Check `$LASTEXITCODE` after each call.
- **Forwarded parameters**: [Create-Clip.ps1](Create-Clip.md) and [Create-Scenes.ps1](Create-Scenes.md) accept the extra parameters of [Clone-Video.ps1](Clone-Video.md) and forward them, for example `-Size`, `-FPS`, `-NoAudio`, `-Audio`, `-CRF`, `-Metadata`, `-Reverse` and `-LTX`. Each reserves `-Video`, `-Frames`, `-Suffix` and `-Target` for itself, so do not repeat them in the extra parameters.
## Safety Rules
- **Never pass `-Confirm`.** Scripts run without interaction. Only `-Confirm` makes them prompt. No script sets a `ConfirmImpact` of `High`, so none prompts by default. [Create-Scenes.ps1](Create-Scenes.md) sets `Medium`.
- Before any script that creates, overwrites or deletes files, run it with **`-WhatIf`**. Show the user the result, then run it for real only if the user agrees.
- Never run a script on files the user has not designated. Never delete source videos.
- Overwrite and delete behavior of every script, as stated on its page:
| Script | Behavior, as documented |
| --- | --- |
| [Clone-Image.ps1](Clone-Image.md) | An existing `-Target` or `-Keep` file is replaced. Both folders must exist. |
| [Clone-Video.ps1](Clone-Video.md) | An existing `-Target` is replaced without a prompt. The folder of `-Target` must exist. |
| [Compare-Videos.ps1](Compare-Videos.md) | An existing `-Output` is replaced. Its folder must exist. |
| [Create-Clip.ps1](Create-Clip.md) | An existing derived clip is replaced without a prompt. The clip is written by Clone-Video.ps1, with the `.skip` marker kept last in the name. |
| [Create-FullSubtitle.ps1](Create-FullSubtitle.md) | Overwrites `-Output` without a prompt. |
| [Create-PillarBox.ps1](Create-PillarBox.md) | An existing `-Target` video is replaced without a prompt. If ffmpeg fails, the target is removed, including a file that existed before the run. |
| [Create-Scenes.ps1](Create-Scenes.md) | Without `-List`, deletes the earlier scene clips of the same source in the source folder, then creates the new ones. `-List` creates and deletes nothing. |
| [Create-ShortClip.ps1](Create-ShortClip.md) | Overwrites the output video. With the default `-Images` and without `-Render`, clears the `Frames` folder. With explicit `-Images`, deletes only matching frames. Removes its temporary folders, and the `Frames` folder unless `-Keep` is given. |
| [Create-Turnaround.ps1](Create-Turnaround.md) | An existing `-Target` image is replaced without a prompt. The folder of `-Target` must exist. |
| [Crop-Clips.ps1](Crop-Clips.md) | Works in place when `-Source` and `-Target` are omitted or equal. See the in-place rule below. |
| [Get-Frame.ps1](Get-Frame.md) | An existing PNG with the same name is replaced. |
| [Get-FramesCount.ps1](Get-FramesCount.md) | Reads only. Creates and deletes nothing. |
| [Get-FramesRange.ps1](Get-FramesRange.md) | An existing PNG frame with the same name is replaced. |
| [Get-LastFrame.ps1](Get-LastFrame.md) | An existing output PNG is replaced without a prompt. With a numbering token in `-Image`, the resolved name is replaced, not the pattern. |
| [Get-VideoAudio.ps1](Get-VideoAudio.md) | The output file, default or from `-Output`, is replaced. |
| [Get-VideoInfo.ps1](Get-VideoInfo.md) | Reads only. Creates and deletes nothing. |
| [Get-VideoSubtitles.ps1](Get-VideoSubtitles.md) | An existing file with the same name in the current directory is replaced. `-List` creates nothing. |
| [Stitch-InBetween.ps1](Stitch-InBetween.md) | Deletes an existing `-Video` before generating. Deletes everything inside an existing `-TempDir`. |
- **Crop-Clips in-place rule.** Crop-Clips works on the current folder when `-Source` and `-Target` are omitted or equal, and it backs up each file it crops to `<name>.bak.mp4`, replacing any existing backup without a prompt. Skipped files are not backed up and keep their backup. A re-run with a different `-Size` crops the already cropped files again, so the backup then holds the previous cropped output and the true original is lost. So:
- Always pass explicit `-Source` and `-Target`, and prefer a separate `-Target` folder. Never pass the same folder for both.
- Use in-place mode only when the user asks for it.
- Warn the user before re-running in place with a different `-Size` on files that already have a `.bak.mp4`.
- Never delete `.bak.mp4` files yourself unless the user asks.
- **Stitch-InBetween `-TempDir` rule.** The script deletes everything inside an existing `-TempDir`. Never pass an existing folder. Omit `-TempDir` unless the user designates a dedicated empty folder.
## Script Catalog
| Script | Purpose | Typical use |
| --- | --- | --- |
| [Clone-Image.ps1](Clone-Image.md) | Resize (cover-scale then crop), convert formats, set quality and compression, copy metadata, AI-upscale with `-Upscale` and optionally keep the upscaled image with `-Keep`, use LTX-2 sizes with `-LTX` | `-Source small.jpg -Target big_4k.png -Upscale 4 -Size 4K` |
| [Clone-Video.ps1](Clone-Video.md) | Re-encode or convert video (`.mp4`, `.webm`, `.mkv`) with frame range, size, FPS, audio, CRF, `-Crop`, `-Reverse`, `-LTX` and metadata options. Also builds a video from one image plus `-Audio`, builds a video from a folder of PNG files, and extracts one frame to PNG with `-Sequence` | `-Source in.mp4 -Target out.mp4 -Frames "0-100"` |
| [Compare-Videos.ps1](Compare-Videos.md) | Side-by-side or grid comparison of 2 to 4 videos at 1080p or 4K, with `-Clip`, `-Sideways` and `-NoAudio` | `-S1 a.mp4 -S2 b.mp4 -Output cmp.mp4` |
| [Create-Clip.ps1](Create-Clip.md) | Clip a frame range. The output name is the source name plus `-Suffix` (default `-Clip`). Forwards extra parameters to Clone-Video.ps1 | `-Video P01.mp4 -Frames "100-200"` |
| [Create-FullSubtitle.ps1](Create-FullSubtitle.md) | Merge two SRT files into one, with timing taken from the video duration | `-Video v.mp4 -Subtitle1 a.srt -Subtitle2 b.srt -Output out.srt` |
| [Create-PillarBox.ps1](Create-PillarBox.md) | Wrap a video in a 16:9 frame with side-bar effects (solid colours, or blurred backgrounds from the video). `-Vertical` gives a 9:16 frame for square sources | `-Video v.mp4 -Effect black` |
| [Create-Scenes.ps1](Create-Scenes.md) | Detect scene changes and create one clip per scene. `-List` only shows the scenes. `-Scenes` searches the level for a given scene count. Forwards extra parameters to Create-Clip.ps1 and Clone-Video.ps1 | `-Video P01.mp4 -Level 0.3 -List` |
| [Create-ShortClip.ps1](Create-ShortClip.md) | Build a vertical short clip from an INI plan, with optional overlay, colour, smoothing and speed. `-Render` builds the video from existing frames | `-Video v.mp4 -Plan plan.ini` |
| [Create-Turnaround.ps1](Create-Turnaround.md) | Arrange same-sized images into a row or grid turnaround sheet, with padding colour, quality and compression options | `-Source "C:\renders\CharacterA" -Target sheet.png` |
| [Crop-Clips.ps1](Crop-Clips.md) | Batch crop LTX-2, Kling and Wan videos to standard sizes. `-Size` fits every video to a chosen size. Square videos become 1080x1080 | `-Source in_dir -Target out_dir` |
| [Get-Frame.ps1](Get-Frame.md) | Extract one frame to PNG with an automatic name (`<video>-<frame>.png`). Negative indices count from the end | `-Video P01.mp4 -Frame 10` |
| [Get-FramesCount.ps1](Get-FramesCount.md) | Print the total frame count of a video | `-Video P01.mp4` |
| [Get-FramesRange.ps1](Get-FramesRange.md) | Extract a frame range to PNG files, with a custom name pattern through `-Images` | `-Video P01.mp4 -Frames "0-50"` |
| [Get-LastFrame.ps1](Get-LastFrame.md) | Extract one frame (default: the last) to a chosen PNG name. `-Image` can include a numbering token | `-Video P01.mp4 -Image last.png` |
| [Get-VideoAudio.ps1](Get-VideoAudio.md) | Extract audio as WAV (default) or MP3 (`-MP3`) | `-Video P01.mp4 -MP3` |
| [Get-VideoInfo.ps1](Get-VideoInfo.md) | Show metadata stored in video comment tags. Accepts wildcards. `-Full` adds JSON output | `-Video *.mp4` |
| [Get-VideoSubtitles.ps1](Get-VideoSubtitles.md) | Extract embedded text (SRT, ASS) and Blu-ray PGS subtitle tracks to the current directory. DVD and DVB bitmap tracks are listed but not extracted. `-List` only shows the tracks | `-Video P01.mkv -List` |
| [Stitch-InBetween.ps1](Stitch-InBetween.md) | Generate in-between frames between two images with RIFE, then encode them to video | `-First a.png -Last b.png -Frames 30 -Video out.mp4` |
## Common Workflows
**Clip a frame range** (count frames, preview, then run):
```powershell
pwsh -NoProfile -File .\Get-FramesCount.ps1 -Video P01.mp4
pwsh -NoProfile -File .\Create-Clip.ps1 -Video P01.mp4 -Frames "100-200" -WhatIf
pwsh -NoProfile -File .\Create-Clip.ps1 -Video P01.mp4 -Frames "100-200"
```
**Split a video into scenes, with forwarded parameters** (list first, then create clips scaled to 720p without audio):
```powershell
pwsh -NoProfile -File .\Create-Scenes.ps1 -Video P01.mp4 -Level 0.3 -List
pwsh -NoProfile -File .\Create-Scenes.ps1 -Video P01.mp4 -Scenes 4 -Size 720p -NoAudio -WhatIf
pwsh -NoProfile -File .\Create-Scenes.ps1 -Video P01.mp4 -Scenes 4 -Size 720p -NoAudio
```
**Extract the last frame to a named PNG:**
```powershell
pwsh -NoProfile -File .\Get-LastFrame.ps1 -Video P01.mp4 -Image last.png
```
**Upscale an image** (needs `UPSCAYL_HOME`, `NCNN_GPU_ID` and the model files):
```powershell
pwsh -NoProfile -File .\Clone-Image.ps1 -Source small.jpg -Target big_4k.png -Upscale 4 -Size 4K
```
**Interpolate between two images with RIFE** (needs `RIFE_HOME` and `NCNN_GPU_ID`):
```powershell
pwsh -NoProfile -File .\Stitch-InBetween.ps1 -First frame1.png -Last frame2.png -Frames 30 -Video output.mp4 -FPS 24 -WhatIf
pwsh -NoProfile -File .\Stitch-InBetween.ps1 -First frame1.png -Last frame2.png -Frames 30 -Video output.mp4 -FPS 24
```
## Troubleshooting
- **"Video file not found"**: check the path. Use an absolute path if relative paths fail. See [index.md](index.md).
- **"ffmpeg failed" or "ffprobe failed"**: run `ffmpeg -version` and `ffprobe -version`. Check that the file is not corrupt and that the disk has space. See [index.md](index.md).
- **"Frame index out of range"**: run [Get-FramesCount.ps1](Get-FramesCount.md). Indices are 0-based.
- **`-Image` rejected by Get-Frame**: `-Image` is not a [Get-Frame.ps1](Get-Frame.md) parameter. Use [Get-LastFrame.ps1](Get-LastFrame.md) for custom names.
- **Missing `RIFE_HOME`, `UPSCAYL_HOME` or `NCNN_GPU_ID`**: set the persistent variable as described above. Then start a new shell.
- **Upscale fails with missing models**: the model files `<Model>.bin` and `<Model>.param` must be in `%UPSCAYL_HOME%\models`. See [Clone-Image.md](Clone-Image.md) and [Upscalers.md](Upscalers.md).