Skip to content

feat: Add Radio and Podcast playing#4

Merged
devohmycode merged 2 commits intomainfrom
0.0.4
Mar 13, 2026
Merged

feat: Add Radio and Podcast playing#4
devohmycode merged 2 commits intomainfrom
0.0.4

Conversation

@devohmycode
Copy link
Owner

@devohmycode devohmycode commented Mar 13, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Radio streaming with station management and history
    • Podcast discovery, subscription, and episode playback with read tracking
    • Enhanced visualizer with live stream loopback capture
    • Multi-language installer support (English/French)
  • Documentation

    • Updated documentation with new radio and podcast feature details
  • Chores

    • Version updated to 0.0.4

- Updated installer version to 0.0.4.
- Added support for online radio streaming, including URL input, recent station management, and live playback indicators.
- Implemented podcast search and management functionality.
- Enhanced UI with new navigation buttons for Radio and Podcasts.
- Updated README to reflect new features and changes in application data structure.
- Added podcast search functionality via iTunes API.
- Implemented subscription management, including saving and loading subscriptions.
- Enhanced UI with new navigation for podcasts and updated episode tracking.
- Updated README to include new podcast features and data structure changes.
- Introduced .cursorindexingignore and .gitignore for SpecStory files.
@devohmycode devohmycode merged commit 8f06520 into main Mar 13, 2026
1 check was pending
@qodo-code-review
Copy link

Review Summary by Qodo

Add radio streaming and podcast management features

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Added radio streaming with URL input, station management, and live playback
• Implemented podcast search, subscription, and episode management features
• Enhanced UI with Radio and Podcasts navigation tabs, replaced Visualizer/Media with "..." menu
• Added WASAPI loopback capture for real-time spectrum visualization of live streams
• Integrated iTunes API for podcast discovery and RSS feed parsing for episodes
Diagram
flowchart LR
  A["Audio Player"] -->|"Stream URL"| B["Radio Streaming"]
  B -->|"WASAPI Loopback"| C["Live Spectrum"]
  D["iTunes API"] -->|"Search Query"| E["Podcast Discovery"]
  E -->|"Feed URL"| F["Episode Fetching"]
  F -->|"Play Episode"| A
  B -->|"Save Station"| G["Settings Manager"]
  E -->|"Subscribe"| G
  F -->|"Mark Read"| G
Loading

Grey Divider

File Changes

1. Audiomatic/MainWindow.xaml.cs ✨ Enhancement +846/-8

Radio and podcast UI implementation with view management

Audiomatic/MainWindow.xaml.cs


2. Audiomatic/Services/AudioPlayerService.cs ✨ Enhancement +75/-0

Stream playback support with buffering event handling

Audiomatic/Services/AudioPlayerService.cs


3. Audiomatic/Services/PodcastService.cs ✨ Enhancement +140/-0

New podcast search and episode management service

Audiomatic/Services/PodcastService.cs


View more (6)
4. Audiomatic/Services/SpectrumAnalyzer.cs ✨ Enhancement +113/-3

WASAPI loopback capture for live stream visualization

Audiomatic/Services/SpectrumAnalyzer.cs


5. Audiomatic/SettingsManager.cs ✨ Enhancement +31/-0

Radio station persistence and serialization

Audiomatic/SettingsManager.cs


6. Audiomatic/MainWindow.xaml ✨ Enhancement +103/-8

Radio and podcast UI containers with search controls

Audiomatic/MainWindow.xaml


7. installer.iss ⚙️ Configuration changes +13/-4

Version bump to 0.0.4 and multilingual support

installer.iss


8. .cursorindexingignore ⚙️ Configuration changes +3/-0

Exclude SpecStory auto-save files from indexing

.cursorindexingignore


9. README.md 📝 Documentation +26/-2

Document radio, podcast, and UI navigation changes

README.md


Grey Divider

Qodo Logo

@qodo-code-review
Copy link

qodo-code-review bot commented Mar 13, 2026

Code Review by Qodo

🐞 Bugs (5) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Stream handlers accumulate 🐞 Bug ⛯ Reliability
Description
AudioPlayerService.PlayStreamAsync attaches MediaPlayer.MediaFailed and
PlaybackSession.BufferingStarted/Ended handlers per stream play but never reliably detaches them, so
repeated stream plays accumulate callbacks and retain stale TaskCompletionSource closures. This can
cause duplicated BufferingChanged events, memory growth, and spurious callbacks after Stop().
Code

Audiomatic/Services/AudioPlayerService.cs[R204-224]

+            // Wait for the source to open before playing to let MediaPlayer buffer
+            var tcs = new TaskCompletionSource<bool>();
+            void onOpened(MediaPlayer mp, object args)
+            {
+                _mediaPlayer.MediaOpened -= onOpened;
+                tcs.TrySetResult(true);
+            }
+            void onFailed(MediaPlayer mp, MediaPlayerFailedEventArgs args)
+            {
+                _mediaPlayer.MediaFailed -= onFailed;
+                tcs.TrySetException(new Exception(args.ErrorMessage));
+            }
+            _mediaPlayer.MediaOpened += onOpened;
+            _mediaPlayer.MediaFailed += onFailed;
+
+            // Subscribe to buffering events
+            var session = _mediaPlayer.PlaybackSession;
+            session.BufferingStarted += (_, _) =>
+                _dispatcherQueue?.TryEnqueue(() => BufferingChanged?.Invoke(true));
+            session.BufferingEnded += (_, _) =>
+                _dispatcherQueue?.TryEnqueue(() => BufferingChanged?.Invoke(false));
Evidence
PlayStreamAsync adds buffering handlers on every call and does not remove them; additionally, the
temporary MediaFailed handler is only removed if MediaFailed fires, but not on success, keeping the
closure alive. Stop() clears Source and flags but does not unsubscribe buffering or temporary
handlers.

Audiomatic/Services/AudioPlayerService.cs[190-259]
Audiomatic/Services/AudioPlayerService.cs[289-307]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`AudioPlayerService.PlayStreamAsync` subscribes to buffering and failure/open events every time a stream is played, but does not reliably unsubscribe them. Over multiple radio/podcast plays this will accumulate handlers, duplicate UI callbacks, and retain closures.

### Issue Context
- `onFailed` is never removed on the success path.
- `PlaybackSession.BufferingStarted/Ended` are subscribed per stream, and `Stop()` does not remove them.

### Fix Focus Areas
- Audiomatic/Services/AudioPlayerService.cs[190-259]
- Audiomatic/Services/AudioPlayerService.cs[289-307]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Loopback sample format unsafe 🐞 Bug ⛯ Reliability
Description
SpectrumAnalyzer.StartLoopback always decodes captured bytes using BitConverter.ToSingle, which
assumes 32-bit float samples. If the loopback capture format is PCM (e.g., 16-bit), this will
misinterpret data and can throw due to reading 4 bytes per sample.
Code

Audiomatic/Services/SpectrumAnalyzer.cs[R98-113]

+            _loopback.DataAvailable += (_, args) =>
+            {
+                int channels = _loopback.WaveFormat.Channels;
+                int bytesPerSample = _loopback.WaveFormat.BitsPerSample / 8;
+                int sampleCount = args.BytesRecorded / bytesPerSample;
+
+                lock (_loopbackLock)
+                {
+                    for (int i = 0; i < sampleCount; i += channels)
+                    {
+                        float sample = 0;
+                        for (int c = 0; c < channels && (i + c) * bytesPerSample + bytesPerSample <= args.BytesRecorded; c++)
+                        {
+                            int offset = (i + c) * bytesPerSample;
+                            sample += BitConverter.ToSingle(args.Buffer, offset);
+                        }
Evidence
The loopback handler computes bytesPerSample from WaveFormat but still calls BitConverter.ToSingle
unconditionally. When bytesPerSample != 4, offsets are stepped by bytesPerSample but ToSingle still
reads 4 bytes, which can produce incorrect values and may throw near the end of the buffer.

Audiomatic/Services/SpectrumAnalyzer.cs[88-119]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Loopback capture decoding assumes 32-bit float samples (`BitConverter.ToSingle`) regardless of the actual `WasapiLoopbackCapture.WaveFormat`. Non-float formats will produce incorrect spectrum data and may throw.

### Issue Context
The code already reads `BitsPerSample` into `bytesPerSample` but does not use it to select a decoding path.

### Fix Focus Areas
- Audiomatic/Services/SpectrumAnalyzer.cs[88-129]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Artwork Uri can crash 🐞 Bug ⛯ Reliability
Description
Podcast list rendering constructs BitmapImage from new Uri(podcast.ArtworkUrl) without validation,
so a malformed artwork URL can throw UriFormatException on the UI thread. The artwork URL string is
taken directly from the iTunes API (and persisted) without validation.
Code

Audiomatic/MainWindow.xaml.cs[R2055-2063]

+        if (!string.IsNullOrEmpty(podcast.ArtworkUrl))
+        {
+            var img = new Image
+            {
+                Width = 44, Height = 44,
+                Stretch = Microsoft.UI.Xaml.Media.Stretch.UniformToFill
+            };
+            img.Source = new BitmapImage(new Uri(podcast.ArtworkUrl));
+            artGrid.Children.Add(img);
Evidence
BuildPodcastItem directly calls new Uri(podcast.ArtworkUrl) for any non-empty string.
PodcastService.SearchAsync reads artworkUrl100 as a string and does not validate it, so
invalid/corrupted values can propagate into UI rendering and crash.

Audiomatic/MainWindow.xaml.cs[2055-2063]
Audiomatic/Services/PodcastService.cs[35-44]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Podcast artwork URLs are treated as trusted and passed into `new Uri(...)` during UI rendering, which can throw on malformed input and crash the app.

### Issue Context
Artwork URLs come from the network and may also be persisted and later reloaded.

### Fix Focus Areas
- Audiomatic/MainWindow.xaml.cs[2041-2075]
- Audiomatic/Services/PodcastService.cs[26-46]
- Audiomatic/MainWindow.xaml.cs[2394-2400]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Radio status loses URL 🐞 Bug ✓ Correctness
Description
MainWindow.OnBufferingChanged builds the 'Playing:' status from RadioUrlBox.Text, but
PlayRadioStreamAsync clears RadioUrlBox after connecting. As a result, after a buffering cycle the
status can become 'Playing:' with an empty URL.
Code

Audiomatic/MainWindow.xaml.cs[R573-577]

+    private void OnBufferingChanged(bool isBuffering)
+    {
+        if (_player.IsStream)
+            RadioStatusText.Text = isBuffering ? "Buffering..." : "Playing: " + (RadioUrlBox.Text?.Trim() ?? "");
+    }
Evidence
The buffering handler reads from the textbox, but the textbox is explicitly cleared on successful
play; subsequent buffering end events will therefore render an empty URL in the status line.

Audiomatic/MainWindow.xaml.cs[573-577]
Audiomatic/MainWindow.xaml.cs[1736-1745]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Radio playback clears the URL textbox after connecting, but buffering status later uses the textbox text, causing the status to lose the stream URL.

### Issue Context
This is user-visible after any buffering start/end cycle.

### Fix Focus Areas
- Audiomatic/MainWindow.xaml.cs[573-577]
- Audiomatic/MainWindow.xaml.cs[1720-1760]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. JsonDocument not disposed 🐞 Bug ➹ Performance
Description
PodcastService.SearchAsync parses JSON with JsonDocument.Parse but never disposes the JsonDocument.
Repeated searches can leak unmanaged memory and increase process memory over time.
Code

Audiomatic/Services/PodcastService.cs[R30-36]

+        var url = $"https://itunes.apple.com/search?term={Uri.EscapeDataString(query)}&media=podcast&limit={limit}";
+        var json = await Http.GetStringAsync(url);
+        var doc = JsonDocument.Parse(json);
+
+        var results = new List<PodcastInfo>();
+        foreach (var item in doc.RootElement.GetProperty("results").EnumerateArray())
+        {
Evidence
JsonDocument is IDisposable; SearchAsync holds it in a local variable without a using scope, so
disposal is skipped on all paths.

Audiomatic/Services/PodcastService.cs[30-36]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`JsonDocument` returned by `JsonDocument.Parse` is never disposed in podcast search.

### Issue Context
This can accumulate across repeated searches.

### Fix Focus Areas
- Audiomatic/Services/PodcastService.cs[26-46]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@coderabbitai
Copy link

coderabbitai bot commented Mar 13, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c38ed0f9-0355-48e7-966b-73c9cd173302

📥 Commits

Reviewing files that changed from the base of the PR and between 91fc5de and 57bd73f.

📒 Files selected for processing (10)
  • .cursorindexingignore
  • .specstory/.gitignore
  • Audiomatic/MainWindow.xaml
  • Audiomatic/MainWindow.xaml.cs
  • Audiomatic/Services/AudioPlayerService.cs
  • Audiomatic/Services/PodcastService.cs
  • Audiomatic/Services/SpectrumAnalyzer.cs
  • Audiomatic/SettingsManager.cs
  • README.md
  • installer.iss

📝 Walkthrough

Walkthrough

The PR introduces radio streaming and podcast features to Audiomatic with new UI navigation views, a PodcastService for iTunes search and RSS episode parsing, MediaPlayer-based stream playback, WASAPI loopback capture for spectrum visualization during streaming, and local persistence for radio stations and podcast subscriptions.

Changes

Cohort / File(s) Summary
Configuration & Ignore Files
.cursorindexingignore, .specstory/.gitignore
Added cursor indexing ignore pattern for .specstory files and .specstory-specific gitignore for project metadata files.
UI Navigation & Layout
Audiomatic/MainWindow.xaml
Extended navigation with Radio and Podcasts views, added NavRadioBtn, NavPodcastBtn, NavMoreBtn with corresponding TextBlocks; added RadioContainer and PodcastContainer with search, URL input, playback controls, and list views; renamed previous nav buttons (PrevButton, NextButton).
UI Logic & Event Handlers
Audiomatic/MainWindow.xaml.cs
Introduced ViewMode enum variants (Radio, Podcast, PodcastEpisodes); added state fields for radio stations, podcasts, episodes, and playback flags; implemented radio stream playback flow (PlayRadioStreamAsync, RadioUrlBox_KeyDown, RadioPlay_Click) with history tracking and context actions; implemented podcast search and subscription flows (SearchPodcastsAsync, ShowPodcastEpisodesAsync, PlayPodcastEpisodeAsync) with read/unread episode tracking; enhanced media playback handlers (OnMediaOpened, OnMediaEnded) for live streams and podcast state; added transport control updates for stream mode; integrated spectrum loopback for streaming visualization.
Stream Playback & Buffering
Audiomatic/Services/AudioPlayerService.cs
Added BufferingChanged event and IsStream property; introduced PlayStreamAsync method for streaming URI playback via MediaPlayer with timeout, buffering state tracking, SystemMediaTransportControls integration, and error handling.
Podcast Discovery & Management
Audiomatic/Services/PodcastService.cs
New static service with PodcastInfo and PodcastEpisode records; SearchAsync queries iTunes API, FetchEpisodesAsync parses RSS feeds, load/save methods persist subscriptions and read-episode tracking via local JSON files.
Spectrum Loopback Capture
Audiomatic/Services/SpectrumAnalyzer.cs
Added WASAPI loopback capture with StartLoopback/StopLoopback methods, loopback ring buffer and sample rate tracking; GetSpectrum dispatches to loopback path when active; IsReady property updated to include loopback state; cleanup during initialization and disposal.
Radio Persistence
Audiomatic/SettingsManager.cs
Added RadioStation record with Url and Name; introduced LoadRadioStations and SaveRadioStations methods to persist radio station list via JSON.
Documentation & Installation
README.md, installer.iss
Updated README with Radio Streaming and Podcasts feature sections and data storage entries; bumped installer version to 0.0.4, enabled language selection, added English and French localization for auto-start and launch tasks.

Sequence Diagrams

sequenceDiagram
    participant User as User
    participant UI as MainWindow (UI)
    participant Radio as PlayRadioStreamAsync
    participant APS as AudioPlayerService
    participant MP as MediaPlayer
    participant Spectrum as SpectrumAnalyzer
    
    User->>UI: Enter radio URL + click Play
    UI->>Radio: PlayRadioStreamAsync()
    Radio->>APS: PlayStreamAsync(Uri)
    APS->>MP: Create MediaPlaybackItem
    APS->>MP: Wire MediaOpened/MediaFailed
    APS->>MP: Subscribe BufferingChanged
    APS->>MP: Play()
    MP-->>APS: MediaOpened (IsStream=true)
    APS->>Spectrum: StartLoopback()
    Spectrum->>Spectrum: Enable WASAPI capture
    APS-->>UI: PlaybackStarted
    UI->>UI: UpdateNowPlaying (Radio)
    UI->>Spectrum: GetSpectrum()
    Spectrum->>Spectrum: GetLoopbackSpectrum()
    Spectrum-->>UI: Spectrum data
    UI->>UI: DrawVisualization()
Loading
sequenceDiagram
    participant User as User
    participant UI as MainWindow (UI)
    participant PodService as PodcastService
    participant iTunes as iTunes API
    participant RSS as RSS Feed
    participant APS as AudioPlayerService
    
    User->>UI: Enter podcast search term
    UI->>PodService: SearchAsync(query)
    PodService->>iTunes: GET /search?term=...
    iTunes-->>PodService: JSON results
    PodService-->>UI: List[PodcastInfo]
    UI->>UI: Display search results
    User->>UI: Click Subscribe
    UI->>PodService: SaveSubscriptions()
    PodService->>PodService: Persist to podcasts.json
    User->>UI: Select podcast
    UI->>PodService: FetchEpisodesAsync(feedUrl)
    PodService->>RSS: GET RSS feed
    RSS-->>PodService: XML episodes
    PodService-->>UI: List[PodcastEpisode]
    UI->>UI: Display episodes
    User->>UI: Click Play episode
    UI->>APS: PlayStreamAsync(episodeAudioUrl)
    APS-->>UI: PlaybackStarted
    UI->>UI: UpdateNowPlaying (Podcast)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • PR #2: Introduced the initial visualizer and media control infrastructure (MainWindow structure, AudioPlayerService base, SettingsManager foundation) that this PR extends with streaming and podcast features.

Poem

🎙️ A rabbit hops through Radio waves,
Podcasts whisper in echo caves,
With WASAPI's loopback song,
Spectrum dances the whole night long! 🐰✨

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 0.0.4
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can suggest fixes for GitHub Check annotations.

Configure the reviews.tools.github-checks setting to adjust the time to wait for GitHub Checks to complete.

Comment on lines +204 to +224
// Wait for the source to open before playing to let MediaPlayer buffer
var tcs = new TaskCompletionSource<bool>();
void onOpened(MediaPlayer mp, object args)
{
_mediaPlayer.MediaOpened -= onOpened;
tcs.TrySetResult(true);
}
void onFailed(MediaPlayer mp, MediaPlayerFailedEventArgs args)
{
_mediaPlayer.MediaFailed -= onFailed;
tcs.TrySetException(new Exception(args.ErrorMessage));
}
_mediaPlayer.MediaOpened += onOpened;
_mediaPlayer.MediaFailed += onFailed;

// Subscribe to buffering events
var session = _mediaPlayer.PlaybackSession;
session.BufferingStarted += (_, _) =>
_dispatcherQueue?.TryEnqueue(() => BufferingChanged?.Invoke(true));
session.BufferingEnded += (_, _) =>
_dispatcherQueue?.TryEnqueue(() => BufferingChanged?.Invoke(false));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Stream handlers accumulate 🐞 Bug ⛯ Reliability

AudioPlayerService.PlayStreamAsync attaches MediaPlayer.MediaFailed and
PlaybackSession.BufferingStarted/Ended handlers per stream play but never reliably detaches them, so
repeated stream plays accumulate callbacks and retain stale TaskCompletionSource closures. This can
cause duplicated BufferingChanged events, memory growth, and spurious callbacks after Stop().
Agent Prompt
### Issue description
`AudioPlayerService.PlayStreamAsync` subscribes to buffering and failure/open events every time a stream is played, but does not reliably unsubscribe them. Over multiple radio/podcast plays this will accumulate handlers, duplicate UI callbacks, and retain closures.

### Issue Context
- `onFailed` is never removed on the success path.
- `PlaybackSession.BufferingStarted/Ended` are subscribed per stream, and `Stop()` does not remove them.

### Fix Focus Areas
- Audiomatic/Services/AudioPlayerService.cs[190-259]
- Audiomatic/Services/AudioPlayerService.cs[289-307]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +98 to +113
_loopback.DataAvailable += (_, args) =>
{
int channels = _loopback.WaveFormat.Channels;
int bytesPerSample = _loopback.WaveFormat.BitsPerSample / 8;
int sampleCount = args.BytesRecorded / bytesPerSample;

lock (_loopbackLock)
{
for (int i = 0; i < sampleCount; i += channels)
{
float sample = 0;
for (int c = 0; c < channels && (i + c) * bytesPerSample + bytesPerSample <= args.BytesRecorded; c++)
{
int offset = (i + c) * bytesPerSample;
sample += BitConverter.ToSingle(args.Buffer, offset);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Loopback sample format unsafe 🐞 Bug ⛯ Reliability

SpectrumAnalyzer.StartLoopback always decodes captured bytes using BitConverter.ToSingle, which
assumes 32-bit float samples. If the loopback capture format is PCM (e.g., 16-bit), this will
misinterpret data and can throw due to reading 4 bytes per sample.
Agent Prompt
### Issue description
Loopback capture decoding assumes 32-bit float samples (`BitConverter.ToSingle`) regardless of the actual `WasapiLoopbackCapture.WaveFormat`. Non-float formats will produce incorrect spectrum data and may throw.

### Issue Context
The code already reads `BitsPerSample` into `bytesPerSample` but does not use it to select a decoding path.

### Fix Focus Areas
- Audiomatic/Services/SpectrumAnalyzer.cs[88-129]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +2055 to +2063
if (!string.IsNullOrEmpty(podcast.ArtworkUrl))
{
var img = new Image
{
Width = 44, Height = 44,
Stretch = Microsoft.UI.Xaml.Media.Stretch.UniformToFill
};
img.Source = new BitmapImage(new Uri(podcast.ArtworkUrl));
artGrid.Children.Add(img);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. Artwork uri can crash 🐞 Bug ⛯ Reliability

Podcast list rendering constructs BitmapImage from new Uri(podcast.ArtworkUrl) without validation,
so a malformed artwork URL can throw UriFormatException on the UI thread. The artwork URL string is
taken directly from the iTunes API (and persisted) without validation.
Agent Prompt
### Issue description
Podcast artwork URLs are treated as trusted and passed into `new Uri(...)` during UI rendering, which can throw on malformed input and crash the app.

### Issue Context
Artwork URLs come from the network and may also be persisted and later reloaded.

### Fix Focus Areas
- Audiomatic/MainWindow.xaml.cs[2041-2075]
- Audiomatic/Services/PodcastService.cs[26-46]
- Audiomatic/MainWindow.xaml.cs[2394-2400]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant