+
Your file
>
@@ -137,6 +168,7 @@ export default function EntityInput({
accept,
aspectRatio = "square",
className,
+ cropProps,
debug,
label,
name = "",
@@ -250,6 +282,7 @@ export default function EntityInput({
{...{
className: "fy-entity-input-full",
clear,
+ cropProps,
media,
render: EntityInputFull,
...sharedProps,
diff --git a/packages/fakeyou.com/src/components/entities/EntityInput/EntityInputImageVideoPreview.tsx b/packages/fakeyou.com/src/components/entities/EntityInput/EntityInputImageVideoPreview.tsx
new file mode 100644
index 0000000000..f81738dbe4
--- /dev/null
+++ b/packages/fakeyou.com/src/components/entities/EntityInput/EntityInputImageVideoPreview.tsx
@@ -0,0 +1,101 @@
+import React, { useState } from "react";
+import Cropper, { Point } from "react-easy-crop";
+import { Button } from "components/common";
+import { useVideo } from "hooks";
+import { faPlay, faPause } from "@fortawesome/free-solid-svg-icons";
+import { CropProps } from "./EntityInput";
+
+interface EntityInputImageVideoPreviewProps {
+ crop: Point;
+ cropProps?: CropProps;
+ cropSet: (crop: Point) => void;
+ image?: string;
+ video?: string;
+ zoom: number;
+ zoomSet: (zoom: number) => void;
+}
+
+export default function EntityInputImageVideoPreview({
+ crop,
+ cropProps,
+ cropSet,
+ image,
+ video,
+ zoom,
+ zoomSet,
+}: EntityInputImageVideoPreviewProps) {
+ const [videoRef, videoRefSet] = useState<
+ React.RefObject
| undefined
+ >();
+ const [isPlaying, isPlayingSet] = useState(false);
+ const [{ playCtrl }, { onEnded }] = useVideo({
+ videoRef,
+ onEnded: (playPause: boolean) => isPlayingSet(!playPause),
+ });
+
+ return (
+
+
{
+ if (cropProps) {
+ cropSet(cropLocation);
+ }
+ },
+ ...(cropProps?.onCropComplete
+ ? { onCropComplete: cropProps.onCropComplete }
+ : {}),
+ onZoomChange: (zoomValue: number) => {
+ if (cropProps) {
+ zoomSet(zoomValue);
+ }
+ },
+ setVideoRef: (ref: React.RefObject) => {
+ videoRefSet(ref);
+ },
+ showGrid: !!cropProps,
+ style: {
+ cropAreaStyle: {
+ border: "none",
+ // color: "rgba(#f00,0.0)",
+ },
+ },
+ ...(video ? { video } : {}),
+ zoom,
+ }}
+ />
+ {video ? (
+
+ );
+}
diff --git a/packages/fakeyou.com/src/components/entities/EntityInput/EntityInputSidePanel.tsx b/packages/fakeyou.com/src/components/entities/EntityInput/EntityInputSidePanel.tsx
new file mode 100644
index 0000000000..b4001296d3
--- /dev/null
+++ b/packages/fakeyou.com/src/components/entities/EntityInput/EntityInputSidePanel.tsx
@@ -0,0 +1,46 @@
+import React from "react";
+import { MediaFile } from "@storyteller/components/src/api/media_files/GetMedia";
+import { Button, ZoomSlider, ZoomSliderOnChangeEvent } from "components/common";
+
+interface Props {
+ clear: () => void;
+ entityType: "image" | "video";
+ media?: MediaFile;
+ showCrop: boolean;
+ zoom: number;
+ zoomSliderChange: (zoom: ZoomSliderOnChangeEvent) => void;
+}
+
+export default function EntityInputSidePanel({
+ clear,
+ entityType,
+ media,
+ showCrop,
+ zoomSliderChange,
+ zoom,
+}: Props) {
+ const uploader = `Uploaded by ${
+ media?.maybe_creator_user?.display_name || "User"
+ }`;
+ return (
+
+
+ {showCrop && (
+
+ )}
+
+
+ {media?.maybe_title || `Untitled ${entityType}`}
+
{uploader}
+
+
+
+ );
+}
diff --git a/packages/fakeyou.com/src/hooks/useVideo/useVideo.tsx b/packages/fakeyou.com/src/hooks/useVideo/useVideo.tsx
index a89c7a7746..88c1cf781b 100644
--- a/packages/fakeyou.com/src/hooks/useVideo/useVideo.tsx
+++ b/packages/fakeyou.com/src/hooks/useVideo/useVideo.tsx
@@ -1,14 +1,21 @@
-import { useRef } from 'react';
+import { useRef } from "react";
interface Props {
onEnded?: any;
+ videoRef?: React.RefObject;
}
-export default function useVideo({ onEnded }: Props) {
- const ref = useRef(null);
- const playCtrl = (toDo = (b:any) => {}) => {
- if (ref.current) {
- let isPlaying = !!(ref.current.currentTime > 0 && !ref.current.paused && !ref.current.ended && ref.current.readyState > 2);
+export default function useVideo({ onEnded, videoRef }: Props) {
+ const internalRef = useRef(null);
+ const ref = videoRef || internalRef;
+ const playCtrl = (toDo = (b: any) => {}) => {
+ if (ref.current) {
+ let isPlaying = !!(
+ ref.current.currentTime > 0 &&
+ !ref.current.paused &&
+ !ref.current.ended &&
+ ref.current.readyState > 2
+ );
if (!isPlaying) {
toDo(false);
ref.current.play();
@@ -19,5 +26,5 @@ export default function useVideo({ onEnded }: Props) {
}
};
- return [{ playCtrl },{ onEnded, ref }];
-};
\ No newline at end of file
+ return [{ playCtrl }, { onEnded, ref }];
+}
diff --git a/packages/fakeyou.com/src/scss/colours.scss b/packages/fakeyou.com/src/scss/colours.scss
index 86f20797a1..9a9ba67889 100644
--- a/packages/fakeyou.com/src/scss/colours.scss
+++ b/packages/fakeyou.com/src/scss/colours.scss
@@ -45,6 +45,9 @@ $primary-light: desaturate(lighten($primary, 20%), 15%);
$primary-dark: saturate(darken($primary, 15%), 10%);
$primary-very-light: desaturate(lighten($primary, 45%), 35%);
+$secondary-dark: saturate(darken($secondary, 6%), 2%);
+$secondary-darker: saturate(darken($secondary, 9%), 2%);
+$secondary-very-dark: saturate(darken($secondary, 12%), 2%);
$secondary-light: lighten($secondary, 5%);
$secondary-lighter: lighten($secondary, 12%);
$secondary-very-light: lighten($secondary, 24%);
diff --git a/packages/fakeyou.com/src/v2/view/pages/ai_face_mirror/AIFaceMirror.tsx b/packages/fakeyou.com/src/v2/view/pages/ai_face_mirror/AIFaceMirror.tsx
index 2d766d38a5..3e5d105e59 100644
--- a/packages/fakeyou.com/src/v2/view/pages/ai_face_mirror/AIFaceMirror.tsx
+++ b/packages/fakeyou.com/src/v2/view/pages/ai_face_mirror/AIFaceMirror.tsx
@@ -14,9 +14,10 @@ import {
import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper";
import { FrontendInferenceJobType } from "@storyteller/components/src/jobs/InferenceJob";
import {
- EnqueueActingFace,
- EnqueueActingFaceResponse,
-} from "@storyteller/components/src/api/workflows/EnqueueActingFace";
+ MediaFileCropArea,
+ EnqueueFaceMirror,
+ EnqueueFaceMirrorResponse,
+} from "@storyteller/components/src/api/workflows/EnqueueFaceMirror";
import { EntityInput } from "components/entities";
import {
useInferenceJobs,
@@ -37,6 +38,13 @@ export default function AIFaceMirror({ sessionSubscriptionsWrapper }: Props) {
"m_41caq6n7nw15y9e68009bgkn23m3yf"
);
const [visibility, visibilitySet] = useState<"private" | "public">("public");
+ const [cropArea, cropAreaSet] = useState({
+ height: 0,
+ width: 0,
+ x: 0,
+ y: 0,
+ });
+
const { enqueue } = useInferenceJobs();
// const { canBanUsers } = useSession();
@@ -51,13 +59,14 @@ export default function AIFaceMirror({ sessionSubscriptionsWrapper }: Props) {
const enqueueClick = () => {
if (hasTokens) {
- EnqueueActingFace("", {
+ EnqueueFaceMirror("", {
creator_set_visibility: visibility,
face_driver_media_file_token: faceDriverToken,
+ maybe_crop: cropArea,
remove_watermark: removeWatermark,
source_media_file_token: sourceMediaToken,
uuid_idempotency_token: uuidv4(),
- }).then((res: EnqueueActingFaceResponse) => {
+ }).then((res: EnqueueFaceMirrorResponse) => {
if (res.success && res.inference_job_token) {
enqueue(
res.inference_job_token,
@@ -121,6 +130,12 @@ export default function AIFaceMirror({ sessionSubscriptionsWrapper }: Props) {
{
+ cropAreaSet(croppedAreaPixels);
+ },
+ },
// aspectRatio: "landscape",
// debug: "AFM driver input",
name: "faceDriverToken",
diff --git a/yarn.lock b/yarn.lock
index 7923dba093..c07136a436 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4486,6 +4486,7 @@ __metadata:
moment: ^2.29.4
prettier: ^3.1.0
pretty-quick: ^3.1.3
+ react-easy-crop: ^5.0.7
universal-cookie: ^6.1.1
yarn: ^1.22.19
languageName: unknown
@@ -15425,6 +15426,13 @@ __metadata:
languageName: node
linkType: hard
+"normalize-wheel@npm:^1.0.1":
+ version: 1.0.1
+ resolution: "normalize-wheel@npm:1.0.1"
+ checksum: 00b32efa040bad9438e732385a4ca27f8532fa2c8c06b54be43b9f75b2da6642bf41a0b4f81e542639dc8d682cde8b059b0a02ae0723fb8ebc3c8f036c8e51d8
+ languageName: node
+ linkType: hard
+
"npm-bundled@npm:^1.1.1":
version: 1.1.2
resolution: "npm-bundled@npm:1.1.2"
@@ -17800,6 +17808,19 @@ __metadata:
languageName: node
linkType: hard
+"react-easy-crop@npm:^5.0.7":
+ version: 5.0.7
+ resolution: "react-easy-crop@npm:5.0.7"
+ dependencies:
+ normalize-wheel: ^1.0.1
+ tslib: ^2.0.1
+ peerDependencies:
+ react: ">=16.4.0"
+ react-dom: ">=16.4.0"
+ checksum: 562ea94b8c938e842965cee05b9ab99d829ff89cbdf155d03a834800a930b5429be4ee62981dca111533c063bc630a6bb1d649a00024481f80426d7a1325ff79
+ languageName: node
+ linkType: hard
+
"react-error-overlay@npm:^6.0.10":
version: 6.0.10
resolution: "react-error-overlay@npm:6.0.10"
@@ -20751,6 +20772,13 @@ __metadata:
languageName: node
linkType: hard
+"tslib@npm:^2.0.1":
+ version: 2.6.3
+ resolution: "tslib@npm:2.6.3"
+ checksum: 74fce0e100f1ebd95b8995fbbd0e6c91bdd8f4c35c00d4da62e285a3363aaa534de40a80db30ecfd388ed7c313c42d930ee0eaf108e8114214b180eec3dbe6f5
+ languageName: node
+ linkType: hard
+
"tslib@npm:^2.0.3":
version: 2.3.1
resolution: "tslib@npm:2.3.1"