Skip to content

Commit

Permalink
Merge pull request #410 from charlielee/issue-404
Browse files Browse the repository at this point in the history
Reimplement playback engine
  • Loading branch information
charlielee committed Aug 12, 2022
2 parents adc4971 + 4ae5062 commit 96dcf4c
Show file tree
Hide file tree
Showing 23 changed files with 421 additions and 99 deletions.
5 changes: 5 additions & 0 deletions src/common/Flavors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@ interface Flavoring<FlavorT> {
}
type Flavor<T, FlavorT> = T & Flavoring<FlavorT>;

export type Primitive = string | number | boolean | NumberFlavor | StringFlavor;

export type FrameCount = Flavor<number, "FrameCount">;
export type FrameRate = Flavor<number, "FrameRate">;
export type TimelineIndex = Flavor<number, "TimelineIndex">;
type NumberFlavor = FrameCount | FrameRate | TimelineIndex;

export type TakeId = Flavor<string, "TakeId">;
export type TrackId = Flavor<string, "TrackId">;
export type TrackItemId = Flavor<string, "TrackItemId">;
export type TrackGroupId = Flavor<string, "TrackGroupId">;
type StringFlavor = TakeId | TrackId | TrackItemId | TrackGroupId;
33 changes: 32 additions & 1 deletion src/common/Project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
FrameCount,
FrameRate,
TakeId,
TimelineIndex,
TrackGroupId,
TrackId,
TrackItemId,
Expand Down Expand Up @@ -97,5 +98,35 @@ export const makeFrameFilePath = (take: Take, fileName?: string): string =>
].join("_")
);

export const getTrackItemsLength = (trackItems: TrackItem[]): FrameCount =>
trackItems.reduce((prev, trackItem) => prev + trackItem.length, 0);

export const getTrackLength = (track: Track): FrameCount =>
track.trackItems.reduce((prev, trackItem) => prev + trackItem.length, 0);
getTrackItemsLength(track.trackItems);

export const getTrackItemStartPosition = (
track: Track,
trackItemIndex: number
): TimelineIndex =>
getTrackItemsLength(
track.trackItems.slice(0, trackItemIndex)
) as TimelineIndex;

export const getTrackItemEndPosition = (
track: Track,
trackItemIndex: number
): TimelineIndex =>
getTrackItemsLength(
track.trackItems.slice(0, trackItemIndex + 1)
) as TimelineIndex;

export const getHighlightedTrackItem = (
track: Track,
timelineIndex: TimelineIndex | undefined
): TrackItem | undefined =>
timelineIndex === undefined
? undefined
: track.trackItems.find(
(_trackItem, index) =>
getTrackItemStartPosition(track, index) >= timelineIndex
);
3 changes: 2 additions & 1 deletion src/common/ipc/IpcHandler.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Primitive } from "../Flavors";
import LogLevel from "../LogLevel";
import { UserPreferences } from "../UserPreferences";

Expand All @@ -21,7 +22,7 @@ namespace Ipc {
export type Payload = {
logLevel: LogLevel;
loggingCode: string;
message?: string | Record<string, string>;
message?: Primitive | Record<string, Primitive>;
};
export type Response = Promise<void>;
}
Expand Down
20 changes: 11 additions & 9 deletions src/main/services/logger/Logger.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { app } from "electron";
import * as fs from "fs";
import { Primitive } from "../../../common/Flavors";
import LogLevel from "../../../common/LogLevel";

const USER_DATA_PATH = app.getPath("userData");
Expand Down Expand Up @@ -31,27 +32,27 @@ class Logger {

info = (
loggingCode: string,
message?: string | Record<string, string>,
writeToFile: boolean = true
message?: Primitive | Record<string, Primitive>,
writeToFile = true
) => this.log(LogLevel.INFO, loggingCode, message, writeToFile);

warn = (
loggingCode: string,
message?: string | Record<string, string>,
writeToFile: boolean = true
message?: Primitive | Record<string, Primitive>,
writeToFile = true
) => this.log(LogLevel.WARN, loggingCode, message, writeToFile);

error = (
loggingCode: string,
message?: string | Record<string, string>,
writeToFile: boolean = true
message?: Primitive | Record<string, Primitive>,
writeToFile = true
) => this.log(LogLevel.ERROR, loggingCode, message, writeToFile);

log(
logLevel: LogLevel,
loggingCode: string,
message?: string | Record<string, string>,
writeToFile: boolean = true,
message?: Primitive | Record<string, Primitive>,
writeToFile = true,
processName: ProcessName = ProcessName.MAIN
) {
const logLine = this.buildLogLine(
Expand All @@ -60,6 +61,7 @@ class Logger {
loggingCode,
message
);
// eslint-disable-next-line no-console
console.log(logLine);

if (this.stream) {
Expand All @@ -73,7 +75,7 @@ class Logger {
processName: ProcessName,
logLevel: LogLevel,
loggingCode: string,
message?: string | Record<string, string>
message?: Primitive | Record<string, Primitive>
) {
const logDate = new Date().toISOString();
return JSON.stringify([
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useState } from "react";
import PlaybackContext from "../../../context/PlaybackContext/PlaybackContext";
import IconName from "../../common/Icon/IconName";
import IconButton from "../../common/IconButton/IconButton";
import InputRange from "../../common/Input/InputRange/InputRange";
Expand All @@ -8,7 +9,15 @@ import ToolbarItem, {
} from "../../common/ToolbarItem/ToolbarItem";
import "./AnimationToolbar.css";

const AnimationToolbar = (): JSX.Element => {
interface AnimationToolbarProps {
startPlayback: () => void;
stopPlayback: () => void;
}

const AnimationToolbar = ({
startPlayback,
stopPlayback,
}: AnimationToolbarProps): JSX.Element => {
const [onionSkinAmount, setOnionSkinAmount] = useState(0);
const [loopPlayback, setLoopPlayback] = useState(false);

Expand Down Expand Up @@ -51,12 +60,12 @@ const AnimationToolbar = (): JSX.Element => {
<IconButton
title="Playback Frames"
icon={IconName.PLAY}
onClick={() => undefined}
onClick={() => startPlayback()}
/>
<IconButton
title="Stop Playback"
icon={IconName.PLAY_STOP}
onClick={() => undefined}
onClick={() => stopPlayback()}
/>
<IconButton
title="Next Frame"
Expand All @@ -82,4 +91,10 @@ const AnimationToolbar = (): JSX.Element => {
);
};

export default AnimationToolbar;
const AnimationToolbarWithContext = (): JSX.Element => (
<PlaybackContext.Consumer>
{(value) => <AnimationToolbar {...value} />}
</PlaybackContext.Consumer>
);

export default AnimationToolbarWithContext;
29 changes: 20 additions & 9 deletions src/renderer/components/animator/Animator/Animator.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Take } from "../../../../common/Project";
import PlaybackContextProvider from "../../../context/PlaybackContext/PlaybackContextProvider";
import Content from "../../common/Content/Content";
import IconName from "../../common/Icon/IconName";
import Page from "../../common/Page/Page";
Expand All @@ -7,27 +8,27 @@ import Sidebar from "../../common/Sidebar/Sidebar";
import SidebarBlock from "../../common/SidebarBlock/SidebarBlock";
import Tab from "../../common/Tab/Tab";
import TabGroup from "../../common/TabGroup/TabGroup";
import AnimationToolbar from "../AnimationToolbar/AnimationToolbar";
import CaptureButtonToolbar from "../CaptureButtonToolbar/CaptureButtonToolbar";
import AnimationToolbarWithContext from "../AnimationToolbar/AnimationToolbar";
import CaptureButtonToolbarWithContext from "../CaptureButtonToolbar/CaptureButtonToolbar";
import CaptureTab from "../CaptureTab/CaptureTab";
import MediaTab from "../MediaTab/MediaTab";
import Preview from "../Preview/Preview";
import StatusToolbar from "../StatusToolbar/StatusToolbar";
import StatusToolbarWithContext from "../StatusToolbar/StatusToolbar";
import Timeline from "../Timeline/Timeline";

interface AnimatorProps {
interface AnimatorWithProviderProps {
take: Take;
}

const Animator = ({ take }: AnimatorProps): JSX.Element => {
const Animator = ({ take }: AnimatorWithProviderProps): JSX.Element => {
return (
<Page>
<PageBody>
<Content>
<StatusToolbar take={take} />
<StatusToolbarWithContext take={take} />
<Preview />
<CaptureButtonToolbar />
<AnimationToolbar />
<CaptureButtonToolbarWithContext />
<AnimationToolbarWithContext />
<Timeline take={take} />
</Content>

Expand Down Expand Up @@ -58,4 +59,14 @@ const Animator = ({ take }: AnimatorProps): JSX.Element => {
);
};

export default Animator;
const AnimatorWithProvider = ({
take,
}: AnimatorWithProviderProps): JSX.Element => {
return (
<PlaybackContextProvider take={take}>
<Animator take={take} />
</PlaybackContextProvider>
);
};

export default AnimatorWithProvider;
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useDispatch } from "react-redux";
import PlaybackContext from "../../../context/PlaybackContext/PlaybackContext";
import { takePhoto } from "../../../redux/capture/actions";
import IconName from "../../common/Icon/IconName";
import IconButton from "../../common/IconButton/IconButton";
Expand All @@ -8,7 +9,15 @@ import ToolbarItem, {
} from "../../common/ToolbarItem/ToolbarItem";
import "./CaptureButtonToolbar.css";

const CaptureButtonToolbar = (): JSX.Element => {
interface CaptureToolbarProps {
stopPlayback: () => void;
isPlaying: boolean;
}

const CaptureButtonToolbar = ({
stopPlayback,
isPlaying,
}: CaptureToolbarProps): JSX.Element => {
const dispatch = useDispatch();

return (
Expand All @@ -18,11 +27,22 @@ const CaptureButtonToolbar = (): JSX.Element => {
title="Capture Frame"
icon={IconName.CAPTURE}
className="animation-toolbar__capture-button"
onClick={() => dispatch(takePhoto())}
onClick={() => {
if (isPlaying) {
stopPlayback();
}
dispatch(takePhoto());
}}
/>
</ToolbarItem>
</Toolbar>
);
};

export default CaptureButtonToolbar;
const CaptureButtonToolbarWithContext = (): JSX.Element => (
<PlaybackContext.Consumer>
{(value) => <CaptureButtonToolbar {...value} />}
</PlaybackContext.Consumer>
);

export default CaptureButtonToolbarWithContext;
28 changes: 23 additions & 5 deletions src/renderer/components/animator/StatusToolbar/StatusToolbar.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
import { TimelineIndex } from "../../../../common/Flavors";
import { PageRoute } from "../../../../common/PageRoute";
import { getTrackLength, Take } from "../../../../common/Project";
import { zeroPad } from "../../../../common/utils";
import PlaybackContext from "../../../context/PlaybackContext/PlaybackContext";
import Button from "../../common/Button/Button";
import { ButtonColor } from "../../common/Button/ButtonColor";
import Toolbar from "../../common/Toolbar/Toolbar";
import ToolbarItem, {
ToolbarItemAlign,
} from "../../common/ToolbarItem/ToolbarItem";

interface StatusToolbarProps {
interface StatusToolbarWithContextProps {
take: Take;
}

const StatusToolbar = ({ take }: StatusToolbarProps): JSX.Element => {
interface StatusToolbarProps extends StatusToolbarWithContextProps {
timelineIndex: TimelineIndex | undefined;
}

const StatusToolbar = ({
take,
timelineIndex,
}: StatusToolbarProps): JSX.Element => {
const makeTakeTitle = (take: Take) =>
`Shot ${zeroPad(take.shotNumber, 3)} Take ${zeroPad(take.takeNumber, 2)}`;

Expand All @@ -26,8 +35,11 @@ const StatusToolbar = ({ take }: StatusToolbarProps): JSX.Element => {
/>
</ToolbarItem>
<ToolbarItem align={ToolbarItemAlign.CENTER}>
Frame {getTrackLength(take.frameTrack) + 1} of{" "}
{getTrackLength(take.frameTrack)}
Frame{" "}
{timelineIndex === undefined
? getTrackLength(take.frameTrack) + 1
: timelineIndex + 1}{" "}
of {getTrackLength(take.frameTrack)}
</ToolbarItem>
<ToolbarItem stretch align={ToolbarItemAlign.RIGHT}>
{take.frameRate} FPS
Expand All @@ -36,4 +48,10 @@ const StatusToolbar = ({ take }: StatusToolbarProps): JSX.Element => {
);
};

export default StatusToolbar;
const StatusToolbarWithContext = (props: StatusToolbarWithContextProps) => (
<PlaybackContext.Consumer>
{(value) => <StatusToolbar {...value} {...props} />}
</PlaybackContext.Consumer>
);

export default StatusToolbarWithContext;

0 comments on commit 96dcf4c

Please sign in to comment.