iTunes xml

Hi Guys,

Has anyone tried comparing 2 iTunes libraries for missing albums. What I’ve got so far is

#Get contents of Library.xml
[xml]$PCDrive = Get-Content “C:\Scripts\OfficePC.xml”
[xml]$NAS = Get-Content “C:\Scripts\Diskstation.xml”

#Get Albums from Library.xml
$PCAlbums = $PCDrive…Album
$NASAlbums = $NASDrive…Album

#Compare Albums (and Output to file)
Compare-Object -ReferenceObject $PCAlbums -DifferenceObject $NASAlbums


I’ve not tried using xml before and have tried looking at the xml but can’t work out what I need to put into $PCAlbums and $NASAlbums from the files (Sample Attached)

Any help would be greatly appreciated - I’ve over 600 albums and one has not synced so I thought PS would be a good tool

Thanks
Tony

That is a seriously ugly XML format… way to go, Apple. :\ Instead of using XML elements or attributes to identify things, they seem to be doing everything with a key element followed by some data type (integer , string , dict , etc). That means the order of the elements in the file is important, and things like XPath (or simple PowerShell code) are just about worthless for navigating its contents.

Here’s an example of what their ridiculous XML file should have looked like (note: All angle brackets have been replaced with square brackets, because the angle brackets screw with our forum software at the moment.)

[?xml version="1.0" encoding="UTF-8"?]
[RootNode]
    [MajorVersion]1[/MajorVersion]
    [MinorVersion]1[/MinorVersion]
    [Date]2014-07-30T23:00:36Z[/Date]
    [ApplicationVersion]11.3[/ApplicationVersion]
    [Features]5[/Features]
    [MusicFolder]file://localhost/D:/Users/Public/Public%20Music/iTunes/[/MusicFolder]
    [LibraryPersistentID]BDD9B4CE963E9B3C[/LibraryPersistentID]

    [Tracks]
        [Track]
            [TrackID]1263[/TrackID]
            [Album]Bigger, Better, Faster, More![/Album]
        [/Track]
    [/Tracks]
[/RootNode]

Here’s what the code to pull album names out of a file with that sane format would have been:

$xml = [xml](Get-Content -Path .\test.xml)

$albums = $xml.RootNode.Tracks.Track.Album | Get-Unique

And here’s what the code to extract album names from their actual format might look like… 70+ lines to accomplish the work that should have been done with two:

function Import-AlbumsFromStupidITunesXml
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateScript({
            if (Test-Path -LiteralPath $_)
            {
                return $true
            }

            throw "Path '$_' does not exist."
        })]
        [string]
        $Path
    )

    $xml = [xml](Get-Content -LiteralPath $Path)

    $xml.plist.dict |
    Get-ValueForAppleDictionaryKey -KeyName Tracks -ValueType dict |
    Get-ValueForAppleDictionaryKey -ValueType dict |
    Get-ValueForAppleDictionaryKey -KeyName Album -ValueType string |
    ForEach-Object { $_.InnerText } |
    Get-Unique
}

function Get-ValueForAppleDictionaryKey
{
    param (
        [Parameter(ValueFromPipeline = $true)]
        [ValidateScript({
            if ($_.Name -eq 'dict') { return $true }
            throw 'Only  elements may be passed to this command'
        })]
        [System.Xml.XmlElement]
        $Dictionary,

        [string]
        $KeyName,

        [ValidateSet('integer', 'string', 'dict')]
        [string]
        $ValueType
    )

    process
    {
        if ($null -eq $Dictionary)
        {
            return
        }

        $childCount = $Dictionary.ChildNodes.Count

        for ($i = 0; $i -lt $childCount-1; $i++)
        {
            $node = $Dictionary.ChildNodes[$i]
            $nextNode = $Dictionary.ChildNodes[$i + 1]

            if ($node.Name -eq 'key' -and
                ([string]::IsNullOrEmpty($KeyName) -or $node.InnerText -eq $KeyName) -and
                ([string]::IsNullOrEmpty($ValueType) -or $nextNode.Name -eq $ValueType))
            {
                $nextNode
                $i++
            }
        }
    }
}

$albums = Import-AlbumsFromStupidITunesXml -Path '.\test.xml'
$albums