@@ -4,12 +4,13 @@ | |||
# | |||
# Usage: | |||
# | |||
# jaddsilence in-file out-file [channel-mode] [sampling-rate] | |||
# jaddsilence [-n|--no-exec] in-file out-file [channel-mode] [sampling-rate] | |||
# | |||
# in-file : input video file | |||
# out-file : write output file here | |||
# channel-mode : channel layout, usually 'mono' or 'stereo'. Default is stereo | |||
# sampling-rate : sampling rate, in Hz. Default is 48000 | |||
# -n or --no-exec : if set, do not execute ffmpeg but print what would have run | |||
# in-file : input video file | |||
# out-file : write output file here | |||
# channel-mode : channel layout, usually 'mono' or 'stereo'. Default is stereo | |||
# sampling-rate : sampling rate, in Hz. Default is 48000 | |||
# | |||
SCRIPT="${0}" | |||
SCRIPT_DIR="$(cd "$(dirname "${SCRIPT}")" && pwd)" | |||
@@ -4,10 +4,11 @@ | |||
# | |||
# Usage: | |||
# | |||
# jconcat out-file in-file-1 [in-file-2 ...] | |||
# jconcat [-n|--no-exec] out-file in-file-1 [in-file-2 ...] | |||
# | |||
# out-file : output file | |||
# in-file-N : input files | |||
# -n or --no-exec : if set, do not execute ffmpeg but print what would have run | |||
# out-file : output file | |||
# in-file-N : input files | |||
# | |||
# BEWARE SHELL WILDCARDS! | |||
# Shell wildcards will match files in non-deterministic order, meaning that | |||
@@ -4,19 +4,20 @@ | |||
# | |||
# Usage: | |||
# | |||
# jkenburns in-file out-file duration [zoom] [x] [y] [start] [end] [fps] [width] [height] | |||
# jkenburns [-n|--no-exec] in-file out-file duration [zoom] [x] [y] [start] [end] [fps] [width] [height] | |||
# | |||
# in-file : file to trim | |||
# out-file : write scaled file here | |||
# duration : how long the output video will be | |||
# zoom : zoom factor, default is 1 (no zoom) | |||
# x : zoom focus X point, default is center | |||
# y : zoom focus Y point, default is center | |||
# start : when to start zooming, default is beginning of video | |||
# end : when to end zooming, default is end of video | |||
# fps : frame per second for output video, default is 25 | |||
# width : output width, default is in-file width | |||
# height : output height, default is in-file height | |||
# -n or --no-exec : if set, do not execute ffmpeg but print what would have run | |||
# in-file : file to trim | |||
# out-file : write scaled file here | |||
# duration : how long the output video will be | |||
# zoom : zoom factor, default is 1 (no zoom) | |||
# x : zoom focus X point, default is center | |||
# y : zoom focus Y point, default is center | |||
# start : when to start zooming, default is beginning of video | |||
# end : when to end zooming, default is end of video | |||
# fps : frame per second for output video, default is 25 | |||
# width : output width, default is in-file width | |||
# height : output height, default is in-file height | |||
# | |||
SCRIPT="${0}" | |||
SCRIPT_DIR="$(cd "$(dirname "${SCRIPT}")" && pwd)" | |||
@@ -5,14 +5,15 @@ | |||
# | |||
# Usage: | |||
# | |||
# jletterbox in-file out-file width height [color] | |||
# jletterbox [-n|--no-exec] in-file out-file width height [color] | |||
# | |||
# in-file : file to trim | |||
# out-file : write scaled file here | |||
# width : output width | |||
# height : output height | |||
# color : padding color, can be a hex value (0xff0000 is red), or a color | |||
# name from https://ffmpeg.org/ffmpeg-utils.html#color-syntax | |||
# -n or --no-exec : if set, do not execute ffmpeg but print what would have run | |||
# in-file : file to trim | |||
# out-file : write scaled file here | |||
# width : output width | |||
# height : output height | |||
# color : padding color, can be a hex value (0xff0000 is red), or a | |||
# color name from https://ffmpeg.org/ffmpeg-utils.html#color-syntax | |||
# | |||
SCRIPT="${0}" | |||
SCRIPT_DIR="$(cd "$(dirname "${SCRIPT}")" && pwd)" | |||
@@ -4,13 +4,14 @@ | |||
# | |||
# Usage: | |||
# | |||
# jmergeaudio video-file audio-file out-file [at] | |||
# jmergeaudio [-n|--no-exec] video-file audio-file out-file [at] | |||
# | |||
# video-file : input video file | |||
# audio-file : audio file to insert into video | |||
# out-file : write output file here | |||
# at : when (on the video timeline) to start playing the audio | |||
# If omitted, audio will start when video starts | |||
# -n or --no-exec : if set, do not execute ffmpeg but print what would have run | |||
# video-file : input video file | |||
# audio-file : audio file to insert into video | |||
# out-file : write output file here | |||
# at : when (on the video timeline) to start playing the audio | |||
# If omitted, audio will start when video starts | |||
# | |||
SCRIPT="${0}" | |||
SCRIPT_DIR="$(cd "$(dirname "${SCRIPT}")" && pwd)" | |||
@@ -4,13 +4,14 @@ | |||
# | |||
# Usage: | |||
# | |||
# jrmtrack in-file out-file track-type [track-number] | |||
# jrmtrack [-n|--no-exec] in-file out-file track-type [track-number] | |||
# | |||
# in-file : file to trim | |||
# out-file : write output file here | |||
# track-type : the track type to remove. Usually 'audio' or 'video' | |||
# track-number : the track number to remove. If omitted, all tracks whose | |||
# type matches `track-type` will be removed | |||
# -n or --no-exec : if set, do not execute ffmpeg but print what would have run | |||
# in-file : file to trim | |||
# out-file : write output file here | |||
# track-type : the track type to remove. Usually 'audio' or 'video' | |||
# track-number : the track number to remove. If omitted, all tracks whose | |||
# type matches `track-type` will be removed | |||
# | |||
SCRIPT="${0}" | |||
SCRIPT_DIR="$(cd "$(dirname "${SCRIPT}")" && pwd)" | |||
@@ -4,15 +4,16 @@ | |||
# | |||
# Usage: | |||
# | |||
# jscale in-file out-file factor | |||
# jscale [-n|--no-exec] in-file out-file factor | |||
# or | |||
# jscale in-file out-file width height | |||
# jscale [-n|--no-exec] in-file out-file width height | |||
# | |||
# in-file : file to trim | |||
# out-file : write scaled file here | |||
# factor : scale factor | |||
# width : output width | |||
# height : output height | |||
# -n or --no-exec : if set, do not execute ffmpeg but print what would have run | |||
# in-file : file to trim | |||
# out-file : write scaled file here | |||
# factor : scale factor | |||
# width : output width | |||
# height : output height | |||
# | |||
SCRIPT="${0}" | |||
SCRIPT_DIR="$(cd "$(dirname "${SCRIPT}")" && pwd)" | |||
@@ -4,12 +4,13 @@ | |||
# | |||
# Usage: | |||
# | |||
# jspeed in-file out-file speed-factor [audio-speed] | |||
# jspeed [-n|--no-exec] in-file out-file speed-factor [audio-speed] | |||
# | |||
# in-file : input video file | |||
# out-file : write output file here | |||
# speed-factor : factor=1 is unchanged, factor>1 is faster, factor<1 is slower | |||
# audio-speed : can be: silent (default), unchanged, or match | |||
# -n or --no-exec : if set, do not execute ffmpeg but print what would have run | |||
# in-file : input video file | |||
# out-file : write output file here | |||
# speed-factor : factor=1 is unchanged, factor>1 is faster, factor<1 is slower | |||
# audio-speed : can be: silent (default), unchanged, or match | |||
# | |||
# Note: if audio-speed is match, then speed-factor must be between 0.5 and 100 | |||
# | |||
@@ -4,13 +4,14 @@ | |||
# | |||
# Usage: | |||
# | |||
# jsplit in-file out-dir interval [start] [end] | |||
# jsplit [-n|--no-exec] in-file out-dir interval [start] [end] | |||
# | |||
# in-file : file to trim | |||
# out-dir : write split files to this directory (will be created if it does not exist) | |||
# interval : time duration of output files, in seconds | |||
# start : when to start splitting the in-file. default is 0 (start) | |||
# end : when to stop splitting the in-file. default is to continue until end of file is reached | |||
# -n or --no-exec : if set, do not execute ffmpeg but print what would have run | |||
# in-file : file to trim | |||
# out-dir : write split files to this directory (will be created if it does not exist) | |||
# interval : time duration of output files, in seconds | |||
# start : when to start splitting the in-file. default is 0 (start) | |||
# end : when to stop splitting the in-file. default is to continue until end of file is reached | |||
# | |||
SCRIPT="${0}" | |||
SCRIPT_DIR="$(cd "$(dirname "${SCRIPT}")" && pwd)" | |||
@@ -4,12 +4,13 @@ | |||
# | |||
# Usage: | |||
# | |||
# jtrim in-file out-file [start] [end] | |||
# jtrim [-n|--no-exec] in-file out-file [start] [end] | |||
# | |||
# in-file : file to trim | |||
# out-file : write trimmed file here | |||
# start : seconds to trim from the beginning. if omitted, default value is start of the file | |||
# end : retain the file until this number of seconds. if omitted, default is to end of file | |||
# -n or --no-exec : if set, do not execute ffmpeg but print what would have run | |||
# in-file : file to trim | |||
# out-file : write trimmed file here | |||
# start : seconds to trim from the beginning. if omitted, default value is start of the file | |||
# end : retain the file until this number of seconds. if omitted, default is to end of file | |||
# | |||
SCRIPT="${0}" | |||
SCRIPT_DIR="$(cd "$(dirname "${SCRIPT}")" && pwd)" | |||
@@ -4,14 +4,14 @@ | |||
# | |||
# Usage: | |||
# | |||
# jvc [-t temp-dir] [-n] spec-file | |||
# jvc [-t temp-dir] [-n|--no-exec] spec-file | |||
# | |||
# spec-file : the JVC to run. If omitted, read a spec from stdin | |||
# | |||
# -t temp-dir : where to write generated assets. If omitted, jvc will | |||
# create a new temporary directory | |||
# | |||
# -n : print commands that would have been run, but don't | |||
# -n or --no-exec : print commands that would have been run, but don't | |||
# actually run anything | |||
# | |||
# Note: If the JVC jar does not exist, it will be built from source. | |||
@@ -27,4 +27,4 @@ if [[ -n "${JVC_DEBUG}" ]] ; then | |||
DEBUG="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005" | |||
fi | |||
java -cp "${JVC_JAR}" ${DEBUG} jvc.main.Jvc "${@}" | |||
java -cp "${JVC_JAR}" ${DEBUG} jvc.main.Jvc ${_JVC_NO_EXEC} "${@}" |
@@ -31,10 +31,10 @@ function handle_help_request() { | |||
echo "# Environment Variables | |||
# | |||
# JVC_SCRATCH_DIR : Use this as the scratch directory | |||
# Default is to create a new temp directory | |||
# Default is to create a new temp directory | |||
# | |||
# JVC_NO_EXEC : If set to anything, print the commands that would | |||
# have run but do not execute anything | |||
# have run but do not execute anything | |||
# | |||
" | |||
fi | |||
@@ -42,6 +42,12 @@ function handle_help_request() { | |||
fi | |||
} | |||
function handle_jvc_noexec() { | |||
if [[ -n "${1}" && ("${1}" == "-n" || "${1}" == "--no-exec" ) ]] ; then | |||
echo -n "--no-exec" | |||
fi | |||
} | |||
# Ensure Java is installed and that it is Java 11 | |||
if [[ -z "$(which java)" ]] ; then | |||
die "Java 11 (or higher) not installed (java command not found on PATH)" | |||
@@ -90,17 +96,23 @@ if [[ -z "${JVC_JAR}" ]] ; then | |||
fi | |||
fi | |||
handle_help_request "${0}" "${1}" | |||
SCRATCH_DIR="" | |||
if [[ -n "${JVC_SCRATCH_DIR}" ]] ; then | |||
SCRATCH_DIR="--temp-dir ${JVC_SCRATCH_DIR}" | |||
fi | |||
NO_EXEC="" | |||
if [[ -z "${JVC_SKIP_ENV_VAR_HELP}" ]] ; then | |||
if [[ -n "${JVC_SCRATCH_DIR}" ]] ; then | |||
SCRATCH_DIR="--temp-dir ${JVC_SCRATCH_DIR}" | |||
fi | |||
if [[ -n "${JVC_NO_EXEC}" ]] ; then | |||
NO_EXEC="--no-exec" | |||
fi | |||
if [[ -n "${JVC_NO_EXEC}" ]] ; then | |||
_JVC_NO_EXEC="$(handle_jvc_noexec "${1}")" | |||
if [[ -n "${_JVC_NO_EXEC}" ]] ; then | |||
if [[ -z "${NO_EXEC}" ]] ; then | |||
NO_EXEC="--no-exec" | |||
fi | |||
shift | |||
fi | |||
JVC_OPTIONS="${SCRATCH_DIR} ${NO_EXEC}" | |||
handle_help_request "${0}" "${1}" |
@@ -1,7 +1,29 @@ | |||
package jvc.model; | |||
import java.util.Collection; | |||
import java.util.stream.Collectors; | |||
import static java.util.Collections.emptyList; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.empty; | |||
public interface JsObjectView { | |||
Object toJs(); | |||
static boolean isJsObjectCollection(Object value) { | |||
return (value instanceof Collection) | |||
&& !empty(value) | |||
&& (((Collection) value).iterator().next() instanceof JsObjectView); | |||
} | |||
static <T extends JsObjectView> Collection<T> toJs(Collection<T> values) { | |||
if (empty(values)) { | |||
return emptyList(); | |||
} else { | |||
return values.stream() | |||
.map(v -> (T) v.toJs()) | |||
.collect(Collectors.toList()); | |||
} | |||
} | |||
} |
@@ -3,6 +3,7 @@ package jvc.model.js; | |||
import jvc.model.JAsset; | |||
import jvc.model.info.JMediaInfo; | |||
import jvc.model.info.JTrack; | |||
import lombok.Getter; | |||
import org.cobbzilla.util.collection.ArrayUtil; | |||
import java.math.BigDecimal; | |||
@@ -14,20 +15,23 @@ public class JAssetJs { | |||
public static final JAssetJs[] EMPTY_ASSETS = new JAssetJs[0]; | |||
public final Double duration; | |||
public final Integer width; | |||
public final Integer height; | |||
public final Double aspectRatio; | |||
public final Integer samplingRate; | |||
public JTrackJs[] allTracks = EMPTY_TRACKS; | |||
public JTrackJs[] tracks = EMPTY_TRACKS; | |||
public JTrackJs[] videoTracks = EMPTY_TRACKS; | |||
public JTrackJs[] audioTracks = EMPTY_TRACKS; | |||
public JAssetJs[] assets = EMPTY_ASSETS; | |||
public final boolean hasAudio; | |||
public final boolean hasVideo; | |||
@Getter public final String path; | |||
@Getter public final Double duration; | |||
@Getter public final Integer width; | |||
@Getter public final Integer height; | |||
@Getter public final Double aspectRatio; | |||
@Getter public final Integer samplingRate; | |||
@Getter public JTrackJs[] allTracks = EMPTY_TRACKS; | |||
@Getter public JTrackJs[] tracks = EMPTY_TRACKS; | |||
@Getter public JTrackJs[] videoTracks = EMPTY_TRACKS; | |||
@Getter public JTrackJs[] audioTracks = EMPTY_TRACKS; | |||
@Getter public JAssetJs[] assets = EMPTY_ASSETS; | |||
@Getter public final boolean hasAudio; | |||
@Getter public final boolean hasVideo; | |||
public JAssetJs(JAsset asset) { | |||
this.path = asset.getPath(); | |||
final BigDecimal d = asset.duration(); | |||
this.duration = d == null ? null : d.doubleValue(); | |||
@@ -13,6 +13,7 @@ import java.math.BigDecimal; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import static jvc.service.Toolbox.jsContext; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.die; | |||
import static org.cobbzilla.util.io.FileUtil.abs; | |||
import static org.cobbzilla.util.io.FileUtil.basename; | |||
@@ -24,7 +25,7 @@ public abstract class ExecBase<OP extends JOperation> { | |||
public abstract Map<String, Object> operate(OP operation, Toolbox toolbox, AssetManager assetManager); | |||
protected String renderScript(Toolbox toolbox, Map<String, Object> ctx, String template) { | |||
return HandlebarsUtil.apply(toolbox.getHandlebars(), template, ctx); | |||
return HandlebarsUtil.apply(toolbox.getHandlebars(), template, jsContext(ctx)); | |||
} | |||
protected File resolveOutputPath(JAsset output, File defaultOutfile) { | |||
@@ -14,10 +14,11 @@ import java.util.Map; | |||
public class TrimExec extends SingleOrMultiSourceExecBase<TrimOperation> { | |||
public static final String TRIM_TEMPLATE | |||
= "{{ffmpeg}} -i {{{source.path}}} " + | |||
"-ss {{startSeconds}} " + | |||
"{{#exists interval}}-t {{interval}} {{/exists}}" + | |||
"-y {{{output.path}}}"; | |||
= "{{ffmpeg}} -i {{{source.path}}} " | |||
+ "-ss {{startSeconds}} " | |||
+ "{{#exists interval}}-t {{interval}} {{/exists}}" | |||
+ "{{#if source.hasAudio}}-c:a copy {{/if}}" | |||
+ "-y {{{output.path}}}"; | |||
@Override protected String getProcessTemplate() { return TRIM_TEMPLATE; } | |||
@@ -14,11 +14,13 @@ import org.cobbzilla.util.javascript.StandardJsEngine; | |||
import java.io.File; | |||
import java.math.BigDecimal; | |||
import java.util.Collection; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import java.util.concurrent.ConcurrentHashMap; | |||
import static java.math.RoundingMode.HALF_EVEN; | |||
import static jvc.model.JsObjectView.isJsObjectCollection; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.*; | |||
import static org.cobbzilla.util.io.FileUtil.*; | |||
import static org.cobbzilla.util.json.JsonUtil.*; | |||
@@ -74,6 +76,10 @@ public class Toolbox { | |||
final Object value = entry.getValue(); | |||
if (value instanceof JsObjectView) { | |||
jsCtx.put(entry.getKey(), ((JsObjectView) value).toJs()); | |||
} else if (isJsObjectCollection(value)) { | |||
jsCtx.put(entry.getKey(), JsObjectView.toJs((Collection) value)); | |||
} else { | |||
jsCtx.put(entry.getKey(), value); | |||
} | |||