ytdl 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,8 @@
1
+ require 'youtube_dl/runner'
2
+ require 'youtube_dl/command'
3
+
4
+ module YoutubeDL
5
+ def self.download(url, **options)
6
+ Runner.new(Command.new(url, **options))
7
+ end
8
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ module YoutubeDL
2
+ VERSION = "0.0.1"
3
+ end
@@ -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: []