• You can now help support WorldwideDX when you shop on Amazon at no additional cost to you! Simply follow this Shop on Amazon link first and a portion of any purchase is sent to WorldwideDX to help with site costs.
  • Click here to find out how to win free radios from Retevis!

ButtFuzz

Anti-BS Advocate ● WO0WOO ● Reverend Doctor
Aug 11, 2005
587
425
173
Sunny Salem, VA
robco.ath.cx
No, 357Magnum, this is not about +P!

I have a relatively robust music collection... mostly of filetypes .m4a and .mp3. There is one indispensable tool that I recommend to tag and rename a music collection and it does not run on Linux. The name of that tool is, strangely enough, mp3tag and you can give it a whirl here: https://www.mp3tag.de/en/ .

This tool makes it a delight to properly tag your files, and I also use it to delete any files with bit rates < 320 Kbps. When I purge these 'inferior' files, I am often left with empty directories. These empty directories can be somewhat difficult to remove manually, and I sure don't want them to continue being backed up by my nightly backup processes... and I really don't want these empty directories to be indexed by my streaming music server software.

So, as an exercise in futility I present 'try 0.1' at a PowerShell script to delete any directories in your production music collection containing no music files:

Code:
######################################################
#Remove-FoldersIfNoMusic.ps1
######################################################
######################################################
function Get-Today()
{
    return get-date -Format yyyy-MM-dd
}
######################################################
######################################################
$tailRecursion = {
    param(
        $Path
    )
    $today = Get-Today
    $logfile = "C:\TEMP\$today-FolderRemovalLog.txt"
    foreach ($childDirectory in Get-ChildItem -Force -LiteralPath $Path -Directory) {
        & $tailRecursion -Path $childDirectory.FullName
    }
    $currentChildren = Get-ChildItem -Force -LiteralPath $Path
    $isEmpty = $currentChildren -eq $null
    if ($isEmpty) {
        Write-Verbose "Removing empty folder at path '${Path}'." -Verbose
        Add-Content "Removing empty folder at path '${Path}'." -Path $logfile
        Remove-Item -Force -LiteralPath $Path
    }
}
#
# Call It like this:
# & $tailRecursion -Path 'C:\a'
######################################################
######################################################
<#
If you want to test it here's code that will create interesting test data (make sure you don't already have a folder c:\a because it will be deleted):
# This creates some test data under C:\a (make sure this is not
# a directory you care about, because this will remove it if it
# exists). This test data contains a directory that is hidden
# that should be removed as well as a file that is hidden in a
# directory that should not be removed.
Remove-Item -Force -Path C:\a -Recurse
New-Item -Force -Path C:\a\b\c\d -ItemType Directory > $null
$hiddenFolder = Get-Item -Force -LiteralPath C:\a\b\c
$hiddenFolder.Attributes = $hiddenFolder.Attributes -bor [System.IO.FileAttributes]::Hidden
New-Item -Force -Path C:\a\b\e -ItemType Directory > $null
New-Item -Force -Path C:\a\f -ItemType Directory > $null
New-Item -Force -Path C:\a\f\g -ItemType Directory > $null
New-Item -Force -Path C:\a\f\h -ItemType Directory > $null
Out-File -Force -FilePath C:\a\f\test.jpg -InputObject 'Dummy file'
Out-File -Force -FilePath C:\a\f\foo.mp3 -InputObject 'Dummy file'
Out-File -Force -FilePath C:\a\f\h\hidden.jpg -InputObject 'Hidden file'
Out-File -Force -FilePath C:\a\f\h\hidden.m4a -InputObject 'Hidden file'
$hiddenFile = Get-Item -Force -LiteralPath C:\a\f\h\hidden.jpg
$hiddenFile.Attributes = $hiddenFile.Attributes -bor [System.IO.FileAttributes]::Hidden
$hiddenFile = Get-Item -Force -LiteralPath C:\a\f\h\hidden.m4a
$hiddenFile.Attributes = $hiddenFile.Attributes -bor [System.IO.FileAttributes]::Hidden
#Here's how you use it. Note that this will remove the top folder
#(the C:\a folder in this example, which gets created if you generated the test data using the script above)
#if that folder winds up being empty after deleting all empty folders under it.
#>
CLS
$0dirfile = "C:\temp\0dirlist.txt"
Clear-Content $0dirfile
$ipath = (Read-Host "Please Enter The filepath to recurse and delete if no music files")
$ipath = $ipath.trim()
Add-Content $ipath -Path $0dirfile


$1dirfile = "C:\temp\1dirlist.txt"
Clear-Content $1dirfile
$path=(Get-Content $0dirfile)
$folders = Get-ChildItem $path -Recurse |where{$_.psiscontainer}|select -ExpandProperty fullname  #list all subfolders under the directory
foreach($folder in $folders){
$judge=Get-ChildItem $folder -Recurse |where {$_.fullname -like "*.m4a" -or $_.name -like "*.mp3"}  #filter the folders have music files
if(!$judge){
write-host "$folder has no music files"}
add-content $folder -Path $1dirfile
}

#Don't boof the pooch!
#Run a single recursion FIRST so as not to delete a folder of folders, you bonehead!

$2dirfile = "C:\temp\2dirlist.txt"
Clear-Content $2dirfile
$noboofpaths = (Get-Content $1dirfile)
ForEach ($path in $noboofpaths)
{
$folders = Get-ChildItem $path -Recurse |where{$_.psiscontainer}|select -ExpandProperty fullname  #list all subfolders under the directory
foreach($folder in $folders){
$judge=Get-ChildItem $folder -Recurse |where {$_.fullname -like "*.m4a" -or $_.name -like "*.mp3"}  #filter folders have music file
if(!$judge){
write-host "$folder has no music files"}
add-content $folder -Path $2dirfile
}
}

# Remove Secondary and Tertiary Directories containing no music.

$path = ""
$paths = ""
$paths = (Get-Content $2dirfile)
foreach ($path in $paths)
{
$path = $path.Trim()
& $tailRecursion -Path $path
}

# Remove the rest! Bye-Bye!

$path = ""
$paths = ""
$paths = (Get-Content $0dirfile)
foreach ($path in $paths)
{
$path = $path.Trim()
& $tailRecursion -Path $path
}

######################################################
######################################################

Change the file extensions to suit your liking.
There will be 'warnings' the first time you run it because certain files do not exist yet.
Log files, etc. are created @ C:\TEMP
 

You've built a lot of safeguards in there to make sure you don't accidentally remove things, and the logging is a nice touch. Good work!

Sorry you have to deal with PowerShell, but at least you're making the best of it.
 
  • Like
Reactions: 357magnum
Rob :whistle::LOL: , I know you are a Big Music Buff . But I wish it was + P I would have understood it better !;) All my vinyl is still in jackets , Cd's in plastic cases . So when I read about " Power Shell " It was Blah , Blah , Blah to me !:LOL: Dee's Truck has music loaded in , it has a USB plug ( I think ) how it got in that radio .... Damn if I know !:ROFLMAO:
 
  • Like
Reactions: S&W357
You've built a lot of safeguards in there to make sure you don't accidentally remove things, and the logging is a nice touch. Good work!

Sorry you have to deal with PowerShell, but at least you're making the best of it.

Non-exhaustive testing reveals try .1 SUCKS. So many safeguards and a few syntax errors guarantees it won't remove ANYTHING, much less granny from her rocker.

v .02 coming right quick like....
 
Ok, here is version .00000000002. See original post for purpose.
This one seems to mostly work!


Code:
######################################################
#Remove-FoldersIfNoMusic.ps1
######################################################
######################################################
function Get-Today()
{
    return get-date -Format yyyy-MM-dd
}
######################################################
######################################################
$tailRecursion = {
    param(
        $Path
    )
    $today = Get-Today
    $logfile = "C:\TEMP\$today-FolderRemovalLog.txt"
    foreach ($childDirectory in Get-ChildItem -Force -LiteralPath $Path -Directory) {
        & $tailRecursion -Path $childDirectory.FullName
    }
    $currentChildren = Get-ChildItem -Force -LiteralPath $Path
    $isEmpty = $currentChildren -eq $null
    if ($isEmpty) {
        Write-Verbose "Removing empty folder at path '${Path}'." -Verbose
        Add-Content "Removing empty folder at path '${Path}'." -Path $logfile
        Remove-Item -Force -LiteralPath $Path
    }
}
#
# Call It like this:
# & $tailRecursion -Path 'C:\a'
######################################################
######################################################
<#
If you want to test it here's code that will create interesting test data (make sure you don't already have a folder c:\a because it will be deleted):
# This creates some test data under C:\a (make sure this is not
# a directory you care about, because this will remove it if it
# exists). This test data contains a directory that is hidden
# that should be removed as well as a file that is hidden in a
# directory that should not be removed.
Remove-Item -Force -Path C:\a -Recurse
New-Item -Force -Path C:\a\b\c\d -ItemType Directory > $null
$hiddenFolder = Get-Item -Force -LiteralPath C:\a\b\c
$hiddenFolder.Attributes = $hiddenFolder.Attributes -bor [System.IO.FileAttributes]::Hidden
New-Item -Force -Path C:\a\b\e -ItemType Directory > $null
New-Item -Force -Path C:\a\f -ItemType Directory > $null
New-Item -Force -Path C:\a\f\g -ItemType Directory > $null
New-Item -Force -Path C:\a\f\h -ItemType Directory > $null
Out-File -Force -FilePath C:\a\f\test.jpg -InputObject 'Dummy file'
Out-File -Force -FilePath C:\a\f\foo.mp3 -InputObject 'Dummy file'
Out-File -Force -FilePath C:\a\f\h\hidden.jpg -InputObject 'Hidden file'
Out-File -Force -FilePath C:\a\f\h\hidden.m4a -InputObject 'Hidden file'
$hiddenFile = Get-Item -Force -LiteralPath C:\a\f\h\hidden.jpg
$hiddenFile.Attributes = $hiddenFile.Attributes -bor [System.IO.FileAttributes]::Hidden
$hiddenFile = Get-Item -Force -LiteralPath C:\a\f\h\hidden.m4a
$hiddenFile.Attributes = $hiddenFile.Attributes -bor [System.IO.FileAttributes]::Hidden
#Here's how you use it. Note that this will remove the top folder
#(the C:\a folder in this example, which gets created if you generated the test data using the script above)
#if that folder winds up being empty after deleting all empty folders under it.
#>
CLS
$0dirfile = "C:\temp\0dirlist.txt"
Clear-Content $0dirfile
$ipath = (Read-Host "Please Enter The filepath to recurse and delete if no music files")
$ipath = $ipath.trim()
Add-Content $ipath -Path $0dirfile


$1dirfile = "C:\temp\1dirlist.txt"
Clear-Content $1dirfile
$path=(Get-Content $0dirfile)
$folders = Get-ChildItem $path -Recurse -Force | where {$_.psiscontainer} | select -ExpandProperty fullname  #list all subfolders under the directory
foreach($folder in $folders){
$judge=Get-ChildItem $folder -Recurse -Force | where {$_.name -like "*.m4a" -or $_.name -like "*.mp3"}  #filter if the folders have music files
if(!$judge){
write-host "$folder has no music files"
add-content $folder -Path $1dirfile}
}

# List the individual files scheduled for removal.

$2dirfile = "C:\temp\2dirlist.txt"
Clear-Content $2dirfile
$path = ""
$paths = ""
$paths = (Get-Content $1dirfile)
foreach($path in $paths)
{
    $path = $path.Trim()
    Remove-Item -Path $path -Recurse # -WhatIf
    Add-Content "Deleted $path" -Path $2dirfile
}

# Remove the all-falls! Bye-Bye!

$path = ""
$paths = ""
$paths = (Get-Content $0dirfile)
foreach ($path in $paths)
{
$path = $path.Trim()
& $tailRecursion -Path $path
}

######################################################
######################################################
 
Non-exhaustive testing reveals try .1 SUCKS. So many safeguards and a few syntax errors guarantees it won't remove ANYTHING, much less granny from her rocker.

v .02 coming right quick like....

Better than nuking your collection, or wiping the OS. Don't ask how I know.

Just remember, doctors get to bury their mistakes. Coders have to support theirs forever.
 
  • Like
Reactions: Shadetree Mechanic

dxChat
Help Users
  • No one is chatting at the moment.
  • @ kopcicle:
    If you know you know. Anyone have Sam's current #? He hasn't been on since Oct 1st. Someone let him know I'm looking.
  • dxBot:
    535A has left the room.
  • @ AmericanEagle575:
    Just wanted to say Good Morning to all my Fellow WDX members out there!!!!!