feature / Preview Button (#65)

* preview button done

* error if there's no video

* fix playing problem with chroma key

* fix the problem of videonode unable to play after being processed
This commit is contained in:
wil authored and GitHub committed 2024-11-09 19:31:27 -05:00
1 parent b1089af55c
commit 78b74f3388
8 files changed
+187 -90

No files matched your search

+73 -17
View File
@@ -272,6 +272,18 @@ export class Engine {
document.body.style.cursor = "default";
return;
}
case AppModes.PREVIEW: {
console.log("APPMODE: RENDER");
this.selectorSquare.disable();
this.selectionManager.disable();
uiAccess.toolbarMain.disable();
uiAccess.toolbarMain.changeButtonState(ToolbarMainButtonNames.SELECT, {
active: false,
});
this.matteBox.enable();
document.body.style.cursor = "not-allowed";
return;
}
case AppModes.RENDERING: {
console.log("APPMODE: RENDER");
this.selectorSquare.disable();
@@ -426,8 +438,14 @@ export class Engine {
uiEvents.toolbarMain.SAVE.onClick(async (/*event*/) => {
this.sceneManager.saveScene();
});
uiEvents.toolbarMain.PREVIEW.onClick(async () => {
console.log("Toolbar Main >> Preview");
this.setAppMode(AppModes.PREVIEW);
await this.handlePreview();
this.setAppMode(AppModes.SELECT);
});
uiEvents.toolbarMain.DOWNLOAD.onClick(async () => {
console.log("Toolbar Main Render and Download");
console.log("Toolbar Main >> Render Download");
try {
this.setAppMode(AppModes.RENDERING);
await this.renderEngine.startProcessing();
@@ -558,30 +576,27 @@ export class Engine {
}
public async setupStage() {
var textNode = new Konva.Text({
x: 0,
y: 0,
// Frame rate inicator
const textNode = new Konva.Text({
x: 5,
y: 5,
text: "",
fontSize: 32,
fontFamily: "Calibri",
fontFamily: "Source Sans 3",
fill: "black",
});
var anim = new Konva.Animation((frame) => {
if (frame) {
if (import.meta.env.DEV) {
const timeDiff = frame.timeDiff;
const frameRate = frame.frameRate;
textNode.setText(
`FrameTime:${timeDiff.toFixed(0)} ms\nFrameRate:${frameRate.toFixed(0)} fps`,
);
}
const anim = new Konva.Animation((frame) => {
if (frame && import.meta.env.DEV) {
const timeDiff = frame.timeDiff;
const frameRate = frame.frameRate;
textNode.setText(
`FrameTime: ${timeDiff.toFixed(0)} ms\nFrameRate: ${frameRate.toFixed(0)} fps`,
);
}
}, this.mediaLayer);
anim.start();
this.uiLayer.add(textNode);
this.addKeyboardShortcuts();
}
@@ -638,6 +653,47 @@ export class Engine {
}
});
}
private async handlePreview(): Promise<void> {
return new Promise((resolve) => {
const allNodes = this.nodesManager.getAllNodes();
let longestVideoNode: VideoNode | undefined;
allNodes.forEach((node) => {
if (node instanceof VideoNode) {
node.videoComponent.pause();
node.videoComponent.currentTime = 0;
if (
longestVideoNode === undefined ||
node.videoComponent.duration >
longestVideoNode.videoComponent.duration
) {
longestVideoNode = node;
}
}
});
const timer = setTimeout(() => {
resolve();
}, 7000);
if (longestVideoNode !== undefined) {
longestVideoNode.videoComponent.addEventListener("ended", () => {
clearTimeout(timer);
resolve();
});
} else {
// no videonode
uiAccess.dialogError.show({
title: "No Video Node",
message: "You have not yet put a video on the board",
});
resolve();
}
allNodes.forEach((node) => {
if (node instanceof VideoNode) {
node.togglePlay();
}
});
});
}
}
function downloadURI(uri: string, name: string) {
+2 -2
View File
@@ -41,8 +41,8 @@ export abstract class BaseNode {
// Do any other clean up and delete the konva node.
this.kNode.destroy();
}
protected async setProcessing() {
this.isProcessing = true;
public async setProcessing(val: boolean) {
this.isProcessing = val;
}
public isKEventRef() {
return this._isKDragEventListening && this._isKTranformEventListening;
+22 -22
View File
@@ -152,26 +152,7 @@ export class VideoNode extends NetworkedNode {
private listenToVideoPlayPause() {
// TODO: for controling video playpause, can be improved with bette ui
this.kNode.on("click", () => {
// console.log("click");
// Shouldn't play if anything things are true
if (this.didFinishLoading == false) {
return;
}
if (this.isProcessing == true) {
return;
}
if (this.isSegmentationMode) {
// console.log("Not Playing because Segmenting");
return;
}
if (this.videoComponent.paused) {
console.log("Playing", this.videoComponent.src);
this.videoComponent.play();
this.chromaKeyRender(0); // For starting Chroma
} else {
console.log("Pause", this.videoComponent.src);
this.videoComponent.pause();
}
this.togglePlay();
});
}
private removeLisentoVideoPlayPause() {
@@ -296,10 +277,29 @@ export class VideoNode extends NetworkedNode {
// TODO: impolement retry
}
}
public togglePlay() {
// Shouldn't play in these situations
if (this.didFinishLoading == false) {
return;
}
if (this.isProcessing == true) {
return;
}
if (this.isSegmentationMode) {
// console.log("Not Playing because Segmenting");
return;
}
async setProcessing() {
this.isProcessing = true;
if (this.videoComponent.paused) {
console.log("Playing", this.videoComponent.src);
this.videoComponent.play();
this.chromaKeyRender(0); // For starting Chroma
} else {
console.log("Pause", this.videoComponent.src);
this.videoComponent.pause();
}
}
async reset() {
this.videoComponent.pause();
await this.seek(0);
@@ -342,7 +342,7 @@ export class RenderEngine {
setTimeout(this.startProcessing.bind(this), 1000);
break;
}
item.setProcessing();
item.setProcessing(true);
}
// todo remove
@@ -371,14 +371,15 @@ export class RenderEngine {
if (!item.kNode) {
return;
}
item.setProcessing(false);
item.kNode.listening(true);
}
}
}
public stopProcessing() {
this.isProcessing = false;
}
// public stopProcessing() {
// this.isProcessing = false;
// }
// find the frame time given the frame number
private calculateFrameTime(frameNumber: number, frameRate: number): number {
+2
View File
@@ -1,11 +1,13 @@
export enum AppModes {
INIT = "INIT",
PREVIEW = "PREVIEW",
SELECT = "SELECT",
RENDERING = "RENDERING",
}
export const VideoResolutions = {
VERTICAL_720: { width: 720, height: 1280 },
LANDSCAPE_720: { width: 1280, height: 720 },
};
export const Colors = {
@@ -0,0 +1,76 @@
import { faCameraMovie, faPlayCircle } from "@fortawesome/pro-solid-svg-icons";
import { Button, Tooltip } from "~/components/ui";
import { ToolbarButtonProps } from "../ToolbarButton";
export const ButtonPreviewAndRender = ({
buttonPreviewProps,
buttonRenderProps,
}: {
buttonPreviewProps: ToolbarButtonProps;
buttonRenderProps: ToolbarButtonProps;
}) => {
return (
<div className="flex">
<ButtonPreview {...buttonPreviewProps} />
<ButtonRender {...buttonRenderProps} />
</div>
);
};
const ButtonPreview = (buttonProps: ToolbarButtonProps) => {
const {
className: customButtonClassNames,
disabled,
active,
hidden,
onClick,
...restButtonProps
} = buttonProps;
return (
<Tooltip tip="Preview">
<Button
className="text-nowrap rounded-r-none border-r border-white"
icon={faPlayCircle}
disabled={disabled}
{...restButtonProps}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
if (onClick) {
onClick(e);
}
}}
{...restButtonProps}
/>
</Tooltip>
);
};
const ButtonRender = (buttonProps: ToolbarButtonProps) => {
const {
className: customButtonClassNames,
disabled,
active,
hidden,
onClick,
...restButtonProps
} = buttonProps;
return (
<Tooltip tip="Click to Finish Movie">
<Button
className="text-nowrap rounded-l-none"
icon={faCameraMovie}
disabled={disabled}
{...restButtonProps}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
if (onClick) {
onClick(e);
}
}}
{...restButtonProps}
>
Render Movie
</Button>
</Tooltip>
);
};
@@ -6,10 +6,6 @@ import {
faArrowRotateLeft,
faArrowRotateRight,
faBars,
faCameraMovie,
// faCameraRotate,
// faDownload,
// faCloudArrowDown,
faFilePlus,
faFilm,
faFloppyDisk,
@@ -19,12 +15,12 @@ import {
faText,
} from "@fortawesome/pro-solid-svg-icons";
import { ToolbarButton, ToolbarButtonProps } from "../ToolbarButton";
import { ToolbarButton } from "../ToolbarButton";
// style and constants
import { paperWrapperStyles, toolTipStyles } from "~/components/styles";
import { ToolbarMainButtonNames } from "./enum";
import { Button, Tooltip } from "~/components/ui";
import { ButtonPreviewAndRender } from "./ButtonPreviewAndRender";
export const ToolbarMain = ({
disabled = false,
@@ -92,11 +88,6 @@ export const ToolbarMain = ({
</ToolbarButton>
</PopoverPanel>
</Popover>
{/* <ToolbarButton
icon={faCameraRotate}
buttonProps={buttonProps.CHANGE_CAMERA_ORIENTATION}
tooltip="Change Orientation"
/> */}
<ToolbarButton
icon={faHatWizard}
buttonProps={buttonProps.AI_STYLIZE}
@@ -119,41 +110,12 @@ export const ToolbarMain = ({
buttonProps={buttonProps.SAVE}
tooltip="Save"
/>
<DownloadButton {...buttonProps.DOWNLOAD} />
<ButtonPreviewAndRender
buttonPreviewProps={buttonProps.PREVIEW}
buttonRenderProps={buttonProps.DOWNLOAD}
/>
</div>
</div>
</>
);
};
const DownloadButton = (buttonProps: ToolbarButtonProps) => {
const {
className: customButtonClassNames,
disabled,
active,
hidden,
onClick,
...restButtonProps
} = buttonProps;
return (
<Tooltip tip="Click to Finish Movie">
<Button
className="text-nowrap"
icon={faCameraMovie}
disabled={disabled}
{...restButtonProps}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
if (onClick) {
onClick(e);
}
}}
{...restButtonProps}
>
Render Movie
</Button>
</Tooltip>
);
};
+1 -1
View File
@@ -4,10 +4,10 @@ export enum ToolbarMainButtonNames {
ADD_TEXT = "ADD_TEXT",
ADD_IMAGE = "ADD_IMAGE",
ADD_VIDEO = "ADD_VIDEO",
// CHANGE_CAMERA_ORIENTATION = "CHANGE_CAMERA_ORIENTATION",
AI_STYLIZE = "AI_STYLIZE",
UNDO = "UNDO",
REDO = "REDO",
SAVE = "SAVE",
PREVIEW = "PREVIEW",
DOWNLOAD = "DOWNLOAD",
}