Public/Add-SpotifyTracks.ps1
<# .SYNOPSIS Adds songs to your Spotify liked tracks .DESCRIPTION Accepts a list of tracks from a CSV or from another cmdlet in this module, searches for those songs on Spotify, and adds them to your Spotify liked tracks if found. .PARAMETER Tracks Mutually exclusive with InputFile. An array of SpotifyTracks, as generated by another cmdlet in this module, such as Get-TracksFromFolder or Get-SpotifyTracks. .PARAMETER InputFile Mutually exclusive with Tracks. The Path to a CSV file containing a list of tracks, where each track has values for three columns: name (song name), artists (a comma-separated list of artist names), and album (album name). .PARAMETER ClientId Optional. The ClientId of the app you registered in the Spotify developer portal. See the 'Authentication' section at https://github.com/niwamo/SpotifyUtils .PARAMETER RedirectURI Optional. The redirect URI used for OAuth authentication. Must match what is configured in the Spotify developer protal. See the 'Authentication' section at https://github.com/niwamo/SpotifyUtils .PARAMETER ConfigFile Optional. The path to a JSON configuration file containing 'ClientId' and 'RedirectURI' properties. See the 'Authentication' section at https://github.com/niwamo/SpotifyUtils .EXAMPLE Add-SpotifyTracks -Tracks $(Get-TracksFromFolder -Path ~\Songs) #> function Add-SpotifyTracks { [CmdletBinding()] param ( [Parameter(Mandatory=$false, ValueFromPipeline=$true, Position=0)] [Object[]] $Tracks, [ValidateScript({ Test-Path $_ })] [Parameter(Mandatory=$false)] [string] $InputFile, [Parameter(Mandatory=$false)] [string] $ClientId, [Parameter(Mandatory=$false)] [string] $RedirectURI, [ValidateScript({Test-Path $_})] [Parameter(Mandatory=$false)] [string] $ConfigFile ) begin { Set-StrictMode -Version 1.0 $ErrorActionPreference = 'Stop' $inputData = [System.Collections.ArrayList]::new() } process { if (!$Tracks -and !$InputFile) { throw "Either 'Tracks' or 'InputFile' required" } elseif ($Tracks) { $inputData.AddRange([array] $Tracks) | Out-Null } elseif ($InputFile.Substring($InputFile.Length-3, 3) -ne 'csv') { throw 'Only CSV files are supported for InputFile' } else { Write-Debug "Importing tracks from CSV" $inputData = Import-Csv -Path $InputFile } } end { Write-Debug "Received $($inputData.Count) tracks to add" $tracks = ConvertTo-SpotifyTrack -Tracks $inputData # authorization $TokenParams = @{ Scopes = @('user-library-modify') } foreach ($param in @('ClientId', 'RedirectURI', 'ConfigFile')) { if ($PSBoundParameters.ContainsKey($param)) { $TokenParams.Add($param, $PSBoundParameters.TryGetValue($param)) } } $token = Get-SpotifyToken @TokenParams $headers = @{ Authorization = "Bearer $token" } ########################## # Region: Add Tracks # ########################## $missing = [System.Collections.ArrayList]::new() $added = 0 foreach ($song in $tracks) { # sleep at beginning in case of a 'continue' [System.Threading.Thread]::Sleep($script:API_DELAY) # FIND SONG # https://stackoverflow.com/questions/73680222 $uri = [string]::Format( "{0}?type=track&q=artist:""{1}"" track:""{2}""", $script:SEARCH_URI, $song.artists[0], $song.name ) $results = ( Invoke-WebRequest -Uri $uri -Headers $headers ).Content | ConvertFrom-Json # HANDLE NULL RESULTS if (! $results.tracks.items -or $results.tracks.items.Count -eq 0) { $log = [string]::Format( "Could not find {0} by {1}; search results were null", $song.name, $song.artists[0] ) Write-Debug $log $missing.Add($song) | Out-Null continue } $top = $results.tracks.items[0] # CHECK MATCH $match = $true $comparisons = @( @($song.name, $top.name), @($song.artists[0], $top.artists[0].name) ) foreach ($set in $comparisons) { $cmp1, $cmp2 = $set $len = [math]::min($cmp1.Length, $cmp2.Length) $match = $cmp1.Substring(0, $len) -eq $cmp2.Substring(0, $len) if (! $match) { break } } # HANDLE MISSING MATCH if (! $match) { $logMessage = [string]::Format( "Could not find {0} by {1}, top result was {2} by {3}", $song.name, $song.artists[0], $top.name, $top.artists[0].name ) Write-Debug $logMessage $missing.Add($song) | Out-Null } # ADD MATCHED SONG $params = @{ URI = "$script:MYTRACKS_URI" Method = 'Put' ContentType = 'application/json' Body = "{""ids"":[""$($top.id)""]}" Headers = $headers } Invoke-WebRequest @params | Out-Null Write-Debug "Added $($song.name)" $added += 1 } Write-Output "${script:GREEN}Added $added songs${script:RESETANSI}" if ($missing.Count) { $msg = [string]::Format( "Failed to add {0} tracks, returning them in an array", $missing.Count ) Write-Warning $msg return $missing } } } |