youtube-dl.rb 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/youtube-dl.rb +21 -8
- data/lib/youtube-dl/options.rb +7 -4
- data/lib/youtube-dl/output.rb +51 -0
- data/lib/youtube-dl/runner.rb +27 -48
- data/lib/youtube-dl/support.rb +21 -1
- data/lib/youtube-dl/version.rb +1 -1
- data/lib/youtube-dl/video.rb +60 -0
- data/test/test_helper.rb +1 -1
- data/test/youtube-dl/options_test.rb +105 -51
- data/test/youtube-dl/output_test.rb +100 -0
- data/test/youtube-dl/runner_test.rb +46 -53
- data/test/youtube-dl/support_test.rb +22 -0
- data/test/youtube-dl/video_test.rb +89 -0
- data/test/youtube-dl_test.rb +84 -0
- data/youtube-dl.rb.gemspec +1 -0
- metadata +24 -4
- data/test/youtube-dl/youtube-dl_test.rb +0 -41
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1c86f76bf2febd54113c84142274c4c496855ebf
|
4
|
+
data.tar.gz: 0da55077bb17767338d0bde1ae75ed1fc1d9e78d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ef750607464a9a53882193569508b45b5f59d9b8c60fa30166bcded52a7a4ef1ae46223849aded5de47cbef64960a4958c78591d15528a46971465ca5d545d7a
|
7
|
+
data.tar.gz: b18889569bf6401ee10bffc6edab536d0f066cd3379cd487b65f2038b743ec1a7509f333e01c492d0dd81791df8c070d53e7b4de9dc9eb1a967d292b8ef0cc2d
|
data/lib/youtube-dl.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
require 'youtube-dl/version'
|
2
2
|
require 'youtube-dl/support'
|
3
3
|
require 'youtube-dl/options'
|
4
|
+
require 'youtube-dl/output'
|
4
5
|
require 'youtube-dl/runner'
|
6
|
+
require 'youtube-dl/video'
|
5
7
|
|
6
8
|
module YoutubeDL
|
7
9
|
extend self
|
@@ -12,22 +14,33 @@ module YoutubeDL
|
|
12
14
|
# @param urls [String, Array] URLs to download
|
13
15
|
# @param options [Hash] Downloader options
|
14
16
|
def download(urls, options={})
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
runner = YoutubeDL::Runner.new(url, YoutubeDL::Options.new(options))
|
20
|
-
runner.run
|
17
|
+
if urls.is_a? Array
|
18
|
+
urls.map { |url| YoutubeDL::Video.get(url, options) }
|
19
|
+
else
|
20
|
+
YoutubeDL::Video.get(urls, options) # Urls should be singular but oh well. url = urls. There. Go cry in a corner.
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
24
|
alias_method :get, :download
|
25
25
|
|
26
|
+
# Lists extractors
|
27
|
+
#
|
28
|
+
# @return [Array] list of extractors
|
26
29
|
def extractors
|
27
|
-
|
30
|
+
@extractors ||= cocaine_line('--list-extractors').run.split("\n")
|
28
31
|
end
|
29
32
|
|
33
|
+
# Returns youtube-dl's version
|
34
|
+
#
|
35
|
+
# @return [String] youtube-dl version
|
30
36
|
def binary_version
|
31
|
-
|
37
|
+
@binary_version ||= cocaine_line('--version').run.strip
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns user agent
|
41
|
+
#
|
42
|
+
# @return [String] user agent
|
43
|
+
def user_agent
|
44
|
+
@user_agent ||= cocaine_line('--dump-user-agent').run.strip
|
32
45
|
end
|
33
46
|
end
|
data/lib/youtube-dl/options.rb
CHANGED
@@ -6,7 +6,11 @@ module YoutubeDL
|
|
6
6
|
#
|
7
7
|
# @param options [Hash] a hash of options
|
8
8
|
def initialize(options={})
|
9
|
-
|
9
|
+
if options.is_a? Hash
|
10
|
+
@store = options
|
11
|
+
else
|
12
|
+
@store = options.to_h
|
13
|
+
end
|
10
14
|
end
|
11
15
|
|
12
16
|
# Returns options as a hash
|
@@ -40,9 +44,8 @@ module YoutubeDL
|
|
40
44
|
# Set options using a block
|
41
45
|
#
|
42
46
|
# @yield [config] self
|
43
|
-
|
44
|
-
|
45
|
-
block.call(self)
|
47
|
+
def configure
|
48
|
+
yield(self) if block_given?
|
46
49
|
end
|
47
50
|
|
48
51
|
# Get option with brackets syntax
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module YoutubeDL
|
2
|
+
# A class of voodoo methods for parsing youtube-dl output
|
3
|
+
class Output < Struct.new(:output)
|
4
|
+
# Takes the output of '--list-formats'
|
5
|
+
#
|
6
|
+
# @return [Array] Array of supported formats
|
7
|
+
def supported_formats
|
8
|
+
# WARNING: This shit won't be documented or even properly tested. It's almost 3 in the morning and I have no idea what I'm doing.
|
9
|
+
header_index = output.index('format code')
|
10
|
+
return nil if header_index.nil?
|
11
|
+
|
12
|
+
formats = []
|
13
|
+
output.slice(header_index..-1).split("\n").each do |line|
|
14
|
+
format = {}
|
15
|
+
format[:format_code], format[:extension], format[:resolution], format[:note] = line.scan(/\A(\d+)\s+(\w+)\s+(\S+)\s(.*)/)[0]
|
16
|
+
formats.push format
|
17
|
+
end
|
18
|
+
formats.shift # The first line is just headers
|
19
|
+
formats.map do |format|
|
20
|
+
format[:note].strip! # Get rid of any trailing whitespace on the note.
|
21
|
+
format[:format_code] = format[:format_code].to_i # convert format code to integer
|
22
|
+
format
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Takes the output of a download
|
27
|
+
#
|
28
|
+
# @return [String] filename saved, nil if no match
|
29
|
+
def filename
|
30
|
+
# Check to see if file was already downloaded
|
31
|
+
if already_downloaded?
|
32
|
+
output.scan(/\[download\]\s(.*)\shas already been downloaded/)[0][0]
|
33
|
+
else
|
34
|
+
if output.include? 'Merging formats into'
|
35
|
+
output.scan(/Merging formats into \"(.*)\"/)[0][0]
|
36
|
+
else
|
37
|
+
output.scan(/\[download\] Destination:\s(.*)$/)[0][0]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
rescue NoMethodError # There wasn't a match somewhere. Kill it with fire
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
|
44
|
+
# Takes the output of a download
|
45
|
+
#
|
46
|
+
# @return [Boolean] Has the file already been downloaded?
|
47
|
+
def already_downloaded?
|
48
|
+
output.include? 'has already been downloaded'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/youtube-dl/runner.rb
CHANGED
@@ -4,18 +4,33 @@ module YoutubeDL
|
|
4
4
|
class Runner
|
5
5
|
include YoutubeDL::Support
|
6
6
|
|
7
|
+
# [String] URL to download
|
7
8
|
attr_accessor :url
|
9
|
+
|
10
|
+
# [YoutubeDL::Options] Options access.
|
8
11
|
attr_accessor :options
|
9
|
-
|
12
|
+
|
13
|
+
# [String] Executable path
|
14
|
+
attr_reader :executable_path
|
15
|
+
|
16
|
+
# [String] Executable name to use
|
17
|
+
attr_accessor :executable
|
10
18
|
|
11
19
|
# Command Line runner initializer
|
12
20
|
#
|
13
21
|
# @param url [String] URL to pass to youtube-dl executable
|
14
22
|
# @param options [Hash, Options] options to pass to the executable. Automatically converted to Options if it isn't already
|
15
|
-
def initialize(url, options=
|
23
|
+
def initialize(url, options = {})
|
16
24
|
@url = url
|
17
|
-
@options = YoutubeDL::Options.new(options
|
18
|
-
@
|
25
|
+
@options = YoutubeDL::Options.new(options)
|
26
|
+
@executable = 'youtube-dl'
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns usable executable path for youtube-dl
|
30
|
+
#
|
31
|
+
# @return [String] usable executable path for youtube-dl
|
32
|
+
def executable_path
|
33
|
+
@executable_path ||= usable_executable_path_for(@executable)
|
19
34
|
end
|
20
35
|
|
21
36
|
# Returns Cocaine's runner engine
|
@@ -28,6 +43,7 @@ module YoutubeDL
|
|
28
43
|
# Sets Cocaine's runner engine
|
29
44
|
#
|
30
45
|
# @param [CommandLineRunner] backend runner class
|
46
|
+
# @return [Object] whatever Cocaine::CommandLine.runner= returns.
|
31
47
|
def backend_runner=(cocaine_runner)
|
32
48
|
Cocaine::CommandLine.runner = cocaine_runner
|
33
49
|
end
|
@@ -41,30 +57,25 @@ module YoutubeDL
|
|
41
57
|
alias_method :command, :to_command
|
42
58
|
|
43
59
|
# Runs the command
|
60
|
+
#
|
61
|
+
# @return [String] the output of youtube-dl
|
44
62
|
def run
|
45
63
|
cocaine_line(options_to_commands).run(@options.store)
|
46
64
|
end
|
47
65
|
alias_method :download, :run
|
48
66
|
|
49
|
-
|
50
|
-
# [{:format_code => '000', :extension => 'avi', :resolution => '320x240', :note => 'More details about the format'}]
|
51
|
-
#
|
52
|
-
# @return [Array] Format list
|
53
|
-
def formats
|
54
|
-
# TODO: Move formats to its own model?
|
55
|
-
parse_format_output(cocaine_line("--list-formats #{quoted(url)}").run)
|
56
|
-
end
|
57
|
-
|
58
|
-
private
|
67
|
+
private
|
59
68
|
|
60
69
|
# Parses options and converts them to Cocaine's syntax
|
61
70
|
#
|
62
71
|
# @return [String] commands ready to do cocaine
|
63
72
|
def options_to_commands
|
64
73
|
commands = []
|
65
|
-
options.sanitize_keys.each_paramized_key do |key, paramized_key|
|
66
|
-
if options[key].to_s == 'true'
|
74
|
+
@options.sanitize_keys.each_paramized_key do |key, paramized_key|
|
75
|
+
if @options[key].to_s == 'true'
|
67
76
|
commands.push "--#{paramized_key}"
|
77
|
+
elsif @options[key].to_s == 'false'
|
78
|
+
commands.push "--no-#{paramized_key}"
|
68
79
|
else
|
69
80
|
commands.push "--#{paramized_key} :#{key}"
|
70
81
|
end
|
@@ -72,37 +83,5 @@ module YoutubeDL
|
|
72
83
|
commands.push quoted(url)
|
73
84
|
commands.join(' ')
|
74
85
|
end
|
75
|
-
|
76
|
-
# Helper to add quotes to beginning and end of a URL.
|
77
|
-
#
|
78
|
-
# @param url [String] Raw URL
|
79
|
-
# @return [String] Quoted URL
|
80
|
-
def quoted(url)
|
81
|
-
"\"#{url}\""
|
82
|
-
end
|
83
|
-
|
84
|
-
# Helper for doing lines of cocaine (initializing, auto executable stuff, etc)
|
85
|
-
#
|
86
|
-
# @param command [String] command switches to run
|
87
|
-
# @return [Cocaine::CommandLine] initialized Cocaine instance
|
88
|
-
def cocaine_line(command)
|
89
|
-
Cocaine::CommandLine.new(@executable_path, command)
|
90
|
-
end
|
91
|
-
|
92
|
-
# Do you like voodoo?
|
93
|
-
#
|
94
|
-
# @param format_output [String] output from youtube-dl --list-formats
|
95
|
-
# @return [Array] Magic.
|
96
|
-
def parse_format_output(format_output)
|
97
|
-
# WARNING: This shit won't be documented or even properly tested. It's almost 3 in the morning and I have no idea what I'm doing.
|
98
|
-
this_shit = []
|
99
|
-
format_output.slice(format_output.index('format code')..-1).split("\n").each do |line|
|
100
|
-
a = {}
|
101
|
-
a[:format_code], a[:extension], a[:resolution], a[:note] = line.scan(/\A(\d+)\s+(\w+)\s+(\S+)\s(.*)/)[0]
|
102
|
-
this_shit.push a
|
103
|
-
end
|
104
|
-
this_shit.shift
|
105
|
-
this_shit.map { |gipo| gipo[:note].strip!; gipo }
|
106
|
-
end
|
107
86
|
end
|
108
87
|
end
|
data/lib/youtube-dl/support.rb
CHANGED
@@ -11,11 +11,31 @@ module YoutubeDL
|
|
11
11
|
if $?.exitstatus == 0 # $? is an object with information on that last command run with backticks.
|
12
12
|
system_path.strip
|
13
13
|
else
|
14
|
-
# TODO: Search vendor bin for executable before just saying it's there.
|
14
|
+
# TODO: Search vendor bin for executable before just saying it's there.
|
15
15
|
vendor_path = File.absolute_path("#{__FILE__}/../../../vendor/bin/#{exe}")
|
16
16
|
File.chmod(775, vendor_path) unless File.executable?(vendor_path) # Make sure vendor binary is executable
|
17
17
|
vendor_path
|
18
18
|
end
|
19
19
|
end
|
20
|
+
|
21
|
+
alias_method :executable_path_for, :usable_executable_path_for
|
22
|
+
|
23
|
+
# Helper for doing lines of cocaine (initializing, auto executable stuff, etc)
|
24
|
+
#
|
25
|
+
# @param command [String] command switches to run
|
26
|
+
# @param executable_path [String] executable to run. Defaults to usable youtube-dl.
|
27
|
+
# @return [Cocaine::CommandLine] initialized Cocaine instance
|
28
|
+
def cocaine_line(command, executable_path = nil)
|
29
|
+
executable_path = executable_path_for('youtube-dl') if executable_path.nil?
|
30
|
+
Cocaine::CommandLine.new(executable_path, command)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Helper to add quotes to beginning and end of a URL or whatever you want....
|
34
|
+
#
|
35
|
+
# @param url [String] Raw URL
|
36
|
+
# @return [String] Quoted URL
|
37
|
+
def quoted(url)
|
38
|
+
"\"#{url}\""
|
39
|
+
end
|
20
40
|
end
|
21
41
|
end
|
data/lib/youtube-dl/version.rb
CHANGED
@@ -0,0 +1,60 @@
|
|
1
|
+
module YoutubeDL
|
2
|
+
# Video model
|
3
|
+
class Video < Runner
|
4
|
+
class << self
|
5
|
+
# Instantiate a new Video model and download the video
|
6
|
+
#
|
7
|
+
# @param url [String] URL to use and download
|
8
|
+
# @param options [Hash] Options to pass in
|
9
|
+
# @return [YoutubeDL::Video] new Video model
|
10
|
+
def download(url, options={})
|
11
|
+
video = new(url, options)
|
12
|
+
video.download
|
13
|
+
video
|
14
|
+
end
|
15
|
+
|
16
|
+
alias_method :get, :download
|
17
|
+
end
|
18
|
+
|
19
|
+
# [YoutubeDL::Options] Download Options for the last download
|
20
|
+
attr_reader :download_options
|
21
|
+
|
22
|
+
# Instantiate new model
|
23
|
+
#
|
24
|
+
# @param url [String] URL to initialize with
|
25
|
+
# @param options [Hash] Options to populate the everything with
|
26
|
+
def initialize(url, options={})
|
27
|
+
@url = url
|
28
|
+
@options = YoutubeDL::Options.new(options)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Download the video.
|
32
|
+
def download
|
33
|
+
@download_options = YoutubeDL::Options.new(runner_options)
|
34
|
+
@last_download_output = YoutubeDL::Runner.new(url, @download_options).run
|
35
|
+
end
|
36
|
+
|
37
|
+
alias_method :get, :download
|
38
|
+
|
39
|
+
# Returns a list of supported formats for the video in the form of
|
40
|
+
# [{:format_code => '000', :extension => 'avi', :resolution => '320x240', :note => 'More details about the format'}]
|
41
|
+
#
|
42
|
+
# @return [Array] Format list
|
43
|
+
def formats
|
44
|
+
@formats ||= YoutubeDL::Output.new(cocaine_line("--list-formats #{quoted(url)}").run).supported_formats
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [String] Filename downloaded to
|
48
|
+
def filename
|
49
|
+
@filename ||= YoutubeDL::Output.new(@last_download_output).filename
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
# Add in other default options here.
|
54
|
+
def runner_options
|
55
|
+
{
|
56
|
+
no_color: true
|
57
|
+
}.merge(@options)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -5,87 +5,141 @@ describe YoutubeDL::Options do
|
|
5
5
|
@options = YoutubeDL::Options.new
|
6
6
|
end
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
describe '#initialize' do
|
9
|
+
it 'should symbolize option keys' do
|
10
|
+
@options.store['key'] = "value"
|
11
|
+
@options.sanitize_keys!
|
12
|
+
assert_equal({key: 'value'}, @options.store)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should accept a parent Options as a param' do
|
16
|
+
parent = YoutubeDL::Options.new(parent_key: 'parent value')
|
17
|
+
child = YoutubeDL::Options.new(parent)
|
18
|
+
assert_equal parent.store, child.store
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should accept a Hash as a param' do
|
22
|
+
hash = {parent_key: 'parent value'}
|
23
|
+
options = YoutubeDL::Options.new(hash)
|
24
|
+
assert_equal hash, options.store
|
25
|
+
end
|
12
26
|
end
|
13
27
|
|
14
|
-
|
15
|
-
|
28
|
+
describe '#to_hash, #to_h' do
|
29
|
+
before do
|
30
|
+
@options.store[:key] = "value"
|
31
|
+
end
|
16
32
|
|
17
|
-
|
33
|
+
it 'should return a hash' do
|
34
|
+
assert_instance_of Hash, @options.to_hash
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should be equal to store' do
|
38
|
+
assert_equal @options.store, @options.to_hash
|
39
|
+
end
|
18
40
|
end
|
19
41
|
|
20
|
-
|
21
|
-
|
42
|
+
describe '#each_paramized' do
|
43
|
+
it 'should properly paramize keys and not values' do
|
44
|
+
@options.some_key = "some value"
|
22
45
|
|
23
|
-
|
46
|
+
@options.each_paramized do |key, value|
|
47
|
+
assert_equal key, 'some-key'
|
48
|
+
assert_equal value, 'some value'
|
49
|
+
end
|
50
|
+
end
|
24
51
|
end
|
25
52
|
|
26
|
-
|
27
|
-
|
28
|
-
|
53
|
+
describe '#each_paramized_key' do
|
54
|
+
it 'should properly paramize keys' do # TODO: Write a better test name
|
55
|
+
@options.some_key = "some value"
|
56
|
+
|
57
|
+
@options.each_paramized_key do |key, paramized_key|
|
58
|
+
assert_equal :some_key, key
|
59
|
+
assert_equal 'some-key', paramized_key
|
60
|
+
end
|
61
|
+
end
|
29
62
|
end
|
30
63
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
64
|
+
describe '#configure' do
|
65
|
+
it 'should be able to use an explicit configuration block' do
|
66
|
+
@options.configure do |c|
|
67
|
+
c.get_operator = true
|
68
|
+
c['get_index'] = true
|
69
|
+
end
|
70
|
+
|
71
|
+
assert @options.store[:get_operator], "Actual: #{@options.store[:get_operator]}"
|
72
|
+
assert @options.store[:get_index], "Actual: #{@options.store[:get_index]}"
|
35
73
|
end
|
36
74
|
|
37
|
-
|
38
|
-
|
75
|
+
it 'should not override parent configuration' do
|
76
|
+
opts = YoutubeDL::Options.new(parent: 'value')
|
77
|
+
opts.configure do |c|
|
78
|
+
c.child = 'vlaue'
|
79
|
+
end
|
80
|
+
|
81
|
+
assert_equal opts.store[:parent], 'value'
|
82
|
+
assert_equal opts.store[:child], 'vlaue'
|
83
|
+
end
|
39
84
|
end
|
40
85
|
|
41
|
-
|
42
|
-
|
43
|
-
|
86
|
+
describe '#[], #[]==' do
|
87
|
+
it 'should be able to use brackets' do
|
88
|
+
@options[:mtn] = :dew
|
89
|
+
assert @options[:mtn] == :dew
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should automatically symbolize keys' do
|
93
|
+
@options.get_operator = true
|
94
|
+
@options['get_index'] = true
|
44
95
|
|
45
|
-
|
46
|
-
|
96
|
+
[:get_operator, :get_index].each do |d|
|
97
|
+
assert @options.store.keys.include?(d), "keys not symbolizing automatically: #{d}"
|
98
|
+
end
|
47
99
|
end
|
48
100
|
end
|
49
101
|
|
50
|
-
|
51
|
-
|
102
|
+
describe '#method_missing' do
|
103
|
+
it 'should be able to set options with method_missing' do
|
104
|
+
@options.test = true
|
52
105
|
|
53
|
-
|
54
|
-
assert_equal key, 'some-key'
|
55
|
-
assert_equal value, 'some value'
|
106
|
+
assert @options.store[:test]
|
56
107
|
end
|
57
|
-
end
|
58
108
|
|
59
|
-
|
60
|
-
|
109
|
+
it 'should be able to retrieve options with method_missing' do
|
110
|
+
@options.store[:walrus] = 'haswalrus'
|
61
111
|
|
62
|
-
|
63
|
-
assert_equal :some_key, key
|
64
|
-
assert_equal 'some-key', paramized_key
|
112
|
+
assert @options.walrus == 'haswalrus'
|
65
113
|
end
|
66
114
|
end
|
67
115
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
116
|
+
describe '#manipulate_keys!' do
|
117
|
+
it 'should manipulate keys' do
|
118
|
+
@options.some_key = 'value'
|
119
|
+
@options.manipulate_keys! do |key|
|
120
|
+
key.to_s.upcase
|
121
|
+
end
|
122
|
+
assert_equal({'SOME_KEY' => 'value'}, @options.store)
|
123
|
+
end
|
72
124
|
end
|
73
125
|
|
74
|
-
|
75
|
-
|
76
|
-
|
126
|
+
describe '#sanitize_keys!' do
|
127
|
+
it 'should convert hyphens to underscores in keys' do # See issue #9
|
128
|
+
@options.store[:"hyphenated-key"] = 'value'
|
129
|
+
@options.sanitize_keys!
|
130
|
+
assert_equal({hyphenated_key: 'value'}, @options.to_h)
|
131
|
+
end
|
77
132
|
end
|
78
133
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
134
|
+
describe '#sanitize_keys' do
|
135
|
+
it 'should not modify the original by calling sanitize_keys without bang' do
|
136
|
+
@options.store['some-key'] = "some_value"
|
137
|
+
refute_equal @options.sanitize_keys, @options
|
138
|
+
end
|
83
139
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
key.to_s.upcase
|
140
|
+
it 'should return instance of Options when calling sanitize_keys' do
|
141
|
+
@options.store['some-key'] = "some_value"
|
142
|
+
assert_instance_of YoutubeDL::Options, @options.sanitize_keys
|
88
143
|
end
|
89
|
-
assert_equal({'SOME_KEY' => 'value'}, @options.store)
|
90
144
|
end
|
91
145
|
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
|
3
|
+
module OutputFactory
|
4
|
+
extend self
|
5
|
+
extend YoutubeDL::Support
|
6
|
+
|
7
|
+
def download
|
8
|
+
remove_downloaded_files # Make sure there isn't a file there
|
9
|
+
@download ||= cocaine_line(quoted(TEST_URL)).run
|
10
|
+
end
|
11
|
+
|
12
|
+
def download_exists
|
13
|
+
@download_exists ||= lambda do
|
14
|
+
cocaine_line(quoted(TEST_URL)).run # two times to make sure it's there.
|
15
|
+
cocaine_line(quoted(TEST_URL)).run
|
16
|
+
end.call
|
17
|
+
end
|
18
|
+
|
19
|
+
def list_formats
|
20
|
+
@list_formats ||= cocaine_line("--list-formats #{quoted(TEST_URL)}").run
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# For debugging
|
25
|
+
# puts
|
26
|
+
# puts " --------------- OutputFactory.download --------------- "
|
27
|
+
# puts OutputFactory.download
|
28
|
+
# puts
|
29
|
+
# puts " --------------- OutputFactory.download_exists --------------- "
|
30
|
+
# puts OutputFactory.download_exists
|
31
|
+
# puts
|
32
|
+
# puts " --------------- OutputFactory.list_formats --------------- "
|
33
|
+
# puts OutputFactory.list_formats
|
34
|
+
# puts
|
35
|
+
|
36
|
+
describe YoutubeDL::Output do
|
37
|
+
describe '#initialize' do
|
38
|
+
it 'should set the output variable' do
|
39
|
+
sample_output = "some sample output\n\n"
|
40
|
+
parser = YoutubeDL::Output.new(sample_output)
|
41
|
+
assert_equal sample_output, parser.output
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '#supported_formats' do
|
46
|
+
before do
|
47
|
+
@parser = YoutubeDL::Output.new(OutputFactory.list_formats)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should find a match given the correct output' do
|
51
|
+
refute_nil @parser.supported_formats
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should find the correct match and in the correct format' do
|
55
|
+
assert_includes(@parser.supported_formats, {format_code: 5, extension: 'flv', resolution: '400x240', note: 'small'})
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should return nil if no match or wrong log format' do
|
59
|
+
bad_parser = YoutubeDL::Output.new(OutputFactory.download)
|
60
|
+
assert_nil bad_parser.supported_formats
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#filename' do
|
65
|
+
before do
|
66
|
+
@parser_download = YoutubeDL::Output.new(OutputFactory.download)
|
67
|
+
@parser_download_exists = YoutubeDL::Output.new(OutputFactory.download_exists)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should find a match given the correct output' do
|
71
|
+
refute_nil @parser_download.filename
|
72
|
+
refute_nil @parser_download_exists.filename
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'should find the correct match' do
|
76
|
+
assert_equal 'nope.avi-gvdf5n-zI14.mp4', @parser_download.filename
|
77
|
+
assert_equal 'nope.avi-gvdf5n-zI14.mp4', @parser_download_exists.filename
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should return nil if no match or wrong log format' do
|
81
|
+
bad_parser = YoutubeDL::Output.new(OutputFactory.list_formats)
|
82
|
+
assert_nil bad_parser.filename
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#already_downloaded?' do
|
87
|
+
before do
|
88
|
+
@parser_download = YoutubeDL::Output.new(OutputFactory.download)
|
89
|
+
@parser_download_exists = YoutubeDL::Output.new(OutputFactory.download_exists)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should return a truthy value if true' do
|
93
|
+
assert @parser_download_exists.already_downloaded?
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'should return a falsy value if false' do
|
97
|
+
refute @parser_download.already_downloaded?
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -9,75 +9,68 @@ describe YoutubeDL::Runner do
|
|
9
9
|
remove_downloaded_files
|
10
10
|
end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
describe '#initialize' do
|
13
|
+
it 'should take options as a hash yet still have configuration blocks work' do
|
14
|
+
r = YoutubeDL::Runner.new(TEST_URL, {some_key: 'some value'})
|
15
|
+
r.options.configure do |c|
|
16
|
+
c.another_key = 'another_value'
|
17
|
+
end
|
18
|
+
|
19
|
+
assert_includes r.to_command, "--some-key"
|
20
|
+
assert_includes r.to_command, "--another-key"
|
21
|
+
end
|
18
22
|
end
|
19
23
|
|
20
|
-
|
21
|
-
|
22
|
-
|
24
|
+
describe '#executable_path' do
|
25
|
+
it 'should set executable path automatically' do
|
26
|
+
assert_match 'youtube-dl', @runner.executable_path
|
27
|
+
end
|
23
28
|
|
24
|
-
|
25
|
-
|
29
|
+
it 'should not have a newline char in the executable_path' do
|
30
|
+
assert_match /youtube-dl\z/, @runner.executable_path
|
31
|
+
end
|
26
32
|
end
|
27
33
|
|
28
|
-
|
29
|
-
|
34
|
+
describe '#backend_runner=, #backend_runner' do
|
35
|
+
it 'should set cocaine runner' do
|
36
|
+
@runner.backend_runner = Cocaine::CommandLine::BackticksRunner.new
|
37
|
+
assert_instance_of Cocaine::CommandLine::BackticksRunner, @runner.backend_runner
|
38
|
+
|
39
|
+
@runner.backend_runner = Cocaine::CommandLine::PopenRunner.new
|
40
|
+
assert_instance_of Cocaine::CommandLine::PopenRunner, @runner.backend_runner
|
41
|
+
end
|
30
42
|
end
|
31
43
|
|
32
|
-
|
33
|
-
|
44
|
+
describe '#to_command' do
|
45
|
+
it 'should parse key-values from options' do
|
46
|
+
@runner.options.some_key = "a value"
|
34
47
|
|
35
|
-
|
36
|
-
|
48
|
+
refute_nil @runner.to_command.match(/--some-key\s.*a value.*/)
|
49
|
+
end
|
37
50
|
|
38
|
-
|
39
|
-
|
51
|
+
it 'should handle true boolean values' do
|
52
|
+
@runner.options.truthy_value = true
|
40
53
|
|
41
|
-
|
42
|
-
|
54
|
+
assert_match /youtube-dl .*--truthy-value\s--|\"http.*/, @runner.to_command
|
55
|
+
end
|
43
56
|
|
44
|
-
|
45
|
-
|
46
|
-
@runner.options.format = TEST_FORMAT
|
47
|
-
@runner.run
|
48
|
-
assert File.exists? TEST_FILENAME
|
49
|
-
end
|
57
|
+
it 'should handle false boolean values' do
|
58
|
+
@runner.options.false_value = false
|
50
59
|
|
51
|
-
|
52
|
-
r = YoutubeDL::Runner.new(TEST_URL, {some_key: 'some value'})
|
53
|
-
r.options.configure do |c|
|
54
|
-
c.another_key = 'another_value'
|
60
|
+
assert_match /youtube-dl .*--no-false-value\s--|\"http.*/, @runner.to_command
|
55
61
|
end
|
56
62
|
|
57
|
-
|
58
|
-
|
63
|
+
it 'should not have newline char in to_command' do
|
64
|
+
assert_match /youtube-dl\s/, @runner.to_command
|
65
|
+
end
|
59
66
|
end
|
60
67
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
assert_includes formats.first, key
|
68
|
-
assert_includes formats.last, key
|
68
|
+
describe '#run' do
|
69
|
+
it 'should run commands' do
|
70
|
+
@runner.options.output = TEST_FILENAME
|
71
|
+
@runner.options.format = TEST_FORMAT
|
72
|
+
@runner.run
|
73
|
+
assert File.exists? TEST_FILENAME
|
69
74
|
end
|
70
75
|
end
|
71
|
-
|
72
|
-
it 'should handle strangely-formatted options correctly' do # See issue #9
|
73
|
-
options = {
|
74
|
-
format: 'bestaudio',
|
75
|
-
:"prefer-ffmpeg" => "true",
|
76
|
-
:"extract-audio" => true,
|
77
|
-
:"audio-format" => "mp3"
|
78
|
-
}
|
79
|
-
|
80
|
-
@runner.options = YoutubeDL::Options.new(options)
|
81
|
-
assert_match /youtube-dl --format 'bestaudio' --prefer-ffmpeg --extract-audio --audio-format 'mp3'/, @runner.to_command
|
82
|
-
end
|
83
76
|
end
|
@@ -33,4 +33,26 @@ describe YoutubeDL::Support do
|
|
33
33
|
assert_match /youtube-dl\z/, @klass.executable_path
|
34
34
|
end
|
35
35
|
end
|
36
|
+
|
37
|
+
describe '#cocaine_line' do
|
38
|
+
it 'should return a Cocaine::CommandLine instance' do
|
39
|
+
assert_instance_of Cocaine::CommandLine, @klass.cocaine_line('')
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should be able to override the executable' do
|
43
|
+
line = @klass.cocaine_line('hello', 'echo')
|
44
|
+
assert_equal "echo hello", line.command
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should default to youtube-dl' do
|
48
|
+
line = @klass.cocaine_line(@klass.quoted(TEST_URL))
|
49
|
+
assert_includes line.command, "youtube-dl \"#{TEST_URL}\""
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '#quoted' do
|
54
|
+
it 'should add quotes' do
|
55
|
+
assert_equal "\"#{TEST_URL}\"", @klass.quoted(TEST_URL)
|
56
|
+
end
|
57
|
+
end
|
36
58
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
|
3
|
+
describe YoutubeDL::Video do
|
4
|
+
before do
|
5
|
+
@video = YoutubeDL::Video.new TEST_URL
|
6
|
+
end
|
7
|
+
|
8
|
+
after do
|
9
|
+
remove_downloaded_files
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '.download' do
|
13
|
+
it 'should download videos without options' do
|
14
|
+
YoutubeDL::Video.download TEST_URL
|
15
|
+
assert_equal 1, Dir.glob(TEST_GLOB).length
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should download videos with options' do
|
19
|
+
YoutubeDL::Video.download TEST_URL, output: TEST_FILENAME, format: TEST_FORMAT
|
20
|
+
assert File.exist? TEST_FILENAME
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should return an instance of YoutubeDL::Video' do
|
24
|
+
video = YoutubeDL::Video.download TEST_URL
|
25
|
+
assert_instance_of YoutubeDL::Video, video
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '.get' do
|
30
|
+
it 'should download videos, exactly like .download' do
|
31
|
+
YoutubeDL::Video.get TEST_URL
|
32
|
+
assert_equal Dir.glob(TEST_GLOB).length, 1
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#initialize' do
|
37
|
+
it 'should return an instance of YoutubeDL::Video' do
|
38
|
+
assert_instance_of YoutubeDL::Video, @video
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should not download anything' do
|
42
|
+
assert_empty Dir.glob(TEST_GLOB)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#download' do
|
47
|
+
it 'should download the file' do
|
48
|
+
assert_equal 0, Dir.glob(TEST_GLOB).length
|
49
|
+
@video.download
|
50
|
+
assert_equal 1, Dir.glob(TEST_GLOB).length
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should set model variables accordingly' do
|
54
|
+
@video.download
|
55
|
+
assert_equal Dir.glob(TEST_GLOB).first, @video.filename
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#formats' do
|
60
|
+
before do
|
61
|
+
@formats = @video.formats
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should be an Array' do
|
65
|
+
assert_instance_of Array, @formats
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should be an Array of Hashes' do
|
69
|
+
assert_instance_of Hash, @formats.first
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should have a hash size of 4' do
|
73
|
+
assert_equal 4, @formats.first.size
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'should include the correct information' do
|
77
|
+
[:format_code, :resolution, :extension, :note].each do |key|
|
78
|
+
assert_includes @formats.first, key
|
79
|
+
assert_includes @formats.last, key
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should not have any whitespace in the notes' do
|
84
|
+
@formats.each do |format|
|
85
|
+
assert_nil format[:note].strip!
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require_relative './test_helper'
|
2
|
+
|
3
|
+
describe YoutubeDL do
|
4
|
+
describe '.download' do
|
5
|
+
after do
|
6
|
+
remove_downloaded_files
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should download videos without options' do
|
10
|
+
YoutubeDL.download TEST_URL
|
11
|
+
assert_equal 1, Dir.glob(TEST_GLOB).length
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should download videos with options' do
|
15
|
+
YoutubeDL.download TEST_URL, output: TEST_FILENAME, format: TEST_FORMAT
|
16
|
+
assert File.exist? TEST_FILENAME
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should download multiple videos without options' do
|
20
|
+
YoutubeDL.download [TEST_URL, TEST_URL2]
|
21
|
+
assert_equal 2, Dir.glob(TEST_GLOB).length
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should download multiple videos with options' do
|
25
|
+
YoutubeDL.download [TEST_URL, TEST_URL2], output: 'test_%(title)s-%(id)s.%(ext)s'
|
26
|
+
assert_equal 2, Dir.glob('test_' + TEST_GLOB).length
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '.get' do
|
31
|
+
after do
|
32
|
+
remove_downloaded_files
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should download videos, exactly like .download' do
|
36
|
+
YoutubeDL.get TEST_URL
|
37
|
+
assert_equal Dir.glob(TEST_GLOB).length, 1
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '.extractors' do
|
42
|
+
before do
|
43
|
+
@extractors = YoutubeDL.extractors
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should return an Array' do
|
47
|
+
assert_instance_of Array, @extractors
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should include the youtube extractors' do
|
51
|
+
['youtube', 'youtube:channel', 'youtube:search', 'youtube:show', 'youtube:user', 'youtube:playlist'].each do |e|
|
52
|
+
assert_includes @extractors, e
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '.binary_version' do
|
58
|
+
before do
|
59
|
+
@version = YoutubeDL.binary_version
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should return a string' do
|
63
|
+
assert_instance_of String, @version
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should be a specific format with no newlines' do
|
67
|
+
assert_match /\d+.\d+.\d+\z/, @version
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '.user_agent' do
|
72
|
+
before do
|
73
|
+
@user_agent = YoutubeDL.user_agent
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'should return a string' do
|
77
|
+
assert_instance_of String, @user_agent
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should be a specific format with no newlines' do
|
81
|
+
assert_match /Mozilla\/5\.0\s.*\)\z/, @user_agent
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/youtube-dl.rb.gemspec
CHANGED
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
|
23
23
|
spec.add_development_dependency "bundler", ">= 1.6"
|
24
24
|
spec.add_development_dependency "pry"
|
25
|
+
spec.add_development_dependency "m"
|
25
26
|
spec.add_development_dependency "rake", "~> 10.0"
|
26
27
|
spec.add_development_dependency "minitest", "~> 5.5.1"
|
27
28
|
spec.add_development_dependency "purdytest"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: youtube-dl.rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- sapslaj
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-09-
|
12
|
+
date: 2015-09-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: cocaine
|
@@ -53,6 +53,20 @@ dependencies:
|
|
53
53
|
- - ">="
|
54
54
|
- !ruby/object:Gem::Version
|
55
55
|
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: m
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
56
70
|
- !ruby/object:Gem::Dependency
|
57
71
|
name: rake
|
58
72
|
requirement: !ruby/object:Gem::Requirement
|
@@ -125,14 +139,18 @@ files:
|
|
125
139
|
- Rakefile
|
126
140
|
- lib/youtube-dl.rb
|
127
141
|
- lib/youtube-dl/options.rb
|
142
|
+
- lib/youtube-dl/output.rb
|
128
143
|
- lib/youtube-dl/runner.rb
|
129
144
|
- lib/youtube-dl/support.rb
|
130
145
|
- lib/youtube-dl/version.rb
|
146
|
+
- lib/youtube-dl/video.rb
|
131
147
|
- test/test_helper.rb
|
132
148
|
- test/youtube-dl/options_test.rb
|
149
|
+
- test/youtube-dl/output_test.rb
|
133
150
|
- test/youtube-dl/runner_test.rb
|
134
151
|
- test/youtube-dl/support_test.rb
|
135
|
-
- test/youtube-dl/
|
152
|
+
- test/youtube-dl/video_test.rb
|
153
|
+
- test/youtube-dl_test.rb
|
136
154
|
- vendor/bin/youtube-dl
|
137
155
|
- vendor/bin/youtube-dl.exe
|
138
156
|
- youtube-dl.rb.gemspec
|
@@ -163,6 +181,8 @@ summary: youtube-dl wrapper for Ruby
|
|
163
181
|
test_files:
|
164
182
|
- test/test_helper.rb
|
165
183
|
- test/youtube-dl/options_test.rb
|
184
|
+
- test/youtube-dl/output_test.rb
|
166
185
|
- test/youtube-dl/runner_test.rb
|
167
186
|
- test/youtube-dl/support_test.rb
|
168
|
-
- test/youtube-dl/
|
187
|
+
- test/youtube-dl/video_test.rb
|
188
|
+
- test/youtube-dl_test.rb
|
@@ -1,41 +0,0 @@
|
|
1
|
-
require_relative '../test_helper'
|
2
|
-
|
3
|
-
describe YoutubeDL do
|
4
|
-
describe '.download' do
|
5
|
-
after do
|
6
|
-
remove_downloaded_files
|
7
|
-
end
|
8
|
-
|
9
|
-
it 'should download videos' do
|
10
|
-
YoutubeDL.get TEST_URL, output: TEST_FILENAME, format: TEST_FORMAT
|
11
|
-
assert File.exist? TEST_FILENAME
|
12
|
-
end
|
13
|
-
|
14
|
-
it 'should download multiple videos' do
|
15
|
-
YoutubeDL.download [TEST_URL, TEST_URL2]
|
16
|
-
assert_equal Dir.glob('nope*').length, 2
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
describe '.extractors' do
|
21
|
-
it 'should return an Array of Strings' do
|
22
|
-
extractors = YoutubeDL.extractors
|
23
|
-
assert_instance_of Array, extractors
|
24
|
-
assert_instance_of String, extractors.first
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
describe '.binary_version' do
|
29
|
-
before do
|
30
|
-
@version = YoutubeDL.binary_version
|
31
|
-
end
|
32
|
-
|
33
|
-
it 'should return a string' do
|
34
|
-
assert_instance_of String, @version
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'should be a specific format with no newlines' do
|
38
|
-
assert_match /\d+.\d+.\d+\z/, @version
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|