mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-12 04:42:16 +00:00
PageScene: trim api_manager + drop dead model/error files
api_manager.ts goes from 460 lines to ~135. The video-render pipeline (uploadMediaFrameGeneration, stylizeVideo, StylizeVideoParams, getMediaBatch, getMediaFile, uploadMedia, gltfExport) was unreachable after videoGeneration was deleted. Surviving public methods: - saveSceneState (called by save_manager) - loadSceneState (called by save_manager) - uploadMediaSceneThumbnail (used internally by saveSceneState) Also drops the local ArtStyle / Visibility / MediaFile types — they only fed the deleted methods. The ArtStyle enum has a separate copy in ~/enums/ArtStyle.ts that other code uses. Delete two files that became unreferenced: - engine/APIManagerResponseError.ts (duplicate of the local class in api_manager.ts; nothing imported it) - models/generationOptions.ts (IGenerationOptions/GenerationOptions fed editor.generation_options, deleted last commit) Net: -1 typecheck error, -326 lines.
This commit is contained in:
1 parent
d09c7320fd
commit
7eef728d22
3 files changed
+13
-371
No files matched your search
@@ -1,12 +0,0 @@
|
||||
/**
|
||||
* This is designed to surface user customer facing messages as errors.
|
||||
* Errors shouldn't be 404 or something confusing should be
|
||||
*/
|
||||
class APIManagerResponseError extends Error {
|
||||
constructor(message?: string) {
|
||||
super(message);
|
||||
this.name = "APIManagerResponseError";
|
||||
}
|
||||
}
|
||||
|
||||
export { APIManagerResponseError };
|
||||
@@ -1,108 +1,18 @@
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import * as THREE from "three";
|
||||
import { GLTFExporter } from "three/addons/exporters/GLTFExporter.js";
|
||||
import { signalScene, startPollingActiveJobs } from "~/signals";
|
||||
import { signalScene } from "~/signals";
|
||||
import { updateExistingScene, uploadNewScene } from "./api_fetchers";
|
||||
import environmentVariables from "~/Classes/EnvironmentVariables";
|
||||
import { GetCdnOrigin } from "~/api/GetCdnOrigin";
|
||||
import { StorytellerApiHostStore } from "@storyteller/api";
|
||||
|
||||
interface StylizeVideoParams {
|
||||
media_token: string;
|
||||
style: ArtStyle;
|
||||
positive_prompt: string;
|
||||
negative_prompt: string;
|
||||
visibility: Visibility;
|
||||
use_face_detailer?: boolean;
|
||||
use_upscaler?: boolean;
|
||||
use_strength?: number;
|
||||
use_lipsync?: boolean;
|
||||
use_cinematic?: boolean;
|
||||
use_global_ipa_media_token?: string | null;
|
||||
input_depth_file?: string | null;
|
||||
input_normal_file?: string | null;
|
||||
input_outline_file?: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Storyteller Studio API Manager
|
||||
* The source of truth of all these media items is the database in the cloud
|
||||
*
|
||||
* The 3D editor used to drive a video-render pipeline through this
|
||||
* file (uploadMediaFrameGeneration, stylizeVideo, getMediaBatch,
|
||||
* etc.); all of that is gone. What's left handles scene save +
|
||||
* load and the cover-image thumbnail that goes with a save.
|
||||
*/
|
||||
export class MediaFile {
|
||||
public_bucket_path: string;
|
||||
media_type: string;
|
||||
media_token: string;
|
||||
|
||||
constructor(
|
||||
public_bucket_path: string,
|
||||
media_type: string,
|
||||
media_token: string,
|
||||
) {
|
||||
this.media_token = media_token;
|
||||
this.public_bucket_path = public_bucket_path;
|
||||
this.media_type = media_type;
|
||||
}
|
||||
}
|
||||
|
||||
export enum ArtStyle {
|
||||
Anime2_5D = "anime_2_5d",
|
||||
Anime2DFlat = "anime_2d_flat",
|
||||
Cartoon3D = "cartoon_3d",
|
||||
ComicBook = "comic_book",
|
||||
AnimeGhibli = "anime_ghibli",
|
||||
InkPunk = "ink_punk",
|
||||
InkSplash = "ink_splash",
|
||||
InkBWStyle = "ink_bw_style",
|
||||
JojoStyle = "jojo_style",
|
||||
PaperOrigami = "paper_origami",
|
||||
PixelArt = "pixel_art",
|
||||
PopArt = "pop_art",
|
||||
Realistic1 = "realistic_1",
|
||||
Realistic2 = "realistic_2",
|
||||
AnimeRetroNeon = "anime_retro_neon",
|
||||
AnimeStandard = "anime_standard",
|
||||
HRGiger = "hr_giger",
|
||||
Simpsons = "simpsons",
|
||||
Carnage = "carnage",
|
||||
PastelCuteAnime = "pastel_cute_anime",
|
||||
Bloom = "bloom_lighting",
|
||||
Horror2_5D = "25d_horror",
|
||||
Creepy = "creepy",
|
||||
CreepyVHS = "creepy_vhs",
|
||||
TrailCamFootage = "trail_cam_footage",
|
||||
OldBWMovie = "old_black_white_movie",
|
||||
HorrorNoirBW = "horror_noir_black_white",
|
||||
TechnoNoirBW = "techno_noir_black_white",
|
||||
BW20s = "black_white_20s",
|
||||
AnimeCyberpunk = "cyberpunk_anime",
|
||||
Dragonball = "dragonball",
|
||||
RealisticMatrix = "realistic_matrix",
|
||||
RealisticCyberpunk = "realistic_cyberpunk",
|
||||
}
|
||||
|
||||
export enum Visibility {
|
||||
Public = "public",
|
||||
Hidden = "hidden",
|
||||
Private = "private",
|
||||
}
|
||||
|
||||
/**
|
||||
* This is designed to surface user customer facing messages as errors.
|
||||
*/
|
||||
// type Data = { [key: string]: any };
|
||||
// class APIManagerResponseSuccess {
|
||||
// public user_message: string;
|
||||
// public data: Data | null;
|
||||
// constructor(user_message: string = "", data: Data | null = null) {
|
||||
// this.data = data;
|
||||
// this.user_message = user_message;
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* This is designed to surface user customer facing messages as errors.
|
||||
* Errors shouldn't be 404 or something confusing should be
|
||||
*/
|
||||
class APIManagerResponseError extends Error {
|
||||
constructor(message?: string) {
|
||||
super(message);
|
||||
@@ -111,21 +21,16 @@ class APIManagerResponseError extends Error {
|
||||
}
|
||||
|
||||
export class APIManager {
|
||||
|
||||
constructor() {
|
||||
// NB(bt,2025-07-06): Cleaning this up. The code was duplicated a half dozen times.
|
||||
// I think all of the code in this file is legacy, but to be safe I'm continuing to update it.
|
||||
}
|
||||
|
||||
protected getApiSchemeAndHost() {
|
||||
return StorytellerApiHostStore.getInstance().getApiSchemeAndHost();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param scene The 3JS Scene we want to save.
|
||||
* @param scene_name The Scene name we want to display
|
||||
* @param scene_media_file_token If null we will we will create a new save or copy the scene, if provided we will overwrite the scene.
|
||||
* @returns APIManagerResponseMessage
|
||||
* @param saveJson Serialized scene JSON to upload as the scene file.
|
||||
* @param sceneTitle Display title.
|
||||
* @param sceneToken If provided, overwrite that scene; otherwise a new scene is created.
|
||||
* @param sceneThumbnail Cover image to attach to the scene record.
|
||||
* @returns The media file token of the saved scene, or "" on failure.
|
||||
*/
|
||||
public async saveSceneState({
|
||||
saveJson,
|
||||
@@ -146,7 +51,6 @@ export class APIManager {
|
||||
? await updateExistingScene(file, sceneToken)
|
||||
: await uploadNewScene(file, sceneTitle);
|
||||
|
||||
// failed so catch this.
|
||||
if (uploadSceneResponse["success"] == false) {
|
||||
return "";
|
||||
}
|
||||
@@ -177,7 +81,6 @@ export class APIManager {
|
||||
}
|
||||
}
|
||||
|
||||
console.log(uploadSceneResponse);
|
||||
return uploadSceneResponse["media_file_token"];
|
||||
}
|
||||
|
||||
@@ -203,16 +106,13 @@ export class APIManager {
|
||||
});
|
||||
}
|
||||
const bucket_path = json["media_file"]["public_bucket_path"];
|
||||
const media_base_url = GetCdnOrigin();
|
||||
//const media_url = `${media_base_url}/vocodes-public${bucket_path}`; // gets you a bucket path
|
||||
const media_url = `${media_base_url}${bucket_path}`; // gets you a bucket path
|
||||
const media_url = `${GetCdnOrigin()}${bucket_path}`;
|
||||
|
||||
const file_response = await fetch(media_url);
|
||||
|
||||
if (!file_response.ok) {
|
||||
throw new APIManagerResponseError("Failed to download file");
|
||||
}
|
||||
// Convert the response from a blob to json text
|
||||
|
||||
const blob = await file_response.blob();
|
||||
const json_result: string = await new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
@@ -224,36 +124,6 @@ export class APIManager {
|
||||
return json_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This recieves the bucket path of a media file
|
||||
* @param media_file_token
|
||||
* @returns
|
||||
*/
|
||||
public async getMediaFile(media_file_token: string): Promise<string> {
|
||||
const url = `${this.getApiSchemeAndHost()}/v1/media_files/file/${media_file_token}`;
|
||||
const response = await fetch(url);
|
||||
const json = await JSON.parse(await response.text());
|
||||
const bucketPath = json["media_file"]["public_bucket_path"];
|
||||
const media_base_url = GetCdnOrigin();
|
||||
const media_url = `${media_base_url}${bucketPath}`; // gets you a bucket path
|
||||
return media_url;
|
||||
}
|
||||
|
||||
/**
|
||||
This will save the scene to keep ids positions.
|
||||
It will also give the file a name which will be a uuidv4()
|
||||
@param scene The 3JS Scene we want to make a file to be uploaded as multipart form.
|
||||
*/
|
||||
private async gltfExport(scene: THREE.Scene): Promise<File> {
|
||||
const gltfExporter = new GLTFExporter();
|
||||
const uuid = uuidv4();
|
||||
const result = await gltfExporter.parseAsync(scene);
|
||||
const file = new File([JSON.stringify(result)], `${uuid}.glb`, {
|
||||
type: "application/json",
|
||||
});
|
||||
return file;
|
||||
}
|
||||
|
||||
public async uploadMediaSceneThumbnail(blob: Blob | File, fileName: string) {
|
||||
const url = `${this.getApiSchemeAndHost()}/v1/media_files/upload/image`;
|
||||
const uuid = uuidv4();
|
||||
@@ -263,9 +133,6 @@ export class APIManager {
|
||||
formData.append("is_intermediate_system_file", "true");
|
||||
formData.append("maybe_title", "Screenshot");
|
||||
formData.append("file", blob, fileName);
|
||||
//formData.append("source", "file");
|
||||
//formData.append("type", "video");
|
||||
//formData.append("source", "file");
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
@@ -277,184 +144,7 @@ export class APIManager {
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new APIManagerResponseError("Upload Media Failed to send file");
|
||||
} else {
|
||||
const json_data = await response.json();
|
||||
//console.log(`uploadMedia: ${JSON.stringify(json_data)}`);
|
||||
return json_data;
|
||||
}
|
||||
}
|
||||
|
||||
public async uploadMedia({
|
||||
blob,
|
||||
fileName,
|
||||
title,
|
||||
styleName,
|
||||
maybe_scene_source_media_file_token,
|
||||
}: {
|
||||
blob: Blob;
|
||||
fileName: string;
|
||||
title: string;
|
||||
styleName?: string;
|
||||
maybe_scene_source_media_file_token: string | undefined;
|
||||
}) {
|
||||
// Promise<APIManagerResponseSuccess>
|
||||
//TODO: UPDATE ENDPOINT!!!!
|
||||
const url = `${this.getApiSchemeAndHost()}/v1/media_files/upload/new_video`;
|
||||
const uuid = uuidv4();
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("uuid_idempotency_token", uuid);
|
||||
|
||||
formData.append("file", blob, fileName);
|
||||
// formData.append("source", "file");
|
||||
// formData.append("type", "video");
|
||||
if (styleName) formData.append("maybe_style_name", styleName);
|
||||
formData.append("maybe_title", title);
|
||||
|
||||
// This signals to the backend to hide the video from view
|
||||
formData.append("is_intermediate_system_file", "true");
|
||||
if (maybe_scene_source_media_file_token !== undefined) {
|
||||
formData.append(
|
||||
"maybe_scene_source_media_file_token",
|
||||
maybe_scene_source_media_file_token,
|
||||
);
|
||||
}
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: formData,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new APIManagerResponseError("Upload Media Failed to send file");
|
||||
} else {
|
||||
const json_data = await response.json();
|
||||
//console.log(`uploadMedia: ${JSON.stringify(json_data)}`);
|
||||
return json_data;
|
||||
}
|
||||
}
|
||||
|
||||
public async uploadMediaFrameGeneration(
|
||||
blob: Blob | File,
|
||||
fileName: string,
|
||||
style: string = "comic_book",
|
||||
positive_prompt: string,
|
||||
negative_prompt: string,
|
||||
): Promise<string> {
|
||||
const url = `${environmentVariables.values.FUNNEL_API}/preview/`;
|
||||
|
||||
const payload = {
|
||||
style: style,
|
||||
positive_prompt: positive_prompt,
|
||||
negative_prompt: negative_prompt,
|
||||
};
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("input_file", blob, fileName);
|
||||
formData.append("request", JSON.stringify(payload));
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: formData,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new APIManagerResponseError("Upload Media Failed to send file");
|
||||
} else {
|
||||
return URL.createObjectURL(await response.blob());
|
||||
}
|
||||
}
|
||||
|
||||
public async getMediaBatch(media_tokens: string[]): Promise<MediaFile[]> {
|
||||
const tokens = media_tokens;
|
||||
const url = new URL(`${this.getApiSchemeAndHost()}/v1/media_files/batch`);
|
||||
tokens.forEach((token) => url.searchParams.append("tokens", token));
|
||||
const result = await fetch(url)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
const result = data["media_files"].map((element: any) => {
|
||||
return new MediaFile(
|
||||
element["public_bucket_path"],
|
||||
element["media_type"],
|
||||
element["token"],
|
||||
);
|
||||
});
|
||||
return result;
|
||||
})
|
||||
.catch((error) => console.error("Error:", error));
|
||||
return result;
|
||||
}
|
||||
|
||||
public async stylizeVideo({
|
||||
media_token,
|
||||
style,
|
||||
positive_prompt,
|
||||
negative_prompt,
|
||||
visibility,
|
||||
use_face_detailer = false,
|
||||
use_upscaler = false,
|
||||
use_strength = 1.0,
|
||||
use_lipsync = false,
|
||||
use_cinematic = false,
|
||||
use_global_ipa_media_token = null,
|
||||
input_depth_file = null,
|
||||
input_normal_file = null,
|
||||
input_outline_file = null,
|
||||
}: StylizeVideoParams) {
|
||||
const uuid = uuidv4();
|
||||
|
||||
const data = {
|
||||
uuid_idempotency_token: uuid,
|
||||
style: style,
|
||||
input_file: media_token,
|
||||
prompt: positive_prompt,
|
||||
negative_prompt: negative_prompt,
|
||||
trim_start_millis: 0,
|
||||
trim_end_millis: 7000,
|
||||
enable_lipsync: use_lipsync,
|
||||
creator_set_visibility: visibility,
|
||||
use_face_detailer: use_face_detailer,
|
||||
use_upscaler: use_upscaler,
|
||||
use_strength: use_strength,
|
||||
use_lipsync: use_lipsync,
|
||||
global_ipa_media_token: use_global_ipa_media_token,
|
||||
use_cinematic: use_cinematic,
|
||||
input_depth_file: input_depth_file,
|
||||
input_normal_file: input_normal_file,
|
||||
input_outline_file: input_outline_file,
|
||||
};
|
||||
|
||||
const json_data = JSON.stringify(data);
|
||||
|
||||
const response = await fetch(
|
||||
`${this.getApiSchemeAndHost()}/v1/workflows/enqueue_studio`,
|
||||
{
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: json_data,
|
||||
},
|
||||
);
|
||||
|
||||
startPollingActiveJobs();
|
||||
|
||||
if (!response.ok) {
|
||||
// Handle HTTP error responses
|
||||
const errorData = await response.json();
|
||||
throw new Error(`API Error: ${response.status} ${errorData.message}`);
|
||||
}
|
||||
|
||||
// Assuming the response is JSON and matches the EnqueueVideoStyleTransferSuccessResponse interface
|
||||
return await response.json();
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
export interface IGenerationOptions {
|
||||
upscale: boolean;
|
||||
faceDetail: boolean;
|
||||
styleStrength: number;
|
||||
lipSync: boolean;
|
||||
cinematic: boolean;
|
||||
globalIpAdapterImageMediaToken: string | null;
|
||||
enginePreProcessing: boolean;
|
||||
}
|
||||
|
||||
export class GenerationOptions implements IGenerationOptions {
|
||||
public upscale: boolean;
|
||||
public faceDetail: boolean;
|
||||
public styleStrength: number;
|
||||
public lipSync: boolean;
|
||||
public cinematic: boolean;
|
||||
public globalIpAdapterImageMediaToken: string | null;
|
||||
public enginePreProcessing: boolean;
|
||||
constructor(
|
||||
upscale: boolean,
|
||||
faceDetail: boolean,
|
||||
styleStrength: number,
|
||||
lipSync: boolean,
|
||||
cinematic: boolean,
|
||||
globalIpAdapterImageMediaToken: string | null,
|
||||
enginePreprocessing: boolean,
|
||||
) {
|
||||
this.upscale = upscale;
|
||||
this.faceDetail = faceDetail;
|
||||
this.styleStrength = styleStrength;
|
||||
this.lipSync = lipSync;
|
||||
this.cinematic = cinematic;
|
||||
this.globalIpAdapterImageMediaToken = globalIpAdapterImageMediaToken;
|
||||
this.enginePreProcessing = enginePreprocessing;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user