From 793449f486548cdb36be598831d841e291e28857 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sun, 15 Jun 2025 23:25:49 +0300 Subject: [PATCH] fixed sub issues --- .../VideoPlayer/SubtitleManager.tsx | 494 ++++++++++++------ .../VideoPlayer/VideoControlsBar.tsx | 4 +- .../VideoPlayer/VideoPlayer-styles.ts | 21 +- src/components/VideoPlayer/VideoPlayer.tsx | 4 +- src/index.css | 11 + 5 files changed, 361 insertions(+), 173 deletions(-) diff --git a/src/components/VideoPlayer/SubtitleManager.tsx b/src/components/VideoPlayer/SubtitleManager.tsx index 01720a3..21fa586 100644 --- a/src/components/VideoPlayer/SubtitleManager.tsx +++ b/src/components/VideoPlayer/SubtitleManager.tsx @@ -1,4 +1,10 @@ -import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import React, { + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from "react"; import { QortalGetMetadata, QortalMetadata, @@ -10,6 +16,7 @@ import { Button, ButtonBase, Card, + CircularProgress, Dialog, DialogActions, DialogContent, @@ -18,9 +25,11 @@ import { Fade, IconButton, Popover, + Skeleton, Tab, Tabs, Typography, + useTheme, } from "@mui/material"; import CheckIcon from "@mui/icons-material/Check"; import ArrowForwardIosIcon from "@mui/icons-material/ArrowForwardIos"; @@ -43,6 +52,12 @@ import { ResourceToPublish } from "../../types/qortalRequests/types"; import { useListReturn } from "../../hooks/useListData"; import { usePublish } from "../../hooks/usePublish"; import { Spacer } from "../../common/Spacer"; +import { + dismissToast, + showError, + showLoading, + showSuccess, +} from "../../utils/toast"; interface SubtitleManagerProps { qortalMetadata: QortalGetMetadata; close: () => void; @@ -90,28 +105,36 @@ const SubtitleManagerComponent = ({ const [mode, setMode] = useState(1); const [isOpenPublish, setIsOpenPublish] = useState(false); const { lists, identifierOperations, auth } = useGlobal(); + const [isLoading, setIsLoading] = useState(false); + const [showAll, setShowAll] = useState(false); const { fetchResources } = useResources(); // const [subtitles, setSubtitles] = useState([]) const subtitles = useListReturn( `subs-${qortalMetadata?.service}-${qortalMetadata?.name}-${qortalMetadata?.identifier}` ); - console.log('subtitles222', subtitles) - const mySubtitles = useMemo(()=> { - if(!auth?.name)return [] - return subtitles?.filter((sub)=> sub.name === auth?.name) - }, [subtitles, auth?.name]) + console.log("subtitles222", subtitles); + const mySubtitles = useMemo(() => { + if (!auth?.name) return []; + return subtitles?.filter((sub) => sub.name === auth?.name); + }, [subtitles, auth?.name]); console.log("subtitles222", subtitles); const getPublishedSubtitles = useCallback(async () => { try { + setIsLoading(true); const videoId = `${qortalMetadata?.service}-${qortalMetadata?.name}-${qortalMetadata?.identifier}`; console.log("videoId", videoId); const postIdSearch = await identifierOperations.buildSearchPrefix( ENTITY_SUBTITLE, videoId ); + let name: string | undefined = qortalMetadata?.name; + if (showAll) { + name = undefined; + } const searchParams = { service: SERVICE_SUBTITLE, identifier: postIdSearch, + name, limit: 0, }; const res = await lists.fetchResources( @@ -123,8 +146,10 @@ const SubtitleManagerComponent = ({ console.log("resres2", res); } catch (error) { console.error(error); + } finally { + setIsLoading(false); } - }, []); + }, [showAll]); useEffect(() => { if ( @@ -215,6 +240,8 @@ const SubtitleManagerComponent = ({ close(); }; + const theme = useTheme(); + return ( <> - {mode === 1 && ( - - )} + + {isLoading && } + {!isLoading && subtitles?.length === 0 && ( + + No subtitles + + )} + + {mode === 1 && !isLoading && subtitles?.length > 0 && ( + + )} + + + + {/* {[ 'Ambient mode', @@ -414,6 +504,23 @@ const PublisherSubtitles = ({ }: PublisherSubtitlesProps) => { return ( <> + onSelect(null)} + sx={{ + px: 2, + py: 1, + "&:hover": { + backgroundColor: "rgba(255, 255, 255, 0.1)", + }, + width: "100%", + justifyContent: "space-between", + }} + > + Off + {!currentSubTrack ? : } + + {subtitles?.map((sub) => { return ( void; + publishHandler: (subs: Subtitle[]) => Promise; isOpen: boolean; setIsOpen: (val: boolean) => void; - mySubtitles: QortalGetMetadata[] + mySubtitles: QortalGetMetadata[]; } const PublishSubtitles = ({ publishHandler, isOpen, setIsOpen, - mySubtitles + mySubtitles, }: PublishSubtitlesProps) => { const [language, setLanguage] = useState(null); const [subtitles, setSubtitles] = useState([]); - const {lists} = useGlobal() + const [isPublishing, setIsPublishing] = useState(false); + const { lists } = useGlobal(); + const theme = useTheme(); const onDrop = useCallback(async (acceptedFiles: File[]) => { const newSubtitles: Subtitle[] = []; for (const file of acceptedFiles) { @@ -495,6 +604,7 @@ const PublishSubtitles = ({ const handleClose = () => { setIsOpen(false); + setSubtitles([]); }; const [value, setValue] = useState(0); @@ -503,15 +613,39 @@ const PublishSubtitles = ({ setValue(newValue); }; - const onDelete = useCallback(async (sub: QortalGetMetadata)=> { + const onDelete = useCallback(async (sub: QortalGetMetadata) => { + let loadId; try { - await lists.deleteResource([ - sub - ]) + setIsPublishing(true); + loadId = showLoading("Deleting subtitle..."); + await lists.deleteResource([sub]); + showSuccess("Deleted subtitle"); } catch (error) { - + showError(error instanceof Error ? error.message : "Unable to delete"); + } finally { + setIsPublishing(false); + dismissToast(loadId); } - },[]) + }, []); + + const publishHandlerLocal = async (subtitles: Subtitle[]) => { + let loadId; + try { + setIsPublishing(true); + loadId = showLoading("Publishing subtitles..."); + await publishHandler(subtitles); + showSuccess("Subtitles published"); + setSubtitles([]); + } catch (error) { + showError(error instanceof Error ? error.message : "Unable to publish"); + } finally { + dismissToast(loadId); + setIsPublishing(false); + } + }; + + const disableButton = + !!subtitles.find((sub) => !sub?.language) || isPublishing; return ( @@ -539,7 +677,26 @@ const PublishSubtitles = ({ > - + {value === 0 && ( - - - - - {subtitles?.map((sub, i) => { - return ( - + + + + {subtitles?.map((sub, i) => { + return ( + - {sub.filename} - - - - onChangeValue("language", val, i) - } - /> - - - - - - ); - })} - + {sub.filename} + + + + onChangeValue("language", val, i) + } + /> + + + + + + ); + })} + )} {value === 1 && ( - - - {mySubtitles?.map((sub, i) => { - return ( - - - - ); - })} - + + {mySubtitles?.map((sub, i) => { + return ( + + + + ); + })} + )} - + {value === 0 && ( + + )} ); @@ -672,7 +830,7 @@ interface SubProps { currentSubtrack: null | string; } const Subtitle = ({ sub, onSelect, currentSubtrack }: SubProps) => { - const { resource, isLoading } = usePublish(2, "JSON", sub); + const { resource, isLoading, error } = usePublish(2, "JSON", sub); console.log("resource", resource); const isSelected = currentSubtrack === resource?.data?.language; return ( @@ -688,8 +846,13 @@ const Subtitle = ({ sub, onSelect, currentSubtrack }: SubProps) => { justifyContent: "space-between", }} > - {resource?.data?.language} - {isSelected ? : } + {isLoading && } + {!isLoading && !error && ( + <> + {resource?.data?.language} + {isSelected ? : } + + )} ); }; @@ -699,46 +862,49 @@ interface MySubtitleProps { onDelete: (subtitle: QortalGetMetadata) => void; } const MySubtitle = ({ sub, onDelete }: MySubtitleProps) => { - const { resource, isLoading } = usePublish(2, "JSON", sub); + const { resource, isLoading, error } = usePublish(2, "JSON", sub); console.log("resource", resource); return ( - - - {resource?.data?.filename} - - - {resource?.data?.language} - - - - - - + + + {resource?.data?.filename} + + + + {resource?.data?.language} + + + + + + ); }; diff --git a/src/components/VideoPlayer/VideoControlsBar.tsx b/src/components/VideoPlayer/VideoControlsBar.tsx index 261c19b..81baa82 100644 --- a/src/components/VideoPlayer/VideoControlsBar.tsx +++ b/src/components/VideoPlayer/VideoControlsBar.tsx @@ -13,7 +13,7 @@ import { VolumeControl, } from "./VideoControls"; import { Ref } from "react"; - +import SubtitlesIcon from '@mui/icons-material/Subtitles'; interface VideoControlsBarProps { canPlay: boolean isScreenSmall: boolean @@ -99,7 +99,7 @@ export const VideoControlsBar = ({subtitleBtnRef, showControls, playbackRate, in - sub + diff --git a/src/components/VideoPlayer/VideoPlayer-styles.ts b/src/components/VideoPlayer/VideoPlayer-styles.ts index c3a54c4..903148d 100644 --- a/src/components/VideoPlayer/VideoPlayer-styles.ts +++ b/src/components/VideoPlayer/VideoPlayer-styles.ts @@ -23,14 +23,23 @@ export const VideoElement = styled("video")(({ theme }) => ({ right: 0, left: 0, background: "rgb(33, 33, 33)", - "&:focus": { outline: "none" }, + + "&:focus": { + outline: "none !important", + boxShadow: "none !important", + }, + "&:focus-visible": { + outline: "none !important", + boxShadow: "none !important", + }, "&::-webkit-media-controls": { - display:"none !important" -}, -"&:fullscreen": { - paddingBottom: '50px' -} + display: "none !important", + }, + "&:fullscreen": { + paddingBottom: '50px', + }, })); + //1075 x 604 export const ControlsContainer = styled(Box)` width: 100%; diff --git a/src/components/VideoPlayer/VideoPlayer.tsx b/src/components/VideoPlayer/VideoPlayer.tsx index da7c4ba..0106433 100644 --- a/src/components/VideoPlayer/VideoPlayer.tsx +++ b/src/components/VideoPlayer/VideoPlayer.tsx @@ -591,6 +591,8 @@ export const VideoPlayer = ({ if (!playerRef.current && ref.current) { playerRef.current = videojs(ref.current, options, () => { setIsPlayerInitialized(true); + ref.current.tabIndex = -1; // Prevents focus entirely + ref.current.style.outline = 'none'; // Backup playerRef.current?.poster(""); playerRef.current?.playbackRate(playbackRate); playerRef.current?.volume(volume); @@ -701,7 +703,7 @@ export const VideoPlayer = ({ />