Warning: session_start(): open(/var/lib/php/sessions/sess_84pv18qafjosco9f320fhb18f3, O_RDWR) failed: No space left on device (28) in /var/www/tb-usercake/models/config.php on line 51
Warning: session_start(): Failed to read session data: files (path: /var/lib/php/sessions) in /var/www/tb-usercake/models/config.php on line 51
!7
cmodule VideoSplicer > DynPrintLog {
S inputFile, outputFile;
S timestamps;
S originalLength, cutLength;
int volumeThresholdPercent = 15;
S minSilence = "0.2", leadIn = "0.2", leadOut = "0.2";
transient double profileSamplingInterval = 0.05; // 50 ms
transient int audioWindowSize = iround(profileSamplingInterval*16000);
transient int timeFieldMinWidth = 40;
transient ImageSurface isPreview;
transient JAmplitudeGraph graph;
transient float[] profile;
visual northAndCenterWithMargins(
vstackWithSpacing(
withLabel("Input video:", filePathInputWithBrowseButton(dm_textField('inputFile), onChoose := rThread loadVideo)),
withLabel("Output video:", filePathInputWithBrowseButton(dm_textField('outputFile))),
centeredLine(
jbutton("Load video", rThread loadVideo),
jbutton("Find jump cuts", rThread autoJumpCuts),
jhspacer(10),
jbutton("Make video", rThread makeVideo)),
jCenteredSection("Jump Cut Parameters", centeredLineWithSpacing(20,
withLabelLeftAndRight("Volume threshold:", dm_spinner volumeThresholdPercent(0, 100), "%"),
withLabelLeftAndRight("Shortest silence to cut:", jMinWidth(timeFieldMinWidth, dm_centeredTextField minSilence()), "s"),
withLabel("Lead-in/out:", jMinWidth(timeFieldMinWidth, dm_centeredTextField leadIn())),
jlabel("/"),
withLabelToTheRight(jMinWidth(timeFieldMinWidth, dm_centeredTextField leadOut()), "s")
))),
jhsplit(0.2,
centerAndSouthWithMargin(
jLiveValueSection(dm_calculatedLiveValue(S, () -> "Timestamps (" + countLines(timestamps) + ")"), dm_textArea('timestamps)),
vstack(
withLabel("In duration (est.):", dm_label('originalLength)),
withLabel("Out duration (est.):", dm_label('cutLength)))
),
northAndCenterWithMargin(
jsection("Audio", jMinHeight(50, graph =
setForeground(Color.red, swingNu(JAmplitudeGraph)))),
hgridWithSpacing(jsection("Preview", jscroll_center(isPreview = jImageSurface())),
super))));
void loadVideo enter {
File f = newFile(inputFile);
if (!fileExists(f)) ret with infoBox("File not found: " + f2s(f));
S id = md5(f2s(f));
File previewFile = prepareCacheProgramFile("preview-" + id + ".jpg");
File audioFile = prepareCacheProgramFile("preview-" + id + ".wav");
if (!fileExists(previewFile)) {
print("Getting preview image...");
ffmpeg_getSingleFrame(f, previewFile, 0.0);
print("Done");
} else print("Have preview image");
isPreview.setImageAndZoomToDisplay(loadImage2(previewFile));
if (!fileExists(audioFile)) {
print("Extracting audio...");
ffmpeg_toMonoAudio_16k(f, audioFile);
print("Done - " + fileInfo(audioFile));
} else print("Have audio");
print("Getting volume profile...");
profile = decodeWAVToMonoSamples_floatVolumeProfile(audioFile, audioWindowSize);
print("Have volume profile (" + nEntries(l(profile)) + ")");
setField(originalLength := formatMinuteAndSeconds(iceil(l(profile)*profileSamplingInterval)));
//printStruct(takeFirstOfFloatArray(100, profile));
graph.setValues(profile);
}
void makeVideo enter {
if (empty(outputFile)) ret with infoBox("Need output file path");
File in = newFile(inputFile), out = newFile(outputFile);
if (empty(timestamps)) autoJumpCuts();
L ranges = parseTimestampRanges(timestamps);
if (empty(ranges)) ret with infoBox("No timestamps");
print(+ranges);
if (fileExists(out) && !confirmOKCancel("Overwrite " + fileName(out) + "?")) ret;
temp tempInfoBox_noHide("Splicing video...");
backtickToConsole(ffmpegCmd() + " -y " + ffmpeg_argsForSplice(in, out, ranges));
if (fileExists(out))
infoBox("Done splicing video!" + fileInfo(out));
else
infoBox("Something went wrong...");
}
void autoJumpCuts enter {
if (profile == null) loadVideo();
if (profile == null) ret;
L ranges = audio_findSpeechPartsFromVolumeProfile(profile, profileSamplingInterval,
volumeThreshold := volumeThresholdPercent/100.0,
minSilenceDuration := parseDouble(minSilence),
pre := parseDouble(leadIn),
post := parseDouble(leadOut));
setField(timestamps := formatTimestampRanges(ranges));
infoBox("Created " + nSlices(ranges) + ", ready to make video");
}
start {
dm_watchField timestamps(r {
pcall-silent {
double len = totalLengthOfDoubleRanges(parseTimestampRanges(timestamps));
setField(cutLength := formatMinuteAndSeconds(iceil(len));
}
});
}
}