ffmprb 0.11.4 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/Dockerfile +11 -5
- data/Gemfile +5 -5
- data/Gemfile.lock +54 -54
- data/README.md +57 -15
- data/TODO.md +0 -1
- data/bin/dev +12 -0
- data/bin/test +9 -3
- 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 +47166 -24254
- 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/lib/defaults.rb +5 -5
- data/lib/ffmprb/execution.rb +1 -1
- data/lib/ffmprb/file/threaded_buffered.rb +1 -1
- data/lib/ffmprb/file.rb +14 -14
- data/lib/ffmprb/filter.rb +96 -60
- 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 +5 -5
- 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 +6 -2
- data/lib/ffmprb/process/output.rb +36 -23
- data/lib/ffmprb/process.rb +3 -6
- data/lib/ffmprb/util/proc_vis.rb +4 -3
- data/lib/ffmprb/util/thread.rb +8 -7
- data/lib/ffmprb/util/threaded_io_buffer.rb +5 -3
- data/lib/ffmprb/util.rb +37 -14
- data/lib/ffmprb/version.rb +1 -1
- data/lib/ffmprb.rb +5 -2
- 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
- metadata +72 -4
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"
|