diff --git a/index.html b/index.html index c61c31b..684ab57 100644 --- a/index.html +++ b/index.html @@ -661,6 +661,16 @@ Romaji anzeigen
+
+ Scrobble senden nach + +
diff --git a/src/player/player.js b/src/player/player.js index d7de7d3..4b9c93c 100644 --- a/src/player/player.js +++ b/src/player/player.js @@ -20,20 +20,33 @@ let _bridgeReady = false; // true once onReady has fired; onQueueEnded is ign // ─── SCROBBLE ───────────────────────────────────────────────────────────── let _scrobbleTimer = null; let _scrobbleId = null; -function _scheduleScrobble(id) { - if (_scrobbleId === String(id)) return; // already scheduled for this track +function _scheduleScrobble(id, durSec) { + if (_scrobbleId === String(id)) return; _cancelScrobble(); _scrobbleId = String(id); + const mode = S.cfg.scrobbleMode || '15s'; + if (mode === 'end') return; // fired explicitly in onEnded / onQueueEnded + let delay; + const dur = (durSec || 0) * 1000; + switch (mode) { + case '30s': delay = 30_000; break; + case '1/3': delay = dur > 0 ? Math.floor(dur / 3) : 30_000; break; + case '2/3': delay = dur > 0 ? Math.floor(dur * 2 / 3) : 60_000; break; + default: delay = 15_000; break; + } _scrobbleTimer = setTimeout(() => { if (_scrobbleId) api('scrobble', `&id=${_scrobbleId}&time=${Date.now()}`).catch(() => {}); _scrobbleId = null; - }, 15_000); + }, delay); } function _cancelScrobble() { clearTimeout(_scrobbleTimer); _scrobbleTimer = null; _scrobbleId = null; } +function _scrobbleNow(s) { + if (s) api('scrobble', `&id=${s.id}&time=${Date.now()}`).catch(() => {}); +} // Unified duration / position / seek helpers — use native state when available, // fall back to HTMLAudioElement when not. @@ -192,7 +205,7 @@ window.NSPlayer = { try { const s = S.queue[S.qIdx]; if (!s) return; - _scheduleScrobble(s.id); + _scheduleScrobble(s.id, s.duration); if (S.radioMode && S.queue.length - S.qIdx < 5) fillRadioQueue(); } catch (e) {} }, @@ -230,6 +243,7 @@ window.NSPlayer = { if (!_bridgeReady || Date.now() - _readyTs < 1500) return; _nativePlaying = false; setPlayIcon(false); + if (S.cfg.scrobbleMode === 'end') _scrobbleNow(S.queue[S.qIdx]); onEnded(); // handles repeat, radio top-up, and queue advance }, @@ -376,7 +390,7 @@ export function playCurrent() { updatePlayerUI(s, true); onNpSongChange(s.id); renderQueue(); - _scheduleScrobble(s.id); // 12s timer — cancelled on pause/skip + _scheduleScrobble(s.id, s.duration); const src = (_preId === s.id && _preUrl) ? _preUrl : streamUrl(s.id); _clearPreload(); @@ -691,7 +705,8 @@ export function nextSong() { export function onEnded() { if(S.repeat === 'one') { S.audio.currentTime = 0; S.audio.play(); return; } - _cancelScrobble(); // timer irrelevant now — song already finished + if (!HAS_BRIDGE && S.cfg.scrobbleMode === 'end') _scrobbleNow(S.queue[S.qIdx]); + _cancelScrobble(); nextSong(); } diff --git a/src/ui/settings.js b/src/ui/settings.js index 8d417e6..23aa9e8 100644 --- a/src/ui/settings.js +++ b/src/ui/settings.js @@ -50,6 +50,7 @@ export function openSettings() { document.getElementById('s-show-playcount').classList.toggle('on', !!S.cfg.showPlayCount); document.getElementById('s-show-romaji').classList.toggle('on', !!S.cfg.showRomaji); document.getElementById('s-cover-color-bg').classList.toggle('on', !!S.cfg.coverColorBg); + document.getElementById('s-scrobble-mode').value = S.cfg.scrobbleMode || '15s'; _renderThemePicker(); document.getElementById('settings-modal').classList.add('open'); checkBackend(); @@ -131,6 +132,7 @@ export function saveSettings() { const romajiChanged = S.cfg.showRomaji !== document.getElementById('s-show-romaji').classList.contains('on'); S.cfg.showRomaji = document.getElementById('s-show-romaji').classList.contains('on'); S.cfg.coverColorBg = document.getElementById('s-cover-color-bg').classList.contains('on'); + S.cfg.scrobbleMode = document.getElementById('s-scrobble-mode').value || '15s'; updateNpColorMode(); localStorage.setItem('ns_cfg', JSON.stringify(S.cfg)); closeSettings(); toast('✅ Gespeichert');