mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-06 02:00:46 +00:00
Merge branch 'feature/mvp' of https://github.com/storytold/storyteller-frontend into feature/mvp
This commit is contained in:
commit
f82d0bf78d
92 files changed
+3007
-203
No files matched your search
@@ -501,6 +501,11 @@ class ApiConfig {
|
||||
return `${this.getApiOrigin()}/v1/workflow/comfy/create`;
|
||||
}
|
||||
|
||||
// =============== Video Styletransfer ===============
|
||||
enqueueVideoStyleTransfer(): string {
|
||||
return `${this.getApiOrigin()}/v1/workflow/comfy/create`;
|
||||
}
|
||||
|
||||
// =============== Convert FBX to glTF ===============
|
||||
enqueueFbxToGltf(): string {
|
||||
return `${this.getApiOrigin()}/v1/conversion/enqueue_fbx_to_gltf`;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import { ApiConfig } from "@storyteller/components";
|
||||
|
||||
export interface EnqueueVideoStyleTransferRequest {
|
||||
"uuid_idempotency_token": string,
|
||||
|
||||
"maybe_sd_model": string,
|
||||
"maybe_workflow_config": string,
|
||||
"maybe_input_file": string,
|
||||
"maybe_output_path": string,
|
||||
"maybe_json_modifications": {
|
||||
"$.510.inputs.Text": string,
|
||||
"$.8.inputs.text": string,
|
||||
"$.173.inputs.seed": string,
|
||||
"$.401.inputs.Value": number,
|
||||
"$.918.inputs.Value": number,
|
||||
"$.137.inputs.Value": number,
|
||||
"$.186.inputs.Value": number,
|
||||
"$.140.inputs.Value":number,
|
||||
"$.154.inputs.Value": number,
|
||||
"$.445.inputs.number": number,
|
||||
"$.947.inputs.Value": number,
|
||||
"$.800.inputs.Value": number,
|
||||
"$.797.inputs.Value": number,
|
||||
"$.796.inputs.Value": number,
|
||||
"$.772.inputs.Value": number,
|
||||
"$.771.inputs.Value": number,
|
||||
"$.527.inputs.Value": number,
|
||||
"$.403.inputs.Value": number,
|
||||
}
|
||||
}
|
||||
|
||||
export interface EnqueueVideoStyleTransferResponse {
|
||||
success: boolean,
|
||||
inference_job_token?: string,
|
||||
}
|
||||
|
||||
export async function EnqueueVideoStyleTransfer(request: EnqueueVideoStyleTransferRequest) : Promise<EnqueueVideoStyleTransferResponse>
|
||||
{
|
||||
// const endpoint = "/v1/workflow/comfy/create";
|
||||
|
||||
const endpoint = new ApiConfig().enqueueVideoStyleTransfer();
|
||||
|
||||
return await fetch(endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(request),
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
if (!res) {
|
||||
return { success : false };
|
||||
}
|
||||
|
||||
if (res && 'success' in res) {
|
||||
return res;
|
||||
} else {
|
||||
return { success : false };
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
return { success : false };
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
export type {
|
||||
EnqueueVideoStyleTransferRequest, EnqueueVideoStyleTransferResponse,
|
||||
} from './EnqueueVideoStyleTransfer';
|
||||
|
||||
export {
|
||||
EnqueueVideoStyleTransfer as default
|
||||
} from './EnqueueVideoStyleTransfer';
|
||||
@@ -13,6 +13,7 @@ export enum FrontendInferenceJobType {
|
||||
ConvertFbxtoGltf,
|
||||
EngineComposition,
|
||||
VideoWorkflow,
|
||||
VideoStyleTransfer,
|
||||
}
|
||||
|
||||
// NB: Many of these fields are optional despite the response payload containing them
|
||||
|
||||
@@ -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 |
@@ -19,6 +19,7 @@
|
||||
"speechVc": "ভয়েস টু ভয়েস",
|
||||
"videoFaceAnimator": "ফেস অ্যানিমেটর",
|
||||
"videoMotionCapture": "ভিডিও মোশন ক্যাপচার",
|
||||
"videoStyleTransfer": "ভিডিও শৈলী স্থানান্তর",
|
||||
"videoTitle": "ভিডিও জেনারেশন",
|
||||
"videoW2l": "ভিডিও লিপ সিঙ্ক",
|
||||
"videoWorkflow": "ভিডিও ওয়ার্কফ্লো"
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "বাতিল করুন",
|
||||
"enqueue": "স্টাইল ট্রান্সফার প্রয়োগ করুন",
|
||||
"generateNew": "অন্য ভিডিও স্থানান্তর করুন",
|
||||
"proceed": "স্টাইল ট্রান্সফারে এগিয়ে যান",
|
||||
"upload": "আপলোড করুন"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "আপনার ভিডিওর স্টাইল একটি থেকে অন্যটিতে পরিবর্তন করুন",
|
||||
"title": "ভিডিও শৈলী স্থানান্তর"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "স্টাইল ট্রান্সফার প্রয়োগ করার পরে একটি ফ্রেমের পূর্বরূপ"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "নেতিবাচক প্রম্পট",
|
||||
"prompt": "একটি প্রম্পট লিখুন",
|
||||
"selectStyle": "একটি শৈলী নির্বাচন করুন",
|
||||
"styleStrength": "শৈলী শক্তি"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "এখানে প্রম্পট টাইপ করুন। যেমন: মানুষ ভাল্লুক নয়",
|
||||
"prompt": "এখানে প্রম্পট টাইপ করুন। যেমন: ম্যান অ্যানিমে স্টাইলে নাচছে"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "শক্তি"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "ফাইল আপলোড হচ্ছে..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "আপলোড করা থেকে বেছে নিন",
|
||||
"upload": "একটি ভিডিও আপলোড করুন"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "একটি ভিডিও বাছাই করুন, তারপর একটি শৈলীর একটি মডেল চয়ন করুন এবং তারপর StoryTeller.ai কে এটি ঘটতে দিন৷",
|
||||
"label": {
|
||||
"original": "অরিজিনাল ভিডিও"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "বাতিল করুন",
|
||||
"enqueue": "স্টাইল ট্রান্সফার প্রয়োগ করুন",
|
||||
"generateNew": "অন্য ভিডিও স্থানান্তর করুন",
|
||||
"proceed": "স্টাইল ট্রান্সফারে এগিয়ে যান",
|
||||
"upload": "আপলোড করুন"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "আপনার ভিডিওর স্টাইল একটি থেকে অন্যটিতে পরিবর্তন করুন",
|
||||
"title": "ভিডিও শৈলী স্থানান্তর"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "স্টাইল ট্রান্সফার প্রয়োগ করার পরে একটি ফ্রেমের পূর্বরূপ"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "নেতিবাচক প্রম্পট",
|
||||
"prompt": "একটি প্রম্পট লিখুন",
|
||||
"selectStyle": "একটি শৈলী নির্বাচন করুন",
|
||||
"styleStrength": "শৈলী শক্তি"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "এখানে প্রম্পট টাইপ করুন। যেমন: মানুষ ভাল্লুক নয়",
|
||||
"prompt": "এখানে প্রম্পট টাইপ করুন। যেমন: ম্যান অ্যানিমে স্টাইলে নাচছে"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "শক্তি"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "ফাইল আপলোড হচ্ছে..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "আপলোড করা থেকে বেছে নিন",
|
||||
"upload": "একটি ভিডিও আপলোড করুন"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "একটি ভিডিও বাছাই করুন, তারপর একটি শৈলীর একটি মডেল চয়ন করুন এবং তারপর StoryTeller.ai কে এটি ঘটতে দিন৷",
|
||||
"label": {
|
||||
"original": "অরিজিনাল ভিডিও"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
"speechVc": "Stimme zu Stimme",
|
||||
"videoFaceAnimator": "Gesichtsanimator",
|
||||
"videoMotionCapture": "Video-Bewegungserfassung",
|
||||
"videoStyleTransfer": "Übertragung des Videostils",
|
||||
"videoTitle": "Videogenerierung",
|
||||
"videoW2l": "Video-Lippensynchronisation",
|
||||
"videoWorkflow": "Video-Workflow"
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "Stornieren",
|
||||
"enqueue": "Stilübertragung anwenden",
|
||||
"generateNew": "Übertragen Sie ein anderes Video",
|
||||
"proceed": "Fahren Sie mit der Stilübertragung fort",
|
||||
"upload": "Hochladen"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "Ändern Sie den Stil Ihres Videos von einem zum anderen",
|
||||
"title": "Übertragung des Videostils"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "Vorschau eines Rahmens nach der Anwendung von Style Transfer"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "Negative Aufforderung",
|
||||
"prompt": "Geben Sie eine Eingabeaufforderung ein",
|
||||
"selectStyle": "Wählen Sie einen Stil aus",
|
||||
"styleStrength": "Stilstärke"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "Geben Sie hier prompt ein. ZB: Der Mensch ist kein Bär",
|
||||
"prompt": "Geben Sie hier prompt ein. Beispiel: Mann tanzt im Anime-Stil"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "Stärke"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "Datei wird hochgeladen..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "Wählen Sie aus den hochgeladenen",
|
||||
"upload": "Ein Video hochladen"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "Wählen Sie ein Video, dann ein Modell eines Stils aus und lassen Sie es dann von StoryTeller.ai Wirklichkeit werden",
|
||||
"label": {
|
||||
"original": "Originalvideo"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "Stornieren",
|
||||
"enqueue": "Stilübertragung anwenden",
|
||||
"generateNew": "Übertragen Sie ein anderes Video",
|
||||
"proceed": "Fahren Sie mit der Stilübertragung fort",
|
||||
"upload": "Hochladen"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "Ändern Sie den Stil Ihres Videos von einem zum anderen",
|
||||
"title": "Übertragung des Videostils"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "Vorschau eines Rahmens nach der Anwendung von Style Transfer"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "Negative Aufforderung",
|
||||
"prompt": "Geben Sie eine Eingabeaufforderung ein",
|
||||
"selectStyle": "Wählen Sie einen Stil aus",
|
||||
"styleStrength": "Stilstärke"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "Geben Sie hier prompt ein. ZB: Der Mensch ist kein Bär",
|
||||
"prompt": "Geben Sie hier prompt ein. Beispiel: Mann tanzt im Anime-Stil"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "Stärke"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "Datei wird hochgeladen..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "Wählen Sie aus den hochgeladenen",
|
||||
"upload": "Ein Video hochladen"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "Wählen Sie ein Video, dann ein Modell eines Stils aus und lassen Sie es dann von StoryTeller.ai Wirklichkeit werden",
|
||||
"label": {
|
||||
"original": "Originalvideo"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
"speechVc": "Voice to Voice",
|
||||
"videoFaceAnimator": "Face Animator",
|
||||
"videoMotionCapture": "Video Motion Capture",
|
||||
"videoStyleTransfer": "Video Style Transfer",
|
||||
"videoTitle": "Video Generation",
|
||||
"videoW2l": "Video Lip Sync",
|
||||
"videoWorkflow": "Video Workflow"
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "Cancel",
|
||||
"enqueue": "Apply Style Transfer",
|
||||
"generateNew": "Transfer another video",
|
||||
"proceed": "Proceed to Style Transfer",
|
||||
"upload": "Upload"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "Change the style of your video from one to another",
|
||||
"title": "Video Style Transfer"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "Preview of a frame after applying Style Transfer"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "Negative Prompt",
|
||||
"prompt": "Enter a prompt",
|
||||
"selectStyle": "Select a style",
|
||||
"styleStrength": "Style Strength"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "Type prompt here. Eg: Man is not a bear",
|
||||
"prompt": "Type prompt here. Eg: Man dancing in the style of anime"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "Strength"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "Uploading File..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "Choose from uploaded",
|
||||
"upload": "Upload a video"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "Pick a video, then pick a model of a style, and then let StoryTeller.ai make it happen",
|
||||
"label": {
|
||||
"original": "Original Video"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "Cancel",
|
||||
"enqueue": "Apply Style Transfer",
|
||||
"generateNew": "Transfer another video",
|
||||
"proceed": "Proceed to Style Transfer",
|
||||
"upload": "Upload"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "Change the style of your video from one to another",
|
||||
"title": "Video Style Transfer"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "Preview of a frame after applying Style Transfer"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "Negative Prompt",
|
||||
"prompt": "Enter a prompt",
|
||||
"selectStyle": "Select a style",
|
||||
"styleStrength": "Style Strength"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "Type prompt here. Eg: Man is not a bear",
|
||||
"prompt": "Type prompt here. Eg: Man dancing in the style of anime"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "Strength"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "Uploading File..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "Choose from uploaded",
|
||||
"upload": "Upload a video"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "Pick a video, then pick a model of a style, and then let StoryTeller.ai make it happen",
|
||||
"label": {
|
||||
"original": "Original Video"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
"speechVc": "Voz a Voz",
|
||||
"videoFaceAnimator": "Animador de rostros",
|
||||
"videoMotionCapture": "Captura de movimiento de vídeo",
|
||||
"videoStyleTransfer": "Transferencia de estilo de vídeo",
|
||||
"videoTitle": "Generación de vídeo",
|
||||
"videoW2l": "Sincronización de labios de vídeo",
|
||||
"videoWorkflow": "Flujo de trabajo de vídeo"
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "Cancelar",
|
||||
"enqueue": "Aplicar transferencia de estilo",
|
||||
"generateNew": "Transferir otro video",
|
||||
"proceed": "Continuar con la transferencia de estilo",
|
||||
"upload": "Subir"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "Cambia el estilo de tu vídeo de uno a otro",
|
||||
"title": "Transferencia de estilo de vídeo"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "Vista previa de un marco después de aplicar Style Transfer"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "Aviso negativo",
|
||||
"prompt": "Ingrese un mensaje",
|
||||
"selectStyle": "Selecciona un estilo",
|
||||
"styleStrength": "Fuerza del estilo"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "Escriba el mensaje aquí. Por ejemplo: el hombre no es un oso",
|
||||
"prompt": "Escriba el mensaje aquí. Por ejemplo: hombre bailando al estilo anime."
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "Fortaleza"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "Subiendo archivo..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "Elige entre cargado",
|
||||
"upload": "Subir un video"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "Elija un video, luego elija un modelo de estilo y luego deje que StoryTeller.ai lo haga realidad.",
|
||||
"label": {
|
||||
"original": "Vídeo original"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "Cancelar",
|
||||
"enqueue": "Aplicar transferencia de estilo",
|
||||
"generateNew": "Transferir otro video",
|
||||
"proceed": "Continuar con la transferencia de estilo",
|
||||
"upload": "Subir"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "Cambia el estilo de tu vídeo de uno a otro",
|
||||
"title": "Transferencia de estilo de vídeo"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "Vista previa de un marco después de aplicar Style Transfer"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "Aviso negativo",
|
||||
"prompt": "Ingrese un mensaje",
|
||||
"selectStyle": "Selecciona un estilo",
|
||||
"styleStrength": "Fuerza del estilo"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "Escriba el mensaje aquí. Por ejemplo: el hombre no es un oso",
|
||||
"prompt": "Escriba el mensaje aquí. Por ejemplo: hombre bailando al estilo anime."
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "Fortaleza"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "Subiendo archivo..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "Elige entre cargado",
|
||||
"upload": "Subir un video"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "Elija un video, luego elija un modelo de estilo y luego deje que StoryTeller.ai lo haga realidad.",
|
||||
"label": {
|
||||
"original": "Vídeo original"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
"speechVc": "Voix à voix",
|
||||
"videoFaceAnimator": "Animateur de visage",
|
||||
"videoMotionCapture": "Capture de mouvement vidéo",
|
||||
"videoStyleTransfer": "Transfert de style vidéo",
|
||||
"videoTitle": "Génération vidéo",
|
||||
"videoW2l": "Synchronisation labiale vidéo",
|
||||
"videoWorkflow": "Flux de travail vidéo"
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "Annuler",
|
||||
"enqueue": "Appliquer le transfert de style",
|
||||
"generateNew": "Transférer une autre vidéo",
|
||||
"proceed": "Passer au transfert de style",
|
||||
"upload": "Télécharger"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "Changez le style de votre vidéo de l'un à l'autre",
|
||||
"title": "Transfert de style vidéo"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "Aperçu d'un cadre après application de Style Transfer"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "Invite négative",
|
||||
"prompt": "Entrez une invite",
|
||||
"selectStyle": "Sélectionnez un style",
|
||||
"styleStrength": "Force du style"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "Tapez l'invite ici. Ex : L'homme n'est pas un ours",
|
||||
"prompt": "Tapez l'invite ici. Par exemple : un homme qui danse à la manière d'un anime"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "Force"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "Téléchargement du fichier..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "Choisissez parmi les fichiers téléchargés",
|
||||
"upload": "Télécharger une vidéo"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "Choisissez une vidéo, puis choisissez un modèle d'un style, puis laissez StoryTeller.ai le réaliser",
|
||||
"label": {
|
||||
"original": "Vidéo originale"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "Annuler",
|
||||
"enqueue": "Appliquer le transfert de style",
|
||||
"generateNew": "Transférer une autre vidéo",
|
||||
"proceed": "Passer au transfert de style",
|
||||
"upload": "Télécharger"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "Changez le style de votre vidéo de l'un à l'autre",
|
||||
"title": "Transfert de style vidéo"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "Aperçu d'un cadre après application de Style Transfer"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "Invite négative",
|
||||
"prompt": "Entrez une invite",
|
||||
"selectStyle": "Sélectionnez un style",
|
||||
"styleStrength": "Force du style"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "Tapez l'invite ici. Ex : L'homme n'est pas un ours",
|
||||
"prompt": "Tapez l'invite ici. Par exemple : un homme qui danse à la manière d'un anime"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "Force"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "Téléchargement du fichier..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "Choisissez parmi les fichiers téléchargés",
|
||||
"upload": "Télécharger une vidéo"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "Choisissez une vidéo, puis choisissez un modèle d'un style, puis laissez StoryTeller.ai le réaliser",
|
||||
"label": {
|
||||
"original": "Vidéo originale"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
"speechVc": "आवाज से आवाज",
|
||||
"videoFaceAnimator": "फेस एनिमेटर",
|
||||
"videoMotionCapture": "वीडियो मोशन कैप्चर",
|
||||
"videoStyleTransfer": "वीडियो शैली स्थानांतरण",
|
||||
"videoTitle": "वीडियो जनरेशन",
|
||||
"videoW2l": "वीडियो लिप सिंक",
|
||||
"videoWorkflow": "वीडियो वर्कफ़्लो"
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "रद्द करना",
|
||||
"enqueue": "स्टाइल ट्रांसफर लागू करें",
|
||||
"generateNew": "दूसरा वीडियो ट्रांसफर करें",
|
||||
"proceed": "स्टाइल ट्रांसफर के लिए आगे बढ़ें",
|
||||
"upload": "डालना"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "अपने वीडियो की शैली को एक से दूसरे में बदलें",
|
||||
"title": "वीडियो शैली स्थानांतरण"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "स्टाइल ट्रांसफर लागू करने के बाद फ्रेम का पूर्वावलोकन"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "नकारात्मक संकेत",
|
||||
"prompt": "एक संकेत दर्ज करें",
|
||||
"selectStyle": "एक शैली चुनें",
|
||||
"styleStrength": "शैली की ताकत"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "यहां प्रॉम्प्ट टाइप करें। उदाहरणार्थ: मनुष्य भालू नहीं है",
|
||||
"prompt": "यहां प्रॉम्प्ट टाइप करें। जैसे: आदमी एनीमे की शैली में नृत्य कर रहा है"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "ताकत"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "फ़ाइल अपलोड हो रही है..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "अपलोड किए गए में से चुनें",
|
||||
"upload": "एक वीडियो अपलोड करें"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "एक वीडियो चुनें, फिर एक शैली का मॉडल चुनें, और फिर StoryTeller.ai को ऐसा करने दें",
|
||||
"label": {
|
||||
"original": "मूल वीडियो"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "रद्द करना",
|
||||
"enqueue": "स्टाइल ट्रांसफर लागू करें",
|
||||
"generateNew": "दूसरा वीडियो ट्रांसफर करें",
|
||||
"proceed": "स्टाइल ट्रांसफर के लिए आगे बढ़ें",
|
||||
"upload": "डालना"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "अपने वीडियो की शैली को एक से दूसरे में बदलें",
|
||||
"title": "वीडियो शैली स्थानांतरण"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "स्टाइल ट्रांसफर लागू करने के बाद फ्रेम का पूर्वावलोकन"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "नकारात्मक संकेत",
|
||||
"prompt": "एक संकेत दर्ज करें",
|
||||
"selectStyle": "एक शैली चुनें",
|
||||
"styleStrength": "शैली की ताकत"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "यहां प्रॉम्प्ट टाइप करें। उदाहरणार्थ: मनुष्य भालू नहीं है",
|
||||
"prompt": "यहां प्रॉम्प्ट टाइप करें। जैसे: आदमी एनीमे की शैली में नृत्य कर रहा है"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "ताकत"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "फ़ाइल अपलोड हो रही है..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "अपलोड किए गए में से चुनें",
|
||||
"upload": "एक वीडियो अपलोड करें"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "एक वीडियो चुनें, फिर एक शैली का मॉडल चुनें, और फिर StoryTeller.ai को ऐसा करने दें",
|
||||
"label": {
|
||||
"original": "मूल वीडियो"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
"speechVc": "Suara ke Suara",
|
||||
"videoFaceAnimator": "Animator Wajah",
|
||||
"videoMotionCapture": "Pengambilan Gerakan Video",
|
||||
"videoStyleTransfer": "Transfer Gaya Video",
|
||||
"videoTitle": "Pembuatan Video",
|
||||
"videoW2l": "Sinkronisasi Bibir Video",
|
||||
"videoWorkflow": "Alur Kerja Video"
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "Membatalkan",
|
||||
"enqueue": "Terapkan Transfer Gaya",
|
||||
"generateNew": "Transfer video lain",
|
||||
"proceed": "Lanjutkan ke Transfer Gaya",
|
||||
"upload": "Mengunggah"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "Ubah gaya video Anda dari satu ke yang lain",
|
||||
"title": "Transfer Gaya Video"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "Pratinjau bingkai setelah menerapkan Style Transfer"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "Perintah Negatif",
|
||||
"prompt": "Masukkan perintah",
|
||||
"selectStyle": "Pilih gaya",
|
||||
"styleStrength": "Kekuatan Gaya"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "Ketik perintah di sini. Misalnya: Manusia bukan beruang",
|
||||
"prompt": "Ketik perintah di sini. Misalnya: Pria menari dengan gaya anime"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "Kekuatan"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "Mengunggah Berkas..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "Pilih dari yang diunggah",
|
||||
"upload": "Unggah video"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "Pilih video, lalu pilih model gayanya, lalu biarkan StoryTeller.ai mewujudkannya",
|
||||
"label": {
|
||||
"original": "Video Asli"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "Membatalkan",
|
||||
"enqueue": "Terapkan Transfer Gaya",
|
||||
"generateNew": "Transfer video lain",
|
||||
"proceed": "Lanjutkan ke Transfer Gaya",
|
||||
"upload": "Mengunggah"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "Ubah gaya video Anda dari satu ke yang lain",
|
||||
"title": "Transfer Gaya Video"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "Pratinjau bingkai setelah menerapkan Style Transfer"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "Perintah Negatif",
|
||||
"prompt": "Masukkan perintah",
|
||||
"selectStyle": "Pilih gaya",
|
||||
"styleStrength": "Kekuatan Gaya"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "Ketik perintah di sini. Misalnya: Manusia bukan beruang",
|
||||
"prompt": "Ketik perintah di sini. Misalnya: Pria menari dengan gaya anime"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "Kekuatan"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "Mengunggah Berkas..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "Pilih dari yang diunggah",
|
||||
"upload": "Unggah video"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "Pilih video, lalu pilih model gayanya, lalu biarkan StoryTeller.ai mewujudkannya",
|
||||
"label": {
|
||||
"original": "Video Asli"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
"speechVc": "Da voce a voce",
|
||||
"videoFaceAnimator": "Animatore di volti",
|
||||
"videoMotionCapture": "Acquisizione del movimento video",
|
||||
"videoStyleTransfer": "Trasferimento di stili video",
|
||||
"videoTitle": "Generazione video",
|
||||
"videoW2l": "Sincronizzazione labiale video",
|
||||
"videoWorkflow": "Flusso di lavoro video"
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "Annulla",
|
||||
"enqueue": "Applica il trasferimento di stile",
|
||||
"generateNew": "Trasferisci un altro video",
|
||||
"proceed": "Procedi al trasferimento dello stile",
|
||||
"upload": "Caricamento"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "Cambia lo stile del tuo video da uno all'altro",
|
||||
"title": "Trasferimento di stili video"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "Anteprima di un fotogramma dopo l'applicazione del Trasferimento di stile"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "Richiesta negativa",
|
||||
"prompt": "Inserisci un messaggio",
|
||||
"selectStyle": "Seleziona uno stile",
|
||||
"styleStrength": "Forza dello stile"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "Digita la richiesta qui. Ad esempio: l'uomo non è un orso",
|
||||
"prompt": "Digita la richiesta qui. Ad esempio: un uomo che balla in stile anime"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "Forza"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "Caricamento file..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "Scegli tra caricati",
|
||||
"upload": "Carica un video"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "Scegli un video, poi scegli un modello di stile e poi lascia che StoryTeller.ai lo realizzi",
|
||||
"label": {
|
||||
"original": "Video originale"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "Annulla",
|
||||
"enqueue": "Applica il trasferimento di stile",
|
||||
"generateNew": "Trasferisci un altro video",
|
||||
"proceed": "Procedi al trasferimento dello stile",
|
||||
"upload": "Caricamento"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "Cambia lo stile del tuo video da uno all'altro",
|
||||
"title": "Trasferimento di stili video"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "Anteprima di un fotogramma dopo l'applicazione del Trasferimento di stile"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "Richiesta negativa",
|
||||
"prompt": "Inserisci un messaggio",
|
||||
"selectStyle": "Seleziona uno stile",
|
||||
"styleStrength": "Forza dello stile"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "Digita la richiesta qui. Ad esempio: l'uomo non è un orso",
|
||||
"prompt": "Digita la richiesta qui. Ad esempio: un uomo che balla in stile anime"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "Forza"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "Caricamento file..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "Scegli tra caricati",
|
||||
"upload": "Carica un video"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "Scegli un video, poi scegli un modello di stile e poi lascia che StoryTeller.ai lo realizzi",
|
||||
"label": {
|
||||
"original": "Video originale"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
"speechVc": "声から声へ",
|
||||
"videoFaceAnimator": "フェイスアニメーター",
|
||||
"videoMotionCapture": "ビデオモーションキャプチャ",
|
||||
"videoStyleTransfer": "ビデオスタイルの転送",
|
||||
"videoTitle": "ビデオ生成",
|
||||
"videoW2l": "ビデオリップシンク",
|
||||
"videoWorkflow": "ビデオワークフロー"
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "キャンセル",
|
||||
"enqueue": "スタイル転送の適用",
|
||||
"generateNew": "別のビデオを転送する",
|
||||
"proceed": "スタイル転送に進む",
|
||||
"upload": "アップロード"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "ビデオのスタイルを別のスタイルに変更する",
|
||||
"title": "ビデオスタイルの転送"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "スタイル転送適用後のフレームのプレビュー"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "否定的なプロンプト",
|
||||
"prompt": "プロンプトを入力してください",
|
||||
"selectStyle": "スタイルを選択してください",
|
||||
"styleStrength": "スタイルの強さ"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "ここに「プロンプト」と入力します。例: 人間はクマではありません",
|
||||
"prompt": "ここに「プロンプト」と入力します。例: アニメ風に踊る男性"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "強さ"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "ファイルをアップロードしています..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "アップロードされたものから選択",
|
||||
"upload": "動画をアップロードする"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "ビデオを選択し、スタイルのモデルを選択して、StoryTeller.ai にそれを実現させましょう",
|
||||
"label": {
|
||||
"original": "オリジナルビデオ"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "キャンセル",
|
||||
"enqueue": "スタイル転送の適用",
|
||||
"generateNew": "別のビデオを転送する",
|
||||
"proceed": "スタイル転送に進む",
|
||||
"upload": "アップロード"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "ビデオのスタイルを別のスタイルに変更する",
|
||||
"title": "ビデオスタイルの転送"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "スタイル転送適用後のフレームのプレビュー"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "否定的なプロンプト",
|
||||
"prompt": "プロンプトを入力してください",
|
||||
"selectStyle": "スタイルを選択してください",
|
||||
"styleStrength": "スタイルの強さ"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "ここに「プロンプト」と入力します。例: 人間はクマではありません",
|
||||
"prompt": "ここに「プロンプト」と入力します。例: アニメ風に踊る男性"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "強さ"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "ファイルをアップロードしています..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "アップロードされたものから選択",
|
||||
"upload": "動画をアップロードする"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "ビデオを選択し、スタイルのモデルを選択して、StoryTeller.ai にそれを実現させましょう",
|
||||
"label": {
|
||||
"original": "オリジナルビデオ"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
"speechVc": "음성 대 음성",
|
||||
"videoFaceAnimator": "얼굴 애니메이터",
|
||||
"videoMotionCapture": "비디오 모션 캡처",
|
||||
"videoStyleTransfer": "비디오 스타일 전송",
|
||||
"videoTitle": "비디오 생성",
|
||||
"videoW2l": "비디오 립싱크",
|
||||
"videoWorkflow": "비디오 워크플로우"
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "취소",
|
||||
"enqueue": "스타일 전송 적용",
|
||||
"generateNew": "다른 비디오 전송",
|
||||
"proceed": "스타일 이전으로 진행",
|
||||
"upload": "업로드"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "비디오 스타일을 다른 스타일로 변경하세요.",
|
||||
"title": "비디오 스타일 전송"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "스타일 전송 적용 후 프레임 미리보기"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "부정적인 프롬프트",
|
||||
"prompt": "프롬프트를 입력하세요",
|
||||
"selectStyle": "스타일을 선택하세요",
|
||||
"styleStrength": "스타일 강도"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "여기에 프롬프트를 입력하세요. 예: 사람은 곰이 아니다",
|
||||
"prompt": "여기에 프롬프트를 입력하세요. 예: 애니메이션 스타일로 춤추는 남자"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "힘"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "파일 업로드 중..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "업로드된 것 중에서 선택하세요",
|
||||
"upload": "동영상 업로드"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "비디오를 선택한 다음 스타일 모델을 선택한 다음 StoryTeller.ai를 통해 이를 구현해 보세요.",
|
||||
"label": {
|
||||
"original": "원본 영상"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "취소",
|
||||
"enqueue": "스타일 전송 적용",
|
||||
"generateNew": "다른 비디오 전송",
|
||||
"proceed": "스타일 이전으로 진행",
|
||||
"upload": "업로드"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "비디오 스타일을 다른 스타일로 변경하세요.",
|
||||
"title": "비디오 스타일 전송"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "스타일 전송 적용 후 프레임 미리보기"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "부정적인 프롬프트",
|
||||
"prompt": "프롬프트를 입력하세요",
|
||||
"selectStyle": "스타일을 선택하세요",
|
||||
"styleStrength": "스타일 강도"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "여기에 프롬프트를 입력하세요. 예: 사람은 곰이 아니다",
|
||||
"prompt": "여기에 프롬프트를 입력하세요. 예: 애니메이션 스타일로 춤추는 남자"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "힘"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "파일 업로드 중..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "업로드된 것 중에서 선택하세요",
|
||||
"upload": "동영상 업로드"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "비디오를 선택한 다음 스타일 모델을 선택한 다음 StoryTeller.ai를 통해 이를 구현해 보세요.",
|
||||
"label": {
|
||||
"original": "원본 영상"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
"speechVc": "Voz para Voz",
|
||||
"videoFaceAnimator": "Animador facial",
|
||||
"videoMotionCapture": "Captura de movimento de vídeo",
|
||||
"videoStyleTransfer": "Transferência de estilo de vídeo",
|
||||
"videoTitle": "Geração de Vídeo",
|
||||
"videoW2l": "Sincronização labial de vídeo",
|
||||
"videoWorkflow": "Fluxo de trabalho de vídeo"
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "Cancelar",
|
||||
"enqueue": "Aplicar transferência de estilo",
|
||||
"generateNew": "Transferir outro vídeo",
|
||||
"proceed": "Prossiga para a transferência de estilo",
|
||||
"upload": "Carregar"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "Mude o estilo do seu vídeo de um para outro",
|
||||
"title": "Transferência de estilo de vídeo"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "Visualização de um quadro após aplicar o Style Transfer"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "Alerta negativo",
|
||||
"prompt": "Insira um prompt",
|
||||
"selectStyle": "Selecione um estilo",
|
||||
"styleStrength": "Força do estilo"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "Digite prompt aqui. Ex.: O homem não é um urso",
|
||||
"prompt": "Digite prompt aqui. Ex.: Homem dançando no estilo anime"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "Força"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "Carregando arquivo..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "Escolha entre os enviados",
|
||||
"upload": "Carregar um vídeo"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "Escolha um vídeo, depois escolha um modelo de estilo e deixe StoryTeller.ai fazer acontecer",
|
||||
"label": {
|
||||
"original": "Vídeo Original"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "Cancelar",
|
||||
"enqueue": "Aplicar transferência de estilo",
|
||||
"generateNew": "Transferir outro vídeo",
|
||||
"proceed": "Prossiga para a transferência de estilo",
|
||||
"upload": "Carregar"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "Mude o estilo do seu vídeo de um para outro",
|
||||
"title": "Transferência de estilo de vídeo"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "Visualização de um quadro após aplicar o Style Transfer"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "Alerta negativo",
|
||||
"prompt": "Insira um prompt",
|
||||
"selectStyle": "Selecione um estilo",
|
||||
"styleStrength": "Força do estilo"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "Digite prompt aqui. Ex.: O homem não é um urso",
|
||||
"prompt": "Digite prompt aqui. Ex.: Homem dançando no estilo anime"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "Força"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "Carregando arquivo..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "Escolha entre os enviados",
|
||||
"upload": "Carregar um vídeo"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "Escolha um vídeo, depois escolha um modelo de estilo e deixe StoryTeller.ai fazer acontecer",
|
||||
"label": {
|
||||
"original": "Vídeo Original"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
"speechVc": "Голос в голос",
|
||||
"videoFaceAnimator": "Лицевой Аниматор",
|
||||
"videoMotionCapture": "Захват видео движения",
|
||||
"videoStyleTransfer": "Передача стиля видео",
|
||||
"videoTitle": "Создание видео",
|
||||
"videoW2l": "Видео синхронизация губ",
|
||||
"videoWorkflow": "Видео рабочий процесс"
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "Отмена",
|
||||
"enqueue": "Применить перенос стиля",
|
||||
"generateNew": "Перенести другое видео",
|
||||
"proceed": "Перейдите к переносу стиля",
|
||||
"upload": "Загрузить"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "Меняйте стиль вашего видео с одного на другой",
|
||||
"title": "Передача стиля видео"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "Предварительный просмотр кадра после применения Style Transfer"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "Отрицательная подсказка",
|
||||
"prompt": "Введите подсказку",
|
||||
"selectStyle": "Выберите стиль",
|
||||
"styleStrength": "Сила стиля"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "Введите подсказку здесь. Например: Человек не медведь",
|
||||
"prompt": "Введите подсказку здесь. Например: Мужчина танцует в стиле аниме."
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "Сила"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "Загрузка файла..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "Выбрать из загруженных",
|
||||
"upload": "Загрузить видео"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "Выберите видео, затем выберите модель стиля, а затем позвольте StoryTeller.ai воплотить это в жизнь.",
|
||||
"label": {
|
||||
"original": "Оригинальное видео"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "Отмена",
|
||||
"enqueue": "Применить перенос стиля",
|
||||
"generateNew": "Перенести другое видео",
|
||||
"proceed": "Перейдите к переносу стиля",
|
||||
"upload": "Загрузить"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "Меняйте стиль вашего видео с одного на другой",
|
||||
"title": "Передача стиля видео"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "Предварительный просмотр кадра после применения Style Transfer"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "Отрицательная подсказка",
|
||||
"prompt": "Введите подсказку",
|
||||
"selectStyle": "Выберите стиль",
|
||||
"styleStrength": "Сила стиля"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "Введите подсказку здесь. Например: Человек не медведь",
|
||||
"prompt": "Введите подсказку здесь. Например: Мужчина танцует в стиле аниме."
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "Сила"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "Загрузка файла..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "Выбрать из загруженных",
|
||||
"upload": "Загрузить видео"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "Выберите видео, затем выберите модель стиля, а затем позвольте StoryTeller.ai воплотить это в жизнь.",
|
||||
"label": {
|
||||
"original": "Оригинальное видео"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
"speechVc": "เสียงเป็นเสียง",
|
||||
"videoFaceAnimator": "แอนิเมชั่นใบหน้า",
|
||||
"videoMotionCapture": "จับภาพวิดีโอเคลื่อนไหว",
|
||||
"videoStyleTransfer": "การถ่ายโอนสไตล์วิดีโอ",
|
||||
"videoTitle": "การสร้างวิดีโอ",
|
||||
"videoW2l": "ลิปซิงค์วิดีโอ",
|
||||
"videoWorkflow": "เวิร์กโฟลว์วิดีโอ"
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "ยกเลิก",
|
||||
"enqueue": "ใช้การถ่ายโอนสไตล์",
|
||||
"generateNew": "ถ่ายโอนวิดีโออื่น",
|
||||
"proceed": "ดำเนินการโอนสไตล์",
|
||||
"upload": "ที่อัพโหลด"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "เปลี่ยนสไตล์วิดีโอของคุณจากที่หนึ่งไปอีกที่หนึ่ง",
|
||||
"title": "การถ่ายโอนสไตล์วิดีโอ"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "ดูตัวอย่างเฟรมหลังจากใช้ Style Transfer"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "พรอมต์เชิงลบ",
|
||||
"prompt": "ป้อนข้อความแจ้ง",
|
||||
"selectStyle": "เลือกสไตล์",
|
||||
"styleStrength": "ความแข็งแกร่งของสไตล์"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "พิมพ์ prompt ที่นี่ เช่น มนุษย์ไม่ใช่หมี",
|
||||
"prompt": "พิมพ์ prompt ที่นี่ เช่น ผู้ชายเต้นตามสไตล์อนิเมะ"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "ความแข็งแกร่ง"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "กำลังอัพโหลดไฟล์..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "เลือกจากการอัพโหลด",
|
||||
"upload": "อัปโหลดวิดีโอ"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "เลือกวิดีโอ จากนั้นเลือกโมเดลของสไตล์ จากนั้นปล่อยให้ StoryTeller.ai ทำให้มันเกิดขึ้น",
|
||||
"label": {
|
||||
"original": "วิดีโอต้นฉบับ"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "ยกเลิก",
|
||||
"enqueue": "ใช้การถ่ายโอนสไตล์",
|
||||
"generateNew": "ถ่ายโอนวิดีโออื่น",
|
||||
"proceed": "ดำเนินการโอนสไตล์",
|
||||
"upload": "ที่อัพโหลด"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "เปลี่ยนสไตล์วิดีโอของคุณจากที่หนึ่งไปอีกที่หนึ่ง",
|
||||
"title": "การถ่ายโอนสไตล์วิดีโอ"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "ดูตัวอย่างเฟรมหลังจากใช้ Style Transfer"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "พรอมต์เชิงลบ",
|
||||
"prompt": "ป้อนข้อความแจ้ง",
|
||||
"selectStyle": "เลือกสไตล์",
|
||||
"styleStrength": "ความแข็งแกร่งของสไตล์"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "พิมพ์ prompt ที่นี่ เช่น มนุษย์ไม่ใช่หมี",
|
||||
"prompt": "พิมพ์ prompt ที่นี่ เช่น ผู้ชายเต้นตามสไตล์อนิเมะ"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "ความแข็งแกร่ง"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "กำลังอัพโหลดไฟล์..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "เลือกจากการอัพโหลด",
|
||||
"upload": "อัปโหลดวิดีโอ"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "เลือกวิดีโอ จากนั้นเลือกโมเดลของสไตล์ จากนั้นปล่อยให้ StoryTeller.ai ทำให้มันเกิดขึ้น",
|
||||
"label": {
|
||||
"original": "วิดีโอต้นฉบับ"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
"speechVc": "Sesten Sese",
|
||||
"videoFaceAnimator": "Yüz Animatörü",
|
||||
"videoMotionCapture": "Video Hareket Yakalama",
|
||||
"videoStyleTransfer": "Video Stili Aktarımı",
|
||||
"videoTitle": "Video Oluşturma",
|
||||
"videoW2l": "Video Dudak Senkronizasyonu",
|
||||
"videoWorkflow": "Video İş Akışı"
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "İptal etmek",
|
||||
"enqueue": "Stil Transferini Uygula",
|
||||
"generateNew": "Başka bir videoyu aktar",
|
||||
"proceed": "Stil Transferine Devam Edin",
|
||||
"upload": "Yüklemek"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "Videonuzun stilini birinden diğerine değiştirin",
|
||||
"title": "Video Stili Aktarımı"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "Stil Aktarımı uygulandıktan sonra bir çerçevenin önizlemesi"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "Olumsuz İstem",
|
||||
"prompt": "Bir istem girin",
|
||||
"selectStyle": "Bir stil seçin",
|
||||
"styleStrength": "Stil Gücü"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "İstemi buraya yazın. Örnek: İnsan ayı değildir",
|
||||
"prompt": "İstemi buraya yazın. Örnek: Anime tarzında dans eden adam"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "Kuvvet"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "Dosya Yükleniyor..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "Yüklenenler arasından seçim yapın",
|
||||
"upload": "Video yüklemek"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "Bir video seçin, ardından bir stil modeli seçin ve bırakın StoryTeller.ai bunu gerçekleştirsin",
|
||||
"label": {
|
||||
"original": "Orijinal Video"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "İptal etmek",
|
||||
"enqueue": "Stil Transferini Uygula",
|
||||
"generateNew": "Başka bir videoyu aktar",
|
||||
"proceed": "Stil Transferine Devam Edin",
|
||||
"upload": "Yüklemek"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "Videonuzun stilini birinden diğerine değiştirin",
|
||||
"title": "Video Stili Aktarımı"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "Stil Aktarımı uygulandıktan sonra bir çerçevenin önizlemesi"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "Olumsuz İstem",
|
||||
"prompt": "Bir istem girin",
|
||||
"selectStyle": "Bir stil seçin",
|
||||
"styleStrength": "Stil Gücü"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "İstemi buraya yazın. Örnek: İnsan ayı değildir",
|
||||
"prompt": "İstemi buraya yazın. Örnek: Anime tarzında dans eden adam"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "Kuvvet"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "Dosya Yükleniyor..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "Yüklenenler arasından seçim yapın",
|
||||
"upload": "Video yüklemek"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "Bir video seçin, ardından bir stil modeli seçin ve bırakın StoryTeller.ai bunu gerçekleştirsin",
|
||||
"label": {
|
||||
"original": "Orijinal Video"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
"speechVc": "Giọng nói thành giọng nói",
|
||||
"videoFaceAnimator": "khuôn mặt hoạt hình",
|
||||
"videoMotionCapture": "Quay video chuyển động",
|
||||
"videoStyleTransfer": "Chuyển kiểu video",
|
||||
"videoTitle": "Tạo video",
|
||||
"videoW2l": "Hát nhép video",
|
||||
"videoWorkflow": "Quy trình làm việc video"
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "Hủy bỏ",
|
||||
"enqueue": "Áp dụng chuyển kiểu",
|
||||
"generateNew": "Chuyển một video khác",
|
||||
"proceed": "Tiến hành Chuyển kiểu",
|
||||
"upload": "Tải lên"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "Thay đổi phong cách video của bạn từ cái này sang cái khác",
|
||||
"title": "Chuyển kiểu video"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "Xem trước khung hình sau khi áp dụng Chuyển kiểu"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "Lời nhắc tiêu cực",
|
||||
"prompt": "Nhập lời nhắc",
|
||||
"selectStyle": "Chọn một phong cách",
|
||||
"styleStrength": "Sức mạnh phong cách"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "Nhập lời nhắc ở đây. Ví dụ: Con người không phải là gấu",
|
||||
"prompt": "Nhập lời nhắc ở đây. Ví dụ: Người đàn ông nhảy theo phong cách anime"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "Sức mạnh"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "Đang tải tệp lên..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "Chọn từ đã tải lên",
|
||||
"upload": "Tải lên một video"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "Chọn một video, sau đó chọn mô hình theo phong cách và để StoryTeller.ai thực hiện điều đó",
|
||||
"label": {
|
||||
"original": "Video gốc"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "Hủy bỏ",
|
||||
"enqueue": "Áp dụng chuyển kiểu",
|
||||
"generateNew": "Chuyển một video khác",
|
||||
"proceed": "Tiến hành Chuyển kiểu",
|
||||
"upload": "Tải lên"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "Thay đổi phong cách video của bạn từ cái này sang cái khác",
|
||||
"title": "Chuyển kiểu video"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "Xem trước khung hình sau khi áp dụng Chuyển kiểu"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "Lời nhắc tiêu cực",
|
||||
"prompt": "Nhập lời nhắc",
|
||||
"selectStyle": "Chọn một phong cách",
|
||||
"styleStrength": "Sức mạnh phong cách"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "Nhập lời nhắc ở đây. Ví dụ: Con người không phải là gấu",
|
||||
"prompt": "Nhập lời nhắc ở đây. Ví dụ: Người đàn ông nhảy theo phong cách anime"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "Sức mạnh"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "Đang tải tệp lên..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "Chọn từ đã tải lên",
|
||||
"upload": "Tải lên một video"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "Chọn một video, sau đó chọn mô hình theo phong cách và để StoryTeller.ai thực hiện điều đó",
|
||||
"label": {
|
||||
"original": "Video gốc"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
"speechVc": "语音到语音",
|
||||
"videoFaceAnimator": "脸部动画师",
|
||||
"videoMotionCapture": "视频动作捕捉",
|
||||
"videoStyleTransfer": "视频风格迁移",
|
||||
"videoTitle": "视频生成",
|
||||
"videoW2l": "视频唇形同步",
|
||||
"videoWorkflow": "视频工作流程"
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "取消",
|
||||
"enqueue": "应用风格转移",
|
||||
"generateNew": "传输另一个视频",
|
||||
"proceed": "继续风格转移",
|
||||
"upload": "上传"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "将视频风格从一种更改为另一种",
|
||||
"title": "视频风格迁移"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "应用样式传输后的帧预览"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "否定提示",
|
||||
"prompt": "输入提示",
|
||||
"selectStyle": "选择风格",
|
||||
"styleStrength": "风格实力"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "在此输入提示。例如:人不是熊",
|
||||
"prompt": "在此输入提示。例如:以动漫风格跳舞的男人"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "力量"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "正在上传文件..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "从上传的中选择",
|
||||
"upload": "上传视频"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "选择一个视频,然后选择一种风格的模型,然后让 StoryTeller.ai 实现它",
|
||||
"label": {
|
||||
"original": "原创视频"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"button": {
|
||||
"cancel": "取消",
|
||||
"enqueue": "应用风格转移",
|
||||
"generateNew": "传输另一个视频",
|
||||
"proceed": "继续风格转移",
|
||||
"upload": "上传"
|
||||
},
|
||||
"headings": {
|
||||
"subtitle": "将视频风格从一种更改为另一种",
|
||||
"title": "视频风格迁移"
|
||||
},
|
||||
"image": {
|
||||
"label": {
|
||||
"preview": "应用样式传输后的帧预览"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"label": {
|
||||
"negPrompt": "否定提示",
|
||||
"prompt": "输入提示",
|
||||
"selectStyle": "选择风格",
|
||||
"styleStrength": "风格实力"
|
||||
},
|
||||
"placeholder": {
|
||||
"negPrompt": "在此输入提示。例如:人不是熊",
|
||||
"prompt": "在此输入提示。例如:以动漫风格跳舞的男人"
|
||||
},
|
||||
"thumbtip": {
|
||||
"styleStrength": "力量"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"fileUploading": "正在上传文件..."
|
||||
},
|
||||
"tabTitle": {
|
||||
"library": "从上传的中选择",
|
||||
"upload": "上传视频"
|
||||
},
|
||||
"video": {
|
||||
"instruction": "选择一个视频,然后选择一种风格的模型,然后让 StoryTeller.ai 实现它",
|
||||
"label": {
|
||||
"original": "原创视频"
|
||||
}
|
||||
}
|
||||
}
|
||||
+127
-117
@@ -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,7 +208,8 @@ function initInferenceJobsByCategoryMap(): Map<
|
||||
inferenceJobsByCategory.set(FrontendInferenceJobType.VideoMotionCapture, []);
|
||||
inferenceJobsByCategory.set(FrontendInferenceJobType.ImageGeneration, []);
|
||||
inferenceJobsByCategory.set(FrontendInferenceJobType.ConvertFbxtoGltf, []);
|
||||
inferenceJobsByCategory.set(FrontendInferenceJobType.VideoWorkflow, [])
|
||||
inferenceJobsByCategory.set(FrontendInferenceJobType.VideoWorkflow, []);
|
||||
inferenceJobsByCategory.set(FrontendInferenceJobType.VideoStyleTransfer, []);
|
||||
return inferenceJobsByCategory;
|
||||
}
|
||||
|
||||
@@ -347,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 });
|
||||
}
|
||||
|
||||
@@ -855,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
|
||||
@@ -866,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>
|
||||
|
||||
@@ -12,6 +12,7 @@ interface Props{
|
||||
max: number;
|
||||
step?: number;
|
||||
initialValue?: number;
|
||||
value?: number;
|
||||
onChange?: (x:number) => void;
|
||||
required?: boolean;
|
||||
withRevert?: boolean;
|
||||
@@ -31,9 +32,11 @@ const renderTrack = ({ props: { style, ...props }, children }: any) => (
|
||||
const thumb =
|
||||
(thumbTip = "") =>
|
||||
({ props: { style, ...props } }: any) => {
|
||||
const key = Date.now()
|
||||
return (
|
||||
<Tippy
|
||||
{...{
|
||||
key,
|
||||
arrow: false,
|
||||
content: thumbTip,
|
||||
placement: "bottom",
|
||||
@@ -56,6 +59,7 @@ export default function NumberSlider({
|
||||
onChange: onChangeCallback,
|
||||
required,
|
||||
initialValue: initialValueProps,
|
||||
value,
|
||||
withRevert = false,
|
||||
propsButtonRevertToDefault,
|
||||
}: Props) {
|
||||
@@ -63,7 +67,8 @@ export default function NumberSlider({
|
||||
const step = stepProps && stepProps <= max-min ? stepProps
|
||||
: max-min >= 1 ? 1 : (max-min) / 10;
|
||||
const initialValue = initialValueProps !== undefined && initialValueProps <= max && initialValueProps >= min ? initialValueProps
|
||||
: max-min === step ? min : roundToStep((max+min)/2, step);
|
||||
: value !== undefined && value <=max && value>=min ? value
|
||||
:max-min === step ? min : roundToStep((max+min)/2, step);
|
||||
|
||||
function handleInputOnChange(e: React.ChangeEvent<HTMLInputElement>){
|
||||
if(onChangeCallback)onChangeCallback(Number.parseInt(e.target.value))
|
||||
@@ -81,7 +86,7 @@ export default function NumberSlider({
|
||||
<input
|
||||
className="fy-number-slider-value"
|
||||
type="number"
|
||||
{...{ min, max, step, value:initialValue}}
|
||||
{...{ min, max, step, value:value || initialValue}}
|
||||
onChange={handleInputOnChange}
|
||||
/>
|
||||
<div className="fy-number-slider-range">
|
||||
@@ -91,7 +96,7 @@ export default function NumberSlider({
|
||||
renderThumb: thumb(thumbTip),
|
||||
renderTrack,
|
||||
thumbTip,
|
||||
values: [initialValue],
|
||||
values: [value || initialValue],
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,28 +1,37 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useState, forwardRef } from "react";
|
||||
import { useMedia } from "hooks";
|
||||
import { MediaFile } from "@storyteller/components/src/api/media_files/GetMediaFile";
|
||||
import { BucketConfig } from "@storyteller/components/src/api/BucketConfig";
|
||||
import makeClass from "resources/makeClass";
|
||||
|
||||
import { Label } from "components/common"
|
||||
|
||||
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
src?: string;
|
||||
mediaToken?: string;
|
||||
label?: string;
|
||||
onResponse?: (res:any)=>void
|
||||
}
|
||||
|
||||
export default function VideoFromFakeyou({
|
||||
type Ref = HTMLVideoElement;
|
||||
|
||||
const VideoFromFakeyou = forwardRef<Ref, Props>(({
|
||||
className,
|
||||
src,
|
||||
mediaToken,
|
||||
label,
|
||||
onResponse,
|
||||
...rest
|
||||
}: Props) {
|
||||
}: Props, ref) => {
|
||||
const [mediaFile, setMediaFile] = useState<MediaFile>();
|
||||
useMedia({
|
||||
mediaToken: mediaToken,
|
||||
onSuccess: (res: any) => {
|
||||
setMediaFile(res)
|
||||
if(onResponse) onResponse(res)
|
||||
},
|
||||
});
|
||||
|
||||
@@ -31,11 +40,14 @@ export default function VideoFromFakeyou({
|
||||
if (mediaLink){
|
||||
return (
|
||||
<div {...{ ...makeClass("fy-basic-video",className) }}>
|
||||
<video controls key={mediaToken} {...rest}>
|
||||
{label && <Label label={label}/>}
|
||||
<video controls key={mediaToken} {...rest} ref={ref}>
|
||||
<source src={mediaLink} type="video/mp4" />
|
||||
</video>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return null;
|
||||
};
|
||||
});
|
||||
|
||||
export default VideoFromFakeyou;
|
||||
@@ -40,6 +40,7 @@ import TextArea from "./TextArea";
|
||||
import TextAreaV2 from "./TextAreaV2";
|
||||
import TintSpinner from "./TintSpinner";
|
||||
import VideoBasic from "./VideoBasic";
|
||||
import VideoFakeyou from "./VideoFakeyou";
|
||||
import VideoInput from "./VideoInput";
|
||||
|
||||
export {
|
||||
@@ -85,5 +86,6 @@ export {
|
||||
TextAreaV2,
|
||||
TintSpinner,
|
||||
VideoBasic,
|
||||
VideoFakeyou,
|
||||
VideoInput
|
||||
};
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
faCameraMovie,
|
||||
faCompass,
|
||||
faCloudUpload,
|
||||
faFilms,
|
||||
faFaceViewfinder,
|
||||
faHome,
|
||||
faMessageDots,
|
||||
@@ -145,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: {
|
||||
@@ -191,6 +193,7 @@ export default function SideNav({
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
history.push("/login");
|
||||
handleNavLinkClick();
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
@@ -203,6 +206,7 @@ export default function SideNav({
|
||||
small
|
||||
onClick={() => {
|
||||
history.push("/signup");
|
||||
handleNavLinkClick();
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
@@ -227,6 +231,7 @@ export default function SideNav({
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
history.push(url);
|
||||
handleNavLinkClick();
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
@@ -241,6 +246,7 @@ export default function SideNav({
|
||||
variant="danger"
|
||||
onClick={async () => {
|
||||
await logoutHandler();
|
||||
handleNavLinkClick();
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
@@ -305,6 +311,17 @@ export default function SideNav({
|
||||
</NavLink>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<NavLink
|
||||
to="/video-styletransfer"
|
||||
activeClassName="active-link"
|
||||
onClick={handleNavLinkClick}
|
||||
>
|
||||
<FontAwesomeIcon icon={faFilms} className="sidebar-heading-icon" />
|
||||
{t("videoStyleTransfer")}
|
||||
</NavLink>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<NavLink
|
||||
to="/studio"
|
||||
@@ -551,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">
|
||||
|
||||
@@ -8,6 +8,7 @@ import { ModerationIpBanListFc } from "./pages/moderation/moderation_ip_ban_list
|
||||
import { ModerationViewIpBanFc } from "./pages/moderation/moderation_view_ip_ban/ModerationViewIpBanFc";
|
||||
import FaceAnimator from "./pages/face_animator";
|
||||
import VideoMocap from "./pages/video_mocap";
|
||||
import VideoStyleTransfer from "./pages/video_styletransfer";
|
||||
import { ProfileEditFc } from "./pages/profile/profile_edit/ProfileEditFc";
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper";
|
||||
@@ -866,6 +867,20 @@ class PageContainer extends React.Component<
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/video-styletransfer">
|
||||
<VideoStyleTransfer
|
||||
{...{
|
||||
enqueueInferenceJob: this.props.enqueueInferenceJob,
|
||||
sessionSubscriptionsWrapper:
|
||||
this.props.sessionSubscriptionsWrapper,
|
||||
inferenceJobs: this.props.inferenceJobs,
|
||||
inferenceJobsByCategory:
|
||||
this.props.inferenceJobsByCategory,
|
||||
sessionWrapper: this.props.sessionWrapper,
|
||||
}}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/text-to-image">
|
||||
<TextToImagePage
|
||||
sessionSubscriptionsWrapper={
|
||||
|
||||
@@ -5,7 +5,7 @@ import React from "react";
|
||||
|
||||
export function StudioNotAvailable() {
|
||||
const subText = (
|
||||
<p> Please visit our <DiscordLink2 /> to join the wait list. </p>
|
||||
<> Please visit our <DiscordLink2 /> to join the wait list. </>
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import React, {useReducer, useEffect} from 'react';
|
||||
|
||||
import { SessionWrapper } from '@storyteller/components/src/session/SessionWrapper';
|
||||
import { StudioNotAvailable } from 'v2/view/_common/StudioNotAvailable';
|
||||
import { useInferenceJobs, useLocalize } from "hooks";
|
||||
import { FrontendInferenceJobType, InferenceJob } from '@storyteller/components/src/jobs/InferenceJob';
|
||||
|
||||
import { Container } from "components/common";
|
||||
import PageHeader from "components/layout/PageHeader";
|
||||
|
||||
import { states, reducer } from "./reducer";
|
||||
import SubRoutes from "./routes";
|
||||
|
||||
export default function VideoStyleTransfer(props:{
|
||||
enqueueInferenceJob: (
|
||||
jobToken: string,
|
||||
frontendInferenceJobType: FrontendInferenceJobType
|
||||
) => void;
|
||||
inferenceJobs: Array<InferenceJob>;
|
||||
inferenceJobsByCategory: Map<FrontendInferenceJobType, Array<InferenceJob>>;
|
||||
sessionWrapper: SessionWrapper;
|
||||
}){
|
||||
const debug = false;
|
||||
const {t} = useLocalize("VideoStyleTransfer");
|
||||
const { NO_FILE } = states;
|
||||
const [pageState, dispatchPageState] = useReducer(reducer, {
|
||||
status: NO_FILE,
|
||||
});
|
||||
|
||||
const { enqueueInferenceJob } = props;
|
||||
useInferenceJobs(
|
||||
FrontendInferenceJobType.VideoStyleTransfer
|
||||
);
|
||||
useEffect(() => {
|
||||
if (
|
||||
pageState.status === states.JOB_ENQUEUED &&
|
||||
pageState.inferenceJobToken
|
||||
) {
|
||||
enqueueInferenceJob(
|
||||
pageState.inferenceJobToken,
|
||||
FrontendInferenceJobType.VideoStyleTransfer
|
||||
);
|
||||
dispatchPageState({
|
||||
type: "enqueueJobSuccess",
|
||||
payload: { inferenceJobToken: undefined },
|
||||
});
|
||||
}
|
||||
}, [pageState, enqueueInferenceJob]);
|
||||
|
||||
if (!props.sessionWrapper.canAccessStudio()) {
|
||||
return <StudioNotAvailable />
|
||||
}
|
||||
|
||||
return(
|
||||
<Container type="panel" className="mb-5">
|
||||
{debug && <p>{`Status:${pageState.status} MediaToken:${pageState.mediaFileToken}`}</p>}
|
||||
<PageHeader
|
||||
title={t("headings.title")}
|
||||
subText={t("headings.subtitle")}
|
||||
/>
|
||||
|
||||
<SubRoutes {...{debug, t, pageState, dispatchPageState}}/>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
|
||||
export const inferenceFailures = (fail = "") => {
|
||||
switch (fail) {
|
||||
case "sample case":
|
||||
return "Sample Case, this should not have been shown";
|
||||
default:
|
||||
return "Unknown failure";
|
||||
}
|
||||
};
|
||||
|
||||
export function TableOfKeyValues(
|
||||
{
|
||||
keyValues,
|
||||
height
|
||||
}:{
|
||||
keyValues:{
|
||||
[key:string]:number|string|boolean|undefined
|
||||
};
|
||||
height?: number | string
|
||||
}){
|
||||
return(<table style={{
|
||||
display:'block',
|
||||
height: height || "100%",
|
||||
overflowY: "scroll",
|
||||
overflowX: "clip",
|
||||
border: "1px solid white",
|
||||
borderTopLeftRadius: "1rem",
|
||||
borderBottomLeftRadius: "1rem",
|
||||
}}><tbody>{
|
||||
Object.entries(keyValues).map(([key, val], index: number)=>{
|
||||
return <tr key={index}><td>{`${key}`}</td><td>{`${val}`}</td></tr>
|
||||
})
|
||||
}</tbody></table>);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import React, { memo } from 'react'
|
||||
import { NavLink } from 'react-router-dom';
|
||||
|
||||
import { FrontendInferenceJobType } from "@storyteller/components/src/jobs/InferenceJob";
|
||||
import InferenceJobsList from "components/layout/InferenceJobsList";
|
||||
import { Analytics } from "common/Analytics";
|
||||
import { Button, Spinner } from 'components/common';
|
||||
import { states, Action, State } from "../reducer";
|
||||
import { inferenceFailures } from '../commons';
|
||||
|
||||
export default memo (function PageWorkflowJoblist({
|
||||
t, pageState, dispatchPageState, parentPath
|
||||
}: {
|
||||
debug?: boolean;
|
||||
t: Function;
|
||||
pageState: State;
|
||||
parentPath: string
|
||||
dispatchPageState: (action: Action) => void;
|
||||
}) {
|
||||
|
||||
return(
|
||||
<>
|
||||
<div className="row mb-3">
|
||||
<NavLink to={`${parentPath}`}>
|
||||
<Button
|
||||
label={t("button.generateNew")}
|
||||
variant="primary"
|
||||
/>
|
||||
</NavLink>
|
||||
</div>
|
||||
{pageState.status === states.JOB_ENQUEUEING &&
|
||||
<div>
|
||||
<h2>t("message.requestingJob")</h2>
|
||||
<Spinner />
|
||||
</div>
|
||||
}
|
||||
<InferenceJobsList {...{
|
||||
showNoJobs:true,
|
||||
failures: inferenceFailures,
|
||||
onSelect: () => Analytics.voiceConversionClickDownload(),
|
||||
jobType: FrontendInferenceJobType.VideoStyleTransfer,
|
||||
}}/>
|
||||
</>
|
||||
);
|
||||
});
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
export type WorkflowValuesType = {
|
||||
// File Settings
|
||||
fileToken: string;
|
||||
outputPath: string;
|
||||
// Video Settings
|
||||
width: number;
|
||||
height: number;
|
||||
maxFrames: number;
|
||||
framesCap: number;
|
||||
skipFrames: number;
|
||||
everyNthFrame: number;
|
||||
inputFps: number;
|
||||
interpolationMultiplier: number;
|
||||
// Basic Inputs
|
||||
workflowConfig: string;
|
||||
seed: string;
|
||||
sdModelToken: string;
|
||||
loraModelToken: string;
|
||||
posPrompt: string;
|
||||
negPrompt: string;
|
||||
// Advance Options Values
|
||||
firstPass: number;
|
||||
upscalePass: number;
|
||||
motionScale: number;
|
||||
upscaleMultiplier: number;
|
||||
useEmptyLatent: boolean;
|
||||
useFaceDetailer: boolean;
|
||||
denoiseFaceDetailer: number;
|
||||
useLCM: boolean;
|
||||
lcmCFG: number;
|
||||
lcmSteps: number;
|
||||
// Control Net Values
|
||||
cnCanny: number;
|
||||
cnDepth: number;
|
||||
cnLinearAnime: number;
|
||||
cnLinearRealistic: number;
|
||||
cnOpenPose: number;
|
||||
cnPipeFace: number;
|
||||
cnSparse: number;
|
||||
cnTile: number;
|
||||
}
|
||||
|
||||
export function isInputValid(keyValues: {[key:string]:number|string|boolean|undefined}){
|
||||
return (
|
||||
keyValues.sdModelToken
|
||||
&& keyValues.sdModelToken !== ""
|
||||
&& keyValues.posPrompt
|
||||
&& keyValues.posPrompt !== ""
|
||||
);
|
||||
}
|
||||
|
||||
export function mapRequest(workflowValues: WorkflowValuesType){
|
||||
//TODO: improve the typing of this
|
||||
return {
|
||||
"uuid_idempotency_token": uuidv4(),
|
||||
"maybe_sd_model": workflowValues.sdModelToken,
|
||||
"maybe_workflow_config": workflowValues.workflowConfig,
|
||||
"maybe_input_file": workflowValues.fileToken,
|
||||
"maybe_output_path": workflowValues.outputPath,
|
||||
"maybe_json_modifications": {
|
||||
"$.510.inputs.Text": workflowValues.posPrompt,
|
||||
"$.8.inputs.text": workflowValues.negPrompt,
|
||||
"$.173.inputs.seed": workflowValues.seed,
|
||||
"$.401.inputs.Value": workflowValues.firstPass,
|
||||
"$.918.inputs.Value": workflowValues.motionScale,
|
||||
"$.137.inputs.Value": workflowValues.framesCap,
|
||||
"$.186.inputs.Value": workflowValues.skipFrames,
|
||||
"$.140.inputs.Value": workflowValues.everyNthFrame,
|
||||
"$.154.inputs.Value": workflowValues.inputFps,
|
||||
"$.445.inputs.number": workflowValues.interpolationMultiplier,
|
||||
"$.947.inputs.Value": workflowValues.cnTile,
|
||||
"$.800.inputs.Value": workflowValues.cnCanny,
|
||||
"$.797.inputs.Value": workflowValues.cnLinearAnime,
|
||||
"$.796.inputs.Value": workflowValues.cnLinearRealistic,
|
||||
"$.772.inputs.Value": workflowValues.cnDepth,
|
||||
"$.771.inputs.Value": workflowValues.cnOpenPose,
|
||||
"$.527.inputs.Value": workflowValues.cnPipeFace,
|
||||
"$.403.inputs.Value": workflowValues.cnSparse
|
||||
},
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export { default } from "./pageVSTApp";
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
const initialValues = {
|
||||
//config
|
||||
outputPath: "vid2vid/SparseUpscaleInterp_00001.mp4",
|
||||
workflowConfig: "weight_q8sz47gmfw2zx02snrbz88ns9",
|
||||
|
||||
//video Settings
|
||||
width: 544,
|
||||
height: 544,
|
||||
maxFrames: 17,
|
||||
framesCap: 16,
|
||||
skipFrames: 0,
|
||||
everyNthFrame: 2,
|
||||
inputFps: 24,
|
||||
interpolationMultiplier: 2,
|
||||
|
||||
//basics
|
||||
seed: "",
|
||||
sdModelToken: "",
|
||||
loraModelToken: "",
|
||||
posPrompt: "",
|
||||
negPrompt: "",
|
||||
|
||||
//advance
|
||||
firstPass: 1,
|
||||
upscalePass: 0.42,
|
||||
motionScale:1,
|
||||
upscaleMultiplier: 1.5,
|
||||
useEmptyLatent: false,
|
||||
useFaceDetailer: false,
|
||||
denoiseFaceDetailer: 0.45,
|
||||
useLCM: false,
|
||||
lcmCFG: 2,
|
||||
lcmSteps: 8,
|
||||
|
||||
//Control Net
|
||||
cnCanny: 0,
|
||||
cnDepth: 0,
|
||||
cnLinearAnime: 0,
|
||||
cnLinearRealistic: 0,
|
||||
cnOpenPose: 0,
|
||||
cnPipeFace: 0,
|
||||
cnSparse: 0.7,
|
||||
cnTile: 0,
|
||||
};
|
||||
export default initialValues;
|
||||
+232
@@ -0,0 +1,232 @@
|
||||
import React, { useRef, useState } from "react";
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import { useParams, useHistory } from "react-router-dom";
|
||||
|
||||
import EnqueueVideoStyleTransfer from "@storyteller/components/src/api/video_styleTransfer";
|
||||
import { Action, State } from "../../reducer";
|
||||
import { TableOfKeyValues } from "../../commons";
|
||||
import ivs from "./initialValues";
|
||||
import {
|
||||
isInputValid,
|
||||
mapRequest,
|
||||
WorkflowValuesType,
|
||||
} from "./helpers";
|
||||
|
||||
import {
|
||||
Button,
|
||||
Label,
|
||||
NumberSliderV2,
|
||||
Panel,
|
||||
SelectModal,
|
||||
TextArea,
|
||||
VideoFakeyou
|
||||
} from "components/common";
|
||||
|
||||
export default function PageVSTApp({
|
||||
debug, t, pageState, dispatchPageState, parentPath
|
||||
}: {
|
||||
debug?: boolean;
|
||||
t: Function;
|
||||
pageState: State;
|
||||
parentPath: string;
|
||||
dispatchPageState: (action: Action) => void;
|
||||
}) {
|
||||
const videoRef = useRef<HTMLVideoElement>(null);
|
||||
const { mediaToken } = useParams<any>();
|
||||
|
||||
const [workflowValues, setWorkflowValues] = useState<WorkflowValuesType>({
|
||||
fileToken: pageState.mediaFileToken || mediaToken,
|
||||
...ivs
|
||||
});
|
||||
|
||||
const [styleStrength, setStyleStrength]= useState<number>(8);
|
||||
|
||||
if(videoRef?.current){
|
||||
if(debug) console.log("set-up video element listeners");
|
||||
const ve = videoRef.current
|
||||
ve.onloadedmetadata = () =>{
|
||||
const newValues : {
|
||||
width?: number;
|
||||
height?: number;
|
||||
maxFrames?: number;
|
||||
framesCap?: number;
|
||||
} = {};
|
||||
if (ve.videoWidth && ve.videoHeight) {
|
||||
const aspectRatio = ve.videoWidth/ve.videoHeight
|
||||
if (aspectRatio > 1) newValues.width = 960;
|
||||
else if (aspectRatio < 1) newValues.height = 960
|
||||
else if (debug) console.log(`aspectRaio: ${aspectRatio}`);
|
||||
}
|
||||
if(ve.duration){
|
||||
newValues.maxFrames = Math.floor(ve.duration)*workflowValues.inputFps;
|
||||
newValues.framesCap = newValues.maxFrames;
|
||||
}
|
||||
setWorkflowValues((curr)=>({
|
||||
...curr,
|
||||
...newValues,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const handleOnChange = (key: string, newValue:any,) => {
|
||||
setWorkflowValues((curr)=>({...curr, [key]: newValue}));
|
||||
}
|
||||
const handleFramesCap = (newValue: number)=>{
|
||||
if(newValue - workflowValues.skipFrames >= 16)
|
||||
setWorkflowValues((curr)=>({...curr, framesCap: newValue}));
|
||||
}
|
||||
const handleSkipFrames = (newValue: number)=>{
|
||||
if(workflowValues.framesCap - newValue >= 16)
|
||||
setWorkflowValues((curr)=>({...curr, skipFrames: newValue}));
|
||||
}
|
||||
const handleStyleStrength = (value:number)=>{
|
||||
setStyleStrength(value);
|
||||
};
|
||||
const history = useHistory();
|
||||
const handleGenerate = ()=>{
|
||||
if(debug) console.log(workflowValues)
|
||||
|
||||
if (isInputValid(workflowValues)){
|
||||
const request = mapRequest(workflowValues);
|
||||
if (debug) console.log(request);
|
||||
EnqueueVideoStyleTransfer(request).then(res => {
|
||||
if (res.success && res.inference_job_token) {
|
||||
dispatchPageState({
|
||||
type: 'enqueueJobSuccess',
|
||||
payload: {
|
||||
inferenceJobToken: res.inference_job_token
|
||||
}
|
||||
})
|
||||
}else{
|
||||
console.log(res);
|
||||
}
|
||||
})
|
||||
dispatchPageState({type: 'enqueueJob'})
|
||||
history.push(`${parentPath}/jobs`);
|
||||
}else{
|
||||
alert("you must pick an sd weight and input a prompt");
|
||||
}
|
||||
}
|
||||
|
||||
return(
|
||||
<Panel className="mb-4 p-4">
|
||||
<div className="row g-3 mb-4">
|
||||
<div className="col-12 col-md-6">
|
||||
<VideoFakeyou
|
||||
label={t("video.label.original")}
|
||||
mediaToken={workflowValues.fileToken}
|
||||
ref={videoRef}
|
||||
onResponse={(res)=>{
|
||||
dispatchPageState({
|
||||
type: 'loadFileSuccess',
|
||||
payload: {
|
||||
mediaFile: res,
|
||||
mediaFileToken: pageState.mediaFileToken || mediaToken
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-12 col-md-6">
|
||||
<Label label={t("image.label.preview")}/>
|
||||
{debug && <TableOfKeyValues keyValues={workflowValues} height={400}/>}
|
||||
|
||||
{/*TODO: MAYBE INTEGRATE INTO PLAYER */}
|
||||
<br/>
|
||||
<NumberSliderV2 {...{
|
||||
min: 16, max: workflowValues.maxFrames, step: 1,
|
||||
value: workflowValues.framesCap,
|
||||
label: "Frames Cap",
|
||||
thumbTip: "24 frames = 1 sec",
|
||||
onChange: handleFramesCap
|
||||
}}/>
|
||||
<br/>
|
||||
<NumberSliderV2 {...{
|
||||
min: 0, max: workflowValues.maxFrames-16, step: 1,
|
||||
value: workflowValues.skipFrames,
|
||||
label: "Skip Frames",
|
||||
thumbTip: "24 frames = 1 sec",
|
||||
onChange: handleSkipFrames
|
||||
}}/>
|
||||
{/*TODO: END */}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div className="row g-3 mb-4">
|
||||
<div className="col-12 col-md-6">
|
||||
<SelectModal
|
||||
modalTitle={t("modal.title.selectStyle")}
|
||||
label={t("input.label.selectStyle")}
|
||||
onSelect={({token})=>{
|
||||
handleOnChange("sdModelToken", token);
|
||||
}}
|
||||
tabs={[
|
||||
{
|
||||
label: "All Weights",
|
||||
tabKey: "allWeights",
|
||||
typeFilter: "sd_1.5",
|
||||
searcher: true,
|
||||
type: "weights",
|
||||
},
|
||||
{
|
||||
label: "Bookmarked",
|
||||
tabKey: "bookmarkedWeights",
|
||||
typeFilter: "sd_1.5",
|
||||
searcher: false,
|
||||
type: "weights",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<br/>
|
||||
<NumberSliderV2 {...{
|
||||
min: 1, max: 10, step: 1,
|
||||
initialValue: styleStrength,
|
||||
label: t("input.label.styleStrength"),
|
||||
thumbTip: t("input.thumbtip.styleStrength"),
|
||||
withRevert:true,
|
||||
onChange: handleStyleStrength
|
||||
}}/>
|
||||
</div>
|
||||
<div className="col-12 col-md-6">
|
||||
<TextArea
|
||||
{...{
|
||||
label: t("input.label.prompt"),
|
||||
placeholder: t("input.placeholder.prompt"),
|
||||
onChange: (e:React.ChangeEvent<HTMLTextAreaElement>)=>handleOnChange("posPrompt", e.target.value),
|
||||
value: workflowValues.posPrompt,
|
||||
required: false,
|
||||
}}
|
||||
/>
|
||||
<br/>
|
||||
<br/>
|
||||
<TextArea
|
||||
{...{
|
||||
label: t("input.label.negPrompt"),
|
||||
placeholder: t("input.placeholder.negPrompt"),
|
||||
onChange: (e:React.ChangeEvent<HTMLTextAreaElement>)=>handleOnChange("negPrompt", e.target.value),
|
||||
value: workflowValues.negPrompt,
|
||||
required: false,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row g-3">
|
||||
<div className="col-12 d-flex justify-content-between">
|
||||
<NavLink to={`${parentPath}`}>
|
||||
<Button
|
||||
label={t("button.cancel")}
|
||||
// onClick={handleGenerate}
|
||||
variant="primary"
|
||||
/>
|
||||
</NavLink>
|
||||
<Button
|
||||
label={t("button.enqueue")}
|
||||
onClick={handleGenerate}
|
||||
variant="primary"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Panel>
|
||||
);
|
||||
};
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
import React from "react";
|
||||
|
||||
|
||||
import { FrontendInferenceJobType } from "@storyteller/components/src/jobs/InferenceJob";
|
||||
import InferenceJobsList from "components/layout/InferenceJobsList";
|
||||
import { Analytics } from "common/Analytics";
|
||||
import { inferenceFailures } from "../commons";
|
||||
|
||||
import { Panel, Tabs } from "components/common";
|
||||
|
||||
import { State, Action } from "../reducer";
|
||||
import TabContentUpload from "./tabContentUpload";
|
||||
import TabContentLibrary from "./tabContentLibrary";
|
||||
|
||||
|
||||
export default function PageVideoProvision({
|
||||
debug=false, parentPath, t, pageState, dispatchPageState
|
||||
}: {
|
||||
debug?: boolean;
|
||||
parentPath: string;
|
||||
t: Function;
|
||||
pageState: State;
|
||||
dispatchPageState: (action: Action) => void;
|
||||
}) {
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
label: t("tabTitle.upload"),
|
||||
content: <TabContentUpload {...{
|
||||
debug, t, pageState, dispatchPageState
|
||||
}} />,
|
||||
to: `${parentPath}/upload`,
|
||||
padding: true,
|
||||
},
|
||||
{
|
||||
label: t("tabTitle.library"),
|
||||
content: <TabContentLibrary {...{
|
||||
debug, t, pageState, dispatchPageState
|
||||
}} />,
|
||||
to: `${parentPath}/select-media`,
|
||||
padding: true,
|
||||
},
|
||||
];
|
||||
|
||||
return(
|
||||
<>
|
||||
<Panel className="mb-4">
|
||||
<div className="row g-0">
|
||||
<div className="col-12 col-md-6">
|
||||
<Tabs tabs={tabs} />
|
||||
</div>
|
||||
<div className="col-12 col-md-6 p-5 mt-3">
|
||||
<p>{t("video.instruction")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Panel>
|
||||
<InferenceJobsList {...{
|
||||
showNoJobs:false,
|
||||
failures: inferenceFailures,
|
||||
onSelect: () => Analytics.voiceConversionClickDownload(),
|
||||
jobType: FrontendInferenceJobType.VideoStyleTransfer,
|
||||
}}/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
import React from "react";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import { states, Action, State } from "../reducer";
|
||||
import { Button } from "components/common";
|
||||
import SelectModal, { SelectModalData } from "components/common/SelectModal/SelectModal";
|
||||
import VideoFakeyou from "components/common/VideoFakeyou";
|
||||
|
||||
export default function TabContentLibrary({
|
||||
t, pageState, dispatchPageState
|
||||
}: {
|
||||
t: Function;
|
||||
pageState: State;
|
||||
dispatchPageState: (action: Action) => void;
|
||||
}){
|
||||
const handleProceed = ()=>{
|
||||
dispatchPageState({type: "loadFile"})
|
||||
}
|
||||
const handleOnSelect = (data:SelectModalData)=>{
|
||||
if (data.token !== ""){
|
||||
dispatchPageState({
|
||||
type: "selectedFile",
|
||||
payload: {mediaFileToken: data.token}
|
||||
});
|
||||
}else{
|
||||
dispatchPageState({type: "clearedFile"});
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="row g-3">
|
||||
<div className="col-12">
|
||||
<SelectModal
|
||||
modalTitle="Select a Video"
|
||||
label="Select a Video"
|
||||
onSelect={handleOnSelect}
|
||||
tabs={[
|
||||
{
|
||||
label: "All Videos",
|
||||
tabKey: "allVideos",
|
||||
typeFilter: "video",
|
||||
searcher: false,
|
||||
type: "media",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
{pageState.mediaFileToken &&
|
||||
<VideoFakeyou mediaToken={pageState.mediaFileToken} />
|
||||
}
|
||||
</div>
|
||||
{pageState.status === states.FILE_SELECTED &&
|
||||
<div className="col-12 d-flex justify-content-center mt-5">
|
||||
<NavLink to={`load/${pageState.mediaFileToken}`}>
|
||||
<Button
|
||||
label={t("button.proceed")}
|
||||
onClick={handleProceed}
|
||||
/>
|
||||
</NavLink>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
import React from "react";
|
||||
import { Redirect } from "react-router-dom";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import { UploadMedia } from "@storyteller/components/src/api/media_files/UploadMedia";
|
||||
import { useFile } from "hooks";
|
||||
import { ErrorMessage, VideoInput, Spinner } from "components/common";
|
||||
import { states, Action, State } from "../reducer";
|
||||
|
||||
export default function TabContentUpload({
|
||||
debug=false, t, pageState, dispatchPageState
|
||||
}: {
|
||||
debug?: boolean;
|
||||
t: Function;
|
||||
pageState: State;
|
||||
dispatchPageState: (action: Action) => void;
|
||||
}) {
|
||||
const videoProps = useFile({});
|
||||
const {NO_FILE, FILE_STAGED, FILE_SELECTED, FILE_UPLOADING, FILE_UPLOADED} = states;
|
||||
|
||||
const makeVideoUploadRequest = () => ({
|
||||
uuid_idempotency_token: uuidv4(),
|
||||
file: videoProps.file,
|
||||
source: "file",
|
||||
type: "video",
|
||||
});
|
||||
|
||||
const handleUploadVideo = () => {
|
||||
dispatchPageState({type: 'uploadFile'});
|
||||
UploadMedia(makeVideoUploadRequest()).then(res => {
|
||||
if (res.success && res.media_file_token) {
|
||||
dispatchPageState({
|
||||
type: 'uploadFileSuccess',
|
||||
payload:{
|
||||
mediaFileToken :res.media_file_token
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// contains upload inout state and controls, see docs
|
||||
if (pageState.status === NO_FILE
|
||||
|| pageState.status === FILE_STAGED
|
||||
|| pageState.status === FILE_SELECTED
|
||||
) {
|
||||
return (
|
||||
<>
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<VideoInput
|
||||
{...{
|
||||
t,
|
||||
...videoProps,
|
||||
onStateChange: () =>{
|
||||
if (pageState.status === NO_FILE && videoProps.file)
|
||||
dispatchPageState({type: "stagedFile"})
|
||||
else if (pageState.status === FILE_STAGED && !videoProps.file)
|
||||
dispatchPageState({type: "clearedFile"})
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row py-3">
|
||||
<div className="col-12">
|
||||
<div className="d-flex justify-content-end gap-3">
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
disabled={pageState.status !== FILE_STAGED}
|
||||
onClick={handleUploadVideo}
|
||||
>
|
||||
{t("button.upload")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}else if (pageState.status === FILE_UPLOADING){
|
||||
return(
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<h1>{t("message.fileUploading")}</h1>
|
||||
</div>
|
||||
<div className="col-12">
|
||||
<Spinner />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}else if (pageState.status === FILE_UPLOADED){
|
||||
return(
|
||||
<Redirect to={`load/${pageState.mediaFileToken}`}/>
|
||||
)
|
||||
}
|
||||
return <ErrorMessage />;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
export {default} from "./VideoStyleTransfer";
|
||||
@@ -0,0 +1,87 @@
|
||||
import { MediaFile } from "@storyteller/components/src/api/media_files/GetMediaFile";
|
||||
|
||||
export enum states{
|
||||
NO_FILE,
|
||||
FILE_STAGED,
|
||||
FILE_SELECTED,
|
||||
FILE_UPLOADING,
|
||||
FILE_UPLOADED,
|
||||
FILE_LOADING,
|
||||
FILE_LOADED,
|
||||
JOB_ENQUEUEING,
|
||||
JOB_ENQUEUED
|
||||
}
|
||||
|
||||
export type State = {
|
||||
status: number;
|
||||
mediaFileToken?: string;
|
||||
mediaFile?: MediaFile;
|
||||
inferenceJobToken?: string;
|
||||
}
|
||||
|
||||
export type Action =
|
||||
| {type: 'reset'}
|
||||
| {type: 'stagedFile'}
|
||||
| {type: 'clearedFile'}
|
||||
| {type: 'selectedFile', payload: { mediaFileToken: string}}
|
||||
| {type: 'uploadFile'}
|
||||
| {type: 'uploadFileSuccess', payload:{ mediaFileToken: string}}
|
||||
| {type: 'loadFile'}
|
||||
| {type: 'loadFileSuccess', payload:{
|
||||
mediaFileToken: string, mediaFile: MediaFile
|
||||
}}
|
||||
| {type: 'enqueueJob'}
|
||||
| {type: 'enqueueJobSuccess', payload: {inferenceJobToken: string|undefined}}
|
||||
|
||||
|
||||
export function reducer (state: State, action: Action): State {
|
||||
|
||||
switch(action.type){
|
||||
case 'stagedFile':
|
||||
return {...state, status: states.FILE_STAGED}
|
||||
case 'clearedFile':
|
||||
return {...state,
|
||||
status: states.NO_FILE,
|
||||
mediaFileToken: ""
|
||||
}
|
||||
case 'selectedFile':
|
||||
return {
|
||||
...state,
|
||||
status: states.FILE_SELECTED,
|
||||
mediaFileToken: action.payload.mediaFileToken
|
||||
}
|
||||
case 'uploadFile':
|
||||
return {...state,status: states.FILE_UPLOADING};
|
||||
case 'uploadFileSuccess':
|
||||
return {
|
||||
...state,
|
||||
status: states.FILE_UPLOADED,
|
||||
mediaFileToken: action.payload.mediaFileToken
|
||||
}
|
||||
case 'loadFile':
|
||||
return {...state, status: states.FILE_LOADING}
|
||||
case 'loadFileSuccess':
|
||||
console.log("LOAD FILE SUCCESS");
|
||||
return {
|
||||
...state,
|
||||
status: states.FILE_LOADED,
|
||||
mediaFile: action.payload.mediaFile,
|
||||
mediaFileToken : action.payload.mediaFileToken,
|
||||
}
|
||||
case 'enqueueJob':
|
||||
return {
|
||||
...state,
|
||||
status: states.JOB_ENQUEUEING
|
||||
}
|
||||
case 'enqueueJobSuccess':
|
||||
return {
|
||||
...state,
|
||||
status: states.JOB_ENQUEUED,
|
||||
inferenceJobToken: action.payload.inferenceJobToken
|
||||
}
|
||||
case 'reset':
|
||||
default:
|
||||
return {status: states.NO_FILE};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import React from "react";
|
||||
import {Switch, Route, useRouteMatch, Redirect, useHistory} from "react-router-dom";
|
||||
import PageVSTApp from "./components/pageVSTApp";
|
||||
import PageVideoProvision from "./components/pageVideoProvision";
|
||||
import PageJoblist from "./components/pageJoblist";
|
||||
import {states,State,Action} from "./reducer";
|
||||
|
||||
export default function SubRoutes ({
|
||||
debug, t, pageState, dispatchPageState
|
||||
}:{
|
||||
debug?:boolean,
|
||||
t:Function,
|
||||
pageState:State,
|
||||
dispatchPageState: (action: Action) => void
|
||||
}){
|
||||
const { path } = useRouteMatch();
|
||||
const history = useHistory();
|
||||
history.listen(({pathname}, action) => {
|
||||
if ((pathname === path || pathname.match(/upload|select-media/g)) && pageState.status >= states.FILE_LOADING){
|
||||
//reset page if landed on page "Freshly"
|
||||
dispatchPageState({type: 'reset'})
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const commonPageProps = {
|
||||
debug, parentPath: path, t, pageState, dispatchPageState
|
||||
};
|
||||
|
||||
return(
|
||||
<Switch>
|
||||
<Route exact path={`${path}/load/:mediaToken`} >
|
||||
<PageVSTApp {...commonPageProps}/>
|
||||
</Route>
|
||||
<Route exact path={`${path}/upload`}>
|
||||
<PageVideoProvision {...commonPageProps}/>
|
||||
</Route>
|
||||
<Route exact path={`${path}/select-media`}>
|
||||
<PageVideoProvision {...commonPageProps}/>
|
||||
</Route>
|
||||
<Route exact path={`${path}/jobs`}>
|
||||
<PageJoblist {...commonPageProps}/>
|
||||
</Route>
|
||||
<Route path={`${path}`}>
|
||||
<Redirect to={`${path}/upload`} />
|
||||
</Route>
|
||||
</Switch>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import SubRoutes from "./videoWorkflowRoutes";
|
||||
import { SessionWrapper } from '@storyteller/components/src/session/SessionWrapper';
|
||||
import { StudioNotAvailable } from 'v2/view/_common/StudioNotAvailable';
|
||||
|
||||
export default function StorytellerFilter(props:{
|
||||
export default function VideoWorkflow(props:{
|
||||
enqueueInferenceJob: (
|
||||
jobToken: string,
|
||||
frontendInferenceJobType: FrontendInferenceJobType
|
||||
|
||||
+13
-4
@@ -24,6 +24,15 @@ export default memo( function SectionVideoSettings({
|
||||
}){
|
||||
const iv = VideoSettingsInitialValues;
|
||||
|
||||
const handleFramesCap = (newValue: number)=>{
|
||||
if(newValue - wfVal.skipFrames >= 16)
|
||||
handleOnChange("framesCap",newValue);
|
||||
}
|
||||
const handleSkipFrames = (newValue: number)=>{
|
||||
if(wfVal.framesCap - newValue >= 16)
|
||||
handleOnChange("skipFrames",newValue);
|
||||
}
|
||||
|
||||
if(ve && ve!==null){
|
||||
ve.onloadedmetadata = () =>{
|
||||
if (ve.videoWidth && ve.videoHeight) {
|
||||
@@ -60,19 +69,19 @@ export default memo( function SectionVideoSettings({
|
||||
<div className="col-md-6">
|
||||
<NumberSliderV2 {...{
|
||||
min: 16, max: wfVal.maxFrames, step: 1,
|
||||
initialValue: wfVal.framesCap,
|
||||
value: wfVal.framesCap,
|
||||
label: "Frames Cap",
|
||||
thumbTip: "24 frames = 1 sec",
|
||||
onChange: (val)=>{handleOnChange("framesCap",val)}
|
||||
onChange: handleFramesCap
|
||||
}}/>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<NumberSliderV2 {...{
|
||||
min: 0, max: wfVal.maxFrames-16, step: 1,
|
||||
initialValue: wfVal.skipFrames,
|
||||
value: wfVal.skipFrames,
|
||||
label: "Skip Frames",
|
||||
thumbTip: "24 frames = 1 sec",
|
||||
onChange: (val)=>{handleOnChange("skipFrames",val)}
|
||||
onChange: handleSkipFrames
|
||||
}}/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -196,6 +196,12 @@ export default function WeightPage({
|
||||
return (
|
||||
<div className="d-flex flex-column gap-3">
|
||||
<SdCoverImagePanel src={sdCoverImage} />
|
||||
{!!weight.description_rendered_html && (
|
||||
<Panel padding={true}>
|
||||
<h5 className="fw-semibold mb-2">Description</h5>
|
||||
<p className="fs-7">{weight.description_rendered_html}</p>
|
||||
</Panel>
|
||||
)}
|
||||
{imageGenPanel}
|
||||
</div>
|
||||
);
|
||||
@@ -487,14 +493,14 @@ 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>
|
||||
|
||||
{!!weight.description_rendered_html && (
|
||||
{ !!weight.description_rendered_html && (
|
||||
<Panel padding={true}>
|
||||
<h4 className="fw-semibold mb-3">Description</h4>
|
||||
<p>{weight.description_rendered_html}</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