Repository navigation
Conversation
This comment has been minimized.
This comment has been minimized.
mrazza
left a comment
There was a problem hiding this comment.
XML doc comments are required on all public classes and public methods.
This comment has been minimized.
This comment has been minimized.
… seek Resolves feedback (1) and (2) from PR #40 review: - Binds HttpListener specifically to the local IP address instead of using the wildcard prefix '*' to prevent permission issues. - Resets/seeks stream position to the beginning on each proxy request to allow multi-request/probing connections from Google Cast devices.
… seek Resolves feedback (1) and (2) from PR #40 review: - Binds HttpListener specifically to the local IP address instead of using the wildcard prefix '*' to prevent permission issues. - Resets/seeks stream position to the beginning on each proxy request to allow multi-request/probing connections from Google Cast devices.
… handling - Implement IAudioService and IPlaybackService interface updates in Cast services - Fix preload track cutoff by supporting concurrent streams in StreamingProxyService - Add HTTP 206 Range header support and thread-safe seeking in StreamingProxyService - Improve local IP discovery using UDP socket probe to target Chromecast - Add UnregisterCompleter and error handling for :output command - Ensure clean playback transition in SetAudioServiceAsync - Clean up developer scratchpad comments and fix xUnit1051 warnings - Expose CommandLine.CommandText and assert completion in unit test - Reformat Cast files to 2-space indentation
0f46ac4 to
83993d7
Compare
- Enqueue Play, Pause, Stop, and Seek onto a linearized task pipeline - Prevent race conditions and out-of-order execution when Stop() and Play() are invoked in rapid succession - Safely log exceptions from underlying Cast operations instead of unhandled async void crashes - Add unit tests verifying sequential command order and pipeline fault tolerance
Review Comments Resolution Summary 🚀All review comments and requested changes have been addressed:
|
All public classes and methods have been updated with complete XML doc comments and verified.
- Calculate CurrentTime and Progress via Stopwatch elapsed time interpolation while Playing - Run periodic 500ms timer to trigger PositionChanged updates for UI views - Freeze CurrentTime on Pause and reset on Stop - Calibrate progress baseline whenever MediaStatusChanged arrives from device - Add GetMediaStatusAsync to IChromecastClient and periodically sync with device - Add comprehensive unit tests covering playback progress advancement, pause freezing, and calibration
- Use fast staged mDNS timeouts (500ms quick scan) for startup Cast discovery - Add EnsureInitialDiscoveryCompletedAsync to synchronize in-flight startup scans - Implement on-demand scan fallback in :output command if target device is not in cache - Support matching device by IP address or hostname in addition to name - Add :output/refresh command to explicitly trigger a device re-scan - Add CastDiscoveryServiceTest suite verifying scan lifecycle and thread safety
Update: Fast Startup Discovery & On-Demand Device Resolution ⚡
|
- Add CastPlaybackService tests for resume from pause, FINISHED idle event SongEnded trigger, and SpectrumData - Add CastAudioService tests for volume get/set, codec MIME mapping, and disposal - Add StreamingProxyService tests for MethodNotAllowed, NotFound, HeadMethod, suffix/offset byte ranges, UDP probe, and non-seekable chunked streaming - Add CommandService test for UnregisterCompleter - Bring CastAudioService and CommandService to 100% line coverage, StreamingProxyService to 89.6%, and CastPlaybackService to 88.5%
Update: Expanded Test Coverage 🧪Added comprehensive unit tests across Cast services, proxy server, and command infrastructure:
Total test suite: 452 tests passed, 0 warnings, 0 errors. |
- Ignore shared client MediaStatus events if the CastPlaybackService instance has not started yet or belongs to another track - Update playback state before invoking SongEnded to prevent out-of-order state transitions - Evaluate state checks inside the enqueued command pipeline so Stop() does not send StopAsync() when already stopped by device completion - Prevent redundant StopAsync() from canceling the preloaded next track during ChangeTrack() - Add end-to-end integration test verifying automatic track transition from song 1 to song 2 on Cast
Fix: Automatic Track Progression When Casting ⏭️Fixed an issue where track transitions failed when the current Cast track ended:
All 454 unit tests pass, 0 warnings, 0 errors. |
…playback - Add EnsureConnectedAndLaunchedAsync to IChromecastClient to recover dropped sockets or receiver app timeouts - Add thread-safe EnsureConnectedAsync to CastAudioService with SemaphoreSlim synchronization - Call EnsureConnectedAsync before LoadAsync or PlayAsync in CastPlaybackService.Play() - Automatically relaunch Default Media Receiver (CC1AD845) if it timed out to the backdrop during idle periods - Add unit tests verifying pre-playback connection and session recovery
Fix: Session Recovery After Idle Inactivity ⏳Resolved an issue where selecting an output and waiting several minutes before playing caused playback requests to silently fail:
All 455 unit tests pass, 0 warnings, 0 errors. |
…rvices - Strip ConfigureAwait(false) across Cast services and UI since console applications lack a UI SynchronizationContext - Clean up un-awaited task calls in Volume and MainWindow background startup discovery - Synchronize DisconnectAsync in CastAudioService.Dispose() before disposing client
…c Cast calls - Keep ConfigureAwait(false) on backend async awaits in Cast services to prevent capturing Terminal.Gui MainLoopSyncContext during test execution - Remove ConfigureAwait(false) from un-awaited statements and use explicit task discarding - Synchronize DisconnectAsync in CastAudioService.Dispose() before disposing client
…ns and remove ConfigureAwait(false) - Propagate CancellationToken across all async Cast APIs (IChromecastClient, ICastDiscoveryService, CastAudioService, CastPlaybackService) - Connect CancellationToken to async awaits via .WaitAsync(cancellationToken) - Detach startup discovery in MainWindow via Task.Run with CancellationToken linked to MainWindow disposal - Remove all ConfigureAwait(false) clutter from business logic
Refactor: CancellationToken Lifecycle & ConfigureAwait Cleanup 🧹
|
oca-agent
left a comment
There was a problem hiding this comment.
Automated Code Review Test
oca-agent
left a comment
There was a problem hiding this comment.
Pull Request Review: Google Cast Support & :output Command (#40)
Thank you for the comprehensive implementation of Google Cast support and the :output switching architecture! The linearized FIFO command queue for CastPlaybackService, the HTTP 206 streaming proxy, and the unit test coverage are great additions.
Below is a detailed review categorized by (1) Correctness, (2) Bugs, (3) Readability, and (4) Code Hygiene, with line-level comments attached to the diff.
Key Findings Summary:
- [Bug] Tab-completion clobbering command names: In
CommandService.GetCompletions, completers are evaluated before checking if an argument delimiter (/) was entered. Pressing Tab on:outputreplaces the command with the first argument completion (e.g.:local/). - [Bug] NullReferenceException on uninitialized device names: If a Chromecast announces on mDNS with a null
Name,:outputtab completion and device lookup throw an unhandled NRE, crashing the UI loop. - [Concurrency / Correctness] Event fired inside lock & CTS cancellation handling:
CastDiscoveryService.DeviceFoundis invoked while holding_devicesLock, risking deadlocks with subscribers. Also,_initialDiscoveryTaskdoes not checkIsCanceled, preventing recovery if startup scan is cancelled. - [Resource Leak] Progress tracking loop stays active after song ends: When a song reaches
Idle/FINISHED,StopProgressTracking()is never called, leaving thePeriodicTimerbackground task running indefinitely. - [Concurrency] SemaphoreSlim disposed prematurely in CastAudioService:
_connectionLock.Dispose()is called before_client.DisconnectAsync()completes, causing_connectionLock.Release()in in-flight calls to throwObjectDisposedException. - [UX / Volume] Volume state lost when switching outputs: Switching audio services resets volume to 100%, and
CastAudioServicedoes not clamp volume to[0.0, 1.0]. - [Hygiene] Noisy error logs on client disconnects: Routine seek/skip disconnections in
StreamingProxyServiceare logged asLogging.Error.
- Fix CommandService tab-completion clobbering command names when no argument slash is present - Add null-checks for ChromecastReceiver Name in MainWindow completer and FindDevice - Prefer exact name/host match before substring match in FindDevice - Invoke CastDiscoveryService.DeviceFound outside _devicesLock to avoid potential deadlocks - Allow CastDiscoveryService to reinitiate scan if previous task was canceled - Remove duplicate State property in CastPlaybackService and synchronize PlaybackState reads - Reset progress tracking and position when media transitions to Idle/Stopped on device - Calibrate position for 0-second media status broadcasts - Clamp Cast volume to [0.0, 1.0] and add safe fault logging to volume task - Dispose CastAudioService _connectionLock after socket disconnect finishes - Filter normal HTTP client aborts (HttpListenerException, IOException) from streaming proxy error logs - Preserve volume setting across audio service transitions in StandardPlaybackQueueService
Code Review Feedback Addressed 🎯Resolved all 11 code review comments:
Verified with 0 compiler warnings and 100% test pass rate (458 tests). |
Summary of Changes
Implemented Google Cast support for Smoc, enabling users to cast music to Cast-enabled devices.
Core Features
CastDiscoveryServiceto discover Chromecast devices on the local network using mDNS viaChromecastLocator.CastAudioServiceandCastPlaybackServiceto control playback on remote devices.IChromecastClientandChromecastClientWrapperto allow for robust unit testing of Cast services via mocking.StreamingProxyServiceto bridge Smoc's internal audio streams to Chromecast using a local HTTP listener.:outputcommand to switch between local and remote playback.:output [tab]).Technical Details
Sharpcaster.Models.Media.MediaStatusupdates and map them to Smoc's internalPlaybackState.StandardPlaybackQueueServiceto support dynamic audio service switching without losing queue state.CommandServicewith a plugin-based completion architecture.Test Coverage
Added unit tests for all major new components:
StreamingProxyServiceTest: Verifies the local HTTP streaming proxy functionality.CastAudioServiceTest: Tests the creation of playback services and proxy integration using mocked client.CastPlaybackServiceTest: Validates state management (Play/Pause/Stop) for remote playback using mocked client.CommandServiceTest: Added coverage for the new tab-completion logic.CommandLineTest: Verified UI-level tab-completion handling.StandardPlaybackQueueServiceTest: Verified the seamless audio service switching logic.Resolves #39