Fix Navidrome playlist sync truncation, clarify discovery label

Navidrome fix:
- createPlaylist and other write operations now use POST instead of
  GET. Large playlists (161+ tracks) exceeded URL length limits when
  all songId params were in the query string, causing silent truncation
  (e.g., only 6 of 161 tracks added). POST sends params as form body
  with no size limit.
- Write operation timeout bumped to 30s (was 10s)
- _WRITE_ENDPOINTS set defines which Subsonic endpoints use POST

UX fix:
- Mirrored playlist cards now show "161/161 discovered on Spotify"
  instead of just "161/161 discovered" — clarifies that discovery
  means metadata matching, not library ownership
This commit is contained in:
Broque Thomas 2026-03-26 14:15:20 -07:00
parent a98108b3ed
commit 8e41feaade
2 changed files with 20 additions and 3 deletions

View file

@ -301,8 +301,19 @@ class NavidromeClient:
'f': 'json' # Response format
}
# Subsonic endpoints that modify data — use POST to avoid URL length limits
_WRITE_ENDPOINTS = frozenset({
'createPlaylist', 'updatePlaylist', 'deletePlaylist',
'star', 'unstar', 'scrobble', 'setRating',
'createShare', 'updateShare', 'deleteShare',
'createUser', 'updateUser', 'deleteUser',
'createBookmark', 'deleteBookmark',
'startScan',
})
def _make_request(self, endpoint: str, params: Optional[Dict[str, Any]] = None) -> Optional[Dict[str, Any]]:
"""Make authenticated request to Navidrome Subsonic API"""
"""Make authenticated request to Navidrome Subsonic API.
Uses POST for write operations (avoids URL length limits with large playlists)."""
if not self.base_url or not self.username:
return None
@ -314,7 +325,12 @@ class NavidromeClient:
auth_params.update(params)
try:
response = requests.get(url, params=auth_params, timeout=10)
# Use POST for write operations to avoid URL length limits
# (e.g., createPlaylist with 161 songId params would exceed GET URL limits)
if endpoint in self._WRITE_ENDPOINTS:
response = requests.post(url, data=auth_params, timeout=30)
else:
response = requests.get(url, params=auth_params, timeout=10)
response.raise_for_status()
data = response.json()

View file

@ -57586,7 +57586,8 @@ function renderMirroredCard(p, container) {
let ratioHtml = '';
if (disc > 0) {
const complete = disc >= tot;
ratioHtml = `<span class="discovery-ratio${complete ? ' complete' : ''}">${disc}/${tot} discovered</span>`;
const srcName = typeof currentMusicSourceName !== 'undefined' ? currentMusicSourceName : 'metadata';
ratioHtml = `<span class="discovery-ratio${complete ? ' complete' : ''}">${disc}/${tot} discovered on ${srcName}</span>`;
}
const card = document.createElement('div');