feat(web+android): Wiedergabe fortsetzen und Streitpunkte im Duell (2.4)
Wiedergabe fortsetzen: Im Web ueberlebten Warteschlange und letzter Titel bereits, die POSITION im Titel aber nicht — beim Neuladen begann der Song von vorn. Android sicherte nur den letzten Song ohne Warteschlange. Beide merken sich jetzt Warteschlange, Listenposition und Sekunde. Web schreibt gedrosselt (alle 5 s) waehrend der Wiedergabe und sicher beim Pausieren, Wegwischen und Tabwechsel — pagehide/visibilitychange statt beforeunload, das am Handy unzuverlaessig feuert. Android sichert bei jedem Titelwechsel und beim Pausieren und stellt nach dem Verbinden wieder her: vorbereitet, aber NICHT abgespielt, wer die App oeffnet will nicht ueberfallen werden. Laeuft schon etwas (Android Auto), passiert nichts. Zwei Feinheiten: lag die Position im letzten Rest des Titels, faengt er von vorn an, statt zwei Sekunden Abspann zu spielen; und gespeichert werden nur Song-Ids, weil Titel und Cover sich durch Metadaten-Bearbeitung aendern koennen — verschwundene Titel werden beim Wiederherstellen uebersprungen. Streitpunkte im Duell: Gemeinsame Kuenstler nach groesstem Ungleichgewicht statt nach Summe, mit Kraefteverhaeltnis-Balken. Mindestens vier gemeinsame Plays und 50 % Unwucht, sonst gilt ein einzelner Play gegen null schon als Streit. Web und Android mit identischen Schwellen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
92c3d96936
commit
65fa950043
8 files changed
+217
-2
No files matched your search
@@ -1364,6 +1364,25 @@ export function reconcileNativeState() {
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// Abspielposition mitschreiben. Der Eintrag entsteht beim Titelwechsel; hier wird nur die Position
|
||||
// nachgetragen, damit ein Neuladen dort weitermacht, wo du warst — nicht am Anfang des Titels.
|
||||
// Gedrosselt, weil timeupdate mehrmals pro Sekunde feuert und localStorage synchron schreibt.
|
||||
let _posLastWrite = 0;
|
||||
export function persistPosition(force = false) {
|
||||
if (HAS_BRIDGE) return; // nativ verwaltet Media3 den Zustand
|
||||
const jetzt = Date.now();
|
||||
if (!force && jetzt - _posLastWrite < 5000) return;
|
||||
_posLastWrite = jetzt;
|
||||
try {
|
||||
const roh = localStorage.getItem('ns_last_played');
|
||||
if (!roh) return;
|
||||
const daten = JSON.parse(roh);
|
||||
daten.position = S.audio?.currentTime || 0;
|
||||
daten.savedAt = Math.floor(jetzt / 1000);
|
||||
localStorage.setItem('ns_last_played', JSON.stringify(daten));
|
||||
} catch {}
|
||||
}
|
||||
|
||||
export function _restoreLastPlayed() {
|
||||
try {
|
||||
const saved = JSON.parse(localStorage.getItem('ns_last_played') || 'null');
|
||||
@@ -1388,5 +1407,20 @@ export function _restoreLastPlayed() {
|
||||
// Web path: prime the audio element so play button works immediately.
|
||||
S.audio.src = streamUrl(s.id);
|
||||
S.audio.load();
|
||||
// Gespeicherte Position wiederherstellen, sobald die Dauer bekannt ist. Lag sie im letzten
|
||||
// Rest des Titels, faengt der Titel lieber von vorn an — sonst spielt man beim Fortsetzen
|
||||
// zwei Sekunden Abspann und springt weiter.
|
||||
const pos = Number(saved.position) || 0;
|
||||
if (pos > 1) {
|
||||
const setzen = () => {
|
||||
const rest = (S.audio.duration || 0) - pos;
|
||||
if (S.audio.duration && rest > 5) {
|
||||
try { S.audio.currentTime = pos; } catch {}
|
||||
updateProgress();
|
||||
}
|
||||
S.audio.removeEventListener('loadedmetadata', setzen);
|
||||
};
|
||||
S.audio.addEventListener('loadedmetadata', setzen);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
Reference in new issue
Block a user