diff --git a/backend/db.js b/backend/db.js index 9807ae8..555946c 100644 --- a/backend/db.js +++ b/backend/db.js @@ -100,31 +100,6 @@ async function insertBulk(table_name, data, columns) { return accumulator; }, []); - - if (table_name.toLowerCase() === "jf_playback_activity") { - let accumulator = []; - for (const currentItem of data) { - const existingInsertCheck = await query( - `SELECT * FROM jf_activity_watchdog where "NowPlayingItemId" =$1 and "SeasonId"=$2 and "EpisodeId"=$3 and "UserId"=$4 and "DeviceId"=$5 order by "ActivityDateInserted" desc limit 1`, - [currentItem.NowPlayingItemId, currentItem.SeasonId, currentItem.EpisodeId, currentItem.UserId, currentItem.DeviceId] - ).then((res) => res.rows); - - let isNotPbDuplicate = true; - if (existingInsertCheck.length > 0) { - const pbTime = moment(existingInsertCheck[0].ActivityDateInserted, "YYYY-MM-DD HH:mm:ss.SSSZ").millisecond(0); - const ciTime = moment(currentItem.ActivityDateInserted, "YYYY-MM-DD HH:mm:ss.SSSZ").millisecond(0); - if (pbTime.isSame(ciTime)) { - isNotPbDuplicate = false; - } - } - - if (isNotPbDuplicate) { - accumulator.push(currentItem); - } - } - - data = accumulator; - } } // diff --git a/backend/migrations/070_jf_playback_activity_add_unique_constraint.js b/backend/migrations/070_jf_playback_activity_add_unique_constraint.js new file mode 100644 index 0000000..40d928a --- /dev/null +++ b/backend/migrations/070_jf_playback_activity_add_unique_constraint.js @@ -0,0 +1,22 @@ +exports.up = async function (knex) { + try { + const hasTable = await knex.schema.hasTable("app_config"); + if (hasTable) { + await knex.schema.alterTable("jf_playback_activity", function (table) { + table.unique("Id"); + }); + } + } catch (error) { + console.error(error); + } +}; + +exports.down = async function (knex) { + try { + await knex.schema.alterTable("jf_playback_activity", function (table) { + table.jf_playback_activity("Id"); + }); + } catch (error) { + console.error(error); + } +}; diff --git a/backend/migrations/071_jf_watchdog_table_add_activity_id_field.js b/backend/migrations/071_jf_watchdog_table_add_activity_id_field.js new file mode 100644 index 0000000..31f5fe9 --- /dev/null +++ b/backend/migrations/071_jf_watchdog_table_add_activity_id_field.js @@ -0,0 +1,29 @@ +exports.up = async function (knex) { + return knex.transaction(async (trx) => { + try { + const hasTable = await trx.schema.hasTable("jf_activity_watchdog"); + if (hasTable) { + await trx("jf_activity_watchdog").truncate(); + await trx.schema.alterTable("jf_activity_watchdog", function (table) { + table.text("ActivityId").notNullable(); + }); + } + } catch (error) { + console.error(error); + throw error; + } + }); +}; + +exports.down = async function (knex) { + return knex.transaction(async (trx) => { + try { + await trx.schema.alterTable("jf_activity_watchdog", function (table) { + table.dropColumn("ActivityId"); + }); + } catch (error) { + console.error(error); + throw error; + } + }); +}; diff --git a/backend/models/jf_activity_watchdog.js b/backend/models/jf_activity_watchdog.js index db9026d..bde3de8 100644 --- a/backend/models/jf_activity_watchdog.js +++ b/backend/models/jf_activity_watchdog.js @@ -1,59 +1,60 @@ -const moment = require('moment'); - - +const moment = require("moment"); +const { randomUUID } = require("crypto"); const jf_activity_watchdog_columns = [ - "Id", - "IsPaused", - "UserId", - "UserName", - "Client", - "DeviceName", - "DeviceId", - "ApplicationVersion", - "NowPlayingItemId", - "NowPlayingItemName", - "EpisodeId", - "SeasonId", - "SeriesName", - "PlaybackDuration", - "PlayMethod", - "ActivityDateInserted", - { name: 'MediaStreams', mod: ':json' }, - { name: 'TranscodingInfo', mod: ':json' }, - { name: 'PlayState', mod: ':json' }, - "OriginalContainer", - "RemoteEndPoint", - "ServerId", - ]; + "Id", + "ActivityId", + "IsPaused", + "UserId", + "UserName", + "Client", + "DeviceName", + "DeviceId", + "ApplicationVersion", + "NowPlayingItemId", + "NowPlayingItemName", + "EpisodeId", + "SeasonId", + "SeriesName", + "PlaybackDuration", + "PlayMethod", + "ActivityDateInserted", + { name: "MediaStreams", mod: ":json" }, + { name: "TranscodingInfo", mod: ":json" }, + { name: "PlayState", mod: ":json" }, + "OriginalContainer", + "RemoteEndPoint", + "ServerId", +]; +const jf_activity_watchdog_mapping = (item) => ({ + Id: item.Id, + ActivityId: item.ActivityId !== undefined ? item.ActivityId : randomUUID(), + IsPaused: item.PlayState.IsPaused !== undefined ? item.PlayState.IsPaused : item.IsPaused, + UserId: item.UserId, + UserName: item.UserName, + Client: item.Client, + DeviceName: item.DeviceName, + DeviceId: item.DeviceId, + ApplicationVersion: item.ApplicationVersion, + NowPlayingItemId: item.NowPlayingItem.SeriesId !== undefined ? item.NowPlayingItem.SeriesId : item.NowPlayingItem.Id, + NowPlayingItemName: item.NowPlayingItem.Name, + EpisodeId: item.NowPlayingItem.SeriesId !== undefined ? item.NowPlayingItem.Id : null, + SeasonId: item.NowPlayingItem.SeasonId || null, + SeriesName: item.NowPlayingItem.SeriesName || null, + PlaybackDuration: item.PlaybackDuration !== undefined ? item.PlaybackDuration : 0, + PlayMethod: item.PlayState.PlayMethod, + ActivityDateInserted: + item.ActivityDateInserted !== undefined ? item.ActivityDateInserted : moment().format("YYYY-MM-DD HH:mm:ss.SSSZ"), + MediaStreams: item.NowPlayingItem.MediaStreams ? item.NowPlayingItem.MediaStreams : null, + TranscodingInfo: item.TranscodingInfo ? item.TranscodingInfo : null, + PlayState: item.PlayState ? item.PlayState : null, + OriginalContainer: item.NowPlayingItem && item.NowPlayingItem.Container ? item.NowPlayingItem.Container : null, + RemoteEndPoint: item.RemoteEndPoint || null, + ServerId: item.ServerId || null, +}); - const jf_activity_watchdog_mapping = (item) => ({ - Id: item.Id , - IsPaused: item.PlayState.IsPaused !== undefined ? item.PlayState.IsPaused : item.IsPaused, - UserId: item.UserId, - UserName: item.UserName, - Client: item.Client, - DeviceName: item.DeviceName, - DeviceId: item.DeviceId, - ApplicationVersion: item.ApplicationVersion, - NowPlayingItemId: item.NowPlayingItem.SeriesId !== undefined ? item.NowPlayingItem.SeriesId : item.NowPlayingItem.Id, - NowPlayingItemName: item.NowPlayingItem.Name, - EpisodeId: item.NowPlayingItem.SeriesId !== undefined ? item.NowPlayingItem.Id: null, - SeasonId: item.NowPlayingItem.SeasonId || null, - SeriesName: item.NowPlayingItem.SeriesName || null, - PlaybackDuration: item.PlaybackDuration !== undefined ? item.PlaybackDuration: 0, - PlayMethod:item.PlayState.PlayMethod, - ActivityDateInserted: item.ActivityDateInserted !== undefined ? item.ActivityDateInserted: moment().format('YYYY-MM-DD HH:mm:ss.SSSZ'), - MediaStreams: item.NowPlayingItem.MediaStreams ? item.NowPlayingItem.MediaStreams : null , - TranscodingInfo: item.TranscodingInfo? item.TranscodingInfo : null, - PlayState: item.PlayState? item.PlayState : null, - OriginalContainer: item.NowPlayingItem && item.NowPlayingItem.Container ? item.NowPlayingItem.Container : null, - RemoteEndPoint: item.RemoteEndPoint || null, - ServerId: item.ServerId || null, - }); - - module.exports = { - jf_activity_watchdog_columns, - jf_activity_watchdog_mapping - }; \ No newline at end of file +module.exports = { + jf_activity_watchdog_columns, + jf_activity_watchdog_mapping, +}; diff --git a/backend/models/jf_playback_activity.js b/backend/models/jf_playback_activity.js index 2908635..9f44bf9 100644 --- a/backend/models/jf_playback_activity.js +++ b/backend/models/jf_playback_activity.js @@ -1,55 +1,54 @@ - const columnsPlayback = [ - "Id", - "IsPaused", - "UserId", - "UserName", - "Client", - "DeviceName", - "DeviceId", - "ApplicationVersion", - "NowPlayingItemId", - "NowPlayingItemName", - "EpisodeId", - "SeasonId", - "SeriesName", - "PlaybackDuration", - "PlayMethod", - "ActivityDateInserted", - { name: 'MediaStreams', mod: ':json' }, - { name: 'TranscodingInfo', mod: ':json' }, - { name: 'PlayState', mod: ':json' }, - "OriginalContainer", - "RemoteEndPoint", - "ServerId" - ]; +const columnsPlayback = [ + "Id", + "IsPaused", + "UserId", + "UserName", + "Client", + "DeviceName", + "DeviceId", + "ApplicationVersion", + "NowPlayingItemId", + "NowPlayingItemName", + "EpisodeId", + "SeasonId", + "SeriesName", + "PlaybackDuration", + "PlayMethod", + "ActivityDateInserted", + { name: "MediaStreams", mod: ":json" }, + { name: "TranscodingInfo", mod: ":json" }, + { name: "PlayState", mod: ":json" }, + "OriginalContainer", + "RemoteEndPoint", + "ServerId", +]; +const mappingPlayback = (item) => ({ + Id: item.ActivityId !== undefined ? item.ActivityId : item.Id, + IsPaused: item.PlayState.IsPaused !== undefined ? item.PlayState.IsPaused : item.IsPaused, + UserId: item.UserId, + UserName: item.UserName, + Client: item.Client, + DeviceName: item.DeviceName, + DeviceId: item.DeviceId, + ApplicationVersion: item.ApplicationVersion, + NowPlayingItemId: item.NowPlayingItem.SeriesId !== undefined ? item.NowPlayingItem.SeriesId : item.NowPlayingItem.Id, + NowPlayingItemName: item.NowPlayingItem.Name, + EpisodeId: item.NowPlayingItem.SeriesId !== undefined ? item.NowPlayingItem.Id : null, + SeasonId: item.NowPlayingItem.SeasonId || null, + SeriesName: item.NowPlayingItem.SeriesName || null, + PlaybackDuration: item.PlaybackDuration !== undefined ? item.PlaybackDuration : 0, + PlayMethod: item.PlayState.PlayMethod !== undefined ? item.PlayState.PlayMethod : item.PlayMethod, + ActivityDateInserted: item.ActivityDateInserted !== undefined ? item.ActivityDateInserted : new Date().toISOString(), + MediaStreams: item.MediaStreams ? item.MediaStreams : null, + TranscodingInfo: item.TranscodingInfo ? item.TranscodingInfo : null, + PlayState: item.PlayState ? item.PlayState : null, + OriginalContainer: item.OriginalContainer ? item.OriginalContainer : null, + RemoteEndPoint: item.RemoteEndPoint ? item.RemoteEndPoint : null, + ServerId: item.ServerId ? item.ServerId : null, +}); - const mappingPlayback = (item) => ({ - Id: item.Id , - IsPaused: item.PlayState.IsPaused !== undefined ? item.PlayState.IsPaused : item.IsPaused, - UserId: item.UserId, - UserName: item.UserName, - Client: item.Client, - DeviceName: item.DeviceName, - DeviceId: item.DeviceId, - ApplicationVersion: item.ApplicationVersion, - NowPlayingItemId: item.NowPlayingItem.SeriesId !== undefined ? item.NowPlayingItem.SeriesId : item.NowPlayingItem.Id, - NowPlayingItemName: item.NowPlayingItem.Name, - EpisodeId: item.NowPlayingItem.SeriesId !== undefined ? item.NowPlayingItem.Id: null, - SeasonId: item.NowPlayingItem.SeasonId || null, - SeriesName: item.NowPlayingItem.SeriesName || null, - PlaybackDuration: item.PlaybackDuration !== undefined ? item.PlaybackDuration: 0, - PlayMethod: item.PlayState.PlayMethod !== undefined ? item.PlayState.PlayMethod : item.PlayMethod , - ActivityDateInserted: item.ActivityDateInserted !== undefined ? item.ActivityDateInserted: new Date().toISOString(), - MediaStreams: item.MediaStreams ? item.MediaStreams : null , - TranscodingInfo: item.TranscodingInfo? item.TranscodingInfo : null, - PlayState: item.PlayState? item.PlayState : null, - OriginalContainer: item.OriginalContainer ? item.OriginalContainer : null, - RemoteEndPoint: item.RemoteEndPoint ? item.RemoteEndPoint : null, - ServerId: item.ServerId ? item.ServerId : null, - }); - - module.exports = { - columnsPlayback, - mappingPlayback, - }; \ No newline at end of file +module.exports = { + columnsPlayback, + mappingPlayback, +}; diff --git a/backend/tasks/ActivityMonitor.js b/backend/tasks/ActivityMonitor.js index 581f2c5..caa2cb0 100644 --- a/backend/tasks/ActivityMonitor.js +++ b/backend/tasks/ActivityMonitor.js @@ -140,10 +140,7 @@ async function ActivityMonitor(interval) { const playbackData = WatchdogData.filter((id) => !SessionData.some((row) => row.Id === id.Id)); let playbackToInsert = playbackData.map((obj) => { - const uuid = randomUUID(); - - obj.Id = uuid; - + obj.Id = obj.ActivityId; let startTime = moment(obj.ActivityDateInserted, "YYYY-MM-DD HH:mm:ss.SSSZ"); let endTime = moment(); diff --git a/src/pages/components/activity/activity-table.jsx b/src/pages/components/activity/activity-table.jsx index e8e94ef..cde3a30 100644 --- a/src/pages/components/activity/activity-table.jsx +++ b/src/pages/components/activity/activity-table.jsx @@ -255,7 +255,17 @@ export default function ActivityTable(props) { enableExpanding: true, enableDensityToggle: false, enableTopToolbar: Object.keys(rowSelection).length > 0, - initialState: { expanded: false, showGlobalFilter: true, pagination: { pageSize: 10, pageIndex: 0 } }, + initialState: { + expanded: false, + showGlobalFilter: true, + pagination: { pageSize: 10, pageIndex: 0 }, + sorting: [ + { + id: "Date", + desc: true, + }, + ], + }, localization: { MRT_Localization_EN }, showAlertBanner: false, enableHiding: false,