Crystalize ruby codebase
This commit is contained in:
parent
f9ad831115
commit
f46e293090
16 changed files with 216 additions and 274 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1,5 @@
|
|||
/bin
|
||||
/lib/*
|
||||
*.mp4
|
||||
*.part
|
||||
/.bundle/
|
||||
|
|
9
Gemfile
9
Gemfile
|
@ -1,9 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
# git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
|
||||
|
||||
# Specify your gem's dependencies in fosdem.gemspec
|
||||
gemspec
|
||||
|
77
Gemfile.lock
77
Gemfile.lock
|
@ -1,77 +0,0 @@
|
|||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
fosdem-recorder (0.1.0)
|
||||
colorize (~> 0.8.1)
|
||||
mechanize (~> 2.7)
|
||||
thor (~> 1.1)
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
ast (2.4.2)
|
||||
colorize (0.8.1)
|
||||
connection_pool (2.2.3)
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
http-cookie (1.0.3)
|
||||
domain_name (~> 0.5)
|
||||
mechanize (2.7.7)
|
||||
domain_name (~> 0.5, >= 0.5.1)
|
||||
http-cookie (~> 1.0)
|
||||
mime-types (>= 1.17.2)
|
||||
net-http-digest_auth (~> 1.1, >= 1.1.1)
|
||||
net-http-persistent (>= 2.5.2)
|
||||
nokogiri (~> 1.6)
|
||||
ntlm-http (~> 0.1, >= 0.1.1)
|
||||
webrick (~> 1.7)
|
||||
webrobots (>= 0.0.9, < 0.2)
|
||||
mime-types (3.3.1)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2020.1104)
|
||||
mini_portile2 (2.5.0)
|
||||
net-http-digest_auth (1.4.1)
|
||||
net-http-persistent (4.0.1)
|
||||
connection_pool (~> 2.2)
|
||||
nokogiri (1.11.1)
|
||||
mini_portile2 (~> 2.5.0)
|
||||
racc (~> 1.4)
|
||||
ntlm-http (0.1.1)
|
||||
parallel (1.20.1)
|
||||
parser (3.0.0.0)
|
||||
ast (~> 2.4.1)
|
||||
racc (1.5.2)
|
||||
rainbow (3.0.0)
|
||||
rake (13.0.3)
|
||||
regexp_parser (2.0.3)
|
||||
rexml (3.2.4)
|
||||
rubocop (1.9.1)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 3.0.0.0)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
regexp_parser (>= 1.8, < 3.0)
|
||||
rexml
|
||||
rubocop-ast (>= 1.2.0, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 1.4.0, < 3.0)
|
||||
rubocop-ast (1.4.1)
|
||||
parser (>= 2.7.1.5)
|
||||
ruby-progressbar (1.11.0)
|
||||
thor (1.1.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.7)
|
||||
unicode-display_width (2.0.0)
|
||||
webrick (1.7.0)
|
||||
webrobots (0.1.2)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
fosdem-recorder!
|
||||
rake (~> 13.0)
|
||||
rubocop (~> 1.7)
|
||||
|
||||
BUNDLED WITH
|
||||
2.2.6
|
10
Makefile
Normal file
10
Makefile
Normal file
|
@ -0,0 +1,10 @@
|
|||
all: build
|
||||
|
||||
build: bin/fosdem-recorder
|
||||
|
||||
bin/fosdem-recorder: $(wildcard src/*.cr)
|
||||
shards build
|
||||
|
||||
clean:
|
||||
rm -f bin/fosdem-recorder
|
||||
|
8
Rakefile
8
Rakefile
|
@ -1,8 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "bundler/gem_tasks"
|
||||
require "rubocop/rake_task"
|
||||
|
||||
RuboCop::RakeTask.new
|
||||
|
||||
task default: :rubocop
|
15
bin/console
15
bin/console
|
@ -1,15 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "bundler/setup"
|
||||
require "fosdem"
|
||||
|
||||
# You can add fixtures and/or initialization code here to make experimenting
|
||||
# with your gem easier. You can also use a different console, if you like.
|
||||
|
||||
# (If you use this, don't forget to add pry to your Gemfile!)
|
||||
# require "pry"
|
||||
# Pry.start
|
||||
|
||||
require "irb"
|
||||
IRB.start(__FILE__)
|
|
@ -1,8 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
set -vx
|
||||
|
||||
bundle install
|
||||
|
||||
# Do any other automated setup that you need to do here
|
|
@ -1,11 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'mechanize'
|
||||
require 'time'
|
||||
require 'colorize'
|
||||
require 'thor'
|
||||
|
||||
require 'fosdem_recorder/cli'
|
||||
|
||||
FosdemRecorder::Cli.start(ARGV)
|
|
@ -1,40 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'lib/fosdem_recorder/version'
|
||||
|
||||
Gem::Specification.new do |spec|
|
||||
spec.name = 'fosdem-recorder'
|
||||
spec.version = FosdemRecorder::VERSION
|
||||
spec.authors = ['Glenn Y. Rolland']
|
||||
spec.email = ['glenux@glenux.net']
|
||||
|
||||
spec.summary = 'Simple FOSDEM conferences recorder'
|
||||
spec.description = 'Simple FOSDEM conferences recorder'
|
||||
spec.homepage = 'https://github.com/glenux/fosdem-recorder'
|
||||
spec.required_ruby_version = Gem::Requirement.new('>= 2.4.0')
|
||||
|
||||
# spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
||||
# spec.metadata["homepage_uri"] = spec.homepage
|
||||
spec.metadata["source_code_uri"] = 'https://github.com/glenux/fosdem-recorder'
|
||||
# spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
|
||||
|
||||
# Specify which files should be added to the gem when it is released.
|
||||
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
||||
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
||||
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
||||
end
|
||||
spec.bindir = 'exe'
|
||||
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
||||
spec.require_paths = ['lib']
|
||||
|
||||
# Uncomment to register a new dependency of your gem
|
||||
spec.add_dependency 'thor', '~> 1.1'
|
||||
spec.add_dependency 'mechanize', '~> 2.7'
|
||||
spec.add_dependency 'colorize', '~> 0.8.1'
|
||||
spec.add_development_dependency 'rake', '~> 13.0'
|
||||
spec.add_development_dependency 'rubocop', '~> 1.7'
|
||||
|
||||
# For more information and examples about making a new gem, checkout our
|
||||
# guide at: https://bundler.io/guides/creating_gem.html
|
||||
end
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Cli part of FosdemRecorder
|
||||
module FosdemRecorder
|
||||
# Fosdem Cli - Download and cut streams video
|
||||
class Cli < Thor
|
||||
desc 'info URL', 'Get information about URL'
|
||||
def info(url)
|
||||
meta = _get_meta(url)
|
||||
puts meta[:title].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
|
||||
|
||||
desc 'download URL', 'Download conference described at URL'
|
||||
def download(url)
|
||||
meta = _get_meta(url)
|
||||
|
||||
localtime = meta[:event_start].localtime
|
||||
timeformat = format(
|
||||
'%<hours>02d:%<minutes>02d %<year>d-%<month>02d-%<day>02d',
|
||||
hours: localtime.hour,
|
||||
minutes: localtime.min,
|
||||
year: localtime.year,
|
||||
month: localtime.month,
|
||||
day: localtime.day
|
||||
)
|
||||
cmd = "echo ffmpeg -i #{meta[:stream_url]} -c copy -t #{meta[:duration_str]} \"#{meta[:title_sane]}.mp4\" | at #{timeformat}"
|
||||
puts "Command: #{cmd}".yellow
|
||||
system cmd
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def _validate_url(url)
|
||||
return if url =~ %r{^https://fosdem.org/\d+/schedule/event/.*}
|
||||
|
||||
warn 'ERROR: not a fosdem stream. URL must start with https://fosdem.org/...'
|
||||
exit 1
|
||||
end
|
||||
|
||||
def _get_meta(url)
|
||||
puts "Loading data from #{url}".yellow
|
||||
mechanize = Mechanize.new
|
||||
page = mechanize.get(url)
|
||||
|
||||
title = page.title
|
||||
title_sane =
|
||||
title
|
||||
.gsub(/[^a-zA-Z0-9]/, '-')
|
||||
.gsub(/--*/, '-')
|
||||
.gsub(/-$/, '')
|
||||
.gsub(/^-/, '')
|
||||
|
||||
play_start_str =
|
||||
page
|
||||
.at('.side-box .icon-play')
|
||||
.parent
|
||||
.at('.value-title')
|
||||
.attr('title')
|
||||
.strip
|
||||
|
||||
play_start = Time.parse(play_start_str)
|
||||
|
||||
play_stop_str =
|
||||
page
|
||||
.at('.side-box .icon-stop')
|
||||
.parent
|
||||
.at('.value-title')
|
||||
.attr('title')
|
||||
.strip
|
||||
|
||||
play_stop = Time.parse(play_stop_str)
|
||||
|
||||
duration = (play_stop - play_start) / 3600
|
||||
duration_h = duration.to_i
|
||||
duration_m = ((duration - duration_h) * 60).to_i
|
||||
duration_str = format('%<hours>02d:%<minutes>02d:00', hours: duration_h, minutes: 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
|
10
shard.lock
Normal file
10
shard.lock
Normal file
|
@ -0,0 +1,10 @@
|
|||
version: 2.0
|
||||
shards:
|
||||
lexbor:
|
||||
git: https://github.com/kostya/lexbor.git
|
||||
version: 3.0.2
|
||||
|
||||
mechanize:
|
||||
git: https://github.com/kanezoh/mechanize.cr.git
|
||||
version: 0.1.0+git.commit.be4d84592176d709df793f14589e6bb6e19a7f2a
|
||||
|
25
shard.yml
Normal file
25
shard.yml
Normal file
|
@ -0,0 +1,25 @@
|
|||
name: fosdem-recorder-cr
|
||||
version: 0.1.0
|
||||
|
||||
authors:
|
||||
- Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
description: |
|
||||
A naïve recorder for FOSDEM streams
|
||||
|
||||
dependencies:
|
||||
mechanize:
|
||||
github: Kanezoh/mechanize.cr
|
||||
commit: be4d845
|
||||
# branch: master
|
||||
# tag: "0.2.0"
|
||||
|
||||
targets:
|
||||
fosdem-recorder:
|
||||
main: src/main.cr
|
||||
|
||||
# development_dependencies:
|
||||
# webmock:
|
||||
# github: manastech/webmock.cr
|
||||
|
||||
license: LGPL-3
|
159
src/cli.cr
Normal file
159
src/cli.cr
Normal file
|
@ -0,0 +1,159 @@
|
|||
|
||||
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
|
10
src/main.cr
Normal file
10
src/main.cr
Normal file
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "mechanize"
|
||||
require "time"
|
||||
require "colorize"
|
||||
|
||||
require "./cli"
|
||||
|
||||
FosdemRecorder::Cli.start(ARGV)
|
Loading…
Reference in a new issue