Merge branch 'feature/mvp' of https://github.com/storytold/storyteller-frontend into feature/mvp

This commit is contained in:
Victor committed 2024-02-13 14:07:07 -08:00
commit 7bd96db165
23 files changed
+190 -90

No files matched your search

@@ -2,7 +2,7 @@ import { ApiConfig } from "../ApiConfig";
export interface EnqueueImageGenRequest {
uuid_idempotency_token: string;
maybe_sd_model_token: string | undefined;
maybe_sd_model_token: string | null;
maybe_lora_model_token: string | null;
maybe_prompt: string;
maybe_n_prompt: string;
@@ -17,5 +17,8 @@
"original": "আসল",
"preview": "পূর্বরূপ",
"upload": "আপলোড করুন"
},
"video": {
"instruction": "লাইব্রেরি থেকে একটি ভিডিও আপলোড করুন বা নির্বাচন করুন, তারপর আমাদের ওয়ার্কফ্লো কন্ট্রোল প্যানেলের সাথে ফিল্টারিং ওয়ার্কফ্লো সেটআপ করুন৷"
}
}
@@ -17,5 +17,8 @@
"original": "Original",
"preview": "Vorschau",
"upload": "Hochladen"
},
"video": {
"instruction": "Laden Sie ein Video hoch oder wählen Sie es aus der Bibliothek aus und richten Sie dann den Filter-Workflow mit unserem Workflow-Kontrollfeld ein."
}
}
@@ -17,5 +17,8 @@
"original": "Original",
"preview": "Preview",
"upload": "Upload"
},
"video": {
"instruction": "Upload or select a video from the library, then setup the filtering workflow with our workflow control panel."
}
}
@@ -17,5 +17,8 @@
"original": "Original",
"preview": "Avance",
"upload": "Subir"
},
"video": {
"instruction": "Cargue o seleccione un video de la biblioteca, luego configure el flujo de trabajo de filtrado con nuestro panel de control de flujo de trabajo."
}
}
@@ -17,5 +17,8 @@
"original": "Original",
"preview": "Aperçu",
"upload": "Télécharger"
},
"video": {
"instruction": "Téléchargez ou sélectionnez une vidéo dans la bibliothèque, puis configurez le flux de travail de filtrage avec notre panneau de contrôle de flux de travail."
}
}
@@ -17,5 +17,8 @@
"original": "मूल",
"preview": "पूर्व दर्शन",
"upload": "डालना"
},
"video": {
"instruction": "लाइब्रेरी से एक वीडियो अपलोड करें या चुनें, फिर हमारे वर्कफ़्लो नियंत्रण कक्ष के साथ फ़िल्टरिंग वर्कफ़्लो सेटअप करें।"
}
}
@@ -17,5 +17,8 @@
"original": "Asli",
"preview": "Pratinjau",
"upload": "Mengunggah"
},
"video": {
"instruction": "Unggah atau pilih video dari perpustakaan, lalu atur alur kerja pemfilteran dengan panel kontrol alur kerja kami."
}
}
@@ -17,5 +17,8 @@
"original": "Originale",
"preview": "Anteprima",
"upload": "Caricamento"
},
"video": {
"instruction": "Carica o seleziona un video dalla libreria, quindi imposta il flusso di lavoro di filtraggio con il nostro pannello di controllo del flusso di lavoro."
}
}
@@ -17,5 +17,8 @@
"original": "オリジナル",
"preview": "プレビュー",
"upload": "アップロード"
},
"video": {
"instruction": "ライブラリからビデオをアップロードまたは選択し、ワークフロー コントロール パネルでフィルタリング ワークフローをセットアップします。"
}
}
@@ -17,5 +17,8 @@
"original": "원래의",
"preview": "시사",
"upload": "업로드"
},
"video": {
"instruction": "라이브러리에서 비디오를 업로드하거나 선택한 다음 워크플로 제어판을 사용하여 필터링 워크플로를 설정하세요."
}
}
@@ -17,5 +17,8 @@
"original": "Original",
"preview": "Visualização",
"upload": "Carregar"
},
"video": {
"instruction": "Carregue ou selecione um vídeo da biblioteca e configure o fluxo de trabalho de filtragem com nosso painel de controle de fluxo de trabalho."
}
}
@@ -17,5 +17,8 @@
"original": "Оригинал",
"preview": "Предварительный просмотр",
"upload": "Загрузить"
},
"video": {
"instruction": "Загрузите или выберите видео из библиотеки, а затем настройте процесс фильтрации с помощью нашей панели управления рабочим процессом."
}
}
@@ -17,5 +17,8 @@
"original": "ต้นฉบับ",
"preview": "ดูตัวอย่าง",
"upload": "ที่อัพโหลด"
},
"video": {
"instruction": "อัปโหลดหรือเลือกวิดีโอจากไลบรารี จากนั้นตั้งค่าเวิร์กโฟลว์การกรองด้วยแผงควบคุมเวิร์กโฟลว์ของเรา"
}
}
@@ -17,5 +17,8 @@
"original": "Orijinal",
"preview": "Ön izleme",
"upload": "Yüklemek"
},
"video": {
"instruction": "Kitaplıktan bir video yükleyin veya seçin, ardından iş akışı kontrol panelimizle filtreleme iş akışını ayarlayın."
}
}
@@ -17,5 +17,8 @@
"original": "Nguyên bản",
"preview": "Xem trước",
"upload": "Tải lên"
},
"video": {
"instruction": "Tải lên hoặc chọn video từ thư viện, sau đó thiết lập quy trình lọc bằng bảng điều khiển quy trình làm việc của chúng tôi."
}
}
@@ -17,5 +17,8 @@
"original": "原来的",
"preview": "预览",
"upload": "上传"
},
"video": {
"instruction": "从库中上传或选择视频,然后使用我们的工作流程控制面板设置过滤工作流程。"
}
}
@@ -46,8 +46,6 @@ const SelectModal = memo(
activeTab: tabs[0].tabKey,
valueType: tabs[0].typeFilter || "all",
});
console.log("🔫", tabs, activeTab, valueType);
// Update mediaType when activeTab changes
useEffect(() => {
const currentTab = tabs.find(tab => tab.tabKey === activeTab);
@@ -15,7 +15,7 @@ import React, { useEffect, useState } from "react";
import { Link, useHistory } from "react-router-dom";
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
import { Logout } from "@storyteller/components/src/api/session/Logout";
import { useModal } from "hooks";
import { useModal, useSession } from "hooks";
import { InferenceJobsModal } from "components/modals";
import { useDomainConfig } from "context/DomainConfigContext";
import NavItem from "../../common/NavItem/NavItem";
@@ -112,12 +112,14 @@ export default function TopNav({
let profileDropdown = <></>;
const { user } = useSession();
if (sessionWrapper.isLoggedIn()) {
let displayName = sessionWrapper.getDisplayName();
let username = sessionWrapper.getUsername();
let emailHash = sessionWrapper.getEmailGravatarHash();
let avatarIndex = 0; //temporary
let backgroundColorIndex = 0; //temporary
let displayName = user.display_name;
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;
profileDropdown = (
<ProfileDropdown
@@ -17,7 +17,7 @@ import {
faLink,
faFileCircleXmark,
faArrowRightArrowLeft,
faVideoPlus
faVideoPlus,
} from "@fortawesome/pro-solid-svg-icons";
import Accordion from "components/common/Accordion";
import DataTable from "components/common/DataTable";
@@ -41,7 +41,11 @@ export default function MediaPage() {
const { token } = useParams<{ token: string }>();
const bookmarks = useBookmarks();
const ratings = useRatings();
const { media: mediaFile, remove, status } = useMedia({
const {
media: mediaFile,
remove,
status,
} = useMedia({
mediaToken: token,
onSuccess: (res: any) => {
ratings.gather({ res, key: "token" });
@@ -52,8 +56,6 @@ export default function MediaPage() {
const dateCreated = moment(mediaFile?.created_at || "").format("LLL");
const [buttonLabel, setButtonLabel] = useState("Copy");
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const closeDeleteModal = () => setIsDeleteModalOpen(false);
@@ -296,7 +298,7 @@ export default function MediaPage() {
break;
default:
mediaDetails = defaultDetails;
break
break;
}
let modMediaDetails = undefined;
@@ -370,7 +372,14 @@ export default function MediaPage() {
<div className="d-flex gap-3 flex-wrap align-items-center">
<div className="d-flex gap-2 align-items-center flex-wrap">
<div>
<Badge {...{ className: `fy-entity-type-${ mediaFile?.media_type || "" }`, label: mediaType, }}/>
<Badge
{...{
className: `fy-entity-type-${
mediaFile?.media_type || ""
}`,
label: mediaType,
}}
/>
</div>
{subtitleDivider}
@@ -424,7 +433,7 @@ export default function MediaPage() {
<div className="col-12 col-xl-4">
<div className="panel panel-clear d-flex flex-column gap-3">
<div className="d-flex gap-2 flex-wrap">
{ mediaFile?.media_type === MediaFileType.Audio ? (
{mediaFile?.media_type === MediaFileType.Audio ? (
<Button
{...{
icon: faFaceViewfinder,
@@ -434,8 +443,8 @@ export default function MediaPage() {
className: "flex-grow-1",
}}
/>
) : null }
{ mediaFile?.media_type === MediaFileType.BVH ? (
) : null}
{mediaFile?.media_type === MediaFileType.BVH ? (
<Button
{...{
icon: faVideoPlus,
@@ -445,7 +454,7 @@ export default function MediaPage() {
className: "flex-grow-1",
}}
/>
) : null }
) : null}
{mediaFile?.media_type !== MediaFileType.Audio && (
<Button
@@ -455,6 +464,7 @@ export default function MediaPage() {
href={audioLink}
download={audioLink}
variant="secondary"
target="_blank"
/>
)}
{mediaFile?.media_type === MediaFileType.Audio && (
@@ -466,6 +476,7 @@ export default function MediaPage() {
href={audioLink}
download={audioLink}
tooltip="Download"
target="_blank"
/>
</div>
)}
@@ -639,7 +650,7 @@ export default function MediaPage() {
handleClose={closeDeleteModal}
title="Delete Media"
content={() => (
<>{`Are you sure you want to delete this media file? This action cannot be undone.`}</> // replace w/ dynamic later -V
<>{`Are you sure you want to delete this media file? This action cannot be undone.`}</> // replace w/ dynamic later -V
// <>{`Are you sure you want to delete "${title}"? This action cannot be undone.`}</>
)}
onConfirm={deleteMedia}
@@ -46,7 +46,7 @@ export default function PageVideoProvision({
<div className="col-12 col-md-6">
<Panel padding={true} clear={true}>
<BasicVideo
title={t("video.sample")}
title={t("video.instruction")}
src="/videos/face-animator-instruction-en.mp4"
/>
</Panel>
@@ -110,6 +110,7 @@ export default function WeightPage({
const deleteWeight = () => remove(!!user?.can_ban_users);
//Studio Access feature flag
switch (weight?.weight_type) {
case WeightType.SD_15:
case WeightType.SDXL:
@@ -119,6 +120,34 @@ export default function WeightPage({
}
}
//Image generation panel if it's a lora weight or sd weight
let imageGenPanel = <></>;
switch (weight?.weight_type) {
case WeightType.SD_15:
case WeightType.SDXL:
imageGenPanel = (
<SdInferencePanel
inferenceJobs={inferenceJobs}
sessionSubscriptionsWrapper={sessionSubscriptionsWrapper}
weight_token={weight?.weight_token}
enqueueInferenceJob={enqueueInferenceJob}
weightPageType="sd"
/>
);
break;
case WeightType.LORA:
imageGenPanel = (
<SdInferencePanel
inferenceJobs={inferenceJobs}
sessionSubscriptionsWrapper={sessionSubscriptionsWrapper}
weight_token={weight?.weight_token}
enqueueInferenceJob={enqueueInferenceJob}
weightPageType="lora"
/>
);
break;
}
function renderWeightComponent(weight: Weight) {
switch (weight.weight_category) {
case WeightCategory.TTS:
@@ -168,12 +197,7 @@ export default function WeightPage({
return (
<div className="d-flex flex-column gap-3">
<SdCoverImagePanel src={sdCoverImage} />
<SdInferencePanel
inferenceJobs={inferenceJobs}
sessionSubscriptionsWrapper={sessionSubscriptionsWrapper}
sd_model_token={weight.weight_token}
enqueueInferenceJob={enqueueInferenceJob}
/>
{imageGenPanel}
</div>
);
default:
@@ -1,4 +1,4 @@
import React, { useRef, useState, memo } from "react";
import React, { useRef, useState, memo, useEffect } from "react";
import {
Accordion,
Button,
@@ -18,7 +18,6 @@ import {
faRectangleLandscape,
faRectanglePortrait,
faSquare,
faUpload,
} from "@fortawesome/pro-solid-svg-icons";
import { v4 as uuidv4 } from "uuid";
import {
@@ -34,30 +33,43 @@ import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session
import PremiumLock from "components/PremiumLock";
import InferenceJobsList from "components/layout/InferenceJobsList";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Link } from "react-router-dom";
interface SdInferencePanelProps {
inferenceJobs: Array<InferenceJob>;
sessionSubscriptionsWrapper: SessionSubscriptionsWrapper;
sd_model_token?: string;
weight_token?: string;
enqueueInferenceJob: (
jobToken: string,
frontendInferenceJobType: FrontendInferenceJobType
) => void;
isStandalone?: boolean;
weightPageType?: "lora" | "sd";
}
function SdInferencePanel({
enqueueInferenceJob,
sd_model_token,
weight_token,
sessionSubscriptionsWrapper,
isStandalone = false,
inferenceJobs,
weightPageType = "sd",
}: SdInferencePanelProps) {
const [loraToken, setLoraToken] = useState<string | null>(null);
const [weightToken, setWeightToken] = useState(sd_model_token);
const [sdToken, setSdToken] = useState<string | null>(null);
useEffect(() => {
if (weightPageType === "sd") {
setSdToken(weight_token || null);
setLoraToken(null);
} else if (weightPageType === "lora") {
setLoraToken(weight_token || null);
setSdToken(null);
}
}, [weightPageType, weight_token]);
const handleOnWeightSelect = (data: { token: string; title: string }) => {
setWeightToken(data.token);
setSdToken(data.token);
};
const handleOnSelect = (data: { token: string; title: string }) => {
setLoraToken(data.token);
@@ -243,7 +255,7 @@ function SdInferencePanel({
) => {
ev.preventDefault();
if (!prompt || weightToken === undefined) {
if (!prompt || sdToken === undefined) {
return false;
}
@@ -258,10 +270,12 @@ function SdInferencePanel({
internalSeed.current = generateRandomSeed();
}
console.log(sdToken);
const request = {
uuid_idempotency_token: uuidv4(),
maybe_sd_model_token: isStandalone ? weightToken : sd_model_token,
maybe_lora_model_token: loraToken,
maybe_sd_model_token: sdToken || null,
maybe_lora_model_token: loraToken || null,
maybe_prompt: prompt,
maybe_n_prompt: negativePrompt,
maybe_seed: internalSeed.current,
@@ -305,29 +319,16 @@ function SdInferencePanel({
<div>
<SplitPanel dividerHeader={true}>
<SplitPanel.Header padding={true}>
<div className="d-flex flex-wrap gap-2 align-items-center">
<h4 className="fw-semibold mb-0 flex-grow-1">Generate an Image</h4>
{isStandalone && (
<div>
<Button
icon={faUpload}
label="Upload Weight"
variant="secondary"
small={true}
to="/upload/sd"
/>
</div>
)}
</div>
<h4 className="fw-semibold mb-0 flex-grow-1">Generate an Image</h4>
</SplitPanel.Header>
<SplitPanel.Body padding={true}>
<div className="d-flex flex-column gap-3 mb-4">
{isStandalone && (
{(isStandalone || weightPageType === "lora") && (
<SelectModal
required={true}
modalTitle="Select a Weight"
label="Select a Weight"
modalTitle="Select Stable Diffusion Weight"
label="Select a Stable Diffusion Weight"
onSelect={handleOnWeightSelect}
tabs={[
{
@@ -433,27 +434,30 @@ function SdInferencePanel({
value: samples,
}}
/>
<SelectModal
label="Additional LoRA Weight"
onSelect={handleOnSelect}
tabs={[
{
label: "All LoRA Weights",
tabKey: "allLoraWeights",
typeFilter: "loRA",
searcher: true,
type: "weights",
},
{
label: "Bookmarked",
tabKey: "bookmarkedLoraWeights",
typeFilter: "loRA",
searcher: false,
type: "weights",
onlyBookmarked: true,
},
]}
/>
{weightPageType === "sd" && (
<SelectModal
modalTitle="Select LoRA Weight"
label="Additional LoRA Weight"
onSelect={handleOnSelect}
tabs={[
{
label: "All LoRA Weights",
tabKey: "allLoraWeights",
typeFilter: "loRA",
searcher: true,
type: "weights",
},
{
label: "Bookmarked",
tabKey: "bookmarkedLoraWeights",
typeFilter: "loRA",
searcher: false,
type: "weights",
onlyBookmarked: true,
},
]}
/>
)}
<div>
<Label label="Number of Generations" />
<PremiumLock
@@ -473,22 +477,35 @@ function SdInferencePanel({
</div>
</Accordion.Item>
</Accordion>
<div className="d-flex gap-2 justify-content-end mt-4 mb-3">
<Button
{...{
label: "Clear All",
variant: "secondary",
onClick: resetToInitialState,
}}
/>
<Button
{...{
label: "Generate Image",
disabled: prompt === "" || weightToken === "",
onClick: handleEnqueueImageGen,
isLoading: isEnqueuing,
}}
/>
<div className="d-flex mt-4 align-items-center flex-wrap gap-4">
{isStandalone && (
<div className="flex-grow-1">
<p className="fs-7">
<span className="opacity-75">
Can't find the model weights you're looking for?
</span>{" "}
<Link to="/upload/sd">Upload your own!</Link>
</p>
</div>
)}
<div className="d-flex gap-2 justify-content-end flex-grow-1">
<Button
{...{
label: "Clear/Reset ",
variant: "secondary",
onClick: resetToInitialState,
}}
/>
<Button
{...{
label: "Generate Image",
disabled: prompt === "" || sdToken === "",
onClick: handleEnqueueImageGen,
isLoading: isEnqueuing,
}}
/>
</div>
</div>
</SplitPanel.Body>