Compare commits

...
10 changed files with 64 additions and 12 deletions
@@ -37,10 +37,10 @@ pub async fn enqueue_veo_2_image_to_video_webhook<U: IntoUrl, V: IntoUrl>(
) -> Result<WebhookResponse, FalErrorPlus> {
let duration = match args.duration {
Veo2Duration::Default => None,
Veo2Duration::FiveSeconds => Some("5".to_string()),
Veo2Duration::SixSeconds => Some("6".to_string()),
Veo2Duration::SevenSeconds => Some("7".to_string()),
Veo2Duration::EightSeconds => Some("8".to_string()),
Veo2Duration::FiveSeconds => Some("5s".to_string()),
Veo2Duration::SixSeconds => Some("6s".to_string()),
Veo2Duration::SevenSeconds => Some("7s".to_string()),
Veo2Duration::EightSeconds => Some("8s".to_string()),
};
let aspect_ratio = match args.aspect_ratio {
@@ -14,7 +14,20 @@ export interface JobMaybeResult {
entity_type: string;
maybe_public_bucket_media_path: string;
maybe_successfully_completed_at: string;
media_links?: JobMediaLinks;
}
export interface JobMediaLinks {
cdn_url: string;
maybe_thumbnail_template?: string;
maybe_video_previews?: JobMediaLinksVideoPreviews;
}
export interface JobMediaLinksVideoPreviews {
still: string;
animated: string;
}
export interface JobRequest {
inference_category: string;
maybe_model_title: string;
@@ -66,7 +66,7 @@ import {
removeImageDropListener,
} from "@storyteller/ui-gallery-modal";
import {
imageGenerationModels,
instructiveImageEditModels,
ModelCategory,
// ModelCategory,
ModelSelector,
@@ -536,7 +536,7 @@ export const PageEditor = () => {
<div className="absolute bottom-6 left-6 z-20 flex items-center gap-2">
<ModelSelector
items={imageGenerationModels}
items={instructiveImageEditModels}
category={ModelCategory.Editor3D}
panelTitle="Select Model"
panelClassName="min-w-[280px]"
@@ -10,11 +10,13 @@ import { JobsApi } from "~/Classes/ApiManager/JobsApi";
import { toast } from "@storyteller/ui-toaster";
import { ActiveJob } from "~/pages/PageEnigma/models";
import { IsJobPollingDisabled } from "~/flags/DevConsoleFlags";
// TODO ensure we de-dupe all this extra code.
interface CompletedItem {
token: string;
maybe_title: string;
public_bucket_path: string;
public_bucket_path: string; // TODO: STOP USING THIS.
maybe_media_links_thumbnail?: string;
updated_at: string;
}
@@ -140,6 +142,12 @@ export function Activity() {
maybe_title: job.request?.maybe_model_title || "Image Generation",
public_bucket_path:
job.maybe_result?.maybe_public_bucket_media_path || "",
maybe_media_links_thumbnail: ((job) => {
// Try the video thumbnail first, then the regular thumbnail.
const maybeVideoThumb = job.maybe_result?.media_links?.maybe_video_previews?.animated;
const maybeGenericThumb = (job.maybe_result?.media_links?.maybe_thumbnail_template || "").replace("{WIDTH}", "500");
return maybeVideoThumb || maybeGenericThumb;
})(job),
updated_at: job.updated_at || new Date().toISOString(),
}));
@@ -12,6 +12,7 @@ interface Props {
updated_at: string;
public_bucket_path: string;
maybe_style_name?: string;
maybe_media_links_thumbnail?: string;
};
}
@@ -21,9 +22,13 @@ export function CompletedCard({ job }: Props) {
const cdnOrigin = GetCdnOrigin();
const mediaPath = job?.public_bucket_path || "";
const downloadLink = `${cdnOrigin}${mediaPath}`;
const imageUrl = downloadLink;
//const imageUrl = downloadLink;
const fullImageUrl = downloadLink;
// NB: Try to use the thumbnail instead of the link to the full asset.
// Passing around full asset URLs and putting them into image tags is causing CORS errors if those assets are not images.
const thumbnailUrl = job?.maybe_media_links_thumbnail;
if (!job) return null;
return (
@@ -36,11 +41,11 @@ export function CompletedCard({ job }: Props) {
<div className="flex aspect-square h-14 w-14 justify-center overflow-hidden rounded-lg border border-[#A9A9A9]/50 bg-black/60">
<img
src={
loadError ? "/resources/images/movie-placeholder.png" : imageUrl
loadError ? "/resources/images/movie-placeholder.png" : thumbnailUrl
}
className="h-full w-full object-cover"
alt={job.maybe_title ?? "unknown"}
crossOrigin="anonymous"
//crossOrigin="anonymous"
onError={() => setLoadError(true)}
loading="lazy"
/>
@@ -110,6 +110,7 @@ export const GalleryDraggableItem: React.FC<GalleryDraggableItemProps> = ({
</div>
) : (
<img
data-gallery-draggable-1="true"
ref={imgRef}
src={item.thumbnail}
alt={item.label}
@@ -177,6 +178,7 @@ export const GalleryDraggableItem: React.FC<GalleryDraggableItemProps> = ({
</div>
) : (
<img
data-gallery-draggable-2="true"
ref={imgRef}
src={item.thumbnail}
alt={item.label}
@@ -153,6 +153,7 @@ export const GalleryModal = React.memo(
const handleImageError = useCallback(
(url: string) => {
console.error(`Failed to load gallery modal image: ${url}`);
failedImageUrls.add(url);
},
[failedImageUrls]
@@ -234,7 +235,7 @@ export const GalleryModal = React.memo(
label: item.maybe_title || "Image Generation",
thumbnail:
item.media_class === "video"
? item.media_links.maybe_video_previews.still
? item.media_links.maybe_video_previews.animated
: item.media_class === "dimensional"
? item.cover_image?.maybe_cover_image_public_bucket_url
: item.media_links.maybe_thumbnail_template?.replace(
@@ -86,6 +86,8 @@ export function LightboxModal({
) : mediaClass === "video" ? (
<video
controls
loop={true}
autoPlay={true}
className="h-full w-full object-contain"
onLoadedData={() => setMediaLoaded(true)}
>
@@ -94,6 +96,7 @@ export function LightboxModal({
</video>
) : (
<img
data-lightbox-modal="true"
src={imageTagImageUrl}
alt={imageAlt}
className="h-full w-full object-contain"
@@ -134,7 +137,7 @@ export function LightboxModal({
//});
let result = await EnqueueImageTo3dObject({
image_media_token: mediaId,
model: EnqueueImageTo3dObjectModel.Hunyuan3d2,
model: EnqueueImageTo3dObjectModel.Hunyuan3d2_0,
});
//e.stopPropagation();
//await onAddToSceneClicked(downloadUrl, mediaId);
@@ -89,7 +89,25 @@ export const allModels = {
// badges: [{ label: "25 sec.", icon: <FontAwesomeIcon icon={faClock} /> }],
//},
],
// These models are used for 2D and 3D editing and scene building.
instructiveImageEdits: [
{
label: "GPT-4o",
icon: <FontAwesomeIcon icon={faImage} className="h-4 w-4" />,
description: "High quality model",
badges: [{ label: "2 min.", icon: <FontAwesomeIcon icon={faClock} /> }],
modelInfo: IMAGE_MODELS.gpt_image_1,
},
{
label: "FLUX.1 Kontext",
icon: <FontAwesomeIcon icon={faImage} className="h-4 w-4" />,
description: "Fast and high-quality model",
badges: [{ label: "20 sec.", icon: <FontAwesomeIcon icon={faClock} /> }],
modelInfo: IMAGE_MODELS.gpt_image_1, // TODO: Wrong metadata!
},
],
};
export const videoGenerationModels: ModelList = allModels.video;
export const imageGenerationModels: ModelList = allModels.image;
export const instructiveImageEditModels: ModelList = allModels.instructiveImageEdits;
@@ -21,6 +21,8 @@ export interface EnqueueImageTo3dObjectRequest {
export enum EnqueueImageTo3dObjectModel {
Hunyuan3d2 = "hunyuan_3d_2",
Hunyuan3d2_0 = "hunyuan_3d_2_0",
Hunyuan3d2_1 = "hunyuan_3d_2_1",
}
export interface EnqueueImageTo3dObjectError extends CommandResult {