- XML Refactoring: Transitioned from manual string concatenation to structured XML marshaling using specialized Go models to match upstream API responses exactly. - Service Enhancements: Implemented robust device discovery via power_on handling, improved source metadata persistence, and standardized ID generation logic. - Parity & Consistency: Fixed data loss and formatting mismatches for lastplayedat, serialNumber, and nested <source> elements. - Infrastructure & Testing: Added a comprehensive suite of regression and parity reproduction tests, centralized common XML constants, and documented progress. Co-authored-by: Junie <junie@jetbrains.com>
6.9 KiB
Overview of Recent Improvements and Next Steps
This document summarizes the improvements made to the Marge service to improve parity with the upstream Bose SoundTouch service, along with open issues and proposed next steps.
✅ Completed Improvements (Marge Service)
- Timestamp-based ID Generation: Implemented a 9-digit ID schema (
YYMMDD+ 3-digit counter) forrecentitems, ensuring IDs are large, unique, and stay within the 32-bit integer range. - Automatic Source Learning: The service now extracts and persists full metadata (credentials, provider IDs, and custom names) from incoming
POST /recentrequests. This improves parity for subsequentGET /recentscalls. - Source Provider Mapping: Synchronized local source provider IDs and timestamps with upstream data. The
RADIO_BROWSERprovider is included in the public/streaming/sourceproviderslist to maintain internal functionality while acknowledging it as a parity gap. - Credential Preservation: Improved
AddRecentto correctly extract and echo back base64 tokens/credentials provided in the incoming request, improving source learning. - XML Formatting Parity:
- Added
standalone="yes"to the XML declaration for all Marge responses, includingrecent,presets,full account,software update, andsourceproviders. - Enforced self-closing
<sourceSettings/>tags for parity. - Standardized date formatting to UTC with milliseconds (
.000+00:00). - Fixed casing for
/streaming/sourceproviders: Root element is<sourceProviders>, but child elements are<sourceprovider>(all lowercase), matching upstream behavior. - Implemented structured XML marshaling with consistent 2-space indentation for recents and source providers.
- Added
- Improved TuneIn Parity: Fixed TuneIn source mapping to use ID
25and ensuringsourcenameis empty in responses, matching upstream behavior for station playback. - High-Fidelity Full Account Sync: Refactored the
/streaming/account/{accountId}/fullresponse to match the upstream structure. This includes:- Structured XML Marshaling: Replaced manual string concatenation with structured Go models and
xml.Marshalfor the entire response. - Specific Response Models: Introduced
FullResponseSource,FullResponsePreset, andFullResponseRecentto accurately reflect the upstream structure where<source>is a child element, rather than a set of attributes. - Correct Nesting: Ensured that
<presets>and<recents>correctly nest their associated<source>details, resolving previous data omissions. - Device Identity: Added
<serialNumber>and<updatedOn>to both the top-level<device>and its<attachedProduct>, ensuring consistent device identification. - Field-Level Parity: Mapped missing fields like
<contentItemType>and<productlabel>to match upstream expectations. - Improved Source Matching: Enhanced internal logic to correctly link presets and recents to their configured sources based on multiple identifiers (ID, Key, or Type).
- Structured XML Marshaling: Replaced manual string concatenation with structured Go models and
- Verified Parity Mismatch Fixes: Comprehensive reproduction tests (
TestParityMismatchReproduction_V2andTestParityMismatchReproduction_V3) now confirm parity for identified mismatches inPOST /recentandGET /recents, including credentials and source-specific metadata. - Unified Response Logic: Refactored the code so that both
POST /recentandGET /recentsuse the same formatting functions, guaranteeing consistency. - Robust Parity Detection: Updated the local parity checker to be whitespace-insensitive for XML bodies, significantly reducing noise from minor indentation or newline differences.
- Maintainable XML Generation: Reduced cyclomatic complexity and code duplication in
marge.goby extracting focused helper functions for mapping internal data to response-specific XML models.
🛠️ Open Issues and Next Steps
Based on the latest parity_mismatches, here are the recommended areas for further work:
1. BMX / TuneIn Playback Parity (Medium)
Current mismatches in /bmx/tunein/v1/playback/station/... show differences in reporting URLs and missing links:
- Mismatched Parameters: Local reporting URLs use
listen_id=3432432423, while upstream uses a different session-based ID. - Missing Links: Some upstream responses include additional
_linksor metadata that are currently omitted in local responses. - Action: Improve the
HandleTuneInPlaybacklogic to better mirror the upstream response structure and parameter generation.
2. Presets and Recents Parity (Medium)
Further align the standalone GET /presets and GET /recents endpoints with the refined structural improvements introduced for the /full account response:
- Source Nesting: Ensure the standalone responses also use the specialized nested
<source>structure instead of mixed attributes when appropriate. - Field Completeness: Verify all metadata fields (e.g.,
<contentItemType>,<lastplayedat>) are consistently populated across all access paths. - Action: Evaluate if the specialized
FullResponsePresetandFullResponseRecentmodels should be shared or mirrored in the standalone handlers.
3. OAuth / Spotify Token Noise (Low/Medium)
The /oauth/device/.../token endpoint frequently reports mismatches because tokens are naturally different between local and upstream.
- The Issue: This creates "noise" in your parity reports that isn't actually a bug.
- Action: Update the parity detection logic (or the handler) to selectively ignore the
access_tokenfield while still verifying that the rest of the JSON structure (expires_in, scope, token_type) matches.
4. Large IDs for Other Models (Medium)
While we fixed IDs for recents, other models like presets or sources might still use small auto-incrementing integers.
- Action: Evaluate if other endpoints should also transition to the timestamp-based ID schema to further reduce diff noise.
5. Improved Data Persistence (Continuous)
Continue the "learning" approach for other services. For example, if we see a new sourceproviderid in a Spotify or TuneIn request, we should ensure it is stored and reused.
6. Local Reboot & Device State Management (Continuous)
Analysis of device reboot logs revealed several data requirements:
- Power-On Details Tracking: Implemented extraction and persistence of detailed device information (serial numbers, firmware version, product details, and MAC addresses) from the
POST /streaming/support/power_onrequest. This data is now stored in the local datastore, improving our ability to respond accurately to subsequent management requests. - Source Provider Mapping: Synchronized local source provider IDs and timestamps with upstream data. The
RADIO_BROWSERprovider is included in the public/streaming/sourceproviderslist to maintain internal functionality while acknowledging it as a parity gap.