diff --git a/frigate/api/media.py b/frigate/api/media.py
index ba4bba608..4aae06672 100644
--- a/frigate/api/media.py
+++ b/frigate/api/media.py
@@ -7,7 +7,6 @@ import os
import subprocess as sp
import time
from datetime import datetime, timedelta, timezone
-from io import BytesIO
from urllib.parse import unquote
import cv2
@@ -15,7 +14,6 @@ import numpy as np
import pytz
from flask import Blueprint, Response, current_app, jsonify, make_response, request
from peewee import DoesNotExist, fn
-from PIL import Image
from tzlocal import get_localzone_name
from werkzeug.utils import secure_filename
diff --git a/web/src/components/icons/FrigatePlusIcon.tsx b/web/src/components/icons/FrigatePlusIcon.tsx
new file mode 100644
index 000000000..1a9ff2e05
--- /dev/null
+++ b/web/src/components/icons/FrigatePlusIcon.tsx
@@ -0,0 +1,21 @@
+import { LuPlus } from "react-icons/lu";
+import Logo from "../Logo";
+
+type FrigatePlusIconProps = {
+ className?: string;
+ onClick?: () => void;
+};
+export default function FrigatePlusIcon({
+ className,
+ onClick,
+}: FrigatePlusIconProps) {
+ return (
+
+
+
+
+ );
+}
diff --git a/web/src/components/player/HlsVideoPlayer.tsx b/web/src/components/player/HlsVideoPlayer.tsx
index 5f96a017d..7024ff971 100644
--- a/web/src/components/player/HlsVideoPlayer.tsx
+++ b/web/src/components/player/HlsVideoPlayer.tsx
@@ -137,10 +137,15 @@ export default function HlsVideoPlayer({
className="absolute bottom-5 left-1/2 -translate-x-1/2 z-50"
video={videoRef.current}
isPlaying={isPlaying}
- show={visible && controls}
+ show={visible && (controls || controlsOpen)}
muted={muted}
volume={volume}
- controlsOpen={controlsOpen}
+ features={{
+ volume: true,
+ seek: true,
+ playbackRate: true,
+ plusUpload: true,
+ }}
setControlsOpen={setControlsOpen}
setMuted={setMuted}
playbackRate={videoRef.current?.playbackRate ?? 1}
diff --git a/web/src/components/player/VideoControls.tsx b/web/src/components/player/VideoControls.tsx
index 872289aa0..7b511ba53 100644
--- a/web/src/components/player/VideoControls.tsx
+++ b/web/src/components/player/VideoControls.tsx
@@ -1,4 +1,4 @@
-import { useCallback, useMemo } from "react";
+import { useCallback, useMemo, useState } from "react";
import { isSafari } from "react-device-detect";
import { LuPause, LuPlay } from "react-icons/lu";
import {
@@ -18,17 +18,30 @@ import {
} from "react-icons/md";
import useKeyboardListener from "@/hooks/use-keyboard-listener";
import { VolumeSlider } from "../ui/slider";
+import FrigatePlusIcon from "../icons/FrigatePlusIcon";
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+ AlertDialogTrigger,
+} from "../ui/alert-dialog";
type VideoControls = {
volume?: boolean;
seek?: boolean;
playbackRate?: boolean;
+ plusUpload?: boolean;
};
const CONTROLS_DEFAULT: VideoControls = {
volume: true,
seek: true,
playbackRate: true,
+ plusUpload: false,
};
const PLAYBACK_RATE_DEFAULT = isSafari ? [0.5, 1, 2] : [0.5, 1, 2, 4, 8, 16];
@@ -40,7 +53,6 @@ type VideoControlsProps = {
show: boolean;
muted?: boolean;
volume?: number;
- controlsOpen?: boolean;
playbackRates?: number[];
playbackRate: number;
hotKeys?: boolean;
@@ -58,7 +70,6 @@ export default function VideoControls({
show,
muted,
volume,
- controlsOpen,
playbackRates = PLAYBACK_RATE_DEFAULT,
playbackRate,
hotKeys = true,
@@ -189,7 +200,6 @@ export default function VideoControls({
)}
{features.playbackRate && (
{
if (setControlsOpen) {
setControlsOpen(open);
@@ -214,6 +224,81 @@ export default function VideoControls({
)}
+ {features.plusUpload && (
+ {
+ if (setControlsOpen) {
+ setControlsOpen(false);
+ }
+ }}
+ onOpen={() => {
+ onPlayPause(false);
+
+ if (setControlsOpen) {
+ setControlsOpen(true);
+ }
+ }}
+ />
+ )}
);
}
+
+type FrigatePlusUploadButtonProps = {
+ video?: HTMLVideoElement | null;
+ onOpen: () => void;
+ onClose: () => void;
+};
+function FrigatePlusUploadButton({
+ video,
+ onOpen,
+ onClose,
+}: FrigatePlusUploadButtonProps) {
+ const [videoImg, setVideoImg] = useState();
+
+ return (
+ {
+ if (!open) {
+ onClose();
+ }
+ }}
+ >
+
+ {
+ onOpen();
+
+ if (video) {
+ const videoSize = [video.clientWidth, video.clientHeight];
+ const canvas = document.createElement("canvas");
+ canvas.width = videoSize[0];
+ canvas.height = videoSize[1];
+
+ const context = canvas?.getContext("2d");
+
+ if (context) {
+ context.drawImage(video, 0, 0, videoSize[0], videoSize[1]);
+ setVideoImg(canvas.toDataURL("image/webp"));
+ }
+ }
+ }}
+ />
+
+
+
+
+ Submit this frame to Frigate Plus?
+
+
+
+
+ Submit
+ Cancel
+
+
+
+ );
+}