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: | 272 / 1093 |
Version history: | 44 change(s) |
Referenced in: | [show references] |