Add synced listening history
This commit is contained in:
@@ -821,7 +821,12 @@
|
||||
</template>
|
||||
</button>
|
||||
<div class="track-info">
|
||||
<div class="track-title" x-text="item.track?.title || item.track_title"></div>
|
||||
<div class="track-title">
|
||||
<span x-text="item.track?.title || item.track_title"></span>
|
||||
<span class="history-device-badge"
|
||||
:title="item.device_id"
|
||||
x-text="item.device_name"></span>
|
||||
</div>
|
||||
<div class="track-artists-inline">
|
||||
<template x-for="(artist, artistIdx) in $store.library.trackArtistLinks(item.track)" :key="artist.label + '-' + artist.id + '-' + artistIdx">
|
||||
<span>
|
||||
|
||||
@@ -726,6 +726,7 @@ document.addEventListener('alpine:init', () => {
|
||||
_historyRecorded: false,
|
||||
_nowPlayingSent: false,
|
||||
_playbackStartedAt: null,
|
||||
_listenId: null,
|
||||
_listenedSeconds: 0,
|
||||
_lastAudioTime: 0,
|
||||
_localSourceTrackId: null,
|
||||
@@ -754,7 +755,7 @@ document.addEventListener('alpine:init', () => {
|
||||
|
||||
audio.addEventListener('ended', () => {
|
||||
this._trackListenedDelta();
|
||||
this._recordHistory(true);
|
||||
this._recordHistory('finished');
|
||||
if (Alpine.store('devices')?.shouldPlayJamLocally()) {
|
||||
this.isPlaying = false;
|
||||
return;
|
||||
@@ -1437,21 +1438,24 @@ document.addEventListener('alpine:init', () => {
|
||||
} catch {}
|
||||
},
|
||||
|
||||
_recordHistory(completed) {
|
||||
_recordHistory(endedReason) {
|
||||
if (this._historyRecorded || !this.currentTrack) return;
|
||||
if (!Number.isInteger(Number(this.currentTrack.id)) || this.currentTrack.federated_cache) {
|
||||
return;
|
||||
}
|
||||
this._historyRecorded = true;
|
||||
const completed = endedReason === 'finished';
|
||||
const listenedSeconds = this._historyListenedSeconds(completed);
|
||||
fetch('/api/player/history', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
track_id: this.currentTrack.id,
|
||||
listen_id: this._listenId,
|
||||
started_at: this._playbackStartedAt,
|
||||
duration_listened: listenedSeconds,
|
||||
completed: completed,
|
||||
ended_reason: endedReason,
|
||||
}),
|
||||
}).catch(() => {});
|
||||
},
|
||||
@@ -1464,13 +1468,15 @@ document.addEventListener('alpine:init', () => {
|
||||
const listened = Math.floor(Number(this._listenedSeconds || 0));
|
||||
const threshold = Math.ceil(duration / 2);
|
||||
if (threshold <= 0 || listened < threshold) return false;
|
||||
this._recordHistory(true);
|
||||
this._recordHistory('unknown');
|
||||
return true;
|
||||
},
|
||||
|
||||
_resetPlaybackTracking() {
|
||||
this._nowPlayingSent = false;
|
||||
this._playbackStartedAt = null;
|
||||
this._listenId = (globalThis.crypto?.randomUUID?.()
|
||||
|| `${Date.now()}-${Math.random().toString(16).slice(2)}`);
|
||||
this._listenedSeconds = 0;
|
||||
this._lastAudioTime = 0;
|
||||
},
|
||||
|
||||
@@ -3701,6 +3701,18 @@ button.user-stat:hover {
|
||||
}
|
||||
|
||||
.history-row:last-child { border-bottom: 0; }
|
||||
.history-device-badge {
|
||||
display: inline-block;
|
||||
margin-left: .45rem;
|
||||
padding: .08rem .38rem;
|
||||
border: 1px solid color-mix(in srgb, currentColor 28%, transparent);
|
||||
border-radius: 999px;
|
||||
color: var(--text-muted);
|
||||
font-size: .68rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
vertical-align: .12rem;
|
||||
}
|
||||
|
||||
.history-cover {
|
||||
width: 40px;
|
||||
|
||||
Reference in New Issue
Block a user