mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-06 02:00:46 +00:00
Merge branch 'feature/mvp' into fixes/select_modal_overhaul
This commit is contained in:
commit
182849cd00
20 files changed
+504
-197
No files matched your search
@@ -1,64 +1,73 @@
|
||||
import { ApiConfig } from "@storyteller/components";
|
||||
import { UserDetailsLight } from "../_common/UserDetailsLight";
|
||||
|
||||
export interface ListCommentsSuccessResponse {
|
||||
success: boolean,
|
||||
comments: Array<Comment>
|
||||
success: boolean;
|
||||
comments: Array<Comment>;
|
||||
}
|
||||
|
||||
export interface Comment {
|
||||
token: string,
|
||||
token: string;
|
||||
user: UserDetailsLight;
|
||||
user_token: string;
|
||||
username: string;
|
||||
user_display_name: string;
|
||||
user_gravatar_hash: string;
|
||||
|
||||
user_token: string,
|
||||
username: string,
|
||||
user_display_name: string,
|
||||
user_gravatar_hash: string,
|
||||
comment_markdown: string;
|
||||
comment_rendered_html: string;
|
||||
|
||||
comment_markdown: string,
|
||||
comment_rendered_html: string,
|
||||
|
||||
created_at: Date,
|
||||
updated_at: Date,
|
||||
maybe_edited_at?: Date,
|
||||
created_at: Date;
|
||||
updated_at: Date;
|
||||
maybe_edited_at?: Date;
|
||||
}
|
||||
|
||||
export interface ListCommentsErrorResponse {
|
||||
success: boolean,
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
type ListCommentsResponse = ListCommentsSuccessResponse | ListCommentsErrorResponse;
|
||||
type ListCommentsResponse =
|
||||
| ListCommentsSuccessResponse
|
||||
| ListCommentsErrorResponse;
|
||||
|
||||
export function ListCommentsIsOk(response: ListCommentsResponse): response is ListCommentsSuccessResponse {
|
||||
export function ListCommentsIsOk(
|
||||
response: ListCommentsResponse
|
||||
): response is ListCommentsSuccessResponse {
|
||||
return response?.success === true;
|
||||
}
|
||||
|
||||
export function ListCommentsIsError(response: ListCommentsResponse): response is ListCommentsErrorResponse {
|
||||
export function ListCommentsIsError(
|
||||
response: ListCommentsResponse
|
||||
): response is ListCommentsErrorResponse {
|
||||
return response?.success === false;
|
||||
}
|
||||
|
||||
export async function ListComments(entityType: string, entityToken: string) : Promise<ListCommentsResponse>
|
||||
{
|
||||
export async function ListComments(
|
||||
entityType: string,
|
||||
entityToken: string
|
||||
): Promise<ListCommentsResponse> {
|
||||
const endpoint = new ApiConfig().commentList(entityType, entityToken);
|
||||
|
||||
return await fetch(endpoint, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
if (!res) {
|
||||
return { success : false }; // TODO: This loses error semantics and is deprecated
|
||||
}
|
||||
|
||||
if (res && 'success' in res) {
|
||||
return res;
|
||||
} else {
|
||||
return { success : false };
|
||||
}
|
||||
return await fetch(endpoint, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.catch(e => {
|
||||
return { success : false };
|
||||
});
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
if (!res) {
|
||||
return { success: false }; // TODO: This loses error semantics and is deprecated
|
||||
}
|
||||
|
||||
if (res && "success" in res) {
|
||||
return res;
|
||||
} else {
|
||||
return { success: false };
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
return { success: false };
|
||||
});
|
||||
}
|
||||
@@ -8,6 +8,7 @@ export interface MediaFile {
|
||||
token: string;
|
||||
media_type: MediaFileType;
|
||||
public_bucket_path: string;
|
||||
maybe_batch_token: string;
|
||||
maybe_creator_user: UserDetailsLight | null;
|
||||
creator_set_visibility: string;
|
||||
created_at: Date;
|
||||
@@ -25,11 +26,17 @@ export interface MediaFile {
|
||||
export interface GetMediaRequest {}
|
||||
|
||||
export interface GetMediaResponse {
|
||||
success: boolean,
|
||||
media_file?: MediaFile
|
||||
success: boolean;
|
||||
media_file?: MediaFile;
|
||||
}
|
||||
|
||||
export const GetMedia = MakeRequest<string, GetMediaRequest, GetMediaResponse,{}>({
|
||||
export const GetMedia = MakeRequest<
|
||||
string,
|
||||
GetMediaRequest,
|
||||
GetMediaResponse,
|
||||
{}
|
||||
>({
|
||||
method: "GET",
|
||||
routingFunction: (mediaFileToken: string) => `/v1/media_files/file/${mediaFileToken}`,
|
||||
});
|
||||
routingFunction: (mediaFileToken: string) =>
|
||||
`/v1/media_files/file/${mediaFileToken}`,
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
import MakeRequest from "../MakeRequest";
|
||||
import { MediaFile } from "./GetMedia";
|
||||
|
||||
export interface GetMediaBatchImagesRequest {}
|
||||
|
||||
export interface GetMediaBatchImagesResponse {
|
||||
success: boolean;
|
||||
results: MediaFile[];
|
||||
}
|
||||
|
||||
export interface GetMediaBatchImagesParams {}
|
||||
|
||||
export const GetMediaBatchImages = MakeRequest<
|
||||
string,
|
||||
GetMediaBatchImagesRequest,
|
||||
GetMediaBatchImagesResponse,
|
||||
GetMediaBatchImagesParams
|
||||
>({
|
||||
method: "GET",
|
||||
routingFunction: (batchToken: string) =>
|
||||
`/v1/media_files/batch/${batchToken}`,
|
||||
});
|
||||
@@ -1,21 +1,26 @@
|
||||
import MakeRequest from "../MakeRequest";
|
||||
import { MediaFile } from "./GetMedia";
|
||||
import { MediaFileType } from "../_common/enums/MediaFileType";
|
||||
import { Pagination } from "../_common/SharedFetchTypes";
|
||||
|
||||
export interface GetMediaRequest {}
|
||||
|
||||
export interface GetMediaListResponse {
|
||||
pagination: Pagination,
|
||||
success: boolean,
|
||||
results: MediaFile[]
|
||||
pagination: Pagination;
|
||||
success: boolean;
|
||||
results: MediaFile[];
|
||||
}
|
||||
|
||||
export interface GetMediaParams {
|
||||
page_index: number,
|
||||
page_index: number;
|
||||
}
|
||||
|
||||
export const GetMediaByUser = MakeRequest<string, GetMediaRequest, GetMediaListResponse, GetMediaParams>({
|
||||
export const GetMediaByUser = MakeRequest<
|
||||
string,
|
||||
GetMediaRequest,
|
||||
GetMediaListResponse,
|
||||
GetMediaParams
|
||||
>({
|
||||
method: "GET",
|
||||
routingFunction: (userToken: string) => `/v1/media_files/list/user/${userToken}`,
|
||||
});
|
||||
routingFunction: (userToken: string) =>
|
||||
`/v1/media_files/list/user/${userToken}`,
|
||||
});
|
||||
@@ -22,6 +22,7 @@ export interface MediaFile {
|
||||
maybe_cover_image_public_bucket_path: string;
|
||||
};
|
||||
maybe_text_transcript?: string;
|
||||
maybe_batch_token?: string;
|
||||
}
|
||||
|
||||
export enum MediaFileLookupError {
|
||||
|
||||
@@ -20,6 +20,7 @@ export interface ByQueueStats {
|
||||
pending_svc_jobs: number,
|
||||
pending_tacotron2_jobs: number,
|
||||
pending_voice_designer: number,
|
||||
pending_stable_diffusion: number,
|
||||
}
|
||||
|
||||
export interface LegacyTtsInfo {
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
"react-twitch-embed": "^3.0.1",
|
||||
"react-wavesurfer.js": "^0.0.8",
|
||||
"sass": "^1.57.1",
|
||||
"swiper": "^11.0.5",
|
||||
"typescript": "^3.7.5",
|
||||
"universal-cookie": "^6.1.1",
|
||||
"uuid": "^8.3.2",
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
+127
-118
@@ -60,11 +60,9 @@ import { VoiceConversionModelUploadJob } from "@storyteller/components/src/jobs/
|
||||
import { VoiceConversionModelListItem } from "@storyteller/components/src/api/voice_conversion/ListVoiceConversionModels";
|
||||
import HttpBackend from "i18next-http-backend";
|
||||
|
||||
import {
|
||||
InferenceJobs,
|
||||
ModalProvider,
|
||||
SessionProvider,
|
||||
} from "components/providers";
|
||||
import { SessionProvider, InferenceJobs } from "components/providers";
|
||||
//this caused an error for me (Bombay) so i had to import it as a default
|
||||
import ModalProvider from "components/providers/ModalProvider";
|
||||
|
||||
// NB: We're transitioning over to this instance of i18n-next that loads translations over HTTP from Json Files.
|
||||
// The old i18n-next instance (see below) bakes in translations into the compiled javascript blob.
|
||||
@@ -210,8 +208,8 @@ function initInferenceJobsByCategoryMap(): Map<
|
||||
inferenceJobsByCategory.set(FrontendInferenceJobType.VideoMotionCapture, []);
|
||||
inferenceJobsByCategory.set(FrontendInferenceJobType.ImageGeneration, []);
|
||||
inferenceJobsByCategory.set(FrontendInferenceJobType.ConvertFbxtoGltf, []);
|
||||
inferenceJobsByCategory.set(FrontendInferenceJobType.VideoWorkflow, [])
|
||||
inferenceJobsByCategory.set(FrontendInferenceJobType.VideoStyleTransfer, [])
|
||||
inferenceJobsByCategory.set(FrontendInferenceJobType.VideoWorkflow, []);
|
||||
inferenceJobsByCategory.set(FrontendInferenceJobType.VideoStyleTransfer, []);
|
||||
return inferenceJobsByCategory;
|
||||
}
|
||||
|
||||
@@ -348,7 +346,7 @@ class App extends React.Component<Props, State> {
|
||||
// state needs to be set here, or if instead can be refreshed locally on
|
||||
// the page where the new "state" needed
|
||||
|
||||
if (this.state.sessionFetched === false){
|
||||
if (this.state.sessionFetched === false) {
|
||||
this.setState({ sessionFetched: true });
|
||||
}
|
||||
|
||||
@@ -856,7 +854,7 @@ class App extends React.Component<Props, State> {
|
||||
{...{
|
||||
enqueue: this.enqueueInferenceJob,
|
||||
byCategory: this.state.inferenceJobsByCategory,
|
||||
inferenceJobs: this.state.inferenceJobs
|
||||
inferenceJobs: this.state.inferenceJobs,
|
||||
}}
|
||||
>
|
||||
<SessionProvider
|
||||
@@ -867,115 +865,126 @@ class App extends React.Component<Props, State> {
|
||||
sessionFetched: this.state.sessionFetched,
|
||||
}}
|
||||
>
|
||||
|
||||
<ModalProvider>
|
||||
<Switch>
|
||||
<Route path="/">
|
||||
<PageContainer
|
||||
sessionWrapper={this.state.sessionWrapper}
|
||||
querySessionAction={this.querySession}
|
||||
sessionSubscriptionsWrapper={
|
||||
this.state.sessionSubscriptionsWrapper
|
||||
}
|
||||
querySessionSubscriptionsAction={
|
||||
this.querySessionSubscriptions
|
||||
}
|
||||
isShowingVocodesNotice={
|
||||
this.state.isShowingVocodesNotice
|
||||
}
|
||||
clearVocodesNotice={this.clearVocodesNotice}
|
||||
isShowingLangaugeNotice={
|
||||
this.state.isShowingLanguageNotice
|
||||
}
|
||||
clearLanguageNotice={this.clearLanguageNotice}
|
||||
displayLanguage={this.state.displayLanguage}
|
||||
primaryLanguageCode={this.state.primaryLanguageCode}
|
||||
isShowingTwitchTtsNotice={
|
||||
this.state.isShowingTwitchTtsNotice
|
||||
}
|
||||
clearTwitchTtsNotice={this.clearTwitchTtsNotice}
|
||||
isShowingPleaseFollowNotice={
|
||||
this.state.isShowingPleaseFollowNotice
|
||||
}
|
||||
clearPleaseFollowNotice={this.clearPleaseFollowNotice}
|
||||
isShowingBootstrapLanguageNotice={
|
||||
this.state.isShowingBootstrapLanguageNotice
|
||||
}
|
||||
clearBootstrapLanguageNotice={
|
||||
this.clearBootstrapLanguageNotice
|
||||
}
|
||||
enqueueInferenceJob={this.enqueueInferenceJob}
|
||||
inferenceJobs={this.state.inferenceJobs}
|
||||
inferenceJobsByCategory={
|
||||
this.state.inferenceJobsByCategory
|
||||
}
|
||||
enqueueTtsJob={this.enqueueTtsJob}
|
||||
ttsInferenceJobs={this.state.ttsInferenceJobs}
|
||||
enqueueW2lJob={this.enqueueW2lJob}
|
||||
w2lInferenceJobs={this.state.w2lInferenceJobs}
|
||||
enqueueTtsModelUploadJob={this.enqueueTtsModelUploadJob}
|
||||
ttsModelUploadJobs={this.state.ttsModelUploadJobs}
|
||||
enqueueW2lTemplateUploadJob={
|
||||
this.enqueueW2lTemplateUploadJob
|
||||
}
|
||||
w2lTemplateUploadJobs={this.state.w2lTemplateUploadJobs}
|
||||
enqueueVocoderUploadJob={this.enqueueVocoderUploadJob}
|
||||
vocoderUploadJobs={this.state.vocoderUploadJobs}
|
||||
enqueueVoiceConversionModelUploadJob={
|
||||
this.enqueueVoiceConversionModelUploadJob
|
||||
}
|
||||
voiceConversionModelUploadJobs={
|
||||
this.state.voiceConversionModelUploadJobs
|
||||
}
|
||||
textBuffer={this.state.textBuffer}
|
||||
setTextBuffer={this.setTextBuffer}
|
||||
clearTextBuffer={this.clearTextBuffer}
|
||||
ttsModels={this.props.allTtsModels}
|
||||
setTtsModels={this.props.setAllTtsModels}
|
||||
allTtsCategories={this.props.allTtsCategories}
|
||||
setAllTtsCategories={this.props.setAllTtsCategories}
|
||||
computedTtsCategoryAssignments={
|
||||
this.props.computedTtsCategoryAssignments
|
||||
}
|
||||
setComputedTtsCategoryAssignments={
|
||||
this.props.setComputedTtsCategoryAssignments
|
||||
}
|
||||
allTtsCategoriesByTokenMap={
|
||||
this.props.allTtsCategoriesByTokenMap
|
||||
}
|
||||
allTtsModelsByTokenMap={
|
||||
this.props.allTtsModelsByTokenMap
|
||||
}
|
||||
ttsModelsByCategoryToken={
|
||||
this.props.ttsModelsByCategoryToken
|
||||
}
|
||||
dropdownCategories={this.props.dropdownCategories}
|
||||
setDropdownCategories={this.props.setDropdownCategories}
|
||||
selectedCategories={this.props.selectedCategories}
|
||||
setSelectedCategories={this.props.setSelectedCategories}
|
||||
maybeSelectedTtsModel={this.props.maybeSelectedTtsModel}
|
||||
setMaybeSelectedTtsModel={
|
||||
this.props.setMaybeSelectedTtsModel
|
||||
}
|
||||
selectedTtsLanguageScope={
|
||||
this.props.selectedTtsLanguageScope
|
||||
}
|
||||
setSelectedTtsLanguageScope={
|
||||
this.props.setSelectedTtsLanguageScope
|
||||
}
|
||||
voiceConversionModels={this.state.voiceConversionModels}
|
||||
setVoiceConversionModels={
|
||||
this.setAllVoiceConversionModels
|
||||
}
|
||||
maybeSelectedVoiceConversionModel={
|
||||
this.state.maybeSelectedVoiceConversionModel
|
||||
}
|
||||
setMaybeSelectedVoiceConversionModel={
|
||||
this.setMaybeSelectedVoiceConversionModel
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
</Switch>
|
||||
<ModalProvider>
|
||||
<Switch>
|
||||
<Route path="/">
|
||||
<PageContainer
|
||||
sessionWrapper={this.state.sessionWrapper}
|
||||
querySessionAction={this.querySession}
|
||||
sessionSubscriptionsWrapper={
|
||||
this.state.sessionSubscriptionsWrapper
|
||||
}
|
||||
querySessionSubscriptionsAction={
|
||||
this.querySessionSubscriptions
|
||||
}
|
||||
isShowingVocodesNotice={
|
||||
this.state.isShowingVocodesNotice
|
||||
}
|
||||
clearVocodesNotice={this.clearVocodesNotice}
|
||||
isShowingLangaugeNotice={
|
||||
this.state.isShowingLanguageNotice
|
||||
}
|
||||
clearLanguageNotice={this.clearLanguageNotice}
|
||||
displayLanguage={this.state.displayLanguage}
|
||||
primaryLanguageCode={this.state.primaryLanguageCode}
|
||||
isShowingTwitchTtsNotice={
|
||||
this.state.isShowingTwitchTtsNotice
|
||||
}
|
||||
clearTwitchTtsNotice={this.clearTwitchTtsNotice}
|
||||
isShowingPleaseFollowNotice={
|
||||
this.state.isShowingPleaseFollowNotice
|
||||
}
|
||||
clearPleaseFollowNotice={this.clearPleaseFollowNotice}
|
||||
isShowingBootstrapLanguageNotice={
|
||||
this.state.isShowingBootstrapLanguageNotice
|
||||
}
|
||||
clearBootstrapLanguageNotice={
|
||||
this.clearBootstrapLanguageNotice
|
||||
}
|
||||
enqueueInferenceJob={this.enqueueInferenceJob}
|
||||
inferenceJobs={this.state.inferenceJobs}
|
||||
inferenceJobsByCategory={
|
||||
this.state.inferenceJobsByCategory
|
||||
}
|
||||
enqueueTtsJob={this.enqueueTtsJob}
|
||||
ttsInferenceJobs={this.state.ttsInferenceJobs}
|
||||
enqueueW2lJob={this.enqueueW2lJob}
|
||||
w2lInferenceJobs={this.state.w2lInferenceJobs}
|
||||
enqueueTtsModelUploadJob={
|
||||
this.enqueueTtsModelUploadJob
|
||||
}
|
||||
ttsModelUploadJobs={this.state.ttsModelUploadJobs}
|
||||
enqueueW2lTemplateUploadJob={
|
||||
this.enqueueW2lTemplateUploadJob
|
||||
}
|
||||
w2lTemplateUploadJobs={
|
||||
this.state.w2lTemplateUploadJobs
|
||||
}
|
||||
enqueueVocoderUploadJob={this.enqueueVocoderUploadJob}
|
||||
vocoderUploadJobs={this.state.vocoderUploadJobs}
|
||||
enqueueVoiceConversionModelUploadJob={
|
||||
this.enqueueVoiceConversionModelUploadJob
|
||||
}
|
||||
voiceConversionModelUploadJobs={
|
||||
this.state.voiceConversionModelUploadJobs
|
||||
}
|
||||
textBuffer={this.state.textBuffer}
|
||||
setTextBuffer={this.setTextBuffer}
|
||||
clearTextBuffer={this.clearTextBuffer}
|
||||
ttsModels={this.props.allTtsModels}
|
||||
setTtsModels={this.props.setAllTtsModels}
|
||||
allTtsCategories={this.props.allTtsCategories}
|
||||
setAllTtsCategories={this.props.setAllTtsCategories}
|
||||
computedTtsCategoryAssignments={
|
||||
this.props.computedTtsCategoryAssignments
|
||||
}
|
||||
setComputedTtsCategoryAssignments={
|
||||
this.props.setComputedTtsCategoryAssignments
|
||||
}
|
||||
allTtsCategoriesByTokenMap={
|
||||
this.props.allTtsCategoriesByTokenMap
|
||||
}
|
||||
allTtsModelsByTokenMap={
|
||||
this.props.allTtsModelsByTokenMap
|
||||
}
|
||||
ttsModelsByCategoryToken={
|
||||
this.props.ttsModelsByCategoryToken
|
||||
}
|
||||
dropdownCategories={this.props.dropdownCategories}
|
||||
setDropdownCategories={
|
||||
this.props.setDropdownCategories
|
||||
}
|
||||
selectedCategories={this.props.selectedCategories}
|
||||
setSelectedCategories={
|
||||
this.props.setSelectedCategories
|
||||
}
|
||||
maybeSelectedTtsModel={
|
||||
this.props.maybeSelectedTtsModel
|
||||
}
|
||||
setMaybeSelectedTtsModel={
|
||||
this.props.setMaybeSelectedTtsModel
|
||||
}
|
||||
selectedTtsLanguageScope={
|
||||
this.props.selectedTtsLanguageScope
|
||||
}
|
||||
setSelectedTtsLanguageScope={
|
||||
this.props.setSelectedTtsLanguageScope
|
||||
}
|
||||
voiceConversionModels={
|
||||
this.state.voiceConversionModels
|
||||
}
|
||||
setVoiceConversionModels={
|
||||
this.setAllVoiceConversionModels
|
||||
}
|
||||
maybeSelectedVoiceConversionModel={
|
||||
this.state.maybeSelectedVoiceConversionModel
|
||||
}
|
||||
setMaybeSelectedVoiceConversionModel={
|
||||
this.setMaybeSelectedVoiceConversionModel
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
</Switch>
|
||||
</ModalProvider>
|
||||
</SessionProvider>
|
||||
</InferenceJobs>
|
||||
|
||||
@@ -146,6 +146,7 @@ export default function SideNav({
|
||||
pending_svc_jobs: 0,
|
||||
pending_tacotron2_jobs: 0,
|
||||
pending_voice_designer: 0,
|
||||
pending_stable_diffusion: 0,
|
||||
},
|
||||
},
|
||||
legacy_tts: {
|
||||
@@ -192,6 +193,7 @@ export default function SideNav({
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
history.push("/login");
|
||||
handleNavLinkClick();
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
@@ -204,6 +206,7 @@ export default function SideNav({
|
||||
small
|
||||
onClick={() => {
|
||||
history.push("/signup");
|
||||
handleNavLinkClick();
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
@@ -228,6 +231,7 @@ export default function SideNav({
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
history.push(url);
|
||||
handleNavLinkClick();
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
@@ -242,6 +246,7 @@ export default function SideNav({
|
||||
variant="danger"
|
||||
onClick={async () => {
|
||||
await logoutHandler();
|
||||
handleNavLinkClick();
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
@@ -312,10 +317,7 @@ export default function SideNav({
|
||||
activeClassName="active-link"
|
||||
onClick={handleNavLinkClick}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faFilms}
|
||||
className="sidebar-heading-icon"
|
||||
/>
|
||||
<FontAwesomeIcon icon={faFilms} className="sidebar-heading-icon" />
|
||||
{t("videoStryleTransfer")}
|
||||
</NavLink>
|
||||
</li>
|
||||
@@ -566,6 +568,12 @@ export default function SideNav({
|
||||
{queueStats.inference.by_queue.pending_svc_jobs}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
Image Generation:{" "}
|
||||
<span className="text-red">
|
||||
{queueStats.inference.by_queue.pending_stable_diffusion}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
{t("queueFaceAnimator")}:{" "}
|
||||
<span className="text-red">
|
||||
|
||||
@@ -26,6 +26,12 @@ export default function SideNavJobs({ byQueue, t, ttsQueuedCount }: Props) {
|
||||
{ byQueue.pending_svc_jobs }
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
Image Generation:{" "}
|
||||
<span className="text-red">
|
||||
{ byQueue.pending_stable_diffusion }
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
{t("queueFaceAnimator")}:{" "}
|
||||
<span className="text-red">
|
||||
|
||||
@@ -2,7 +2,6 @@ import React from "react";
|
||||
import { formatDistance } from "date-fns";
|
||||
import { Comment } from "@storyteller/components/src/api/comments/ListComments";
|
||||
import { SafeDeleteCommentButton } from "./SafeDeleteCommentButton";
|
||||
|
||||
import { Gravatar } from "@storyteller/components/src/elements/Gravatar";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useSession } from "hooks";
|
||||
@@ -57,12 +56,22 @@ function CommentList(props: Props) {
|
||||
}
|
||||
|
||||
let profileLink = `/profile/${comment.username}`;
|
||||
let username = comment?.user.username;
|
||||
let gravatarHash = comment?.user.gravatar_hash;
|
||||
let avatarIndex = comment?.user.default_avatar.image_index;
|
||||
let backgroundColorIndex = comment?.user.default_avatar.color_index;
|
||||
|
||||
rows.push(
|
||||
<tr key={comment.token}>
|
||||
<td className="px-0">
|
||||
<div className="d-flex gap-3 py-3">
|
||||
<Gravatar size={42} email_hash={comment.user_gravatar_hash} />
|
||||
<Gravatar
|
||||
email_hash={gravatarHash || ""}
|
||||
username={username || ""}
|
||||
avatarIndex={avatarIndex || 0}
|
||||
backgroundIndex={backgroundColorIndex || 0}
|
||||
size={40}
|
||||
/>
|
||||
<div>
|
||||
<div className="d-flex gap-2 align-items-center flex-wrap">
|
||||
<Link to={profileLink} className="fw-medium text-white">
|
||||
|
||||
@@ -24,9 +24,8 @@ function CreateCommentForm(props: Props) {
|
||||
const { entityType, entityToken, loadComments } = props;
|
||||
|
||||
const [commentMarkdown, setCommentMarkdown] = useState<string>("");
|
||||
const [uuidIdempotencyToken, setUuidIdempotencyToken] = useState<string>(
|
||||
uuidv4()
|
||||
);
|
||||
const [uuidIdempotencyToken, setUuidIdempotencyToken] =
|
||||
useState<string>(uuidv4());
|
||||
const [buttonVisible, setButtonVisible] = useState(false);
|
||||
|
||||
const postComment = useCallback(async () => {
|
||||
@@ -91,8 +90,21 @@ function CreateCommentForm(props: Props) {
|
||||
|
||||
let gravatarHash = user?.email_gravatar_hash;
|
||||
let gravatar = <span />;
|
||||
let username = user?.username;
|
||||
let emailHash = user?.email_gravatar_hash;
|
||||
let avatarIndex = user?.core_info.default_avatar.image_index;
|
||||
let backgroundColorIndex = user?.core_info.default_avatar.color_index;
|
||||
|
||||
if (gravatarHash !== undefined) {
|
||||
gravatar = <Gravatar email_hash={gravatarHash} size={40} />;
|
||||
gravatar = (
|
||||
<Gravatar
|
||||
email_hash={emailHash || ""}
|
||||
username={username || ""}
|
||||
avatarIndex={avatarIndex || 0}
|
||||
backgroundIndex={backgroundColorIndex || 0}
|
||||
size={40}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
let commentButton = <></>;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Link, useParams } from "react-router-dom";
|
||||
import MediaAudioComponent from "./MediaAudioComponent";
|
||||
import MediaVideoComponent from "./MediaVideoComponent";
|
||||
@@ -32,9 +32,10 @@ import { Badge, Input, Modal } from "components/common";
|
||||
import BookmarkButton from "components/common/BookmarkButton";
|
||||
import LikeButton from "components/common/LikeButton";
|
||||
import { useBookmarks, useMedia, useRatings, useSession } from "hooks";
|
||||
import SdCoverImagePanel from "../weight/cover_image_panels/SdCoverImagePanel";
|
||||
import { WeightCategory } from "@storyteller/components/src/api/_common/enums/WeightCategory";
|
||||
import Iframe from "react-iframe";
|
||||
import SdBatchMediaPanel from "./components/SdBatchMediaPanel/SdBatchMediaPanel";
|
||||
import { GetMediaBatchImages } from "@storyteller/components/src/api/media_files/GetMediaBatchImages";
|
||||
|
||||
export default function MediaPage() {
|
||||
const { canEditTtsModel, user } = useSession();
|
||||
@@ -51,11 +52,31 @@ export default function MediaPage() {
|
||||
ratings.gather({ res, key: "token" });
|
||||
},
|
||||
});
|
||||
|
||||
const batchToken = mediaFile?.maybe_batch_token;
|
||||
const [images, setImages] = useState<string[]>([]);
|
||||
const bucketConfig = new BucketConfig();
|
||||
const timeCreated = moment(mediaFile?.created_at || "").fromNow();
|
||||
const dateCreated = moment(mediaFile?.created_at || "").format("LLL");
|
||||
const [buttonLabel, setButtonLabel] = useState("Copy");
|
||||
|
||||
useEffect(() => {
|
||||
if (batchToken) {
|
||||
GetMediaBatchImages(batchToken, {}, {})
|
||||
.then(response => {
|
||||
if (response.success) {
|
||||
const imageUrls = response.results.map(result =>
|
||||
bucketConfig.getGcsUrl(result.public_bucket_path)
|
||||
);
|
||||
setImages(imageUrls);
|
||||
} else {
|
||||
console.error("Failed to fetch batch images");
|
||||
}
|
||||
})
|
||||
.catch(err => console.error("Error fetching batch images:", err));
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [batchToken]);
|
||||
|
||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||
|
||||
const closeDeleteModal = () => setIsDeleteModalOpen(false);
|
||||
@@ -98,11 +119,17 @@ export default function MediaPage() {
|
||||
);
|
||||
|
||||
case MediaFileType.Image:
|
||||
let sdMediaImage = "/images/avatars/default-pfp.png";
|
||||
let sdMediaImage = ["/images/avatars/default-pfp.png"];
|
||||
if (mediaFile.public_bucket_path) {
|
||||
sdMediaImage = bucketConfig.getGcsUrl(mediaFile.public_bucket_path);
|
||||
sdMediaImage = [bucketConfig.getGcsUrl(mediaFile.public_bucket_path)];
|
||||
}
|
||||
return <SdCoverImagePanel src={sdMediaImage} />;
|
||||
|
||||
return (
|
||||
<SdBatchMediaPanel
|
||||
key={images.length}
|
||||
images={mediaFile.maybe_batch_token ? images : sdMediaImage}
|
||||
/>
|
||||
);
|
||||
case MediaFileType.BVH:
|
||||
const bvhUrl = bucketConfig.getGcsUrl(mediaFile.public_bucket_path);
|
||||
return (
|
||||
@@ -335,8 +362,6 @@ export default function MediaPage() {
|
||||
|
||||
const subtitleDivider = <span className="opacity-25 fs-5 fw-light">|</span>;
|
||||
|
||||
const bucketConfig = new BucketConfig();
|
||||
|
||||
let weightUsedCoverImage = "/images/avatars/default-pfp.png";
|
||||
if (
|
||||
mediaFile?.maybe_model_weight_info !== null &&
|
||||
@@ -438,7 +463,9 @@ export default function MediaPage() {
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
{mediaFile?.media_type === MediaFileType.BVH || mediaFile?.media_type === MediaFileType.GLTF || mediaFile?.media_type === MediaFileType.GLB ? (
|
||||
{mediaFile?.media_type === MediaFileType.BVH ||
|
||||
mediaFile?.media_type === MediaFileType.GLTF ||
|
||||
mediaFile?.media_type === MediaFileType.GLB ? (
|
||||
<Button
|
||||
{...{
|
||||
icon: faVideoPlus,
|
||||
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
@import "scss/colours.scss";
|
||||
|
||||
.swiper {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
margin: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.swiper-slide {
|
||||
background-size: contain;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.secondSwiper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.landscape {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.portrait {
|
||||
height: 70vh;
|
||||
}
|
||||
|
||||
// .secondSwiper .swiper-slide {
|
||||
// height: 100%;
|
||||
// }
|
||||
|
||||
.firstSwiper {
|
||||
height: 20%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.firstSwiper .swiper-slide {
|
||||
transition: 0.2s;
|
||||
width: 25%;
|
||||
height: 100%;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.firstSwiper .swiper-slide-thumb-active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.swiper-slide img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
border-radius: 0.5rem;
|
||||
cursor: pointer;
|
||||
transition: 0.2s;
|
||||
background-color: $panel;
|
||||
}
|
||||
|
||||
.swiper-button-prev,
|
||||
.swiper-button-next {
|
||||
color: $white;
|
||||
opacity: 0.5;
|
||||
transition: 0.2s;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.portrait {
|
||||
height: 550px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
.portrait {
|
||||
height: 400px;
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
import { Panel } from "components/common";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Swiper, SwiperSlide } from "swiper/react";
|
||||
import "swiper/css";
|
||||
import "swiper/css/free-mode";
|
||||
import "swiper/css/navigation";
|
||||
import "swiper/css/thumbs";
|
||||
import { FreeMode, Navigation, Thumbs } from "swiper/modules";
|
||||
import { Swiper as SwiperType } from "swiper/types";
|
||||
import "./SdBatchMediaPanel.scss";
|
||||
|
||||
interface SdCoverImagePanelProps {
|
||||
images: string[];
|
||||
}
|
||||
|
||||
export default function SdBatchMediaPanel({ images }: SdCoverImagePanelProps) {
|
||||
const [thumbsSwiper, setThumbsSwiper] = useState<SwiperType | null>(null);
|
||||
const [forceUpdateKey, setForceUpdateKey] = useState(0);
|
||||
const [isPortrait, setIsPortrait] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
images.forEach(imgSrc => {
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
// If any image is portrait, update isPortrait to true
|
||||
if (img.height > img.width && !isPortrait) {
|
||||
setIsPortrait(true);
|
||||
}
|
||||
};
|
||||
img.src = imgSrc;
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [images]);
|
||||
|
||||
useEffect(() => {
|
||||
if (images.length > 0) {
|
||||
// Force re-render of the Swiper component by updating a key
|
||||
setForceUpdateKey(prevKey => prevKey + 1);
|
||||
}
|
||||
}, [images]);
|
||||
|
||||
const handleSwiper = (swiper: SwiperType) => {
|
||||
setThumbsSwiper(swiper);
|
||||
};
|
||||
|
||||
const secondSwiperClass = `secondSwiper ${
|
||||
isPortrait ? "portrait" : "landscape"
|
||||
}`;
|
||||
|
||||
return (
|
||||
<Panel padding={false} clear className="d-flex flex-column gap-3">
|
||||
<Swiper
|
||||
key={forceUpdateKey}
|
||||
loop={images.length > 1}
|
||||
spaceBetween={10}
|
||||
navigation={images.length > 1}
|
||||
thumbs={{ swiper: images.length > 1 ? thumbsSwiper : null }}
|
||||
modules={[FreeMode, Navigation, Thumbs]}
|
||||
className={secondSwiperClass}
|
||||
slidesPerView={1}
|
||||
initialSlide={0}
|
||||
>
|
||||
{images.map((imgSrc, index) => (
|
||||
<SwiperSlide key={index}>
|
||||
<img src={imgSrc} alt={`Slide ${index + 1}`} />
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
{images.length > 1 && (
|
||||
<Swiper
|
||||
onSwiper={handleSwiper}
|
||||
loop={true}
|
||||
spaceBetween={10}
|
||||
slidesPerView={5}
|
||||
freeMode={true}
|
||||
watchSlidesProgress={true}
|
||||
modules={[FreeMode, Navigation, Thumbs]}
|
||||
className="firstSwiper"
|
||||
initialSlide={0}
|
||||
>
|
||||
{images.map((imgSrc, index) => (
|
||||
<SwiperSlide key={index}>
|
||||
<img src={imgSrc} alt={`Thumbnail ${index + 1}`} />
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
)}
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
@@ -197,6 +197,12 @@ export default function WeightPage({
|
||||
return (
|
||||
<div className="d-flex flex-column gap-3">
|
||||
<SdCoverImagePanel src={sdCoverImage} />
|
||||
{descriptionMD !== "" && (
|
||||
<Panel padding={true}>
|
||||
<h5 className="fw-semibold mb-2">Description</h5>
|
||||
<p className="fs-7">{descriptionMD}</p>
|
||||
</Panel>
|
||||
)}
|
||||
{imageGenPanel}
|
||||
</div>
|
||||
);
|
||||
@@ -488,14 +494,15 @@ export default function WeightPage({
|
||||
|
||||
<div className="row g-4">
|
||||
<div className="col-12 col-xl-8 d-flex flex-column gap-3">
|
||||
<div className="media-wrapper">{renderWeightComponent(weight)}</div>
|
||||
{descriptionMD !== "" &&
|
||||
weight.weight_category !== WeightCategory.SD && (
|
||||
<Panel padding={true}>
|
||||
<h5 className="fw-semibold mb-2">Description</h5>
|
||||
<p className="fs-7">{descriptionMD}</p>
|
||||
</Panel>
|
||||
)}
|
||||
|
||||
{descriptionMD !== "" && (
|
||||
<Panel padding={true}>
|
||||
<h4 className="fw-semibold mb-3">Description</h4>
|
||||
<p>{descriptionMD}</p>
|
||||
</Panel>
|
||||
)}
|
||||
<div className="media-wrapper">{renderWeightComponent(weight)}</div>
|
||||
|
||||
<div className="panel p-3 py-4 p-md-4 d-none d-xl-block">
|
||||
<h4 className="fw-semibold mb-3">Comments</h4>
|
||||
|
||||
@@ -6,6 +6,7 @@ MASTER_BRANCH='master'
|
||||
PRODUCTION_BRANCH='production-fakeyou'
|
||||
|
||||
git stash
|
||||
git branch --set-upstream-to="origin/${MASTER_BRANCH}" ${MASTER_BRANCH}
|
||||
git checkout $MASTER_BRANCH
|
||||
git pull
|
||||
|
||||
|
||||
@@ -4484,6 +4484,7 @@ __metadata:
|
||||
react-twitch-embed: ^3.0.1
|
||||
react-wavesurfer.js: ^0.0.8
|
||||
sass: ^1.57.1
|
||||
swiper: ^11.0.5
|
||||
typescript: ^3.7.5
|
||||
universal-cookie: ^6.1.1
|
||||
uuid: ^8.3.2
|
||||
@@ -20178,6 +20179,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"swiper@npm:^11.0.5":
|
||||
version: 11.0.5
|
||||
resolution: "swiper@npm:11.0.5"
|
||||
checksum: 83b125d16733a66ed5f58627c81263116e39eea5cead92667d2e24692ff94632de9aa05fec1ed80dc352779c0d7525e9a18424fce1c39c5e970823414b212edb
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"swiper@npm:^8.3.2":
|
||||
version: 8.3.2
|
||||
resolution: "swiper@npm:8.3.2"
|
||||
|
||||
Reference in new issue
Block a user