mirror of
https://github.com/Qortal/qapp-core.git
synced 2025-07-11 04:01:21 +00:00
912 lines
23 KiB
TypeScript
912 lines
23 KiB
TypeScript
import React, {
|
|
useCallback,
|
|
useEffect,
|
|
useMemo,
|
|
useRef,
|
|
useState,
|
|
} from "react";
|
|
import {
|
|
QortalGetMetadata,
|
|
QortalMetadata,
|
|
Service,
|
|
} from "../../types/interfaces/resources";
|
|
import {
|
|
alpha,
|
|
Box,
|
|
Button,
|
|
ButtonBase,
|
|
Card,
|
|
CircularProgress,
|
|
Dialog,
|
|
DialogActions,
|
|
DialogContent,
|
|
DialogTitle,
|
|
Divider,
|
|
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";
|
|
import ArrowBackIosIcon from "@mui/icons-material/ArrowBackIos";
|
|
import ModeEditIcon from "@mui/icons-material/ModeEdit";
|
|
import CloseIcon from "@mui/icons-material/Close";
|
|
import { useListStore } from "../../state/lists";
|
|
import { Resource, useResources } from "../../hooks/useResources";
|
|
import { useGlobal } from "../../context/GlobalProvider";
|
|
import { ENTITY_SUBTITLE, SERVICE_SUBTITLE } from "./video-player-constants";
|
|
import ISO6391, { LanguageCode } from "iso-639-1";
|
|
import LanguageSelect from "./LanguageSelect";
|
|
import {
|
|
useDropzone,
|
|
DropzoneRootProps,
|
|
DropzoneInputProps,
|
|
} from "react-dropzone";
|
|
import { fileToBase64, objectToBase64 } from "../../utils/base64";
|
|
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;
|
|
open: boolean;
|
|
onSelect: (subtitle: SubtitlePublishedData) => void;
|
|
subtitleBtnRef: any;
|
|
currentSubTrack: null | string;
|
|
}
|
|
export interface Subtitle {
|
|
language: string | null;
|
|
base64: string;
|
|
type: string;
|
|
filename: string;
|
|
size: number;
|
|
}
|
|
export interface SubtitlePublishedData {
|
|
language: string | null;
|
|
subtitleData: string;
|
|
type: string;
|
|
filename: string;
|
|
size: number;
|
|
}
|
|
|
|
export const languageOptions = ISO6391.getAllCodes().map((code) => ({
|
|
code,
|
|
name: ISO6391.getName(code),
|
|
nativeName: ISO6391.getNativeName(code),
|
|
}));
|
|
|
|
function a11yProps(index: number) {
|
|
return {
|
|
id: `subtitle-tab-${index}`,
|
|
"aria-controls": `subtitle-tabpanel-${index}`,
|
|
};
|
|
}
|
|
|
|
const SubtitleManagerComponent = ({
|
|
qortalMetadata,
|
|
open,
|
|
close,
|
|
onSelect,
|
|
subtitleBtnRef,
|
|
currentSubTrack,
|
|
}: SubtitleManagerProps) => {
|
|
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 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(
|
|
searchParams,
|
|
`subs-${videoId}`,
|
|
"BASE64"
|
|
);
|
|
lists.addList(`subs-${videoId}`, res || []);
|
|
console.log("resres2", res);
|
|
} catch (error) {
|
|
console.error(error);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
}, [showAll]);
|
|
|
|
useEffect(() => {
|
|
if (
|
|
!qortalMetadata?.identifier ||
|
|
!qortalMetadata?.name ||
|
|
!qortalMetadata?.service
|
|
)
|
|
return;
|
|
|
|
getPublishedSubtitles();
|
|
}, [
|
|
qortalMetadata?.identifier,
|
|
qortalMetadata?.service,
|
|
qortalMetadata?.name,
|
|
getPublishedSubtitles,
|
|
]);
|
|
|
|
const handleClose = () => {
|
|
close();
|
|
setMode(1);
|
|
// setTitle("");
|
|
// setDescription("");
|
|
// setHasMetadata(false);
|
|
};
|
|
|
|
const publishHandler = async (subtitles: Subtitle[]) => {
|
|
try {
|
|
const videoId = `${qortalMetadata?.service}-${qortalMetadata?.name}-${qortalMetadata?.identifier}`;
|
|
|
|
const identifier = await identifierOperations.buildIdentifier(
|
|
ENTITY_SUBTITLE,
|
|
videoId
|
|
);
|
|
const name = auth?.name;
|
|
console.log("identifier2", identifier);
|
|
if (!name) return;
|
|
const resources: ResourceToPublish[] = [];
|
|
const tempResources: { qortalMetadata: QortalMetadata; data: any }[] = [];
|
|
for (const sub of subtitles) {
|
|
const data = {
|
|
subtitleData: sub.base64,
|
|
language: sub.language,
|
|
filename: sub.filename,
|
|
type: sub.type,
|
|
};
|
|
|
|
const base64Data = await objectToBase64(data);
|
|
const resource = {
|
|
name,
|
|
identifier,
|
|
service: SERVICE_SUBTITLE,
|
|
base64: base64Data,
|
|
filename: sub.filename,
|
|
title: sub.language || undefined,
|
|
};
|
|
resources.push(resource);
|
|
tempResources.push({
|
|
qortalMetadata: {
|
|
identifier,
|
|
service: SERVICE_SUBTITLE,
|
|
name,
|
|
size: 100,
|
|
created: Date.now(),
|
|
},
|
|
data: data,
|
|
});
|
|
}
|
|
console.log("resources", resources);
|
|
|
|
await qortalRequest({
|
|
action: "PUBLISH_MULTIPLE_QDN_RESOURCES",
|
|
resources,
|
|
});
|
|
|
|
lists.addNewResources(
|
|
`subs-${qortalMetadata?.service}-${qortalMetadata?.name}-${qortalMetadata?.identifier}`,
|
|
tempResources
|
|
);
|
|
} catch (error) {}
|
|
};
|
|
const onBack = () => {
|
|
if (mode === 1) close();
|
|
};
|
|
|
|
const onSelectHandler = (sub: SubtitlePublishedData) => {
|
|
console.log("onSelectHandler");
|
|
onSelect(sub);
|
|
close();
|
|
};
|
|
|
|
const theme = useTheme();
|
|
|
|
return (
|
|
<>
|
|
<Popover
|
|
open={!!open}
|
|
anchorEl={subtitleBtnRef.current}
|
|
onClose={handleClose}
|
|
slots={{
|
|
transition: Fade,
|
|
}}
|
|
slotProps={{
|
|
transition: {
|
|
timeout: 200,
|
|
},
|
|
paper: {
|
|
sx: {
|
|
bgcolor: alpha("#181818", 0.98),
|
|
color: "white",
|
|
opacity: 0.9,
|
|
borderRadius: 2,
|
|
boxShadow: 5,
|
|
p: 1,
|
|
minWidth: 225,
|
|
height: 300,
|
|
overflow: "hidden",
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
},
|
|
},
|
|
}}
|
|
anchorOrigin={{
|
|
vertical: "top",
|
|
horizontal: "center",
|
|
}}
|
|
transformOrigin={{
|
|
vertical: "bottom",
|
|
horizontal: "center",
|
|
}}
|
|
>
|
|
<Box
|
|
sx={{
|
|
padding: "5px 0px 10px 0px",
|
|
display: "flex",
|
|
gap: "10px",
|
|
width: "100%",
|
|
}}
|
|
>
|
|
<ButtonBase onClick={onBack}>
|
|
<ArrowBackIosIcon
|
|
sx={{
|
|
fontSize: "1.15em",
|
|
}}
|
|
/>
|
|
</ButtonBase>
|
|
<ButtonBase>
|
|
<Typography
|
|
onClick={onBack}
|
|
sx={{
|
|
fontSize: "0.85rem",
|
|
}}
|
|
>
|
|
Subtitles
|
|
</Typography>
|
|
</ButtonBase>
|
|
<ButtonBase
|
|
sx={{
|
|
marginLeft: "auto",
|
|
}}
|
|
onClick={() => setIsOpenPublish(true)}
|
|
>
|
|
<ModeEditIcon
|
|
sx={{
|
|
fontSize: "1.15rem",
|
|
}}
|
|
/>
|
|
</ButtonBase>
|
|
</Box>
|
|
<Divider />
|
|
<Box
|
|
sx={{
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
flexGrow: 1,
|
|
overflow: "auto",
|
|
"::-webkit-scrollbar-track": {
|
|
backgroundColor: "transparent",
|
|
},
|
|
|
|
"::-webkit-scrollbar": {
|
|
width: "16px",
|
|
height: "10px",
|
|
},
|
|
|
|
"::-webkit-scrollbar-thumb": {
|
|
backgroundColor: theme.palette.primary.main,
|
|
borderRadius: "8px",
|
|
backgroundClip: "content-box",
|
|
border: "4px solid transparent",
|
|
transition: "0.3s background-color",
|
|
},
|
|
|
|
"::-webkit-scrollbar-thumb:hover": {
|
|
backgroundColor: theme.palette.primary.dark,
|
|
},
|
|
}}
|
|
>
|
|
{isLoading && <CircularProgress />}
|
|
{!isLoading && subtitles?.length === 0 && (
|
|
<Typography
|
|
sx={{
|
|
fontSize: "1rem",
|
|
width: "100%",
|
|
textAlign: "center",
|
|
marginTop: "20px",
|
|
}}
|
|
>
|
|
No subtitles
|
|
</Typography>
|
|
)}
|
|
|
|
{mode === 1 && !isLoading && subtitles?.length > 0 && (
|
|
<PublisherSubtitles
|
|
subtitles={subtitles}
|
|
publisherName={qortalMetadata.name}
|
|
setMode={setMode}
|
|
onSelect={onSelectHandler}
|
|
onBack={onBack}
|
|
currentSubTrack={currentSubTrack}
|
|
/>
|
|
)}
|
|
</Box>
|
|
<Box
|
|
sx={{
|
|
display: "flex",
|
|
width: "100%",
|
|
justifyContent: "center",
|
|
}}
|
|
>
|
|
<Button
|
|
variant="contained"
|
|
size="small"
|
|
disabled={showAll}
|
|
onClick={() => setShowAll(true)}
|
|
>
|
|
Load all
|
|
</Button>
|
|
</Box>
|
|
{/* <Box>
|
|
{[
|
|
'Ambient mode',
|
|
'Annotations',
|
|
'Subtitles/CC',
|
|
'Sleep timer',
|
|
'Playback speed',
|
|
'Quality',
|
|
].map((label) => (
|
|
<Typography
|
|
key={label}
|
|
sx={{
|
|
px: 2,
|
|
py: 1,
|
|
'&:hover': {
|
|
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
|
cursor: 'pointer',
|
|
},
|
|
}}
|
|
>
|
|
{label}
|
|
</Typography>
|
|
))}
|
|
</Box> */}
|
|
</Popover>
|
|
|
|
<PublishSubtitles
|
|
isOpen={isOpenPublish}
|
|
setIsOpen={setIsOpenPublish}
|
|
publishHandler={publishHandler}
|
|
mySubtitles={mySubtitles}
|
|
/>
|
|
</>
|
|
// <Dialog
|
|
// open={!!open}
|
|
// fullWidth={true}
|
|
// maxWidth={"md"}
|
|
// sx={{
|
|
// zIndex: 999990,
|
|
// }}
|
|
// slotProps={{
|
|
// paper: {
|
|
// elevation: 0,
|
|
// },
|
|
// }}
|
|
// >
|
|
// <DialogTitle>Subtitles</DialogTitle>
|
|
// <IconButton
|
|
// aria-label="close"
|
|
// onClick={handleClose}
|
|
// sx={(theme) => ({
|
|
// position: "absolute",
|
|
// right: 8,
|
|
// top: 8,
|
|
// })}
|
|
// >
|
|
// <CloseIcon />
|
|
// </IconButton>
|
|
// <Button onClick={() => setMode(5)}>New subtitles</Button>
|
|
// {mode === 1 && (
|
|
// <PublisherSubtitles
|
|
// subtitles={subtitles}
|
|
// publisherName={qortalMetadata.name}
|
|
// setMode={setMode}
|
|
// onSelect={onSelect}
|
|
// />
|
|
// )}
|
|
// {mode === 5 && <PublishSubtitles publishHandler={publishHandler} />}
|
|
// {/* {mode === 2 && (
|
|
// <CommunitySubtitles
|
|
// link={open?.link}
|
|
// name={open?.name}
|
|
// mode={mode}
|
|
// setMode={setMode}
|
|
// username={username}
|
|
// category={open?.category}
|
|
// rootName={open?.rootName}
|
|
// />
|
|
// )}
|
|
|
|
// {mode === 4 && (
|
|
// <MySubtitles
|
|
// link={open?.link}
|
|
// name={open?.name}
|
|
// mode={mode}
|
|
// setMode={setMode}
|
|
// username={username}
|
|
// title={title}
|
|
// description={description}
|
|
// setDescription={setDescription}
|
|
// setTitle={setTitle}
|
|
// />
|
|
// )} */}
|
|
// </Dialog>
|
|
);
|
|
};
|
|
|
|
interface PublisherSubtitlesProps {
|
|
publisherName: string;
|
|
subtitles: any[];
|
|
setMode: (val: number) => void;
|
|
onSelect: (subtitle: any) => void;
|
|
onBack: () => void;
|
|
currentSubTrack: string | null;
|
|
}
|
|
|
|
const PublisherSubtitles = ({
|
|
publisherName,
|
|
subtitles,
|
|
setMode,
|
|
onSelect,
|
|
onBack,
|
|
currentSubTrack,
|
|
}: PublisherSubtitlesProps) => {
|
|
return (
|
|
<>
|
|
<ButtonBase
|
|
disabled={!currentSubTrack}
|
|
onClick={() => onSelect(null)}
|
|
sx={{
|
|
px: 2,
|
|
py: 1,
|
|
"&:hover": {
|
|
backgroundColor: "rgba(255, 255, 255, 0.1)",
|
|
},
|
|
width: "100%",
|
|
justifyContent: "space-between",
|
|
}}
|
|
>
|
|
<Typography>Off</Typography>
|
|
{!currentSubTrack ? <CheckIcon /> : <ArrowForwardIosIcon />}
|
|
</ButtonBase>
|
|
|
|
{subtitles?.map((sub) => {
|
|
return (
|
|
<Subtitle
|
|
currentSubtrack={currentSubTrack}
|
|
onSelect={onSelect}
|
|
sub={sub}
|
|
key={`${sub?.qortalMetadata?.service}-${sub?.qortalMetadata?.name}-${sub?.qortalMetadata?.identifier}`}
|
|
/>
|
|
);
|
|
})}
|
|
</>
|
|
);
|
|
};
|
|
|
|
interface PublishSubtitlesProps {
|
|
publishHandler: (subs: Subtitle[]) => Promise<void>;
|
|
isOpen: boolean;
|
|
setIsOpen: (val: boolean) => void;
|
|
mySubtitles: QortalGetMetadata[];
|
|
}
|
|
|
|
const PublishSubtitles = ({
|
|
publishHandler,
|
|
isOpen,
|
|
setIsOpen,
|
|
mySubtitles,
|
|
}: PublishSubtitlesProps) => {
|
|
const [language, setLanguage] = useState<null | string>(null);
|
|
const [subtitles, setSubtitles] = useState<Subtitle[]>([]);
|
|
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) {
|
|
try {
|
|
const newSubtitle = {
|
|
base64: await fileToBase64(file),
|
|
language: null,
|
|
type: file.type,
|
|
filename: file.name,
|
|
size: file.size,
|
|
};
|
|
newSubtitles.push(newSubtitle);
|
|
} catch (error) {
|
|
console.error("Failed to parse audio file:", error);
|
|
}
|
|
}
|
|
setSubtitles((prev) => [...newSubtitles, ...prev]);
|
|
}, []);
|
|
const {
|
|
getRootProps,
|
|
getInputProps,
|
|
}: {
|
|
getRootProps: () => DropzoneRootProps;
|
|
getInputProps: () => DropzoneInputProps;
|
|
isDragActive: boolean;
|
|
} = useDropzone({
|
|
onDrop,
|
|
accept: {
|
|
"application/x-subrip": [".srt"], // SRT subtitles
|
|
"text/vtt": [".vtt"], // WebVTT subtitles
|
|
},
|
|
multiple: true,
|
|
maxSize: 2 * 1024 * 1024, // 2MB
|
|
});
|
|
|
|
const onChangeValue = (field: string, data: any, index: number) => {
|
|
const sub = subtitles[index];
|
|
if (!sub) return;
|
|
|
|
const copySub = { ...sub, [field]: data };
|
|
|
|
setSubtitles((prev) => {
|
|
const copyPrev = [...prev];
|
|
copyPrev[index] = copySub;
|
|
return copyPrev;
|
|
});
|
|
};
|
|
console.log("subtitles", subtitles);
|
|
|
|
const handleClose = () => {
|
|
setIsOpen(false);
|
|
setSubtitles([]);
|
|
};
|
|
|
|
const [value, setValue] = useState(0);
|
|
|
|
const handleChange = (event: React.SyntheticEvent, newValue: number) => {
|
|
setValue(newValue);
|
|
};
|
|
|
|
const onDelete = useCallback(async (sub: QortalGetMetadata) => {
|
|
let loadId;
|
|
try {
|
|
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 (
|
|
<Dialog
|
|
open={isOpen}
|
|
fullWidth={true}
|
|
maxWidth={"md"}
|
|
sx={{
|
|
zIndex: 999990,
|
|
}}
|
|
slotProps={{
|
|
paper: {
|
|
elevation: 0,
|
|
sx: {
|
|
height: "600px",
|
|
maxHeight: "100vh",
|
|
},
|
|
},
|
|
}}
|
|
>
|
|
<DialogTitle>My Subtitles</DialogTitle>
|
|
<IconButton
|
|
aria-label="close"
|
|
onClick={handleClose}
|
|
sx={(theme) => ({
|
|
position: "absolute",
|
|
right: 8,
|
|
top: 8,
|
|
})}
|
|
>
|
|
<CloseIcon />
|
|
</IconButton>
|
|
<DialogContent
|
|
sx={{
|
|
"::-webkit-scrollbar": {
|
|
width: "16px",
|
|
height: "10px",
|
|
},
|
|
|
|
"::-webkit-scrollbar-thumb": {
|
|
backgroundColor: theme.palette.primary.main,
|
|
borderRadius: "8px",
|
|
backgroundClip: "content-box",
|
|
border: "4px solid transparent",
|
|
transition: "0.3s background-color",
|
|
},
|
|
|
|
"::-webkit-scrollbar-thumb:hover": {
|
|
backgroundColor: theme.palette.primary.dark,
|
|
},
|
|
}}
|
|
>
|
|
<Box sx={{ width: "100%" }}>
|
|
<Box sx={{ borderBottom: 1, borderColor: "divider" }}>
|
|
<Tabs
|
|
value={value}
|
|
onChange={handleChange}
|
|
aria-label="basic tabs example"
|
|
>
|
|
<Tab label="New" {...a11yProps(0)} />
|
|
<Tab label="Existing" {...a11yProps(1)} />
|
|
</Tabs>
|
|
</Box>
|
|
</Box>
|
|
<Spacer height="25px" />
|
|
{value === 0 && (
|
|
<Box
|
|
sx={{
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
gap: "20px",
|
|
width: "100%",
|
|
alignItems: "center",
|
|
}}
|
|
>
|
|
<Box {...getRootProps()}>
|
|
<Button
|
|
sx={{
|
|
display: "flex",
|
|
gap: "10px",
|
|
}}
|
|
variant="contained"
|
|
>
|
|
<input {...getInputProps()} />
|
|
Import subtitles
|
|
</Button>
|
|
</Box>
|
|
{subtitles?.map((sub, i) => {
|
|
return (
|
|
<Card
|
|
sx={{
|
|
padding: "10px",
|
|
width: "500px",
|
|
maxWidth: "100%",
|
|
}}
|
|
>
|
|
<Typography
|
|
sx={{
|
|
fontSize: "1rem",
|
|
}}
|
|
>
|
|
{sub.filename}
|
|
</Typography>
|
|
<Spacer height="10px" />
|
|
<LanguageSelect
|
|
value={sub.language}
|
|
onChange={(val: string | null) =>
|
|
onChangeValue("language", val, i)
|
|
}
|
|
/>
|
|
<Spacer height="10px" />
|
|
<Box
|
|
sx={{
|
|
justifyContent: "flex-end",
|
|
width: "100%",
|
|
display: "flex",
|
|
}}
|
|
>
|
|
<Button
|
|
onClick={() => {
|
|
setSubtitles((prev) => {
|
|
const newSubtitles = [...prev];
|
|
newSubtitles.splice(i, 1); // Remove 1 item at index i
|
|
return newSubtitles;
|
|
});
|
|
}}
|
|
variant="contained"
|
|
size="small"
|
|
color="secondary"
|
|
>
|
|
remove
|
|
</Button>
|
|
</Box>
|
|
</Card>
|
|
);
|
|
})}
|
|
</Box>
|
|
)}
|
|
{value === 1 && (
|
|
<Box
|
|
sx={{
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
gap: "20px",
|
|
width: "100%",
|
|
alignItems: "center",
|
|
}}
|
|
>
|
|
{mySubtitles?.map((sub, i) => {
|
|
return (
|
|
<Card
|
|
sx={{
|
|
padding: "10px",
|
|
width: "500px",
|
|
maxWidth: "100%",
|
|
}}
|
|
>
|
|
<MySubtitle onDelete={onDelete} sub={sub} />
|
|
</Card>
|
|
);
|
|
})}
|
|
</Box>
|
|
)}
|
|
</DialogContent>
|
|
<DialogActions>
|
|
{value === 0 && (
|
|
<Button
|
|
onClick={() => publishHandlerLocal(subtitles)}
|
|
disabled={disableButton}
|
|
variant="contained"
|
|
>
|
|
Publish
|
|
</Button>
|
|
)}
|
|
</DialogActions>
|
|
</Dialog>
|
|
);
|
|
};
|
|
|
|
interface SubProps {
|
|
sub: QortalGetMetadata;
|
|
onSelect: (subtitle: Subtitle) => void;
|
|
currentSubtrack: null | string;
|
|
}
|
|
const Subtitle = ({ sub, onSelect, currentSubtrack }: SubProps) => {
|
|
const { resource, isLoading, error } = usePublish(2, "JSON", sub);
|
|
console.log("resource", resource);
|
|
const isSelected = currentSubtrack === resource?.data?.language;
|
|
return (
|
|
<ButtonBase
|
|
onClick={() => onSelect(isSelected ? null : resource?.data)}
|
|
sx={{
|
|
px: 2,
|
|
py: 1,
|
|
"&:hover": {
|
|
backgroundColor: "rgba(255, 255, 255, 0.1)",
|
|
},
|
|
width: "100%",
|
|
justifyContent: "space-between",
|
|
}}
|
|
>
|
|
{isLoading && <Skeleton variant="text" sx={{ fontSize: "1.25rem", width: '100%' }} />}
|
|
{!isLoading && !error && (
|
|
<>
|
|
<Typography>{resource?.data?.language}</Typography>
|
|
{isSelected ? <CheckIcon /> : <ArrowForwardIosIcon />}
|
|
</>
|
|
)}
|
|
</ButtonBase>
|
|
);
|
|
};
|
|
|
|
interface MySubtitleProps {
|
|
sub: QortalGetMetadata;
|
|
onDelete: (subtitle: QortalGetMetadata) => void;
|
|
}
|
|
const MySubtitle = ({ sub, onDelete }: MySubtitleProps) => {
|
|
const { resource, isLoading, error } = usePublish(2, "JSON", sub);
|
|
console.log("resource", resource);
|
|
return (
|
|
<Card
|
|
sx={{
|
|
padding: "10px",
|
|
width: "500px",
|
|
maxWidth: "100%",
|
|
}}
|
|
>
|
|
<Typography
|
|
sx={{
|
|
fontSize: "1rem",
|
|
}}
|
|
>
|
|
{resource?.data?.filename}
|
|
</Typography>
|
|
<Spacer height="10px" />
|
|
<Typography
|
|
sx={{
|
|
fontSize: "1rem",
|
|
}}
|
|
>
|
|
{resource?.data?.language}
|
|
</Typography>
|
|
<Spacer height="10px" />
|
|
<Box
|
|
sx={{
|
|
justifyContent: "flex-end",
|
|
width: "100%",
|
|
display: "flex",
|
|
}}
|
|
>
|
|
<Button
|
|
onClick={() => onDelete(sub)}
|
|
variant="contained"
|
|
size="small"
|
|
color="secondary"
|
|
>
|
|
delete
|
|
</Button>
|
|
</Box>
|
|
</Card>
|
|
);
|
|
};
|
|
|
|
export const SubtitleManager = React.memo(SubtitleManagerComponent);
|