mirror of
https://github.com/Qortal/qapp-core.git
synced 2025-06-22 20:21:22 +00:00
added qortal multi publish status
This commit is contained in:
parent
979c6d4265
commit
378c80cf3c
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "qapp-core",
|
"name": "qapp-core",
|
||||||
"version": "1.0.31",
|
"version": "1.0.32",
|
||||||
"description": "Qortal's core React library with global state, UI components, and utilities",
|
"description": "Qortal's core React library with global state, UI components, and utilities",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"module": "dist/index.mjs",
|
"module": "dist/index.mjs",
|
||||||
@ -49,7 +49,6 @@
|
|||||||
"@emotion/styled": "^11.14.0",
|
"@emotion/styled": "^11.14.0",
|
||||||
"@mui/icons-material": "^7.0.1",
|
"@mui/icons-material": "^7.0.1",
|
||||||
"@mui/material": "^7.0.1",
|
"@mui/material": "^7.0.1",
|
||||||
"mediainfo.js": "^0.3.5",
|
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
"react-router-dom": "^7.6.2"
|
"react-router-dom": "^7.6.2"
|
||||||
|
@ -13,157 +13,307 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
Box,
|
Box,
|
||||||
LinearProgress,
|
LinearProgress,
|
||||||
Stack
|
Stack,
|
||||||
|
DialogActions,
|
||||||
|
Button
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { PublishStatus, useMultiplePublishStore, usePublishStatusStore } from "../../state/multiplePublish";
|
import { PublishStatus, useMultiplePublishStore, usePublishStatusStore } from "../../state/multiplePublish";
|
||||||
import { ResourceToPublish } from "../../types/qortalRequests/types";
|
import { ResourceToPublish } from "../../types/qortalRequests/types";
|
||||||
|
import { QortalGetMetadata } from "../../types/interfaces/resources";
|
||||||
|
import ErrorIcon from '@mui/icons-material/Error';
|
||||||
|
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
|
||||||
|
|
||||||
|
export interface MultiplePublishError {
|
||||||
|
error: {
|
||||||
|
unsuccessfulPublishes: any[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const MultiPublishDialogComponent = () => {
|
||||||
|
const {
|
||||||
const MultiPublishDialogComponent = () => {
|
resources,
|
||||||
const { resources, isPublishing } = useMultiplePublishStore((state) => ({
|
isPublishing,
|
||||||
|
failedResources,
|
||||||
|
reset,
|
||||||
|
reject,
|
||||||
|
error: publishError,
|
||||||
|
isLoading,
|
||||||
|
setIsLoading,
|
||||||
|
setError,
|
||||||
|
setFailedPublishResources,
|
||||||
|
complete
|
||||||
|
} = useMultiplePublishStore((state) => ({
|
||||||
resources: state.resources,
|
resources: state.resources,
|
||||||
isPublishing: state.isPublishing
|
isPublishing: state.isPublishing,
|
||||||
}));
|
failedResources: state.failedResources,
|
||||||
const { publishStatus, setPublishStatusByKey, getPublishStatusByKey } = usePublishStatusStore();
|
reset: state.reset,
|
||||||
|
reject: state.reject,
|
||||||
|
error: state.error,
|
||||||
|
isLoading: state.isLoading,
|
||||||
|
setIsLoading: state.setIsLoading,
|
||||||
|
setError: state.setError,
|
||||||
|
setFailedPublishResources: state.setFailedPublishResources,
|
||||||
|
complete: state.complete
|
||||||
|
}));
|
||||||
|
|
||||||
|
const { publishStatus, setPublishStatusByKey } = usePublishStatusStore();
|
||||||
|
|
||||||
|
const resourcesToPublish = useMemo(() => {
|
||||||
|
return resources.filter((item) =>
|
||||||
|
failedResources.some((f) =>
|
||||||
|
f?.name === item?.name && f?.identifier === item?.identifier && f?.service === item?.service
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}, [resources, failedResources]);
|
||||||
|
|
||||||
|
const publishMultipleResources = useCallback(async () => {
|
||||||
|
const timeout = resources.length * 1200000;
|
||||||
|
|
||||||
|
try {
|
||||||
|
resourcesToPublish.forEach((item) => {
|
||||||
|
const key = `${item?.service}-${item?.name}-${item?.identifier}`;
|
||||||
|
setPublishStatusByKey(key, {
|
||||||
|
error: undefined,
|
||||||
|
chunks: 0,
|
||||||
|
totalChunks: 0
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
setIsLoading(true);
|
||||||
|
setError(null);
|
||||||
|
setFailedPublishResources([]);
|
||||||
|
|
||||||
|
const result = await qortalRequestWithTimeout(
|
||||||
|
{ action: 'PUBLISH_MULTIPLE_QDN_RESOURCES', resources: resourcesToPublish },
|
||||||
|
timeout
|
||||||
|
);
|
||||||
|
|
||||||
|
complete(result);
|
||||||
|
} catch (error: any) {
|
||||||
|
const unPublished = error?.error?.unsuccessfulPublishes;
|
||||||
|
const failedPublishes: QortalGetMetadata[] = [];
|
||||||
|
|
||||||
|
if (unPublished && Array.isArray(unPublished)) {
|
||||||
|
unPublished.forEach((item) => {
|
||||||
|
const key = `${item?.service}-${item?.name}-${item?.identifier}`;
|
||||||
|
setPublishStatusByKey(key, {
|
||||||
|
error: { reason: item?.reason }
|
||||||
|
});
|
||||||
|
|
||||||
|
failedPublishes.push({
|
||||||
|
name: item?.name,
|
||||||
|
service: item?.service,
|
||||||
|
identifier: item?.identifier
|
||||||
|
});
|
||||||
|
});
|
||||||
|
setFailedPublishResources(failedPublishes);
|
||||||
|
} else {
|
||||||
|
setError(error instanceof Error ? error.message : 'Error during publish');
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, [resourcesToPublish, resources, setPublishStatusByKey, setIsLoading, setError, setFailedPublishResources, complete]);
|
||||||
|
|
||||||
|
const handleNavigation = useCallback((event: any) => {
|
||||||
|
if (event.data?.action !== 'PUBLISH_STATUS') return;
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
function handleNavigation(event: any) {
|
|
||||||
if (event.data?.action === 'PUBLISH_STATUS') {
|
|
||||||
const data = event.data;
|
const data = event.data;
|
||||||
console.log('datadata', data)
|
|
||||||
// Validate required structure before continuing
|
|
||||||
if (
|
if (
|
||||||
!data.publishLocation ||
|
!data.publishLocation ||
|
||||||
typeof data.publishLocation.name !== 'string' ||
|
typeof data.publishLocation?.name !== 'string' ||
|
||||||
typeof data.publishLocation.identifier !== 'string' ||
|
typeof data.publishLocation?.service !== 'string'
|
||||||
typeof data.publishLocation.service !== 'string'
|
|
||||||
) {
|
) {
|
||||||
console.warn('Invalid PUBLISH_STATUS data, skipping:', data);
|
console.warn('Invalid PUBLISH_STATUS data, skipping:', data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { publishLocation, chunks, totalChunks } = data;
|
const {
|
||||||
|
publishLocation,
|
||||||
|
chunks,
|
||||||
|
totalChunks,
|
||||||
|
retry,
|
||||||
|
filename,
|
||||||
|
processed
|
||||||
|
} = data;
|
||||||
|
|
||||||
const key = `${publishLocation?.service}-${publishLocation?.name}-${publishLocation?.identifier}`;
|
const key = `${publishLocation?.service}-${publishLocation?.name}-${publishLocation?.identifier}`;
|
||||||
|
|
||||||
try {
|
const update: any = {
|
||||||
const dataToBeSent: any = {};
|
|
||||||
if (chunks !== undefined && chunks !== null) {
|
|
||||||
dataToBeSent.chunks = chunks;
|
|
||||||
}
|
|
||||||
if (totalChunks !== undefined && totalChunks !== null) {
|
|
||||||
dataToBeSent.totalChunks = totalChunks;
|
|
||||||
}
|
|
||||||
|
|
||||||
setPublishStatusByKey(key, {
|
|
||||||
publishLocation,
|
publishLocation,
|
||||||
...dataToBeSent,
|
processed: processed || false
|
||||||
processed: data?.processed || false
|
};
|
||||||
});
|
if (chunks != null && chunks !== undefined) update.chunks = chunks;
|
||||||
|
if (totalChunks != null && totalChunks !== undefined) update.totalChunks = totalChunks;
|
||||||
|
if (retry != null && retry !== undefined) update.retry = retry;
|
||||||
|
if (filename != null && retry !== undefined) update.filename = filename;
|
||||||
|
|
||||||
|
try {
|
||||||
|
setPublishStatusByKey(key, update);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to set publish status:', err);
|
console.error('Failed to set publish status:', err);
|
||||||
}
|
}
|
||||||
}
|
}, [setPublishStatusByKey]);
|
||||||
}
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
window.addEventListener("message", handleNavigation);
|
window.addEventListener("message", handleNavigation);
|
||||||
|
return () => window.removeEventListener("message", handleNavigation);
|
||||||
|
}, [handleNavigation]);
|
||||||
|
|
||||||
|
if (!isPublishing) return null;
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener("message", handleNavigation);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
if(!isPublishing) return null
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={isPublishing} fullWidth maxWidth="sm">
|
<Dialog
|
||||||
|
open={isPublishing}
|
||||||
|
fullWidth
|
||||||
|
maxWidth="sm"
|
||||||
|
sx={{ zIndex: 999990 }}
|
||||||
|
slotProps={{ paper: { elevation: 0 } }}
|
||||||
|
>
|
||||||
<DialogTitle>Publishing Status</DialogTitle>
|
<DialogTitle>Publishing Status</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
|
{publishError && (
|
||||||
<Stack spacing={3}>
|
<Stack spacing={3}>
|
||||||
{resources.map((publish: ResourceToPublish) => {
|
<Box mt={2} sx={{ display: 'flex', gap: '5px', alignItems: 'center' }}>
|
||||||
|
<ErrorIcon color="error" />
|
||||||
|
<Typography variant="body2">{publishError}</Typography>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!publishError && (
|
||||||
|
<Stack spacing={3}>
|
||||||
|
{resources.map((publish) => {
|
||||||
const key = `${publish?.service}-${publish?.name}-${publish?.identifier}`;
|
const key = `${publish?.service}-${publish?.name}-${publish?.identifier}`;
|
||||||
const individualPublishStatus = publishStatus[key] || null
|
const individualPublishStatus = publishStatus[key] || null;
|
||||||
return (
|
return (
|
||||||
<IndividualResourceComponent key={key} publishKey={key} publish={publish} publishStatus={individualPublishStatus} />
|
<IndividualResourceComponent
|
||||||
|
key={key}
|
||||||
|
publishKey={key}
|
||||||
|
publish={publish}
|
||||||
|
publishStatus={individualPublishStatus}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
)}
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button disabled={isLoading} color="error" variant="contained" onClick={() => {
|
||||||
|
reject(new Error('Canceled Publish'));
|
||||||
|
reset();
|
||||||
|
}}>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
{failedResources?.length > 0 && (
|
||||||
|
<Button
|
||||||
|
disabled={isLoading || resourcesToPublish.length === 0}
|
||||||
|
color="success"
|
||||||
|
variant="contained"
|
||||||
|
onClick={publishMultipleResources}
|
||||||
|
>
|
||||||
|
Retry
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
interface IndividualResourceComponentProps {
|
interface IndividualResourceComponentProps {
|
||||||
publish: ResourceToPublish
|
publish: ResourceToPublish
|
||||||
publishStatus: PublishStatus
|
publishStatus: PublishStatus
|
||||||
publishKey: string
|
publishKey: string
|
||||||
}
|
}
|
||||||
const ESTIMATED_PROCESSING_MS = 5 * 60 * 1000; // 5 minutes
|
|
||||||
|
|
||||||
const IndividualResourceComponent = ({publish, publishKey, publishStatus}: IndividualResourceComponentProps)=> {
|
const IndividualResourceComponent = ({ publish, publishKey, publishStatus }: IndividualResourceComponentProps) => {
|
||||||
const [now, setNow] = useState(Date.now());
|
const [now, setNow] = useState(Date.now());
|
||||||
console.log('key500',publishKey, publishStatus)
|
|
||||||
|
|
||||||
const chunkPercent = useMemo(()=> {
|
|
||||||
if(!publishStatus?.chunks || !publishStatus?.totalChunks) return 0
|
|
||||||
return (publishStatus?.chunks / publishStatus?.totalChunks) * 100
|
|
||||||
}, [publishStatus])
|
|
||||||
console.log('chunkPercent', chunkPercent)
|
|
||||||
const chunkDone = useMemo(()=> {
|
|
||||||
if(!publishStatus?.chunks || !publishStatus?.totalChunks) return false
|
|
||||||
return publishStatus?.chunks === publishStatus?.totalChunks
|
|
||||||
}, [publishStatus])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if(!chunkDone) return
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
setNow(Date.now());
|
|
||||||
}, 1000);
|
|
||||||
return () => clearInterval(interval);
|
|
||||||
}, [chunkDone]);
|
|
||||||
|
|
||||||
const [processingStart, setProcessingStart] = useState<number | undefined>();
|
const [processingStart, setProcessingStart] = useState<number | undefined>();
|
||||||
|
|
||||||
useEffect(() => {
|
const chunkPercent = useMemo(() => {
|
||||||
|
if (!publishStatus?.chunks || !publishStatus?.totalChunks) return 0;
|
||||||
|
return (publishStatus.chunks / publishStatus.totalChunks) * 100;
|
||||||
|
}, [publishStatus?.chunks, publishStatus?.totalChunks]);
|
||||||
|
|
||||||
|
const chunkDone = useMemo(() => {
|
||||||
|
return (
|
||||||
|
publishStatus?.chunks > 0 &&
|
||||||
|
publishStatus?.totalChunks > 0 &&
|
||||||
|
publishStatus?.chunks === publishStatus?.totalChunks
|
||||||
|
);
|
||||||
|
}, [publishStatus?.chunks, publishStatus?.totalChunks]);
|
||||||
|
|
||||||
|
// Start processing timer once chunking completes
|
||||||
|
useEffect(() => {
|
||||||
if (chunkDone && !processingStart) {
|
if (chunkDone && !processingStart) {
|
||||||
setProcessingStart(Date.now());
|
setProcessingStart(Date.now());
|
||||||
}
|
}
|
||||||
}, [chunkDone, processingStart]);
|
}, [chunkDone, processingStart]);
|
||||||
|
|
||||||
|
// Keep time ticking for progress simulation
|
||||||
|
useEffect(() => {
|
||||||
|
if (!chunkDone) return;
|
||||||
|
const interval = setInterval(() => setNow(Date.now()), 1000);
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, [chunkDone]);
|
||||||
|
|
||||||
const processingPercent = useMemo(() => {
|
const processingPercent = useMemo(() => {
|
||||||
if (!chunkDone || !processingStart || !publishStatus?.totalChunks || !now) return 0;
|
if (publishStatus?.error || !chunkDone || !processingStart || !publishStatus?.totalChunks || !now) return 0;
|
||||||
|
|
||||||
const totalMB = publishStatus.totalChunks * 5;
|
const totalMB = publishStatus.totalChunks * 5; // assume 5MB per chunk
|
||||||
const estimatedProcessingMs = (300_000 / 2048) * totalMB;
|
const estimatedProcessingMs = (300_000 / 2048) * totalMB; // 5min per 2GB scaled
|
||||||
|
|
||||||
const elapsed = now - processingStart;
|
const elapsed = now - processingStart;
|
||||||
if(!elapsed || elapsed < 0) return 0
|
if (elapsed <= 0) return 0;
|
||||||
return Math.min((elapsed / estimatedProcessingMs) * 100, 100);
|
return Math.min((elapsed / estimatedProcessingMs) * 100, 100);
|
||||||
}, [chunkDone, processingStart, now, publishStatus?.totalChunks]);
|
}, [chunkDone, processingStart, now, publishStatus?.totalChunks, publishStatus?.error]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box p={1} border={1} borderColor="divider" borderRadius={2}>
|
<Box p={1} border={1} borderColor="divider" borderRadius={2}>
|
||||||
<Typography variant="subtitle1" fontWeight="bold">
|
<Typography variant="subtitle1" fontWeight="bold">
|
||||||
{publishKey}
|
{publish?.filename || publishStatus?.filename || publishKey}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Box mt={2}>
|
<Box mt={2}>
|
||||||
<Typography variant="body2" gutterBottom>
|
<Typography variant="body2" gutterBottom>
|
||||||
File Chunk { (!publishStatus?.chunks || !publishStatus?.totalChunks) ? '' : publishStatus?.chunks}/{publishStatus?.totalChunks} ({(chunkPercent).toFixed(0)}%)
|
File Chunk {publishStatus?.chunks || 0}/{publishStatus?.totalChunks || 0} ({chunkPercent.toFixed(0)}%)
|
||||||
</Typography>
|
</Typography>
|
||||||
<LinearProgress variant="determinate" value={chunkPercent} />
|
<LinearProgress variant="determinate" value={chunkPercent} />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box mt={2}>
|
<Box mt={2}>
|
||||||
<Typography variant="body2" gutterBottom>
|
<Typography variant="body2" gutterBottom>
|
||||||
File Processing ({(!processingPercent || !processingStart) ? '0' : processingPercent.toFixed(0)}%)
|
File Processing ({processingStart ? processingPercent.toFixed(0) : '0'}%)
|
||||||
</Typography>
|
</Typography>
|
||||||
<LinearProgress variant="determinate" value={processingPercent} />
|
<LinearProgress variant="determinate" value={processingPercent} />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{publishStatus?.processed && (
|
||||||
|
<Box mt={2} display="flex" gap={1} alignItems="center">
|
||||||
|
<CheckCircleIcon color="success" />
|
||||||
|
<Typography variant="body2">Published successfully</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)}
|
||||||
}
|
|
||||||
|
{publishStatus?.retry && !publishStatus?.error && !publishStatus?.processed && (
|
||||||
|
<Box mt={2} display="flex" gap={1} alignItems="center">
|
||||||
|
<ErrorIcon color="error" />
|
||||||
|
<Typography variant="body2">Publish failed. Attempting retry...</Typography>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{publishStatus?.error && !publishStatus?.processed && (
|
||||||
|
<Box mt={2} display="flex" gap={1} alignItems="center">
|
||||||
|
<ErrorIcon color="error" />
|
||||||
|
<Typography variant="body2">
|
||||||
|
Publish failed. {publishStatus?.error?.reason || 'Unknown error'}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export const MultiPublishDialog = React.memo(MultiPublishDialogComponent);
|
export const MultiPublishDialog = React.memo(MultiPublishDialogComponent);
|
||||||
|
@ -119,17 +119,14 @@ const SubtitleManagerComponent = ({
|
|||||||
const subtitles = useListReturn(
|
const subtitles = useListReturn(
|
||||||
`subs-${qortalMetadata?.service}-${qortalMetadata?.name}-${qortalMetadata?.identifier}`
|
`subs-${qortalMetadata?.service}-${qortalMetadata?.name}-${qortalMetadata?.identifier}`
|
||||||
);
|
);
|
||||||
console.log("subtitles222", subtitles);
|
|
||||||
const mySubtitles = useMemo(() => {
|
const mySubtitles = useMemo(() => {
|
||||||
if (!auth?.name) return [];
|
if (!auth?.name) return [];
|
||||||
return subtitles?.filter((sub) => sub.name === auth?.name);
|
return subtitles?.filter((sub) => sub.name === auth?.name);
|
||||||
}, [subtitles, auth?.name]);
|
}, [subtitles, auth?.name]);
|
||||||
console.log("subtitles222", subtitles);
|
|
||||||
const getPublishedSubtitles = useCallback(async () => {
|
const getPublishedSubtitles = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const videoId = `${qortalMetadata?.service}-${qortalMetadata?.name}-${qortalMetadata?.identifier}`;
|
const videoId = `${qortalMetadata?.service}-${qortalMetadata?.name}-${qortalMetadata?.identifier}`;
|
||||||
console.log("videoId", videoId);
|
|
||||||
const postIdSearch = await identifierOperations.buildLooseSearchPrefix(
|
const postIdSearch = await identifierOperations.buildLooseSearchPrefix(
|
||||||
ENTITY_SUBTITLE,
|
ENTITY_SUBTITLE,
|
||||||
videoId
|
videoId
|
||||||
@ -149,7 +146,6 @@ const SubtitleManagerComponent = ({
|
|||||||
searchParams
|
searchParams
|
||||||
);
|
);
|
||||||
lists.addList(`subs-${videoId}`, res?.filter((item)=> !!item?.metadata?.title) || []);
|
lists.addList(`subs-${videoId}`, res?.filter((item)=> !!item?.metadata?.title) || []);
|
||||||
console.log("resres2", res);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
} finally {
|
} finally {
|
||||||
@ -174,7 +170,6 @@ const SubtitleManagerComponent = ({
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const ref = useRef<any>(null)
|
const ref = useRef<any>(null)
|
||||||
console.log('isOpen', open)
|
|
||||||
|
|
||||||
useEffect(()=> {
|
useEffect(()=> {
|
||||||
if(open){
|
if(open){
|
||||||
@ -184,7 +179,6 @@ const SubtitleManagerComponent = ({
|
|||||||
|
|
||||||
const handleBlur = (e: React.FocusEvent) => {
|
const handleBlur = (e: React.FocusEvent) => {
|
||||||
if (!e.currentTarget.contains(e.relatedTarget) && !isOpenPublish) {
|
if (!e.currentTarget.contains(e.relatedTarget) && !isOpenPublish) {
|
||||||
console.log('handleBlur')
|
|
||||||
close();
|
close();
|
||||||
setIsOpenPublish(false)
|
setIsOpenPublish(false)
|
||||||
}
|
}
|
||||||
@ -235,7 +229,6 @@ const SubtitleManagerComponent = ({
|
|||||||
data: data,
|
data: data,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
console.log("resources", resources);
|
|
||||||
|
|
||||||
await qortalRequest({
|
await qortalRequest({
|
||||||
action: "PUBLISH_MULTIPLE_QDN_RESOURCES",
|
action: "PUBLISH_MULTIPLE_QDN_RESOURCES",
|
||||||
@ -253,7 +246,6 @@ const SubtitleManagerComponent = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onSelectHandler = (sub: SubtitlePublishedData) => {
|
const onSelectHandler = (sub: SubtitlePublishedData) => {
|
||||||
console.log("onSelectHandler");
|
|
||||||
onSelect(sub);
|
onSelect(sub);
|
||||||
close();
|
close();
|
||||||
};
|
};
|
||||||
@ -608,7 +600,6 @@ const PublishSubtitles = ({
|
|||||||
return copyPrev;
|
return copyPrev;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
console.log("subtitles", subtitles);
|
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
@ -843,7 +834,6 @@ const Subtitle = ({ sub, onSelect, currentSubtrack }: SubProps) => {
|
|||||||
const [selectedToDownload, setSelectedToDownload] = useState<null | QortalGetMetadata>(null)
|
const [selectedToDownload, setSelectedToDownload] = useState<null | QortalGetMetadata>(null)
|
||||||
const [isReady, setIsReady] = useState(false)
|
const [isReady, setIsReady] = useState(false)
|
||||||
const { resource, isLoading, error, refetch } = usePublish(2, "JSON", sub, true);
|
const { resource, isLoading, error, refetch } = usePublish(2, "JSON", sub, true);
|
||||||
console.log("resource", resource, isLoading);
|
|
||||||
const isSelected = currentSubtrack === resource?.data?.language;
|
const isSelected = currentSubtrack === resource?.data?.language;
|
||||||
const [isGettingStatus, setIsGettingStatus] = useState(true)
|
const [isGettingStatus, setIsGettingStatus] = useState(true)
|
||||||
// useEffect(()=> {
|
// useEffect(()=> {
|
||||||
@ -852,7 +842,6 @@ const Subtitle = ({ sub, onSelect, currentSubtrack }: SubProps) => {
|
|||||||
// onSelect(resource?.data)
|
// onSelect(resource?.data)
|
||||||
// }
|
// }
|
||||||
// }, [isSelected, resource?.data])
|
// }, [isSelected, resource?.data])
|
||||||
console.log('isReady', resource?.data)
|
|
||||||
const getStatus = useCallback(async (service: Service, name: string, identifier: string)=> {
|
const getStatus = useCallback(async (service: Service, name: string, identifier: string)=> {
|
||||||
try {
|
try {
|
||||||
if(subtitlesStatus[`${service}-${name}-${identifier}`]){
|
if(subtitlesStatus[`${service}-${name}-${identifier}`]){
|
||||||
@ -890,7 +879,6 @@ const Subtitle = ({ sub, onSelect, currentSubtrack }: SubProps) => {
|
|||||||
getStatus(sub?.service, sub?.name, sub?.identifier)
|
getStatus(sub?.service, sub?.name, sub?.identifier)
|
||||||
}
|
}
|
||||||
}, [sub?.identifier, sub?.name, sub?.service])
|
}, [sub?.identifier, sub?.name, sub?.service])
|
||||||
console.log('tester', isReady,isLoading,error,resource?.data)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ButtonBase
|
<ButtonBase
|
||||||
|
@ -92,7 +92,6 @@ export const ProgressSlider = ({ progress, duration, playerRef }: any) => {
|
|||||||
const x = e.clientX - rect.left;
|
const x = e.clientX - rect.left;
|
||||||
const percent = x / rect.width;
|
const percent = x / rect.width;
|
||||||
const time = Math.min(Math.max(0, percent * duration), duration);
|
const time = Math.min(Math.max(0, percent * duration), duration);
|
||||||
console.log("hello100");
|
|
||||||
setHoverX(e.clientX);
|
setHoverX(e.clientX);
|
||||||
|
|
||||||
setShowDuration(time);
|
setShowDuration(time);
|
||||||
@ -129,7 +128,6 @@ export const ProgressSlider = ({ progress, duration, playerRef }: any) => {
|
|||||||
console.log("thumbnailUrl", thumbnailUrl, hoverX);
|
console.log("thumbnailUrl", thumbnailUrl, hoverX);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("duration", duration);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@ -445,7 +443,6 @@ interface PlayBackMenuProps {
|
|||||||
export const PlayBackMenu = ({close, onSelect, isOpen, playbackRate}: PlayBackMenuProps)=> {
|
export const PlayBackMenu = ({close, onSelect, isOpen, playbackRate}: PlayBackMenuProps)=> {
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
const ref = useRef<any>(null)
|
const ref = useRef<any>(null)
|
||||||
console.log('isOpen', isOpen)
|
|
||||||
|
|
||||||
useEffect(()=> {
|
useEffect(()=> {
|
||||||
if(isOpen){
|
if(isOpen){
|
||||||
|
@ -47,7 +47,6 @@ export async function srtBase64ToVttBlobUrl(
|
|||||||
|
|
||||||
// Step 2: Create a Blob from the Uint8Array with correct MIME type
|
// Step 2: Create a Blob from the Uint8Array with correct MIME type
|
||||||
const srtBlob = new Blob([bytes], { type: "application/x-subrip" });
|
const srtBlob = new Blob([bytes], { type: "application/x-subrip" });
|
||||||
console.log("srtBlob", srtBlob);
|
|
||||||
// Step 3: Use convert() with the Blob
|
// Step 3: Use convert() with the Blob
|
||||||
const vttBlobUrl: string = await convert(srtBlob);
|
const vttBlobUrl: string = await convert(srtBlob);
|
||||||
return vttBlobUrl;
|
return vttBlobUrl;
|
||||||
@ -93,14 +92,7 @@ const videoStyles = {
|
|||||||
video: {},
|
video: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
async function loadMediaInfo(wasmPath = "/MediaInfoModule.wasm") {
|
|
||||||
const mediaInfoModule = await import("mediainfo.js");
|
|
||||||
return await mediaInfoModule.default({
|
|
||||||
format: "JSON",
|
|
||||||
full: true,
|
|
||||||
locateFile: () => wasmPath,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getVideoMimeTypeFromUrl(
|
async function getVideoMimeTypeFromUrl(
|
||||||
qortalVideoResource: any
|
qortalVideoResource: any
|
||||||
@ -114,71 +106,6 @@ async function getVideoMimeTypeFromUrl(
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// const mediaInfo = await loadMediaInfo();
|
|
||||||
// const chunkCache = new Map<string, Uint8Array>();
|
|
||||||
|
|
||||||
// let fileSize = 0;
|
|
||||||
// try {
|
|
||||||
// const headResp = await fetch(videoUrl, { method: 'HEAD' });
|
|
||||||
// const lengthHeader = headResp.headers.get('Content-Length');
|
|
||||||
// if (!lengthHeader) throw new Error('Missing content length');
|
|
||||||
// fileSize = parseInt(lengthHeader, 10);
|
|
||||||
// } catch (err) {
|
|
||||||
// console.error('Error fetching content length:', err);
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// try {
|
|
||||||
// const rawResult = await mediaInfo.analyzeData(
|
|
||||||
// () => fileSize,
|
|
||||||
// async (chunkSize: number, offset: number): Promise<Uint8Array> => {
|
|
||||||
// const key = `${offset}:${chunkSize}`;
|
|
||||||
// if (chunkCache.has(key)) return chunkCache.get(key)!;
|
|
||||||
|
|
||||||
// const end = Math.min(fileSize - 1, offset + chunkSize - 1);
|
|
||||||
// const resp = await fetch(videoUrl, {
|
|
||||||
// headers: { Range: `bytes=${offset}-${end}` },
|
|
||||||
// });
|
|
||||||
|
|
||||||
// if (!resp.ok || (resp.status !== 206 && fileSize > chunkSize)) {
|
|
||||||
// console.warn(`Range request failed: ${resp.status}`);
|
|
||||||
// return new Uint8Array();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const blob = await resp.blob();
|
|
||||||
// const buffer = new Uint8Array(await blob.arrayBuffer());
|
|
||||||
// chunkCache.set(key, buffer);
|
|
||||||
// return buffer;
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
|
|
||||||
// const result = JSON.parse(rawResult);
|
|
||||||
// const tracks = result?.media?.track;
|
|
||||||
|
|
||||||
// const videoTrack = tracks?.find((t: any) => t['@type'] === 'Video');
|
|
||||||
// const format = videoTrack?.Format?.toLowerCase();
|
|
||||||
|
|
||||||
// switch (format) {
|
|
||||||
// case 'avc':
|
|
||||||
// case 'h264':
|
|
||||||
// case 'mpeg-4':
|
|
||||||
// case 'mp4':
|
|
||||||
// return 'video/mp4';
|
|
||||||
// case 'vp8':
|
|
||||||
// case 'vp9':
|
|
||||||
// return 'video/webm';
|
|
||||||
// case 'hevc':
|
|
||||||
// case 'h265':
|
|
||||||
// return 'video/mp4'; // still usually wrapped in MP4
|
|
||||||
// case 'matroska':
|
|
||||||
// return 'video/webm';
|
|
||||||
// default:
|
|
||||||
// return 'video/mp4'; // fallback
|
|
||||||
// }
|
|
||||||
// } catch (err) {
|
|
||||||
// console.error('Error analyzing media info:', err);
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const VideoPlayer = ({
|
export const VideoPlayer = ({
|
||||||
@ -259,7 +186,6 @@ export const VideoPlayer = ({
|
|||||||
}
|
}
|
||||||
},[location])
|
},[location])
|
||||||
|
|
||||||
console.log('isFullscreen', isFullscreen)
|
|
||||||
const { getProgress } = useProgressStore();
|
const { getProgress } = useProgressStore();
|
||||||
|
|
||||||
const enterFullscreen = useCallback(() => {
|
const enterFullscreen = useCallback(() => {
|
||||||
@ -276,7 +202,6 @@ export const VideoPlayer = ({
|
|||||||
}, [isFullscreen]);
|
}, [isFullscreen]);
|
||||||
|
|
||||||
const toggleFullscreen = useCallback(() => {
|
const toggleFullscreen = useCallback(() => {
|
||||||
console.log('togglefull', isFullscreen)
|
|
||||||
isFullscreen ? exitFullscreen() : enterFullscreen();
|
isFullscreen ? exitFullscreen() : enterFullscreen();
|
||||||
}, [isFullscreen]);
|
}, [isFullscreen]);
|
||||||
|
|
||||||
@ -531,7 +456,6 @@ const videoLocationRef = useRef< null | string>(null)
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
console.log("onSelectSubtitle", subtitle);
|
|
||||||
const player = playerRef.current;
|
const player = playerRef.current;
|
||||||
if (!player || !subtitle.subtitleData || !subtitle.type) return;
|
if (!player || !subtitle.subtitleData || !subtitle.type) return;
|
||||||
|
|
||||||
@ -604,16 +528,13 @@ const videoLocationRef = useRef< null | string>(null)
|
|||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
});
|
||||||
const tracksInfo = playerRef.current?.textTracks();
|
const tracksInfo = playerRef.current?.textTracks();
|
||||||
console.log("tracksInfo", tracksInfo);
|
|
||||||
if (!tracksInfo) return;
|
if (!tracksInfo) return;
|
||||||
|
|
||||||
const tracks = Array.from(
|
const tracks = Array.from(
|
||||||
{ length: (tracksInfo as any).length },
|
{ length: (tracksInfo as any).length },
|
||||||
(_, i) => (tracksInfo as any)[i]
|
(_, i) => (tracksInfo as any)[i]
|
||||||
);
|
);
|
||||||
console.log("tracks", tracks);
|
|
||||||
for (const track of tracks) {
|
for (const track of tracks) {
|
||||||
console.log("track", track);
|
|
||||||
|
|
||||||
if (track.kind === "subtitles") {
|
if (track.kind === "subtitles") {
|
||||||
track.mode = "showing"; // force display
|
track.mode = "showing"; // force display
|
||||||
@ -706,7 +627,6 @@ savedVideoRef.current = video.current;
|
|||||||
{ length: (tracksInfo as any).length },
|
{ length: (tracksInfo as any).length },
|
||||||
(_, i) => (tracksInfo as any)[i]
|
(_, i) => (tracksInfo as any)[i]
|
||||||
);
|
);
|
||||||
console.log("tracks", tracks);
|
|
||||||
for (const track of tracks) {
|
for (const track of tracks) {
|
||||||
|
|
||||||
if (track.kind === 'subtitles' || track.kind === 'captions') {
|
if (track.kind === 'subtitles' || track.kind === 'captions') {
|
||||||
@ -718,10 +638,7 @@ savedVideoRef.current = video.current;
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (activeTrack) {
|
if (activeTrack) {
|
||||||
console.log("Subtitle active:", {
|
|
||||||
label: activeTrack.label,
|
|
||||||
srclang: activeTrack.language || activeTrack.srclang, // srclang for native, language for VTT
|
|
||||||
});
|
|
||||||
setCurrentSubTrack(activeTrack.language || activeTrack.srclang)
|
setCurrentSubTrack(activeTrack.language || activeTrack.srclang)
|
||||||
} else {
|
} else {
|
||||||
setCurrentSubTrack(null)
|
setCurrentSubTrack(null)
|
||||||
@ -748,12 +665,10 @@ savedVideoRef.current = video.current;
|
|||||||
console.error("useEffect start player", error);
|
console.error("useEffect start player", error);
|
||||||
}
|
}
|
||||||
return () => {
|
return () => {
|
||||||
console.log('hello1002')
|
|
||||||
const video = savedVideoRef as any
|
const video = savedVideoRef as any
|
||||||
const videoEl = video?.current!;
|
const videoEl = video?.current!;
|
||||||
const player = playerRef.current;
|
const player = playerRef.current;
|
||||||
|
|
||||||
console.log('videohello', videoEl);
|
|
||||||
const isPlaying = !player?.paused();
|
const isPlaying = !player?.paused();
|
||||||
|
|
||||||
if (videoEl && isPlaying && videoLocationRef.current) {
|
if (videoEl && isPlaying && videoLocationRef.current) {
|
||||||
|
@ -140,7 +140,6 @@ export const useVideoPlayerController = (props: UseVideoControls) => {
|
|||||||
|
|
||||||
const toggleMute = useCallback(() => {
|
const toggleMute = useCallback(() => {
|
||||||
try {
|
try {
|
||||||
console.log('toggleMute')
|
|
||||||
const ref = playerRef as any;
|
const ref = playerRef as any;
|
||||||
if (!ref.current) return;
|
if (!ref.current) return;
|
||||||
|
|
||||||
|
@ -16,8 +16,8 @@ export const GlobalPipPlayer = () => {
|
|||||||
const [playing , setPlaying] = useState(false)
|
const [playing , setPlaying] = useState(false)
|
||||||
const [hasStarted, setHasStarted] = useState(false)
|
const [hasStarted, setHasStarted] = useState(false)
|
||||||
const playerRef = useRef<any>(null);
|
const playerRef = useRef<any>(null);
|
||||||
const {navigate} = useContext(GlobalContext)
|
const context = useContext(GlobalContext)
|
||||||
|
const navigate = context?.navigate
|
||||||
const videoNode = useRef<HTMLVideoElement>(null);
|
const videoNode = useRef<HTMLVideoElement>(null);
|
||||||
const { setProgress } = useProgressStore();
|
const { setProgress } = useProgressStore();
|
||||||
|
|
||||||
@ -26,7 +26,6 @@ export const GlobalPipPlayer = () => {
|
|||||||
if (!player || typeof player?.currentTime !== "function") return;
|
if (!player || typeof player?.currentTime !== "function") return;
|
||||||
|
|
||||||
const currentTime = player.currentTime();
|
const currentTime = player.currentTime();
|
||||||
console.log('videoId', videoId)
|
|
||||||
if (typeof currentTime === "number" && videoId && currentTime > 0.1) {
|
if (typeof currentTime === "number" && videoId && currentTime > 0.1) {
|
||||||
setProgress(videoId, currentTime);
|
setProgress(videoId, currentTime);
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,9 @@ import { base64ToObject, retryTransaction } from "../utils/publish";
|
|||||||
import { useGlobal } from "../context/GlobalProvider";
|
import { useGlobal } from "../context/GlobalProvider";
|
||||||
import { ReturnType } from "../components/ResourceList/ResourceListDisplay";
|
import { ReturnType } from "../components/ResourceList/ResourceListDisplay";
|
||||||
import { useCacheStore } from "../state/cache";
|
import { useCacheStore } from "../state/cache";
|
||||||
import { useMultiplePublishStore } from "../state/multiplePublish";
|
import { useMultiplePublishStore, usePublishStatusStore } from "../state/multiplePublish";
|
||||||
import { ResourceToPublish } from "../types/qortalRequests/types";
|
import { ResourceToPublish } from "../types/qortalRequests/types";
|
||||||
|
import { MultiplePublishError } from "../components/MultiPublish/MultiPublishDialog";
|
||||||
|
|
||||||
interface StoredPublish {
|
interface StoredPublish {
|
||||||
qortalMetadata: QortalMetadata;
|
qortalMetadata: QortalMetadata;
|
||||||
@ -31,7 +32,7 @@ interface StoredPublish {
|
|||||||
}>;
|
}>;
|
||||||
updatePublish: (publish: QortalGetMetadata, data: any) => Promise<void>;
|
updatePublish: (publish: QortalGetMetadata, data: any) => Promise<void>;
|
||||||
deletePublish: (publish: QortalGetMetadata) => Promise<boolean | undefined>;
|
deletePublish: (publish: QortalGetMetadata) => Promise<boolean | undefined>;
|
||||||
publishMultipleResources: (resources: ResourceToPublish[])=> void
|
publishMultipleResources: (resources: ResourceToPublish[])=> Promise<Error | QortalGetMetadata[]>
|
||||||
};
|
};
|
||||||
|
|
||||||
type UsePublishWithoutMetadata = {
|
type UsePublishWithoutMetadata = {
|
||||||
@ -42,7 +43,7 @@ interface StoredPublish {
|
|||||||
}>;
|
}>;
|
||||||
updatePublish: (publish: QortalGetMetadata, data: any) => Promise<void>;
|
updatePublish: (publish: QortalGetMetadata, data: any) => Promise<void>;
|
||||||
deletePublish: (publish: QortalGetMetadata) => Promise<boolean | undefined>;
|
deletePublish: (publish: QortalGetMetadata) => Promise<boolean | undefined>;
|
||||||
publishMultipleResources: (resources: ResourceToPublish[])=> void
|
publishMultipleResources: (resources: ResourceToPublish[])=> Promise<Error | QortalGetMetadata[]>
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -77,7 +78,7 @@ interface StoredPublish {
|
|||||||
const getPublish = usePublishStore(state=> state.getPublish)
|
const getPublish = usePublishStore(state=> state.getPublish)
|
||||||
const setResourceCache = useCacheStore((s) => s.setResourceCache);
|
const setResourceCache = useCacheStore((s) => s.setResourceCache);
|
||||||
const markResourceAsDeleted = useCacheStore((s) => s.markResourceAsDeleted);
|
const markResourceAsDeleted = useCacheStore((s) => s.markResourceAsDeleted);
|
||||||
|
const setPublishStatusByKey = usePublishStatusStore((s)=> s.setPublishStatusByKey)
|
||||||
const setPublishResources = useMultiplePublishStore((state) => state.setPublishResources);
|
const setPublishResources = useMultiplePublishStore((state) => state.setPublishResources);
|
||||||
const resetPublishResources = useMultiplePublishStore((state) => state.reset);
|
const resetPublishResources = useMultiplePublishStore((state) => state.reset);
|
||||||
const [hasResource, setHasResource] = useState<boolean | null>(null);
|
const [hasResource, setHasResource] = useState<boolean | null>(null);
|
||||||
@ -271,19 +272,55 @@ interface StoredPublish {
|
|||||||
|
|
||||||
}, [getStorageKey, setPublish]);
|
}, [getStorageKey, setPublish]);
|
||||||
|
|
||||||
const publishMultipleResources = useCallback(async (resources: ResourceToPublish[]) => {
|
const publishMultipleResources = useCallback(async (resources: ResourceToPublish[]): Promise<Error | QortalGetMetadata[]> => {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
const store = useMultiplePublishStore.getState();
|
||||||
|
store.setPublishResources(resources);
|
||||||
|
store.setIsPublishing(true);
|
||||||
|
store.setCompletionResolver(resolve);
|
||||||
|
store.setRejectionResolver(reject);
|
||||||
try {
|
try {
|
||||||
setPublishResources(resources)
|
store.setIsLoading(true);
|
||||||
|
setPublishResources(resources);
|
||||||
|
store.setError(null)
|
||||||
|
store.setFailedPublishResources([])
|
||||||
const lengthOfResources = resources?.length;
|
const lengthOfResources = resources?.length;
|
||||||
const lengthOfTimeout = lengthOfResources * 1200000; // Time out in QR, Seconds = 20 Minutes
|
const lengthOfTimeout = lengthOfResources * 1200000; // 20 minutes per resource
|
||||||
return await qortalRequestWithTimeout({
|
|
||||||
|
const result = await qortalRequestWithTimeout({
|
||||||
action: "PUBLISH_MULTIPLE_QDN_RESOURCES",
|
action: "PUBLISH_MULTIPLE_QDN_RESOURCES",
|
||||||
resources
|
resources
|
||||||
}, lengthOfTimeout);
|
}, lengthOfTimeout);
|
||||||
} catch (error) {
|
store.complete(result);
|
||||||
|
} catch (error: any) {
|
||||||
|
const unPublished = error?.error?.unsuccessfulPublishes;
|
||||||
|
const failedPublishes: QortalGetMetadata[] = []
|
||||||
|
if (unPublished && Array.isArray(unPublished)) {
|
||||||
|
unPublished.forEach((item) => {
|
||||||
|
const key = `${item?.service}-${item?.name}-${item?.identifier}`;
|
||||||
|
|
||||||
|
setPublishStatusByKey(key, {
|
||||||
|
error: {
|
||||||
|
reason: item.reason
|
||||||
}
|
}
|
||||||
}, [getStorageKey, setPublish]);
|
});
|
||||||
|
failedPublishes.push({
|
||||||
|
name: item?.name,
|
||||||
|
service: item?.service,
|
||||||
|
identifier: item?.identifier
|
||||||
|
})
|
||||||
|
});
|
||||||
|
store.setFailedPublishResources(failedPublishes)
|
||||||
|
} else {
|
||||||
|
store.setError(error?.message || 'Error during publish')
|
||||||
|
}
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
store.setIsLoading(false);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, [setPublishResources]);
|
||||||
|
|
||||||
|
|
||||||
if (!metadata)
|
if (!metadata)
|
||||||
return {
|
return {
|
||||||
|
@ -207,10 +207,8 @@ export const useResources = (retryAttempts: number = 2, maxSize = 5242880) => {
|
|||||||
if (cancelRequests) {
|
if (cancelRequests) {
|
||||||
cancelAllRequests();
|
cancelAllRequests();
|
||||||
}
|
}
|
||||||
console.log('listName', listName)
|
|
||||||
const cacheKey = generateCacheKey(params);
|
const cacheKey = generateCacheKey(params);
|
||||||
const searchCache = getSearchCache(listName, cacheKey);
|
const searchCache = getSearchCache(listName, cacheKey);
|
||||||
console.log('searchCache', searchCache)
|
|
||||||
if (searchCache) {
|
if (searchCache) {
|
||||||
const copyParams = {...params}
|
const copyParams = {...params}
|
||||||
delete copyParams.after
|
delete copyParams.after
|
||||||
@ -223,12 +221,10 @@ export const useResources = (retryAttempts: number = 2, maxSize = 5242880) => {
|
|||||||
let responseData: QortalMetadata[] = [];
|
let responseData: QortalMetadata[] = [];
|
||||||
let filteredResults: QortalMetadata[] = [];
|
let filteredResults: QortalMetadata[] = [];
|
||||||
let lastCreated = params.before || undefined;
|
let lastCreated = params.before || undefined;
|
||||||
console.log('lastCreated', lastCreated)
|
|
||||||
const targetLimit = params.limit ?? 20; // Use `params.limit` if provided, else default to 20
|
const targetLimit = params.limit ?? 20; // Use `params.limit` if provided, else default to 20
|
||||||
const isUnlimited = params.limit === 0;
|
const isUnlimited = params.limit === 0;
|
||||||
|
|
||||||
while (isUnlimited || filteredResults.length < targetLimit) {
|
while (isUnlimited || filteredResults.length < targetLimit) {
|
||||||
console.log('beforebefore')
|
|
||||||
const response = await qortalRequest({
|
const response = await qortalRequest({
|
||||||
action: "SEARCH_QDN_RESOURCES",
|
action: "SEARCH_QDN_RESOURCES",
|
||||||
mode: "ALL",
|
mode: "ALL",
|
||||||
@ -236,14 +232,12 @@ export const useResources = (retryAttempts: number = 2, maxSize = 5242880) => {
|
|||||||
limit: targetLimit - filteredResults.length, // Adjust limit dynamically
|
limit: targetLimit - filteredResults.length, // Adjust limit dynamically
|
||||||
before: lastCreated,
|
before: lastCreated,
|
||||||
});
|
});
|
||||||
console.log('responseresponse', response)
|
|
||||||
if (!response || response.length === 0) {
|
if (!response || response.length === 0) {
|
||||||
break; // No more data available
|
break; // No more data available
|
||||||
}
|
}
|
||||||
|
|
||||||
responseData = response;
|
responseData = response;
|
||||||
const validResults = responseData.filter((item) => item.size !== 32 && item.size < maxSize);
|
const validResults = responseData.filter((item) => item.size !== 32 && item.size < maxSize);
|
||||||
console.log('validResults', validResults)
|
|
||||||
filteredResults = [...filteredResults, ...validResults];
|
filteredResults = [...filteredResults, ...validResults];
|
||||||
|
|
||||||
if (filteredResults.length >= targetLimit && !isUnlimited) {
|
if (filteredResults.length >= targetLimit && !isUnlimited) {
|
||||||
@ -312,7 +306,6 @@ export const useResources = (retryAttempts: number = 2, maxSize = 5242880) => {
|
|||||||
|
|
||||||
const addNewResources = useCallback(
|
const addNewResources = useCallback(
|
||||||
(listName: string, resources: Resource[]) => {
|
(listName: string, resources: Resource[]) => {
|
||||||
console.log('resources1212', resources)
|
|
||||||
addTemporaryResource(
|
addTemporaryResource(
|
||||||
listName,
|
listName,
|
||||||
resources.map((item) => item.qortalMetadata)
|
resources.map((item) => item.qortalMetadata)
|
||||||
|
@ -1,23 +1,75 @@
|
|||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { ResourceToPublish } from '../types/qortalRequests/types';
|
import { ResourceToPublish } from '../types/qortalRequests/types';
|
||||||
import { Service } from '../types/interfaces/resources';
|
import { QortalGetMetadata, Service } from '../types/interfaces/resources';
|
||||||
|
|
||||||
|
|
||||||
interface MultiplePublishState {
|
interface MultiplePublishState {
|
||||||
resources: ResourceToPublish[];
|
resources: ResourceToPublish[];
|
||||||
setPublishResources: (resources: ResourceToPublish[])=> void
|
failedResources: QortalGetMetadata[];
|
||||||
reset: ()=> void
|
isPublishing: boolean;
|
||||||
isPublishing: boolean
|
resolveCallback?: (result: QortalGetMetadata[]) => void;
|
||||||
}
|
rejectCallback?: (error: Error) => void;
|
||||||
const initialState = {
|
|
||||||
resources: [],
|
|
||||||
isPublishing: false
|
|
||||||
};
|
|
||||||
export const useMultiplePublishStore = create<MultiplePublishState>((set) => ({
|
|
||||||
...initialState,
|
|
||||||
setPublishResources: (resources: ResourceToPublish[]) => set(() => ({ resources, isPublishing: true })),
|
|
||||||
reset: () => set(initialState),
|
|
||||||
|
|
||||||
|
setPublishResources: (resources: ResourceToPublish[]) => void;
|
||||||
|
setFailedPublishResources: (resources: QortalGetMetadata[]) => void;
|
||||||
|
setIsPublishing: (value: boolean) => void;
|
||||||
|
setCompletionResolver: (resolver: (result: QortalGetMetadata[]) => void) => void;
|
||||||
|
setRejectionResolver: (resolver: (reject: Error) => void) => void;
|
||||||
|
complete: (result: any) => void;
|
||||||
|
reject: (Error: Error) => void;
|
||||||
|
reset: () => void;
|
||||||
|
setError: (message: string | null)=> void
|
||||||
|
error: string | null
|
||||||
|
isLoading: boolean
|
||||||
|
setIsLoading: (val: boolean)=> void
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
resources: [],
|
||||||
|
failedResources: [],
|
||||||
|
isPublishing: false,
|
||||||
|
resolveCallback: undefined,
|
||||||
|
rejectCallback: undefined,
|
||||||
|
error: "",
|
||||||
|
isLoading: false
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useMultiplePublishStore = create<MultiplePublishState>((set, get) => ({
|
||||||
|
...initialState,
|
||||||
|
|
||||||
|
setPublishResources: (resources) => {
|
||||||
|
set({ resources, isPublishing: true });
|
||||||
|
},
|
||||||
|
setFailedPublishResources: (resources) => {
|
||||||
|
set({ failedResources: resources });
|
||||||
|
},
|
||||||
|
setIsPublishing: (value) => {
|
||||||
|
set({ isPublishing: value });
|
||||||
|
},
|
||||||
|
setIsLoading: (value) => {
|
||||||
|
set({ isLoading: value });
|
||||||
|
},
|
||||||
|
setCompletionResolver: (resolver) => {
|
||||||
|
set({ resolveCallback: resolver });
|
||||||
|
},
|
||||||
|
setRejectionResolver: (reject) => {
|
||||||
|
set({ rejectCallback: reject });
|
||||||
|
},
|
||||||
|
complete: (result) => {
|
||||||
|
const resolver = get().resolveCallback;
|
||||||
|
if (resolver) resolver(result);
|
||||||
|
set({ resolveCallback: undefined, isPublishing: false });
|
||||||
|
},
|
||||||
|
reject: (result) => {
|
||||||
|
const resolver = get().rejectCallback;
|
||||||
|
if (resolver) resolver(result);
|
||||||
|
set({ resolveCallback: undefined, isPublishing: false });
|
||||||
|
},
|
||||||
|
setError: (message) => {
|
||||||
|
set({ error: message });
|
||||||
|
},
|
||||||
|
|
||||||
|
reset: () => set(initialState),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export type PublishLocation = {
|
export type PublishLocation = {
|
||||||
@ -31,6 +83,11 @@ export type PublishStatus = {
|
|||||||
chunks: number;
|
chunks: number;
|
||||||
totalChunks: number;
|
totalChunks: number;
|
||||||
processed: boolean;
|
processed: boolean;
|
||||||
|
error?: {
|
||||||
|
reason: string
|
||||||
|
}
|
||||||
|
retry: boolean
|
||||||
|
filename: string
|
||||||
};
|
};
|
||||||
|
|
||||||
type PublishStatusStore = {
|
type PublishStatusStore = {
|
||||||
@ -58,6 +115,7 @@ export const usePublishStatusStore = create<PublishStatusStore>((set, get) => ({
|
|||||||
chunks: 0,
|
chunks: 0,
|
||||||
totalChunks: 0,
|
totalChunks: 0,
|
||||||
processed: false,
|
processed: false,
|
||||||
|
retry: false
|
||||||
};
|
};
|
||||||
|
|
||||||
const newStatus: PublishStatus = {
|
const newStatus: PublishStatus = {
|
||||||
|
@ -121,7 +121,6 @@ export function base64ToObject(base64: string){
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const base64ToBlobUrl = (base64: string, mimeType = 'text/vtt'): string => {
|
export const base64ToBlobUrl = (base64: string, mimeType = 'text/vtt'): string => {
|
||||||
console.log('base64ToBlobUrl', base64, mimeType)
|
|
||||||
const cleanedBase64 = base64.length % 4 === 0 ? base64 : base64 + '='.repeat(4 - base64.length % 4);
|
const cleanedBase64 = base64.length % 4 === 0 ? base64 : base64 + '='.repeat(4 - base64.length % 4);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -9,7 +9,6 @@ export default defineConfig({
|
|||||||
'@mui/material',
|
'@mui/material',
|
||||||
'@mui/system',
|
'@mui/system',
|
||||||
'@emotion/react',
|
'@emotion/react',
|
||||||
'@emotion/styled',
|
'@emotion/styled'
|
||||||
'mediainfo.js'
|
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user