functions/Get-TrackMetadata.ps1


function Get-TrackMetadata {
<#
.SYNOPSIS
    Retrieves metadata from audio files using TagLibSharp (read-only).
.DESCRIPTION
    Accepts files via pipeline or scans a directory (with -Filter). Returns a
    normalized object with common + catalog tags. Supports parallel processing.
.PARAMETER Path
    One or more file paths (accepts pipeline: string or FileInfo.FullName).
.PARAMETER SourcePath
    Root folder to scan for audio files (use with -Filter).
.PARAMETER Filter
    File glob filter when using -SourcePath (default: '*.aiff').
.PARAMETER TagLibPath
    Optional path to TagLibSharp.dll.
.PARAMETER Parallel
    Use parallel processing (PowerShell 7+).
.PARAMETER ThrottleLimit
    Max parallel workers when -Parallel (default: 8).
.PARAMETER ModulePath
    When using -Parallel, path to the module that contains the helper functions
    (so each runspace can Import-Module and find Read-TrackMetadataSingle).
.EXAMPLE
    Get-ChildItem ~/Music -Recurse -Include *.mp3 | Get-TrackMetadata -Parallel -ThrottleLimit 12
.EXAMPLE
    Get-TrackMetadata -SourcePath "/Users/username/Music" -Filter "*.aiff"
.EXAMPLE
    Get-TrackMetadata -Path "/Users/username/Music/track.flac","/Users/username/Music/track.m4a"
#>

    [CmdletBinding(DefaultParameterSetName='ByPath')]
    param(
        [Parameter(ParameterSetName='ByPath',
                   ValueFromPipeline,
                   ValueFromPipelineByPropertyName)]
        [Alias('FullName','LiteralPath')]
        [string[]]$FilePath,

        [Parameter(Mandatory, ParameterSetName='FromDirectory')]
        [string]$SourcePath,

        [Parameter(ParameterSetName='FromDirectory')]
        [string]$Filter = '*.aiff',

        [switch]$Parallel,

        [int]$ThrottleLimit = 8,
        [string]$ModulePath
        
    )

    begin {
        Write-Verbose "[BEGIN] Get-TrackMetadata called with ParameterSet: $($PSCmdlet.ParameterSetName)"
        # Collect paths in memory; we process in 'end' (enables clean parallelism).
        $allPaths = New-Object System.Collections.Generic.List[string]
    }

    process {
        Write-Verbose "[PROCESS] Get-TrackMetadata processing"
        switch ($PSCmdlet.ParameterSetName) {
            'ByPath' {
                foreach ($p in $FilePath) {
                    $resolved = Resolve-AudioPath -InputObject $p
                    if ($resolved) { [void]$allPaths.Add($resolved) }
                }
            }
            'FromDirectory' {
                if (-not (Test-Path -LiteralPath $SourcePath)) {
                    throw "SourcePath not found: $SourcePath"
                }
                Get-ChildItem -LiteralPath $SourcePath -Recurse -File -Filter $Filter |
                    ForEach-Object {
                        $resolved = Resolve-AudioPath -InputObject $_
                        if ($resolved) { [void]$allPaths.Add($resolved) }
                    }
            }
        }
    }

    end {
        Write-Verbose "[END] Get-TrackMetadata processing $($allPaths.Count) files"
        if ($allPaths.Count -eq 0) { return }

        if ($Parallel) {
            $allPaths | ForEach-Object -Parallel {
                # Re-import the module in each parallel session
                if ($using:ModulePath) {
                    Import-Module $using:ModulePath -Force
                } else {
                    Import-Module psmusictagger -Force
                }
                Read-TrackMetadataSingle -FilePath $_ #-TagLibPath $using:TagLibPath
            } -ThrottleLimit $ThrottleLimit 
        } else {
            $allPaths | ForEach-Object {
                Read-TrackMetadataSingle -FilePath $_ #-TagLibPath $TagLibPath
            }
        }
    }
}