powershell – Analyse des dossiers les plus profonds de l’arborescence et recherche du parent de niveau supérieur le moins profond qui correspond aux conditions

powershell – Analyse des dossiers les plus profonds de l’arborescence et recherche du parent de niveau supérieur le moins profond qui correspond aux conditions

2023-08-02 07:57:49

Dans Powershell, je construis un script plus performant, qui cible un partage de lecteur NAS SMB accessible à partir d’un PC Win10 Pro, qui rencontre de nombreux problèmes pour résumer une structure de répertoires.

Résultats souhaités et résumé global : Cela commence par les dossiers les plus profonds des différentes branches d’un arbre. À ce niveau, il détermine s’il correspond aux conditions (a les extensions répertoriées sous $fileExtensions), puis si les extensions musicales non ciblées occupent moins de 30 % du dossier par rapport aux fichiers musicaux $fileExtensions. Le script sélectionne jusqu’à 3 fichiers et formats aléatoires non consécutifs et concatène ces fichiers avec les arguments et les envoie à exiftool.exe pour ensuite analyser les fichiers de ces dossiers si les champs de métadonnées choisis existent. Si ceux-ci existent, le dossier passe aux conditions d’évaluation de la profondeur par rapport à d’autres profondeurs de dossier relatives, il enregistrera celles qui correspondent à la table ToplevelFolder, mais il ne doit pas revenir au dossier source, il ne doit supposer que des dossiers sous le répertoire source. D’autres cas sont couverts comme les dossiers -gt 30 % qui peuvent être traités par eux-mêmes sans récursivité, puis les fichiers individuels (un cas pour cela est si des fichiers cibles se trouvent dans le répertoire source).

En ce moment, pour 1 exemple, j’obtiens des résultats pour Ariana Grande comme ceux-ci alors qu’en fait leurs 2 principaux dossiers parents sont ‘.Ariana Grande 2016 Discography’ & ‘.Ariana Grande – iTunes Discography’. Tous les fichiers musicaux sont au format m4a et remplissent les conditions

Dossier du toplevel_v2 : .Ariana Grande 2016 DiscographyAlbums2016 Dangerous Woman (Deluxe) [Clean]

Dossier du toplevel_v2 : .Ariana Grande 2016 DiscographyAlbums2014 Yours Truly (Japanese Version)

Dossier du toplevel_v2 : .Ariana Grande 2016 DiscographyAlbums2014 My Everything (Japanese Version)

Dossier du toplevel_v2 : .Ariana Grande 2016 DiscographyAlbums2014 My Everything (Deluxe)

Dossier du toplevel_v2 : .Ariana Grande 2016 DiscographyAlbums2013 Yours Truly

Dossier du toplevel_v2 : .Ariana Grande – iTunes DiscographyStudio Albums2014.08.27 My Everything (Japanese Version)

Dossier du toplevel_v2 : .Ariana Grande – iTunes DiscographyStudio Albums2014.08.25 My Everything (Deluxe)

Dossier du toplevel_v2 : .Ariana Grande – iTunes DiscographyStudio Albums2014.02.05 Yours Truly (Japanese Version)

Dossier du toplevel_v2 : .Ariana Grande – iTunes DiscographyStudio Albums2013.01.01 Yours Truly

Dossier du toplevel_v2 : .Ariana Grande – iTunes DiscographySingles2014.08.13 Baby I (Feat. Taro Hakase) – Single jp

voici des journaux plus détaillés de mon script :

——–Début du nouveau dossier : .Ariana Grande 2016 DiscographyAlbums2014 My Everything (Deluxe)——

Il s’agit du ratio : 0 % Nombre de fichiers de dossiers : 15 File d’attente sans extension de fichier : 0

Nombre de RandFile avant la vérification des métadonnées : 3 RandFiles Fichier de métadonnées requis trouvé. Traiter tout le dossier

Profondeur de correspondance la plus faible : 3

——-Fin de la boucle Full FolderObj Nombre : 53—–

——Début du nouveau dossier : .Ariana Grande 2016 DiscographyAlbums2013 Yours Truly—-

Il s’agit du ratio : 0 % Nombre de fichiers de dossiers : 13 File d’attente non FileExtension : 0

Nombre de RandFile avant la vérification des métadonnées : 2 RandFiles Fichier de métadonnées requis trouvé. Traiter tout le dossier

Profondeur de correspondance la plus faible : 3

—–Fin de la boucle Full FolderObj Nombre : 54—–

—–Début du nouveau dossier : .Ariana Grande – iTunes DiscographyStudio Albums2014.08.27 My Everything (Japanese Version)——

Il s’agit du ratio : 0 % Nombre de fichiers de dossiers : 18 File d’attente non FileExtension : 0

Nombre de RandFile avant la vérification des métadonnées : 3 RandFiles Fichier de métadonnées requis trouvé. Traiter le dossier entier.

Profondeur de correspondance la plus faible : 3

—–Fin de la boucle Full FolderObj Nombre : 55—-

—–Début du nouveau dossier : .Ariana Grande – iTunes DiscographyStudio Albums2014.08.25 My Everything (Deluxe)—-

Il s’agit du ratio : 0 % Nombre de fichiers de dossiers : 15 File d’attente sans extension de fichier : 0

Nombre de RandFile avant la vérification des métadonnées : 3 RandFiles Fichier de métadonnées requis trouvé. Traiter tout le dossier

Profondeur de correspondance la plus faible : 3

——Fin du nombre complet de boucles FolderObj : 56——-

—–Début du nouveau dossier : .Ariana Grande – iTunes DiscographyStudio Albums2014.02.05 Yours Truly (Japanese Version)——

Il s’agit du ratio : 0 % Nombre de fichiers de dossiers : 15 File d’attente sans extension de fichier : 0

Nombre de RandFile avant la vérification des métadonnées : 3 RandFiles Fichier de métadonnées requis trouvé. Traiter tout le dossier

Profondeur de correspondance la plus faible : 3

—————Fin de la boucle Full FolderObj Count: 57———————> Ou Ces 2 ont le même situation:

  1. Z:Music!CompletePavarotti Edition 10+1BonusCD-OrigCD1 Donizetti Arias
  2. Z:Music!CompletePavarotti Edition 10+1BonusCD-OrigCD10 Chansons populaires napolitaines et italiennes
  3. Z:Music!CompletePavarotti Edition 10+1BonusCD-OrigCD11 Bonus – L’EP de 1964
  4. Z:Music!CompletePavarotti Edition 10+1BonusCD-OrigCD2 Bellini,Donizetti,Verdi
  5. Z:Music!CompletePavarotti Edition 10+1BonusCD-OrigCD3 Verdi 1
  6. Z:Music!CompletePavarotti Edition 10+1BonusCD-OrigCD4 Verdi 2
  7. Z:Music!CompletePavarotti Edition 10+1BonusCD-OrigCD5 Puccini
  8. Z:Music!CompletePavarotti Edition 10+1BonusCD-OrigCD6 Puccini et Verismo
  9. Z:Music!CompletePavarotti Edition 10+1BonusCD-OrigCD7 Arias 1
  10. Z:Music!CompletePavarotti Edition 10+1BonusCD-OrigCD8 Arias 2
  11. Z:Music!CompletePavarotti Edition 10+1BonusCD-OrigCD9 Chansons italiennes

Le script commence par définir les extensions de fichier sur et sans intérêt (pour éviter que les fichiers non musicaux soient comptés avec des pourcentages et des nombres). Il construit ensuite des variables RegEx pour modifier ces 2 variables d’extension à utiliser pour exécuter des calculs lors de l’analyse des répertoires. Je crée ensuite des vars compilées pour ces Regex.

Avec $MetadataFields je définis les champs de métadonnées cibles qui seront utilisés par le programme exiftool.exe (la lecture avec l’outil a des formats d’arguments différents de ceux de l’écriture). La boucle for prend ensuite ces champs, les place dans une seule chaîne et ajoute un tiret par extension et des espaces entre eux.

$sourceDirectory = Get-Location
$ScriptMainBlocktoRun = {
    $FileExtensions = @('.m4a', '.alac')
    $nonFileMusicExts = @('.3gp', '.aiff', '.ape', '.dsd', '.flac', '.mp3', '.mp4', '.ra', '.ogg', '.opus', '.tta', '.wav', '.wma', '.wv')
    # Join the extensions into a single regular expression
    $FileExtensionsRegex = '.(' + (($FileExtensions -replace '.', '') -join '|') + ')$'
    # Combine file extensions and non-file music extensions into a single regex pattern
    $CombinedExtensions = $FileExtensions + $nonFileMusicExts
    $CombinedExtensionsRegex = '.(' + (($CombinedExtensions -replace '.', '') -join '|') + ')$'
    $compilFileExtensionsRegex = [regex]::new($FileExtensionsRegex, [System.Text.RegularExpressions.RegexOptions]::Compiled)
    $compilCombinedExtsRegex = [regex]::new($CombinedExtensionsRegex, [System.Text.RegularExpressions.RegexOptions]::Compiled) 
    $MetadataFields = [System.Collections.Generic.List[String]]::new() 
    [void]$MetadataFields.Add( [string[]]("AppleStoreCountry","AppleStoreAccountType","Certificate","AppleStoreCatalogID") )
    # Create a StringBuilder instance
    $metaDataFieldList = [System.Text.StringBuilder]::new()
    # Loop through the metadata fields and append to StringBuilder
    for ($i = 0; $i -lt $MetadataFields.Count; $i++) {
        $MetadataField = "-$($MetadataFields[$i])"
        [void]$metaDataFieldList.Append($MetadataField)
        if ($i -lt ($MetadataFields.Count - 1)) {
            [void]$metaDataFieldList.Append(" ")
        }
    }
    # Get the final concatenated string
    $metaDataFieldList = $metaDataFieldList.ToString()
    $folderObjLoopCount = [int]0
    $shallowestMatchingFolderDepth = [int]::MaxValue
    function Get-RandomFilesBard {
        param (
            [Array]$ext_Files,
            [int]$ext_FilesCount,
            [Int]$NumFiles = 3
        )
        If ($ext_Files.Count -eq 0) { return $null
        } elseif ($ext_Files.Count -le $NumFiles) {
            return $ext_Files
        } else {    
        $randomIndexes = [System.Collections.Generic.List[String]]::new()
            $usedIndexes = [System.Collections.Generic.List[String]]::new()
            $randomGen = [System.Random]::new()
            for ($i = 0; $i -lt $NumFiles; $i++) {
                $randomIndex = $i + [Math]::Floor($randomGen.NextDouble() * ($ext_Files.Count - $i) )
                while ($usedIndexes.Contains($randomIndex)) {
                                            $i++
                    $randomIndex = $i + [Math]::Floor($randomGen.NextDouble() * ($ext_Files.Count - $i) )
                }
                [void]$randomIndexes.Add($randomIndex)
                [void]$usedIndexes.Add($randomIndex)
            } 
            return $ext_Files[$randomIndexes] }
    }
    function HasMetadataFields {
        param ( [Array]$FilePath    ) 
        # Enclose each file path with single quotes
            $formattedPaths = $FilePath | ForEach-Object { "'" + $_.Replace("'", "''") + "'" }
        # Join the paths into a single string, separated by spaces
            $joinedPaths = [String]::Join(' ', $formattedPaths)
            $command = "& exiftool.exe -charset UTF8 -s -s -s $metaDataFieldList $joinedPaths"
            $output = "& $command"
            return ($null -ne $output)
    }
            function Get-MaxCounts {
                param($folders)
                #Used for Percentage Diagnostics 
                $maxAllCount = ($folders | Measure-Object -Property { $_.allFiles.Count } -Maximum).Maximum
                $maxExtAllCount = ($folders | Measure-Object -Property { $_.ext_AllFiles.Count } -Maximum).Maximum
                return @{
                    AllFilesCount = [Math]::Floor([Math]::Log10($maxAllCount) + 1)
                    ExtAllFilesCount = [Math]::Floor([Math]::Log10($maxExtAllCount) + 1)
                }
            }
    # Get all directories and files
    Write-Color "Indexing Directories and Files..."-Color DarkYellow
    $folderPaths = [System.IO.Directory]::GetDirectories($sourceDirectory, "*", [System.IO.SearchOption]::AllDirectories)
    $allDirectoriesAndFiles = [System.IO.Directory]::GetFileSystemEntries($sourceDirectory, "*.*", [System.IO.SearchOption]::AllDirectories)
    $lookupTable = [System.Collections.Generic.Dictionary[string,System.Collections.Generic.List[string]]]::new()
    #Build the lookup table
    foreach ($path in $allDirectoriesAndFiles) {
        # Check if the entry is a file
        if ([System.IO.File]::Exists($path)) {
            # If it's a file, add it to the lookup table for its directory and all parent directories
            $directory = [System.IO.Path]::GetDirectoryName($path)
             while ($null -ne $directory -and $directory.StartsWith($sourceDirectory) -and $directory -ne '') {
                if (-not $lookupTable.ContainsKey($directory)) {
                    $lookupTable[$directory] = [System.Collections.Generic.List[string]]::new()
                }
                [void]$lookupTable[$directory].Add($path)
                # Move up to the parent directory
                $directory = [System.IO.Path]::GetDirectoryName($directory)
            }
        }
    }

        $allFolders = [System.Collections.Generic.List[object]]::new()
        $matchedFolders = [System.Collections.Generic.List[object]]::new()
        $CurrentMatchingSubFoldersTable = [System.Collections.Generic.Dictionary[string,string]]::new()
        $matchedFolderTable = [System.Collections.Generic.Dictionary[string,object]]::new()
        $fileTable = [System.Collections.Generic.Dictionary[string,string]]::new()
        $TopLevelFolderTable = [System.Collections.Generic.List[String]]::new()

        # Loop through each folder and process them
        foreach ($folderPath in $folderPaths) {
            $allNonfiles_and_Files = [System.Collections.Generic.List[string]]::new()
            $Parent = (Split-Path $folderPath -Parent)
            $Depth = ($folderPath.Split('')).Count
            $allFiles = $lookupTable[$folderPath]
            if ($allFiles -eq $null) { Continue } 
            $allNonfiles_and_Files = $allFiles | Where-Object { $compilCombinedExtsRegex.IsMatch($_) }
            $Files = $allFiles | Where-Object { (Split-Path $_ -Parent) -eq $folderPath }
            $Nonfiles_and_Files = $Files | Where-Object { $compilCombinedExtsRegex.IsMatch($_)  }
            if ($allNonfiles_and_Files.Count -le 0) { continue
            } else {
                $ext_AllFiles = $allNonfiles_and_Files | Where-Object { $compilFileExtensionsRegex.IsMatch($_) }
                $ext_Files = $Nonfiles_and_Files | Where-Object { $_ -match $FileExtensionsRegex }
                    $nonExtAllFilesCount = $allNonfiles_and_Files.Count - $ext_AllFiles.Count
                $NonMusicFileExt_Percentage = [Math]::Round(($nonExtAllFilesCount / $allNonfiles_and_Files.Count)*100,2)
                if ($Nonfiles_and_Files.Count -le 0) {continue} else {
                    $currentFolderFilesPercentage = [Math]::Round(($ext_Files.Count / $Nonfiles_and_Files.Count) * 100, 2)
                }
                $folderObj = New-Object PSObject -Property @{
                    FullName = $folderPath
                    Depth = $Depth
                    Parent = $Parent
                    AllFiles = $allNonfiles_and_Files ;                     Files = $Nonfiles_and_Files  ;
                    ext_AllFiles = $ext_AllFiles ;                          ext_Files =  $ext_Files ;           ext_FileCount = $ext_Files.Count
                    nonExtAllFilesCount = $nonExtAllFilesCount
                    NonMusicFileExt_Percentage = $NonMusicFileExt_Percentage
                    CurrentFolderFilesPercentage = $currentFolderFilesPercentage
                    Match = [System.Collections.ArrayList]::new()
                }
                [void]$allFolders.Add($folderObj)
                If   ( $folderObj.NonMusicFileExt_Percentage -gt 30 -and $folderObj.ext_Files.Count -gt 0 ) {
                    If ( $folderObj.CurrentFolderFilesPercentage -gt 49 ) {
                        $modifiedPath = "'$($folderObj.FullName)'."   # Add a backslash and a period at the end, and enclose with apostrophes
                        $CurrentMatchingSubFoldersTable.add($folderObj.FullName, $modifiedPath) | Out-Null
                    } else {
                        foreach ($file in $folderObj.ext_Files) {
                            if ($compilFileExtensionsRegex.IsMatch($file)) {
                                $modifiedFile = $file  # non enclosed for ProcessChunks Function
                                $fileTable[$file] = $file
                            }
                        }
                    }
                } else {continue}
            }
        }
        $totalAllFilesCount = ($allFolders | Measure-Object -Property { $_.allFiles.Count } -Sum).Sum       ;   $totalExtAllFilesCount = ($allFolders | Measure-Object -Property { $_.ext_AllFiles.Count } -Sum).Sum    
    #------Diagnotics Outputs for Percentage----------- # Output total and maximum counts
            $messageTTLFiles = "                    Total AllFiles Count: {0}       Total extAllFiles Count: {1}" -f $totalAllFilesCount, $totalExtAllFilesCount
            [System.Console]::WriteLine($messageTTLFiles)
    # Now we have a list of folder objects
    $matchedFolders = $allFolders | Where-Object { $_.ext_AllFiles.Count -gt 0 -and (($_.NonFileExtensionPercentage -le 30) -or ($_.ext_FilesCount -gt 0)) } | Sort-Object Depth -Descending
    [System.Console]::WriteLine("Indexing & Sorting - Applicable Folders...")   
    [System.Console]::WriteLine("Matched Folders: $($matchedFolders.Count)`n")
    $FolderTable = [System.Collections.Generic.Dictionary[string,object]]::new()
    foreach ($folderObj in $matchedFolders) {
        [void]$FolderTable.Add($folderObj.FullName, $folderObj)
    }
    [System.Console]::WriteLine("Refining - to Parents...") 
    # Create a new array to hold the final folder objects
        $finalFolders = [System.Collections.Generic.List[object]]::new()
            foreach ($folderObj in $matchedFolders) {
                $isChild = $false
                foreach ($folderPath in $FolderTable.Keys) {
                    if ($folderObj.FullName.StartsWith($folderPath) -and $folderObj.FullName -ne $folderPath) {
                        $isChild = $true
                        break
                    }
                }
        if (-not $isChild) {
            [void]$finalFolders.Add($folderObj)
            }
        }
    $finalFolders = $finalFolders | Sort-Object Depth -Descending
    foreach ($folderObj in $finalFolders) {
        Write-Color "--------------","Start of new folder: ",".$([System.IO.Path]::GetRelativePath($sourceDirectory, $folderObj.FullName))","---------------"-Color Magenta,Gray,DarkYellow,Magenta
        Write-Color "This is the ratio: $($folderObj.NonMusicFileExt_Percentage)%   ","Folder FileCount: ","$($folderObj.Files.Count)","    NonFileExtension Queue: ","$($folderObj.nonExtAllFilesCount)"-Color DarkGray,DarkYellow,DarkGray,DarkYellow,DarkGray
        if (($folderObj.ext_Files.Count -eq 0) -and ($folderObj.ext_allFiles.Count -gt 0) -and ($folderObj.NonMusicFileExt_Percentage -le 30)) {
            $folderRelDepth = $folderObj.Depth - ($sourceDirectory -split '\').Count
        } elseif ($folderObj.ext_Files.Count -gt 0) {
            $randfiles = Get-RandomFilesBard -ext_Files $($folderObj.ext_Files) -ext_FilesCount $($folderObj.ext_Files.Count) ; [System.Console]::Write("RandFile count before metadatacheck: ") ; Write-Color "$($randFiles.count)"-Color DarkYellow
            if ($randFiles.Count -eq 0) {continue } else {
                if (HasMetadataFields -FilePath $randFiles) {
                    # Use the top-level parent instead of the immediate parent
                    $folderRelDepth = $folderObj.Depth - ($sourceDirectory -split '\').Count
                    [System.Console]::Write("RandFiles Found required metadata file. Process entire Folder: ") ; Write-Color ".$([System.IO.Path]::GetRelativePath($sourceDirectory, $folderObj.FullName))"-Color DarkYellow
                }
            }
        }

        if (($folderRelDepth -le $shallowestMatchingFolderDepth)) {
            $shallowestMatchingFolderDepth = $folderRelDepth
            [System.Console]::WriteLine("Shallowest Matching Depth: $($shallowestMatchingFolderDepth)")
            $TopMostMatchingFolder = ".$([System.IO.Path]::GetRelativePath($sourceDirectory, $folderObj.FullName))"
            [System.Console]::WriteLine("The Matching Folder: $TopMostMatchingFolder")
                [void]$TopLevelFolderTable.Add($folderObj.FullName)
        }
    }
    #A console summary of Folders found
    ForEach ($item2 in $TopLevelFolderTable) {
        $item2 = ".$([System.IO.Path]::GetRelativePath($sourceDirectory, $item2))"
        [System.Console]::WriteLine("Folder of the toplevel_v2: $item2")
    } ; [System.Console]::WriteLine("---- TotalFolders: $TopLevelFolderTable.Count ")
    #A console summary of Folders found, that had greater than 30% NonFileExtensionPercentage and other conditions were not applicable so these folders are considered a non-recursive batch with these folders where each full folder path needs to end in a period
    ForEach ($item3 in $CurrentMatchingSubFoldersTable.Keys) {
        $item3 = (".$([System.IO.Path]::GetRelativePath($sourceDirectory, $item3))")
        [System.Console]::WriteLine("Matching SpecialFolders: $item3" )
    }
    # And a list of individual files, A console summary of Folders found, that had greater than 30% NonFileExtensionPercentage and other conditions were 
    foreach ($filePath in $fileTable.Keys) {
        $consoleListPath = (".$([System.IO.Path]::GetRelativePath($sourceDirectory, $filePath))")
        [System.Console]::WriteLine("This is a file: $consoleListPath")
    }

$extArgs = $FileExtensions | ForEach-Object { "-ext $_" }
$isFile = $true
    # For matched folders
    $TopLevelFolderTable | ForEach-Object {
        import-module $m
        $isFile = $false
        $Recursion = $true
        ProcessFolder $_ $extArgs $isFile $Recursion $TopLevelFolderTable
    }
    # For special folders (CurrentMatchingSubFoldersTable)
    $CurrentMatchingSubFoldersTable | ForEach-Object{
        import-module $m
        $isFile = $false
        $Recursion = $false
        ProcessFolder $($_) $extArgs $isFile $Recursion $specialFolderArray
    }
    # For files
    $fileArray = @($fileTable.Values | ForEach-Object { "$_" })
        ProcessFileChunks $fileTable.Keys $extArgs $m
#End of Nain ScriptBloack
}
& $ScriptMainBlocktoRun

#powershell #Analyse #des #dossiers #les #profonds #larborescence #recherche #parent #niveau #supérieur #moins #profond #qui #correspond #aux #conditions
1690956780

Facebook
Twitter
LinkedIn
Pinterest

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.