From eb0b901d89bd183eb59fb769ee6e822318798f9d Mon Sep 17 00:00:00 2001 From: CyferShepard Date: Sun, 7 Apr 2024 16:54:24 +0200 Subject: [PATCH] fixes to prevent updating existing records with playback greater than item duration --- ...recent_playback_activity_function_fix_4.js | 156 ++++++++++++++++++ backend/tasks/ActivityMonitor.js | 6 +- 2 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 backend/migrations/069_jf_recent_playback_activity_function_fix_4.js diff --git a/backend/migrations/069_jf_recent_playback_activity_function_fix_4.js b/backend/migrations/069_jf_recent_playback_activity_function_fix_4.js new file mode 100644 index 0000000..c4d57cb --- /dev/null +++ b/backend/migrations/069_jf_recent_playback_activity_function_fix_4.js @@ -0,0 +1,156 @@ +exports.up = async function (knex) { + try { + await knex.schema.raw(` + CREATE OR REPLACE FUNCTION jf_recent_playback_activity(hour_offset INT) + RETURNS TABLE ( + "RunTimeTicks" BIGINT, + "Progress" NUMERIC, + "Id" TEXT, + "IsPaused" BOOLEAN, + "UserId" TEXT, + "UserName" TEXT, + "Client" TEXT, + "DeviceName" TEXT, + "DeviceId" TEXT, + "ApplicationVersion" TEXT, + "NowPlayingItemId" TEXT, + "NowPlayingItemName" TEXT, + "SeasonId" TEXT, + "SeriesName" TEXT, + "EpisodeId" TEXT, + "PlaybackDuration" BIGINT, + "ActivityDateInserted" timestamptz, + "PlayMethod" TEXT, + "MediaStreams" JSON, + "TranscodingInfo" JSON, + "PlayState" JSON, + "OriginalContainer" TEXT, + "RemoteEndPoint" TEXT, + "ServerId" TEXT, + "Imported" BOOLEAN, + "RowNum" BIGINT + ) + AS $$ + BEGIN + RETURN QUERY + WITH rankedactivities AS ( + SELECT COALESCE(i."RunTimeTicks", e."RunTimeTicks") AS "RunTimeTicks", + ((a."PlaybackDuration" * 10000000)::numeric(100,0) / COALESCE(i."RunTimeTicks"::numeric(100,0), e."RunTimeTicks"::numeric(100,0), 1.0) * 100::numeric)::numeric(100,2) AS "Progress", + a."Id", + a."IsPaused", + a."UserId", + a."UserName", + a."Client", + a."DeviceName", + a."DeviceId", + a."ApplicationVersion", + a."NowPlayingItemId", + a."NowPlayingItemName", + a."SeasonId", + a."SeriesName", + a."EpisodeId", + a."PlaybackDuration", + a."ActivityDateInserted", + a."PlayMethod", + a."MediaStreams", + a."TranscodingInfo", + a."PlayState", + a."OriginalContainer", + a."RemoteEndPoint", + a."ServerId", + a.imported, + row_number() OVER (PARTITION BY a."NowPlayingItemId",a."EpisodeId",a."UserId" ORDER BY a."ActivityDateInserted" DESC) AS rownum + FROM jf_playback_activity a + LEFT JOIN jf_library_items i ON a."NowPlayingItemId" = i."Id" + LEFT JOIN jf_library_episodes e ON a."EpisodeId" = e."EpisodeId" + WHERE a."ActivityDateInserted" > (CURRENT_TIMESTAMP - (hour_offset || ' hours')::interval) + ORDER BY a."ActivityDateInserted" DESC + ) + SELECT * FROM rankedactivities WHERE rankedactivities.rownum = 1; + END; + $$ LANGUAGE plpgsql; + + `); + } catch (error) { + console.error(error); + } +}; + +exports.down = async function (knex) { + try { + await knex.raw(` + + CREATE OR REPLACE FUNCTION jf_recent_playback_activity(hour_offset INT) + RETURNS TABLE ( + "RunTimeTicks" BIGINT, + "Progress" NUMERIC, + "Id" TEXT, + "IsPaused" BOOLEAN, + "UserId" TEXT, + "UserName" TEXT, + "Client" TEXT, + "DeviceName" TEXT, + "DeviceId" TEXT, + "ApplicationVersion" TEXT, + "NowPlayingItemId" TEXT, + "NowPlayingItemName" TEXT, + "SeasonId" TEXT, + "SeriesName" TEXT, + "EpisodeId" TEXT, + "PlaybackDuration" BIGINT, + "ActivityDateInserted" timestamptz, + "PlayMethod" TEXT, + "MediaStreams" JSON, + "TranscodingInfo" JSON, + "PlayState" JSON, + "OriginalContainer" TEXT, + "RemoteEndPoint" TEXT, + "ServerId" TEXT, + "Imported" BOOLEAN, + "RowNum" BIGINT + ) + AS $$ + BEGIN + RETURN QUERY + WITH rankedactivities AS ( + SELECT COALESCE(i."RunTimeTicks", e."RunTimeTicks") AS "RunTimeTicks", + ((a."PlaybackDuration" * 10000000)::numeric(100,0) / COALESCE(i."RunTimeTicks"::numeric(100,0), e."RunTimeTicks"::numeric(100,0), 1.0) * 100::numeric)::numeric(10,2) AS "Progress", + a."Id", + a."IsPaused", + a."UserId", + a."UserName", + a."Client", + a."DeviceName", + a."DeviceId", + a."ApplicationVersion", + a."NowPlayingItemId", + a."NowPlayingItemName", + a."SeasonId", + a."SeriesName", + a."EpisodeId", + a."PlaybackDuration", + a."ActivityDateInserted", + a."PlayMethod", + a."MediaStreams", + a."TranscodingInfo", + a."PlayState", + a."OriginalContainer", + a."RemoteEndPoint", + a."ServerId", + a.imported, + row_number() OVER (PARTITION BY a."NowPlayingItemId",a."EpisodeId",a."UserId" ORDER BY a."ActivityDateInserted" DESC) AS rownum + FROM jf_playback_activity a + LEFT JOIN jf_library_items i ON a."NowPlayingItemId" = i."Id" + LEFT JOIN jf_library_episodes e ON a."EpisodeId" = e."EpisodeId" + WHERE a."ActivityDateInserted" > (CURRENT_TIMESTAMP - (hour_offset || ' hours')::interval) + ORDER BY a."ActivityDateInserted" DESC + ) + SELECT * FROM rankedactivities WHERE rankedactivities.rownum = 1; + END; + $$ LANGUAGE plpgsql; + + `); + } catch (error) { + console.error(error); + } +}; diff --git a/backend/tasks/ActivityMonitor.js b/backend/tasks/ActivityMonitor.js index 56a6624..0eee8f8 100644 --- a/backend/tasks/ActivityMonitor.js +++ b/backend/tasks/ActivityMonitor.js @@ -173,14 +173,16 @@ async function ActivityMonitor(interval) { ); let ExistingDataToUpdate = []; - //for each item in playbackToInsert, check if it exists in the recent playback activity and update accordingly + //for each item in playbackToInsert, check if it exists in the recent playback activity and update accordingly. insert new row if updating existing exceeds the runtime if (playbackToInsert.length > 0 && ExistingRecords.length > 0) { ExistingDataToUpdate = playbackToInsert.filter((playbackData) => { const existingrow = ExistingRecords.find( (existing) => existing.NowPlayingItemId === playbackData.NowPlayingItemId && existing.EpisodeId === playbackData.EpisodeId && - existing.UserId === playbackData.UserId + existing.UserId === playbackData.UserId && + (Number(existing.PlaybackDuration) + Number(playbackData.PlaybackDuration)) * 10000000 <= + Number(existing.RunTimeTicks) ); if (existingrow) {