ytdl 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: []