@@ -152,3 +152,6 @@ Here is a complex example using multiple assets and operations: | |||||
] | ] | ||||
} | } | ||||
``` | ``` | ||||
## What's up with the name? | |||||
I dunno, a cross between a javelin and an icicle? does that have any positive connotations? ok then... |
@@ -20,6 +20,7 @@ import java.math.BigDecimal; | |||||
import static org.cobbzilla.util.daemon.ZillaRuntime.*; | import static org.cobbzilla.util.daemon.ZillaRuntime.*; | ||||
import static org.cobbzilla.util.http.HttpSchemes.isHttpOrHttps; | import static org.cobbzilla.util.http.HttpSchemes.isHttpOrHttps; | ||||
import static org.cobbzilla.util.io.FileUtil.abs; | import static org.cobbzilla.util.io.FileUtil.abs; | ||||
import static org.cobbzilla.util.io.FileUtil.mkdirOrDie; | |||||
import static org.cobbzilla.util.io.StreamUtil.loadResourceAsStream; | import static org.cobbzilla.util.io.StreamUtil.loadResourceAsStream; | ||||
import static org.cobbzilla.util.json.JsonUtil.json; | import static org.cobbzilla.util.json.JsonUtil.json; | ||||
import static org.cobbzilla.util.reflect.ReflectionUtil.copy; | import static org.cobbzilla.util.reflect.ReflectionUtil.copy; | ||||
@@ -42,6 +43,10 @@ public class JAsset { | |||||
@Getter @Setter private String dest; | @Getter @Setter private String dest; | ||||
public boolean hasDest() { return !empty(dest); } | public boolean hasDest() { return !empty(dest); } | ||||
public boolean destExists() { return new File(dest).exists(); } | public boolean destExists() { return new File(dest).exists(); } | ||||
public boolean destIsDirectory() { return new File(dest).isDirectory(); } | |||||
public File destDirectory() { | |||||
return mkdirOrDie(new File(dest.endsWith("/") ? dest.substring(0, dest.length()-1) : dest)); | |||||
} | |||||
// if path was not a file, it got resolved to a file | // if path was not a file, it got resolved to a file | ||||
// the original value of 'path' is stored here | // the original value of 'path' is stored here | ||||
@@ -19,8 +19,10 @@ import java.util.Map; | |||||
import static jvcl.model.JAsset.json2asset; | import static jvcl.model.JAsset.json2asset; | ||||
import static jvcl.service.Toolbox.getDuration; | 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.daemon.ZillaRuntime.empty; | ||||
import static org.cobbzilla.util.io.FileUtil.abs; | import static org.cobbzilla.util.io.FileUtil.abs; | ||||
import static org.cobbzilla.util.io.FileUtil.mkdirOrDie; | |||||
import static org.cobbzilla.util.json.JsonUtil.json; | import static org.cobbzilla.util.json.JsonUtil.json; | ||||
import static org.cobbzilla.util.system.CommandShell.execScript; | import static org.cobbzilla.util.system.CommandShell.execScript; | ||||
@@ -54,10 +56,27 @@ public class SplitOperation implements JOperator { | |||||
i.compareTo(endTime) < 0; | i.compareTo(endTime) < 0; | ||||
i = i.add(incr)) { | i = i.add(incr)) { | ||||
final File outfile = assetManager.assetPath(op, source, formatType, new Object[]{i, incr}); | |||||
final File outfile; | |||||
if (output.hasDest()) { | |||||
if (!output.destExists()) { | |||||
outfile = sliceFile(output, formatType, i, incr); | |||||
} else { | |||||
if (output.destIsDirectory()) { | |||||
outfile = sliceFile(output, formatType, i, incr); | |||||
} else { | |||||
die("dest exists and is not a directory: "+output.getDest()); | |||||
return; | |||||
} | |||||
} | |||||
} else { | |||||
outfile = assetManager.assetPath(op, source, formatType, new Object[]{i, incr}); | |||||
} | |||||
if (outfile.exists()) { | if (outfile.exists()) { | ||||
log.info("operate: outfile exists, not re-creating: "+abs(outfile)); | log.info("operate: outfile exists, not re-creating: "+abs(outfile)); | ||||
return; | return; | ||||
} else { | |||||
mkdirOrDie(outfile.getParentFile()); | |||||
} | } | ||||
final JAsset slice = new JAsset(output); | final JAsset slice = new JAsset(output); | ||||
slice.setPath(abs(outfile)); | slice.setPath(abs(outfile)); | ||||
@@ -75,6 +94,10 @@ public class SplitOperation implements JOperator { | |||||
log.info("operate: completed"); | log.info("operate: completed"); | ||||
} | } | ||||
private File sliceFile(JAsset output, JFileExtension formatType, BigDecimal i, BigDecimal incr) { | |||||
return new File(output.destDirectory(), output.getName() + "_" + i + "_" + i.add(incr) + formatType.ext()); | |||||
} | |||||
@NoArgsConstructor | @NoArgsConstructor | ||||
private static class SplitConfig { | private static class SplitConfig { | ||||
@@ -11,9 +11,9 @@ 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; | ||||
public class SplitTest { | |||||
public class BasicTest { | |||||
@Test public void testSplit () throws Exception { | |||||
@Test public void testSplitAndConcat () throws Exception { | |||||
@Cleanup("delete") final File specFile = stream2file(loadResourceAsStream("tests/test_split.json")); | @Cleanup("delete") final File specFile = stream2file(loadResourceAsStream("tests/test_split.json")); | ||||
Jvcl.main(new String[]{JvclOptions.LONGOPT_SPEC, abs(specFile)}); | Jvcl.main(new String[]{JvclOptions.LONGOPT_SPEC, abs(specFile)}); | ||||
} | } |
@@ -0,0 +1,98 @@ | |||||
{ | |||||
"media": { | |||||
"@ref": "/Users/jonathan/git/javicle/src/test/resources/outputs/vid1_splits_65_75.mp4", | |||||
"track": [ | |||||
{ | |||||
"@type": "General", | |||||
"VideoCount": "1", | |||||
"AudioCount": "1", | |||||
"FileExtension": "mp4", | |||||
"Format": "MPEG-4", | |||||
"Format_Profile": "Base Media", | |||||
"CodecID": "isom", | |||||
"CodecID_Compatible": "isom/iso2/avc1/mp41", | |||||
"FileSize": "4887637", | |||||
"Duration": "75.022", | |||||
"OverallBitRate_Mode": "VBR", | |||||
"OverallBitRate": "521195", | |||||
"FrameRate": "29.970", | |||||
"FrameCount": "2248", | |||||
"StreamSize": "82479", | |||||
"HeaderSize": "40", | |||||
"DataSize": "4805166", | |||||
"FooterSize": "82431", | |||||
"IsStreamable": "No", | |||||
"Title": "Moonwalk One, ca. 1970 - http://www.archive.org/details/gov.archives.arc.1257628", | |||||
"Movie": "Moonwalk One, ca. 1970 - http://www.archive.org/details/gov.archives.arc.1257628", | |||||
"File_Modified_Date": "UTC 2020-12-12 00:09:41", | |||||
"File_Modified_Date_Local": "2020-12-11 19:09:41", | |||||
"Encoded_Application": "Lavf58.45.100", | |||||
"Comment": "license:http://creativecommons.org/licenses/publicdomain/" | |||||
}, | |||||
{ | |||||
"@type": "Video", | |||||
"StreamOrder": "0", | |||||
"ID": "1", | |||||
"Format": "AVC", | |||||
"Format_Profile": "High", | |||||
"Format_Level": "1.3", | |||||
"Format_Settings_CABAC": "Yes", | |||||
"Format_Settings_RefFrames": "4", | |||||
"CodecID": "avc1", | |||||
"Duration": "75.009", | |||||
"BitRate": "386043", | |||||
"Width": "320", | |||||
"Height": "240", | |||||
"Sampled_Width": "320", | |||||
"Sampled_Height": "240", | |||||
"PixelAspectRatio": "1.000", | |||||
"DisplayAspectRatio": "1.333", | |||||
"Rotation": "0.000", | |||||
"FrameRate_Mode": "CFR", | |||||
"FrameRate_Mode_Original": "VFR", | |||||
"FrameRate": "29.970", | |||||
"FrameCount": "2248", | |||||
"ColorSpace": "YUV", | |||||
"ChromaSubsampling": "4:2:0", | |||||
"BitDepth": "8", | |||||
"ScanType": "Progressive", | |||||
"StreamSize": "3619556", | |||||
"Encoded_Library": "x264 - core 161 r3027 4121277", | |||||
"Encoded_Library_Name": "x264", | |||||
"Encoded_Library_Version": "core 161 r3027 4121277", | |||||
"Encoded_Library_Settings": "cabac=1 / ref=3 / deblock=1:0:0 / analyse=0x3:0x113 / me=hex / subme=7 / psy=1 / psy_rd=1.00:0.00 / mixed_ref=1 / me_range=16 / chroma_me=1 / trellis=1 / 8x8dct=1 / cqm=0 / deadzone=21,11 / fast_pskip=1 / chroma_qp_offset=-2 / threads=7 / lookahead_threads=1 / sliced_threads=0 / nr=0 / decimate=1 / interlaced=0 / bluray_compat=0 / constrained_intra=0 / bframes=3 / b_pyramid=2 / b_adapt=1 / b_bias=0 / direct=1 / weightb=1 / open_gop=0 / weightp=2 / keyint=250 / keyint_min=25 / scenecut=40 / intra_refresh=0 / rc_lookahead=40 / rc=crf / mbtree=1 / crf=23.0 / qcomp=0.60 / qpmin=0 / qpmax=69 / qpstep=4 / ip_ratio=1.40 / aq=1:1.00", | |||||
"extra": { | |||||
"CodecConfigurationBox": "avcC" | |||||
} | |||||
}, | |||||
{ | |||||
"@type": "Audio", | |||||
"StreamOrder": "1", | |||||
"ID": "2", | |||||
"Format": "AAC", | |||||
"Format_Settings_SBR": "No (Explicit)", | |||||
"Format_AdditionalFeatures": "LC", | |||||
"CodecID": "mp4a-40-2", | |||||
"Duration": "75.022", | |||||
"Duration_LastFrame": "-0.008", | |||||
"BitRate_Mode": "VBR", | |||||
"BitRate": "126428", | |||||
"BitRate_Maximum": "128000", | |||||
"Channels": "2", | |||||
"ChannelPositions": "Front: L R", | |||||
"ChannelLayout": "L R", | |||||
"SamplesPerFrame": "1024", | |||||
"SamplingRate": "48000", | |||||
"SamplingCount": "3601056", | |||||
"FrameRate": "46.875", | |||||
"FrameCount": "3517", | |||||
"Compression_Mode": "Lossy", | |||||
"StreamSize": "1185602", | |||||
"StreamSize_Proportion": "0.24257", | |||||
"Default": "Yes", | |||||
"AlternateGroup": "1" | |||||
} | |||||
] | |||||
} | |||||
} | |||||
@@ -0,0 +1,14 @@ | |||||
{ | |||||
"assets": [ | |||||
{ "name": "vid1_splits" } | |||||
], | |||||
"operations": [ | |||||
{ | |||||
"operation": "concat", | |||||
"creates": "combined_vid", | |||||
"perform": { | |||||
"concat": ["vid1_splits[1..{{vid1_splits.length}}"] | |||||
} | |||||
} | |||||
] | |||||
} |
@@ -14,7 +14,10 @@ | |||||
"operations": [ | "operations": [ | ||||
{ | { | ||||
"operation": "split", // name of the operation | "operation": "split", // name of the operation | ||||
"creates": "vid1_splits", // assets it creates, will be an array asset | |||||
"creates": { | |||||
"name": "vid1_splits", | |||||
"dest": "src/test/resources/outputs/" | |||||
}, | |||||
"perform": { | "perform": { | ||||
"split": "vid1", // split this source asset | "split": "vid1", // split this source asset | ||||
"interval": "10s", // split every ten seconds | "interval": "10s", // split every ten seconds | ||||