0

I have mixed music projects, where the software generates a txt file list of the songs in each project. The problem is that the list is generated with only the first 36 characters of the name of each song.

I need to correct the names inside the txt file, so that they are the same as the names of the original files and my script can copy the files using the list.

I created a script, in two blocks, where in the first block I read the original files and generate a variable with the same name as the name inside the txt file.

First block (what I managed to do):
Read the original files, in the Original Musics folder, and create the variable with the same name as the one inside the List.txt file.

Goal of the first block (what I couldn't do):
When the name in the variable in this block is the same as the name inside List.txt, the script will change the name inside List.txt to the full name of the original file to which the variable is related. Thus, the List.txt file can be read in the second block with the correct names inside it.

Note:
It may happen that there are two files with the same name in the Original Musics folder, but with different versions, in this situation, the two full names of these files must be in the List.txt file to be copied to the destination folder. eg.

Name in List.txt file:
Got To Be Real (CM_Gex Bootleg Exten

Original name (full name in the Original Musics folder):

Cheryl Lynn - Got To Be Real (CM_Gex Bootleg Extended SHORT Re Mix).mp3
Cheryl Lynn - Got To Be Real (CM_Gex Bootleg Extended LONG Re Mix).mp3

Variable $ShortOriginalSongName:

Got To Be Real (CM_Gex Bootleg Exten
Got To Be Real (CM_Gex Bootleg Exten

As it is the same file, the difference is that one is the long version and the other is the short version, both full names must be in the List.txt file, that is, in addition to changing the names in the list.txt, when the files are the same, the script must include the original full names in the List.txt

Script:

$files = "C:\Users\$env:username\Desktop\List.txt"
$location = "G:\Original Musics\"
$destination = "C:\Users\$env:username\Desktop\Copy Selected Musics\"


# First block:
Get-ChildItem -Recurse $location* -Include *.mp3 | % {
    $ShortOriginalSongName = $_.Basename.Split("-")[1].Trim();
    If ($ShortOriginalSongName.Length -gt 36) {$ShortOriginalSongName = $ShortOriginalSongName.Substring(0, 36)};
    };

# Second Block:
Get-Content $files | % {

  $result = gci -recurse "$location*$_" -Include *.mp3
  
  If($result) { 
    Add-Content $destination"AddList.txt" -Value $result.Fullname
    $musicName = $result.Name    
    $tot+=1       
    Write-Host -ForegroundColor Green "$musicName found on $location!"
    Write-Host "$musicName copied to $destination..."
    Copy-Item $result.FullName -Destination $destination\$($_.Name)
  } 
}

Second Block:
Read the List.txt file that was generated with the correct names of the songs in the first block, and copy the original files to the Copy Selected Musics folder.

8
  • Sample filename format would help --- $_.BaseName.Split('-')[1] is unexplained, Commented May 25, 2022 at 16:14
  • @Keith Miller I edited my question and added more information, the information you requested is on the line below original name (full name in the Original Musics folder):
    – Clamarc
    Commented May 25, 2022 at 17:05
  • Bonus points for your selection of sample data -- had to pull up "Star Love" on YouTube and indulge in a moment of nostalgia! :D Commented May 25, 2022 at 18:47
  • I'm going to compose an answer with suggested modifications to your code. Question: *Do any of the filenames have characters with special meaning to **PowerShell strings, such as square brackets ( [ or ] ) , dollar sign ( $ )? If so, some file ops may require the -LiteralPath parameter... Commented May 25, 2022 at 18:54
  • I'll add that if a song name has a dash, the existing split[1] would fail. Another way to do it is $ShortOriginalSongName = -join (($_.Basename.Split("-")|select -Skip 1).Trim().toCharArray() | select -first 36)
    – Cpt.Whale
    Commented May 25, 2022 at 19:09

1 Answer 1

1

Your code has a lot of repeated calls to Get-ChildItem for $Location, so I suggest creating a hashtable that associates short names with their associated files.

You want to:

  1. Round up your files:

    gci $Location *.mp3 -File -Recurse
    
  2. create a custom object consisting of the calculated short name and the source FileInfo object:

    [PSCustomObject]@{
        'ShortName' = $_.BaseName.Split('-')[-1].TrimStart().PadRIght(36).Substring(0, 36).TrimEnd()
        'FileInfo'  = $_
    }
    
  3. Group those by short name ( since multiple files can produce the same short name).

So we start with this:

$files       = "C:\Users\$env:username\Desktop\List.txt"
$location    = "G:\Original Musics\"
$destination = "C:\Users\$env:username\Desktop\Copy Selected Musics\"


$ShortNameList = gci $Location *.mp3 -File -Recurse | %{
    [PSCustomObject]@{
        'ShortName' = $_.BaseName.Split('-')[-1].TrimStart().PadRight(36).Substring(0, 36).TrimEnd()
        'FileInfo'  = $_
}} | Group ShortName

Then convert that object to a hashtable:

$ShortNameLookup = $ShortNameList | ForEach { $hash = @{} } {
    $hash.Add($_.Name , @( $_.Group.FileInfo ))
} { $hash }

Then the core processing uses each entry in $files as the hash key:

Get-Content $files | %{
    If ( $FileInfo = $ShortNameLookup[$_] ) {
        Add-Content ( Join-Path $Destination 'AddList.txt' ) $FileInfo.FullName
        $FileInfo | %{
            Write-Host ( '"{0}" found in "{1}"' -f $_.BaseName , $_.DirectoryName)
        }
        $FileInfo | Copy-Item -Destination $Destination
    }
    Else { Write-Host "No files found that match '$_'." }
}

Untested, but pretty sure the logic is sound. Wanted to get the code up -- will add notes later this evening.

12
  • Error Exception when calling "Add" with "2" argument(s): Key cannot be null. $hash.Add($_.ShortName , @( $_.Group.FileInfo )) Maybe this error occurs because the name in List.txt has a maximum of 36 characters, but 19, 6, 16 characters can occur. Notice in my script the If ($ShortOriginalSongName.Length -gt 36) to only set the $musicNameRight if that length is greater than 36. I noticed now is that in the original folder, files which contain more than one -, eg. 03 - Cheryl Lynn - Got To Be Real so the ShortName must be made from the last -.
    – Clamarc
    Commented May 26, 2022 at 1:45
  • The multiple hyphen issue is easy enough to solve. If you always want the content to the right of the final hyphen in the basename, we just change the index into the .Split() result to [-1] -- I'll edit that. Commented May 26, 2022 at 5:10
  • OK, the $hash.Add() error was due to the fact that since I had grouped on ShortName, that was now the Name property of the GroupInfo object, That is now fixed. Commented May 26, 2022 at 7:08
  • I was in a hurry earlier, and hadn't even added the .SubString() to the code. I avoided the length test and branching by using .PadRigth(36) before .SubString(0,36) and split the trimming into two parts so leading spaces are removed before the substring is created. Should be good now or quite close. Commented May 26, 2022 at 7:13
  • OK, I'll test the code... just one more question: what if the name of the original song contains some traction, eg. Cheryl Lynn - Got To Be Real (CM_Gex Bootleg Extended - SHORT Re Mix) note that after Extended has a dash how will the ShortName command handle this?
    – Clamarc
    Commented May 26, 2022 at 7:34

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .