From 58f6435689222b9cfafdcaed2e7b8bfc4781bad5 Mon Sep 17 00:00:00 2001 From: Varun Raj Date: Wed, 28 Aug 2024 08:30:19 +0530 Subject: [PATCH] fix: support videos Signed-off-by: Varun Raj --- .../PotentialAlbumsAssets.tsx | 71 +++++++++++-------- src/config/routes.ts | 3 +- src/helpers/asset.helper.ts | 3 +- .../api/albums/potential-albums-assets.ts | 1 + .../api/immich-proxy/asset/video/[id].ts | 48 +++++++++++++ src/types/asset.d.ts | 1 + 6 files changed, 96 insertions(+), 31 deletions(-) create mode 100644 src/pages/api/immich-proxy/asset/video/[id].ts diff --git a/src/components/albums/potential-albums/PotentialAlbumsAssets.tsx b/src/components/albums/potential-albums/PotentialAlbumsAssets.tsx index c6f2685..9dc755b 100644 --- a/src/components/albums/potential-albums/PotentialAlbumsAssets.tsx +++ b/src/components/albums/potential-albums/PotentialAlbumsAssets.tsx @@ -4,12 +4,13 @@ import { listPotentialAlbumsAssets } from "@/handlers/api/album.handler"; import { IAsset } from "@/types/asset"; import React, { useEffect, useMemo, useState } from "react"; import { Gallery } from "react-grid-gallery"; -import Lightbox from "yet-another-react-lightbox"; +import Lightbox, { SlideImage, SlideTypes } from "yet-another-react-lightbox"; import Captions from "yet-another-react-lightbox/plugins/captions"; import { CalendarArrowDown, CalendarArrowUp, Hourglass } from "lucide-react"; - +import Video from "yet-another-react-lightbox/plugins/video"; export default function PotentialAlbumsAssets() { - const { startDate, selectedIds, assets, updateContext } = usePotentialAlbumContext(); + const { startDate, selectedIds, assets, updateContext } = + usePotentialAlbumContext(); const [loading, setLoading] = useState(false); const [errorMessage, setErrorMessage] = useState(null); @@ -20,7 +21,7 @@ export default function PotentialAlbumsAssets() { setLoading(true); updateContext({ assets: [], - }) + }); return listPotentialAlbumsAssets({ startDate }) .then((assets) => updateContext({ assets })) .catch(setErrorMessage) @@ -40,10 +41,20 @@ export default function PotentialAlbumsAssets() { const slides = useMemo( () => - images.map(({ original, width, height }) => ({ + images.map(({ original, width, height, type, videoURL }) => ({ src: original, width, height, + type: (type === "VIDEO" ? "video" : "image") as any, + sources: + type === "VIDEO" + ? [ + { + src: videoURL, + type: "video/mp4", + }, + ] + : [], })), [images] ); @@ -51,41 +62,44 @@ export default function PotentialAlbumsAssets() { const handleClick = (idx: number) => setIndex(idx); const handleSelect = (_idx: number, asset: IAsset) => { - const isPresent =selectedIds.includes(asset.id) - if(isPresent){ - updateContext({selectedIds:selectedIds.filter((id)=>id!==asset.id)}) + const isPresent = selectedIds.includes(asset.id); + if (isPresent) { + updateContext({ + selectedIds: selectedIds.filter((id) => id !== asset.id), + }); + } else { + updateContext({ selectedIds: [...selectedIds, asset.id] }); } - else{ - updateContext({selectedIds:[...selectedIds,asset.id]}) - } - }; useEffect(() => { if (startDate) fetchAssets(); }, [startDate]); - if (loading) return ( -
- -

Loading...

-
- ) + if (loading) + return ( +
+ +

Loading...

+
+ ); - if (!startDate) return ( -
- -

Please select a date

-

- When you select a date from the left, you will see all the orphan assets captured on that date -

-
- ) + if (!startDate) + return ( +
+ +

Please select a date

+

+ When you select a date from the left, you will see all the orphan + assets captured on that date +

+
+ ); return ( <> = 0} index={index} close={() => setIndex(-1)} @@ -96,7 +110,6 @@ export default function PotentialAlbumsAssets() { onClick={handleClick} enableImageSelection={true} onSelect={handleSelect} - /> diff --git a/src/config/routes.ts b/src/config/routes.ts index 746073e..abf6e37 100644 --- a/src/config/routes.ts +++ b/src/config/routes.ts @@ -25,4 +25,5 @@ export const LIST_MISSING_LOCATION_DATES_PATH = BASE_API_ENDPOINT + "/assets/mis export const LIST_MISSING_LOCATION_ASSETS_PATH = BASE_API_ENDPOINT + "/assets/missing-location-assets"; export const ASSET_THUMBNAIL_PATH = (id: string) => BASE_PROXY_ENDPOINT + "/asset/thumbnail/" + id; -export const ASSET_PREVIEW_PATH = (id: string) => BASE_PROXY_ENDPOINT + "/asset/thumbnail/" + id + "?size=preview"; \ No newline at end of file +export const ASSET_PREVIEW_PATH = (id: string) => BASE_PROXY_ENDPOINT + "/asset/thumbnail/" + id + "?size=preview"; +export const ASSET_VIDEO_PATH = (id: string) => BASE_PROXY_ENDPOINT + "/asset/video/" + id; \ No newline at end of file diff --git a/src/helpers/asset.helper.ts b/src/helpers/asset.helper.ts index f505826..a8236fa 100644 --- a/src/helpers/asset.helper.ts +++ b/src/helpers/asset.helper.ts @@ -1,4 +1,4 @@ -import { ASSET_PREVIEW_PATH, ASSET_THUMBNAIL_PATH, PERSON_THUBNAIL_PATH } from "@/config/routes" +import { ASSET_PREVIEW_PATH, ASSET_THUMBNAIL_PATH, ASSET_VIDEO_PATH, PERSON_THUBNAIL_PATH } from "@/config/routes" import { IPerson } from "@/types/person" import { parseDate } from "./date.helper" import { IAsset } from "@/types/asset" @@ -9,5 +9,6 @@ export const cleanUpAsset = (asset: IAsset): IAsset => { ...asset, url: ASSET_THUMBNAIL_PATH(asset.id), previewUrl: ASSET_PREVIEW_PATH(asset.id), + videoURL: ASSET_VIDEO_PATH(asset.id), } } \ No newline at end of file diff --git a/src/pages/api/albums/potential-albums-assets.ts b/src/pages/api/albums/potential-albums-assets.ts index e5c753e..7ad94e9 100644 --- a/src/pages/api/albums/potential-albums-assets.ts +++ b/src/pages/api/albums/potential-albums-assets.ts @@ -38,6 +38,7 @@ const SELECT_ORPHAN_PHOTOS = (date: string, ownerId: string) => aaa."albumsId" IS NULL AND a."ownerId" = '${ownerId}' AND a."localDateTime"::date = '${date}' + AND a."type" = 'VIDEO' `); export default async function handler( diff --git a/src/pages/api/immich-proxy/asset/video/[id].ts b/src/pages/api/immich-proxy/asset/video/[id].ts new file mode 100644 index 0000000..66c475d --- /dev/null +++ b/src/pages/api/immich-proxy/asset/video/[id].ts @@ -0,0 +1,48 @@ +// pages/api/proxy.js + +import { ENV } from '@/config/environment'; +import { NextApiRequest, NextApiResponse } from 'next' + +export const config = { + api: { + bodyParser: false, + }, +} + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + if (req.method !== 'GET') { + return res.status(405).json({ message: 'Method Not Allowed' }) + } + + const { id } = req.query; + const targetUrl = `${ENV.IMMICH_URL}/api/assets/${id}/original`; + + try { + // Forward the request to the target API + const response = await fetch(targetUrl, { + method: 'GET', + headers: { + 'Content-Type': 'application/octet-stream', + 'x-api-key': ENV.IMMICH_API_KEY, + }, + }) + + if (!response.ok) { + console.error('HTTP error:', response) + throw new Error(`HTTP error! status: ${response.status}`) + } + + // Get the image data from the response + const imageBuffer = await response.arrayBuffer() + + // Set the appropriate headers for the image response + res.setHeader('Content-Type', response.headers.get('Content-Type') || 'image/png') + res.setHeader('Content-Length', imageBuffer.byteLength) + + // Send the image data + res.send(Buffer.from(imageBuffer)) + } catch (error) { + console.error('Error:', error) + res.status(500).json({ message: 'Internal Server Error' }) + } +} \ No newline at end of file diff --git a/src/types/asset.d.ts b/src/types/asset.d.ts index d9ee2f5..1466a50 100644 --- a/src/types/asset.d.ts +++ b/src/types/asset.d.ts @@ -18,6 +18,7 @@ export interface IAsset { exifImageHeight: number; url: string; previewUrl: string; + videoURL?: string; } export interface IAssetThumbhash {