Document Get-VideoSubtitles.ps1 in README.md, index.md and SKILL.md, describing subtitle extraction and -List behavior, supported text and Blu-ray PGS formats, unsupported DVD and DVB bitmap tracks, and replacement of same-named output files in the current directory
This commit is contained in:
1 parent
fcc208e351
commit
dad95f0728
6 files changed
+208
-1
No files matched your search
@@ -0,0 +1,2 @@
|
|||||||
|
@echo off
|
||||||
|
pwsh.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0Get-VideoSubtitles.ps1" %*
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Extracts the embedded subtitle tracks of a video file into the current directory.
|
||||||
|
.PARAMETER Video
|
||||||
|
Path to the input video file. Can be absolute or relative to current directory.
|
||||||
|
.PARAMETER List
|
||||||
|
When present, lists the subtitle tracks instead of extracting them. Creates no files.
|
||||||
|
.DESCRIPTION
|
||||||
|
This script uses ffprobe to list the subtitle streams of a video file and ffmpeg to copy each one out:
|
||||||
|
- Text subtitles (SubRip, WebVTT and mov_text, found in MKV, WebM, MP4, MOV and M4V) are written as .srt.
|
||||||
|
- ASS/SSA subtitles (MKV) are written as .ass, keeping their styling.
|
||||||
|
- Blu-ray PGS subtitles (hdmv_pgs_subtitle, found in MKV, M2TS and TS) are copied unchanged to .sup.
|
||||||
|
- Bitmap DVD subtitles (dvd_subtitle, found in MKV and VOB) and DVB subtitles (dvb_subtitle, found in TS) are listed but not extracted, because ffmpeg cannot write them as text or as a standalone file.
|
||||||
|
- AVI files have no standard subtitle track, so nothing is extracted from them.
|
||||||
|
Bitmap subtitles can only be turned into text with OCR, which this script does not do.
|
||||||
|
Each file is named "<video name>.<language>.<extension>" in the current directory. The language is the 2-letter ISO 639-1 code when one exists, otherwise the 3-letter code from the file, or "und" when the track has no language tag. A repeated name gets a counter: "<video name>.<language>.2.<extension>".
|
||||||
|
An existing file with the same name is overwritten.
|
||||||
|
.NOTES
|
||||||
|
Requires ffmpeg and ffprobe to be installed and available in the system PATH.
|
||||||
|
#>
|
||||||
|
[CmdletBinding(SupportsShouldProcess)]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Video,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[switch]$List
|
||||||
|
)
|
||||||
|
|
||||||
|
function Get-LanguageCode([string]$Tag) {
|
||||||
|
if (-not $Tag -or $Tag -eq 'und') { return 'und' }
|
||||||
|
$culture = [System.Globalization.CultureInfo]::GetCultures([System.Globalization.CultureTypes]::NeutralCultures) | Where-Object { $_.ThreeLetterISOLanguageName -eq $Tag } | Select-Object -First 1
|
||||||
|
if ($culture) { return $culture.TwoLetterISOLanguageName }
|
||||||
|
return $Tag
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (-not (Test-Path -LiteralPath $Video)) {
|
||||||
|
throw "Video file not found: $Video"
|
||||||
|
}
|
||||||
|
$videoPath = (Resolve-Path -LiteralPath $Video).Path
|
||||||
|
$baseName = [System.IO.Path]::GetFileNameWithoutExtension($videoPath)
|
||||||
|
$outputDir = (Get-Location).ProviderPath
|
||||||
|
|
||||||
|
$ffprobeJson = & ffprobe -v error -select_streams s -show_entries 'stream=index,codec_name:stream_tags=language' -of json -- $videoPath
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "ffprobe failed to read video file: $videoPath"
|
||||||
|
}
|
||||||
|
$streams = @((ConvertFrom-Json ($ffprobeJson -join "`n")).streams | Where-Object { $_ })
|
||||||
|
|
||||||
|
$plan = @()
|
||||||
|
$used = @{}
|
||||||
|
foreach ($s in $streams) {
|
||||||
|
$ext = switch ($s.codec_name) {
|
||||||
|
{ $_ -in 'subrip', 'mov_text', 'webvtt' } { '.srt' }
|
||||||
|
{ $_ -in 'ass', 'ssa' } { '.ass' }
|
||||||
|
'hdmv_pgs_subtitle' { '.sup' }
|
||||||
|
default { $null }
|
||||||
|
}
|
||||||
|
$code = Get-LanguageCode $s.tags.language
|
||||||
|
$fileName = "$baseName.$code$ext"
|
||||||
|
$counter = 1
|
||||||
|
while ($used.ContainsKey($fileName.ToLowerInvariant())) {
|
||||||
|
$counter++
|
||||||
|
$fileName = "$baseName.$code.$counter$ext"
|
||||||
|
}
|
||||||
|
$used[$fileName.ToLowerInvariant()] = $true
|
||||||
|
$plan += [pscustomobject]@{ Index = [int]$s.index; Codec = $s.codec_name; Ext = $ext; File = Join-Path $outputDir $fileName }
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Subtitle tracks in: $videoPath"
|
||||||
|
if ($streams.Count -eq 0) {
|
||||||
|
Write-Host "No subtitle stream found in video file." -ForegroundColor Yellow
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($List) {
|
||||||
|
$plan | Format-Table -Property @{ Label = 'Index'; Expression = { $_.Index } }, @{ Label = 'Codec'; Expression = { $_.Codec } }, @{ Label = 'Output'; Expression = { if ($_.Ext) { Split-Path -Leaf $_.File } else { 'not supported' } } } -AutoSize | Out-String | Write-Host
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($p in $plan) {
|
||||||
|
if (-not $p.Ext) {
|
||||||
|
Write-Warning "Subtitle stream $($p.Index) ($($p.Codec)) is not supported and was skipped."
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ($PSCmdlet.ShouldProcess($p.File, "Extract subtitle stream $($p.Index) ($($p.Codec))")) {
|
||||||
|
$codecArgs = if ($p.Ext -eq '.srt') { @('-c:s', 'srt') } else { @('-c:s', 'copy') }
|
||||||
|
if ($p.Ext -eq '.sup') { $codecArgs += @('-f', 'sup') }
|
||||||
|
& ffmpeg -v error -y -i $videoPath -map "0:$($p.Index)" @codecArgs $p.File
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "ffmpeg failed to extract subtitle stream $($p.Index) with exit code $LASTEXITCODE"
|
||||||
|
}
|
||||||
|
if (-not (Test-Path -LiteralPath $p.File)) {
|
||||||
|
throw "Subtitle file was not created: $($p.File)"
|
||||||
|
}
|
||||||
|
Write-Host "Subtitle extracted: $($p.File)" -ForegroundColor Green
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "Error: $_"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
@@ -65,6 +65,7 @@ These map directly to the `-g` flag values accepted by both `rife-ncnn-vulkan` a
|
|||||||
| Get-LastFrame.ps1 | Extract a specific frame or last frame to PNG, with an optional filename pattern |
|
| Get-LastFrame.ps1 | Extract a specific frame or last frame to PNG, with an optional filename pattern |
|
||||||
| Get-VideoAudio.ps1 | Extracts audio in either WAV or MP3 format from a video file |
|
| Get-VideoAudio.ps1 | Extracts audio in either WAV or MP3 format from a video file |
|
||||||
| Get-VideoInfo.ps1 | Extract and display video metadata from comment tags |
|
| Get-VideoInfo.ps1 | Extract and display video metadata from comment tags |
|
||||||
|
| Get-VideoSubtitles.ps1 | Extract the embedded subtitle tracks of a video to SRT, ASS or SUP files in the current directory, or list them with `-List` |
|
||||||
| Stitch-InBetween.ps1 | Generate interpolated in-between frames between two images using RIFE and encode to video |
|
| Stitch-InBetween.ps1 | Generate interpolated in-between frames between two images using RIFE and encode to video |
|
||||||
|
|
||||||
**Important**:
|
**Important**:
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
# Get-VideoSubtitles.ps1
|
||||||
|
|
||||||
|
Extracts the embedded subtitle tracks of a video file into the current directory, or lists them with `-List`.
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
- **`-Video`** (required): Path to input video file (absolute or relative)
|
||||||
|
- **`-List`**: List the subtitle tracks (index, codec and output name) instead of extracting them. Creates no files
|
||||||
|
|
||||||
|
## Supported Formats
|
||||||
|
|
||||||
|
ffmpeg can copy text subtitles out of a container, and can copy Blu-ray bitmap subtitles to a `.sup` file. It cannot write DVD or DVB bitmap subtitles as a standalone file, and it cannot turn bitmaps into text without OCR.
|
||||||
|
|
||||||
|
| Container | Subtitle codec | Output | Notes |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| MKV | SubRip (`subrip`) | `.srt` | |
|
||||||
|
| MKV, WebM | WebVTT (`webvtt`) | `.srt` | Converted to SubRip |
|
||||||
|
| MP4, MOV, M4V | mov_text (`mov_text`) | `.srt` | Converted to SubRip |
|
||||||
|
| MKV | ASS / SSA (`ass`) | `.ass` | Copied unchanged, styling is kept |
|
||||||
|
| MKV, M2TS, TS | Blu-ray PGS (`hdmv_pgs_subtitle`) | `.sup` | Copied unchanged. Bitmap, not text |
|
||||||
|
| MKV, VOB | DVD (`dvd_subtitle`) | not extracted | Listed as not supported |
|
||||||
|
| TS | DVB (`dvb_subtitle`) | not extracted | Listed as not supported |
|
||||||
|
| AVI | none | not extracted | AVI has no standard subtitle track |
|
||||||
|
|
||||||
|
Any other subtitle codec is listed as not supported and skipped with a warning.
|
||||||
|
|
||||||
|
## Output Names
|
||||||
|
|
||||||
|
Each extracted file is saved in the current directory, not next to the video. The name is built from the video name and the track language:
|
||||||
|
|
||||||
|
- `<video>.<language>.<extension>`, for example `movie.en.srt` for an English track
|
||||||
|
- The language is the 2-letter ISO 639-1 code when one exists (`eng` becomes `en`, `fra` becomes `fr`). Otherwise it is the 3-letter code from the file (for example `fre`), or `und` when the track has no language tag
|
||||||
|
- When two tracks produce the same name, the second and later ones get a counter: `movie.en.2.srt`, `movie.en.3.srt`
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
**List the subtitle tracks without creating files:**
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\Get-VideoSubtitles.ps1 -Video video.mkv -List
|
||||||
|
```
|
||||||
|
|
||||||
|
**Extract all subtitle tracks to the current directory:**
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\Get-VideoSubtitles.ps1 -Video video.mkv
|
||||||
|
# Creates video.en.srt, video.fr.ass, ... in the current directory
|
||||||
|
```
|
||||||
|
|
||||||
|
**Using absolute paths:**
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\Get-VideoSubtitles.ps1 -Video "C:\Videos\movie.mkv"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Using relative paths:**
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\Get-VideoSubtitles.ps1 -Video ..\videos\clip.mp4
|
||||||
|
```
|
||||||
|
|
||||||
|
## Behavior
|
||||||
|
|
||||||
|
- If the video has no subtitle stream, the script displays a message and exits successfully. It creates no file
|
||||||
|
- Each track is copied with ffmpeg in its own pass. Text tracks are written as SubRip (`.srt`), ASS tracks as `.ass`, and PGS tracks as `.sup`
|
||||||
|
- Unsupported tracks produce a warning and the other tracks are still extracted
|
||||||
|
- The video file itself is never changed
|
||||||
|
|
||||||
|
## Existing Files
|
||||||
|
|
||||||
|
- An existing file with the same name in the current directory is **replaced** without a prompt. ffmpeg runs with `-y`, so the old file is lost. Use `-WhatIf` first, or run from a different folder
|
||||||
|
- If ffmpeg fails, or an expected output file is not created, the script stops with an error
|
||||||
|
- `-List` creates and replaces nothing
|
||||||
|
|
||||||
|
## Safe Execution
|
||||||
|
|
||||||
|
This script supports `-WhatIf` and `-Confirm` for safe execution:
|
||||||
|
|
||||||
|
- **`-WhatIf`**: Simulate the operation without creating files. Shows which files would be written.
|
||||||
|
- **`-Confirm`**: Prompt for confirmation before each subtitle file is written.
|
||||||
|
|
||||||
|
**Simulate without making changes:**
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\Get-VideoSubtitles.ps1 -Video video.mkv -WhatIf
|
||||||
|
```
|
||||||
|
|
||||||
|
**Prompt before each file is written:**
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\Get-VideoSubtitles.ps1 -Video video.mkv -Confirm
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Requires `ffmpeg` and `ffprobe` to be installed and available in the system PATH.
|
||||||
|
- Language codes come from the file's language tags. Tracks without a tag are named with `und`.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: video-processing-scripts
|
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, 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.
|
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
|
# Video Processing PowerShell Scripts
|
||||||
@@ -137,6 +137,7 @@ pwsh -NoProfile -File "C:\path\to\Video Processing Scripts\Create-Clip.ps1" -Vid
|
|||||||
| [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-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-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-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`. |
|
| [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:
|
- **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:
|
||||||
@@ -166,6 +167,7 @@ pwsh -NoProfile -File "C:\path\to\Video Processing Scripts\Create-Clip.ps1" -Vid
|
|||||||
| [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-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-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-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` |
|
| [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
|
## Common Workflows
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ These map directly to the `-g` flag values accepted by both `rife-ncnn-vulkan` a
|
|||||||
| Get-LastFrame.ps1 | Extract a specific frame or last frame to PNG, with an optional filename pattern |
|
| Get-LastFrame.ps1 | Extract a specific frame or last frame to PNG, with an optional filename pattern |
|
||||||
| Get-VideoAudio.ps1 | Extracts audio in either WAV or MP3 format from a video file |
|
| Get-VideoAudio.ps1 | Extracts audio in either WAV or MP3 format from a video file |
|
||||||
| Get-VideoInfo.ps1 | Extract and display video metadata from comment tags |
|
| Get-VideoInfo.ps1 | Extract and display video metadata from comment tags |
|
||||||
|
| Get-VideoSubtitles.ps1 | Extract the embedded subtitle tracks of a video to SRT, ASS or SUP files in the current directory, or list them with `-List` |
|
||||||
| Stitch-InBetween.ps1 | Generate interpolated in-between frames between two images using RIFE and encode to video |
|
| Stitch-InBetween.ps1 | Generate interpolated in-between frames between two images using RIFE and encode to video |
|
||||||
|
|
||||||
**Important**:
|
**Important**:
|
||||||
|
|||||||
Reference in new issue
Block a user