fix: Navidrome REST auth for playlist cover — use JWT Bearer token

POST /auth/login to get JWT token, then X-Nd-Authorization: Bearer <token>
for cover upload/delete. Resets cached token on auth failure.
Bump SW to ns-v32.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Sonnet 4.6 committed 2026-04-26 01:28:21 +02:00
1 parent 23d7c62045
commit 729e9d260c
2 files changed
+30 -10

No files matched your search

+29 -9
View File
@@ -481,6 +481,20 @@ async function saveEditPlaylist(){
finally{btn.disabled=false;btn.textContent='Speichern';}
}
let _ndToken=null;
async function ndAuth(){
if(_ndToken)return _ndToken;
const res=await fetch(`${cfg.url}/auth/login`,{
method:'POST',
headers:{'Content-Type':'application/json'},
body:JSON.stringify({username:cfg.user,password:cfg.pass})
});
if(!res.ok)throw new Error('Navidrome login fehlgeschlagen');
const d=await res.json();
_ndToken=d.token;
return _ndToken;
}
function editPlUploadCover(){document.getElementById('edit-pl-file').click()}
async function editPlCoverSelected(input){
@@ -489,18 +503,20 @@ async function editPlCoverSelected(input){
const preview=URL.createObjectURL(file);
document.getElementById('edit-pl-cover').innerHTML=`<img src="${preview}" style="width:100%;height:100%;object-fit:cover">`;
try{
const token=await ndAuth();
const form=new FormData();form.append('image',file);
const auth='Basic '+btoa(`${cfg.user}:${cfg.pass}`);
const res=await fetch(`${cfg.url}/api/playlist/${_currentPlaylist.id}/image`,{method:'POST',headers:{Authorization:auth},body:form});
const res=await fetch(`${cfg.url}/api/playlist/${_currentPlaylist.id}/image`,{
method:'POST',headers:{'X-Nd-Authorization':'Bearer '+token},body:form
});
if(!res.ok)throw new Error(res.status);
toast('Cover aktualisiert');
// Refresh cover in detail view
setTimeout(()=>{
const cv=document.getElementById('playlist-cover');
const img=cv?.querySelector('img');if(img)img.src=img.src.split('?')[0]+'?t='+Date.now();
},500);
}catch{
toast('Cover-Upload nicht unterstützt (Navidrome-Version)');
},800);
}catch(e){
_ndToken=null;
toast('Cover-Upload fehlgeschlagen: '+e.message);
document.getElementById('edit-pl-cover').innerHTML=
(coverUrl(_currentPlaylist.coverArt,300)
?`<img src="${coverUrl(_currentPlaylist.coverArt,300)}" style="width:100%;height:100%;object-fit:cover">`
@@ -511,12 +527,16 @@ async function editPlCoverSelected(input){
async function editPlRemoveCover(){
if(!_currentPlaylist)return;
try{
const auth='Basic '+btoa(`${cfg.user}:${cfg.pass}`);
const res=await fetch(`${cfg.url}/api/playlist/${_currentPlaylist.id}/image`,{method:'DELETE',headers:{Authorization:auth}});
const token=await ndAuth();
const res=await fetch(`${cfg.url}/api/playlist/${_currentPlaylist.id}/image`,{
method:'DELETE',headers:{'X-Nd-Authorization':'Bearer '+token}
});
if(!res.ok)throw new Error(res.status);
toast('Cover entfernt');
_currentPlaylist.coverArt=null;
document.getElementById('edit-pl-cover').innerHTML='<div class="no-art" style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-size:36px">🎶</div>';
}catch{toast('Entfernen nicht unterstützt (Navidrome-Version)')}
document.getElementById('playlist-cover').innerHTML='<div class="no-art">🎶</div>';
}catch(e){_ndToken=null;toast('Fehler: '+e.message)}
}
// ─── RADIO MODE (endloser Shuffle aus der eigenen Bibliothek) ───