mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-02 16:38:14 +00:00
Continue - Inference Job for Pre-loading User Media (#434)
- Raised these signals for background loaded media to the app level - Raised the enums and types for these signals to the app level - Delete the old useInferenceManager that only specifically handles audio inferences - Use the new signal and its implementation pattern to handle audio inferences - Clean up and simplify the implementation pattern to this new signal - Variable polling frequencies depending what kind of active jobs exist in the list --------- Co-authored-by: wil <wil@storyteller.ai>
This commit is contained in:
1 parent
a4e8e68b8b
commit
65b91ce54e
30 files changed
+423
-563
No files matched your search
@@ -9,7 +9,9 @@ import { activeWorkflowJobs } from "~/signals";
|
||||
|
||||
export const MyMoviesButton = () => {
|
||||
useSignals();
|
||||
const activeCount = activeWorkflowJobs.value.length;
|
||||
const activeCount = activeWorkflowJobs.value
|
||||
? activeWorkflowJobs.value.length
|
||||
: 0;
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
|
||||
@@ -6,3 +6,4 @@ export const ONE_MINUTE = 60000;
|
||||
export const TWO_SECONDS = 2000;
|
||||
export const FIVE_SECONDS = 5000;
|
||||
export const SEVEN_SECONDS = 7000;
|
||||
export const TEN_SECONDS = 10000;
|
||||
@@ -8,3 +8,9 @@ export enum JobStatus {
|
||||
CANCELLED_BY_USER = "cancelled_by_user",
|
||||
CANCCELLED_BY_SYSTEM = "cancelled_by_system",
|
||||
}
|
||||
|
||||
export enum JobType {
|
||||
TextToSpeech = "text_to_speech",
|
||||
VoiceConversion = "voice_conversion",
|
||||
VideoStyleTransfer = "workflow",
|
||||
}
|
||||
+1
-1
@@ -1 +1 @@
|
||||
export * from "./useInferenceJobManager";
|
||||
export * from "./useActiveJobs";
|
||||
@@ -0,0 +1 @@
|
||||
export { useActiveJobs } from "./useActiveJobs";
|
||||
@@ -1,5 +1,9 @@
|
||||
import { Job } from "~/models";
|
||||
|
||||
export type WindowExtended = typeof window & {
|
||||
stopPollingActiveJobs: () => void;
|
||||
};
|
||||
|
||||
export type GetJobsResponse = {
|
||||
success: boolean;
|
||||
error_message?: string;
|
||||
@@ -0,0 +1,130 @@
|
||||
import { useCallback, useEffect, useLayoutEffect, useState } from "react";
|
||||
import { useSignalEffect, useSignals } from "@preact/signals-react/runtime";
|
||||
|
||||
import { JobType } from "~/enums";
|
||||
|
||||
import {
|
||||
activeJobs,
|
||||
shouldPollActiveJobs,
|
||||
startPollingActiveJobs,
|
||||
stopPollingActiveJobs,
|
||||
} from "~/signals";
|
||||
|
||||
import { PollRecentJobs } from "./utilities";
|
||||
import { TEN_SECONDS, TWO_SECONDS } from "~/constants";
|
||||
import { WindowExtended } from "./types";
|
||||
|
||||
export const useActiveJobs = () => {
|
||||
useSignals();
|
||||
|
||||
const [timerState, setTimerState] = useState<{
|
||||
timer: NodeJS.Timeout | undefined;
|
||||
interval: number;
|
||||
}>({
|
||||
timer: undefined,
|
||||
interval: TWO_SECONDS,
|
||||
});
|
||||
|
||||
const resetTimer = useCallback((newTimeInterval: number) => {
|
||||
setTimerState((curr) => {
|
||||
if (!curr.timer) {
|
||||
// if no timer, just change interval
|
||||
return {
|
||||
timer: curr.timer,
|
||||
interval: newTimeInterval,
|
||||
};
|
||||
}
|
||||
// if timer already exist, first clear
|
||||
// then set new one with new interval
|
||||
clearInterval(curr.timer);
|
||||
const newTimer = setInterval(() => {
|
||||
PollRecentJobs();
|
||||
}, newTimeInterval);
|
||||
return {
|
||||
timer: newTimer,
|
||||
interval: newTimeInterval,
|
||||
};
|
||||
});
|
||||
}, []);
|
||||
|
||||
const clearTimer = useCallback(() => {
|
||||
if (timerState.timer) {
|
||||
clearInterval(timerState.timer);
|
||||
setTimerState((curr) => ({
|
||||
...curr,
|
||||
timer: undefined,
|
||||
}));
|
||||
}
|
||||
}, [timerState.timer]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
//make sure we run clear timer as well on dismount;
|
||||
clearInterval(timerState.timer);
|
||||
};
|
||||
}, [timerState.timer]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
//TODO: add dev account guard
|
||||
(window as WindowExtended).stopPollingActiveJobs = stopPollingActiveJobs;
|
||||
}, []);
|
||||
|
||||
useSignalEffect(() => {
|
||||
// if active jobs is not initiated, do do a first pull;
|
||||
if (!activeJobs.value) {
|
||||
PollRecentJobs();
|
||||
return;
|
||||
}
|
||||
// if there are no active jobs, stop making polls
|
||||
if (activeJobs.value.length === 0) {
|
||||
stopPollingActiveJobs();
|
||||
return;
|
||||
}
|
||||
|
||||
if (activeJobs.value.length > 0) {
|
||||
// if there are active jobs, first, manage polling interval
|
||||
const hasShortJobs =
|
||||
activeJobs.value.find((job) => {
|
||||
return (
|
||||
job.request.inference_category === JobType.TextToSpeech ||
|
||||
job.request.inference_category === JobType.VoiceConversion
|
||||
);
|
||||
}) !== undefined;
|
||||
if (!hasShortJobs && timerState.interval < TEN_SECONDS) {
|
||||
resetTimer(TEN_SECONDS);
|
||||
}
|
||||
if (hasShortJobs && timerState.interval > TWO_SECONDS) {
|
||||
resetTimer(TWO_SECONDS);
|
||||
}
|
||||
|
||||
// if there are active jobs, and is not polling, start polling
|
||||
if (!shouldPollActiveJobs.value) {
|
||||
startPollingActiveJobs();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
useSignalEffect(() => {
|
||||
//trigger the pull via a timer
|
||||
if (shouldPollActiveJobs.value) {
|
||||
setTimerState((curr) => {
|
||||
if (curr.timer) {
|
||||
//do nothing if timer is already running;
|
||||
return curr;
|
||||
}
|
||||
|
||||
//else make and set timer
|
||||
const newTimer = setInterval(() => {
|
||||
PollRecentJobs();
|
||||
}, curr.interval);
|
||||
return {
|
||||
...curr,
|
||||
timer: newTimer,
|
||||
};
|
||||
});
|
||||
return;
|
||||
}
|
||||
// should not poll, then clear timer
|
||||
clearTimer();
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
import { getRecentJobs } from "~/api";
|
||||
import { ToastTypes } from "~/enums";
|
||||
import { authentication, addToast, setJobs } from "~/signals";
|
||||
|
||||
export function PollRecentJobs() {
|
||||
console.log("Poll recent jobs");
|
||||
const endpoint = getRecentJobs;
|
||||
|
||||
fetch(endpoint, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
session: authentication.sessionToken.value || "",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
if (res.jobs && res.jobs.length > 0) {
|
||||
setJobs(res.jobs);
|
||||
// just no jobs, not an error, do nothing
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
addToast(ToastTypes.ERROR, "Unknown Error in Getting Recent Jobs");
|
||||
});
|
||||
}
|
||||
@@ -1,47 +1,18 @@
|
||||
import { useCallback } from "react";
|
||||
import { useSignalEffect, useSignals } from "@preact/signals-react/runtime";
|
||||
import { GetUserMovies, instanceOfMediaListResponse } from "./utilities";
|
||||
import { addToast, authentication, completedJobs } from "~/signals";
|
||||
import { PollUserMovies, PollUserAudioItems } from "./utilities";
|
||||
|
||||
import {
|
||||
myMovies,
|
||||
setMyMovies,
|
||||
shouldPollMyMovies,
|
||||
} from "~/pages/PageEnigma/signals";
|
||||
import { ToastTypes } from "~/enums";
|
||||
import { completedJobs, userMovies, userAudioItems } from "~/signals";
|
||||
|
||||
import { JobType } from "~/enums";
|
||||
|
||||
export const useBackgroundLoadingMedia = () => {
|
||||
useSignals();
|
||||
|
||||
const PollMyMovies = useCallback(() => {
|
||||
// console.log("polling my movies");
|
||||
if (authentication.userInfo.value?.username) {
|
||||
GetUserMovies(authentication.userInfo.value.username).then((res) => {
|
||||
// console.log("GetUerMovies has response:", res);
|
||||
if (instanceOfMediaListResponse(res)) {
|
||||
setMyMovies(res.results);
|
||||
} else {
|
||||
addToast(ToastTypes.ERROR, res.error_reason);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useSignalEffect(() => {
|
||||
if (shouldPollMyMovies.value) {
|
||||
// shouldPollMyMovies is initiated as true to guaruntee first pull on load
|
||||
PollMyMovies();
|
||||
}
|
||||
});
|
||||
|
||||
useSignalEffect(() => {
|
||||
//CASE 1: first load
|
||||
// if myMovies undefined, first load is not done
|
||||
if (!myMovies.value) {
|
||||
// turn on polling if it is not already on
|
||||
if (!shouldPollMyMovies.value) {
|
||||
shouldPollMyMovies.value = true;
|
||||
}
|
||||
// if myMovies undefined, poll for the first time
|
||||
if (!userMovies.value) {
|
||||
PollUserMovies();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -53,7 +24,7 @@ export const useBackgroundLoadingMedia = () => {
|
||||
const workflowJobsTokens = completedJobs.value
|
||||
.filter((job) => {
|
||||
if (
|
||||
job.request.inference_category === "workflow" &&
|
||||
job.request.inference_category === JobType.VideoStyleTransfer &&
|
||||
job.maybe_result.entity_token
|
||||
) {
|
||||
return true;
|
||||
@@ -67,21 +38,61 @@ export const useBackgroundLoadingMedia = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const myMoviesTokens = myMovies.value.map((movie) => movie.token);
|
||||
const userMoviesTokens = userMovies.value.map((movie) => movie.token);
|
||||
const isEveryTokenIncludedInPolled = workflowJobsTokens.every((token) =>
|
||||
myMoviesTokens.includes(token),
|
||||
userMoviesTokens.includes(token),
|
||||
);
|
||||
if (!isEveryTokenIncludedInPolled) {
|
||||
//there are videos newly completed, poll
|
||||
shouldPollMyMovies.value = true;
|
||||
PollUserMovies();
|
||||
return;
|
||||
}
|
||||
|
||||
// at this point, make sure we stop polling because:
|
||||
// - myMovies first load is done
|
||||
// - completed jobs that contain new videos are all polled
|
||||
if (shouldPollMyMovies.value) {
|
||||
shouldPollMyMovies.value = false;
|
||||
//else, no new movies, no need to poll;
|
||||
});
|
||||
|
||||
useSignalEffect(() => {
|
||||
//CASE 1: first load
|
||||
// if audioItems undefined, poll for the first time
|
||||
if (!userAudioItems.value) {
|
||||
PollUserAudioItems();
|
||||
return;
|
||||
}
|
||||
|
||||
//CASE 2: pull after jobs completion
|
||||
if (!completedJobs.value || completedJobs.value.length === 0) {
|
||||
return; // nothing to do if there's no complete jobs
|
||||
}
|
||||
|
||||
const audioJobsTokens = completedJobs.value
|
||||
.filter((job) => {
|
||||
if (
|
||||
(job.request.inference_category === JobType.TextToSpeech ||
|
||||
job.request.inference_category === JobType.VoiceConversion) &&
|
||||
job.maybe_result.entity_token
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.map((job) => job.maybe_result.entity_token);
|
||||
|
||||
if (audioJobsTokens.length === 0) {
|
||||
//nothing to do if no complete jobs involves audio
|
||||
return;
|
||||
}
|
||||
|
||||
const userAudioItemsTokens = userAudioItems.value.map(
|
||||
(audio) => audio.media_id,
|
||||
);
|
||||
const isEveryTokenIncludedInPolled = audioJobsTokens.every((token) =>
|
||||
userAudioItemsTokens.includes(token),
|
||||
);
|
||||
if (!isEveryTokenIncludedInPolled) {
|
||||
//there are videos newly completed, poll
|
||||
PollUserAudioItems();
|
||||
return;
|
||||
}
|
||||
//else, no new movies, no need to poll;
|
||||
});
|
||||
};
|
||||
@@ -1,30 +1,56 @@
|
||||
import { MediaFileType } from "~/pages/PageEnigma/enums";
|
||||
|
||||
import { ToastTypes } from "~/enums";
|
||||
|
||||
import {
|
||||
GetMediaByUser,
|
||||
GetMediaListResponse,
|
||||
} from "~/api/media_files/GetMediaByUser";
|
||||
|
||||
export function instanceOfMediaListResponse(
|
||||
object: GetMediaListResponse | { success: boolean; error_reason: string },
|
||||
): object is GetMediaListResponse {
|
||||
return "results" in object;
|
||||
}
|
||||
export function GetUserMovies(username: string) {
|
||||
import {
|
||||
addToast,
|
||||
authentication,
|
||||
setUserAudioItems,
|
||||
setUserMovies,
|
||||
} from "~/signals";
|
||||
const { userInfo } = authentication;
|
||||
|
||||
export function PollUserMovies() {
|
||||
if (!userInfo.value) {
|
||||
//do nothing return if login info does not exist
|
||||
return;
|
||||
}
|
||||
return GetMediaByUser(
|
||||
username,
|
||||
userInfo.value.username,
|
||||
{},
|
||||
{
|
||||
filter_media_type: MediaFileType.Video,
|
||||
},
|
||||
)
|
||||
.then((res: GetMediaListResponse) => {
|
||||
return res;
|
||||
setUserMovies(res.results);
|
||||
})
|
||||
.catch(() => {
|
||||
return {
|
||||
success: false,
|
||||
error_reason: "Unknown Error in Loading My Movies",
|
||||
};
|
||||
addToast(ToastTypes.ERROR, "Unknown Error in Loading My Movies");
|
||||
});
|
||||
}
|
||||
|
||||
export function PollUserAudioItems() {
|
||||
if (!userInfo.value) {
|
||||
//do nothing return if login info does not exist
|
||||
return;
|
||||
}
|
||||
return GetMediaByUser(
|
||||
userInfo.value.username,
|
||||
{},
|
||||
{
|
||||
filter_media_type: MediaFileType.Audio,
|
||||
},
|
||||
)
|
||||
.then((res: GetMediaListResponse) => {
|
||||
setUserAudioItems(res.results);
|
||||
})
|
||||
.catch(() => {
|
||||
addToast(ToastTypes.ERROR, "Unknown Error in Loading My Audio Items");
|
||||
});
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
export { useInferenceJobManager } from "./useInferenceJobManager";
|
||||
export { useActiveJobs } from "./useActiveJobs";
|
||||
@@ -1,68 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useSignalEffect, useSignals } from "@preact/signals-react/runtime";
|
||||
|
||||
import { GetJobsResponse } from "./types";
|
||||
import { ToastTypes } from "~/enums";
|
||||
import { SEVEN_SECONDS } from "~/constants";
|
||||
|
||||
import {
|
||||
activeJobs,
|
||||
addToast,
|
||||
setJobs,
|
||||
shouldPollActiveJobs,
|
||||
startPollingActiveJobs,
|
||||
stopPollingActiveJobs,
|
||||
} from "~/signals";
|
||||
|
||||
import { GetRecentJobs } from "./utilities";
|
||||
|
||||
export const useActiveJobs = () => {
|
||||
useSignals();
|
||||
|
||||
useEffect(() => {
|
||||
const PollRecentJobs = () => {
|
||||
GetRecentJobs().then((res: GetJobsResponse) => {
|
||||
if (!res.success) {
|
||||
addToast(
|
||||
ToastTypes.ERROR,
|
||||
res.error_message || "Unknown Error in Getting Recent Jobs",
|
||||
);
|
||||
}
|
||||
if (res.jobs && res.jobs.length > 0) {
|
||||
setJobs(res.jobs);
|
||||
}
|
||||
// just no jobs, not an error, do nothing
|
||||
});
|
||||
};
|
||||
|
||||
// Initial Poll
|
||||
PollRecentJobs();
|
||||
|
||||
//trigger the pull every two seconds
|
||||
const intervalTimer = setInterval(() => {
|
||||
if (shouldPollActiveJobs.value) {
|
||||
//but only do it if necessary
|
||||
PollRecentJobs();
|
||||
}
|
||||
}, SEVEN_SECONDS);
|
||||
return () => {
|
||||
clearInterval(intervalTimer);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useSignalEffect(() => {
|
||||
// if active jobs is not initiated, do nothing
|
||||
if (!activeJobs.value) {
|
||||
return;
|
||||
}
|
||||
// if there are no active jobs, stop making polls
|
||||
if (activeJobs.value.length === 0) {
|
||||
stopPollingActiveJobs();
|
||||
return;
|
||||
}
|
||||
// if there are active jobs, and is not polling, start polling
|
||||
if (activeJobs.value.length > 0 && !shouldPollActiveJobs.value) {
|
||||
startPollingActiveJobs();
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -1,55 +0,0 @@
|
||||
import { useCallback, useEffect } from "react";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
|
||||
import { ErrorResponse, GetJobStatusResponse } from "~/pages/PageEnigma/models";
|
||||
import { JobState } from "~/pages/PageEnigma/enums";
|
||||
import { ToastTypes } from "~/enums";
|
||||
|
||||
import { addToast } from "~/signals";
|
||||
import {
|
||||
inferenceJobs,
|
||||
updateInferenceJob,
|
||||
} from "~/pages/PageEnigma/signals/inferenceJobs";
|
||||
|
||||
import { GetInferenceJobStatus } from "./utilities";
|
||||
|
||||
function shouldKeepPolling(currStatus: string) {
|
||||
return currStatus === JobState.PENDING || currStatus === JobState.STARTED;
|
||||
}
|
||||
|
||||
export const useInferenceJobManager = () => {
|
||||
useSignals();
|
||||
|
||||
const pollInferenceJobs = useCallback(() => {
|
||||
if (inferenceJobs.value.length > 0) {
|
||||
inferenceJobs.value.forEach((job) => {
|
||||
if (shouldKeepPolling(job.job_status)) {
|
||||
GetInferenceJobStatus(job.job_id).then(
|
||||
(res: GetJobStatusResponse | ErrorResponse) => {
|
||||
if (res?.success) {
|
||||
console.log("inference", res);
|
||||
const response = res as GetJobStatusResponse;
|
||||
updateInferenceJob({
|
||||
...job,
|
||||
job_status: response.state.status.status,
|
||||
result: response.state.maybe_result,
|
||||
});
|
||||
return;
|
||||
}
|
||||
addToast(ToastTypes.ERROR, (res as ErrorResponse).error_reason);
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const intervalTimer = setInterval(() => {
|
||||
pollInferenceJobs();
|
||||
}, 2000);
|
||||
return () => {
|
||||
clearInterval(intervalTimer);
|
||||
};
|
||||
}, [pollInferenceJobs]);
|
||||
};
|
||||
@@ -1,76 +0,0 @@
|
||||
import { ErrorResponse, GetJobStatusResponse } from "~/pages/PageEnigma/models";
|
||||
|
||||
import { getActiveJobs, getJobStatus, getRecentJobs } from "~/api";
|
||||
import { authentication } from "~/signals";
|
||||
|
||||
export function GetInferenceJobStatus(
|
||||
jobToken: string,
|
||||
): Promise<GetJobStatusResponse | ErrorResponse> {
|
||||
const endpoint = getJobStatus(jobToken);
|
||||
|
||||
return fetch(endpoint, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
session: authentication.sessionToken.value || "",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
const response: GetJobStatusResponse | ErrorResponse = res;
|
||||
return response;
|
||||
})
|
||||
.catch(() => {
|
||||
return {
|
||||
success: false,
|
||||
error_reason: "Unknown error",
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function GetRecentJobs() {
|
||||
const endpoint = getRecentJobs;
|
||||
|
||||
return fetch(endpoint, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
session: authentication.sessionToken.value || "",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
return res;
|
||||
})
|
||||
.catch(() => {
|
||||
return {
|
||||
success: false,
|
||||
error_reason: "Unknown Error in Getting Recent Jobs",
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function GetActiveJobs() {
|
||||
const endpoint = getActiveJobs;
|
||||
|
||||
return fetch(endpoint, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
session: authentication.sessionToken.value || "",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
return res;
|
||||
})
|
||||
.catch(() => {
|
||||
return {
|
||||
success: false,
|
||||
error_reason: "Unknown error",
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -2,14 +2,13 @@ import { PageEditor } from "~/pages/PageEnigma/PageEditor";
|
||||
import { TrackProvider } from "~/pages/PageEnigma/contexts/TrackContext/TrackProvider";
|
||||
import { DragComponent } from "~/pages/PageEnigma/comps/DragComponent/DragComponent";
|
||||
import { EngineProvider } from "./contexts/EngineContext";
|
||||
import { useInferenceJobManager, useActiveJobs } from "~/hooks";
|
||||
import { useActiveJobs } from "~/hooks";
|
||||
import { useQueueHandler } from "./hooks/useQueueHandler";
|
||||
import { ErrorDialog, LoadingBar } from "~/components";
|
||||
import { GenerateModals } from "~/pages/PageEnigma/comps/GenerateModals/GenerateModals";
|
||||
import { useBackgroundLoadingMedia } from "~/hooks/useBackgroundLoadingMedia";
|
||||
|
||||
export const PageEnigma = ({ sceneToken }: { sceneToken?: string }) => {
|
||||
useInferenceJobManager();
|
||||
useActiveJobs();
|
||||
useBackgroundLoadingMedia();
|
||||
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import {
|
||||
// generateMovieId,
|
||||
myMovies,
|
||||
viewMyMovies,
|
||||
} from "~/pages/PageEnigma/signals";
|
||||
import { viewMyMovies } from "~/pages/PageEnigma/signals";
|
||||
import { CompletedCard } from "~/pages/PageEnigma/comps/GenerateModals/CompletedCard";
|
||||
import { InProgressCard } from "~/pages/PageEnigma/comps/GenerateModals/InProgressCard";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import { faFilm } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { TransitionDialogue } from "~/components";
|
||||
|
||||
import { activeWorkflowJobs } from "~/signals";
|
||||
import { activeWorkflowJobs, userMovies } from "~/signals";
|
||||
|
||||
interface Props {
|
||||
setMovieId: (page: string) => void;
|
||||
}
|
||||
@@ -41,7 +38,7 @@ export function MyMovies({ setMovieId }: Props) {
|
||||
<div>
|
||||
<div className="mx-5 mb-1 font-medium">Completed</div>
|
||||
<div className="flex flex-col">
|
||||
{myMovies.value?.map((movie) => (
|
||||
{userMovies.value?.map((movie) => (
|
||||
<CompletedCard
|
||||
key={movie.token}
|
||||
movie={movie}
|
||||
|
||||
@@ -1,40 +1,21 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useSignalEffect, useSignals } from "@preact/signals-react/runtime";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
|
||||
import { AssetType } from "~/enums";
|
||||
import { AudioTabPages } from "~/pages/PageEnigma/enums";
|
||||
import { AudioPanelState } from "~/pages/PageEnigma/models/voice";
|
||||
import {
|
||||
AudioTabPages,
|
||||
InferenceJobType,
|
||||
JobState,
|
||||
} from "~/pages/PageEnigma/enums";
|
||||
import { AudioPanelState } from "../../../../models/voice";
|
||||
import {
|
||||
AudioMediaItem,
|
||||
MediaInfo,
|
||||
TtsModelListItem,
|
||||
VoiceConversionModelListItem,
|
||||
} from "~/pages/PageEnigma/models";
|
||||
|
||||
import {
|
||||
addInferenceJob,
|
||||
inferenceJobs,
|
||||
} from "~/pages/PageEnigma/signals/inferenceJobs";
|
||||
import {
|
||||
audioItemsFromServer,
|
||||
isRetreivingAudioItems,
|
||||
} from "~/pages/PageEnigma/signals/mediaFromServer";
|
||||
|
||||
import {
|
||||
ListAudioByUser,
|
||||
ListTtsModels,
|
||||
ListVoiceConversionModels,
|
||||
} from "./utilities";
|
||||
import { ListTtsModels, ListVoiceConversionModels } from "./utilities";
|
||||
import { initialTtsState, initialV2VState } from "./values";
|
||||
|
||||
import { PageLibrary } from "./pageLibrary";
|
||||
import { PageAudioLibrary } from "./pageAudioLibrary";
|
||||
import { PageAudioGeneration } from "./pageAudioGeneration";
|
||||
import { PageSelectTtsModel } from "./pageSelectTtsModel";
|
||||
import { PageSelectV2VModel } from "./pageSelectV2VModel";
|
||||
import { PollUserAudioItems } from "~/hooks/useBackgroundLoadingMedia/utilities";
|
||||
|
||||
export const AudioTab = () => {
|
||||
useSignals();
|
||||
@@ -54,71 +35,9 @@ export const AudioTab = () => {
|
||||
Array<VoiceConversionModelListItem>
|
||||
>([]);
|
||||
|
||||
const handleListAudioByUser = useCallback(() => {
|
||||
function getTitle(item: MediaInfo) {
|
||||
// console.log(item);
|
||||
if (item.maybe_title) return item.maybe_title;
|
||||
if (
|
||||
item.origin &&
|
||||
item.origin.maybe_model &&
|
||||
item.origin.maybe_model.title
|
||||
)
|
||||
return item.origin.maybe_model.title;
|
||||
return "Media Audio";
|
||||
}
|
||||
function getCategory(item: MediaInfo) {
|
||||
if (
|
||||
item.origin &&
|
||||
item.origin.product_category &&
|
||||
item.origin.product_category !== "unknown"
|
||||
)
|
||||
return item.origin.product_category;
|
||||
if (item.origin_category) return item.origin_category;
|
||||
return "unknown";
|
||||
}
|
||||
function checkIsNew(previousItems: AudioMediaItem[], token: string) {
|
||||
const foundItem = previousItems.find((item) => {
|
||||
return token === item.media_id && item.isNew !== true;
|
||||
});
|
||||
return foundItem === undefined;
|
||||
}
|
||||
function getLength(item: MediaInfo) {
|
||||
return item.maybe_duration_millis
|
||||
? (item.maybe_duration_millis / 1000) * 60
|
||||
: undefined;
|
||||
}
|
||||
isRetreivingAudioItems.value = true;
|
||||
ListAudioByUser().then((res: MediaInfo[]) => {
|
||||
isRetreivingAudioItems.value = false;
|
||||
const previousItems = [...audioItemsFromServer.value];
|
||||
audioItemsFromServer.value = res.map((item) => {
|
||||
const morphedItem: AudioMediaItem = {
|
||||
version: 1,
|
||||
type: AssetType.AUDIO,
|
||||
category: getCategory(item),
|
||||
media_id: item.token,
|
||||
object_uuid: item.token,
|
||||
name: getTitle(item),
|
||||
description: item.maybe_text_transcript || "",
|
||||
publicBucketPath: item.public_bucket_path,
|
||||
length: getLength(item),
|
||||
thumbnail: "/resources/placeholders/audio_placeholder.png",
|
||||
isMine: true,
|
||||
isNew:
|
||||
previousItems.length > 0
|
||||
? checkIsNew(previousItems, item.token)
|
||||
: false,
|
||||
// isBookmarked?: boolean;
|
||||
};
|
||||
return morphedItem;
|
||||
});
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!state.firstLoad) {
|
||||
//fetch all the data on first load once, after securing auth info
|
||||
handleListAudioByUser();
|
||||
ListTtsModels().then((res) => {
|
||||
if (res) setTtsModels(res);
|
||||
});
|
||||
@@ -128,79 +47,7 @@ export const AudioTab = () => {
|
||||
setState((curr) => ({ ...curr, firstLoad: true }));
|
||||
// completed the first load
|
||||
}
|
||||
}, [state, handleListAudioByUser]);
|
||||
|
||||
useEffect(() => {
|
||||
//this listens to ttsState and push its new inference jobs
|
||||
setState((curr) => {
|
||||
const { ttsState: currTtsState } = curr;
|
||||
if (currTtsState.hasEnqueued < currTtsState.inferenceTokens.length) {
|
||||
addInferenceJob({
|
||||
version: 1,
|
||||
job_id:
|
||||
currTtsState.inferenceTokens[
|
||||
currTtsState.inferenceTokens.length - 1
|
||||
],
|
||||
job_type: InferenceJobType.TextToSpeech,
|
||||
job_status: JobState.PENDING,
|
||||
});
|
||||
return {
|
||||
...curr,
|
||||
ttsState: {
|
||||
...currTtsState,
|
||||
hasEnqueued: currTtsState.hasEnqueued + 1,
|
||||
},
|
||||
};
|
||||
}
|
||||
return curr; //case of no new jobs, do nothing
|
||||
});
|
||||
}, [state.ttsState]);
|
||||
|
||||
useEffect(() => {
|
||||
//this listens to v2vState and push its new inference jobs
|
||||
setState((curr) => {
|
||||
const { v2vState: currV2VState } = curr;
|
||||
if (currV2VState.hasEnqueued < currV2VState.inferenceTokens.length) {
|
||||
addInferenceJob({
|
||||
version: 1,
|
||||
job_id:
|
||||
currV2VState.inferenceTokens[
|
||||
currV2VState.inferenceTokens.length - 1
|
||||
],
|
||||
job_type: InferenceJobType.VoiceConversion,
|
||||
job_status: JobState.PENDING,
|
||||
});
|
||||
return {
|
||||
...curr,
|
||||
v2vState: {
|
||||
...currV2VState,
|
||||
hasEnqueued: currV2VState.hasEnqueued + 1,
|
||||
},
|
||||
};
|
||||
}
|
||||
return curr; //case of no new jobs, do nothing
|
||||
});
|
||||
}, [state.v2vState]);
|
||||
|
||||
useSignalEffect(() => {
|
||||
// when inference changes, check if there's a new audio to refresh for
|
||||
let hasNewCompletedJob = false;
|
||||
inferenceJobs.value.forEach((job) => {
|
||||
if (
|
||||
job.job_status === JobState.COMPLETE_SUCCESS &&
|
||||
(job.job_type === InferenceJobType.TextToSpeech ||
|
||||
job.job_type === InferenceJobType.VoiceConversion)
|
||||
) {
|
||||
const foundItemOfJob = audioItemsFromServer.value.find((item) => {
|
||||
return item.media_id === job.result.entity_token;
|
||||
});
|
||||
hasNewCompletedJob = foundItemOfJob === undefined;
|
||||
}
|
||||
});
|
||||
if (hasNewCompletedJob) {
|
||||
handleListAudioByUser();
|
||||
}
|
||||
});
|
||||
}, [state]);
|
||||
|
||||
const changePage = useCallback((newPage: AudioTabPages) => {
|
||||
setState((curr) => ({
|
||||
@@ -212,9 +59,9 @@ export const AudioTab = () => {
|
||||
switch (state.page) {
|
||||
case AudioTabPages.LIBRARY: {
|
||||
return (
|
||||
<PageLibrary
|
||||
<PageAudioLibrary
|
||||
changePage={changePage}
|
||||
reloadLibrary={handleListAudioByUser}
|
||||
reloadLibrary={PollUserAudioItems}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,33 +6,33 @@ import {
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { InferenceJob } from "~/pages/PageEnigma/models";
|
||||
import { JobStatus } from "~/enums";
|
||||
import { Job } from "~/models";
|
||||
import { ButtonIcon, H5 } from "~/components";
|
||||
import { JobState } from "~/pages/PageEnigma/enums";
|
||||
|
||||
export const InferenceElement = ({ job }: { job: InferenceJob }) => {
|
||||
export const InferenceElement = ({ job }: { job: Job }) => {
|
||||
const className = twMerge(
|
||||
"rounded-md w-full flex justify-between items-center p-2 gap-2",
|
||||
job.job_status === JobState.PENDING ? "bg-inference-pending" : "",
|
||||
job.job_status === JobState.STARTED ? "bg-inference-generating" : "",
|
||||
job.job_status === JobState.DEAD ? "bg-inference-error" : "",
|
||||
job.status.status === JobStatus.PENDING ? "bg-inference-pending" : "",
|
||||
job.status.status === JobStatus.STARTED ? "bg-inference-generating" : "",
|
||||
job.status.status === JobStatus.DEAD ? "bg-inference-error" : "",
|
||||
);
|
||||
|
||||
const statusText = "".concat(
|
||||
job.job_status === JobState.PENDING ? "Pending..." : "",
|
||||
job.job_status === JobState.STARTED ? "Generating..." : "",
|
||||
job.job_status === JobState.DEAD ? "Error" : "",
|
||||
job.status.status === JobStatus.PENDING ? "Pending..." : "",
|
||||
job.status.status === JobStatus.STARTED ? "Generating..." : "",
|
||||
job.status.status === JobStatus.DEAD ? "Error" : "",
|
||||
);
|
||||
return (
|
||||
<div className={className}>
|
||||
{job.job_status === JobState.DEAD && (
|
||||
{job.status.status === JobStatus.DEAD && (
|
||||
<FontAwesomeIcon icon={faCircleXmark} />
|
||||
)}
|
||||
{job.job_status !== JobState.DEAD && (
|
||||
{job.status.status !== JobStatus.DEAD && (
|
||||
<FontAwesomeIcon icon={faSpinnerThird} spin />
|
||||
)}
|
||||
<H5 className="grow">{statusText}</H5>
|
||||
{job.job_status === JobState.DEAD && (
|
||||
{job.status.status === JobStatus.DEAD && (
|
||||
<ButtonIcon
|
||||
icon={faXmark}
|
||||
onClick={() => {
|
||||
|
||||
+10
-23
@@ -3,8 +3,6 @@ import { faCirclePlus } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { useSignals, useComputed } from "@preact/signals-react/runtime";
|
||||
import { audioFilter, audioItems } from "~/pages/PageEnigma/signals";
|
||||
import { audioItemsFromServer } from "~/pages/PageEnigma/signals/mediaFromServer";
|
||||
import { inferenceJobs } from "~/pages/PageEnigma/signals/inferenceJobs";
|
||||
|
||||
import { Button, Pagination, UploadAudioButtonDialogue } from "~/components";
|
||||
|
||||
@@ -12,13 +10,11 @@ import { AudioItemElements } from "./audioItemElements";
|
||||
import { TabTitle } from "~/pages/PageEnigma/comps/SidePanelTabs/comps/TabTitle";
|
||||
import { InferenceElement } from "./inferenceElement";
|
||||
import { AssetFilterOption } from "~/enums";
|
||||
import {
|
||||
AudioTabPages,
|
||||
InferenceJobType,
|
||||
JobState,
|
||||
} from "~/pages/PageEnigma/enums";
|
||||
import { AudioTabPages } from "~/pages/PageEnigma/enums";
|
||||
|
||||
export const PageLibrary = ({
|
||||
import { activeAudioJobs, userAudioItems } from "~/signals";
|
||||
|
||||
export const PageAudioLibrary = ({
|
||||
changePage,
|
||||
reloadLibrary,
|
||||
}: {
|
||||
@@ -26,9 +22,10 @@ export const PageLibrary = ({
|
||||
reloadLibrary: () => void;
|
||||
}) => {
|
||||
useSignals();
|
||||
const loadUserAudioItems = userAudioItems.value ? userAudioItems.value : [];
|
||||
const allAudioItems = useComputed(() => [
|
||||
...audioItems.value,
|
||||
...audioItemsFromServer.value,
|
||||
...loadUserAudioItems,
|
||||
]);
|
||||
const displayedItems = allAudioItems.value.filter((item) => {
|
||||
if (audioFilter.value === AssetFilterOption.ALL) {
|
||||
@@ -39,17 +36,7 @@ export const PageLibrary = ({
|
||||
}
|
||||
return item.isBookmarked;
|
||||
});
|
||||
const audioInferenceJobs = useComputed(() =>
|
||||
inferenceJobs.value.filter((job) => {
|
||||
if (
|
||||
job.job_status !== JobState.COMPLETE_SUCCESS &&
|
||||
(job.job_type === InferenceJobType.TextToSpeech ||
|
||||
job.job_type === InferenceJobType.VoiceConversion)
|
||||
) {
|
||||
return job;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
const pageSize = 20;
|
||||
const totalPages = Math.ceil(allAudioItems.value.length / pageSize);
|
||||
const [currentPage, setCurrentPage] = useState<number>(0);
|
||||
@@ -109,10 +96,10 @@ export const PageLibrary = ({
|
||||
</div>
|
||||
|
||||
<div className="w-full grow overflow-y-auto px-4">
|
||||
{audioInferenceJobs.value.length > 0 && (
|
||||
{activeAudioJobs.value && activeAudioJobs.value.length > 0 && (
|
||||
<div className="mb-4 grid grid-cols-1 gap-2">
|
||||
{audioInferenceJobs.value.map((job) => {
|
||||
return <InferenceElement key={job.job_id} job={job} />;
|
||||
{activeAudioJobs.value.map((job) => {
|
||||
return <InferenceElement key={job.job_token} job={job} />;
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
@@ -11,6 +11,7 @@ import { GenerateTtsAudioResponse } from "~/pages/PageEnigma/models/tts";
|
||||
import { GenerateTtsAudio } from "./utilities";
|
||||
import { TtsState } from "../../../../models/voice";
|
||||
import { AudioTabPages } from "~/pages/PageEnigma/enums";
|
||||
import { startPollingActiveJobs } from "~/signals";
|
||||
|
||||
export const PageTTS = ({
|
||||
changePage,
|
||||
@@ -33,21 +34,14 @@ export const PageTTS = ({
|
||||
|
||||
GenerateTtsAudio(request).then((res: GenerateTtsAudioResponse) => {
|
||||
if (res && res.inference_job_token) {
|
||||
setTtsState({
|
||||
...ttsState,
|
||||
inferenceTokens: [
|
||||
...ttsState.inferenceTokens,
|
||||
res.inference_job_token,
|
||||
],
|
||||
});
|
||||
|
||||
startPollingActiveJobs();
|
||||
changePage(AudioTabPages.LIBRARY);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log("no voice model selected");
|
||||
}
|
||||
}, [ttsState, changePage, setTtsState]);
|
||||
}, [ttsState, changePage]);
|
||||
|
||||
const handleTextInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
setTtsState({
|
||||
|
||||
@@ -13,6 +13,7 @@ import { GenerateVoiceConversion } from "./utilities";
|
||||
import { H4, H6, Button, Label, UploadAudioComponent } from "~/components";
|
||||
|
||||
import { AudioTabPages } from "~/pages/PageEnigma/enums";
|
||||
import { startPollingActiveJobs } from "~/signals";
|
||||
|
||||
export const PageVoicetoVoice = ({
|
||||
changePage,
|
||||
@@ -35,19 +36,14 @@ export const PageVoicetoVoice = ({
|
||||
GenerateVoiceConversion(request).then(
|
||||
(res: EnqueueVoiceConversionResponse) => {
|
||||
if (res && res.inference_job_token) {
|
||||
setV2VState({
|
||||
...v2vState,
|
||||
inferenceTokens: [
|
||||
...v2vState.inferenceTokens,
|
||||
res.inference_job_token,
|
||||
],
|
||||
});
|
||||
startPollingActiveJobs();
|
||||
changePage(AudioTabPages.LIBRARY);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}, [v2vState, changePage, setV2VState]);
|
||||
}, [v2vState, changePage]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-col">
|
||||
|
||||
@@ -3,14 +3,10 @@ import { TtsState, V2VState } from "../../../../models/voice";
|
||||
export const initialTtsState: TtsState = {
|
||||
voice: undefined,
|
||||
text: "",
|
||||
hasEnqueued: 0,
|
||||
inferenceTokens: [],
|
||||
};
|
||||
|
||||
export const initialV2VState: V2VState = {
|
||||
voice: undefined,
|
||||
file: undefined,
|
||||
inputFileToken: undefined,
|
||||
hasEnqueued: 0,
|
||||
inferenceTokens: [],
|
||||
};
|
||||
@@ -1,13 +0,0 @@
|
||||
export enum InferenceJobType {
|
||||
FaceAnimation,
|
||||
TextToSpeech,
|
||||
VoiceConversion,
|
||||
VoiceDesignerCreateVoice,
|
||||
VoiceDesignerTts,
|
||||
ImageGeneration,
|
||||
VideoMotionCapture,
|
||||
ConvertFbxtoGltf,
|
||||
EngineComposition,
|
||||
VideoWorkflow,
|
||||
VideoStyleTransfer,
|
||||
}
|
||||
@@ -5,7 +5,6 @@ export * from "./ClipType";
|
||||
export * from "./EditorState";
|
||||
export * from "./FetchStatus";
|
||||
export * from "./GenerateTtsAudioErrorType";
|
||||
export * from "./InferenceJobType";
|
||||
export * from "./JobState";
|
||||
export * from "./MediaFileClass";
|
||||
export * from "./MediaFileType";
|
||||
|
||||
@@ -12,14 +12,10 @@ export type AudioPanelState = {
|
||||
export type TtsState = {
|
||||
voice: TtsModelListItem | undefined;
|
||||
text: string;
|
||||
hasEnqueued: number;
|
||||
inferenceTokens: string[];
|
||||
};
|
||||
|
||||
export type V2VState = {
|
||||
voice: VoiceConversionModelListItem | undefined;
|
||||
file: File | undefined;
|
||||
inputFileToken: string | undefined;
|
||||
hasEnqueued: number;
|
||||
inferenceTokens: string[];
|
||||
};
|
||||
@@ -1,42 +0,0 @@
|
||||
import { signal } from "@preact/signals-core";
|
||||
|
||||
import { InferenceJob } from "../models";
|
||||
|
||||
export const inferenceJobs = signal<InferenceJob[]>([]);
|
||||
|
||||
export function addInferenceJob(newJob: InferenceJob) {
|
||||
const existingInferenceJobs = inferenceJobs.value;
|
||||
const jobExist = existingInferenceJobs.find(
|
||||
(job) => job.job_id == newJob.job_id,
|
||||
);
|
||||
if (!jobExist) {
|
||||
inferenceJobs.value = [...existingInferenceJobs, newJob];
|
||||
}
|
||||
// else do nothing
|
||||
}
|
||||
|
||||
export function updateInferenceJob(updatableJob: InferenceJob) {
|
||||
const existingInferenceJobs = inferenceJobs.value;
|
||||
existingInferenceJobs.forEach((job) => {
|
||||
if (job.job_id === updatableJob.job_id) {
|
||||
job.job_status = updatableJob.job_status;
|
||||
}
|
||||
if (updatableJob.result) {
|
||||
job.result = updatableJob.result;
|
||||
}
|
||||
});
|
||||
inferenceJobs.value = [...existingInferenceJobs];
|
||||
}
|
||||
|
||||
export function deleteInferenceJob(deletableJob: InferenceJob) {
|
||||
// console.log("delete");
|
||||
const newList: InferenceJob[] = [];
|
||||
inferenceJobs.value.forEach((job) => {
|
||||
if (job.job_id !== deletableJob.job_id) {
|
||||
newList.push(job);
|
||||
}
|
||||
// or else do nothing
|
||||
});
|
||||
// console.log(newList);
|
||||
inferenceJobs.value = [...newList];
|
||||
}
|
||||
@@ -1,14 +1,4 @@
|
||||
import { signal } from "@preact/signals-core";
|
||||
|
||||
import { MediaInfo } from "~/pages/PageEnigma/models/movies";
|
||||
|
||||
export const viewMyMovies = signal(false);
|
||||
export const generateMovieId = signal("");
|
||||
|
||||
export const myMovies = signal<MediaInfo[] | undefined>(undefined);
|
||||
|
||||
export const setMyMovies = (newSet: MediaInfo[]) => {
|
||||
myMovies.value = newSet;
|
||||
};
|
||||
|
||||
export const shouldPollMyMovies = signal<boolean>(true);
|
||||
@@ -1,10 +1,11 @@
|
||||
import { signal, computed } from "@preact/signals-core";
|
||||
|
||||
import { JobStatus } from "~/enums";
|
||||
import { JobStatus, JobType } from "~/enums";
|
||||
import { PollRecentJobs } from "~/hooks/useActiveJobs/utilities";
|
||||
import { Job } from "~/models";
|
||||
import { isJobStatusTerminal } from "~/utilities";
|
||||
|
||||
export const recentJobs = signal<Job[] | undefined>([]);
|
||||
export const recentJobs = signal<Job[] | undefined>(undefined);
|
||||
export const setJobs = (newJobList: Job[]) => {
|
||||
recentJobs.value = [...newJobList];
|
||||
};
|
||||
@@ -24,9 +25,22 @@ export const activeWorkflowJobs = computed(() => {
|
||||
return undefined;
|
||||
}
|
||||
return activeJobs.value.filter((job) => {
|
||||
return job.request.inference_category === "workflow";
|
||||
return job.request.inference_category === JobType.VideoStyleTransfer;
|
||||
});
|
||||
});
|
||||
|
||||
export const activeAudioJobs = computed(() => {
|
||||
if (!activeJobs.value) {
|
||||
return undefined;
|
||||
}
|
||||
return activeJobs.value.filter((job) => {
|
||||
return (
|
||||
job.request.inference_category === JobType.TextToSpeech ||
|
||||
job.request.inference_category === JobType.VoiceConversion
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
export const completedJobs = computed(() => {
|
||||
if (!recentJobs.value) {
|
||||
return undefined;
|
||||
@@ -36,13 +50,13 @@ export const completedJobs = computed(() => {
|
||||
});
|
||||
});
|
||||
|
||||
export const shouldPollActiveJobs = signal<boolean>(true);
|
||||
export const shouldPollActiveJobs = signal<boolean>(false);
|
||||
|
||||
export const startPollingActiveJobs = () => {
|
||||
console.info("start polling Active Jobs");
|
||||
PollRecentJobs(); //poll once regardless of polling intervals
|
||||
shouldPollActiveJobs.value = true;
|
||||
};
|
||||
|
||||
export const stopPollingActiveJobs = () => {
|
||||
console.info("stop polling Active Jobs");
|
||||
shouldPollActiveJobs.value = false;
|
||||
};
|
||||
@@ -0,0 +1,92 @@
|
||||
import { signal } from "@preact/signals-core";
|
||||
import { AssetType } from "~/enums";
|
||||
import { MediaInfo } from "~/pages/PageEnigma/models/movies";
|
||||
import { AudioMediaItem } from "~/pages/PageEnigma/models";
|
||||
|
||||
// Signal for background loading User's Movies
|
||||
export const userMovies = signal<MediaInfo[] | undefined>(undefined);
|
||||
|
||||
export const setUserMovies = (newSet: MediaInfo[]) => {
|
||||
userMovies.value = newSet;
|
||||
};
|
||||
|
||||
//Signal for background loading User's Audio Items.
|
||||
export const userAudioItems = signal<AudioMediaItem[] | undefined>(undefined);
|
||||
export const setUserAudioItems = (newSet: MediaInfo[]) => {
|
||||
const previousItems = userAudioItems.value ? [...userAudioItems.value] : [];
|
||||
const morphedNewSet = newSet.map((item) => {
|
||||
const morphedItem: AudioMediaItem = {
|
||||
version: 1,
|
||||
type: AssetType.AUDIO,
|
||||
category: getCategory(item),
|
||||
media_id: item.token,
|
||||
object_uuid: item.token,
|
||||
name: getTitle(item),
|
||||
description: item.maybe_text_transcript || "",
|
||||
publicBucketPath: item.public_bucket_path,
|
||||
length: getLength(item),
|
||||
thumbnail: "/resources/placeholders/audio_placeholder.png",
|
||||
isMine: true,
|
||||
isNew:
|
||||
previousItems.length > 0
|
||||
? checkIsNew(previousItems, item.token)
|
||||
: false,
|
||||
// isBookmarked?: boolean;
|
||||
};
|
||||
return morphedItem;
|
||||
});
|
||||
userAudioItems.value = morphedNewSet;
|
||||
};
|
||||
|
||||
export const cancelNewFromAudioItem = (mediaId: string) => {
|
||||
if (!userAudioItems.value || userAudioItems.value.length === 0) {
|
||||
return;
|
||||
}
|
||||
const newList = userAudioItems.value.map((item) => {
|
||||
if (item.media_id === mediaId) item.isNew = false;
|
||||
return item;
|
||||
});
|
||||
userAudioItems.value = [...newList];
|
||||
};
|
||||
|
||||
export const updateAudioItemLength = (mediaId: string, duration: number) => {
|
||||
if (!userAudioItems.value || userAudioItems.value.length === 0) {
|
||||
return;
|
||||
}
|
||||
const newList = userAudioItems.value.map((item) => {
|
||||
if (item.media_id === mediaId) item.length = duration;
|
||||
return item;
|
||||
});
|
||||
userAudioItems.value = [...newList];
|
||||
};
|
||||
|
||||
// Helper functions to massage Media Info to AudioMediaItem
|
||||
// that's usable as a signal.
|
||||
function getTitle(item: MediaInfo) {
|
||||
// console.log(item);
|
||||
if (item.maybe_title) return item.maybe_title;
|
||||
if (item.origin && item.origin.maybe_model && item.origin.maybe_model.title)
|
||||
return item.origin.maybe_model.title;
|
||||
return "Media Audio";
|
||||
}
|
||||
function getCategory(item: MediaInfo) {
|
||||
if (
|
||||
item.origin &&
|
||||
item.origin.product_category &&
|
||||
item.origin.product_category !== "unknown"
|
||||
)
|
||||
return item.origin.product_category;
|
||||
if (item.origin_category) return item.origin_category;
|
||||
return "unknown";
|
||||
}
|
||||
function checkIsNew(previousItems: AudioMediaItem[], token: string) {
|
||||
const foundItem = previousItems.find((item) => {
|
||||
return token === item.media_id && item.isNew !== true;
|
||||
});
|
||||
return foundItem === undefined;
|
||||
}
|
||||
function getLength(item: MediaInfo) {
|
||||
return item.maybe_duration_millis
|
||||
? (item.maybe_duration_millis / 1000) * 60
|
||||
: undefined;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
export * from "./authentication";
|
||||
export * from "./activeJobs";
|
||||
export * from "./backgroundLoadedMedia";
|
||||
export * from "./global";
|
||||
export * from "./loadingBar";
|
||||
export * from "./scene"; // THIS IS SHOULE BE PAGE LEVEL
|
||||
|
||||
Reference in new issue
Block a user