ytdl 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/youtube_dl.rb +8 -0
- data/lib/youtube_dl/command.rb +49 -0
- data/lib/youtube_dl/listener.rb +17 -0
- data/lib/youtube_dl/output_parser.rb +128 -0
- data/lib/youtube_dl/runner.rb +58 -0
- data/lib/youtube_dl/state.rb +28 -0
- data/lib/youtube_dl/version.rb +3 -0
- data/lib/ytdl.rb +1 -0
- metadata +83 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7ff16ab36991f68eb0ed450552918d2875c1b1d54c995d1dfe489975c3e263a0
|
4
|
+
data.tar.gz: 949f1257581559b86ce257b65f4bf3f7a1e9bd8c3ce678f00c58e43b3e88961e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3bb295a1865bbd86332f0c36e57cd27a13e116af371eb21177c837b765c07a0a9f2c2477028e15c5a77f69fbdfe538399ff48259cbe88a32ac013462068515a5
|
7
|
+
data.tar.gz: 4fb9df0465aed1b3e8a96ffa4a6852dfad4fe3d393a58c04d4d475dd6b9fad24de6c57823732fd24faf349f3a9a61941b94d69d617c295875ccdf1ba81f10b7b
|
data/lib/youtube_dl.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
require 'dry/configurable'
|
3
|
+
|
4
|
+
module YoutubeDL
|
5
|
+
class Command
|
6
|
+
extend Dry::Configurable
|
7
|
+
|
8
|
+
def config
|
9
|
+
self.class.config
|
10
|
+
end
|
11
|
+
|
12
|
+
setting :executable, 'youtube-dl'
|
13
|
+
setting :default_options, {}
|
14
|
+
setting :forced_options, {
|
15
|
+
newline: true,
|
16
|
+
color: false,
|
17
|
+
write_info_json: true,
|
18
|
+
playlist: false,
|
19
|
+
}
|
20
|
+
|
21
|
+
def initialize(url, **options)
|
22
|
+
@url = url
|
23
|
+
@options =
|
24
|
+
config.default_options
|
25
|
+
.merge(options)
|
26
|
+
.merge(config.forced_options)
|
27
|
+
end
|
28
|
+
|
29
|
+
def arguments
|
30
|
+
@options.flat_map do |option, value|
|
31
|
+
option = option.to_s.gsub('_', '-')
|
32
|
+
case value
|
33
|
+
when [] then nil
|
34
|
+
when true then "--#{option}"
|
35
|
+
when false then "--no-#{option}"
|
36
|
+
else ["--#{option}", value]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_a
|
42
|
+
[config.executable] + arguments + [@url]
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
Shellwords.join(to_a)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module YoutubeDL
|
2
|
+
class Listener
|
3
|
+
def initialize
|
4
|
+
@callbacks = Hash.new { |h, k| h[k] = [] }
|
5
|
+
end
|
6
|
+
|
7
|
+
def call(event, state:, line: nil)
|
8
|
+
(@callbacks[event] + @callbacks[:any]).each do |cb|
|
9
|
+
cb.call(state: state, line: line)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def register(event, &block)
|
14
|
+
@callbacks[event] << block
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'filesize'
|
2
|
+
require 'youtube_dl/state'
|
3
|
+
|
4
|
+
module YoutubeDL
|
5
|
+
class OutputParser
|
6
|
+
def initialize
|
7
|
+
@state = State.new
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :state
|
11
|
+
|
12
|
+
def process(line)
|
13
|
+
line = line.strip
|
14
|
+
|
15
|
+
m = regex.match(line)
|
16
|
+
return :unparsable if m.nil?
|
17
|
+
|
18
|
+
if m[:destination]
|
19
|
+
state.destination = Pathname(m[:destination])
|
20
|
+
return :destination
|
21
|
+
end
|
22
|
+
|
23
|
+
if m[:info_json]
|
24
|
+
state.info_json = Pathname(m[:info_json]) if m[:info_json]
|
25
|
+
return :info_json
|
26
|
+
end
|
27
|
+
|
28
|
+
if m[:progress]
|
29
|
+
if m[:progress]
|
30
|
+
if m[:progress] == '100'
|
31
|
+
state.progress = 100
|
32
|
+
else
|
33
|
+
state.progress = m[:progress].to_f
|
34
|
+
end
|
35
|
+
end
|
36
|
+
state.total_size = Filesize.from("#{m[:total_size]} #{m[:total_size_unit]}") if m[:total_size]
|
37
|
+
state.speed = Filesize.from("#{m[:speed]} #{m[:speed_unit]}") if m[:speed]
|
38
|
+
return :progress
|
39
|
+
end
|
40
|
+
|
41
|
+
if m[:error]
|
42
|
+
state.error = m[:error] if m[:error]
|
43
|
+
return :error
|
44
|
+
end
|
45
|
+
|
46
|
+
raise 'Not sure how I got here...'
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def regex
|
52
|
+
%r{
|
53
|
+
#{progress_regex} |
|
54
|
+
#{destination_regex} |
|
55
|
+
#{existing_destination_regex} |
|
56
|
+
#{info_json_regex} |
|
57
|
+
#{error_regex}
|
58
|
+
}x
|
59
|
+
end
|
60
|
+
|
61
|
+
def progress_regex
|
62
|
+
%r{
|
63
|
+
\[download\] \s+
|
64
|
+
#{num_regex('progress')}%
|
65
|
+
(
|
66
|
+
\s+ of \s+
|
67
|
+
#{num_with_unit_regex('total_size')}
|
68
|
+
(
|
69
|
+
\s+ at \s+
|
70
|
+
#{num_with_unit_regex('speed')}
|
71
|
+
)?
|
72
|
+
)?
|
73
|
+
}x
|
74
|
+
end
|
75
|
+
|
76
|
+
def destination_regex
|
77
|
+
%r{
|
78
|
+
\[download\] \s+ Destination: \s+
|
79
|
+
(?<destination>.*)
|
80
|
+
}x
|
81
|
+
end
|
82
|
+
|
83
|
+
def existing_destination_regex
|
84
|
+
%r{
|
85
|
+
\[download\] \s
|
86
|
+
(?<destination>.*?) \s
|
87
|
+
has \s already \s been \s downloaded
|
88
|
+
}x
|
89
|
+
end
|
90
|
+
|
91
|
+
def info_json_regex
|
92
|
+
%r{
|
93
|
+
\[info\] \s Writing \s video \s description \s metadata \s as \s JSON \s to: \s
|
94
|
+
(?<info_json>.*)
|
95
|
+
}x
|
96
|
+
end
|
97
|
+
|
98
|
+
def error_regex
|
99
|
+
%r{
|
100
|
+
ERROR: \s
|
101
|
+
(?<error>.*)
|
102
|
+
}x
|
103
|
+
end
|
104
|
+
|
105
|
+
def num_regex(name)
|
106
|
+
%r{
|
107
|
+
(?<#{name}>
|
108
|
+
\d+(\.\d+)?
|
109
|
+
)
|
110
|
+
}x
|
111
|
+
end
|
112
|
+
|
113
|
+
def unit_regex(name)
|
114
|
+
%r{
|
115
|
+
(?<#{name}_unit>
|
116
|
+
\w+
|
117
|
+
)
|
118
|
+
}x
|
119
|
+
end
|
120
|
+
|
121
|
+
def num_with_unit_regex(name)
|
122
|
+
%r{
|
123
|
+
#{num_regex(name)}
|
124
|
+
#{unit_regex(name)}
|
125
|
+
}x
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'pty'
|
2
|
+
require 'youtube_dl/listener'
|
3
|
+
require 'youtube_dl/output_parser'
|
4
|
+
|
5
|
+
module YoutubeDL
|
6
|
+
class Runner
|
7
|
+
def initialize(command, listener: Listener.new)
|
8
|
+
@command = command
|
9
|
+
@listener = listener
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
parser = OutputParser.new
|
14
|
+
run(command: @command, parser: parser)
|
15
|
+
|
16
|
+
if parser.state.complete?
|
17
|
+
parser.state.load_and_delete_info_json
|
18
|
+
@listener.call(:complete, state: parser.state)
|
19
|
+
elsif parser.state.error?
|
20
|
+
@listener.call(:error, state: parser.state)
|
21
|
+
else
|
22
|
+
@listener.call(:unclear_exit_state, state: parser.state)
|
23
|
+
end
|
24
|
+
|
25
|
+
parser.state
|
26
|
+
end
|
27
|
+
|
28
|
+
def on(event, &block)
|
29
|
+
@listener.register(event, &block)
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
def respond_to_missing?(method, *)
|
34
|
+
method.to_s.start_with?('on_') || super
|
35
|
+
end
|
36
|
+
|
37
|
+
def method_missing(method, &block)
|
38
|
+
return super unless method.to_s.start_with?('on_')
|
39
|
+
|
40
|
+
event = method.to_s.sub(/^on_/, '').to_sym
|
41
|
+
on(event, &block)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def run(command:, parser:)
|
47
|
+
PTY.spawn(command.to_s) do |stdout, stdin, pid|
|
48
|
+
begin
|
49
|
+
stdout.each do |line|
|
50
|
+
type = parser.process(line)
|
51
|
+
@listener.call(type, state: parser.state, line: line)
|
52
|
+
end
|
53
|
+
rescue Errno::EIO
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module YoutubeDL
|
4
|
+
class State
|
5
|
+
attr_accessor(
|
6
|
+
:progress,
|
7
|
+
:destination,
|
8
|
+
:total_size,
|
9
|
+
:speed,
|
10
|
+
:error,
|
11
|
+
:info_json,
|
12
|
+
:info,
|
13
|
+
)
|
14
|
+
|
15
|
+
def error?
|
16
|
+
!error.nil?
|
17
|
+
end
|
18
|
+
|
19
|
+
def complete?
|
20
|
+
destination&.exist? && (info || info_json&.exist?)
|
21
|
+
end
|
22
|
+
|
23
|
+
def load_and_delete_info_json
|
24
|
+
self.info = JSON.parse(info_json.read())
|
25
|
+
info_json.unlink()
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/ytdl.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'youtube_dl'
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ytdl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Max Hollmann
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-05-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: dry-configurable
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.11.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.11.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: filesize
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.2.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.2.0
|
41
|
+
description:
|
42
|
+
email:
|
43
|
+
- maxhollmann@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- lib/youtube_dl.rb
|
49
|
+
- lib/youtube_dl/command.rb
|
50
|
+
- lib/youtube_dl/listener.rb
|
51
|
+
- lib/youtube_dl/output_parser.rb
|
52
|
+
- lib/youtube_dl/runner.rb
|
53
|
+
- lib/youtube_dl/state.rb
|
54
|
+
- lib/youtube_dl/version.rb
|
55
|
+
- lib/ytdl.rb
|
56
|
+
homepage: https://github.com/maxhollmann/ruby-ytdl
|
57
|
+
licenses:
|
58
|
+
- MIT
|
59
|
+
metadata:
|
60
|
+
source_code_uri: https://github.com/maxhollmann/ruby-ytdl
|
61
|
+
bug_tracker_uri: https://github.com/maxhollmann/ruby-ytdl/issues
|
62
|
+
changelog_uri: https://github.com/maxhollmann/ruby-ytdl/releases
|
63
|
+
homepage_uri: https://github.com/maxhollmann/ruby-ytdl
|
64
|
+
post_install_message:
|
65
|
+
rdoc_options: []
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 2.3.0
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
requirements: []
|
79
|
+
rubygems_version: 3.1.2
|
80
|
+
signing_key:
|
81
|
+
specification_version: 4
|
82
|
+
summary: A wrapper for youtube-dl with progress callbacks
|
83
|
+
test_files: []
|