diff --git a/Grayjay.Desktop.Web/src/components/PlaylistDetailView/index.tsx b/Grayjay.Desktop.Web/src/components/PlaylistDetailView/index.tsx index d50d16e1..366ccfdc 100644 --- a/Grayjay.Desktop.Web/src/components/PlaylistDetailView/index.tsx +++ b/Grayjay.Desktop.Web/src/components/PlaylistDetailView/index.tsx @@ -1,4 +1,4 @@ -import { type Component, createMemo, createSignal, batch, Show } from 'solid-js'; +import { type Component, createMemo, createSignal, createEffect, batch, Show } from 'solid-js'; import LoaderContainer from '../basics/loaders/LoaderContainer'; import NavigationBar from '../topbars/NavigationBar'; import styles from './index.module.css'; @@ -19,6 +19,7 @@ import iconDownload from '../../assets/icons/icon24_download.svg'; import iconTrash from '../../assets/icons/icon_trash.svg'; import UIOverlay from '../../state/UIOverlay'; import { IPlatformVideo } from '../../backend/models/content/IPlatformVideo'; +import { HistoryBackend } from '../../backend/HistoryBackend'; import PlaylistItemView from '../PlaylistItemView'; import { Menus } from '../../Menus'; import { useNavigate } from '@solidjs/router'; @@ -99,6 +100,23 @@ const PlaylistDetailView: Component = (props) => { }); } + const [positionMap$, setPositionMap] = createSignal>({}); + createEffect(async () => { + const videos = props.videos; + if (!videos || videos.length === 0) return; + const entries = await Promise.all( + videos.map(async (v) => { + try { + const pos = await HistoryBackend.getHistoricalPosition(v.url); + return [v.url, pos] as [string, number]; + } catch { + return [v.url, 0] as [string, number]; + } + }) + ); + setPositionMap(Object.fromEntries(entries)); + }); + const [filterText$, setFilterText] = createSignal(""); const isEditable$ = createMemo(() => (filterText$()?.length ?? 0) == 0); @@ -194,7 +212,8 @@ const PlaylistDetailView: Component = (props) => { builder={(index, item, containerRef, dragControls) => { const video = createMemo(() => item() as IPlatformVideo | undefined); return ( - { const v = video(); diff --git a/Grayjay.Desktop.Web/src/components/PlaylistItemView/index.tsx b/Grayjay.Desktop.Web/src/components/PlaylistItemView/index.tsx index 2d77b324..9024226e 100644 --- a/Grayjay.Desktop.Web/src/components/PlaylistItemView/index.tsx +++ b/Grayjay.Desktop.Web/src/components/PlaylistItemView/index.tsx @@ -4,7 +4,7 @@ import styles from './index.module.css'; import iconDrag from '../../assets/icons/icon_drag.svg'; import iconClose from '../../assets/icons/icon24_close.svg'; import iconMore from '../../assets/icons/icon_button_more.svg'; -import { proxyImage, toHumanNowDiffString, toHumanNumber } from '../../utility'; +import { getVideoProgressPercentage, proxyImage, toHumanNowDiffString, toHumanNumber } from '../../utility'; import { DateTime } from 'luxon'; import IconButton from '../buttons/IconButton'; @@ -15,6 +15,7 @@ import { focusable } from '../../focusable';import { useFocus } from '../../Focu interface PlaylistItemViewProps { item?: IPlatformVideo; + position?: number; onPlay?: () => void; onRemove?: () => void; onSettings?: (e: HTMLElement) => void; @@ -41,7 +42,17 @@ const PlaylistItemView: Component = (props) => { }> props.onDragStart?.(e, e.target as HTMLElement)} /> - +
+ +
0 && (props.item?.duration ?? 0) > 0 ? getVideoProgressPercentage(props.position!, props.item!.duration!) : 0}%` + }} /> +
{props.item?.name}
diff --git a/Grayjay.Desktop.Web/src/components/content/VideoThumbnailView/index.tsx b/Grayjay.Desktop.Web/src/components/content/VideoThumbnailView/index.tsx index fd89d689..dbf728a3 100644 --- a/Grayjay.Desktop.Web/src/components/content/VideoThumbnailView/index.tsx +++ b/Grayjay.Desktop.Web/src/components/content/VideoThumbnailView/index.tsx @@ -4,7 +4,7 @@ import styles from './index.module.css'; import IconButton from '../../buttons/IconButton'; import more from '../../../assets/icons/more_horiz_FILL0_wght400_GRAD0_opsz24.svg'; import addToQueueIcon from '../../../assets/icons/icon_add_to_queue.svg'; -import { dateFromAny, toHumanNowDiffString, toHumanNumber, toHumanTime } from '../../../utility'; +import { dateFromAny, getVideoProgressPercentage, toHumanNowDiffString, toHumanNumber, toHumanTime } from '../../../utility'; import { DateTime } from 'luxon'; import { useNavigate } from '@solidjs/router'; import StateGlobal from '../../../state/StateGlobal'; @@ -16,6 +16,7 @@ import { focusable } from '../../../focusable';import { useFocus } from '../../. interface VideoProps { video?: IPlatformVideo; + position?: number; onClick: () => void; onSettings?: (element: HTMLDivElement, content: IPlatformVideo) => void; onAddtoQueue?: (element: HTMLDivElement, content: IPlatformVideo) => void; @@ -33,7 +34,8 @@ const VideoThumbnailView: Component = (props) => { }) var progress$ = createMemo(()=>{ let videoAny = props.video as any; - return (videoAny?.metadata?.position && props.video?.duration && props.video.duration > 0) ? (videoAny?.metadata?.position / props.video!.duration) : 0; + const position = props.position ?? videoAny?.metadata?.position; + return (position && props.video?.duration && props.video.duration > 0) ? getVideoProgressPercentage(position, props.video!.duration) / 100 : 0; }) const navigate = useNavigate(); diff --git a/Grayjay.Desktop.Web/src/components/downloads/DownloadedView/index.tsx b/Grayjay.Desktop.Web/src/components/downloads/DownloadedView/index.tsx index 68f3be43..6f570a9c 100644 --- a/Grayjay.Desktop.Web/src/components/downloads/DownloadedView/index.tsx +++ b/Grayjay.Desktop.Web/src/components/downloads/DownloadedView/index.tsx @@ -1,7 +1,8 @@ -import { Component, Show, createMemo, createSignal, onCleanup } from 'solid-js' +import { Component, Show, createMemo, createSignal, createResource, onCleanup } from 'solid-js' import styles from './index.module.css'; -import { getBestThumbnail, positiveOrQ, proxyImage, resolutionOrUnknown, toHumanBitrate, toHumanBytesSize, toHumanBytesSpeed, toHumanNumber, toHumanTime } from '../../../utility'; +import { getBestThumbnail, getVideoProgressPercentage, positiveOrQ, proxyImage, resolutionOrUnknown, toHumanBitrate, toHumanBytesSize, toHumanBytesSpeed, toHumanNumber, toHumanTime } from '../../../utility'; +import { HistoryBackend } from '../../../backend/HistoryBackend'; import StateGlobal from '../../../state/StateGlobal'; import SubscribeButton from '../../buttons/SubscribeButton'; import settings from '../../../assets/icons/icon24_settings.svg'; @@ -55,6 +56,10 @@ const DownloadedView: Component = (props) => { } } + const [position] = createResource(() => props.downloaded?.url, async (url) => { + try { return await HistoryBackend.getHistoricalPosition(url); } catch { return 0; } + }); + let refMoreButton: HTMLDivElement | undefined; return ( @@ -69,6 +74,16 @@ const DownloadedView: Component = (props) => {
{toHumanBytesSize(calcSize())}
+ 0 && (props.downloaded?.videoDetails?.duration ?? 0) > 0}> +
+
{props.downloaded?.name} diff --git a/Grayjay.Desktop.Web/src/pages/Playlists/index.tsx b/Grayjay.Desktop.Web/src/pages/Playlists/index.tsx index 14a8db83..fa969333 100644 --- a/Grayjay.Desktop.Web/src/pages/Playlists/index.tsx +++ b/Grayjay.Desktop.Web/src/pages/Playlists/index.tsx @@ -1,4 +1,4 @@ -import { createResource, type Component, Switch, Match, createSignal, batch, createMemo, Show, onMount } from 'solid-js'; +import { createResource, type Component, Switch, Match, createSignal, batch, createMemo, createEffect, Show, onMount } from 'solid-js'; import NavigationBar from '../../components/topbars/NavigationBar'; import styles from './index.module.css'; import { WatchLaterBackend } from '../../backend/WatchLaterBackend'; @@ -29,6 +29,7 @@ import EmptyContentView from '../../components/EmptyContentView'; import { Menus } from '../../Menus'; import StateWebsocket from '../../state/StateWebsocket'; import { createResourceDefault } from '../../utility'; +import { HistoryBackend } from '../../backend/HistoryBackend'; import LoaderGrid from '../../components/basics/loaders/LoaderGrid'; import InputText from '../../components/basics/inputs/InputText'; import Dropdown from '../../components/basics/inputs/Dropdown'; @@ -119,6 +120,23 @@ const PlaylistsPage: Component = () => { }); } + const [watchLaterPositions$, setWatchLaterPositions] = createSignal>({}); + createEffect(async () => { + const videos = video?.watchLater(); + if (!videos || videos.length === 0) return; + const entries = await Promise.all( + videos.map(async (v) => { + try { + const pos = await HistoryBackend.getHistoricalPosition(v.url); + return [v.url, pos] as [string, number]; + } catch { + return [v.url, 0] as [string, number]; + } + }) + ); + setWatchLaterPositions(Object.fromEntries(entries)); + }); + const [filterText, setFilterText] = createSignal(""); const [sortBy, setSortBy] = createSignal(0); @@ -231,6 +249,7 @@ const PlaylistsPage: Component = () => { }} builder={(index, item) => { const queue = video?.watchLater(); if (!queue) {