diff --git a/packages/components/src/api/_common/enums/MediaFileType.ts b/packages/components/src/api/_common/enums/MediaFileType.ts index 4ca8a0b07f..0098e692db 100644 --- a/packages/components/src/api/_common/enums/MediaFileType.ts +++ b/packages/components/src/api/_common/enums/MediaFileType.ts @@ -24,5 +24,8 @@ export enum MediaFileType { // This is a temporary format that will eventually go away. SceneRon = "scene_ron", + // Engine scene + SceneJson = "scene_json", + None = "none", } diff --git a/packages/components/src/api/featured_items/CreateFeaturedItem.ts b/packages/components/src/api/featured_items/CreateFeaturedItem.ts new file mode 100644 index 0000000000..e29fa7452c --- /dev/null +++ b/packages/components/src/api/featured_items/CreateFeaturedItem.ts @@ -0,0 +1,20 @@ +import MakeRequest from "../MakeRequest"; + +export interface CreateFeaturedItemRequest { + entity_token: string, + entity_type: string, +} + +export interface CreateFeaturedItemResponse { + success: boolean +} + +export const CreateFeaturedItem = MakeRequest< + string, + CreateFeaturedItemRequest, + CreateFeaturedItemResponse, + {} +>({ + method: "POST", + routingFunction: () => "/v1/featured_item/create", +}); \ No newline at end of file diff --git a/packages/components/src/api/featured_items/DeleteFeaturedItem.ts b/packages/components/src/api/featured_items/DeleteFeaturedItem.ts new file mode 100644 index 0000000000..43d056b2d8 --- /dev/null +++ b/packages/components/src/api/featured_items/DeleteFeaturedItem.ts @@ -0,0 +1,20 @@ +import MakeRequest from "../MakeRequest"; + +export interface DeleteFeaturedItemRequest { + entity_token: string, + entity_type: string, +} + +export interface DeleteFeaturedItemResponse { + success: boolean +} + +export const DeleteFeaturedItem = MakeRequest< + string, + DeleteFeaturedItemRequest, + DeleteFeaturedItemResponse, + {} +>({ + method: "DELETE", + routingFunction: () => "/v1/featured_item/delete", +}); \ No newline at end of file diff --git a/packages/components/src/api/media_files/GetMedia.ts b/packages/components/src/api/media_files/GetMedia.ts index b46a633e23..e8e3c18ca9 100644 --- a/packages/components/src/api/media_files/GetMedia.ts +++ b/packages/components/src/api/media_files/GetMedia.ts @@ -19,8 +19,7 @@ export interface MediaFile { maybe_creator_user: UserDetailsLight | null; maybe_prompt_token: string | null; creator_set_visibility: string; - created_at: Date; - updated_at: Date; + is_featured: boolean; maybe_model_weight_info: { title: string; weight_token: string; @@ -36,6 +35,8 @@ export interface MediaFile { }, maybe_cover_image_public_bucket_path: string | null }; + created_at: Date; + updated_at: Date; } export interface GetMediaRequest {} diff --git a/packages/fakeyou.com/public/locales/en/EntityGeneral.json b/packages/fakeyou.com/public/locales/en/EntityGeneral.json index 1800d1ac4b..a4fa438da5 100644 --- a/packages/fakeyou.com/public/locales/en/EntityGeneral.json +++ b/packages/fakeyou.com/public/locales/en/EntityGeneral.json @@ -1,4 +1,24 @@ { + "MediaCategories": { + "all": "All", + "audio": "Audio", + "image": "Image", + "video": "Video", + "engine_asset": "3D Asset" + }, + "MediaCategoriesPlural": { + "all": "All", + "audio": "Audio", + "image": "Images", + "video": "Videos", + "engine_asset": "3D Assets" + }, + "WeightsCategories": { + "all": "All", + "faceAnimation": "Face Animator", + "tts": "Text to speech", + "voiceConversion": "Voice conversion" + }, "WeightTypes": { "all": "All weights", "hifigan_tt2": "HiFi-GAN TT2", @@ -20,5 +40,5 @@ "glb": "GLB", "gltf": "gltf", "fbx": "FBX" - }, -} \ No newline at end of file + } +} diff --git a/packages/fakeyou.com/src/common/StyleOptions.ts b/packages/fakeyou.com/src/common/StyleOptions.ts index 52ff9a9091..e6e2be5dcf 100644 --- a/packages/fakeyou.com/src/common/StyleOptions.ts +++ b/packages/fakeyou.com/src/common/StyleOptions.ts @@ -17,6 +17,24 @@ export type VstStyle | "pop_art" | "realistic_1" | "realistic_2" + + | "hr_giger" + | "simpsons" + | "carnage" + | "pastel_cute_anime" + | "bloom_lighting" + | "25d_horror" + | "creepy" + | "creepy_vhs" + | "trail_cam_footage" + | "old_black_white_movie" + | "horror_noir_black_white" + | "techno_noir_black_white" + | "black_white_20s" + | "cyberpunk_anime" + | "dragonball" + | "realistic_matrix" + | "realistic_cyberpunk" ; // Definition of the different options. @@ -102,6 +120,80 @@ export const STYLE_OPTIONS: readonly StyleOption[] = [ label: "Realistic 2", value: "realistic_2", }, + + + { + label: "HR Giger", + value: "hr_giger", + }, + { + label: "Simpsons", + value: "simpsons", + }, + { + label: "Carnage", + value: "carnage", + }, + { + label: "Anime Pastel Cute", + value: "pastel_cute_anime", + }, + { + label: "iBloom Lighting", + value: "bloom_lighting", + }, + { + label: "Horror 2.5D", + value: "25d_horror", + }, + { + label: "Creepy", + value: "creepy", + }, + { + label: "Creepy VHS", + value: "creepy_vhs", + }, + { + label: "Trail Cam Footage", + value: "trail_cam_footage", + }, + { + label: "Old Black and White Movie", + value: "old_black_white_movie", + }, + { + label: "Horror Noir Black and White", + value: "horror_noir_black_white", + }, + { + label: "Techno Noir Black and White", + value: "techno_noir_black_white", + }, + { + label: "Black White 20s", + value: "black_white_20s", + }, + { + label: "Cyberpunk Anime", + value: "cyberpunk_anime", + }, + { + label: "Dragonball", + value: "dragonball", + }, + { + label: "Realistic Matrix", + value: "realistic_matrix", + }, + { + label: "Realistic Cyberpunk", + value: "realistic_cyberpunk", + }, + + + + ]; export const STYLES_BY_KEY : Map = new Map(STYLE_OPTIONS.map((opt) => [opt.value, opt])); diff --git a/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.test.ts b/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.test.ts index 2459220917..5870826531 100644 --- a/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.test.ts +++ b/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.test.ts @@ -75,6 +75,7 @@ describe('media files', () => { updated_at: new Date(), maybe_creator_user: null, creator_set_visibility: "public", + is_featured: false, // TODO(bt,2024-03-11): Make these fields optional maybe_model_weight_info: { title: "title", diff --git a/packages/fakeyou.com/src/components/entities/EntityTypes.tsx b/packages/fakeyou.com/src/components/entities/EntityTypes.tsx index 597df501a1..ae3d2b280e 100644 --- a/packages/fakeyou.com/src/components/entities/EntityTypes.tsx +++ b/packages/fakeyou.com/src/components/entities/EntityTypes.tsx @@ -1,6 +1,6 @@ import { enumToKeyArr } from "resources"; import { UploadMediaResponse } from "@storyteller/components/src/api/media_files/UploadMedia"; -import { UploadEngineAssetResponse }from "@storyteller/components/src/api/media_files/UploadEngineAsset"; +import { UploadEngineAssetResponse } from "@storyteller/components/src/api/media_files/UploadEngineAsset"; // 1 @@ -10,7 +10,7 @@ import { UploadEngineAssetResponse }from "@storyteller/components/src/api/media_ export enum EntityType { unknown, media, - weights + weights, } // 2 @@ -23,14 +23,14 @@ export enum MediaFilters { audio, image, video, - engine_asset + engine_asset, } export enum WeightsCategories { all, faceAnimation, tts, - voiceConversion + voiceConversion, } // 3 @@ -49,7 +49,7 @@ export enum WeightsFilters { so_vits_svc, tt2, loRA, - vall_e + vall_e, } // media file types @@ -60,17 +60,17 @@ export enum EngineTypes { glb, gltf, obj, - ron + ron, } export enum AudioTypes { mp3, - wav + wav, } export enum ImageTypes { jpg, - png + png, } export enum VideoTypes { @@ -128,15 +128,19 @@ export type JobSelection = WeightCategoriesProp | WeightFilterProp; export type UploaderResponse = UploadEngineAssetResponse | UploadMediaResponse; export const ListEntityFilters = (mode?: EntityInputMode) => { - // this an object of all types // in most cases it is better to have granular caterogization of types // but because any entity can/could(?) be bookmarked all types are agregated here // this is condensed because typescript didn't like when I broke it apart. // Will clean up soon, I promise - const bookmarkFilters = Object.keys({ ...MediaFilters, ...WeightsFilters }).filter(val => isNaN(Number(val))).reduce((obj,current) => ({ ...obj, [current]: current }),{}); + const bookmarkFilters = Object.keys({ ...MediaFilters, ...WeightsFilters }) + .filter(val => isNaN(Number(val))) + .reduce((obj, current) => ({ ...obj, [current]: current }), {}); - const selectedFilters = mode !== undefined ? [bookmarkFilters,MediaFilters,WeightsFilters,WeightsFilters][mode] : EntityInputMode; + const selectedFilters = + mode !== undefined + ? [bookmarkFilters, MediaFilters, WeightsFilters, WeightsFilters][mode] + : EntityInputMode; return Object.values(selectedFilters).filter(val => isNaN(Number(val))); }; @@ -145,36 +149,59 @@ export const ListEntityFilters = (mode?: EntityInputMode) => { // note the use t() to translate labels // this allows easily maintained translations for inputs -export const EntityFilterOptions = (mode?: EntityInputMode, t = (v:string) => v) => { - return ListEntityFilters(mode).map((value) => { - if (typeof value === "string") return { value, label: t(value) } - return { label: "all", value: "all" }; +export const EntityFilterOptions = ( + mode?: EntityInputMode, + t = (v: string) => v +) => { + const translationNamespaces = [ + "BookmarksCategories", + "MediaCategoriesPlural", + "WeightsCategories", + "WeightsCategories", + ]; + const selectedNamespace = translationNamespaces[mode || -1]; + return ListEntityFilters(mode).map(value => { + if (typeof value === "string") + return { + value, + label: + selectedNamespace !== undefined + ? t(`${selectedNamespace}.${value}`) + : value, + }; + return { label: t(`${selectedNamespace}.all`), value: "all" }; }); }; -export const getMediaTypesByCategory = (mediaCategory: MediaFilters) => enumToKeyArr([ - { ...AudioTypes, ...ImageTypes, ...VideoTypes, ...EngineTypes }, - AudioTypes, - ImageTypes, - VideoTypes, - EngineTypes -][mediaCategory]); +export const getMediaTypesByCategory = (mediaCategory: MediaFilters) => + enumToKeyArr( + [ + { ...AudioTypes, ...ImageTypes, ...VideoTypes, ...EngineTypes }, + AudioTypes, + ImageTypes, + VideoTypes, + EngineTypes, + ][mediaCategory] + ); -export const isSelectedType = (mediaCategory: MediaFilters, fileExtension: string) => - getMediaTypesByCategory(mediaCategory).includes(fileExtension); +export const isSelectedType = ( + mediaCategory: MediaFilters, + fileExtension: string +) => getMediaTypesByCategory(mediaCategory).includes(fileExtension); -export const mediaCategoryfromString = (categoryStr: string) => enumToKeyArr(MediaFilters).indexOf(categoryStr); +export const mediaCategoryfromString = (categoryStr: string) => + enumToKeyArr(MediaFilters).indexOf(categoryStr); //renaming to getMediaCatgoryByType export const getMediaCategory = (fileExtension: string) => { - isSelectedType(MediaFilters.image,fileExtension); - if (isSelectedType(MediaFilters.engine_asset, fileExtension)) return MediaFilters.engine_asset; - if (isSelectedType(MediaFilters.audio, fileExtension)) return MediaFilters.audio; - if (isSelectedType(MediaFilters.image, fileExtension)) return MediaFilters.image; - if (isSelectedType(MediaFilters.video, fileExtension)) return MediaFilters.video; + isSelectedType(MediaFilters.image, fileExtension); + if (isSelectedType(MediaFilters.engine_asset, fileExtension)) + return MediaFilters.engine_asset; + if (isSelectedType(MediaFilters.audio, fileExtension)) + return MediaFilters.audio; + if (isSelectedType(MediaFilters.image, fileExtension)) + return MediaFilters.image; + if (isSelectedType(MediaFilters.video, fileExtension)) + return MediaFilters.video; return MediaFilters.all; // will change to "unknown" eventually -} - - - - +}; diff --git a/packages/fakeyou.com/src/utils/mediaTypeLabels.ts b/packages/fakeyou.com/src/utils/mediaTypeLabels.ts index d86c3d4c1a..9f21ccb212 100644 --- a/packages/fakeyou.com/src/utils/mediaTypeLabels.ts +++ b/packages/fakeyou.com/src/utils/mediaTypeLabels.ts @@ -9,6 +9,7 @@ export const mediaTypeLabels = { [MediaFileType.GLB]: "GLB", [MediaFileType.GLTF]: "glTF", [MediaFileType.SceneRon]: "RON", + [MediaFileType.SceneJson]: "Scene", [MediaFileType.FBX]: "FBX", [MediaFileType.None]: "Unknown", }; 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 2f240db072..bcf6f0c27f 100644 --- a/packages/fakeyou.com/src/v2/view/pages/media/MediaPage.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/media/MediaPage.tsx @@ -36,13 +36,16 @@ import { useBookmarks, useMedia, useRatings, useSession } from "hooks"; import { WeightCategory } from "@storyteller/components/src/api/_common/enums/WeightCategory"; import SdBatchMediaPanel from "./components/SdBatchMediaPanel/SdBatchMediaPanel"; import { GetMediaBatchImages } from "@storyteller/components/src/api/media_files/GetMediaBatchImages"; +import { CreateFeaturedItem } from "@storyteller/components/src/api/featured_items/CreateFeaturedItem"; +import { DeleteFeaturedItem } from "@storyteller/components/src/api/featured_items/DeleteFeaturedItem"; import { mediaTypeLabels } from "utils/mediaTypeLabels"; import { EngineMediaPanel } from "./components/EngineMediaPanel/EngineMediaPanel"; import { GetMediaFileTitle } from "common/GetMediaFileTitle"; import { STYLES_BY_KEY } from "common/StyleOptions"; +import { faStarShooting } from "@fortawesome/pro-duotone-svg-icons"; export default function MediaPage() { - const { canAccessStudio, canEditMediaFile, user } = useSession(); + const { canAccessStudio, canEditMediaFile, canBanUsers, user } = useSession(); const { token } = useParams<{ token: string }>(); const bookmarks = useBookmarks(); const ratings = useRatings(); @@ -70,6 +73,8 @@ export default function MediaPage() { mediaFile?.maybe_creator_user?.user_token || "" ); + const viewerCanMakeFeatured = canBanUsers() || false; + // Inside MediaPage.tsx useEffect(() => { @@ -99,6 +104,31 @@ export default function MediaPage() { const deleteMedia = () => remove(!!user?.can_ban_users); + const handleFeatureMedia = async () => { + setFeatureMedia(!mediaFile?.is_featured); + }; + + const setFeatureMedia = async (setFeatured: boolean) => { + if (mediaFile === undefined) { + return; + } + + const request = { + entity_type: "media_file", + entity_token: mediaFile.token, + }; + + // NB: Victor, I don't know how to re-query media with the media context thing. :( + // Sorry, I'm forcing a page reload instead. I know this sucks. + if (setFeatured) { + await CreateFeaturedItem("", request); + window.location.reload(); + } else { + await DeleteFeaturedItem("", request); + window.location.reload(); + } + }; + const copyToClipboard = async ( text: string, setButtonText: React.Dispatch> @@ -167,16 +197,15 @@ export default function MediaPage() { {prompt?.maybe_style_name && ( <>
-
- Style Name -
+
Style Name
-

{STYLES_BY_KEY.get(prompt.maybe_style_name)?.label}

+

+ {STYLES_BY_KEY.get(prompt.maybe_style_name)?.label} +

)} - )} @@ -270,8 +299,24 @@ export default function MediaPage() { ); + case MediaFileType.SceneJson: + return ( +
+
+ ); default: - return
Unsupported media type
; + return
Unsupported media typeaaa
; } } @@ -475,30 +520,37 @@ export default function MediaPage() { const title = GetMediaFileTitle(mediaFile); const showEngineCover = () => { - const coverMediaPath = mediaFile?.cover_image?.maybe_cover_image_public_bucket_path || ""; + const coverMediaPath = + mediaFile?.cover_image?.maybe_cover_image_public_bucket_path || ""; if (mediaFile) { switch (mediaFile.media_type) { - case MediaFileType.BVH: - case MediaFileType.GLB: - case MediaFileType.GLTF: - case MediaFileType.SceneRon: return ; - default: return null; + case MediaFileType.BVH: + case MediaFileType.GLB: + case MediaFileType.GLTF: + case MediaFileType.SceneRon: + return ( + + ); + default: + return null; } } - } + }; return (
- { - showEngineCover() - } + {showEngineCover()}

{title}

@@ -578,7 +630,8 @@ export default function MediaPage() { }} /> ) : null} - { canAccessStudio() && mediaFile?.media_type === MediaFileType.Video ? ( + {canAccessStudio() && + mediaFile?.media_type === MediaFileType.Video ? (
+ + )}
diff --git a/packages/fakeyou.com/src/v2/view/pages/style-video/StyleVideo.tsx b/packages/fakeyou.com/src/v2/view/pages/style-video/StyleVideo.tsx index 398d4283cb..43be8c4455 100644 --- a/packages/fakeyou.com/src/v2/view/pages/style-video/StyleVideo.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/style-video/StyleVideo.tsx @@ -2,11 +2,18 @@ import React, { useState } from "react"; import { FrontendInferenceJobType } from "@storyteller/components/src/jobs/InferenceJob"; import { v4 as uuidv4 } from "uuid"; import { useInferenceJobs, useSession } from "hooks"; -import { Button, Container, Panel, TextArea, TempSelect as Select } from "components/common"; +import { + Button, + Container, + Panel, + SegmentButtons, + TextArea, + TempSelect as Select, +} from "components/common"; import { EntityInput } from "components/entities"; import { EnqueueVST, - EnqueueVSTResponse + EnqueueVSTResponse, } from "@storyteller/components/src/api/video_styleTransfer/Enqueue_VST"; import "./StyleVideo.scss"; import { useParams } from "react-router-dom"; @@ -19,24 +26,28 @@ export default function StyleVideo() { const [mediaToken, mediaTokenSet] = useState(pageMediaToken || ""); const [prompt, promptSet] = useState(""); const [negativePrompt, negativePromptSet] = useState(""); + const [length, lengthSet] = useState(3000); const { enqueue } = useInferenceJobs(); const onClick = () => { if (mediaToken) { - EnqueueVST("",{ + EnqueueVST("", { creator_set_visibility: "private", enable_lipsync: true, input_file: mediaToken, negative_prompt: negativePrompt, prompt, style, - trim_end_millis: 3000, + trim_end_millis: length, trim_start_millis: 0, - uuid_idempotency_token: uuidv4() - }) - .then((res: EnqueueVSTResponse) => { + uuid_idempotency_token: uuidv4(), + }).then((res: EnqueueVSTResponse) => { if (res.success && res.inference_job_token) { - enqueue(res.inference_job_token,true,FrontendInferenceJobType.VideoStyleTransfer); + enqueue( + res.inference_job_token, + true, + FrontendInferenceJobType.VideoStyleTransfer + ); } else { console.log("Failed to enqueue job", res); } @@ -44,55 +55,92 @@ export default function StyleVideo() { } }; - const options = STYLE_OPTIONS.map((option) => { + const styleOptions = STYLE_OPTIONS.map(option => { return { - label: option.label, + label: option.label, value: option.value, - } + }; }); - return studioAccessCheck( + const lengthOptions = [ + { label: 3, value: 3000 }, + { label: 5, value: 5000 }, + { label: 7, value: 7000 }, + ]; + + return studioAccessCheck( +
-

- Style a Video -

+

Style a Video

- { mediaTokenSet(target.value) }, - type: "media" - }}/> - { + styleSet(target.value); + }, + options: styleOptions, + value: style, + }} + />
-