blakeblackshear / frigate

NVR with realtime local object detection for IP cameras
https://frigate.video
MIT License
18.15k stars 1.66k forks source link

[FR] Link to recording from events page #9964

Closed ohslyfox closed 6 months ago

ohslyfox commented 6 months ago

Describe what you are trying to accomplish and why in non technical terms

I want to be able to click a link on an event from the Events page to view the recording where the event took place at the relevant timestamp. This feature will make it easier to navigate to the recording for an event and allow users to quickly audit footage before/after an event.

Describe the solution you'd like Events should include a link to the recording at the relevant timestamp if such recording exists.

Describe alternatives you've considered Currently users can navigate to the recording page for a specific camera to find timestamp links for events. This solution is not ideal since the events page can display events for all cameras making it more convenient for browsing recent events. This causes indirection when using Frigate as users start by browsing the Events page but need to redirect to the Recording page to find the recording for a particular event.

Additional context N/A

nwish commented 6 months ago

Are you referring to the Frigate card in Home Assistant?

Mincka commented 6 months ago

He's referring to the Events page of the add-on. I agree it would be great to be able to jump from an event in the /events page to the timestamp of its recording for that camera: /recording/Frigate_Maison_Avant/2024-03-02/15/16/49. I often need to seek before / after an event and you need to go back to the recordings find the corresponding event in the recordings for a specific camera. Currently, you need to: 1) Click on Cameras 2) Find the corresponding camera and click on Recordings 3) Find the event Actually, always displaying the list of cameras like it's done when at least one camera recordings are shown (/recording/Frigate_Buanderie) would save one click and help the navigation.

NickM-27 commented 6 months ago

Thanks for the request. The UI is being rewritten for 0.14 and this functionality has already been implemented

Mincka commented 6 months ago

For anyone interested, I just made a ViolentMonkey script that adds a "Go to Recording" link for each event. It works only for the recent events, the link is not added if you scroll and additional elements are added. It won't work with Ingress URL like "ccab4aaf_frigate-proxy/ingress". You currently need a direct access to the container to reach the http://xxxx:5000/events page directly. Don't forget to update the @matchpath. It's just a quick and dirty PoC while we wait for version 0.14.

image

// ==UserScript==
// @name         Add Navigation Button
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Add a button to navigate to a specific URL based on timestamp and camera name
// @author       Julien Ehrhart (Mincka)
// @match        http://10.0.0.84:5000/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

     // Unique identifier for the script run
    const SCRIPT_RUN_FLAG = 'violentmonkey-script-has-run';

    // Check if the script has already been run
    if (!document.body.classList.contains(SCRIPT_RUN_FLAG)) {
        // Mark the script as run
        document.body.classList.add(SCRIPT_RUN_FLAG);
    }

    // Function to format the timestamp into the desired URL format
    function formatTimestamp(timestampText, cameraName) {
        const [datePart, timePart] = timestampText.split(', ');
        const [month, day, year] = datePart.split('/').map(part => part.padStart(2, '0'));

        // Extract time and AM/PM part
        let [hour, minute, secondPart] = timePart.split(':');
        const ampm = secondPart.slice(3,5);
        secondPart = secondPart.slice(0, 2); // Extract only the seconds

        // Convert to 24-hour format
        hour = ((ampm === 'PM' && hour !== '12') ? parseInt(hour, 10) + 12 : hour);
        hour = ((ampm === 'AM' && hour === '12') ? '00' : hour); // Convert 12 AM to 00
        hour = '' + hour;
        hour = hour.padStart(2, '0');

        const formattedCameraName = cameraName.replace(/\s+/g, '_');
        return `/recording/${formattedCameraName}/20${year}-${month}-${day}/${hour}/${minute}/${secondPart}`;
    }

    // Function to create and add the link
    function addButtonToElement(element) {
        const timestampElement = element.querySelector('.text-sm.flex');
        const cameraNameElement = element.querySelector('.capitalize.text-sm.flex.align-center');

        if (timestampElement && cameraNameElement) {
            const timestampText = timestampElement.textContent.trim();
            const cameraName = cameraNameElement.textContent.trim();

            const url = formatTimestamp(timestampText, cameraName);

            // Create the link element
            const link = document.createElement('a');
            link.href = url;
            link.textContent = 'Go to Recording';
            link.style.marginLeft = '10px';

            // Append the button next to the timestamp
            timestampElement.appendChild(link);
        }
    }

    // Function to add buttons to all matching elements
    function addButtonsToAll() {
        // Find all elements that match the pattern
        const allElements = document.querySelectorAll('.flex.bg-slate-100, .flex.bg-slate-800');

        allElements.forEach(addButtonToElement);
    }

  let lastUrl;
  new MutationObserver(() => {
    if (location.href !== lastUrl) {
      if(location.href.includes("/events"))
      {
         setTimeout(addButtonsToAll, 500);
      }
      lastUrl = location.href;
    }
  }).observe(document, {subtree: true, childList: true})

})();