From f5473ab3b705b1ecf3e902b7345427acc3f95097 Mon Sep 17 00:00:00 2001 From: Jonathan Cobb Date: Tue, 15 Dec 2020 09:05:55 -0500 Subject: [PATCH] simplify json formatting. complicates parser, but it's a relatively clean solution --- README.md | 65 ++++---- pom.xml | 7 + src/main/java/jvcl/main/JvclOptions.java | 17 ++- src/main/java/jvcl/model/JOperation.java | 32 +++- src/main/java/jvcl/model/JOperationType.java | 32 ---- src/main/java/jvcl/op/OverlayOperation.java | 140 ------------------ .../java/jvcl/operation/ConcatOperation.java | 29 ++++ .../java/jvcl/operation/OverlayOperation.java | 79 ++++++++++ .../java/jvcl/operation/SplitOperation.java | 28 ++++ .../java/jvcl/operation/TrimOperation.java | 39 +++++ .../exec/ConcatExec.java} | 21 +-- .../exec/ExecBase.java} | 25 ++-- .../java/jvcl/operation/exec/OverlayExec.java | 82 ++++++++++ .../exec/SplitExec.java} | 38 +---- .../exec/TrimExec.java} | 48 ++---- .../java/jvcl/service/OperationEngine.java | 2 +- src/main/java/jvcl/service/Toolbox.java | 15 +- .../jvcl/service/json/JOperationFactory.java | 58 ++++++++ .../jvcl/service/json/JOperationModule.java | 15 ++ .../jvcl/service/json/JOperationType.java | 26 ++++ src/test/resources/tests/test_concat.json | 4 +- src/test/resources/tests/test_split.json | 12 +- src/test/resources/tests/test_trim.json | 10 +- 23 files changed, 484 insertions(+), 340 deletions(-) delete mode 100644 src/main/java/jvcl/model/JOperationType.java delete mode 100644 src/main/java/jvcl/op/OverlayOperation.java create mode 100644 src/main/java/jvcl/operation/ConcatOperation.java create mode 100644 src/main/java/jvcl/operation/OverlayOperation.java create mode 100644 src/main/java/jvcl/operation/SplitOperation.java create mode 100644 src/main/java/jvcl/operation/TrimOperation.java rename src/main/java/jvcl/{op/ConcatOperation.java => operation/exec/ConcatExec.java} (83%) rename src/main/java/jvcl/{service/JOperator.java => operation/exec/ExecBase.java} (58%) create mode 100644 src/main/java/jvcl/operation/exec/OverlayExec.java rename src/main/java/jvcl/{op/SplitOperation.java => operation/exec/SplitExec.java} (70%) rename src/main/java/jvcl/{op/TrimOperation.java => operation/exec/TrimExec.java} (62%) create mode 100644 src/main/java/jvcl/service/json/JOperationFactory.java create mode 100644 src/main/java/jvcl/service/json/JOperationModule.java create mode 100644 src/main/java/jvcl/service/json/JOperationType.java diff --git a/README.md b/README.md index 84c6942..d7c8805 100644 --- a/README.md +++ b/README.md @@ -20,14 +20,12 @@ With JVCL, you'd create this spec: { "assets": [ {"name": "src", "path": "/tmp/my/source.mp4"} ], "operations": [{ - "operation": "split", - "creates": "src_splits", - "perform": { + "operation": "split", + "creates": "src_split_files", "split": "src", "interval": "10s", "start": "10s", "end": "130s" - } }] } ``` @@ -100,53 +98,44 @@ Here is a complex example using multiple assets and operations: ], "operations": [ { - "operation": "split", // name of the operation + "operation": "split", // name of the operation, "creates": "vid1_split_%", // assets it creates, the '%' will be replaced with a counter - "perform": { - "split": "vid1", // split this source asset - "interval": "10s" // split every ten seconds - } + "split": "vid1", // split this source asset + "interval": "10s" // split every ten seconds }, { - "operation": "concat", // name of the operation + "operation": "concat", // name of the operation, "creates": "recombined_vid1", // assets it creates, the '%' will be replaced with a counter - "perform": { - "concat": ["vid1_split"] // recombine all split assets - } + "concat": ["vid1_split"] // recombine all split assets }, { - "operation": "concat", // name of the operation + "operation": "concat", // name of the operation, "creates": "combined_vid", // asset it creates, can be referenced later - "perform": { - "concat": ["vid1", "vid2"] // operation-specific: this says, concatenate these named assets - } + "concat": ["vid1", "vid2"] // operation-specific: this says, concatenate these named assets }, { - "operation": "concat", // name of the operation + "operation": "concat", // name of the operation, "creates": "combined_vid", // the asset it creates, can be referenced later - "perform": { - "concat": ["vid1", "vid2"] // operation-specific: this says, concatenate these named assets - } + "concat": ["vid1", "vid2"] // operation-specific: this says, concatenate these named assets }, { - "operation": "overlay", // name of the operation - "creates": "overlay1", // asset it creates - "perform": { - "source": "combined_vid1", // main video asset - "overlay": "vid2", // overlay this video on the main video - - "offset": "30", // when (on the main video timeline) to begin showing the overlay. default is 0 (beginning) - "overlayStart": "0", // when (on the overlay video timeline) to begin playback on the overlay. default is 0 (beginning) - "overlayEnd": "0", // when (on the overlay video timeline) to end playback on the overlay. default is to play the whole overlay - + "operation": "overlay", // name of the operation, + "creates": { + "name": "overlay1", // asset it creates + "width": "1920", // output width in pixels. default is source width + "height": "1024" // output height in pixes. default is source height + }, + "main": "combined_vid1", // main video asset + "startTime": "30", // when (on the main video timeline) to begin showing the overlay. default is 0 (beginning) + "endTime": "60", // when (on the main video timeline) to stop showing the overlay. default is to play the entire overlay + "overlay": { + "source": "vid2", // overlay this video on the main video + "startTime": "0", // when (on the overlay video timeline) to begin playback on the overlay. default is 0 (beginning) + "endTime": "0", // when (on the overlay video timeline) to end playback on the overlay. default is to play the entire overlay "width": "overlay.width / 2", // how wide the overlay will be, in pixels. default is the full overlay width, or maintain aspect ratio if height was set - "height": "", // how tall the overlay will be, in pixels. default is the full overlay height, or maintain aspect ratio if width was set - - "x": "source.width/2", // horizontal overlay position on main video. default is 0 - "y": "source.height/2", // vertical overlay position on main video. default is 0 - - "outputWidth": "1920", // output width in pixels. default is source width - "outputHeight": "1024" // output height in pixes. default is source height + "height": "source.height", // how tall the overlay will be, in pixels. default is the full overlay height, or maintain aspect ratio if width was set + "x": "source.width / 2", // horizontal overlay position on main video. default is 0 + "y": "source.height / 2" // vertical overlay position on main video. default is 0 } } ] diff --git a/pom.xml b/pom.xml index 4c052f9..16315bf 100644 --- a/pom.xml +++ b/pom.xml @@ -41,6 +41,7 @@ javicle is available under the Apache License, version 2: http://www.apache.org/ 1.2.3 4.2.0 4.13.1 + 3.3.0 @@ -146,6 +147,12 @@ javicle is available under the Apache License, version 2: http://www.apache.org/ + + cglib + cglib + ${cglib.version} + + junit junit diff --git a/src/main/java/jvcl/main/JvclOptions.java b/src/main/java/jvcl/main/JvclOptions.java index 0c2fbc4..2c6a076 100644 --- a/src/main/java/jvcl/main/JvclOptions.java +++ b/src/main/java/jvcl/main/JvclOptions.java @@ -9,11 +9,11 @@ import org.kohsuke.args4j.Option; import java.io.File; +import static jvcl.service.Toolbox.JSON_MAPPER; import static org.cobbzilla.util.daemon.ZillaRuntime.die; import static org.cobbzilla.util.daemon.ZillaRuntime.readStdin; import static org.cobbzilla.util.io.FileUtil.abs; import static org.cobbzilla.util.io.FileUtil.toStringOrDie; -import static org.cobbzilla.util.json.JsonUtil.FULL_MAPPER_ALLOW_COMMENTS; import static org.cobbzilla.util.json.JsonUtil.json; @Slf4j @@ -26,12 +26,19 @@ public class JvclOptions extends BaseMainOptions { @Getter @Setter private File specFile; public JSpec getSpec() { + final String json; if (specFile != null && !specFile.getName().equals("-")) { - if (!specFile.exists()) return die("File not found: "+abs(specFile)); - return json(toStringOrDie(specFile), JSpec.class, FULL_MAPPER_ALLOW_COMMENTS); + if (!specFile.exists() || !specFile.canRead()) return die("File not found or unreadable: "+abs(specFile)); + json = toStringOrDie(specFile); + } else { + log.info("reading JVCL spec from stdin..."); + json = readStdin(); + } + try { + return json(json, JSpec.class, JSON_MAPPER); + } catch (Exception e) { + return die("getSpec: invalid spec: "+specFile); } - log.info("reading JVCL spec from stdin..."); - return json(readStdin(), JSpec.class); } public static final String USAGE_SCRATCH_DIR = "Scratch directory. Default is to create a temp directory under /tmp"; diff --git a/src/main/java/jvcl/model/JOperation.java b/src/main/java/jvcl/model/JOperation.java index 3063ffb..c2ef3d7 100644 --- a/src/main/java/jvcl/model/JOperation.java +++ b/src/main/java/jvcl/model/JOperation.java @@ -1,25 +1,47 @@ package jvcl.model; +import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.databind.JsonNode; +import jvcl.operation.exec.ExecBase; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.experimental.Accessors; +import lombok.extern.slf4j.Slf4j; +import java.util.HashMap; +import java.util.Map; + +import static jvcl.service.json.JOperationFactory.getOperationExecClass; import static org.cobbzilla.util.daemon.ZillaRuntime.hashOf; import static org.cobbzilla.util.json.JsonUtil.json; +import static org.cobbzilla.util.reflect.ReflectionUtil.instantiate; -@NoArgsConstructor @Accessors(chain=true) -public class JOperation { +@NoArgsConstructor @Accessors(chain=true) @Slf4j +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.EXISTING_PROPERTY, + property = "operation", + visible = true +) +public abstract class JOperation { - @Getter @Setter private JOperationType operation; + @Getter @Setter private String operation; @Getter @Setter private JsonNode creates; - @Getter @Setter private JsonNode perform; public String hash(JAsset[] sources) { return hash(sources, null); } public String hash(JAsset[] sources, Object[] args) { - return hashOf(getOperation(), json(creates), json(perform), sources, args); + return hashOf(operation, json(this), sources, args); + } + + private static final Map, ExecBase> execMap = new HashMap<>(); + public ExecBase getExec() { + return (ExecBase) execMap.computeIfAbsent(getClass(), c -> instantiate(getExecClass())); + } + + protected Class> getExecClass() { + return getOperationExecClass(getClass()); } } diff --git a/src/main/java/jvcl/model/JOperationType.java b/src/main/java/jvcl/model/JOperationType.java deleted file mode 100644 index 939e916..0000000 --- a/src/main/java/jvcl/model/JOperationType.java +++ /dev/null @@ -1,32 +0,0 @@ -package jvcl.model; - -import com.fasterxml.jackson.annotation.JsonCreator; -import jvcl.op.ConcatOperation; -import jvcl.op.OverlayOperation; -import jvcl.op.SplitOperation; -import jvcl.op.TrimOperation; -import jvcl.service.AssetManager; -import jvcl.service.JOperator; -import jvcl.service.Toolbox; -import lombok.AllArgsConstructor; - -@AllArgsConstructor -public enum JOperationType { - - concat (new ConcatOperation()), - split (new SplitOperation()), - trim (new TrimOperation()), - overlay (new OverlayOperation()), - ken_burns (null), - letterbox (null), - split_silence (null); - - @JsonCreator public static JOperationType fromString(String v) { return valueOf(v.toLowerCase().replace("-", "_")); } - - private final JOperator operator; - - public void perform(JOperation op, Toolbox toolbox, AssetManager assetManager) { - operator.operate(op, toolbox, assetManager); - } - -} diff --git a/src/main/java/jvcl/op/OverlayOperation.java b/src/main/java/jvcl/op/OverlayOperation.java deleted file mode 100644 index 221541a..0000000 --- a/src/main/java/jvcl/op/OverlayOperation.java +++ /dev/null @@ -1,140 +0,0 @@ -package jvcl.op; - -import jvcl.model.JAsset; -import jvcl.model.JFileExtension; -import jvcl.model.JOperation; -import jvcl.service.AssetManager; -import jvcl.service.JOperator; -import jvcl.service.Toolbox; -import lombok.Getter; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import org.cobbzilla.util.javascript.JsEngine; -import org.cobbzilla.util.javascript.StandardJsEngine; - -import java.io.File; -import java.math.BigDecimal; -import java.util.HashMap; -import java.util.Map; - -import static java.math.RoundingMode.HALF_EVEN; -import static jvcl.model.JAsset.json2asset; -import static jvcl.service.Toolbox.getDuration; -import static org.cobbzilla.util.daemon.ZillaRuntime.big; -import static org.cobbzilla.util.daemon.ZillaRuntime.empty; -import static org.cobbzilla.util.io.FileUtil.abs; -import static org.cobbzilla.util.system.CommandShell.execScript; - -@Slf4j -public class OverlayOperation implements JOperator { - - public static final String OVERLAY_TEMPLATE - = "{{ffmpeg}} -i {{{source.path}}} -filter_complex \"" + - "movie={{{overlay.path}}}{{#exists overlayStart}}:seek_point={{overlayStart}}{{/exists}} [ovl]; " + - "[v:0] setpts=PTS-(STARTPTS+{{#exists offset}}{{offset}}{{else}}0{{/exists}}) [main]; " + - "[ovl] setpts=PTS-STARTPTS{{#exists width}}, scale={{width}}x{{height}}{{/exists}} ; " + - "[main][ovl] overlay=shortest=1{{#exists x}}:x={{x}}{{/exists}}{{#exists y}}:y={{y}}{{/exists}} " + - "\" {{{output.path}}}"; - - @Override public void operate(JOperation op, Toolbox toolbox, AssetManager assetManager) { - final OverlayConfig config = loadConfig(op, OverlayConfig.class); - final JAsset source = assetManager.resolve(config.getSource()); - final JAsset overlay = assetManager.resolve(config.getOverlay()); - - final JAsset output = json2asset(op.getCreates()); - output.mergeFormat(source.getFormat()); - - final JFileExtension formatType = output.getFormat().getFileExtension(); - - final File defaultOutfile = assetManager.assetPath(op, source, formatType, new Object[]{config}); - final File path = resolveOutputPath(output, defaultOutfile); - if (path == null) return; - output.setPath(abs(path)); - - final StandardJsEngine js = toolbox.getJs(); - final Map ctx = new HashMap<>(); - ctx.put("ffmpeg", toolbox.getFfmpeg()); - ctx.put("source", source); - ctx.put("overlay", overlay); - ctx.put("output", output); - ctx.put("offset", config.getOffsetSeconds(ctx, js)); - ctx.put("overlayStart", config.getOverlayStartSeconds(ctx, js)); - if (config.hasOverlayEnd()) ctx.put("overlayEnd", config.getOverlayEndSeconds(ctx, js)); - if (config.hasWidth()) { - final String width = config.getWidth(ctx, js); - ctx.put("width", width); - if (!config.hasHeight()) { - final int height = big(width).divide(overlay.aspectRatio(), HALF_EVEN).intValue(); - ctx.put("height", height); - } - } - if (config.hasHeight()) { - final String height = config.getHeight(ctx, js); - ctx.put("height", height); - if (!config.hasWidth()) { - final int width = big(height).multiply(overlay.aspectRatio()).intValue(); - ctx.put("width", width); - } - } - if (config.hasX()) ctx.put("x", config.getX(ctx, js)); - if (config.hasY()) ctx.put("y", config.getY(ctx, js)); - - final String script = renderScript(toolbox, ctx, OVERLAY_TEMPLATE); - - log.debug("operate: running script: "+script); - final String scriptOutput = execScript(script); - log.debug("operate: command output: "+scriptOutput); - assetManager.addOperationAsset(output); - } - - private static class OverlayConfig { - @Getter @Setter private String source; - @Getter @Setter private String overlay; - - private String eval(String val, Map ctx, JsEngine js) { - final Map jsCtx = Toolbox.jsContext(ctx); - final Object result = js.evaluate(val, jsCtx); - return result == null ? null : result.toString(); - } - - @Getter @Setter private String offset; - public BigDecimal getOffsetSeconds (Map ctx, JsEngine js) { - return empty(offset) ? BigDecimal.ZERO : getDuration(eval(offset, ctx, js)); - } - - @Getter @Setter private String overlayStart; - public BigDecimal getOverlayStartSeconds (Map ctx, JsEngine js) { - return empty(overlayStart) ? BigDecimal.ZERO : getDuration(eval(overlayStart, ctx, js)); - } - - @Getter @Setter private String overlayEnd; - public boolean hasOverlayEnd () { return !empty(overlayEnd); } - public BigDecimal getOverlayEndSeconds (Map ctx, JsEngine js) { - return getDuration(eval(overlayEnd, ctx, js)); - } - - @Getter @Setter private String width; - public boolean hasWidth () { return !empty(width); } - public String getWidth(Map ctx, JsEngine js) { return eval(width, ctx, js); } - - @Getter @Setter private String height; - public boolean hasHeight () { return !empty(height); } - public String getHeight(Map ctx, JsEngine js) { return eval(height, ctx, js); } - - @Getter @Setter private String x; - public boolean hasX () { return !empty(x); } - public String getX(Map ctx, JsEngine js) { return eval(x, ctx, js); } - - @Getter @Setter private String y; - public boolean hasY () { return !empty(y); } - public String getY(Map ctx, JsEngine js) { return eval(y, ctx, js); } - - @Getter @Setter private String outputWidth; - public boolean hasOutputWidth () { return !empty(outputWidth); } - public String getOutputWidth(Map ctx, JsEngine js) { return eval(outputWidth, ctx, js); } - - @Getter @Setter private String outputHeight; - public boolean hasOutputHeight () { return !empty(outputHeight); } - public String getOutputHeight(Map ctx, JsEngine js) { return eval(outputHeight, ctx, js); } - } -} diff --git a/src/main/java/jvcl/operation/ConcatOperation.java b/src/main/java/jvcl/operation/ConcatOperation.java new file mode 100644 index 0000000..2e9eb05 --- /dev/null +++ b/src/main/java/jvcl/operation/ConcatOperation.java @@ -0,0 +1,29 @@ +package jvcl.operation; + +import jvcl.model.JAsset; +import jvcl.model.JFileExtension; +import jvcl.model.JOperation; +import jvcl.service.AssetManager; +import jvcl.service.Toolbox; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static jvcl.model.JAsset.flattenAssetList; +import static jvcl.model.JAsset.json2asset; +import static org.cobbzilla.util.daemon.ZillaRuntime.die; +import static org.cobbzilla.util.daemon.ZillaRuntime.empty; +import static org.cobbzilla.util.io.FileUtil.abs; +import static org.cobbzilla.util.system.CommandShell.execScript; + +@Slf4j +public class ConcatOperation extends JOperation { + + @Getter @Setter private String[] concat; + +} diff --git a/src/main/java/jvcl/operation/OverlayOperation.java b/src/main/java/jvcl/operation/OverlayOperation.java new file mode 100644 index 0000000..a214f0c --- /dev/null +++ b/src/main/java/jvcl/operation/OverlayOperation.java @@ -0,0 +1,79 @@ +package jvcl.operation; + +import jvcl.model.JAsset; +import jvcl.model.JFileExtension; +import jvcl.model.JOperation; +import jvcl.service.AssetManager; +import jvcl.service.Toolbox; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.cobbzilla.util.javascript.JsEngine; +import org.cobbzilla.util.javascript.StandardJsEngine; + +import java.io.File; +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Map; + +import static java.math.RoundingMode.HALF_EVEN; +import static jvcl.model.JAsset.json2asset; +import static jvcl.service.Toolbox.getDuration; +import static org.cobbzilla.util.daemon.ZillaRuntime.big; +import static org.cobbzilla.util.daemon.ZillaRuntime.empty; +import static org.cobbzilla.util.io.FileUtil.abs; +import static org.cobbzilla.util.system.CommandShell.execScript; + +@Slf4j +public class OverlayOperation extends JOperation { + + @Getter @Setter private String source; + @Getter @Setter private String overlay; + + private String eval(String val, Map ctx, JsEngine js) { + final Map jsCtx = Toolbox.jsContext(ctx); + final Object result = js.evaluate(val, jsCtx); + return result == null ? null : result.toString(); + } + + @Getter @Setter private String offset; + public BigDecimal getOffsetSeconds (Map ctx, JsEngine js) { + return empty(offset) ? BigDecimal.ZERO : getDuration(eval(offset, ctx, js)); + } + + @Getter @Setter private String overlayStart; + public BigDecimal getOverlayStartSeconds (Map ctx, JsEngine js) { + return empty(overlayStart) ? BigDecimal.ZERO : getDuration(eval(overlayStart, ctx, js)); + } + + @Getter @Setter private String overlayEnd; + public boolean hasOverlayEnd () { return !empty(overlayEnd); } + public BigDecimal getOverlayEndSeconds (Map ctx, JsEngine js) { + return getDuration(eval(overlayEnd, ctx, js)); + } + + @Getter @Setter private String width; + public boolean hasWidth () { return !empty(width); } + public String getWidth(Map ctx, JsEngine js) { return eval(width, ctx, js); } + + @Getter @Setter private String height; + public boolean hasHeight () { return !empty(height); } + public String getHeight(Map ctx, JsEngine js) { return eval(height, ctx, js); } + + @Getter @Setter private String x; + public boolean hasX () { return !empty(x); } + public String getX(Map ctx, JsEngine js) { return eval(x, ctx, js); } + + @Getter @Setter private String y; + public boolean hasY () { return !empty(y); } + public String getY(Map ctx, JsEngine js) { return eval(y, ctx, js); } + + @Getter @Setter private String outputWidth; + public boolean hasOutputWidth () { return !empty(outputWidth); } + public String getOutputWidth(Map ctx, JsEngine js) { return eval(outputWidth, ctx, js); } + + @Getter @Setter private String outputHeight; + public boolean hasOutputHeight () { return !empty(outputHeight); } + public String getOutputHeight(Map ctx, JsEngine js) { return eval(outputHeight, ctx, js); } + +} diff --git a/src/main/java/jvcl/operation/SplitOperation.java b/src/main/java/jvcl/operation/SplitOperation.java new file mode 100644 index 0000000..6c41a89 --- /dev/null +++ b/src/main/java/jvcl/operation/SplitOperation.java @@ -0,0 +1,28 @@ +package jvcl.operation; + +import jvcl.model.JAsset; +import jvcl.model.JOperation; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +import java.math.BigDecimal; + +import static jvcl.service.Toolbox.getDuration; +import static org.cobbzilla.util.daemon.ZillaRuntime.empty; + +@Slf4j +public class SplitOperation extends JOperation { + + @Getter @Setter private String split; + + @Getter @Setter private String interval; + public BigDecimal getIntervalIncr() { return getDuration(interval); } + + @Getter @Setter private String start; + public BigDecimal getStartTime() { return empty(start) ? BigDecimal.ZERO : getDuration(start); } + + @Getter @Setter private String end; + public BigDecimal getEndTime(JAsset source) { return empty(end) ? source.duration() : getDuration(end); } + +} diff --git a/src/main/java/jvcl/operation/TrimOperation.java b/src/main/java/jvcl/operation/TrimOperation.java new file mode 100644 index 0000000..f142d27 --- /dev/null +++ b/src/main/java/jvcl/operation/TrimOperation.java @@ -0,0 +1,39 @@ +package jvcl.operation; + +import jvcl.model.JAsset; +import jvcl.model.JFileExtension; +import jvcl.model.JOperation; +import jvcl.service.AssetManager; +import jvcl.service.Toolbox; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Map; + +import static jvcl.model.JAsset.json2asset; +import static jvcl.service.Toolbox.getDuration; +import static org.cobbzilla.util.daemon.ZillaRuntime.die; +import static org.cobbzilla.util.daemon.ZillaRuntime.empty; +import static org.cobbzilla.util.io.FileUtil.*; +import static org.cobbzilla.util.system.CommandShell.execScript; + +@Slf4j +public class TrimOperation extends JOperation { + + @Getter @Setter private String trim; + + @Getter @Setter private String start; + public BigDecimal getStartTime() { return empty(start) ? BigDecimal.ZERO : getDuration(start); } + + @Getter @Setter private String end; + public boolean hasEnd() { return !empty(end); } + public BigDecimal getEndTime() { return getDuration(end); } + + public String shortString() { return "trim_"+getStart()+(hasEnd() ? "_"+getEnd() : ""); } + public String toString() { return trim+"_"+getStart()+(hasEnd() ? "_"+getEnd() : ""); } + +} diff --git a/src/main/java/jvcl/op/ConcatOperation.java b/src/main/java/jvcl/operation/exec/ConcatExec.java similarity index 83% rename from src/main/java/jvcl/op/ConcatOperation.java rename to src/main/java/jvcl/operation/exec/ConcatExec.java index 4fea2cf..7ff4041 100644 --- a/src/main/java/jvcl/op/ConcatOperation.java +++ b/src/main/java/jvcl/operation/exec/ConcatExec.java @@ -1,14 +1,10 @@ -package jvcl.op; +package jvcl.operation.exec; import jvcl.model.JAsset; import jvcl.model.JFileExtension; -import jvcl.model.JOperation; +import jvcl.operation.ConcatOperation; import jvcl.service.AssetManager; -import jvcl.service.JOperator; import jvcl.service.Toolbox; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; import lombok.extern.slf4j.Slf4j; import java.io.File; @@ -24,7 +20,7 @@ import static org.cobbzilla.util.io.FileUtil.abs; import static org.cobbzilla.util.system.CommandShell.execScript; @Slf4j -public class ConcatOperation implements JOperator { +public class ConcatExec extends ExecBase { public static final String CONCAT_RECODE_TEMPLATE_OLD // concat inputs @@ -45,12 +41,10 @@ public class ConcatOperation implements JOperator { // output combined result + "-map \"[v]\" -map \"[a]\" {{{output.path}}}"; - @Override public void operate(JOperation op, Toolbox toolbox, AssetManager assetManager) { - - final ConcatConfig config = loadConfig(op, ConcatConfig.class); + @Override public void operate(ConcatOperation op, Toolbox toolbox, AssetManager assetManager) { // validate sources - final List sources = flattenAssetList(assetManager.resolve(config.getConcat())); + final List sources = flattenAssetList(assetManager.resolve(op.getConcat())); if (empty(sources)) die("operate: no sources"); // create output object @@ -78,9 +72,4 @@ public class ConcatOperation implements JOperator { assetManager.addOperationAsset(output); } - @NoArgsConstructor - private static class ConcatConfig { - @Getter @Setter private String[] concat; - } - } diff --git a/src/main/java/jvcl/service/JOperator.java b/src/main/java/jvcl/operation/exec/ExecBase.java similarity index 58% rename from src/main/java/jvcl/service/JOperator.java rename to src/main/java/jvcl/operation/exec/ExecBase.java index d1edb8c..96d1c4f 100644 --- a/src/main/java/jvcl/service/JOperator.java +++ b/src/main/java/jvcl/operation/exec/ExecBase.java @@ -1,33 +1,28 @@ -package jvcl.service; +package jvcl.operation.exec; import jvcl.model.JAsset; import jvcl.model.JOperation; +import jvcl.service.AssetManager; +import jvcl.service.Toolbox; +import lombok.extern.slf4j.Slf4j; import org.cobbzilla.util.handlebars.HandlebarsUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.io.File; import java.util.Map; import static org.cobbzilla.util.io.FileUtil.abs; import static org.cobbzilla.util.io.FileUtil.basename; -import static org.cobbzilla.util.json.JsonUtil.json; -public interface JOperator { +@Slf4j +public abstract class ExecBase { - Logger log = LoggerFactory.getLogger(JOperator.class); + public abstract void operate(OP operation, Toolbox toolbox, AssetManager assetManager); - void operate(JOperation op, Toolbox toolbox, AssetManager assetManager); - - default T loadConfig(JOperation op, Class configClass) { - return json(json(op.getPerform()), configClass); - } - - default String renderScript(Toolbox toolbox, Map ctx, String template) { + protected String renderScript(Toolbox toolbox, Map ctx, String template) { return HandlebarsUtil.apply(toolbox.getHandlebars(), template, ctx); } - default File resolveOutputPath(JAsset output, File defaultOutfile) { + protected File resolveOutputPath(JAsset output, File defaultOutfile) { if (output.hasDest()) { if (output.destExists() && !output.destIsDirectory()) { log.info("resolveOutputPath: dest exists: " + output.getDest()); @@ -41,6 +36,4 @@ public interface JOperator { return defaultOutfile; } } - - } diff --git a/src/main/java/jvcl/operation/exec/OverlayExec.java b/src/main/java/jvcl/operation/exec/OverlayExec.java new file mode 100644 index 0000000..7f73c52 --- /dev/null +++ b/src/main/java/jvcl/operation/exec/OverlayExec.java @@ -0,0 +1,82 @@ +package jvcl.operation.exec; + +import jvcl.model.JAsset; +import jvcl.model.JFileExtension; +import jvcl.operation.OverlayOperation; +import jvcl.service.AssetManager; +import jvcl.service.Toolbox; +import lombok.extern.slf4j.Slf4j; +import org.cobbzilla.util.javascript.StandardJsEngine; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import static java.math.RoundingMode.HALF_EVEN; +import static jvcl.model.JAsset.json2asset; +import static org.cobbzilla.util.daemon.ZillaRuntime.big; +import static org.cobbzilla.util.io.FileUtil.abs; +import static org.cobbzilla.util.system.CommandShell.execScript; + +@Slf4j +public class OverlayExec extends ExecBase { + + public static final String OVERLAY_TEMPLATE + = "{{ffmpeg}} -i {{{source.path}}} -filter_complex \"" + + "movie={{{overlay.path}}}{{#exists overlayStart}}:seek_point={{overlayStart}}{{/exists}} [ovl]; " + + "[v:0] setpts=PTS-(STARTPTS+{{#exists offset}}{{offset}}{{else}}0{{/exists}}) [main]; " + + "[ovl] setpts=PTS-STARTPTS{{#exists width}}, scale={{width}}x{{height}}{{/exists}} ; " + + "[main][ovl] overlay=shortest=1{{#exists x}}:x={{x}}{{/exists}}{{#exists y}}:y={{y}}{{/exists}} " + + "\" {{{output.path}}}"; + + @Override public void operate(OverlayOperation op, Toolbox toolbox, AssetManager assetManager) { + final JAsset source = assetManager.resolve(op.getSource()); + final JAsset overlay = assetManager.resolve(op.getOverlay()); + + final JAsset output = json2asset(op.getCreates()); + output.mergeFormat(source.getFormat()); + + final JFileExtension formatType = output.getFormat().getFileExtension(); + + final File defaultOutfile = assetManager.assetPath(op, source, formatType); + final File path = resolveOutputPath(output, defaultOutfile); + if (path == null) return; + output.setPath(abs(path)); + + final StandardJsEngine js = toolbox.getJs(); + final Map ctx = new HashMap<>(); + ctx.put("ffmpeg", toolbox.getFfmpeg()); + ctx.put("source", source); + ctx.put("overlay", overlay); + ctx.put("output", output); + ctx.put("offset", op.getOffsetSeconds(ctx, js)); + ctx.put("overlayStart", op.getOverlayStartSeconds(ctx, js)); + if (op.hasOverlayEnd()) ctx.put("overlayEnd", op.getOverlayEndSeconds(ctx, js)); + if (op.hasWidth()) { + final String width = op.getWidth(ctx, js); + ctx.put("width", width); + if (!op.hasHeight()) { + final int height = big(width).divide(overlay.aspectRatio(), HALF_EVEN).intValue(); + ctx.put("height", height); + } + } + if (op.hasHeight()) { + final String height = op.getHeight(ctx, js); + ctx.put("height", height); + if (!op.hasWidth()) { + final int width = big(height).multiply(overlay.aspectRatio()).intValue(); + ctx.put("width", width); + } + } + if (op.hasX()) ctx.put("x", op.getX(ctx, js)); + if (op.hasY()) ctx.put("y", op.getY(ctx, js)); + + final String script = renderScript(toolbox, ctx, OVERLAY_TEMPLATE); + + log.debug("operate: running script: "+script); + final String scriptOutput = execScript(script); + log.debug("operate: command output: "+scriptOutput); + assetManager.addOperationAsset(output); + } + +} diff --git a/src/main/java/jvcl/op/SplitOperation.java b/src/main/java/jvcl/operation/exec/SplitExec.java similarity index 70% rename from src/main/java/jvcl/op/SplitOperation.java rename to src/main/java/jvcl/operation/exec/SplitExec.java index 438241c..f8b9bf4 100644 --- a/src/main/java/jvcl/op/SplitOperation.java +++ b/src/main/java/jvcl/operation/exec/SplitExec.java @@ -1,14 +1,10 @@ -package jvcl.op; +package jvcl.operation.exec; import jvcl.model.JAsset; import jvcl.model.JFileExtension; -import jvcl.model.JOperation; +import jvcl.operation.SplitOperation; import jvcl.service.AssetManager; -import jvcl.service.JOperator; import jvcl.service.Toolbox; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; import lombok.extern.slf4j.Slf4j; import java.io.File; @@ -17,23 +13,20 @@ import java.util.HashMap; import java.util.Map; import static jvcl.model.JAsset.json2asset; -import static jvcl.service.Toolbox.getDuration; import static org.cobbzilla.util.daemon.ZillaRuntime.die; -import static org.cobbzilla.util.daemon.ZillaRuntime.empty; import static org.cobbzilla.util.io.FileUtil.abs; import static org.cobbzilla.util.io.FileUtil.mkdirOrDie; import static org.cobbzilla.util.system.CommandShell.execScript; @Slf4j -public class SplitOperation implements JOperator { +public class SplitExec extends ExecBase { public static final String SPLIT_TEMPLATE = "{{ffmpeg}} -i {{{source.path}}} -ss {{startSeconds}} -t {{interval}} {{{output.path}}}"; - @Override public void operate(JOperation op, Toolbox toolbox, AssetManager assetManager) { - final SplitConfig config = loadConfig(op, SplitConfig.class); + @Override public void operate(SplitOperation op, Toolbox toolbox, AssetManager assetManager) { - final JAsset source = assetManager.resolve(config.getSplit()); + final JAsset source = assetManager.resolve(op.getSplit()); // create output object final JAsset output = json2asset(op.getCreates()); @@ -48,9 +41,9 @@ public class SplitOperation implements JOperator { final Map ctx = new HashMap<>(); ctx.put("ffmpeg", toolbox.getFfmpeg()); ctx.put("source", source); - final BigDecimal incr = config.getIntervalIncr(); - final BigDecimal endTime = config.getEndTime(source); - for (BigDecimal i = config.getStartTime(); + final BigDecimal incr = op.getIntervalIncr(); + final BigDecimal endTime = op.getEndTime(source); + for (BigDecimal i = op.getStartTime(); i.compareTo(endTime) < 0; i = i.add(incr)) { @@ -97,19 +90,4 @@ public class SplitOperation implements JOperator { return new File(output.destDirectory(), output.getName() + "_" + i + "_" + incr + formatType.ext()); } - @NoArgsConstructor - private static class SplitConfig { - - @Getter @Setter private String split; - - @Getter @Setter private String interval; - public BigDecimal getIntervalIncr() { return getDuration(interval); } - - @Getter @Setter private String start; - public BigDecimal getStartTime() { return empty(start) ? BigDecimal.ZERO : getDuration(start); } - - @Getter @Setter private String end; - public BigDecimal getEndTime(JAsset source) { return empty(end) ? source.duration() : getDuration(end); } - } - } diff --git a/src/main/java/jvcl/op/TrimOperation.java b/src/main/java/jvcl/operation/exec/TrimExec.java similarity index 62% rename from src/main/java/jvcl/op/TrimOperation.java rename to src/main/java/jvcl/operation/exec/TrimExec.java index 901cfd2..2f1cfa0 100644 --- a/src/main/java/jvcl/op/TrimOperation.java +++ b/src/main/java/jvcl/operation/exec/TrimExec.java @@ -1,15 +1,10 @@ -package jvcl.op; +package jvcl.operation.exec; import jvcl.model.JAsset; import jvcl.model.JFileExtension; -import jvcl.model.JOperation; +import jvcl.operation.TrimOperation; import jvcl.service.AssetManager; -import jvcl.service.JOperator; import jvcl.service.Toolbox; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; import lombok.extern.slf4j.Slf4j; import java.io.File; @@ -18,22 +13,19 @@ import java.util.HashMap; import java.util.Map; import static jvcl.model.JAsset.json2asset; -import static jvcl.service.Toolbox.getDuration; import static org.cobbzilla.util.daemon.ZillaRuntime.die; -import static org.cobbzilla.util.daemon.ZillaRuntime.empty; import static org.cobbzilla.util.io.FileUtil.*; import static org.cobbzilla.util.system.CommandShell.execScript; @Slf4j -public class TrimOperation implements JOperator { +public class TrimExec extends ExecBase { public static final String TRIM_TEMPLATE = "{{ffmpeg}} -i {{{source.path}}} -ss {{startSeconds}} {{#exists interval}}-t {{interval}} {{/exists}}{{{output.path}}}"; - @Override public void operate(JOperation op, Toolbox toolbox, AssetManager assetManager) { + @Override public void operate(TrimOperation op, Toolbox toolbox, AssetManager assetManager) { - final TrimConfig config = loadConfig(op, TrimConfig.class); - final JAsset source = assetManager.resolve(config.getTrim()); + final JAsset source = assetManager.resolve(op.getTrim()); final JAsset output = json2asset(op.getCreates()); output.mergeFormat(source.getFormat()); @@ -47,10 +39,10 @@ public class TrimOperation implements JOperator { assetManager.addOperationArrayAsset(output); for (JAsset asset : source.getList()) { final JAsset subOutput = new JAsset(output); - final File defaultOutfile = assetManager.assetPath(op, asset, formatType, new Object[]{config}); + final File defaultOutfile = assetManager.assetPath(op, asset, formatType); final File outfile; if (output.hasDest()) { - outfile = new File(output.destDirectory(), basename(appendToFileNameBeforeExt(asset.getPath(), "_"+config.shortString()))); + outfile = new File(output.destDirectory(), basename(appendToFileNameBeforeExt(asset.getPath(), "_"+op.shortString()))); if (outfile.exists()) { log.info("operate: dest exists: "+abs(outfile)); return; @@ -59,18 +51,18 @@ public class TrimOperation implements JOperator { outfile = defaultOutfile; } subOutput.setPath(abs(outfile)); - trim(config, asset, output, subOutput, toolbox, assetManager); + trim(op, asset, output, subOutput, toolbox, assetManager); } } else { - final File defaultOutfile = assetManager.assetPath(op, source, formatType, new Object[]{config}); + final File defaultOutfile = assetManager.assetPath(op, source, formatType); final File path = resolveOutputPath(output, defaultOutfile); if (path == null) return; output.setPath(abs(path)); - trim(config, source, output, output, toolbox, assetManager); + trim(op, source, output, output, toolbox, assetManager); } } - private void trim(TrimConfig config, + private void trim(TrimOperation op, JAsset source, JAsset output, JAsset subOutput, @@ -81,9 +73,9 @@ public class TrimOperation implements JOperator { ctx.put("source", source); ctx.put("output", subOutput); - final BigDecimal startTime = config.getStartTime(); + final BigDecimal startTime = op.getStartTime(); ctx.put("startSeconds", startTime); - if (config.hasEnd()) ctx.put("interval", config.getEndTime().subtract(startTime)); + if (op.hasEnd()) ctx.put("interval", op.getEndTime().subtract(startTime)); final String script = renderScript(toolbox, ctx, TRIM_TEMPLATE); log.debug("operate: running script: "+script); @@ -96,18 +88,4 @@ public class TrimOperation implements JOperator { } } - @NoArgsConstructor @EqualsAndHashCode - private static class TrimConfig { - @Getter @Setter private String trim; - - @Getter @Setter private String start; - public BigDecimal getStartTime() { return empty(start) ? BigDecimal.ZERO : getDuration(start); } - - @Getter @Setter private String end; - public boolean hasEnd() { return !empty(end); } - public BigDecimal getEndTime() { return getDuration(end); } - - public String shortString() { return "trim_"+getStart()+(hasEnd() ? "_"+getEnd() : ""); } - public String toString() { return trim+"_"+getStart()+(hasEnd() ? "_"+getEnd() : ""); } - } } diff --git a/src/main/java/jvcl/service/OperationEngine.java b/src/main/java/jvcl/service/OperationEngine.java index 29efd24..c47b7f0 100644 --- a/src/main/java/jvcl/service/OperationEngine.java +++ b/src/main/java/jvcl/service/OperationEngine.java @@ -13,6 +13,6 @@ public class OperationEngine { } public void perform(JOperation op) { - op.getOperation().perform(op, toolbox, assetManager); + op.getExec().operate(op, toolbox, assetManager); } } diff --git a/src/main/java/jvcl/service/Toolbox.java b/src/main/java/jvcl/service/Toolbox.java index 989cdd4..8239a5d 100644 --- a/src/main/java/jvcl/service/Toolbox.java +++ b/src/main/java/jvcl/service/Toolbox.java @@ -1,13 +1,14 @@ package jvcl.service; +import com.fasterxml.jackson.databind.ObjectMapper; import com.github.jknack.handlebars.Handlebars; import jvcl.model.JAsset; import jvcl.model.JsObjectView; import jvcl.model.info.JMediaInfo; +import jvcl.service.json.JOperationModule; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.cobbzilla.util.handlebars.HandlebarsUtil; -import org.cobbzilla.util.io.FileUtil; import org.cobbzilla.util.javascript.StandardJsEngine; import java.io.File; @@ -18,10 +19,8 @@ import java.util.concurrent.ConcurrentHashMap; import static java.math.RoundingMode.HALF_EVEN; import static org.cobbzilla.util.daemon.ZillaRuntime.*; -import static org.cobbzilla.util.io.FileUtil.abs; -import static org.cobbzilla.util.io.FileUtil.replaceExt; -import static org.cobbzilla.util.json.JsonUtil.FULL_MAPPER_ALLOW_UNKNOWN_FIELDS; -import static org.cobbzilla.util.json.JsonUtil.json; +import static org.cobbzilla.util.io.FileUtil.*; +import static org.cobbzilla.util.json.JsonUtil.*; import static org.cobbzilla.util.system.CommandShell.execScript; import static org.cobbzilla.util.time.TimeUtil.parseDuration; @@ -30,6 +29,9 @@ public class Toolbox { public static final Toolbox DEFAULT_TOOLBOX = new Toolbox(); + public static final ObjectMapper JSON_MAPPER = FULL_MAPPER_ALLOW_COMMENTS; + static { JSON_MAPPER.registerModule(new JOperationModule()); } + @Getter(lazy=true) private final Handlebars handlebars = initHandlebars(); @Getter(lazy=true) private final StandardJsEngine js = new StandardJsEngine(); @@ -90,10 +92,11 @@ public class Toolbox { } return infoCache.computeIfAbsent(infoPath, p -> { try { - return json(FileUtil.toStringOrDie(infoFile), JMediaInfo.class, FULL_MAPPER_ALLOW_UNKNOWN_FIELDS); + return json(toStringOrDie(infoFile), JMediaInfo.class, FULL_MAPPER_ALLOW_UNKNOWN_FIELDS); } catch (Exception e) { return die("getInfo: "+shortError(e), e); } }); } + } diff --git a/src/main/java/jvcl/service/json/JOperationFactory.java b/src/main/java/jvcl/service/json/JOperationFactory.java new file mode 100644 index 0000000..79397ca --- /dev/null +++ b/src/main/java/jvcl/service/json/JOperationFactory.java @@ -0,0 +1,58 @@ +package jvcl.service.json; + +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler; +import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; +import jvcl.model.JOperation; +import jvcl.operation.exec.ExecBase; + +import java.io.IOException; + +import static com.fasterxml.jackson.databind.type.TypeBindings.emptyBindings; +import static org.apache.commons.lang3.StringUtils.capitalize; +import static org.cobbzilla.util.daemon.ZillaRuntime.die; +import static org.cobbzilla.util.daemon.ZillaRuntime.shortError; +import static org.cobbzilla.util.reflect.ReflectionUtil.forName; + +public class JOperationFactory extends DeserializationProblemHandler { + + @Override public JavaType handleUnknownTypeId(DeserializationContext ctxt, + JavaType baseType, + String subTypeId, + TypeIdResolver idResolver, + String failureMsg) throws IOException { + try { + return new JOperationType(getOperationClass(subTypeId), emptyBindings(), baseType, null); + } catch (Exception e) { + throw new IOException("handleUnknownTypeId: '"+subTypeId+"' is not a valid operation type: "+shortError(e)); + } + } + + public static final String OPERATION_DEFAULT_PACKAGE = "jvcl.operation"; + public static final String OPERATION_CLASSNAME_SUFFIX = "Operation"; + + public static Class getOperationClass(String id) { + final String className; + if (id.contains(".")) { + className = id; + } else { + className = OPERATION_DEFAULT_PACKAGE + "." + capitalize(id) + OPERATION_CLASSNAME_SUFFIX; + } + return forName(className); + } + + public static Class> getOperationExecClass(Class opClass) { + final String name = opClass.getSimpleName(); + if (!name.endsWith(OPERATION_CLASSNAME_SUFFIX)) { + return die("getOperationExecClass: expected JOperation class to end with '" + OPERATION_CLASSNAME_SUFFIX + "'"); + } + final String execClassName + = opClass.getPackageName() + + ".exec." + + name.substring(0, name.length() - OPERATION_CLASSNAME_SUFFIX.length()) + + "Exec"; + return forName(execClassName); + } + +} diff --git a/src/main/java/jvcl/service/json/JOperationModule.java b/src/main/java/jvcl/service/json/JOperationModule.java new file mode 100644 index 0000000..ceecd61 --- /dev/null +++ b/src/main/java/jvcl/service/json/JOperationModule.java @@ -0,0 +1,15 @@ +package jvcl.service.json; + +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.databind.Module; + +public class JOperationModule extends Module { + + @Override public String getModuleName() { return "JOperationFactoryModule"; } + @Override public Version version() { return new Version(1, 0, 0, "", "", ""); } + + @Override public void setupModule(SetupContext context) { + context.addDeserializationProblemHandler(new JOperationFactory()); + } + +} diff --git a/src/main/java/jvcl/service/json/JOperationType.java b/src/main/java/jvcl/service/json/JOperationType.java new file mode 100644 index 0000000..9c4e1fb --- /dev/null +++ b/src/main/java/jvcl/service/json/JOperationType.java @@ -0,0 +1,26 @@ +package jvcl.service.json; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.SimpleType; +import com.fasterxml.jackson.databind.type.TypeBase; +import com.fasterxml.jackson.databind.type.TypeBindings; + +public class JOperationType extends SimpleType { + + protected JOperationType(Class cls) { super(cls); } + + public JOperationType(Class cls, TypeBindings bindings, JavaType superClass, JavaType[] superInts) { + super(cls, bindings, superClass, superInts); + } + + protected JOperationType(TypeBase base) { super(base); } + + protected JOperationType(Class cls, TypeBindings bindings, JavaType superClass, JavaType[] superInts, Object valueHandler, Object typeHandler, boolean asStatic) { + super(cls, bindings, superClass, superInts, valueHandler, typeHandler, asStatic); + } + + protected JOperationType(Class cls, TypeBindings bindings, JavaType superClass, JavaType[] superInts, int extraHash, Object valueHandler, Object typeHandler, boolean asStatic) { + super(cls, bindings, superClass, superInts, extraHash, valueHandler, typeHandler, asStatic); + } + +} diff --git a/src/test/resources/tests/test_concat.json b/src/test/resources/tests/test_concat.json index a0fd053..e41f030 100644 --- a/src/test/resources/tests/test_concat.json +++ b/src/test/resources/tests/test_concat.json @@ -9,9 +9,7 @@ "name": "combined_vid", "dest": "src/test/resources/outputs/combined.mp4" }, - "perform": { - "concat": ["vid1_splits[1..]"] - } + "concat": ["vid1_splits[1..]"] } ] } diff --git a/src/test/resources/tests/test_split.json b/src/test/resources/tests/test_split.json index 4c272a2..1a32fe2 100644 --- a/src/test/resources/tests/test_split.json +++ b/src/test/resources/tests/test_split.json @@ -13,17 +13,15 @@ ], "operations": [ { - "operation": "split", // name of the operation + "operation": "split", // name of the operation "creates": { "name": "vid1_splits", "dest": "src/test/resources/outputs/" }, - "perform": { - "split": "vid1", // split this source asset - "interval": "10s", // split every ten seconds - "start": "65s", // start one minute and five seconds into the video - "end": "100s" // end 100 seconds into the video - } + "split": "vid1", // split this source asset + "interval": "10s", // split every ten seconds + "start": "65s", // start one minute and five seconds into the video + "end": "100s" // end 100 seconds into the video } ] } diff --git a/src/test/resources/tests/test_trim.json b/src/test/resources/tests/test_trim.json index 796d733..fc98767 100644 --- a/src/test/resources/tests/test_trim.json +++ b/src/test/resources/tests/test_trim.json @@ -4,16 +4,14 @@ ], "operations": [ { - "operation": "trim", // name of the operation + "operation": "trim", // name of the operation "creates": { "name": "vid1_trims", "dest": "src/test/resources/outputs/trims/" }, - "perform": { - "trim": "vid1_splits", // trim these source assets - "start": "1s", // cropped region starts here, default is zero - "end": "6s" // cropped region ends here, default is end of video - } + "trim": "vid1_splits", // trim these source assets + "start": "1s", // cropped region starts here, default is zero + "end": "6s" // cropped region ends here, default is end of video } ] }