Skip to content

Commit

Permalink
Fix issue with default duration overriding video layer duration
Browse files Browse the repository at this point in the history
Introduced in #314, which is unreleased.
  • Loading branch information
bkeepers committed Feb 19, 2025
1 parent aef2d32 commit 2144705
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 27 deletions.
7 changes: 4 additions & 3 deletions src/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,15 +216,13 @@ export class Configuration {
this.defaults = merge({}, globalDefaults, input.defaults);

this.clips = input.clips.map((clip) => {
const { transition, duration } = merge({}, this.defaults, clip);
let { layers } = clip;

if (layers && !Array.isArray(layers)) layers = [layers]; // Allow single layer for convenience
assert(
Array.isArray(layers) && layers.length > 0,
"clip.layers must be an array with at least one layer.",
);
assert(transition == null || typeof transition === "object", "Transition must be an object");

layers = layers
.map(expandLayerAliases)
Expand All @@ -239,7 +237,10 @@ export class Configuration {
);
});

return { transition, duration, layers };
const { transition } = merge({}, this.defaults, clip);
assert(transition == null || typeof transition === "object", "Transition must be an object");

return { transition, layers, duration: clip.duration };
});

// Testing options:
Expand Down
10 changes: 9 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ async function Editly(input: ConfigurationOptions): Promise<void> {
customOutputArgs,
isGif,
tmpDir,
defaults,
} = config;

await configureFf(config);
Expand All @@ -63,6 +64,7 @@ async function Editly(input: ConfigurationOptions): Promise<void> {
backgroundAudioVolume,
loopAudio,
allowRemoteRequests,
defaults,
});
if (verbose) console.log("Calculated", JSON5.stringify({ clips, arbitraryAudio }, null, 2));

Expand Down Expand Up @@ -492,13 +494,19 @@ export async function renderSingleFrame(input: RenderSingleFrameConfig): Promise
verbose,
logTimes,
outPath = `${Math.floor(Math.random() * 1e12)}.png`,
defaults,
} = config;

configureFf(config);

console.log({ clipsIn });

const { clips } = await parseConfig({ clips: clipsIn, arbitraryAudio: [], allowRemoteRequests });
const { clips } = await parseConfig({
clips: clipsIn,
arbitraryAudio: [],
allowRemoteRequests,
defaults,
});
let clipStartTime = 0;
const clip = clips.find((c) => {
if (clipStartTime <= time && clipStartTime + c.duration > time) return true;
Expand Down
39 changes: 17 additions & 22 deletions src/parseConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { registerFont } from "canvas";
import flatMap from "lodash-es/flatMap.js";
import pMap from "p-map";
import { basename } from "path";
import { Configuration } from "./configuration.js";
import { readDuration, readVideoFileInfo } from "./ffmpeg.js";
import { Transition } from "./transition.js";
import type {
AudioTrack,
CanvasLayer,
Clip,
FabricLayer,
ImageLayer,
ImageOverlayLayer,
Expand Down Expand Up @@ -49,13 +49,12 @@ async function validateArbitraryAudio(
}

type ParseConfigOptions = {
clips: Clip[];
backgroundAudioVolume?: string | number;
backgroundAudioPath?: string;
loopAudio?: boolean;
allowRemoteRequests?: boolean;
arbitraryAudio: AudioTrack[];
};
} & Pick<
Configuration,
"clips" | "backgroundAudioVolume" | "loopAudio" | "allowRemoteRequests" | "defaults"
>;

export default async function parseConfig({
clips,
Expand All @@ -64,6 +63,7 @@ export default async function parseConfig({
backgroundAudioVolume,
loopAudio,
allowRemoteRequests,
defaults,
}: ParseConfigOptions) {
async function handleLayer(layer: Layer): Promise<Layer | Layer[]> {
// https://github.com/mifi/editly/issues/39
Expand Down Expand Up @@ -122,14 +122,8 @@ export default async function parseConfig({
let clipsOut: ProcessedClip[] = await pMap(
clips,
async (clip, clipIndex) => {
const { transition: userTransition, duration, layers } = clip;

const videoLayers = layers.filter((layer) => layer.type === "video");

if (videoLayers.length === 0)
assert(duration, `Duration parameter is required for videoless clip ${clipIndex}`);

const transition = new Transition(userTransition, clipIndex === clips.length - 1);
const { layers } = clip;
const transition = new Transition(clip.transition, clipIndex === clips.length - 1);

let layersOut = flatMap(
await pMap(
Expand Down Expand Up @@ -179,13 +173,14 @@ export default async function parseConfig({
),
);

let clipDuration = duration;
let clipDuration = clip.duration;

const firstVideoLayer = layersOut.find(
(layer): layer is VideoLayer => layer.type === "video",
);
if (firstVideoLayer && !duration) clipDuration = firstVideoLayer.layerDuration!;
assert(clipDuration);
if (!clipDuration) {
const video = layersOut.find((layer): layer is VideoLayer => layer.type === "video");
clipDuration = video?.layerDuration ?? defaults.duration;
}

assert(clipDuration, `Duration parameter is required for videoless clip ${clipIndex}`);

// We need to map again, because for audio, we need to know the correct clipDuration
layersOut = (
Expand Down Expand Up @@ -229,9 +224,9 @@ export default async function parseConfig({
let speedFactor;

// If user explicitly specified duration for clip, it means that should be the output duration of the video
if (duration) {
if (clipDuration) {
// Later we will speed up or slow down video using this factor
speedFactor = duration / layerDuration;
speedFactor = clipDuration / layerDuration;
} else {
speedFactor = 1;
}
Expand Down
1 change: 0 additions & 1 deletion test/configuration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ describe("Configuration", () => {
audioInCurve: "tri",
});

expect(config.clips[1].duration).toBe(5);
expect(config.clips[1].transition).toEqual({
duration: 1,
name: "random",
Expand Down

0 comments on commit 2144705

Please sign in to comment.