Public/Get-MusicForProgramming.ps1
|
function Get-MusicForProgramming { <# .SYNOPSIS Returns Music For Programming episodes — local collection first, then online. .DESCRIPTION By default, checks the local music directory for downloaded episodes and returns those. If no local files are found (or the directory does not exist), fetches the full episode list from the RSS feed at musicforprogramming.net. Use -Local to restrict results to only locally downloaded files without querying the internet at all. Output objects always have EpisodeNumber and Title. Local episodes include FilePath; online episodes include Url, Duration, and PublishedDate. .PARAMETER EpisodeNumber Filter to a specific episode number. If the episode is not in the local collection, it is fetched from the RSS feed automatically (unless -Local). .PARAMETER Path Directory to scan for locally downloaded MFP files. Defaults to ~/Music/MusicForProgramming. Override for all commands at once via: $PSDefaultParameterValues['*-MusicForProgramming:Path'] = 'D:\MyMusic' .PARAMETER Local Return only locally downloaded episodes. Does not query the RSS feed even if no local files are found. .EXAMPLE Get-MusicForProgramming Returns local episodes if any are downloaded, otherwise lists all episodes available online. .EXAMPLE Get-MusicForProgramming | Format-Table EpisodeNumber, Title, Duration Browse all available episodes in a table. .EXAMPLE Get-MusicForProgramming -EpisodeNumber 42 Returns episode 42 — from local collection if present, otherwise from the feed. .EXAMPLE Get-MusicForProgramming -Local Returns only locally downloaded episodes, no network request. .EXAMPLE Get-MusicForProgramming | Where-Object Title -like '*Datassette*' Finds all Datassette episodes. #> [CmdletBinding()] [OutputType([PSCustomObject])] param( [Parameter(Position = 0)] [int]$EpisodeNumber, [Parameter()] [string]$Path = $script:DefaultMusicPath, [Parameter()] [switch]$Local ) # Check for a local collection $localEpisodes = $null if (Test-Path $Path -PathType Container) { $localEpisodes = Get-LocalMusicForProgramming -Path $Path } if ($Local) { # Caller explicitly wants only local files $results = $localEpisodes } elseif ($localEpisodes) { Write-Verbose "Returning $($localEpisodes.Count) local episode(s) from '$Path'" $results = $localEpisodes } else { Write-Verbose "No local files found in '$Path'; fetching RSS feed" $results = Get-MFPFeedEpisodes } if ($PSBoundParameters.ContainsKey('EpisodeNumber')) { $filtered = $results | Where-Object { $_.EpisodeNumber -eq $EpisodeNumber } # If a specific episode was requested but not found locally, fall back to RSS if (-not $filtered -and -not $Local -and $localEpisodes) { Write-Verbose "Episode $EpisodeNumber not in local collection; fetching from RSS" $filtered = Get-MFPFeedEpisodes | Where-Object { $_.EpisodeNumber -eq $EpisodeNumber } } if (-not $filtered) { Write-Warning "Episode $EpisodeNumber was not found." } return $filtered } $results } # --------------------------------------------------------------------------- # Private helper — not exported # --------------------------------------------------------------------------- function Get-MFPFeedEpisodes { <# Fetches and parses the MFP RSS feed into episode objects. #> [CmdletBinding()] param() $rssUrl = 'https://musicforprogramming.net/rss.xml' Write-Verbose "Fetching RSS feed from $rssUrl" try { [xml]$rss = (Invoke-WebRequest -Uri $rssUrl -UseBasicParsing).Content } catch { throw "Failed to fetch RSS feed from ${rssUrl}: $_" } $nsManager = New-Object System.Xml.XmlNamespaceManager($rss.NameTable) $nsManager.AddNamespace('itunes', 'http://www.itunes.com/dtds/podcast-1.0.dtd') foreach ($item in $rss.rss.channel.item) { $episodeNum = 0 $artist = $item.title if ($item.title -match 'Episode (\d+): (.+)') { $episodeNum = [int]$Matches[1] $artist = $Matches[2].Trim() } $durationNode = $item.SelectSingleNode('itunes:duration', $nsManager) $duration = if ($durationNode) { $durationNode.InnerText } else { '' } [PSCustomObject]@{ PSTypeName = 'MusicForProgramming.Episode' EpisodeNumber = $episodeNum Title = $artist FullTitle = $item.title Url = $item.enclosure.url Duration = $duration PublishedDate = [datetime]$item.pubDate PageUrl = $item.link } } } |