@@ -15,7 +15,7 @@ and bash, you might do something like this: | |||||
ffmpeg -i /tmp/my/source.mp4 -ss ${i} -t $((i+INCR)) /tmp/my/slice_${i}_$((i+INCR)).mp4 | ffmpeg -i /tmp/my/source.mp4 -ss ${i} -t $((i+INCR)) /tmp/my/slice_${i}_$((i+INCR)).mp4 | ||||
done | done | ||||
``` | ``` | ||||
With JVCL, you'd write this spec file: | |||||
With JVCL, you'd write this spec file and save it to a file (for example `my-spec.jvcl`): | |||||
```json | ```json | ||||
{ | { | ||||
"assets": [ {"name": "src", "path": "/tmp/my/source.mp4"} ], | "assets": [ {"name": "src", "path": "/tmp/my/source.mp4"} ], | ||||
@@ -31,7 +31,7 @@ With JVCL, you'd write this spec file: | |||||
``` | ``` | ||||
and then run it like this: | and then run it like this: | ||||
```shell script | ```shell script | ||||
jvcl my-spec.json | |||||
jvcl my-spec.jvcl | |||||
``` | ``` | ||||
Yes, the JVCL is longer, but I think many would agree it is easier to read and maintain. | Yes, the JVCL is longer, but I think many would agree it is easier to read and maintain. | ||||
@@ -67,16 +67,40 @@ Unlike most JSON, comments *are* allowed in JVCL spec files: | |||||
* A line comment starts with `//` and continue to the end of the line | * A line comment starts with `//` and continue to the end of the line | ||||
* A multi-line block syntax starts with `/*` and ends with `*/` | * A multi-line block syntax starts with `/*` and ends with `*/` | ||||
### Executing a JVCL Spec | |||||
To execute a spec stored in the file `my-spec.json`, you would run: | |||||
```shell script | |||||
jvcl my-spec.jvcl | |||||
``` | |||||
#### Scratch Directory | |||||
Output assets will be placed in the scratch directory, unless otherwise specified | |||||
in the spec file. By default, JVCL will create a new temporary directory to use as the scratch | |||||
directory. You can set the scratch directory explicitly using the `-t` or `--temp-dir` option: | |||||
```shell script | |||||
jvcl -t /some/tempdir my-spec.json | |||||
``` | |||||
#### Command Help | |||||
To view a list of all `jvcl` command-line options, run `jvcl -h` or `jvcl --help` | |||||
## Assets | ## Assets | ||||
Assets are the inputs: generally image, audio and video files. Assets have a name and a path. | |||||
Assets are your media files: generally image, audio and video files. | |||||
The path can be a file or a URL. | |||||
All assets have a name and a path. | |||||
Input assets are defined using the `assets` array of a JVCL spec. | Input assets are defined using the `assets` array of a JVCL spec. | ||||
Operations produce one or more assets, as specified in the `creates` property of | |||||
For input assets, the path can be a file or a URL. URL-based assets will be downloaded | |||||
to the scratch directory. This can be overridden using the `dest` property on the asset. | |||||
Operations produce one or more output assets, as specified in the `creates` property of | |||||
an operation JSON object. | an operation JSON object. | ||||
For output assets, the path will be within the scratch directory. | |||||
You can override this using the `dest` property on the `creates` object associated with the operation. | |||||
### Asset Properties | ### Asset Properties | ||||
Assets expose properties that can be referenced in operations. The properties currently exposed are: | Assets expose properties that can be referenced in operations. The properties currently exposed are: | ||||
@@ -5,6 +5,7 @@ import lombok.Getter; | |||||
import lombok.Setter; | import lombok.Setter; | ||||
import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||
import org.cobbzilla.util.main.BaseMainOptions; | import org.cobbzilla.util.main.BaseMainOptions; | ||||
import org.kohsuke.args4j.Argument; | |||||
import org.kohsuke.args4j.Option; | import org.kohsuke.args4j.Option; | ||||
import java.io.File; | import java.io.File; | ||||
@@ -19,10 +20,8 @@ import static org.cobbzilla.util.json.JsonUtil.json; | |||||
@Slf4j | @Slf4j | ||||
public class JvclOptions extends BaseMainOptions { | public class JvclOptions extends BaseMainOptions { | ||||
public static final String USAGE_SPEC = "Spec file to run. Default is to read from stdin."; | |||||
public static final String OPT_SPEC = "-f"; | |||||
public static final String LONGOPT_SPEC = "--file"; | |||||
@Option(name=OPT_SPEC, aliases=LONGOPT_SPEC, usage=USAGE_SPEC) | |||||
public static final String USAGE_SPEC = "Spec file to run. Set to '-' to read from stdin."; | |||||
@Argument(usage=USAGE_SPEC, required=true) | |||||
@Getter @Setter private File specFile; | @Getter @Setter private File specFile; | ||||
public JSpec getSpec() { | public JSpec getSpec() { | ||||
@@ -6,21 +6,30 @@ import org.junit.Test; | |||||
import java.io.File; | import java.io.File; | ||||
import static jvcl.main.JvclOptions.LONGOPT_SPEC; | |||||
import static org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace; | |||||
import static org.cobbzilla.util.daemon.ZillaRuntime.shortError; | |||||
import static org.cobbzilla.util.io.FileUtil.abs; | import static org.cobbzilla.util.io.FileUtil.abs; | ||||
import static org.cobbzilla.util.io.StreamUtil.loadResourceAsStream; | import static org.cobbzilla.util.io.StreamUtil.loadResourceAsStream; | ||||
import static org.cobbzilla.util.io.StreamUtil.stream2file; | import static org.cobbzilla.util.io.StreamUtil.stream2file; | ||||
import static org.junit.Assert.fail; | |||||
public class BasicTest { | public class BasicTest { | ||||
@Test public void testSplit () { runSpec("tests/test_split.json"); } | |||||
@Test public void testConcat () { runSpec("tests/test_concat.json"); } | |||||
@Test public void testTrim () { runSpec("tests/test_trim.json"); } | |||||
@Test public void testOverlay() { runSpec("tests/test_overlay.json"); } | |||||
@Test public void testSplitConcatAndTrim () { | |||||
runSpec("tests/test_split.jvcl"); | |||||
runSpec("tests/test_concat.jvcl"); | |||||
runSpec("tests/test_trim.jvcl"); | |||||
} | |||||
// @Test public void test4Overlay() { runSpec("tests/test_overlay.jvcl"); } | |||||
private void runSpec(String specPath) { | private void runSpec(String specPath) { | ||||
@Cleanup("delete") final File specFile = stream2file(loadResourceAsStream(specPath)); | |||||
Jvcl.main(new String[]{LONGOPT_SPEC, abs(specFile)}); | |||||
try { | |||||
@Cleanup("delete") final File specFile = stream2file(loadResourceAsStream(specPath)); | |||||
Jvcl.main(new String[]{abs(specFile)}); | |||||
} catch (Exception e) { | |||||
fail("runSpec("+specPath+") failed: "+shortError(e)+"\n"+getStackTrace(e)); | |||||
} | |||||
} | } | ||||
} | } |
@@ -18,7 +18,6 @@ | |||||
"name": "overlay1", | "name": "overlay1", | ||||
"dest": "src/test/resources/outputs/overlay/" | "dest": "src/test/resources/outputs/overlay/" | ||||
}, | }, | ||||
"perform": { | |||||
"source": "vid1", // main video asset | "source": "vid1", // main video asset | ||||
"overlay": "vid2", // overlay this video on the main video | "overlay": "vid2", // overlay this video on the main video | ||||