160 lines
4.2 KiB
Crystal
160 lines
4.2 KiB
Crystal
|
|
require "option_parser"
|
|
|
|
# Cli part of FosdemRecorder
|
|
module FosdemRecorder
|
|
# Fosdem Cli - Download and cut streams video
|
|
class Cli
|
|
enum Actions
|
|
Info
|
|
None
|
|
end
|
|
|
|
property action : Actions = Actions::None
|
|
|
|
def initialize(args)
|
|
if args
|
|
parse args
|
|
end
|
|
end
|
|
|
|
def self.start(args)
|
|
Cli.new(args)
|
|
end
|
|
|
|
private def info(url)
|
|
_validate_url(url)
|
|
meta = _get_meta(url)
|
|
puts meta[:title].colorize.fore(:green)
|
|
puts "* event start = #{meta[:event_start]}"
|
|
puts "* event stop = #{meta[:event_stop]}"
|
|
puts "* event length = #{meta[:duration_str]}"
|
|
puts "* stream url = #{meta[:stream_url]}"
|
|
end
|
|
|
|
private def download(url)
|
|
_validate_url(url)
|
|
meta = _get_meta(url)
|
|
|
|
localtime = meta[:event_start].to_local
|
|
timeformat = localtime.to_s("%H:%M %Y-%m-%d")
|
|
cmd = "echo ffmpeg -i #{meta[:stream_url]} -c copy -t #{meta[:duration_str]} \"#{meta[:title_sane]}.mp4\" | at #{timeformat}"
|
|
puts "Command: #{cmd}".colorize.fore(:yellow)
|
|
system cmd
|
|
end
|
|
|
|
private def parse(args)
|
|
commands = [] of Proc(String, Nil)
|
|
|
|
OptionParser.parse(args) do |opts|
|
|
opts.banner = "Usage: fosdem-recorder [subcommand] [arguments]"
|
|
|
|
opts.on("info", "Get information about URL") do
|
|
commands << ->info(String)
|
|
end
|
|
|
|
opts.on("download", "Download conference described at URL") do
|
|
commands << ->download(String)
|
|
end
|
|
|
|
opts.on("-h", "--help", "Shows this help") do
|
|
puts opts
|
|
exit 0
|
|
end
|
|
end
|
|
|
|
commands.each do |proc|
|
|
targs = Tuple(String).from(args)
|
|
proc.call(*targs)
|
|
end
|
|
end
|
|
|
|
private def _validate_url(url)
|
|
return if url =~ %r{^https://fosdem.org/\d+/schedule/event/.*}
|
|
|
|
if url =~ %r{^https://fosdem.org/.*}
|
|
STDERR.puts "ERROR: not a schedule page. URL must contain .../schedule/event/..."
|
|
exit 1
|
|
end
|
|
|
|
STDERR.puts "ERROR: not a fosdem stream. URL must start with https://fosdem.org/..."
|
|
exit 1
|
|
end
|
|
|
|
private def _get_meta(url)
|
|
puts "Loading data from #{url}".colorize.fore(:yellow)
|
|
mechanize = Mechanize.new
|
|
|
|
begin
|
|
page = mechanize.get(url)
|
|
rescue ex : Socket::Addrinfo::Error
|
|
STDERR.puts "ERROR: #{ex.message}"
|
|
exit 1
|
|
end
|
|
|
|
# body_class = page.at('body').attr('class')
|
|
# if body_class != 'schedule-event'
|
|
# STDERR.puts "ERROR: Not an event schedule page!"
|
|
# exit 1
|
|
# end
|
|
# puts body_class
|
|
|
|
title = page.title
|
|
title_sane =
|
|
title
|
|
.gsub(/[^a-zA-Z0-9]/, "-")
|
|
.gsub(/--*/, "-")
|
|
.gsub(/-$/, "")
|
|
.gsub(/^-/, "")
|
|
|
|
play_start_str =
|
|
page
|
|
.css(".side-box .icon-play").first.parent
|
|
.try &.css(".value-title").first["title"].strip
|
|
play_start_str = "" if play_start_str.nil?
|
|
|
|
puts "PLAY_START = #{play_start_str}"
|
|
location = Time::Location.load("Europe/Brussels")
|
|
# play_start = Time.parse(play_start_str, "%H:%S", location)
|
|
play_start = Time.parse_rfc3339(play_start_str) #, location)
|
|
|
|
play_stop_str =
|
|
page
|
|
.css(".side-box .icon-stop").first.parent
|
|
.try &.css(".value-title").first["title"].strip
|
|
play_stop_str = "" if play_stop_str.nil?
|
|
|
|
# play_stop = Time.parse(play_stop_str, "%H:%S", location)
|
|
play_stop = Time.parse_rfc3339(play_stop_str)
|
|
|
|
duration = (play_stop - play_start).to_i / 3600
|
|
duration_h = duration.to_i
|
|
duration_m = ((duration - duration_h) * 60 + 1).to_i
|
|
duration_str = sprintf("%02d:%02d:00", { duration_h, duration_m })
|
|
|
|
stream_page =
|
|
page
|
|
.links
|
|
.select { |link| link.href =~ /live.fosdem.org/ }
|
|
.first
|
|
.href
|
|
|
|
stream_url =
|
|
stream_page
|
|
.gsub(%r{.*watch/}, "https://stream.fosdem.org/")
|
|
.gsub(/$/, ".m3u8")
|
|
|
|
{
|
|
title: title,
|
|
title_sane: title_sane,
|
|
stream_url: stream_url,
|
|
event_start: play_start,
|
|
event_stop: play_stop,
|
|
event_start_str: play_start_str,
|
|
event_stop_str: play_stop_str,
|
|
duration_str: duration_str
|
|
}
|
|
end
|
|
end
|
|
end
|