Skip to content

Instantly share code, notes, and snippets.

@maksii
Last active October 12, 2024 09:07
Show Gist options
  • Save maksii/5d11166c346417459b6b5b350ea6ae0c to your computer and use it in GitHub Desktop.
Save maksii/5d11166c346417459b6b5b350ea6ae0c to your computer and use it in GitHub Desktop.
windows PowerShell scripts to batch(bulk) ffmpeg commands to manipulate media. require ffmpeg installed
# Check FFmpeg availability
$ffmpegPath = "ffmpeg" # Default to ffmpeg in PATH
# Check FFprobe availability
$ffprobePath = "ffprobe" # Default to ffprobe in PATH
# Function to check if ffmpeg exists in PATH
function Check-FFmpeg {
try {
$output = & $ffmpegPath -version 2>&1
if ($output -like "*ffmpeg version*") {
return $true
} else {
Write-Host "FFmpeg output did not contain expected version information."
return $false
}
} catch {
Write-Host "Error executing ffmpeg: $_"
return $false
}
}
# Function to check if ffprobe exists in PATH
function Check-FFprobe {
try {
$output = & $ffprobePath -version 2>&1
if ($output -like "*ffprobe version*") {
return $true
} else {
Write-Host "FFprobe output did not contain expected version information."
return $false
}
} catch {
Write-Host "Error executing ffprobe: $_"
return $false
}
}
# Prompt for folder path
$folderPath = Read-Host "Enter the full path to the folder containing media files (e.g., 'C:\Media\Folder')"
# Remove quotes from folder path if present
$folderPath = $folderPath.Trim('"')
# Validate folder path
if (-not (Test-Path $folderPath)) {
Write-Host "The specified folder does not exist."
Read-Host
exit
}
if (-not (Check-FFmpeg)) {
Write-Host "FFmpeg is not found in the system PATH."
$ffmpegPath = Read-Host "Please specify the full path to the ffmpeg executable (e.g., 'C:\Path\To\ffmpeg.exe')"
$ffmpegPath = $ffmpegPath.Trim('"')
# Validate FFmpeg path
if (-not (Test-Path $ffmpegPath)) {
Write-Host "The specified FFmpeg path is invalid."
Read-Host
exit
}
}
if (-not (Check-FFprobe)) {
Write-Host "FFprobe is not found in the system PATH."
$ffprobePath = Read-Host "Please specify the full path to the ffprobe executable (e.g., 'C:\Path\To\ffprobe.exe')"
$ffprobePath = $ffprobePath.Trim('"')
# Validate FFprobe path
if (-not (Test-Path $ffprobePath)) {
Write-Host "The specified FFprobe path is invalid."
Read-Host
exit
}
}
Write-Host "Using FFmpeg at path: $ffmpegPath"
Write-Host "Using FFprobe at path: $ffprobePath"
# Create 'extract' folder if it doesn't exist
$extractFolderPath = Join-Path -Path $folderPath -ChildPath "extract"
if (-not (Test-Path $extractFolderPath)) {
New-Item -Path $extractFolderPath -ItemType Directory | Out-Null
}
# Get MP4 and AVI files in the folder
$mediaFiles = Get-ChildItem -Path $folderPath -File | Where-Object { $_.Extension -eq ".mp4" -or $_.Extension -eq ".avi" }
# Define codec to extension mapping
$codecExtensions = @{
"aac" = ".aac"
"ac3" = ".ac3"
"mp3" = ".mp3"
"wav" = ".wav"
"dts" = ".dts"
"flac" = ".flac"
"pcm" = ".pcm"
"opus" = ".opus"
}
# Process each media file
foreach ($file in $mediaFiles) {
$inputFilePath = $file.FullName
$fileBaseName = [System.IO.Path]::GetFileNameWithoutExtension($file.Name)
# Get FFmpeg output
$ffmpegProbeCommand = "$ffmpegPath -i `"$inputFilePath`" -hide_banner"
$ffmpegOutput = & cmd /c $ffmpegProbeCommand 2>&1
# Get the number of audio streams
$audioStreams = ($ffmpegOutput | Select-String -Pattern "Stream.*Audio").Count
# Extract each audio stream
for ($i = 0; $i -lt $audioStreams; $i++) {
try {
# Identify the audio codec
$audioCodecLine = ($ffmpegOutput | Select-String -Pattern "Stream.*Audio")[$i].Line
$audioCodec = if ($audioCodecLine -match "aac") { "aac" }
elseif ($audioCodecLine -match "ac3") { "ac3" }
elseif ($audioCodecLine -match "mp3") { "mp3" }
elseif ($audioCodecLine -match "wav") { "wav" }
elseif ($audioCodecLine -match "dts") { "dts" }
elseif ($audioCodecLine -match "flac") { "flac" }
elseif ($audioCodecLine -match "pcm") { "pcm" }
elseif ($audioCodecLine -match "opus") { "opus" }
else { "unknown" }
# Determine file extension based on codec
$outputExtension = if ($codecExtensions.ContainsKey($audioCodec)) { $codecExtensions[$audioCodec] } else { ".unknown" }
$outputFileName = "${fileBaseName}_audio${i}${outputExtension}"
$outputFilePath = Join-Path -Path $extractFolderPath -ChildPath $outputFileName
# Build FFmpeg command to extract audio
$ffmpegCommand = "$ffmpegPath -i `"$inputFilePath`" -map 0:a:$i -vn -acodec copy `"$outputFilePath`""
# Execute FFmpeg command
Write-Verbose "Extracting audio stream $i from $($file.Name) as $audioCodec..."
Invoke-Expression -Command $ffmpegCommand
# Update progress
Write-Verbose "Completed processing audio stream $i of $($file.Name)."
} catch {
Write-Error "Failed to process audio stream $i of $($file.Name): $_"
}
}
}
# Completion Message
Write-Host "Audio extraction completed for all files. Press Enter to exit."
Read-Host
# Check FFmpeg availability
$ffmpegPath = "ffmpeg" # Default to ffmpeg in PATH
# Check FFprobe availability
$ffprobePath = "ffprobe" # Default to ffprobe in PATH
# Function to check if ffmpeg exists in PATH
function Check-FFmpeg {
try {
$output = & $ffmpegPath -version 2>&1
if ($output -like "*ffmpeg version*") {
return $true
} else {
Write-Host "FFmpeg output did not contain expected version information."
return $false
}
} catch {
Write-Host "Error executing ffmpeg: $_"
return $false
}
}
# Function to check if ffprobe exists in PATH
function Check-FFprobe {
try {
$output = & $ffprobePath -version 2>&1
if ($output -like "*ffprobe version*") {
return $true
} else {
Write-Host "FFprobe output did not contain expected version information."
return $false
}
} catch {
Write-Host "Error executing ffprobe: $_"
return $false
}
}
# Prompt for folder path
$folderPath = Read-Host "Enter the full path to the folder containing media files (e.g., 'C:\Media\Folder')"
# Remove quotes from folder path if present
$folderPath = $folderPath.Trim('"')
# Validate folder path
if (-not (Test-Path $folderPath)) {
Write-Host "The specified folder does not exist."
Read-Host
exit
}
if (-not (Check-FFmpeg)) {
Write-Host "FFmpeg is not found in the system PATH."
$ffmpegPath = Read-Host "Please specify the full path to the ffmpeg executable (e.g., 'C:\Path\To\ffmpeg.exe')"
$ffmpegPath = $ffmpegPath.Trim('"')
# Validate FFmpeg path
if (-not (Test-Path $ffmpegPath)) {
Write-Host "The specified FFmpeg path is invalid."
Read-Host
exit
}
}
if (-not (Check-FFprobe)) {
Write-Host "FFprobe is not found in the system PATH."
$ffprobePath = Read-Host "Please specify the full path to the ffprobe executable (e.g., 'C:\Path\To\ffprobe.exe')"
$ffprobePath = $ffprobePath.Trim('"')
# Validate FFprobe path
if (-not (Test-Path $ffprobePath)) {
Write-Host "The specified FFprobe path is invalid."
Read-Host
exit
}
}
Write-Host "Using FFmpeg at path: $ffmpegPath"
Write-Host "Using FFprobe at path: $ffprobePath"
# Create 'channels' folder if it doesn't exist
$extractFolderPath = Join-Path -Path $folderPath -ChildPath "channels"
if (-not (Test-Path $extractFolderPath)) {
New-Item -Path $extractFolderPath -ItemType Directory | Out-Null
}
function ProcessAudio {
param (
[string]$fileFullName,
[string]$channelLayout,
[array]$channels,
[string]$outputFolder
)
$fileBaseName = [System.IO.Path]::GetFileNameWithoutExtension($fileFullName)
Write-Host "Processing file: $($fileBaseName)"
# Construct the filter_complex and map options
$filterComplex = "channelsplit=channel_layout=$channelLayout"
$mapOptions = ""
for ($i = 0; $i -lt $channels.Length; $i++) {
$channel = $channels[$i]
$outputFileName = "${fileBaseName}_${channel}.wav"
$outputFilePath = Join-Path -Path $outputFolder -ChildPath $outputFileName
$filterComplex += "[$channel]"
$mapOptions += "-c pcm_s24le -rf64 auto -map `[$channel`] `"$outputFilePath`" "
}
$ffmpegCommand = "$ffmpegPath -i `"$fileFullName`" -filter_complex `"$filterComplex`" $mapOptions"
Write-Host "Executing command: $ffmpegCommand"
Start-Process -NoNewWindow -Wait -FilePath "cmd.exe" -ArgumentList "/c $ffmpegCommand"
}
# Get all audio files in the folder
$audioFiles = Get-ChildItem -Path $folderPath -Include *.wav, *.mp3, *.aac, *.flac, *.ogg, *.m4a, *.opus, *.ac3, *.eac3, *.ac4, *.dts, *.dtshd, *.truehd, *.mlp -Recurse
# Count the number of audio files found
$numberOfFiles = $audioFiles.Count
# Output the number of files found
Write-Output "Number of audio files found: $numberOfFiles"
foreach ($file in $audioFiles) {
# Create a folder for each file inside 'channels'
$outputFolder = Join-Path -Path $extractFolderPath -ChildPath "$($file.BaseName)_channels"
if (-not (Test-Path $outputFolder)) {
New-Item -Path $outputFolder -ItemType Directory | Out-Null
}
# Get file information using ffprobe
$ffprobeCommand = "$ffprobePath -v error -select_streams a:0 -show_entries stream=channel_layout -of json `"$($file.FullName)`""
$ffprobeOutput = & cmd /c $ffprobeCommand
$ffprobeJson = $ffprobeOutput | ConvertFrom-Json
#Write-Host "FFprobe JSON output: $($ffprobeJson | ConvertTo-Json -Depth 5)"
if ($ffprobeJson.streams.Count -gt 0) {
$channelLayout = $ffprobeJson.streams[0].channel_layout
# Determine the number of channels and perform splitting
switch ($channelLayout) {
"mono" { ProcessAudio -fileFullName $file.FullName -channelLayout "mono" -channels @("C") -outputFolder $outputFolder }
"1.0" { ProcessAudio -fileFullName $file.FullName -channelLayout "1.0" -channels @("C") -outputFolder $outputFolder }
"stereo" { ProcessAudio -fileFullName $file.FullName -channelLayout "stereo" -channels @("L", "R") -outputFolder $outputFolder }
"2.0" { ProcessAudio -fileFullName $file.FullName -channelLayout "2.0" -channels @("L", "R") -outputFolder $outputFolder }
"2.1" { ProcessAudio -fileFullName $file.FullName -channelLayout "2.1" -channels @("L", "R", "LFE") -outputFolder $outputFolder }
"3.0" { ProcessAudio -fileFullName $file.FullName -channelLayout "3.0" -channels @("L", "C", "R") -outputFolder $outputFolder }
"3.1" { ProcessAudio -fileFullName $file.FullName -channelLayout "3.1" -channels @("L", "C", "R", "LFE") -outputFolder $outputFolder }
"4.0" { ProcessAudio -fileFullName $file.FullName -channelLayout "4.0" -channels @("L", "R", "Ls", "Rs") -outputFolder $outputFolder }
"4.1" { ProcessAudio -fileFullName $file.FullName -channelLayout "4.1" -channels @("L", "R", "Ls", "Rs", "LFE") -outputFolder $outputFolder }
"5.0" { ProcessAudio -fileFullName $file.FullName -channelLayout "5.0" -channels @("L", "R", "C", "Ls", "Rs") -outputFolder $outputFolder }
"5.0(side)" { ProcessAudio -fileFullName $file.FullName -channelLayout "5.0(side)" -channels @("L", "R", "C", "Ls", "Rs") -outputFolder $outputFolder }
"5.1" { ProcessAudio -fileFullName $file.FullName -channelLayout "5.1" -channels @("L", "R", "C", "LFE", "Ls", "Rs") -outputFolder $outputFolder }
"5.1(side)" { ProcessAudio -fileFullName $file.FullName -channelLayout "5.1(side)" -channels @("L", "R", "C", "LFE", "Ls", "Rs") -outputFolder $outputFolder }
"6.0" { ProcessAudio -fileFullName $file.FullName -channelLayout "6.0" -channels @("L", "R", "C", "Ls", "Rs", "Cs") -outputFolder $outputFolder }
"6.0(front)" { ProcessAudio -fileFullName $file.FullName -channelLayout "6.0(front)" -channels @("L", "R", "C", "Ls", "Rs", "Cs") -outputFolder $outputFolder }
"6.1" { ProcessAudio -fileFullName $file.FullName -channelLayout "6.1" -channels @("L", "R", "C", "LFE", "Ls", "Rs", "Cs") -outputFolder $outputFolder }
"7.0" { ProcessAudio -fileFullName $file.FullName -channelLayout "7.0" -channels @("L", "R", "C", "Ls", "Rs", "Lrs", "Rrs") -outputFolder $outputFolder }
"7.0(front)" { ProcessAudio -fileFullName $file.FullName -channelLayout "7.0(front)" -channels @("L", "R", "C", "Ls", "Rs", "Lrs", "Rrs") -outputFolder $outputFolder }
"7.1" { ProcessAudio -fileFullName $file.FullName -channelLayout "7.1" -channels @("L", "R", "C", "LFE", "Ls", "Rs", "Lrs", "Rrs") -outputFolder $outputFolder }
"7.1(wide)" { ProcessAudio -fileFullName $file.FullName -channelLayout "7.1(wide)" -channels @("L", "R", "C", "LFE", "Ls", "Rs", "Lrs", "Rrs") -outputFolder $outputFolder }
"7.1(wide-side)" { ProcessAudio -fileFullName $file.FullName -channelLayout "7.1(wide-side)" -channels @("L", "R", "C", "LFE", "Ls", "Rs", "Lrs", "Rrs") -outputFolder $outputFolder }
default { Write-Host "Unknown channel layout for file $($file.Name). Channels: $($channelLayout)" }
}
} else {
Write-Host "No audio stream found in file $($file.Name)"
}
}
# Completion Message
Write-Host "Channel extraction completed for all files. Press Enter to exit."
Read-Host
# Check FFmpeg availability
$ffmpegPath = "ffmpeg" # Default to ffmpeg in PATH
# Check FFprobe availability
$ffprobePath = "ffprobe" # Default to ffprobe in PATH
# Function to check if ffmpeg exists in PATH
function Check-FFmpeg {
try {
$output = & $ffmpegPath -version 2>&1
if ($output -like "*ffmpeg version*") {
return $true
} else {
Write-Host "FFmpeg output did not contain expected version information."
return $false
}
} catch {
Write-Host "Error executing ffmpeg: $_"
return $false
}
}
# Function to check if ffprobe exists in PATH
function Check-FFprobe {
try {
$output = & $ffprobePath -version 2>&1
if ($output -like "*ffprobe version*") {
return $true
} else {
Write-Host "FFprobe output did not contain expected version information."
return $false
}
} catch {
Write-Host "Error executing ffprobe: $_"
return $false
}
}
# Prompt for folder path
$folderPath = Read-Host "Enter the full path to the folder containing media files (e.g., 'C:\Media\Folder')"
# Remove quotes from folder path if present
$folderPath = $folderPath.Trim('"')
# Validate folder path
if (-not (Test-Path $folderPath)) {
Write-Host "The specified folder does not exist."
Read-Host
exit
}
if (-not (Check-FFmpeg)) {
Write-Host "FFmpeg is not found in the system PATH."
$ffmpegPath = Read-Host "Please specify the full path to the ffmpeg executable (e.g., 'C:\Path\To\ffmpeg.exe')"
$ffmpegPath = $ffmpegPath.Trim('"')
# Validate FFmpeg path
if (-not (Test-Path $ffmpegPath)) {
Write-Host "The specified FFmpeg path is invalid."
Read-Host
exit
}
}
if (-not (Check-FFprobe)) {
Write-Host "FFprobe is not found in the system PATH."
$ffprobePath = Read-Host "Please specify the full path to the ffprobe executable (e.g., 'C:\Path\To\ffprobe.exe')"
$ffprobePath = $ffprobePath.Trim('"')
# Validate FFprobe path
if (-not (Test-Path $ffprobePath)) {
Write-Host "The specified FFprobe path is invalid."
Read-Host
exit
}
}
Write-Host "Using FFmpeg at path: $ffmpegPath"
Write-Host "Using FFprobe at path: $ffprobePath"
# Create 'converted' folder if it doesn't exist
$convertFolderPath = Join-Path -Path $folderPath -ChildPath "converted"
if (-not (Test-Path $convertFolderPath)) {
New-Item -Path $convertFolderPath -ItemType Directory | Out-Null
}
# Function to get the number of audio channels using ffprobe
function Get-AudioChannels {
param (
[string]$fileFullName
)
$ffprobeCommand = "$ffprobePath -v error -select_streams a:0 -show_entries stream=channels -of default=noprint_wrappers=1:nokey=1 `"$fileFullName`""
$channels = & cmd.exe /c $ffprobeCommand
return [int]$channels.Trim()
}
# Function to convert audio to 24-bit WAV
function ConvertToWav24bit {
param (
[string]$fileFullName,
[string]$outputFolder
)
$fileBaseName = [System.IO.Path]::GetFileNameWithoutExtension($fileFullName)
$outputFileName = "${fileBaseName}.wav"
$outputFilePath = Join-Path -Path $outputFolder -ChildPath $outputFileName
$channels = Get-AudioChannels -fileFullName $fileFullName
$ffmpegCommand = "$ffmpegPath -i `"$fileFullName`" -c:a pcm_s24le -ac $channels -rf64 auto `"$outputFilePath`""
Write-Host "Executing command: $ffmpegCommand"
Start-Process -NoNewWindow -Wait -FilePath "cmd.exe" -ArgumentList "/c $ffmpegCommand"
}
# Get all audio files in the folder
$audioFiles = Get-ChildItem -Path $folderPath -Include *.wav, *.mp3, *.aac, *.flac, *.ogg, *.m4a, *.opus, *.ac3, *.eac3, *.ac4, *.dts, *.dtshd, *.truehd, *.mlp -Recurse
# Count the number of audio files found
$numberOfFiles = $audioFiles.Count
# Output the number of files found
Write-Output "Number of audio files found: $numberOfFiles"
foreach ($file in $audioFiles) {
ConvertToWav24bit -fileFullName $file.FullName -outputFolder $convertFolderPath
}
# Completion Message
Write-Host "Conversion to 24-bit WAV completed for all files. Press Enter to exit."
Read-Host
# Ask for the folder path
$folderPath = Read-Host "Enter the full path to the folder containing audio files (e.g., 'C:\Media\Folder')"
# Ask for the desired audio format
$audioFormat = Read-Host "Enter the desired audio format (e.g., 'aac', 'mp3', 'wav')"
# Ask for the desired bitrate
$bitrate = Read-Host "Enter the desired bitrate (e.g., '192k')"
# Ensure FFmpeg is available
$ffmpegPath = "ffmpeg" # Assumes FFmpeg is in the system PATH
# Validate inputs
if (-not (Test-Path $folderPath)) {
Write-Host "The specified folder does not exist."
exit
}
if (-not $audioFormat) {
Write-Host "Please provide a valid audio format."
exit
}
if (-not $bitrate) {
Write-Host "Please provide a valid bitrate."
exit
}
# Remove quotes from folder path if present
$folderPath = $folderPath.Trim('"')
# Create a subfolder named 'converted' if it doesn't exist
$convertedFolderPath = Join-Path -Path $folderPath -ChildPath "converted"
if (-not (Test-Path $convertedFolderPath)) {
New-Item -ItemType Directory -Path $convertedFolderPath | Out-Null
}
# Supported audio file extensions
$audioExtensions = @(".mp3", ".wav", ".flac", ".aac", ".ogg", ".m4a")
# Get audio files in the folder
$audioFiles = Get-ChildItem -Path $folderPath -File | Where-Object {
$audioExtensions -contains $_.Extension.ToLower()
}
# Process each audio file
foreach ($file in $audioFiles) {
$inputFilePath = $file.FullName
$outputFileName = [System.IO.Path]::ChangeExtension($file.Name, $audioFormat)
$outputFilePath = Join-Path -Path $convertedFolderPath -ChildPath $outputFileName
# Build FFmpeg command to convert audio
$ffmpegCommand = "$ffmpegPath -i `"$inputFilePath`" -b:a $bitrate `"$outputFilePath`""
# Execute FFmpeg command
Write-Host "Processing $($file.Name)..."
Start-Process -NoNewWindow -Wait -FilePath "cmd.exe" -ArgumentList "/c $ffmpegCommand"
# Update progress
Write-Host "Completed processing $($file.Name)."
}
# Completion Message
Write-Host "All files converted. Press Enter to exit."
Read-Host
# Ask for the folder path
$folderPath = Read-Host "Enter the full path to the folder containing media files (e.g., \"C:\Media\Folder\")"
# Ask for start and end times
$endTime = Read-Host "Enter the end time for trimming (format HH:MM:SS, e.g., 00:02:30)"
# Ensure FFmpeg is available
$ffmpegPath = "ffmpeg" # Assumes FFmpeg is in the system PATH
# Validate inputs
if (-not (Test-Path $folderPath)) {
Write-Host "The specified folder does not exist."
exit
}
if (-not $endTime) {
Write-Host "Please provide the end time in the format HH:MM:SS (e.g., 00:02:30 for 2 minutes and 30 seconds)."
exit
}
# Remove quotes from folder path if present
$folderPath = $folderPath.Trim('"')
# Create a subfolder named 'trim' if it doesn't exist
$trimFolderPath = Join-Path -Path $folderPath -ChildPath "trim"
if (-not (Test-Path $trimFolderPath)) {
New-Item -ItemType Directory -Path $trimFolderPath | Out-Null
}
# Supported media file extensions
$mediaExtensions = @(".mp4", ".mkv", ".mov", ".avi", ".mp3", ".wav", ".flac")
# Get media files in the folder
$mediaFiles = Get-ChildItem -Path $folderPath -File | Where-Object {
$mediaExtensions -contains $_.Extension.ToLower()
}
# Process each media file
foreach ($file in $mediaFiles) {
$inputFilePath = $file.FullName
$outputFilePath = Join-Path -Path $trimFolderPath -ChildPath $file.Name
# Build FFmpeg command to trim after the end time
$ffmpegCommand = "$ffmpegPath -i `"$inputFilePath`" -ss $endTime -c copy `"$outputFilePath`""
# Execute FFmpeg command
Write-Host "Processing $($file.Name)..."
Start-Process -NoNewWindow -Wait -FilePath "cmd.exe" -ArgumentList "/c $ffmpegCommand"
# Update progress
Write-Host "Completed processing $($file.Name)."
}
# Completion Message
Write-Host "All files processed. Press Enter to exit."
Read-Host
# Ask for the folder path
$folderPath = Read-Host "Enter the full path to the folder containing media files (e.g., \"C:\Media\Folder\")"
# Ask for start and end times
$startTime = Read-Host "Enter the start time for trimming (format HH:MM:SS, e.g., 00:00:15)"
$endTime = Read-Host "Enter the end time for trimming (format HH:MM:SS, e.g., 00:23:40)"
# Ensure FFmpeg is available
$ffmpegPath = "ffmpeg" # Assumes FFmpeg is in the system PATH
# Validate inputs
if (-not (Test-Path $folderPath)) {
Write-Host "The specified folder does not exist."
exit
}
if (-not $startTime -or -not $endTime) {
Write-Host "Please provide both start and end times in the format HH:MM:SS (e.g., 00:01:30 for 1 minute and 30 seconds)."
exit
}
# Remove quotes from folder path if present
$folderPath = $folderPath.Trim('"')
# Create a subfolder named 'trim' if it doesn't exist
$trimFolderPath = Join-Path -Path $folderPath -ChildPath "trim"
if (-not (Test-Path $trimFolderPath)) {
New-Item -ItemType Directory -Path $trimFolderPath | Out-Null
}
# Supported media file extensions
$mediaExtensions = @(".mp4", ".mkv", ".mov", ".avi", ".mp3", ".wav", ".flac")
# Get media files in the folder
$mediaFiles = Get-ChildItem -Path $folderPath -File | Where-Object {
$mediaExtensions -contains $_.Extension.ToLower()
}
# Process each media file
foreach ($file in $mediaFiles) {
$inputFilePath = $file.FullName
$outputFilePath = Join-Path -Path $trimFolderPath -ChildPath $file.Name
# Build FFmpeg command
$ffmpegCommand = "$ffmpegPath -i `"$inputFilePath`" -ss $startTime -to $endTime -c copy `"$outputFilePath`""
# Execute FFmpeg command
Write-Host "Processing $($file.Name)..."
Start-Process -NoNewWindow -Wait -FilePath "cmd.exe" -ArgumentList "/c $ffmpegCommand"
# Update progress
Write-Host "Completed processing $($file.Name)."
}
# Completion Message
Write-Host "All files processed. Press Enter to exit."
Read-Host
# Ask for the folder path
$folderPath = Read-Host "Enter the full path to the folder containing media files (e.g., \"C:\Media\Folder\")"
# Ask for start and end times
$startTime = Read-Host "Enter the start time for trimming (format HH:MM:SS, e.g., 00:00:15)"
$endTime = Read-Host "Enter the end time for trimming (format HH:MM:SS, e.g., 00:23:40)"
# Ensure FFmpeg is available
$ffmpegPath = "ffmpeg" # Assumes FFmpeg is in the system PATH
# Validate inputs
if (-not (Test-Path $folderPath)) {
Write-Host "The specified folder does not exist."
exit
}
if (-not $startTime -or -not $endTime) {
Write-Host "Please provide both start and end times in the format HH:MM:SS (e.g., 00:01:30 for 1 minute and 30 seconds)."
exit
}
# Remove quotes from folder path if present
$folderPath = $folderPath.Trim('"')
# Create a subfolder named 'trim' if it doesn't exist
$trimFolderPath = Join-Path -Path $folderPath -ChildPath "trim"
if (-not (Test-Path $trimFolderPath)) {
New-Item -ItemType Directory -Path $trimFolderPath | Out-Null
}
# Supported media file extensions
$mediaExtensions = @(".mp4", ".mkv", ".mov", ".avi", ".mp3", ".wav", ".flac")
# Get media files in the folder
$mediaFiles = Get-ChildItem -Path $folderPath -File | Where-Object {
$mediaExtensions -contains $_.Extension.ToLower()
}
# Function to convert HH:MM:SS to seconds
function ConvertTo-Seconds($time) {
$parts = $time -split ":"
return [int]$parts[0] * 3600 + [int]$parts[1] * 60 + [int]$parts[2]
}
# Function to find the closest black screen time
function Find-ClosestBlackScreen($filePath, $targetTime) {
Write-Host "Analyzing black screen times for $($filePath)..."
$blackDetectCommand = "$ffmpegPath -i `"$filePath`" -vf blackdetect=d=0.1:pic_th=0.90 -an -f null - 2>&1"
$output = & cmd.exe /c $blackDetectCommand
$blackTimes = @()
foreach ($line in $output -split "`r`n") {
if ($line -match "black_start:(\d+\.\d+)") {
$blackTimes += [double]$matches[1]
}
}
$targetSeconds = ConvertTo-Seconds $targetTime
$closestTime = $blackTimes | Sort-Object { [math]::Abs($_ - $targetSeconds) } | Select-Object -First 1
$closestTimeFormatted = [TimeSpan]::FromSeconds($closestTime).ToString("hh\:mm\:ss")
Write-Host "Closest black screen to $targetTime is at $closestTimeFormatted"
return $closestTimeFormatted
}
# Process each media file
foreach ($file in $mediaFiles) {
$inputFilePath = $file.FullName
$outputFilePath = Join-Path -Path $trimFolderPath -ChildPath $file.Name
# Find closest black screen times
Write-Host "Finding closest black screen times for $($file.Name)..."
$adjustedStartTime = Find-ClosestBlackScreen $inputFilePath $startTime
$adjustedEndTime = Find-ClosestBlackScreen $inputFilePath $endTime
# Build FFmpeg command
$ffmpegCommand = "$ffmpegPath -i `"$inputFilePath`" -ss $adjustedStartTime -to $adjustedEndTime -c copy `"$outputFilePath`""
# Execute FFmpeg command
Write-Host "Processing $($file.Name) from $adjustedStartTime to $adjustedEndTime..."
Start-Process -NoNewWindow -Wait -FilePath "cmd.exe" -ArgumentList "/c $ffmpegCommand"
# Update progress
Write-Host "Completed processing $($file.Name)."
}
# Completion Message
Write-Host "All files processed. Press Enter to exit."
Read-Host
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment