fix scrobble durations (#269)

* fix scrobble durations

* Fix scrobble condition on last song in queue, normalize ms

---------

Co-authored-by: jeffvli <jeffvictorli@gmail.com>
This commit is contained in:
Kendall Garner 2023-10-06 04:45:47 +00:00 committed by GitHub
parent 118a9f73d1
commit 6bac172bbe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -36,17 +36,18 @@ Progress Events (Jellyfin only):
const checkScrobbleConditions = (args: { const checkScrobbleConditions = (args: {
scrobbleAtDuration: number; scrobbleAtDuration: number;
scrobbleAtPercentage: number; scrobbleAtPercentage: number;
songCompletedDuration: number; songCompletedDurationMs: number;
songDuration: number; songDurationMs: number;
}) => { }) => {
const { scrobbleAtDuration, scrobbleAtPercentage, songCompletedDuration, songDuration } = args; const { scrobbleAtDuration, scrobbleAtPercentage, songCompletedDurationMs, songDurationMs } =
const percentageOfSongCompleted = songDuration args;
? (songCompletedDuration / songDuration) * 100 const percentageOfSongCompleted = songDurationMs
? (songCompletedDurationMs / songDurationMs) * 100
: 0; : 0;
return ( return (
percentageOfSongCompleted >= scrobbleAtPercentage || percentageOfSongCompleted >= scrobbleAtPercentage ||
songCompletedDuration >= scrobbleAtDuration songCompletedDurationMs >= scrobbleAtDuration
); );
}; };
@ -97,15 +98,15 @@ export const useScrobble = () => {
// const currentSong = current[0] as QueueSong | undefined; // const currentSong = current[0] as QueueSong | undefined;
const previousSong = previous[0] as QueueSong; const previousSong = previous[0] as QueueSong;
const previousSongTime = previous[1] as number; const previousSongTimeSec = previous[1] as number;
// Send completion scrobble when song changes and a previous song exists // Send completion scrobble when song changes and a previous song exists
if (previousSong?.id) { if (previousSong?.id) {
const shouldSubmitScrobble = checkScrobbleConditions({ const shouldSubmitScrobble = checkScrobbleConditions({
scrobbleAtDuration: scrobbleSettings?.scrobbleAtDuration, scrobbleAtDuration: scrobbleSettings?.scrobbleAtDuration,
scrobbleAtPercentage: scrobbleSettings?.scrobbleAtPercentage, scrobbleAtPercentage: scrobbleSettings?.scrobbleAtPercentage,
songCompletedDuration: previousSongTime, songCompletedDurationMs: previousSongTimeSec * 1000,
songDuration: previousSong.duration, songDurationMs: previousSong.duration,
}); });
if ( if (
@ -114,7 +115,7 @@ export const useScrobble = () => {
) { ) {
const position = const position =
previousSong?.serverType === ServerType.JELLYFIN previousSong?.serverType === ServerType.JELLYFIN
? previousSongTime * 1e7 ? previousSongTimeSec * 1e7
: undefined; : undefined;
sendScrobble.mutate({ sendScrobble.mutate({
@ -168,7 +169,10 @@ export const useScrobble = () => {
); );
const handleScrobbleFromStatusChange = useCallback( const handleScrobbleFromStatusChange = useCallback(
(status: PlayerStatus | undefined) => { (
current: (PlayerStatus | number | undefined)[],
previous: (PlayerStatus | number | undefined)[],
) => {
if (!isScrobbleEnabled) return; if (!isScrobbleEnabled) return;
const currentSong = usePlayerStore.getState().current.song; const currentSong = usePlayerStore.getState().current.song;
@ -180,8 +184,11 @@ export const useScrobble = () => {
? usePlayerStore.getState().current.time * 1e7 ? usePlayerStore.getState().current.time * 1e7
: undefined; : undefined;
const currentStatus = current[0] as PlayerStatus;
const currentTimeSec = current[1] as number;
// Whenever the player is restarted, send a 'start' scrobble // Whenever the player is restarted, send a 'start' scrobble
if (status === PlayerStatus.PLAYING) { if (currentStatus === PlayerStatus.PLAYING) {
sendScrobble.mutate({ sendScrobble.mutate({
query: { query: {
event: 'unpause', event: 'unpause',
@ -194,7 +201,7 @@ export const useScrobble = () => {
if (currentSong?.serverType === ServerType.JELLYFIN) { if (currentSong?.serverType === ServerType.JELLYFIN) {
progressIntervalId.current = setInterval(() => { progressIntervalId.current = setInterval(() => {
const currentTime = usePlayerStore.getState().current.time; const currentTime = currentTimeSec;
handleScrobbleFromSeek(currentTime); handleScrobbleFromSeek(currentTime);
}, 10000); }, 10000);
} }
@ -215,12 +222,17 @@ export const useScrobble = () => {
clearInterval(progressIntervalId.current as ReturnType<typeof setInterval>); clearInterval(progressIntervalId.current as ReturnType<typeof setInterval>);
} }
} else { } else {
const isLastTrackInQueue = usePlayerStore.getState().actions.checkIsLastTrack();
const previousTimeSec = previous[1] as number;
// If not already scrobbled, send a 'submission' scrobble if conditions are met // If not already scrobbled, send a 'submission' scrobble if conditions are met
const shouldSubmitScrobble = checkScrobbleConditions({ const shouldSubmitScrobble = checkScrobbleConditions({
scrobbleAtDuration: scrobbleSettings?.scrobbleAtDuration, scrobbleAtDuration: scrobbleSettings?.scrobbleAtDuration,
scrobbleAtPercentage: scrobbleSettings?.scrobbleAtPercentage, scrobbleAtPercentage: scrobbleSettings?.scrobbleAtPercentage,
songCompletedDuration: usePlayerStore.getState().current.time, // If scrobbling the last song in the queue, use the previous time instead of the current time since otherwise time value will be 0
songDuration: currentSong.duration, songCompletedDurationMs:
(isLastTrackInQueue ? previousTimeSec : currentTimeSec) * 1000,
songDurationMs: currentSong.duration,
}); });
if (!isCurrentSongScrobbled && shouldSubmitScrobble) { if (!isCurrentSongScrobbled && shouldSubmitScrobble) {
@ -263,8 +275,8 @@ export const useScrobble = () => {
const shouldSubmitScrobble = checkScrobbleConditions({ const shouldSubmitScrobble = checkScrobbleConditions({
scrobbleAtDuration: scrobbleSettings?.scrobbleAtDuration, scrobbleAtDuration: scrobbleSettings?.scrobbleAtDuration,
scrobbleAtPercentage: scrobbleSettings?.scrobbleAtPercentage, scrobbleAtPercentage: scrobbleSettings?.scrobbleAtPercentage,
songCompletedDuration: currentTime, songCompletedDurationMs: currentTime,
songDuration: currentSong.duration, songDurationMs: currentSong.duration,
}); });
if (!isCurrentSongScrobbled && shouldSubmitScrobble) { if (!isCurrentSongScrobbled && shouldSubmitScrobble) {
@ -313,8 +325,11 @@ export const useScrobble = () => {
); );
const unsubStatusChange = usePlayerStore.subscribe( const unsubStatusChange = usePlayerStore.subscribe(
(state) => state.current.status, (state) => [state.current.status, state.current.time],
handleScrobbleFromStatusChange, handleScrobbleFromStatusChange,
{
equalityFn: (a, b) => (a[0] as PlayerStatus) === (b[0] as PlayerStatus),
},
); );
return () => { return () => {