rubytube 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []