ffmprb 0.11.3 → 0.12.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/Dockerfile +18 -0
- data/Gemfile +8 -1
- data/Gemfile.lock +121 -0
- data/README.md +65 -21
- data/TODO.md +0 -0
- data/bin/dev +12 -0
- data/bin/test +13 -0
- data/coverage/assets/0.10.0/application.css +799 -0
- data/coverage/assets/0.10.0/application.js +1707 -0
- data/coverage/assets/0.10.0/colorbox/border.png +0 -0
- data/coverage/assets/0.10.0/colorbox/controls.png +0 -0
- data/coverage/assets/0.10.0/colorbox/loading.gif +0 -0
- data/coverage/assets/0.10.0/colorbox/loading_background.png +0 -0
- data/coverage/assets/0.10.0/favicon_green.png +0 -0
- data/coverage/assets/0.10.0/favicon_red.png +0 -0
- data/coverage/assets/0.10.0/favicon_yellow.png +0 -0
- data/coverage/assets/0.10.0/loading.gif +0 -0
- data/coverage/assets/0.10.0/magnify.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-icons_222222_256x240.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-icons_454545_256x240.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-icons_888888_256x240.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/coverage/assets/0.10.2/application.css +799 -0
- data/coverage/assets/0.10.2/application.js +1707 -0
- data/coverage/assets/0.10.2/colorbox/border.png +0 -0
- data/coverage/assets/0.10.2/colorbox/controls.png +0 -0
- data/coverage/assets/0.10.2/colorbox/loading.gif +0 -0
- data/coverage/assets/0.10.2/colorbox/loading_background.png +0 -0
- data/coverage/assets/0.10.2/favicon_green.png +0 -0
- data/coverage/assets/0.10.2/favicon_red.png +0 -0
- data/coverage/assets/0.10.2/favicon_yellow.png +0 -0
- data/coverage/assets/0.10.2/loading.gif +0 -0
- data/coverage/assets/0.10.2/magnify.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-icons_222222_256x240.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-icons_454545_256x240.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-icons_888888_256x240.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_asc.png +0 -0
- data/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_asc_disabled.png +0 -0
- data/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_both.png +0 -0
- data/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_desc.png +0 -0
- data/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_desc_disabled.png +0 -0
- data/coverage/assets/0.12.2/application.css +1 -0
- data/coverage/assets/0.12.2/application.js +7 -0
- data/coverage/assets/0.12.2/colorbox/border.png +0 -0
- data/coverage/assets/0.12.2/colorbox/controls.png +0 -0
- data/coverage/assets/0.12.2/colorbox/loading.gif +0 -0
- data/coverage/assets/0.12.2/colorbox/loading_background.png +0 -0
- data/coverage/assets/0.12.2/favicon_green.png +0 -0
- data/coverage/assets/0.12.2/favicon_red.png +0 -0
- data/coverage/assets/0.12.2/favicon_yellow.png +0 -0
- data/coverage/assets/0.12.2/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/coverage/assets/0.12.2/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/coverage/assets/0.12.2/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/coverage/assets/0.12.2/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/coverage/assets/0.12.2/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/coverage/assets/0.12.2/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/coverage/assets/0.12.2/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/coverage/assets/0.12.2/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/coverage/assets/0.12.2/images/ui-icons_222222_256x240.png +0 -0
- data/coverage/assets/0.12.2/images/ui-icons_2e83ff_256x240.png +0 -0
- data/coverage/assets/0.12.2/images/ui-icons_454545_256x240.png +0 -0
- data/coverage/assets/0.12.2/images/ui-icons_888888_256x240.png +0 -0
- data/coverage/assets/0.12.2/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/coverage/assets/0.12.2/loading.gif +0 -0
- data/coverage/assets/0.12.2/magnify.png +0 -0
- data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_asc.png +0 -0
- data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_asc_disabled.png +0 -0
- data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_both.png +0 -0
- data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_desc.png +0 -0
- data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_desc_disabled.png +0 -0
- data/coverage/assets/0.12.3/application.css +1 -0
- data/coverage/assets/0.12.3/application.js +7 -0
- data/coverage/assets/0.12.3/colorbox/border.png +0 -0
- data/coverage/assets/0.12.3/colorbox/controls.png +0 -0
- data/coverage/assets/0.12.3/colorbox/loading.gif +0 -0
- data/coverage/assets/0.12.3/colorbox/loading_background.png +0 -0
- data/coverage/assets/0.12.3/favicon_green.png +0 -0
- data/coverage/assets/0.12.3/favicon_red.png +0 -0
- data/coverage/assets/0.12.3/favicon_yellow.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/coverage/assets/0.12.3/images/ui-icons_222222_256x240.png +0 -0
- data/coverage/assets/0.12.3/images/ui-icons_2e83ff_256x240.png +0 -0
- data/coverage/assets/0.12.3/images/ui-icons_454545_256x240.png +0 -0
- data/coverage/assets/0.12.3/images/ui-icons_888888_256x240.png +0 -0
- data/coverage/assets/0.12.3/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/coverage/assets/0.12.3/loading.gif +0 -0
- data/coverage/assets/0.12.3/magnify.png +0 -0
- data/coverage/index.html +47324 -0
- data/exp/EXP +7 -0
- data/exp/av-cut-mp4you60.ffmprb +10 -0
- data/exp/docker-compose.yml +9 -0
- data/exp/gop-cut-cat-you60 +141 -0
- data/exp/present/Dockerfile +13 -0
- data/exp/present/Gemfile +3 -0
- data/exp/present/Gemfile.lock +22 -0
- data/exp/present/bin/up-deps +8 -0
- data/exp/present/docker-compose.yml +21 -0
- data/exp/present/exp/present +10 -0
- data/exp/present/exp/present.rb +37 -0
- data/exp/run +9 -0
- data/exp/stitch2.ffmprb +5 -0
- data/exp/unzip-mp4you60 +58 -0
- data/exp/youtubby/Dockerfile +13 -0
- data/exp/youtubby/Gemfile +7 -0
- data/exp/youtubby/Gemfile.lock +73 -0
- data/exp/youtubby/README.md +21 -0
- data/exp/youtubby/bin/up-deps +8 -0
- data/exp/youtubby/docker-compose.yml +20 -0
- data/exp/youtubby/exp/gop-raw-cut-you-HD60 +13 -0
- data/exp/youtubby/exp/gop-raw-cut-you-HD60.rb +230 -0
- data/exp/youtubby/exp/media-upload +13 -0
- data/exp/youtubby/exp/tmp/CURRENT +2 -0
- data/exp/youtubby/google_youtube.rb +39 -0
- data/exp/youtubby/old-ul.py +181 -0
- data/exp/youtubby/old-ul.rb +87 -0
- data/exp/youtubby/py-Dockerfile +11 -0
- data/exp/youtubby/py-docker-compose.yml +13 -0
- data/exp/youtubby/requirements.txt +19 -0
- data/exp/youtubby/upload.rb +38 -0
- data/exp/zip-cut-mp4you60 +42 -0
- data/exp/zip2mp4k60 +27 -0
- data/ffmprb.gemspec +34 -14
- data/lib/defaults.rb +6 -6
- data/lib/ffmprb/execution.rb +1 -1
- data/lib/ffmprb/file/sample.rb +2 -2
- data/lib/ffmprb/file/threaded_buffered.rb +4 -4
- data/lib/ffmprb/file.rb +20 -20
- data/lib/ffmprb/filter.rb +100 -61
- data/lib/ffmprb/find_silence.rb +5 -2
- data/lib/ffmprb/process/input/chain_base.rb +6 -4
- data/lib/ffmprb/process/input/channeled.rb +1 -1
- data/lib/ffmprb/process/input/cropped.rb +4 -1
- data/lib/ffmprb/process/input/cut.rb +2 -2
- data/lib/ffmprb/process/input/looping.rb +11 -16
- data/lib/ffmprb/process/input/loud.rb +1 -1
- data/lib/ffmprb/process/input/paced.rb +35 -0
- data/lib/ffmprb/process/input/postprocessed.rb +29 -0
- data/lib/ffmprb/process/input/reversed.rb +29 -0
- data/lib/ffmprb/process/input.rb +7 -3
- data/lib/ffmprb/process/output.rb +45 -29
- data/lib/ffmprb/process.rb +8 -9
- data/lib/ffmprb/util/proc_vis.rb +5 -4
- data/lib/ffmprb/util/thread.rb +13 -11
- data/lib/ffmprb/util/threaded_io_buffer.rb +25 -22
- data/lib/ffmprb/util.rb +38 -13
- data/lib/ffmprb/version.rb +2 -2
- data/lib/ffmprb.rb +8 -5
- data/tmp/exp/docker-compose.yml +9 -0
- data/tmp/exp/src/SAM_3132.MP4 +0 -0
- data/tmp/ffmprb-0.11.4.gem +0 -0
- data/tmp/output.rb +383 -0
- metadata +159 -139
- data/.gitignore +0 -10
- data/.rspec +0 -4
- data/.ruby-version +0 -1
- data/.travis.yml +0 -3
- data/circle.yml +0 -7
data/exp/EXP
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
ffmpeg -y -noautorotate -i studio3.mp4 -filter_complex "[0:v] scale=iw*min(640/iw\,480/ih):ih*min(640/iw\,480/ih), setsar=1, pad=640:480:(640-iw*min(640/iw\,480/ih))/2:(480-ih*min(640/iw\,480/ih))/2, setsar=1 [tmpco0rl0:v]; [0:a] anull [tmpco0rl0:a]; color=0x000000@0:d=3:s=640x480:r=30 [blpco0rl0:v]; [tmpco0rl0:v] [blpco0rl0:v] concat=2:v=1:a=0 [pdpco0rl0:v]; [pdpco0rl0:v] trim=1:17, setpts=PTS-STARTPTS [pco0rl0:v]; aevalsrc=0:d=3 [blpco0rl0:a]; [tmpco0rl0:a] [blpco0rl0:a] concat=2:v=0:a=1 [pdpco0rl0:a]; [pdpco0rl0:a] atrim=1:17, asetpts=PTS-STARTPTS [pco0rl0:a]; [pco0rl0:v] setpts=0.25*PTS, minterpolate=mi_mode=mci:mc_mode=aobmc:vsbmc=1 [o0rl0:v]; [pco0rl0:a] atempo=4.0 [o0rl0:a]; [o0rl0:v] concat=1:v=1:a=0 [o0o:v]; [o0rl0:a] concat=1:v=0:a=1 [o0o:a]" -map "[o0o:v]" -map "[o0o:a]" -c:a libmp3lame ex-ex.mp4
|
2
|
+
|
3
|
+
ffmpeg -y -noautorotate -i /tmp/20221228-17-1719wj1.mp4 -filter_complex "[0:v] scale=iw*min(640/iw\,480/ih):ih*min(640/iw\,480/ih), setsar=1, pad=640:480:(640-iw*min(640/iw\,480/ih))/2:(480-ih*min(640/iw\,480/ih))/2, setsar=1 [tmpco0rl0:v]; [0:a] anull [tmpco0rl0:a]; color=0x000000@0:d=3:s=640x480:r=30 [blpco0rl0:v]; [tmpco0rl0:v] [blpco0rl0:v] concat=2:v=1:a=0 [pdpco0rl0:v]; [pdpco0rl0:v] trim=1:7, setpts=PTS-STARTPTS [pco0rl0:v]; aevalsrc=0:d=3 [blpco0rl0:a]; [tmpco0rl0:a] [blpco0rl0:a] concat=2:v=0:a=1 [pdpco0rl0:a]; [pdpco0rl0:a] atrim=1:7, asetpts=PTS-STARTPTS [pco0rl0:a]; [pco0rl0:v] setpts=0.25*PTS, minterpolate=mi_mode=mci:mc_mode=aobmc:vsbmc=1 [o0rl0:v]; [pco0rl0:a] atempo=4.0 [o0rl0:a]; [o0rl0:v] concat=1:v=1:a=0 [o0o:v]; [o0rl0:a] concat=1:v=0:a=1 [o0o:a]" -map "[o0o:v]" -map "[o0o:a]" -c:a libmp3lame ex-ex.mp4
|
4
|
+
|
5
|
+
ffmpeg -y -noautorotate -i /tmp/20221228-17-1719wj1.mp4 -noautorotate -i /tmp/20221228-17-ov4gbl.flv -filter_complex "[0:v] scale=iw*min(320/iw\,200/ih):ih*min(320/iw\,200/ih), setsar=1, pad=320:200:(320-iw*min(320/iw\,200/ih))/2:(200-ih*min(320/iw\,200/ih))/2, setsar=1, fps=fps=16 [tmpco0rl0:v]; [0:a] anull [tmpco0rl0:a]; color=0x000000@0:d=3:s=320x200:r=16 [blpco0rl0:v]; [tmpco0rl0:v] [blpco0rl0:v] concat=2:v=1:a=0 [pdpco0rl0:v]; [pdpco0rl0:v] trim=0:8, setpts=PTS-STARTPTS [pco0rl0:v]; aevalsrc=0:d=3 [blpco0rl0:a]; [tmpco0rl0:a] [blpco0rl0:a] concat=2:v=0:a=1 [pdpco0rl0:a]; [pdpco0rl0:a] atrim=0:8, asetpts=PTS-STARTPTS [pco0rl0:a]; [pco0rl0:v] setpts=0.25*PTS, minterpolate=fps=60:mi_mode=mci:mc_mode=aobmc:vsbmc=1 [o0rl0:v]; [pco0rl0:a] atempo=0.5, atempo=0.5 [o0rl0:a]; [1:v] scale=iw*min(320/iw\,200/ih):ih*min(320/iw\,200/ih), setsar=1, pad=320:200:(320-iw*min(320/iw\,200/ih))/2:(200-ih*min(320/iw\,200/ih))/2, setsar=1, fps=60 [o0rl1:v]; [1:a] anull [o0rl1:a]; [o0rl0:v] [o0rl1:v] concat=2:v=1:a=0 [o0o:v]; [o0rl0:a] [o0rl1:a] concat=2:v=0:a=1 [o0o:a]" -map "[o0o:v]" -map "[o0o:a]" -c:a libmp3lame ex-ex.mp4
|
6
|
+
|
7
|
+
, fps=fps=16
|
@@ -0,0 +1,10 @@
|
|
1
|
+
|*inp_opt|
|
2
|
+
inp, from, to = inp_opt
|
3
|
+
# XXX :: :: ::
|
4
|
+
oup = ::File.join(::File.dirname(inp), "#{::File.basename inp, '.*'}-you.mp4")
|
5
|
+
cut_opt = {}
|
6
|
+
cut_opt[:from] = from.to_i if from
|
7
|
+
cut_opt[:to] = to.to_i if to
|
8
|
+
output oup, video: {resolution: HD_1080p, fps: 60} do
|
9
|
+
roll input(inp).cut cut_opt
|
10
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
gem 'ffmprb'
|
6
|
+
require 'ffmprb'
|
7
|
+
|
8
|
+
YOU_VIDEO_OPT = {resolution: '1920x1080', fps: 60}
|
9
|
+
int_video_opt = {resolution: '3840x2160', fps: 60}
|
10
|
+
|
11
|
+
GOP_MP4_RE = /\b(GX(\d\d)(\d\d\d\d)\.MP4)\b/i
|
12
|
+
GOP_ZIP_URL_RE = %r[/zip/]i
|
13
|
+
|
14
|
+
|
15
|
+
def dura_to_sec(dura_str)
|
16
|
+
dura_str.split(':').reverse.each_with_index.reduce(0) do |sec, (ns, i)|
|
17
|
+
sec + ns.to_i*(60**i)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
out_dir = Dir.pwd
|
23
|
+
av_src_cuts = []
|
24
|
+
|
25
|
+
Dir.mktmpdir do |tmp_dir|
|
26
|
+
Dir.chdir tmp_dir do
|
27
|
+
warn "Working in #{tmp_dir} --"
|
28
|
+
system "df -h #{tmp_dir}"
|
29
|
+
warn "\nEnter lines containing GoP media D/L URLs and cut times:\n\n"
|
30
|
+
|
31
|
+
dl_q = Queue.new
|
32
|
+
fetcher = Thread.new do
|
33
|
+
while (url, name, cuts = dl_q.deq)
|
34
|
+
srcs = []
|
35
|
+
while srcs.empty? # NOTE sometimes (zip) D/L silently fails, see below
|
36
|
+
unless system "curl -so #{name} #{url}"
|
37
|
+
warn "ERROR downloading #{url}"
|
38
|
+
exit 3
|
39
|
+
end
|
40
|
+
if name == 'tmp.zip'
|
41
|
+
zip_lines = `unzip #{name}`.lines
|
42
|
+
if $?.success? # NOTE if the D/L in fact has failed, it'll be retried
|
43
|
+
zip_lines.each do |line|
|
44
|
+
srcs << $1 if
|
45
|
+
line =~ GOP_MP4_RE
|
46
|
+
end
|
47
|
+
end
|
48
|
+
File.delete name
|
49
|
+
else
|
50
|
+
srcs << name
|
51
|
+
end
|
52
|
+
end
|
53
|
+
av_src_cuts << [
|
54
|
+
srcs.sort do |a, b|
|
55
|
+
a_m = GOP_MP4_RE.match(a)
|
56
|
+
b_m = GOP_MP4_RE.match(b)
|
57
|
+
if (fst = a_m[3] <=> b_m[3]) != 0
|
58
|
+
fst
|
59
|
+
else
|
60
|
+
a_m[2] <=> b_m[2]
|
61
|
+
end
|
62
|
+
end,
|
63
|
+
cuts
|
64
|
+
]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
while (url_cut = gets)
|
69
|
+
url, *cut = url_cut.chomp.split(' ')
|
70
|
+
last_cut = -1
|
71
|
+
cuts = cut.map do |ns|
|
72
|
+
dura_to_sec(ns).tap do |curr_cut|
|
73
|
+
unless curr_cut > last_cut
|
74
|
+
warn "ERROR cut times must be ascending (look it up)"
|
75
|
+
exit 1
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
case url
|
80
|
+
when GOP_ZIP_URL_RE
|
81
|
+
dl_q.enq [url, 'tmp.zip', cuts]
|
82
|
+
when GOP_MP4_RE
|
83
|
+
dl_q.enq [url, $1, cuts]
|
84
|
+
else
|
85
|
+
warn "ERROR invalid URL, cannot go on"
|
86
|
+
exit 1
|
87
|
+
end
|
88
|
+
end
|
89
|
+
dl_q.enq nil
|
90
|
+
|
91
|
+
warn "\nFetching those files..."
|
92
|
+
fetcher.join
|
93
|
+
|
94
|
+
warn av_src_cuts.inspect
|
95
|
+
|
96
|
+
if av_src_cuts.empty?
|
97
|
+
warn "ERROR no inputs given"
|
98
|
+
exit 1
|
99
|
+
end
|
100
|
+
|
101
|
+
out_name = av_src_cuts.reduce([]) do |n, sc|
|
102
|
+
n + sc[0].map{ |s| File.basename(s, '.*') }
|
103
|
+
end.join('-')
|
104
|
+
|
105
|
+
out_path = File.join(out_dir, "#{out_name}-you.mp4")
|
106
|
+
warn "\nCut-catting to #{out_path}..."
|
107
|
+
|
108
|
+
pipe_cut_threads = av_src_cuts.map do |srcs, cuts|
|
109
|
+
[
|
110
|
+
(av_pipe = Ffmprb::File.temp_fifo('.flv')),
|
111
|
+
cuts.each_slice(2).map { |from, to| {from: from, to: to} },
|
112
|
+
Thread.new do
|
113
|
+
Ffmprb.process do
|
114
|
+
output av_pipe, video: int_video_opt do
|
115
|
+
srcs.each do |src|
|
116
|
+
roll input src
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
]
|
122
|
+
end
|
123
|
+
|
124
|
+
Ffmprb.process do
|
125
|
+
output out_path, video: YOU_VIDEO_OPT do
|
126
|
+
pipe_cut_threads.each do |av_pipe, cut_opts, _|
|
127
|
+
(cut_opts.empty?? [{}] : cut_opts).each do |cut_opt|
|
128
|
+
roll input(av_pipe).cut cut_opt
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
pipe_cut_threads.each do |av_pipe, _, thr|
|
135
|
+
thr.join
|
136
|
+
File.delete av_pipe
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
warn "\nAll done and clean..."
|
@@ -0,0 +1,13 @@
|
|
1
|
+
FROM debian:bullseye
|
2
|
+
|
3
|
+
RUN bash -ec 'apt update; apt -y install ruby-all-dev=1:2.7+2 ffmpeg=7:4.3.5-0+deb11u1 sox=14.4.2+git20190427-2 build-essential=12.9 libmagickwand-dev=8:6.9.11.60+dfsg-1.3 git=1:2.30.2-1 pkg-config=0.29.2-1 curl=7.74.0-1.3+deb11u3'
|
4
|
+
RUN gem install bundler -v '~> 2.0'
|
5
|
+
|
6
|
+
WORKDIR /present
|
7
|
+
|
8
|
+
COPY Gemfile Gemfile.lock ./
|
9
|
+
RUN bundle install
|
10
|
+
|
11
|
+
COPY . .
|
12
|
+
|
13
|
+
# XXX ENTRYPOINT bundle exec
|
data/exp/present/Gemfile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
GIT
|
2
|
+
remote: http://git/ffmprb/.git
|
3
|
+
revision: cafbbe2c33cb9eca853412f1f96aafbe428abcdc
|
4
|
+
specs:
|
5
|
+
ffmprb (0.11.5)
|
6
|
+
mkfifo (~> 0.1.1)
|
7
|
+
thor (~> 0.19.1)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
mkfifo (0.1.1)
|
13
|
+
thor (0.19.4)
|
14
|
+
|
15
|
+
PLATFORMS
|
16
|
+
ruby
|
17
|
+
|
18
|
+
DEPENDENCIES
|
19
|
+
ffmprb!
|
20
|
+
|
21
|
+
BUNDLED WITH
|
22
|
+
2.1.4
|
@@ -0,0 +1,8 @@
|
|
1
|
+
#!/bin/bash -e
|
2
|
+
test "$#" -ne 0 && echo "Unsupported args: $@" >&2 && exit 145
|
3
|
+
cd "$( dirname "${BASH_SOURCE[0]}" )"/..
|
4
|
+
|
5
|
+
# XXX docker run --rm -v "$( pwd ):/build" -w /build python:3.9 bash -c \
|
6
|
+
# "pip install google-api-python-client && pip freeze > requirements.txt"
|
7
|
+
|
8
|
+
docker run --rm -v "$( pwd ):/build" -w /build --network git_default ruby:2.7 bundle update
|
@@ -0,0 +1,21 @@
|
|
1
|
+
version: '2.1'
|
2
|
+
services:
|
3
|
+
present:
|
4
|
+
build:
|
5
|
+
context: .
|
6
|
+
network: git_default
|
7
|
+
volumes:
|
8
|
+
- .:/present
|
9
|
+
# XXX hardcoded
|
10
|
+
- /mnt/data/comp/drive/data/master/media:/media
|
11
|
+
- creds_fun:/var/fun/creds
|
12
|
+
- ../../tmp:/samp
|
13
|
+
environment:
|
14
|
+
- GOOGLE_CREDENTIAL_STORE=/var/fun/creds/gc/credentials.yml
|
15
|
+
- GOOGLE_CLIENT_ID=$GOOGLE_CLIENT_ID
|
16
|
+
- GOOGLE_CLIENT_SECRET=$GOOGLE_CLIENT_SECRET
|
17
|
+
- MEDIA_DIR=/media/tmp/exp
|
18
|
+
- FFMPRB_DEBUG=$DEBUG
|
19
|
+
entrypoint: ["bundle", "exec", "ruby"]
|
20
|
+
volumes:
|
21
|
+
creds_fun:
|
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/bin/bash -ex
|
2
|
+
cd "$( dirname "${BASH_SOURCE[0]}" )"/..
|
3
|
+
# test "$#" -ne 1 && echo "Usage: XXX" >&2 && exit 145
|
4
|
+
|
5
|
+
COMPOSE_FILE=docker-compose.yml
|
6
|
+
COMPOSE_PROJECT_NAME=present_exp
|
7
|
+
|
8
|
+
docker-compose build --force-rm
|
9
|
+
|
10
|
+
docker-compose run --rm present exp/present.rb
|
@@ -0,0 +1,37 @@
|
|
1
|
+
MEDIA_DIR = ENV['MEDIA_DIR'] or abort "MEDIA_DIR needed"
|
2
|
+
require 'fileutils' # XXX
|
3
|
+
|
4
|
+
require 'ffmprb'
|
5
|
+
Ffmprb::Util::Thread.timeout = 150
|
6
|
+
|
7
|
+
VIDEO_OPT = {resolution: Ffmprb::HD_1080p, fps: 30}
|
8
|
+
|
9
|
+
|
10
|
+
# XXX
|
11
|
+
def dura_to_sec(dura_str)
|
12
|
+
dura_str.split(':').reverse.each_with_index.reduce(0) do |sec, (ns, i)|
|
13
|
+
sec + ns.to_i*(60**i)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
inp_path = File.join(MEDIA_DIR, 'term-samp.mov') # XXX '/samp/term-samp.mov'
|
18
|
+
out_path = nil
|
19
|
+
|
20
|
+
FileUtils.mkdir_p (tmp_dir = File.join(MEDIA_DIR, 'present-tmp'))
|
21
|
+
begin
|
22
|
+
Dir.chdir tmp_dir do
|
23
|
+
warn "Working in #{tmp_dir} --"
|
24
|
+
system "df -h #{tmp_dir}"
|
25
|
+
|
26
|
+
out_path = File.join(MEDIA_DIR, 'present-term.mp4')
|
27
|
+
warn "\nCut-catting to #{out_path}..."
|
28
|
+
|
29
|
+
Ffmprb.process do
|
30
|
+
output out_path, video: VIDEO_OPT do
|
31
|
+
roll input(inp_path).cut to: 60 # XXX .crop(top: 0.35, bottom: 0.15, left: 0.15, right: 0.35).cut from: 10, to: 20
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
ensure
|
36
|
+
FileUtils.rm_r tmp_dir
|
37
|
+
end
|
data/exp/run
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
#!/bin/bash -e
|
2
|
+
cd "$( dirname "${BASH_SOURCE[0]}" )"/..
|
3
|
+
|
4
|
+
export COMPOSE_FILE=exp/docker-compose.yml
|
5
|
+
export COMPOSE_PROJECT_NAME=ffmprb_exp
|
6
|
+
|
7
|
+
docker-compose build --force-rm # XXX --pull
|
8
|
+
|
9
|
+
docker-compose run --rm experimenter bubu.mp4 bubu2.mp4 bubu1.mp4 < exp/stitch2.ffmprb
|
data/exp/stitch2.ffmprb
ADDED
data/exp/unzip-mp4you60
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
gem 'ffmprb'
|
5
|
+
require 'ffmprb'
|
6
|
+
|
7
|
+
YOU_VIDEO_OPT = {resolution: '1920x1080', fps: 60}
|
8
|
+
ZIP_RE = /\.zip$/i
|
9
|
+
GOP_RE = /\bGX(\d\d)(\d\d\d\d)\.mp4$/
|
10
|
+
|
11
|
+
if ARGV.length < 1
|
12
|
+
warn "Usage: zip2mp4k60 <{zip|mp4}-file>..."
|
13
|
+
exit 1
|
14
|
+
end
|
15
|
+
|
16
|
+
# TODO? maybe, just maybe, provide cut_opts by path[from:to]... ...
|
17
|
+
|
18
|
+
zip_paths =
|
19
|
+
ARGV.map{ |inp| File.expand_path inp }
|
20
|
+
out_path =
|
21
|
+
File.join(
|
22
|
+
File.dirname(zip_paths[0]),
|
23
|
+
"#{zip_paths.map{ |zip_path| File.basename zip_path, '.*'}.join '-'}-you.mp4"
|
24
|
+
)
|
25
|
+
|
26
|
+
Dir.mktmpdir do |tmp_dir|
|
27
|
+
Dir.chdir tmp_dir do
|
28
|
+
zip_paths.each do |zip_path|
|
29
|
+
if zip_path =~ ZIP_RE
|
30
|
+
system "unzip '#{zip_path}'"
|
31
|
+
else
|
32
|
+
FileUtils.cp zip_path, '.'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
Ffmprb.process do
|
36
|
+
output out_path, video: YOU_VIDEO_OPT do
|
37
|
+
paths = Dir['*']
|
38
|
+
fail "GoP or don't" unless
|
39
|
+
(gops_count = paths.grep(GOP_RE).size) == paths.size
|
40
|
+
if gops_count == 0
|
41
|
+
paths.sort
|
42
|
+
else
|
43
|
+
paths.sort do |a, b|
|
44
|
+
a_m = GOP_RE.match(a)
|
45
|
+
b_m = GOP_RE.match(b)
|
46
|
+
if (fst = a_m[2] <=> b_m[2]) != 0
|
47
|
+
fst
|
48
|
+
else
|
49
|
+
a_m[1] <=> b_m[1]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end.each do |in_path|
|
53
|
+
roll input in_path
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
FROM debian:bullseye
|
2
|
+
|
3
|
+
RUN bash -ec 'apt update; apt -y install ruby-all-dev=1:2.7+2 ffmpeg=7:4.3.5-0+deb11u1 sox=14.4.2+git20190427-2 build-essential=12.9 libmagickwand-dev=8:6.9.11.60+dfsg-1.3 git=1:2.30.2-1 pkg-config=0.29.2-1 curl=7.74.0-1.3+deb11u3'
|
4
|
+
RUN gem install bundler -v '~> 2.0'
|
5
|
+
|
6
|
+
WORKDIR /youtubby
|
7
|
+
|
8
|
+
COPY Gemfile Gemfile.lock ./
|
9
|
+
RUN bundle install
|
10
|
+
|
11
|
+
COPY . .
|
12
|
+
|
13
|
+
# XXX ENTRYPOINT bundle exec
|
@@ -0,0 +1,73 @@
|
|
1
|
+
GIT
|
2
|
+
remote: http://git/ffmprb/.git
|
3
|
+
revision: 90ab02d748e2f86211b6cfd60cf35741deac318c
|
4
|
+
branch: pacing
|
5
|
+
specs:
|
6
|
+
ffmprb (0.12.1)
|
7
|
+
mkfifo (~> 0.1.1)
|
8
|
+
thor (~> 0.19.1)
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: https://rubygems.org/
|
12
|
+
specs:
|
13
|
+
addressable (2.8.1)
|
14
|
+
public_suffix (>= 2.0.2, < 6.0)
|
15
|
+
byebug (11.1.3)
|
16
|
+
declarative (0.0.20)
|
17
|
+
faraday (2.7.2)
|
18
|
+
faraday-net_http (>= 2.0, < 3.1)
|
19
|
+
ruby2_keywords (>= 0.0.4)
|
20
|
+
faraday-net_http (3.0.2)
|
21
|
+
google-apis-core (0.9.2)
|
22
|
+
addressable (~> 2.5, >= 2.5.1)
|
23
|
+
googleauth (>= 0.16.2, < 2.a)
|
24
|
+
httpclient (>= 2.8.1, < 3.a)
|
25
|
+
mini_mime (~> 1.0)
|
26
|
+
representable (~> 3.0)
|
27
|
+
retriable (>= 2.0, < 4.a)
|
28
|
+
rexml
|
29
|
+
webrick
|
30
|
+
google-apis-youtube_v3 (0.25.0)
|
31
|
+
google-apis-core (>= 0.9.1, < 2.a)
|
32
|
+
googleauth (1.3.0)
|
33
|
+
faraday (>= 0.17.3, < 3.a)
|
34
|
+
jwt (>= 1.4, < 3.0)
|
35
|
+
memoist (~> 0.16)
|
36
|
+
multi_json (~> 1.11)
|
37
|
+
os (>= 0.9, < 2.0)
|
38
|
+
signet (>= 0.16, < 2.a)
|
39
|
+
httpclient (2.8.3)
|
40
|
+
jwt (2.6.0)
|
41
|
+
memoist (0.16.2)
|
42
|
+
mini_mime (1.1.2)
|
43
|
+
mkfifo (0.1.1)
|
44
|
+
multi_json (1.15.0)
|
45
|
+
os (1.1.4)
|
46
|
+
public_suffix (5.0.1)
|
47
|
+
representable (3.2.0)
|
48
|
+
declarative (< 0.1.0)
|
49
|
+
trailblazer-option (>= 0.1.1, < 0.2.0)
|
50
|
+
uber (< 0.2.0)
|
51
|
+
retriable (3.1.2)
|
52
|
+
rexml (3.2.5)
|
53
|
+
ruby2_keywords (0.0.5)
|
54
|
+
signet (0.17.0)
|
55
|
+
addressable (~> 2.8)
|
56
|
+
faraday (>= 0.17.5, < 3.a)
|
57
|
+
jwt (>= 1.5, < 3.0)
|
58
|
+
multi_json (~> 1.10)
|
59
|
+
thor (0.19.4)
|
60
|
+
trailblazer-option (0.1.2)
|
61
|
+
uber (0.1.0)
|
62
|
+
webrick (1.7.0)
|
63
|
+
|
64
|
+
PLATFORMS
|
65
|
+
ruby
|
66
|
+
|
67
|
+
DEPENDENCIES
|
68
|
+
byebug
|
69
|
+
ffmprb!
|
70
|
+
google-apis-youtube_v3
|
71
|
+
|
72
|
+
BUNDLED WITH
|
73
|
+
2.1.4
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Youtubby
|
2
|
+
|
3
|
+
## TODO
|
4
|
+
|
5
|
+
The first goal is to create a (container-based) script --
|
6
|
+
for uploading raw footage -- from GoPro media storage -- to YouTube
|
7
|
+
-- that receives:
|
8
|
+
a. Google/YouTube creds (in source [exp/.google-\*] config files, plus,
|
9
|
+
after a built-in workaround manual auth'n procedure, within a persistent volume)
|
10
|
+
b. the name (id?) of the channel as a single command-line parameter
|
11
|
+
c. the GoPro media URLs (which apparently don't require auth'n creds to work),
|
12
|
+
snatched off the former's web app interface, one per line, complete
|
13
|
+
with an optional space-separated series of cut times
|
14
|
+
(\<URL\> [\<start hh:mm:ss\> \<end hh:mm:ss\>...])
|
15
|
+
-- and produces you-\<GOPs\>-YYYY-MM-DD-HH-MM.mp4 files within the media volume
|
16
|
+
as well as "Topublish uploaded on YYYY-MM-DD-HH-MM" *private* videos
|
17
|
+
on the channel
|
18
|
+
|
19
|
+
The temporary location of the runner script is `exp/gop-raw-cut-you-HD60`
|
20
|
+
and of the containerised script is `exp/gop-raw-cut-you-HD60.rb`;
|
21
|
+
the docker compose project is `youtubby_exp`
|
@@ -0,0 +1,8 @@
|
|
1
|
+
#!/bin/bash -e
|
2
|
+
test "$#" -ne 0 && echo "Unsupported args: $@" >&2 && exit 145
|
3
|
+
cd "$( dirname "${BASH_SOURCE[0]}" )"/..
|
4
|
+
|
5
|
+
# XXX docker run --rm -v "$( pwd ):/build" -w /build python:3.9 bash -c \
|
6
|
+
# "pip install google-api-python-client && pip freeze > requirements.txt"
|
7
|
+
|
8
|
+
docker run --rm -v "$( pwd ):/build" -w /build --network git_default ruby:2.7 bundle update
|
@@ -0,0 +1,20 @@
|
|
1
|
+
version: '2.1'
|
2
|
+
services:
|
3
|
+
youtubby:
|
4
|
+
build:
|
5
|
+
context: .
|
6
|
+
network: git_default
|
7
|
+
volumes:
|
8
|
+
- .:/youtubby
|
9
|
+
# XXX hardcoded
|
10
|
+
- /mnt/data/comp/drive/data/master/media:/media
|
11
|
+
- creds_fun:/var/fun/creds
|
12
|
+
environment:
|
13
|
+
- GOOGLE_CREDENTIAL_STORE=/var/fun/creds/gc/credentials.yml
|
14
|
+
- GOOGLE_CLIENT_ID=$GOOGLE_CLIENT_ID
|
15
|
+
- GOOGLE_CLIENT_SECRET=$GOOGLE_CLIENT_SECRET
|
16
|
+
- MEDIA_DIR=/media/tmp/exp
|
17
|
+
- FFMPRB_DEBUG=$DEBUG
|
18
|
+
entrypoint: ["bundle", "exec", "ruby"]
|
19
|
+
volumes:
|
20
|
+
creds_fun:
|
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/bin/bash -ex
|
2
|
+
cd "$( dirname "${BASH_SOURCE[0]}" )"/..
|
3
|
+
test "$#" -ne 1 && echo "Usage: gop-raw-cut-you-HD60 [CHANNEL] < GOP-URL-CUT-S" >&2 && exit 145
|
4
|
+
|
5
|
+
COMPOSE_FILE=docker-compose.yml
|
6
|
+
COMPOSE_PROJECT_NAME=youtubby_exp
|
7
|
+
|
8
|
+
docker-compose build --force-rm
|
9
|
+
|
10
|
+
export GOOGLE_CLIENT_ID="$(< exp/.google-client-id)"
|
11
|
+
export GOOGLE_CLIENT_SECRET="$(< exp/.google-client-secret)"
|
12
|
+
|
13
|
+
docker-compose run --rm youtubby exp/gop-raw-cut-you-HD60.rb "$1"
|