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:
1 parent
23d7c62045
commit
729e9d260c
2 files changed
+30
-10
No files matched your search
@@ -481,6 +481,20 @@ async function saveEditPlaylist(){
|
|||||||
finally{btn.disabled=false;btn.textContent='Speichern';}
|
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()}
|
function editPlUploadCover(){document.getElementById('edit-pl-file').click()}
|
||||||
|
|
||||||
async function editPlCoverSelected(input){
|
async function editPlCoverSelected(input){
|
||||||
@@ -489,18 +503,20 @@ async function editPlCoverSelected(input){
|
|||||||
const preview=URL.createObjectURL(file);
|
const preview=URL.createObjectURL(file);
|
||||||
document.getElementById('edit-pl-cover').innerHTML=`<img src="${preview}" style="width:100%;height:100%;object-fit:cover">`;
|
document.getElementById('edit-pl-cover').innerHTML=`<img src="${preview}" style="width:100%;height:100%;object-fit:cover">`;
|
||||||
try{
|
try{
|
||||||
|
const token=await ndAuth();
|
||||||
const form=new FormData();form.append('image',file);
|
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`,{
|
||||||
const res=await fetch(`${cfg.url}/api/playlist/${_currentPlaylist.id}/image`,{method:'POST',headers:{Authorization:auth},body:form});
|
method:'POST',headers:{'X-Nd-Authorization':'Bearer '+token},body:form
|
||||||
|
});
|
||||||
if(!res.ok)throw new Error(res.status);
|
if(!res.ok)throw new Error(res.status);
|
||||||
toast('Cover aktualisiert');
|
toast('Cover aktualisiert');
|
||||||
// Refresh cover in detail view
|
|
||||||
setTimeout(()=>{
|
setTimeout(()=>{
|
||||||
const cv=document.getElementById('playlist-cover');
|
const cv=document.getElementById('playlist-cover');
|
||||||
const img=cv?.querySelector('img');if(img)img.src=img.src.split('?')[0]+'?t='+Date.now();
|
const img=cv?.querySelector('img');if(img)img.src=img.src.split('?')[0]+'?t='+Date.now();
|
||||||
},500);
|
},800);
|
||||||
}catch{
|
}catch(e){
|
||||||
toast('Cover-Upload nicht unterstützt (Navidrome-Version)');
|
_ndToken=null;
|
||||||
|
toast('Cover-Upload fehlgeschlagen: '+e.message);
|
||||||
document.getElementById('edit-pl-cover').innerHTML=
|
document.getElementById('edit-pl-cover').innerHTML=
|
||||||
(coverUrl(_currentPlaylist.coverArt,300)
|
(coverUrl(_currentPlaylist.coverArt,300)
|
||||||
?`<img src="${coverUrl(_currentPlaylist.coverArt,300)}" style="width:100%;height:100%;object-fit:cover">`
|
?`<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(){
|
async function editPlRemoveCover(){
|
||||||
if(!_currentPlaylist)return;
|
if(!_currentPlaylist)return;
|
||||||
try{
|
try{
|
||||||
const auth='Basic '+btoa(`${cfg.user}:${cfg.pass}`);
|
const token=await ndAuth();
|
||||||
const res=await fetch(`${cfg.url}/api/playlist/${_currentPlaylist.id}/image`,{method:'DELETE',headers:{Authorization:auth}});
|
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);
|
if(!res.ok)throw new Error(res.status);
|
||||||
toast('Cover entfernt');
|
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>';
|
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) ───
|
// ─── RADIO MODE (endloser Shuffle aus der eigenen Bibliothek) ───
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const CACHE = 'ns-v31';
|
const CACHE = 'ns-v32';
|
||||||
const PRECACHE = ['/', 'index.html', 'styles.css', 'app.js', 'manifest.json', 'icon.svg'];
|
const PRECACHE = ['/', 'index.html', 'styles.css', 'app.js', 'manifest.json', 'icon.svg'];
|
||||||
const SKIP_PATHS = ['/rest/stream', '/rest/download', '/config.js'];
|
const SKIP_PATHS = ['/rest/stream', '/rest/download', '/config.js'];
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user