diff --git a/packages/components/src/api/comments/ListComments.ts b/packages/components/src/api/comments/ListComments.ts index defb7f79b8..00d45d50df 100644 --- a/packages/components/src/api/comments/ListComments.ts +++ b/packages/components/src/api/comments/ListComments.ts @@ -1,64 +1,73 @@ import { ApiConfig } from "@storyteller/components"; +import { UserDetailsLight } from "../_common/UserDetailsLight"; export interface ListCommentsSuccessResponse { - success: boolean, - comments: Array + success: boolean; + comments: Array; } 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 -{ +export async function ListComments( + entityType: string, + entityToken: string +): Promise { 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 }; + }); } diff --git a/packages/components/src/api/media_files/GetMedia.ts b/packages/components/src/api/media_files/GetMedia.ts index 1e005058b3..1fd37c665a 100644 --- a/packages/components/src/api/media_files/GetMedia.ts +++ b/packages/components/src/api/media_files/GetMedia.ts @@ -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({ +export const GetMedia = MakeRequest< + string, + GetMediaRequest, + GetMediaResponse, + {} +>({ method: "GET", - routingFunction: (mediaFileToken: string) => `/v1/media_files/file/${mediaFileToken}`, -}); \ No newline at end of file + routingFunction: (mediaFileToken: string) => + `/v1/media_files/file/${mediaFileToken}`, +}); diff --git a/packages/components/src/api/media_files/GetMediaBatchImages.ts b/packages/components/src/api/media_files/GetMediaBatchImages.ts new file mode 100644 index 0000000000..8fbb96042a --- /dev/null +++ b/packages/components/src/api/media_files/GetMediaBatchImages.ts @@ -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}`, +}); diff --git a/packages/components/src/api/media_files/GetMediaByUser.ts b/packages/components/src/api/media_files/GetMediaByUser.ts index 340b623620..d5de1230b7 100644 --- a/packages/components/src/api/media_files/GetMediaByUser.ts +++ b/packages/components/src/api/media_files/GetMediaByUser.ts @@ -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({ +export const GetMediaByUser = MakeRequest< + string, + GetMediaRequest, + GetMediaListResponse, + GetMediaParams +>({ method: "GET", - routingFunction: (userToken: string) => `/v1/media_files/list/user/${userToken}`, -}); \ No newline at end of file + routingFunction: (userToken: string) => + `/v1/media_files/list/user/${userToken}`, +}); diff --git a/packages/components/src/api/media_files/GetMediaFile.ts b/packages/components/src/api/media_files/GetMediaFile.ts index 1e87adf051..3d59eb5d6e 100644 --- a/packages/components/src/api/media_files/GetMediaFile.ts +++ b/packages/components/src/api/media_files/GetMediaFile.ts @@ -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 { diff --git a/packages/components/src/api/stats/queues/GetQueueStats.ts b/packages/components/src/api/stats/queues/GetQueueStats.ts index 839dde3a36..dac2474ddc 100644 --- a/packages/components/src/api/stats/queues/GetQueueStats.ts +++ b/packages/components/src/api/stats/queues/GetQueueStats.ts @@ -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 { diff --git a/packages/fakeyou.com/package.json b/packages/fakeyou.com/package.json index 9345c24955..42897d0898 100644 --- a/packages/fakeyou.com/package.json +++ b/packages/fakeyou.com/package.json @@ -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", diff --git a/packages/fakeyou.com/public/images/test/castle-1.png b/packages/fakeyou.com/public/images/test/castle-1.png new file mode 100644 index 0000000000..997b1bf1e7 Binary files /dev/null and b/packages/fakeyou.com/public/images/test/castle-1.png differ diff --git a/packages/fakeyou.com/public/images/test/castle-2.png b/packages/fakeyou.com/public/images/test/castle-2.png new file mode 100644 index 0000000000..b216668716 Binary files /dev/null and b/packages/fakeyou.com/public/images/test/castle-2.png differ diff --git a/packages/fakeyou.com/src/App.tsx b/packages/fakeyou.com/src/App.tsx index c4c690dbd9..29860ebe30 100644 --- a/packages/fakeyou.com/src/App.tsx +++ b/packages/fakeyou.com/src/App.tsx @@ -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 { // 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 { {...{ enqueue: this.enqueueInferenceJob, byCategory: this.state.inferenceJobsByCategory, - inferenceJobs: this.state.inferenceJobs + inferenceJobs: this.state.inferenceJobs, }} > { sessionFetched: this.state.sessionFetched, }} > - - - - - - - + + + + + + diff --git a/packages/fakeyou.com/src/components/layout/SideNav/SideNav.tsx b/packages/fakeyou.com/src/components/layout/SideNav/SideNav.tsx index 6072312522..a25b83b418 100644 --- a/packages/fakeyou.com/src/components/layout/SideNav/SideNav.tsx +++ b/packages/fakeyou.com/src/components/layout/SideNav/SideNav.tsx @@ -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} > - + {t("videoStryleTransfer")} @@ -566,6 +568,12 @@ export default function SideNav({ {queueStats.inference.by_queue.pending_svc_jobs} +
+ Image Generation:{" "} + + {queueStats.inference.by_queue.pending_stable_diffusion} + +
{t("queueFaceAnimator")}:{" "} diff --git a/packages/fakeyou.com/src/components/layout/SideNav/SideNavJobs.tsx b/packages/fakeyou.com/src/components/layout/SideNav/SideNavJobs.tsx index f8654c1a76..be454db528 100644 --- a/packages/fakeyou.com/src/components/layout/SideNav/SideNavJobs.tsx +++ b/packages/fakeyou.com/src/components/layout/SideNav/SideNavJobs.tsx @@ -26,6 +26,12 @@ export default function SideNavJobs({ byQueue, t, ttsQueuedCount }: Props) { { byQueue.pending_svc_jobs }
+
+ Image Generation:{" "} + + { byQueue.pending_stable_diffusion } + +
{t("queueFaceAnimator")}:{" "} diff --git a/packages/fakeyou.com/src/v2/view/_common/comments/CommentList.tsx b/packages/fakeyou.com/src/v2/view/_common/comments/CommentList.tsx index dde414003b..679182970f 100644 --- a/packages/fakeyou.com/src/v2/view/_common/comments/CommentList.tsx +++ b/packages/fakeyou.com/src/v2/view/_common/comments/CommentList.tsx @@ -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(
- +
diff --git a/packages/fakeyou.com/src/v2/view/_common/comments/CreateCommentForm.tsx b/packages/fakeyou.com/src/v2/view/_common/comments/CreateCommentForm.tsx index 00c4640d32..b83bb2bbbd 100644 --- a/packages/fakeyou.com/src/v2/view/_common/comments/CreateCommentForm.tsx +++ b/packages/fakeyou.com/src/v2/view/_common/comments/CreateCommentForm.tsx @@ -24,9 +24,8 @@ function CreateCommentForm(props: Props) { const { entityType, entityToken, loadComments } = props; const [commentMarkdown, setCommentMarkdown] = useState(""); - const [uuidIdempotencyToken, setUuidIdempotencyToken] = useState( - uuidv4() - ); + const [uuidIdempotencyToken, setUuidIdempotencyToken] = + useState(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 = ; + 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 = ( + + ); } let commentButton = <>; diff --git a/packages/fakeyou.com/src/v2/view/pages/media/MediaPage.tsx b/packages/fakeyou.com/src/v2/view/pages/media/MediaPage.tsx index 0d5b6893ea..87a0068c8f 100644 --- a/packages/fakeyou.com/src/v2/view/pages/media/MediaPage.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/media/MediaPage.tsx @@ -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([]); + 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 ; + + return ( + + ); case MediaFileType.BVH: const bvhUrl = bucketConfig.getGcsUrl(mediaFile.public_bucket_path); return ( @@ -335,8 +362,6 @@ export default function MediaPage() { const subtitleDivider = |; - 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 ? (