Libraryless. Click here for Pure Java version (21298L/156K).
1 | !7 |
2 | |
3 | sclass VidFile {
|
4 | File file; |
5 | double length = -1; |
6 | |
7 | transient float[] profile; |
8 | transient L<DoubleRange> ranges; |
9 | |
10 | S id() { ret md5FromFilePathSizeAndDate(file); }
|
11 | File audioFile() { ret prepareCacheProgramFile("preview-" + id() + ".wav"); }
|
12 | } |
13 | |
14 | module AJCBatch > DynObjectTable<VidFile> {
|
15 | S outputFile; |
16 | int volumeThresholdPercent = 15; |
17 | S minSilence = "0.2", leadIn = "0.2", leadOut = "0.2"; |
18 | transient double profileSamplingInterval = 0.05; // 50 ms |
19 | transient int audioWindowSize = iround(profileSamplingInterval*16000); |
20 | S originalLength, cutLength; |
21 | |
22 | transient ReliableSingleThread rstScan = dm_rst(this, r scan); |
23 | |
24 | visualize {
|
25 | JComponent sup = super.visualize(); |
26 | ret centerAndSouthWithMargins( |
27 | jHandleMultiFileDrop(vf<L<File>> onFileDrop, |
28 | withCenteredTitle("Input videos to jump-cut & concatenate (you can drag&drop files here):",
|
29 | withRightAlignedButtons(sup, |
30 | tableDependentButton(table, "Move up", r moveUp), |
31 | tableDependentButton(table, "Move down", r moveDown), |
32 | tableDependentButton(table, "Remove selected", r removeSelected) |
33 | ))), |
34 | vstackWithSpacing( |
35 | centeredLine(withLabel("Total input duration:", dm_boldLabel('originalLength))),
|
36 | dm_ajc_parametersSection(), |
37 | centeredLine(withLabel("Total output duration (est.):", dm_boldLabel('cutLength))),
|
38 | withLabel("Output video:", filePathInputWithBrowseButton(dm_textField('outputFile))),
|
39 | rightAlignedLine(fontSizePlus(3, jbutton("Make video", rThread makeVideo))),
|
40 | ffmpegVersionPanel() |
41 | )); |
42 | } |
43 | |
44 | void onFileDrop(L<File> files) enter {
|
45 | print("Have file drop");
|
46 | Set<File> haveFiles = collectAsSet file(data()); |
47 | addAll(map(listMinusSet(files, haveFiles), f -> nu VidFile(file := f))); |
48 | rstScan.trigger(); |
49 | } |
50 | |
51 | start {
|
52 | itemToMap = func(VidFile v) -> Map {
|
53 | litorderedmap( |
54 | "Video" := fileName(v.file), |
55 | "Folder" := dirPath(v.file), |
56 | "Duration" := !fileExists(v.file) ? "File not found" |
57 | : v.length < 0 ? "[calculating]" : formatMinuteAndSeconds(iceil(v.length)) |
58 | ) |
59 | }; |
60 | rstScan.trigger(); |
61 | dm_watchFields(allNonStaticNonTransientFields(AJCParameters), r recut); |
62 | } |
63 | |
64 | void recut {
|
65 | for (VidFile v : clonedList()) |
66 | v.ranges = null; |
67 | rstScan.trigger(); |
68 | } |
69 | |
70 | void scan {
|
71 | bool change; |
72 | double lenIn = 0, lenOut = 0; |
73 | for (VidFile v : clonedList()) pcall {
|
74 | if (v.ranges == null || v.length < 0 && fileExists(v.file)) {
|
75 | File audio = v.audioFile(); |
76 | if (fileLength(audio) == 0) {
|
77 | print("Extracting audio from " + v.file);
|
78 | ffmpeg_toMonoAudio_16k(v.file, audio); // TODO: temp file for safety? |
79 | v.profile = null; |
80 | } |
81 | if (v.profile == null) |
82 | v.profile = decodeWAVToMonoSamples_floatVolumeProfile(audio, audioWindowSize); |
83 | v.length = l(v.profile)*profileSamplingInterval; |
84 | v.ranges = ajc_findSpeechPartsFromVolumeProfile(v.profile, |
85 | shallowCloneToClass AJCParameters(module())); |
86 | |
87 | set change; |
88 | } |
89 | |
90 | lenIn += v.length; |
91 | lenOut += totalLengthOfDoubleRanges(v.ranges); |
92 | } |
93 | setField(originalLength := formatMinuteAndSeconds(iceil(lenIn)); |
94 | setField(cutLength := formatMinuteAndSeconds(iceil(lenOut)); |
95 | if (change) fireDataChanged(); |
96 | } |
97 | |
98 | void makeVideo {
|
99 | temp dm_tempDisableAllButtons(); |
100 | |
101 | while (rstScan.running()) sleep(10); |
102 | |
103 | if (emptyAfterTrim(outputFile)) ret with infoBox("Need output file path");
|
104 | File out = newFile(trim(outputFile)); |
105 | |
106 | L<VidFile> l = clonedList(); |
107 | if (empty(l)) ret with infoBox("No input files");
|
108 | |
109 | if (fileExists(out) && !confirmOKCancel("Overwrite " + fileName(out) + "?")) ret;
|
110 | |
111 | L<File> files = collect file(l); |
112 | LL<DoubleRange> ranges = collect ranges(l); |
113 | |
114 | temp tempInfoBox_noHide("Splicing video...");
|
115 | backtickToConsole(ffmpegCmd() + " -y " + ffmpeg_argsForSplice_multipleInputVideos(files, out, ranges)); |
116 | |
117 | if (fileExists(out)) |
118 | infoBox("Done splicing video!" + fileInfo(out));
|
119 | else |
120 | infoBox("Something went wrong...");
|
121 | } |
122 | |
123 | void moveUp {
|
124 | L<Int> indices = selectedIndices(); |
125 | int i = first(indices), j = last(indices)+1; |
126 | if (i <= 0) ret; |
127 | VidFile v = data.get(i-1); |
128 | remove(v); |
129 | add(j-1, v); |
130 | new L<Int> sel; |
131 | for (int k = i; k < j; k++) sel.add(k-1); |
132 | selectTableRows(table, asIntArray(sel)); |
133 | } |
134 | |
135 | void moveDown {
|
136 | L<Int> indices = selectedIndices(); |
137 | int i = first(indices), j = last(indices)+1; |
138 | if (j >= l(data)) ret; |
139 | VidFile v = data.get(j); |
140 | remove(v); |
141 | add(i, v); |
142 | new L<Int> sel; |
143 | for (int k = i; k < j; k++) sel.add(k+1); |
144 | selectTableRows(table, asIntArray(sel)); |
145 | } |
146 | } |
download show line numbers debug dex old transpilations
Travelled to 7 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt, xrpafgyirdlv
No comments. add comment
| Snippet ID: | #1024630 |
| Snippet name: | AutoJumpCut Batch Mode (Concatenate All Videos) |
| Eternal ID of this version: | #1024630/45 |
| Text MD5: | c3fc75f21c48259711760d8b14b08d4a |
| Transpilation MD5: | f3e6d307b30ad6c941ade157a29ebd57 |
| Author: | stefan |
| Category: | javax / ajc |
| Type: | JavaX source code (Dynamic Module) |
| Public (visible to everyone): | Yes |
| Archived (hidden from active list): | No |
| Created/modified: | 2020-01-09 22:25:25 |
| Source code size: | 4932 bytes / 146 lines |
| Pitched / IR pitched: | No / No |
| Views / Downloads: | 503 / 1367 |
| Version history: | 44 change(s) |
| Referenced in: | [show references] |