channel = ARGV.shift || 'default' abort "USAGE: gop-raw-cut-you-HD60.rb [CHANNEL]" unless ARGV.empty? MEDIA_DIR = ENV['MEDIA_DIR'] or abort "MEDIA_DIR needed" time_s = Time.now.strftime('%y-%m-%d-%H-%M') title = "Topublish uploaded at #{time_s}" require 'cgi' WE_URI = 'http://localhost' require_relative '../google_youtube' youtube = google_youtube(WE_URI, channel) do |auth_url| puts "Open the following URL in your browser and authorize the application." puts "(you'll have to copy the code= URL param when redrircted to #{WE_URI})" puts auth_url puts "Paste the authorization code." $stdin.gets.chomp end require 'fileutils' require 'ffmprb' # Ffmprb.debug = true # XXX Ffmprb::Util::Thread.timeout = 150 int_video_opt = {resolution: Ffmprb::HD_4K, fps: 60} fin_video_opt = {resolution: Ffmprb::HD_1080p, fps: 60, encoder: 'libx264 -crf 31'} # XXX -preset veryslow YOU_VIDEO_OPT = {resolution: Ffmprb::HD_4K, fps: 60, encoder: 'libx264'} GOP_MP4_RE = /\b(GX(\d\d)(\d\d\d\d)\.MP4)\b/i GOP_ZIP_URL_RE = %r[/zip/]i def dura_to_sec(dura_str) dura_str.split(':').reverse.each_with_index.reduce(0) do |sec, (ns, i)| sec + ns.to_i*(60**i) end end def out_path(name) File.join MEDIA_DIR, "#{name}.mp4" end FileUtils.mkdir_p (tmp_dir = File.join(MEDIA_DIR, 'gop-raw-cut-you-tmp')) begin Dir.chdir tmp_dir do warn "Working in #{tmp_dir} --" system "df -h #{tmp_dir}" warn "\nEnter lines containing GoP media D/L URLs and cut times:\n\n" av_src_cuts = [] dl_q = Queue.new shots = [] fetcher = Thread.new do while (url, cuts = dl_q.deq) srcs = [] while srcs.empty? # NOTE sometimes (zip) D/L silently fails, see below name = case CGI.unescape url when GOP_ZIP_URL_RE 'tmp.zip' when GOP_MP4_RE $1 else abort "ERROR invalid URL, cannot go on" end abort "ERROR downloading #{url}" unless system "curl -so #{name} '#{url}'" if name == 'tmp.zip' zip_lines = `unzip -o #{name}`.lines if $?.success? # NOTE if the D/L in fact has failed, it'll be retried zip_lines.each do |line| if line =~ GOP_MP4_RE srcs << $1 shots << $3 end end end File.delete name else srcs << name shots << $3 end end av_src_cuts << [ srcs.sort do |a, b| a_m = GOP_MP4_RE.match(a) b_m = GOP_MP4_RE.match(b) if (fst = a_m[3] <=> b_m[3]) != 0 fst else a_m[2] <=> b_m[2] end end, cuts ] end end while (url_cut = gets) url, *cut = url_cut.chomp.split(' ') next if url.split('#')[0].empty? last_cut = -1 cuts = cut.map do |ns| dura_to_sec(ns).tap do |curr_cut| abort "ERROR cut times must be ascending (look it up)" unless curr_cut > last_cut end end dl_q.enq [url, cuts] end dl_q.enq nil warn "\nFetching those files..." fetcher.join abort "ERROR no inputs given" if av_src_cuts.empty? out_name = "GX-#{shots.uniq.join '-'}-#{time_s}" you_out_path = "_you_#{out_name}.mp4" warn "\nCut-catting to out (#{out_name}) paths + you..." pipe_cut_threads = av_src_cuts.map do |srcs, cuts| [ (av_pipe = Ffmprb::File.temp_fifo('.flv')), cuts.each_slice(2).map { |from, to| {from: from, to: to} }, Thread.new do Ffmprb.process do output av_pipe, video: int_video_opt do srcs.each do |src| roll input src end end end end ] end Ffmprb::File.temp_fifo('.flv') do |av_pipe| thr = Thread.new do Ffmprb.process do inp_cut_opts = pipe_cut_threads.map do |av_pipe, cut_opts, _| (cut_opts.empty?? [{}] : cut_opts).map do |cut_opt| [input(av_pipe), cut_opt] end end.reduce :+ output av_pipe, video: YOU_VIDEO_OPT do inp_cut_opts.each do |inp, cut_opt| roll inp.cut cut_opt end end # [Rational(1)/8, Rational(1)/4, Rational(1)/2, 1, 2, 4, 8].each do |r| # XXX # output out_path("#{out_name}x#{r.to_f}"), video: fin_video_opt do # inp_cut_opts.each do |inp, cut_opt| # roll inp.cut(cut_opt).pace r # end # end # end # XXX # output out_path("#{out_name}x-8"), video: fin_video_opt do # inp_cut_opts.each do |inp, cut_opt| # roll inp.cut(cut_opt).reverse.pace 8 # end # end # output out_path("#{out_name}x8-"), video: fin_video_opt do # inp_cut_opts.each do |inp, cut_opt| # roll inp.cut(cut_opt).pace(8).reverse # end # end end end begin Ffmprb.process do in1 = input(av_pipe) output you_out_path, video: YOU_VIDEO_OPT do roll in1.pace(16).reverse roll in1.pp end end ensure thr.join end end # XXX this is flawed: # the simple threads fail because of oom docker signals (9) # (not because of broken pipes) pipe_cut_threads.each do |av_pipe, _, thr| begin thr.join rescue warn "WARN errors-a-happening: #{$!}" end av_pipe.unlink end metadata = { snippet: { title: title }, status: { privacy_status: 'private' } } video = youtube.insert_video('snippet,status', metadata, upload_source: you_out_path, content_type: 'video/mp4') if video.status.upload_status == 'uploaded' warn "OK" else warn "FAIL" end end ensure FileUtils.rm_r tmp_dir end