Conversation
My first take as basis for discussions. I added micrososfts configuration.json package to parse application.json. Hope this is compatible with the AOT and multi target goal. Not yet clear how to really structure the config. I tend to have a List<playlists> to be able to have one level of 'category' like "Classic-Cds", "Pop-Cds", "Web-Radios". On the other hand I want to avoid to much keystrokes to navigate. So That would be a Command line switch where I need to address somehow which playlist to use. (by "cat"-"Name" or some - self generated unique IDs?). Or I could start with a category only and directly reach the according playlist selection menu....
Replaced hierarchical playlist management with a flat structure loaded from `userSettings.json`, removing reliance on `Microsoft.Extensions.Configuration` and `appsettings.json`. Refactored `PlaylistService` to deserialize playlists directly from JSON, simplifying the API and removing unused classes (`Category`, `MenuNode`). Updated `Playlist` to use `QueueItem[]` for better alignment with `Sharpcaster.Models.Queue`. Simplified `QueueController` logic for playlist selection and casting.
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdds playlist support across the console app: new models (Playlist, UserSettingsModel), a PlaylistService loading userSettings.json, DI wiring, CLI parsing for playlist IDs, command execution to play playlists, queue UI to load playlists, and a new “Back” option in media URL selection. Serialization context and tests updated accordingly. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant User as User
participant App as Console App
participant Flow as ApplicationFlows
participant QC as QueueController
participant PS as PlaylistService
participant Dev as DeviceService/Chromecast
User->>App: Start / Open menu
App->>Flow: Show main actions
User-->>Flow: Select "Cast playlist"
Flow->>QC: CastPlaylistAsync()
QC->>PS: List/validate playlists
PS-->>QC: Playlist selection/instance
QC->>Dev: ConnectToDeviceQuietAsync()
Dev-->>QC: Connected (join/launch app)
QC->>Dev: QueueLoadAsync(playlist.QueueItems)
Dev-->>QC: Load status
QC-->>Flow: Result (success/error)
Flow-->>User: Display outcome
sequenceDiagram
autonumber
participant User as User
participant MC as MediaController
participant UI as UIHelper
User->>MC: CastMediaAsync()
MC->>UI: Prompt URL options ["Back", ...]
alt User selects "Back"
UI-->>MC: "Back"
MC-->>User: Return without loading media
else User selects a URL
UI-->>MC: Selected URL
MC->>MC: Proceed to cast selected media
MC-->>User: Status/result
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
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. Comment |
There was a problem hiding this comment.
Pull Request Overview
This PR adds configurable playlist functionality to the SharpCaster console application, allowing users to define playlists in a JSON configuration file and play them via command line or interactive mode.
- Introduces a JSON-based playlist configuration system through
userSettings.json - Adds playlist support to both command-line and interactive UI workflows
- Enhances the queue management system with playlist loading capabilities
Reviewed Changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| userSettings.json | Adds playlist configuration file with sample radio stations and music tracks |
| PlaylistService.cs | Implements playlist loading and validation from JSON configuration |
| Program.cs | Integrates PlaylistService into dependency injection and command parsing |
| CommandLineArgs.cs | Adds playlist ID support to command line argument parsing |
| CommandExecutor.cs | Adds playlist playback functionality to command execution |
| Playlist.cs | Defines playlist data model with queue items |
| UserSettingsModel.cs | Defines user settings container for playlists |
| ConsoleJsonContext.cs | Adds JSON serialization context for new models |
| QueueController.cs | Adds interactive playlist selection and enhanced queue display |
| MediaController.cs | Adds back navigation option to media casting |
| ApplicationFlows.cs | Integrates playlist casting into main application flow |
| SharpCaster.Console.csproj | Configures userSettings.json to copy to output directory |
| TestHelper.cs | Enhances test logging with additional receiver information |
| LoggingTester.cs | Updates test assertion to include PONG message type |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| System.Console.WriteLine("Error: Media URL or valid Playlist ID is required for play command."); | ||
| return 1; | ||
| } | ||
| return await PlayPlaylistAsync(_playlistService.Playlists.First(p => p.Name == args.PlaylistId)); |
There was a problem hiding this comment.
The First() method will throw an exception if no playlist with the specified name is found. Since IsPlaylistId() has the same issue, this could fail even after validation. Use FirstOrDefault() and add null checking.
| return await PlayPlaylistAsync(_playlistService.Playlists.First(p => p.Name == args.PlaylistId)); | |
| var playlist = _playlistService.Playlists.FirstOrDefault(p => p.Name == args.PlaylistId); | |
| if (playlist == null) | |
| { | |
| System.Console.WriteLine($"Error: Playlist with name '{args.PlaylistId}' not found."); | |
| return 1; | |
| } | |
| return await PlayPlaylistAsync(playlist); |
| .StartAsync("Loading playlist", async ctx => | ||
| { | ||
| ctx.Status("Loading queue..."); | ||
| var status = await _state.Client.MediaChannel.QueueLoadAsync(_playlistService.Playlists.First(p => p.Name == urlChoice).QueueItems); |
There was a problem hiding this comment.
The First() method will throw an exception if no playlist with the specified name is found. Use FirstOrDefault() and add null checking to prevent runtime exceptions.
| var status = await _state.Client.MediaChannel.QueueLoadAsync(_playlistService.Playlists.First(p => p.Name == urlChoice).QueueItems); | |
| var playlist = _playlistService.Playlists.FirstOrDefault(p => p.Name == urlChoice); | |
| if (playlist == null) | |
| throw new Exception($"Playlist '{urlChoice}' not found."); | |
| var status = await _state.Client.MediaChannel.QueueLoadAsync(playlist.QueueItems); |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
@RobertK66 I took your pull request and made some changes on it. I have few todos still left before I will merge with this and make a new release:
|
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (3)
SharpCaster.Console/Models/UserSettingsModel.cs (1)
1-5: Remove unused using statements.The file includes several using statements that aren't used by the simple UserSettingsModel class definition.
Apply this diff to remove unused imports:
-using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - +SharpCaster.Console/Controllers/QueueController.cs (1)
55-59: Avoid First() throw when playlist not found; use FirstOrDefault() + null check.
Even though the choice comes from the list, this prevents edge cases and aligns with prior feedback.Apply this diff:
- var status = await _state.Client.MediaChannel.QueueLoadAsync(_playlistService.Playlists.First(p => p.Name == urlChoice).QueueItems); + var playlist = _playlistService.Playlists.FirstOrDefault(p => p.Name == urlChoice); + if (playlist == null) + throw new Exception($"Playlist '{urlChoice}' not found."); + var status = await _state.Client.MediaChannel.QueueLoadAsync(playlist.QueueItems);SharpCaster.Console/Services/CommandExecutor.cs (1)
291-297: Case-insensitive check and null-safe lookup for playlist; avoid First() throw.
Matches CLI lowercasing and prevents exceptions.Apply this diff:
- if (string.IsNullOrEmpty(args.PlaylistId) || !_playlistService.IsPlaylistId(args.PlaylistId)) + if (string.IsNullOrEmpty(args.PlaylistId) || !_playlistService.IsPlaylistId(args.PlaylistId)) { System.Console.WriteLine("Error: Media URL or valid Playlist ID is required for play command."); return 1; } - return await PlayPlaylistAsync(_playlistService.Playlists.First(p => p.Name == args.PlaylistId)); + var playlist = _playlistService.Playlists.FirstOrDefault(p => string.Equals(p.Name, args.PlaylistId, StringComparison.OrdinalIgnoreCase)); + if (playlist == null) + { + System.Console.WriteLine($"Error: Playlist '{args.PlaylistId}' not found."); + return 1; + } + return await PlayPlaylistAsync(playlist);
🧹 Nitpick comments (4)
SharpCaster.Console/Flows/ApplicationFlows.cs (2)
196-206: Add option LGTM; make PageSize dynamic to avoid future truncation.
Hard‑coding 11 risks truncation if options change.Apply this diff:
- .PageSize(11) + .PageSize(choices.Length)Also applies to: 211-217
237-240: Prevent nested menus; invoke Queue Management from here instead of inside CastPlaylistAsync.
Calling ShowQueueManagementAsync inside CastPlaylistAsync leads to nested loops when invoked from Queue Management. Trigger it here after casting.Apply this diff:
- case "Cast playlist": - _ui.AddSeparator("Casting Playlist"); - await _queueController.CastPlaylistAsync(); - break; + case "Cast playlist": + _ui.AddSeparator("💿 Casting Playlist"); + await _queueController.CastPlaylistAsync(); + await _queueController.ShowQueueManagementAsync(); + break;SharpCaster.Console/Services/PlaylistService.cs (1)
7-10: Prefer a property and encapsulate mutation. Remove unused using.
Exposing a public field invites unintended writes.Apply this diff:
- public class PlaylistService + public class PlaylistService { - public Playlist[] Playlists; + public Playlist[] Playlists { get; private set; }And at the top:
-using Microsoft.Extensions.Configuration; +using System; using SharpCaster.Console.Models; using System.Text.Json; +using System.IO;SharpCaster.Console/Controllers/QueueController.cs (1)
131-146: Guard next-track when queue is empty or status unavailable.
Avoid misleading “last track” message when there’s no queue.Apply this diff:
- var ids = await mediaChannel.QueueGetItemIdsAsync(); - - if (ids?.LastOrDefault() == mediaChannel.MediaStatus?.CurrentItemId) + var ids = await mediaChannel.QueueGetItemIdsAsync(); + if (ids == null || ids.Length == 0 || mediaChannel.MediaStatus?.CurrentItemId == null) + { + AnsiConsole.MarkupLine("[yellow]📋 No queue loaded or no current item.[/]"); + } + else if (ids.LastOrDefault() == mediaChannel.MediaStatus.CurrentItemId) { AnsiConsole.MarkupLine("[yellow]⚠️ Already at the last track in the queue. Cannot skip to next track.[/]"); } else {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
SharpCaster.Console/Controllers/MediaController.cs(3 hunks)SharpCaster.Console/Controllers/QueueController.cs(6 hunks)SharpCaster.Console/Flows/ApplicationFlows.cs(3 hunks)SharpCaster.Console/Models/CommandLineArgs.cs(3 hunks)SharpCaster.Console/Models/ConsoleJsonContext.cs(1 hunks)SharpCaster.Console/Models/Playlist.cs(1 hunks)SharpCaster.Console/Models/UserSettingsModel.cs(1 hunks)SharpCaster.Console/Program.cs(3 hunks)SharpCaster.Console/Services/CommandExecutor.cs(6 hunks)SharpCaster.Console/Services/PlaylistService.cs(1 hunks)SharpCaster.Console/SharpCaster.Console.csproj(1 hunks)SharpCaster.Console/userSettings.json(1 hunks)Sharpcaster.Test/LoggingTester.cs(1 hunks)Sharpcaster.Test/helper/TestHelper.cs(2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{cs,csproj}
📄 CodeRabbit inference engine (CLAUDE.md)
Prefer System.Text.Json for JSON serialization (do not use Newtonsoft.Json)
Files:
SharpCaster.Console/Models/UserSettingsModel.csSharpCaster.Console/SharpCaster.Console.csprojSharpCaster.Console/Models/CommandLineArgs.csSharpCaster.Console/Controllers/MediaController.csSharpCaster.Console/Models/Playlist.csSharpCaster.Console/Services/CommandExecutor.csSharpCaster.Console/Flows/ApplicationFlows.csSharpCaster.Console/Services/PlaylistService.csSharpCaster.Console/Controllers/QueueController.csSharpCaster.Console/Program.csSharpCaster.Console/Models/ConsoleJsonContext.csSharpcaster.Test/LoggingTester.csSharpcaster.Test/helper/TestHelper.cs
Sharpcaster.Test/**/*.cs
📄 CodeRabbit inference engine (CLAUDE.md)
Use xUnit for tests with Moq for mocking in the main test suite
Files:
Sharpcaster.Test/LoggingTester.csSharpcaster.Test/helper/TestHelper.cs
🧠 Learnings (8)
📓 Common learnings
Learnt from: RobertK66
PR: Tapanila/SharpCaster#384
File: SharpCaster.Console/appsettings.json:88-100
Timestamp: 2025-09-08T09:11:28.905Z
Learning: In SharpCaster.Console appsettings.json playlist configuration, inconsistent ID usage is intentional flexibility - some playlists have explicit IDs while others rely on auto-generation from names. This design choice allows configuration authors to choose between explicit control and automatic ID generation.
📚 Learning: 2025-09-08T09:11:28.905Z
Learnt from: RobertK66
PR: Tapanila/SharpCaster#384
File: SharpCaster.Console/appsettings.json:88-100
Timestamp: 2025-09-08T09:11:28.905Z
Learning: In SharpCaster.Console appsettings.json playlist configuration, inconsistent ID usage is intentional flexibility - some playlists have explicit IDs while others rely on auto-generation from names. This design choice allows configuration authors to choose between explicit control and automatic ID generation.
Applied to files:
SharpCaster.Console/Models/UserSettingsModel.csSharpCaster.Console/Models/CommandLineArgs.csSharpCaster.Console/Models/Playlist.csSharpCaster.Console/Services/PlaylistService.csSharpCaster.Console/userSettings.jsonSharpCaster.Console/Models/ConsoleJsonContext.cs
📚 Learning: 2025-08-29T20:06:59.311Z
Learnt from: CR
PR: Tapanila/SharpCaster#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-29T20:06:59.311Z
Learning: Applies to Sharpcaster/Sharpcaster.csproj : Target .NET Standard 2.0 and .NET 9 for the main library (AOT compatibility)
Applied to files:
SharpCaster.Console/SharpCaster.Console.csproj
📚 Learning: 2025-08-29T20:06:59.311Z
Learnt from: CR
PR: Tapanila/SharpCaster#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-29T20:06:59.311Z
Learning: Applies to Sharpcaster.Test.Aot/Sharpcaster.Test.Aot.csproj : Maintain a separate AOT test project to verify Native AOT compatibility
Applied to files:
SharpCaster.Console/SharpCaster.Console.csproj
📚 Learning: 2025-08-29T20:06:59.311Z
Learnt from: CR
PR: Tapanila/SharpCaster#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-29T20:06:59.311Z
Learning: Applies to Sharpcaster/Sharpcaster.csproj : Use Google.Protobuf for binary communication with Chromecast devices
Applied to files:
SharpCaster.Console/SharpCaster.Console.csproj
📚 Learning: 2025-08-29T20:06:59.311Z
Learnt from: CR
PR: Tapanila/SharpCaster#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-29T20:06:59.311Z
Learning: Applies to Sharpcaster/**/*.cs : Use Microsoft.Extensions.DependencyInjection for channel management and logging
Applied to files:
SharpCaster.Console/Services/CommandExecutor.csSharpCaster.Console/Controllers/QueueController.csSharpCaster.Console/Program.cs
📚 Learning: 2025-08-29T20:06:59.311Z
Learnt from: CR
PR: Tapanila/SharpCaster#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-29T20:06:59.311Z
Learning: Applies to Sharpcaster/**/*.cs : Make operations async and support CancellationToken throughout the Sharpcaster library
Applied to files:
SharpCaster.Console/Services/CommandExecutor.csSharpCaster.Console/Controllers/QueueController.cs
📚 Learning: 2025-08-29T20:06:59.311Z
Learnt from: CR
PR: Tapanila/SharpCaster#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-29T20:06:59.311Z
Learning: Applies to Sharpcaster/Sharpcaster.csproj : Use Zeroconf for mDNS device discovery to find Chromecast devices
Applied to files:
SharpCaster.Console/Program.cs
🧬 Code graph analysis (11)
SharpCaster.Console/Models/UserSettingsModel.cs (1)
SharpCaster.Console/Models/Playlist.cs (1)
Playlist(5-10)
SharpCaster.Console/Models/CommandLineArgs.cs (1)
SharpCaster.Console/Services/PlaylistService.cs (3)
PlaylistService(7-36)PlaylistService(11-29)IsPlaylistId(31-34)
SharpCaster.Console/Models/Playlist.cs (2)
Sharpcaster/Models/Queue/QueueItem.cs (1)
QueueItem(5-39)Sharpcaster/Models/Queue/QueueData.cs (2)
Sharpcaster(5-73)QueueData(10-72)
SharpCaster.Console/Services/CommandExecutor.cs (6)
SharpCaster.Console/Services/PlaylistService.cs (3)
PlaylistService(7-36)PlaylistService(11-29)IsPlaylistId(31-34)SharpCaster.Console/Services/DeviceService.cs (2)
DeviceService(11-285)DeviceService(18-24)SharpCaster.Console/Controllers/QueueController.cs (4)
Task(25-78)Task(80-227)Task(229-288)Task(290-328)SharpCaster.Console/Program.cs (2)
Task(16-46)Task(113-137)SharpCaster.Console/Models/Playlist.cs (1)
Playlist(5-10)Sharpcaster/Channels/MediaChannel.cs (2)
MediaChannel(22-378)MediaChannel(41-43)
SharpCaster.Console/Flows/ApplicationFlows.cs (4)
SharpCaster.Console/UI/UIHelper.cs (1)
AddSeparator(17-33)Sharpcaster/Messages/Queue/QueueUpdateMessage.cs (2)
QueueUpdateMessage(12-46)Sharpcaster(7-47)Sharpcaster/Messages/Queue/QueueInsertMessage.cs (2)
QueueInsertMessage(10-26)Sharpcaster(5-27)Sharpcaster/Models/Queue/QueueItem.cs (1)
QueueItem(5-39)
SharpCaster.Console/Services/PlaylistService.cs (2)
SharpCaster.Console/Models/Playlist.cs (1)
Playlist(5-10)SharpCaster.Console/Models/UserSettingsModel.cs (1)
UserSettingsModel(8-11)
SharpCaster.Console/Controllers/QueueController.cs (6)
SharpCaster.Console/Services/PlaylistService.cs (2)
PlaylistService(7-36)PlaylistService(11-29)SharpCaster.Console/Services/DeviceService.cs (2)
DeviceService(11-285)DeviceService(18-24)SharpCaster.Console/UI/UIHelper.cs (3)
UIHelper(8-131)UIHelper(12-15)AddSeparator(17-33)SharpCaster.Console/Flows/ApplicationFlows.cs (4)
Task(35-81)Task(83-162)Task(164-311)Task(313-332)SharpCaster.Console/Services/CommandExecutor.cs (10)
Task(42-172)Task(174-192)Task(194-206)Task(208-253)Task(282-355)Task(357-378)Task(380-414)Task(416-434)Task(436-449)Task(451-473)Sharpcaster/Channels/MediaChannel.cs (2)
MediaChannel(22-378)MediaChannel(41-43)
SharpCaster.Console/Program.cs (2)
SharpCaster.Console/Models/CommandLineArgs.cs (1)
CommandLineParser(23-272)SharpCaster.Console/Services/PlaylistService.cs (2)
PlaylistService(7-36)PlaylistService(11-29)
SharpCaster.Console/userSettings.json (4)
Sharpcaster/Models/Media/Media.cs (2)
Sharpcaster(5-133)Media(11-132)Sharpcaster/Models/Media/MusicTrackMetadata.cs (2)
Sharpcaster(3-44)MusicTrackMetadata(5-43)Sharpcaster/Extensions/SharpcasteSerialiationContext.cs (1)
Sharpcaster(16-106)Sharpcaster.Test/JsonTester.cs (1)
Sharpcaster(11-212)
SharpCaster.Console/Models/ConsoleJsonContext.cs (3)
SharpCaster.Console/Models/CommandLineArgs.cs (2)
CommandLineArgs(5-21)CommandLineArgs(25-140)SharpCaster.Console/Models/Playlist.cs (1)
Playlist(5-10)SharpCaster.Console/Models/UserSettingsModel.cs (1)
UserSettingsModel(8-11)
Sharpcaster.Test/LoggingTester.cs (1)
Sharpcaster/Channels/HeartbeatChannel.cs (1)
OnMessageReceived(61-76)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build windows arm64
- GitHub Check: Build windows x64
🔇 Additional comments (18)
Sharpcaster.Test/LoggingTester.cs (1)
19-19: Test correctly reflects updated message types.The test expectation now includes "PONG" in the logged MessageTypes string, which aligns with the Chromecast CASTV2 heartbeat protocol where PONG messages acknowledge PING messages to maintain connection aliveness. The relevant code in HeartbeatChannel.cs shows PONG handling logic, confirming this message type should be logged.
Sharpcaster.Test/helper/TestHelper.cs (2)
64-64: Enhanced receiver logging with additional metadata.The log now includes receiver Name and Version alongside existing Model and DeviceUri information, providing better visibility during test execution.
258-260: Fixed array initialization syntax error.The QueueItem array initialization was missing a closing brace, which has been corrected. The syntax fix ensures proper compilation and test data structure.
SharpCaster.Console/SharpCaster.Console.csproj (1)
30-38: Proper configuration for userSettings.json deployment.The project correctly copies userSettings.json to the output directory, ensuring the PlaylistService can load playlist data at runtime. The empty Properties folder inclusion is standard for .NET project structure.
SharpCaster.Console/Controllers/MediaController.cs (2)
35-49: Good UX enhancement with Back navigation.Adding the "Back" option with appropriate emoji icon (🔙) provides users with a clean exit path from media selection without loading media. The converter mapping maintains UI consistency.
77-78: Clean early return for Back navigation.The "Back" case correctly returns early from the method without performing media operations, maintaining the expected navigation flow.
SharpCaster.Console/Models/UserSettingsModel.cs (1)
8-11: Simple and effective model structure.The UserSettingsModel correctly exposes the Playlists array that aligns with the JSON structure in userSettings.json. The array initialization with
[]is appropriate for the required property.SharpCaster.Console/userSettings.json (1)
1-70: Well-structured playlist configuration data.The JSON structure provides good variety of playlist types (radio streams, music tracks) with appropriate metadata. The use of different content structures (some with metadata, others without) demonstrates the flexibility mentioned in the retrieved learnings about intentional ID usage patterns.
SharpCaster.Console/Models/CommandLineArgs.cs (3)
20-20: Playlist support integrated into command-line arguments.The PlaylistId property addition enables playlist-driven command execution alongside existing URL-based flows.
25-25: Enhanced parser signature with PlaylistService dependency.The updated Parse method signature correctly accepts PlaylistService to enable playlist ID validation during argument parsing.
127-134: Consistent playlist handling with URL logic.The playlist ID detection logic mirrors the URL handling pattern - using PlaylistService.IsPlaylistId for validation and defaulting to "play" command when no explicit command is provided. This maintains consistency with existing argument parsing behavior.
SharpCaster.Console/Program.cs (2)
74-74: PlaylistService properly registered as singleton.The DI registration ensures PlaylistService is available throughout the application lifecycle, appropriate for a service that loads configuration data.
28-29: Command-line parsing integrated with playlist support.The Parse call correctly retrieves PlaylistService from the service provider, enabling playlist-aware argument parsing. This maintains the separation of concerns while providing necessary dependencies.
SharpCaster.Console/Models/Playlist.cs (1)
5-10: LGTM: Simple, correct model using System.Text.Json.
Matches QueueItem shape and keeps default empty array to avoid nulls.SharpCaster.Console/Models/ConsoleJsonContext.cs (1)
8-11: LGTM: Added source-gen types for playlists and user settings.
Keeps to System.Text.Json as per guidelines.SharpCaster.Console/Controllers/QueueController.cs (1)
17-23: DI wiring LGTM.
Injecting PlaylistService here is appropriate.SharpCaster.Console/Services/CommandExecutor.cs (2)
23-34: DI wiring LGTM.
PlaylistService added cleanly to constructor and stored.
357-378: PlayPlaylistAsync LGTM.
Straightforward QueueLoad with clear messages.
| _ui.AddSeparator(); | ||
| AnsiConsole.MarkupLine("[green]✅ Playlist loaded and playing successfully![/]"); | ||
| _ui.AddSeparator("📝 Queue Management"); | ||
| await ShowQueueManagementAsync(); | ||
| } |
There was a problem hiding this comment.
Remove internal call to ShowQueueManagementAsync to prevent nested loops.
CastPlaylistAsync should just load; let callers decide what to show next.
Apply this diff:
- _ui.AddSeparator();
- AnsiConsole.MarkupLine("[green]✅ Playlist loaded and playing successfully![/]");
- _ui.AddSeparator("📝 Queue Management");
- await ShowQueueManagementAsync();
+ _ui.AddSeparator();
+ AnsiConsole.MarkupLine("[green]✅ Playlist loaded and playing successfully![/]");📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| _ui.AddSeparator(); | |
| AnsiConsole.MarkupLine("[green]✅ Playlist loaded and playing successfully![/]"); | |
| _ui.AddSeparator("📝 Queue Management"); | |
| await ShowQueueManagementAsync(); | |
| } | |
| _ui.AddSeparator(); | |
| AnsiConsole.MarkupLine("[green]✅ Playlist loaded and playing successfully![/]"); | |
| } |
🤖 Prompt for AI Agents
In SharpCaster.Console/Controllers/QueueController.cs around lines 61 to 65, the
CastPlaylistAsync method currently calls await ShowQueueManagementAsync() which
can create nested interactive loops; remove the internal call so
CastPlaylistAsync only loads and reports success and let callers decide whether
to invoke ShowQueueManagementAsync. Edit the method to delete the await
ShowQueueManagementAsync(); (and any immediate surrounding blank line if
desired) so the method ends after the success markup/separator, leaving UI
separators and success message intact.
| // Check for loaded app | ||
| var application = _state.Client.ChromecastStatus.Application; | ||
| if (application == null) | ||
| { | ||
| AnsiConsole.MarkupLine("[dim]No existing application found. Launching Default Media Receiver...[/]"); | ||
| await _state.Client.LaunchApplicationAsync("B3419EF5", false); | ||
| AnsiConsole.MarkupLine("[green]? Default Media Receiver launched successfully![/]"); | ||
| return; | ||
| } | ||
| else | ||
| { | ||
| AnsiConsole.MarkupLine("[green]Joining existing application {0:appId} ...[/]", application.AppId); | ||
| await _state.Client.LaunchApplicationAsync(application.AppId, true); | ||
| } |
There was a problem hiding this comment.
Fix Spectre.Console MarkupLine usage and message text.
Current call passes a format arg to MarkupLine (no such overload) and shows an odd “?” glyph.
Apply this diff:
- if (application == null)
+ if (application == null)
{
AnsiConsole.MarkupLine("[dim]No existing application found. Launching Default Media Receiver...[/]");
await _state.Client.LaunchApplicationAsync("B3419EF5", false);
- AnsiConsole.MarkupLine("[green]? Default Media Receiver launched successfully![/]");
+ AnsiConsole.MarkupLine("[green]✅ Default Media Receiver launched successfully![/]");
return;
}
else
{
- AnsiConsole.MarkupLine("[green]Joining existing application {0:appId} ...[/]", application.AppId);
- await _state.Client.LaunchApplicationAsync(application.AppId, true);
+ AnsiConsole.MarkupLine($"[green]Joining existing application {application.AppId}...[/]");
+ await _state.Client.LaunchApplicationAsync(application.AppId, joinExistingApplicationSession: true);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Check for loaded app | |
| var application = _state.Client.ChromecastStatus.Application; | |
| if (application == null) | |
| { | |
| AnsiConsole.MarkupLine("[dim]No existing application found. Launching Default Media Receiver...[/]"); | |
| await _state.Client.LaunchApplicationAsync("B3419EF5", false); | |
| AnsiConsole.MarkupLine("[green]? Default Media Receiver launched successfully![/]"); | |
| return; | |
| } | |
| else | |
| { | |
| AnsiConsole.MarkupLine("[green]Joining existing application {0:appId} ...[/]", application.AppId); | |
| await _state.Client.LaunchApplicationAsync(application.AppId, true); | |
| } | |
| // Check for loaded app | |
| var application = _state.Client.ChromecastStatus.Application; | |
| if (application == null) | |
| { | |
| AnsiConsole.MarkupLine("[dim]No existing application found. Launching Default Media Receiver...[/]"); | |
| await _state.Client.LaunchApplicationAsync("B3419EF5", false); | |
| AnsiConsole.MarkupLine("[green]✅ Default Media Receiver launched successfully![/]"); | |
| return; | |
| } | |
| else | |
| { | |
| AnsiConsole.MarkupLine($"[green]Joining existing application {application.AppId}...[/]"); | |
| await _state.Client.LaunchApplicationAsync(application.AppId, joinExistingApplicationSession: true); | |
| } |
🤖 Prompt for AI Agents
In SharpCaster.Console/Services/CommandExecutor.cs around lines 230 to 243, the
Spectre.Console.MarkupLine calls incorrectly pass a format argument and include
an extraneous “?” glyph; change the messages to use string interpolation (or
build the full string before passing) instead of format args and remove the “?”
glyph from the success message, e.g. construct the markup strings with the AppId
inserted via $"..." and update "[green]? Default Media Receiver launched
successfully![/]" to "[green]Default Media Receiver launched successfully![/]".
| var playlists = File.ReadAllText("userSettings.json"); | ||
| if ( string.IsNullOrWhiteSpace(playlists)) | ||
| { | ||
| Playlists = []; | ||
| return; | ||
| } | ||
|
|
||
| var userSettings = JsonSerializer.Deserialize<UserSettingsModel>(playlists, ConsoleJsonContext.Default.UserSettingsModel); | ||
|
|
There was a problem hiding this comment.
Handle missing/invalid userSettings.json safely (FileNotFound, JSON errors).
Reading a relative file without existence check will throw and break startup.
Apply this diff:
- var playlists = File.ReadAllText("userSettings.json");
- if ( string.IsNullOrWhiteSpace(playlists))
- {
- Playlists = [];
- return;
- }
-
- var userSettings = JsonSerializer.Deserialize<UserSettingsModel>(playlists, ConsoleJsonContext.Default.UserSettingsModel);
+ var path = Path.Combine(AppContext.BaseDirectory, "userSettings.json");
+ if (!File.Exists(path))
+ {
+ Playlists = [];
+ return;
+ }
+
+ var json = File.ReadAllText(path);
+ if (string.IsNullOrWhiteSpace(json))
+ {
+ Playlists = [];
+ return;
+ }
+
+ UserSettingsModel? userSettings;
+ try
+ {
+ userSettings = JsonSerializer.Deserialize<UserSettingsModel>(json, ConsoleJsonContext.Default.UserSettingsModel);
+ }
+ catch (JsonException)
+ {
+ Playlists = [];
+ return;
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| var playlists = File.ReadAllText("userSettings.json"); | |
| if ( string.IsNullOrWhiteSpace(playlists)) | |
| { | |
| Playlists = []; | |
| return; | |
| } | |
| var userSettings = JsonSerializer.Deserialize<UserSettingsModel>(playlists, ConsoleJsonContext.Default.UserSettingsModel); | |
| var path = Path.Combine(AppContext.BaseDirectory, "userSettings.json"); | |
| if (!File.Exists(path)) | |
| { | |
| Playlists = []; | |
| return; | |
| } | |
| var json = File.ReadAllText(path); | |
| if (string.IsNullOrWhiteSpace(json)) | |
| { | |
| Playlists = []; | |
| return; | |
| } | |
| UserSettingsModel? userSettings; | |
| try | |
| { | |
| userSettings = JsonSerializer.Deserialize<UserSettingsModel>(json, ConsoleJsonContext.Default.UserSettingsModel); | |
| } | |
| catch (JsonException) | |
| { | |
| Playlists = []; | |
| return; | |
| } |
🤖 Prompt for AI Agents
In SharpCaster.Console/Services/PlaylistService.cs around lines 13–21, reading
"userSettings.json" directly can throw if the file is missing or contains
invalid JSON; to fix, first construct a safe path (e.g.
Path.Combine(AppContext.BaseDirectory, "userSettings.json")) and check
File.Exists before reading — if missing, set Playlists = new List<...>() and
return; if the file exists, read its contents and guard against empty/whitespace
by setting Playlists to an empty list and returning; wrap
JsonSerializer.Deserialize in a try/catch catching JsonException and IOException
(or general Exception), log the error, and on any failure set Playlists to an
empty list instead of letting startup crash; ensure you continue to use
ConsoleJsonContext.Default.UserSettingsModel when deserializing.
| public bool IsPlaylistId(string arg) | ||
| { | ||
| return Playlists.Any(p => p.Name == arg); | ||
| } |
There was a problem hiding this comment.
Make playlist ID checks case‑insensitive (CLI lowercases args).
Current check will fail if playlist names in settings use mixed case.
Apply this diff:
- public bool IsPlaylistId(string arg)
+ public bool IsPlaylistId(string arg)
{
- return Playlists.Any(p => p.Name == arg);
+ return Playlists.Any(p => string.Equals(p.Name, arg, StringComparison.OrdinalIgnoreCase));
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public bool IsPlaylistId(string arg) | |
| { | |
| return Playlists.Any(p => p.Name == arg); | |
| } | |
| public bool IsPlaylistId(string arg) | |
| { | |
| return Playlists.Any(p => string.Equals(p.Name, arg, StringComparison.OrdinalIgnoreCase)); | |
| } |
🤖 Prompt for AI Agents
In SharpCaster.Console/Services/PlaylistService.cs around lines 31 to 34, the
IsPlaylistId method compares playlist names case-sensitively which fails when
CLI args are lowercased; update the comparison to be case-insensitive by using
string.Equals with StringComparison.OrdinalIgnoreCase (or compare both strings
with ToLowerInvariant) and make the comparison null-safe (e.g., p.Name != null
&& string.Equals(p.Name, arg, StringComparison.OrdinalIgnoreCase)) so playlist
lookup succeeds regardless of case.
No description provided.