Created
June 9, 2026 14:31
-
-
Save BenMcLean/a2cb70c0321288320c14c1014e333059 to your computer and use it in GitHub Desktop.
Split full-disc FLAC images into frame-accurate, lossless individual tracks using EAC .cue sheets and FFmpeg
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| @ECHO OFF | |
| cd /d "%~dp0" | |
| PowerShell.exe -NoProfile -ExecutionPolicy Bypass -Command "& '%~dpn0.ps1' \"%1\" \"%2\" \"%3\" \"%4\" \"%5\" \"%6\" \"%7\" \"%8\"" | |
| @PAUSE |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| param([string]$cueFile) | |
| # Ensure strict error handling | |
| $ErrorActionPreference = "Stop" | |
| if (-not $cueFile -or -not (Test-Path $cueFile)) { | |
| Write-Host "Error: Please drag and drop a valid .cue file." -ForegroundColor Red | |
| pause | |
| return | |
| } | |
| $workingDir = Split-Path $cueFile | |
| $baseName = [System.IO.Path]::GetFileNameWithoutExtension($cueFile) | |
| $flacFile = Join-Path $workingDir "$baseName.flac" | |
| if (-not (Test-Path $flacFile)) { | |
| Write-Host "Error: Could not find matching FLAC file: $flacFile" -ForegroundColor Red | |
| pause | |
| return | |
| } | |
| # Explicitly cast global matches to strings before parsing | |
| function Convert-CueTime ([string]$cueTime) { | |
| if ($cueTime -match "(\d{2}):(\d{2}):(\d{2})") { | |
| [int]$mins = [int]$Matches[1] | |
| [int]$secs = [int]$Matches[2] | |
| [int]$frames = [int]$Matches[3] | |
| [int]$hours = [math]::Floor($mins / 60) | |
| [int]$remMins = $mins % 60 | |
| [int]$ms = [math]::Round(($frames / 75) * 1000) | |
| return "{0:D2}:{1:D2}:{2:D2}.{3:D3}" -f $hours, $remMins, $secs, $ms | |
| } | |
| throw "Failed to parse timestamp string: $cueTime" | |
| } | |
| # Parse CUE file securely | |
| try { | |
| $cueContent = Get-Content $cueFile | |
| } | |
| catch { | |
| Write-Host "Error: Failed to read CUE sheet layout text." -ForegroundColor Red | |
| pause | |
| return | |
| } | |
| $tracks = @() | |
| $currentTrack = $null | |
| foreach ($line in $cueContent) { | |
| $line = $line.Trim() | |
| if ($line -match '^TRACK\s+(\d+)\s+AUDIO') { | |
| if ($currentTrack) { $tracks += $currentTrack } | |
| $currentTrack = @{ Number = $Matches[1]; Title = "Track_" + $Matches[1]; Start = $null } | |
| } | |
| elseif ($line -match '^TITLE\s+"([^"]+)"' -and $currentTrack) { | |
| $currentTrack.Title = $Matches[1] -replace '[\/?:*""<>|]', '' | |
| } | |
| elseif ($line -match '^INDEX\s+01\s+(\d{2}:\d{2}:\d{2})' -and $currentTrack) { | |
| $currentTrack.Start = Convert-CueTime $Matches[1] | |
| } | |
| } | |
| if ($currentTrack) { $tracks += $currentTrack } | |
| if ($tracks.Count -eq 0) { | |
| Write-Host "Error: No valid tracks found in the CUE sheet." -ForegroundColor Red | |
| pause | |
| return | |
| } | |
| # Execute processing loop safely | |
| for ($i = 0; $i -lt $tracks.Count; $i++) { | |
| $track = $tracks[$i] | |
| $num = $track.Number | |
| $title = $track.Title | |
| $start = $track.Start | |
| $outputName = Join-Path $workingDir "${num} - ${title}.flac" | |
| # Accurate Stream Copy Ordering: TIMESTAMPS MUST COME BEFORE INPUT (-i) | |
| $ffmpegArgs = @("-ss", $start) | |
| if ($i -lt ($tracks.Count - 1)) { | |
| $end = $tracks[$i + 1].Start | |
| $ffmpegArgs += @("-to", $end) | |
| } | |
| #To skip re-encoding: $ffmpegArgs += @("-i", $flacFile, "-c", "copy", $outputName, "-y") | |
| $ffmpegArgs += @("-i", $flacFile, "-c:a", "flac", "-compression_level", "5", $outputName, "-y") | |
| Write-Host "Stream Copying Track ${num}: ${title} (Start: $start)..." -ForegroundColor Cyan | |
| # Run FFmpeg and capture validation exceptions | |
| & ffmpeg -hide_banner -loglevel error @ffmpegArgs | |
| if ($LASTEXITCODE -ne 0) { | |
| Write-Host "FFmpeg aborted with exit code $LASTEXITCODE processing Track ${num}." -ForegroundColor Red | |
| pause | |
| return | |
| } | |
| } | |
| Write-Host "`nProcessing complete! All tracks successfully split using direct stream copy." -ForegroundColor Green |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment