diff --git a/web/src/pages/History.tsx b/web/src/pages/History.tsx index 9a9568eaf..e2342e423 100644 --- a/web/src/pages/History.tsx +++ b/web/src/pages/History.tsx @@ -10,7 +10,7 @@ import axios from "axios"; import TimelinePlayerCard from "@/components/card/TimelinePlayerCard"; import { getHourlyTimelineData } from "@/utils/historyUtil"; -const API_LIMIT = 120; +const API_LIMIT = 200; function History() { const { data: config } = useSWR("config"); diff --git a/web/src/utils/historyUtil.ts b/web/src/utils/historyUtil.ts index 6b7bcfab9..b372dc1c2 100644 --- a/web/src/utils/historyUtil.ts +++ b/web/src/utils/historyUtil.ts @@ -1,3 +1,6 @@ +// group history cards by 60 seconds of activity +const GROUP_SECONDS = 60; + export function getHourlyTimelineData( timelinePages: HourlyTimeline[], detailLevel: string @@ -10,26 +13,42 @@ export function getHourlyTimelineData( const day = new Date(parseInt(hour) * 1000); day.setHours(0, 0, 0, 0); const dayKey = (day.getTime() / 1000).toString(); + + // build a map of course to the types that are included in this hour + // which allows us to know what items to keep depending on detail level const source_to_types: { [key: string]: string[] } = {}; + let cardTypeStart: { [camera: string]: number } = {}; Object.values(hourlyTimeline["hours"][hour]).forEach((i) => { - const time = new Date(i.timestamp * 1000); - time.setSeconds(0); - time.setMilliseconds(0); - const key = `${i.source_id}-${time.getMinutes()}`; - if (key in source_to_types) { - source_to_types[key].push(i.class_type); + if (i.timestamp > (cardTypeStart[i.camera] ?? 0) + GROUP_SECONDS) { + cardTypeStart[i.camera] = i.timestamp; + } + + const groupKey = `${i.source_id}-${cardTypeStart[i.camera]}`; + + if (groupKey in source_to_types) { + source_to_types[groupKey].push(i.class_type); } else { - source_to_types[key] = [i.class_type]; + source_to_types[groupKey] = [i.class_type]; } }); - if (!Object.keys(cards).includes(dayKey)) { + if (!(dayKey in cards)) { cards[dayKey] = {}; } - cards[dayKey][hour] = {}; + + if (!(hour in cards[dayKey])) { + cards[dayKey][hour] = {}; + } + + let cardStart: { [camera: string]: number } = {}; Object.values(hourlyTimeline["hours"][hour]).forEach((i) => { + if (i.timestamp > (cardStart[i.camera] ?? 0) + GROUP_SECONDS) { + cardStart[i.camera] = i.timestamp; + } + const time = new Date(i.timestamp * 1000); - const minuteKey = `${i.camera}-${time.getMinutes()}`; + const groupKey = `${i.camera}-${cardStart[i.camera]}`; + const sourceKey = `${i.source_id}-${cardStart[i.camera]}`; const uniqueKey = `${i.source_id}-${i.class_type}`; // detail level for saving items @@ -42,8 +61,7 @@ export function getHourlyTimelineData( let add = true; if (detailLevel == "normal") { if ( - source_to_types[`${i.source_id}-${time.getMinutes()}`].length > - 1 && + source_to_types[sourceKey].length > 1 && ["active", "attribute", "gone", "stationary", "visible"].includes( i.class_type ) @@ -52,8 +70,7 @@ export function getHourlyTimelineData( } } else if (detailLevel == "extra") { if ( - source_to_types[`${i.source_id}-${time.getMinutes()}`].length > - 1 && + source_to_types[sourceKey].length > 1 && i.class_type in ["attribute", "gone", "visible"] ) { add = false; @@ -61,18 +78,16 @@ export function getHourlyTimelineData( } if (add) { - if (minuteKey in cards[dayKey][hour]) { + if (groupKey in cards[dayKey][hour]) { if ( - !cards[dayKey][hour][minuteKey].uniqueKeys.includes( - uniqueKey - ) || + !cards[dayKey][hour][groupKey].uniqueKeys.includes(uniqueKey) || detailLevel == "full" ) { - cards[dayKey][hour][minuteKey].entries.push(i); - cards[dayKey][hour][minuteKey].uniqueKeys.push(uniqueKey); + cards[dayKey][hour][groupKey].entries.push(i); + cards[dayKey][hour][groupKey].uniqueKeys.push(uniqueKey); } } else { - cards[dayKey][hour][minuteKey] = { + cards[dayKey][hour][groupKey] = { camera: i.camera, time: time.getTime() / 1000, entries: [i],