Created
August 11, 2025 14:44
-
-
Save Venipa/f04c51e318c003cda0a0b1632a31b1e3 to your computer and use it in GitHub Desktop.
Moves video files under folders to the upper level and adopts the folder name.
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
# Moves video files under folders to the upper level and adopts the folder name. | |
# for example FolderA\FileA.mp4 => FolderA.mp4 | |
# duplicates will append a number, FolderA_<1-100>.mp4, max 100 files can be moved to the upper level. | |
# | |
# Use cases: | |
# - Movie folders with the movie inside | |
# - Single Album Covers with single or multiple of the same cover. | |
# | |
# Check if the first argument is a folder; otherwise, prompt for a folder | |
# Check if the first argument exists and is a folder; if so, use it as the base directory | |
# Use WPF Open Directory Dialog instead of WinForms | |
Add-Type -AssemblyName PresentationFramework | |
function Select-FolderDialogWPF { | |
$dialog = New-Object Microsoft.Win32.OpenFileDialog | |
$dialog.CheckFileExists = $false | |
$dialog.FileName = "Select Folder" | |
$dialog.ValidateNames = $false | |
$dialog.Filter = "Folders|." | |
if ($dialog.ShowDialog() -eq $true) { | |
return ([System.IO.Path]::GetDirectoryName($dialog.FileName)) | |
} else { | |
return $null | |
} | |
} | |
if ($args.Count -gt 0 -and (Test-Path $args[0] -PathType Container)) { | |
$baseDir = $args[0] | |
} else { | |
$baseDir = Select-FolderDialogWPF | |
if (-not $baseDir) { | |
Write-Host "No folder selected. Exiting." -ForegroundColor Red | |
exit | |
} | |
} | |
function Exit-WithAnyKey { | |
Write-Host "Press any key to exit..." | |
[void][System.Console]::ReadKey($true) | |
exit | |
} | |
if ($baseDir -like "C:\Windows*" -or (Resolve-Path $baseDir).ProviderPath -like "C:\Windows*") { | |
Write-Host "Error: The selected base directory is inside C:\Windows. Exiting for safety." -ForegroundColor Red | |
Exit-WithAnyKey | |
} | |
# Check for dry-run parameter | |
$dryRun = $false | |
if ($args -contains "-dry" -or $args -contains "--dry-run" -or $args -contains "-d") { | |
$dryRun = $true | |
Write-Host "DRY RUN MODE - No files will be moved" -ForegroundColor Yellow | |
Write-Host "" | |
} | |
# Get all subfolders in the base directory | |
$folders = Get-ChildItem -Path $baseDir -Directory | |
Write-Host "Processing folders in: $baseDir" -ForegroundColor Cyan | |
Write-Host "" | |
foreach ($folder in $folders) { | |
Write-Host "Processing folder: $($folder.Name)" -ForegroundColor Green | |
# Get all .mp4 and .mkv files in the folder | |
$mediaFiles = Get-ChildItem -Path $folder.FullName -File | Where-Object { $_.Name -match '\.(mp4|mkv)$' } | |
foreach ($mediaFile in $mediaFiles) { | |
$ext = $mediaFile.Extension | |
$newBaseName = $folder.Name | |
$newName = "$newBaseName$ext" | |
# Check for duplicates and append _<number> if needed, try up to 100 times | |
$baseNameOnly = [System.IO.Path]::GetFileNameWithoutExtension($folder.Name) | |
$tryName = "$baseNameOnly$ext" | |
$counter = 1 | |
while ((Test-Path (Join-Path -Path $baseDir -ChildPath $tryName)) -and ($counter -le 100)) { | |
$tryName = "${baseNameOnly}_$counter$ext" | |
$counter++ | |
} | |
$newName = $tryName | |
$destPath = Join-Path -Path $baseDir -ChildPath $newName | |
if ($dryRun) { | |
Write-Host " Would move: $($mediaFile.Name) -> $newName" -ForegroundColor White | |
Write-Host " From: $($mediaFile.FullName)" -ForegroundColor Gray | |
Write-Host " To: $destPath" -ForegroundColor Gray | |
} else { | |
Write-Host " Moving: $($mediaFile.Name) -> $newName" -ForegroundColor White | |
Move-Item -Path $mediaFile.FullName -Destination $destPath -Force | |
} | |
Write-Host "" | |
} | |
} | |
if ($dryRun) { | |
Write-Host "DRY RUN COMPLETE - No files were moved" -ForegroundColor Yellow | |
Write-Host "Run the script without -dry, --dry-run, or -d to actually move the files" -ForegroundColor Cyan | |
Exit-WithAnyKey | |
} else { | |
Write-Host "File conversion complete!" -ForegroundColor Green | |
Exit-WithAnyKey | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment