rubytube 0.1.0

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,81 @@
1
+ module RubyTube
2
+ class Stream
3
+ attr_accessor(
4
+ :monostate,
5
+ :url,
6
+ :itag,
7
+ :mime_type,
8
+ :codecs,
9
+ :type,
10
+ :subtype,
11
+ :file_size,
12
+ :is_otf,
13
+ :bitrate
14
+ )
15
+
16
+ def initialize(stream, monostate)
17
+ self.monostate = monostate
18
+
19
+ self.url = stream['url']
20
+ self.itag = stream['itag'].to_i
21
+
22
+ self.mime_type, self.codecs = Extractor.mime_type_codec(stream['mimeType'])
23
+ self.type, self.subtype = mime_type.split('/')
24
+
25
+ self.is_otf = stream['is_otf']
26
+ self.bitrate = stream['bitrate']
27
+
28
+ self.file_size = stream.fetch('contentLength', 0).to_i
29
+ end
30
+
31
+ def download(filename: nil, output_dir: nil)
32
+ file_path = get_file_path(filename, output_dir)
33
+
34
+ return file_path if File.exist?(file_path)
35
+
36
+ bytes_remaining = file_size
37
+
38
+ File.open(file_path, 'wb') do |f|
39
+ begin
40
+ Request.stream(url) do |chunk|
41
+ bytes_remaining -= chunk.bytesize
42
+ f.write(chunk)
43
+ end
44
+ rescue HTTPError => e
45
+ raise e if e.code != 404
46
+ end
47
+ end
48
+
49
+ file_path
50
+ end
51
+
52
+ def is_audio?
53
+ type == 'audio'
54
+ end
55
+
56
+ def is_video?
57
+ type == 'video'
58
+ end
59
+
60
+ def title
61
+ monostate.title
62
+ end
63
+
64
+ private
65
+
66
+ def get_file_path(filename, output_dir, prefix = '')
67
+ filename = default_filename unless filename
68
+
69
+ if prefix
70
+ filename = "#{prefix}#{filename}"
71
+ end
72
+
73
+ output_path = Utils.target_directory(output_dir)
74
+ File.join(output_path, filename)
75
+ end
76
+
77
+ def default_filename
78
+ "#{monostate.title}.#{subtype}"
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,33 @@
1
+ module RubyTube
2
+ class StreamQuery < Array
3
+ attr_reader :streams
4
+
5
+ def initialize(fmt_streams)
6
+ super
7
+ @streams = fmt_streams
8
+ end
9
+
10
+ def filter(file_extension: nil, only_audio: false, only_video: false)
11
+ filters = []
12
+
13
+ filters << ->(stream) { stream.subtype == file_extension } if file_extension
14
+ filters << ->(stream) { stream.is_audio? } if only_audio
15
+ filters << ->(stream) { stream.is_video? } if only_video
16
+
17
+ r = streams
18
+ filters.each do |f|
19
+ r = r.select(&f)
20
+ end
21
+
22
+ r
23
+ end
24
+
25
+ def first
26
+ streams.first
27
+ end
28
+
29
+ def get_by_itag(itag)
30
+ streams.find { |s| s.itag == itag }
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,24 @@
1
+ module RubyTube
2
+ module Utils
3
+ module_function
4
+
5
+ def regex_search(pattern, string, group)
6
+ match = string.match(pattern)
7
+ if match
8
+ return match[group]
9
+ end
10
+ nil
11
+ end
12
+
13
+ def target_directory(output_path = nil)
14
+ if output_path
15
+ result = File.join(Dir.pwd, output_path) unless File.absolute_path?(output_path)
16
+ else
17
+ result = Dir.pwd
18
+ end
19
+
20
+ FileUtils.mkdir_p(result) unless Dir.exist?(result)
21
+ result
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyTube
4
+ VERSION = "0.1.0"
5
+ end
data/lib/rubytube.rb ADDED
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require 'faraday/follow_redirects'
5
+
6
+ require_relative 'rubytube/version'
7
+
8
+ require_relative 'rubytube/cipher'
9
+ require_relative 'rubytube/client'
10
+ require_relative 'rubytube/extractor'
11
+ require_relative 'rubytube/innertube'
12
+ require_relative 'rubytube/monostate'
13
+ require_relative 'rubytube/parser'
14
+ require_relative 'rubytube/request'
15
+ require_relative 'rubytube/stream_query'
16
+ require_relative 'rubytube/stream'
17
+ require_relative 'rubytube/utils'
18
+
19
+ module RubyTube
20
+ class Error < StandardError; end
21
+ class HTMLParseError < StandardError; end
22
+ class ExtractError < StandardError; end
23
+ class MaxRetriesExceeded < StandardError; end
24
+ class VideoUnavailable < StandardError; end
25
+
26
+ class RegexMatchError < StandardError
27
+ def initialize(caller, pattern)
28
+ super("Regex match error in #{caller} for pattern #{pattern}")
29
+ end
30
+ end
31
+
32
+ class MembersOnly < StandardError
33
+ def initialize(video_id)
34
+ super("Members only video: #{video_id}")
35
+ end
36
+ end
37
+
38
+ class RecordingUnavailable < StandardError
39
+ def initialize(video_id)
40
+ super("Recording unavailable: #{video_id}")
41
+ end
42
+ end
43
+
44
+ class VideoUnavailable < StandardError
45
+ def initialize(video_id)
46
+ super("Video unavailable: #{video_id}")
47
+ end
48
+ end
49
+
50
+ class VideoPrivate < StandardError
51
+ def initialize(video_id)
52
+ super("Video is private: #{video_id}")
53
+ end
54
+ end
55
+
56
+ class LiveStreamError < StandardError
57
+ def initialize(video_id)
58
+ super("Video is a live stream: #{video_id}")
59
+ end
60
+ end
61
+
62
+ class << self
63
+ def new(url)
64
+ Client.new(url)
65
+ end
66
+ end
67
+ end
data/sig/rubytube.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module RubyTube
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubytube
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - nightswinger
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-09-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 2.0.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 2.0.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday-follow_redirects
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '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'
41
+ description: Write a longer description or delete this line.
42
+ email:
43
+ - stardustkids83@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".rspec"
49
+ - ".standard.yml"
50
+ - CHANGELOG.md
51
+ - CODE_OF_CONDUCT.md
52
+ - Gemfile
53
+ - LICENSE.txt
54
+ - README.md
55
+ - Rakefile
56
+ - lib/rubytube.rb
57
+ - lib/rubytube/cipher.rb
58
+ - lib/rubytube/client.rb
59
+ - lib/rubytube/extractor.rb
60
+ - lib/rubytube/innertube.rb
61
+ - lib/rubytube/monostate.rb
62
+ - lib/rubytube/parser.rb
63
+ - lib/rubytube/request.rb
64
+ - lib/rubytube/stream.rb
65
+ - lib/rubytube/stream_query.rb
66
+ - lib/rubytube/utils.rb
67
+ - lib/rubytube/version.rb
68
+ - sig/rubytube.rbs
69
+ homepage: https://github.com/nightswinger/rubytube
70
+ licenses:
71
+ - MIT
72
+ metadata:
73
+ homepage_uri: https://github.com/nightswinger/rubytube
74
+ source_code_uri: https://github.com/nightswinger/rubytube
75
+ changelog_uri: https://github.com/nightswinger/rubytube
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: 2.6.0
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubygems_version: 3.4.10
92
+ signing_key:
93
+ specification_version: 4
94
+ summary: Write a short summary, because RubyGems requires one.
95
+ test_files: []