vidload 0.1.11 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5f49b5b5ccfd0249511e3d7d9f14e13409d8c578efc202351fd5ebc5d82bbeba
4
- data.tar.gz: 88f6b50466ce36d590f1407ad8daecd0dddeee00c4739b9d315d9feab8a3293c
3
+ metadata.gz: e25ffa5a4bdbfdadd4ee261da31fab7f78e15f6865196bec9ed6824d827918cc
4
+ data.tar.gz: 85cbad777cffda334cf26c8992fe42b8a209eaed2b6210cea733e1f54f7cadcb
5
5
  SHA512:
6
- metadata.gz: ff4a4ecf10a0df9d1d339f1d7e2d2af8c033d967b98a137bea85c41e65179e15f28e031c0bbf9fb7eeda36c1c16d05870072c4f98739912cf04241c05bb546bc
7
- data.tar.gz: 8867c690eaac5b3ecfa177e22b6d44699657ca50d99f820a71b0f50dda3db8b34c56df99ddcd37c7c2a6a00ead4eda672725daaa6a3c4dce601ee7a0681851b4
6
+ metadata.gz: 898294dbb222d1d84f31cde9b4b30f7689de6cd18bacf2dd452496346be24f83245d24f52c1125291256020533e5b759091f4c67a7d7a9b47ed7457416a29633
7
+ data.tar.gz: 6bfae7fb0047df91c7aecfc6cc0f6302f895b6872e52dedad7751a2fbb39f97ee29966a437a5f1ba8b1dfd8324c34704e846cd930e1768324e8a2f0d64343bde
data/lib/vidload/cli.rb CHANGED
@@ -1,34 +1,32 @@
1
- require "thor"
1
+ # frozen_string_literal: true
2
2
 
3
- class Vidload::Cli < Thor
4
- desc "mp2t VIDEO_URL", "download a mp2t containerized video"
5
- method_option :video_name, type: :string, required: false
6
- method_option :author_name, type: :string, required: false
7
- method_option :hls_url, type: :string, required: true
8
- method_option :master_playlist_name, type: :string, required: true
9
- method_option :playwright_cli_path, type: :string, required: true
10
- method_option :video_referer, type: :string, required: true
11
- method_option :ts_seg_pattern, type: :string, required: true
12
- method_option :hls_index_pattern, type: :string, required: true
13
- def mp2t(video_url)
14
- params = {
15
- video_url: video_url,
16
- video_name: options[:video_name],
17
- hls_url: options[:hls_url],
18
- master_playlist_name: options[:master_playlist_name],
19
- playwright_cli_path: options[:playwright_cli_path],
20
- video_referer: options[:video_referer],
21
- ts_seg_pattern: options[:ts_seg_pattern],
22
- hls_index_pattern: options[:hls_index_pattern],
23
- author_name: options[:author_name]
24
- }
3
+ require 'thor'
25
4
 
26
- process_mp2t(params)
27
- end
5
+ module Vidload
6
+ class Cli < Thor
7
+ desc 'mp2t VIDEO_URL', 'download a mp2t containerized video'
8
+ method_option :video_name, type: :string, required: false
9
+ method_option :author_name, type: :string, required: false
10
+ method_option :output_dir, type: :string, required: false
11
+ method_option :hls_url, type: :string, required: true
12
+ method_option :master_playlist_name, type: :string, required: true
13
+ method_option :playwright_cli_path, type: :string, required: true
14
+ method_option :video_referer, type: :string, required: true
15
+ method_option :ts_seg_pattern, type: :string, required: true
16
+ method_option :hls_index_pattern, type: :string, required: true
17
+ def mp2t(video_url)
18
+ params = {
19
+ video_url: video_url,
20
+ **options
21
+ }
22
+
23
+ process_mp2t(params)
24
+ end
28
25
 
29
- private
26
+ private
30
27
 
31
- def process_mp2t(params)
32
- raise NotImplementedError
28
+ def process_mp2t(params)
29
+ raise NotImplementedError
30
+ end
33
31
  end
34
32
  end
@@ -1,218 +1,239 @@
1
- require "playwright"
2
- require "thread"
3
- require "tty-spinner"
4
- require "open3"
5
- require "m3u8"
6
- require "io/console"
7
-
8
- module Vidload::Mp2t::Api
9
- DEMUXER_PATH = "#{__dir__}/remuxer.sh"
10
- VIDEO_DOWNLOADED_EVENT_QUEUE = Queue.new
11
- VIDEO_INDEX_EVENT_QUEUE = Queue.new
12
- ANSI_BOLD_WHITE="\033[1;97m"
13
- ANSI_LIGHT_GREY="\033[37m"
14
- ANSI_RESET="\033[0m"
15
-
16
- class Downloader
17
- attr_reader :video_url, :video_name, :hls_url, :master_playlist_name, :playwright_cli_path, :video_referer
18
-
19
- def initialize(
20
- video_url:,
21
- video_name:,
22
- hls_url:,
23
- master_playlist_name:,
24
- playwright_cli_path:,
25
- video_referer:,
26
- ts_seg_pattern:,
27
- hls_index_pattern:,
28
- author_name:
29
- )
30
- raise ArgumentError, "video_url must be provided" unless video_url
31
- raise ArgumentError, "hls_url must be provided" unless hls_url
32
- raise ArgumentError, "master_playlist_name must be provided" unless master_playlist_name
33
- raise ArgumentError, "playwright_cli_path must be provided" unless playwright_cli_path
34
- raise ArgumentError, "video_referer must be provided" unless video_referer
35
- raise ArgumentError, "ts_seg_pattern must be provided" unless ts_seg_pattern
36
- raise ArgumentError, "hls_index_pattern must be provided" unless hls_index_pattern
37
-
38
- @video_url = video_url
39
- @hls_url = hls_url
40
- @master_playlist_name = master_playlist_name
41
- @playwright_cli_path = playwright_cli_path
42
- @video_referer = video_referer
43
- @ts_seg_pattern = ts_seg_pattern
44
- @hls_index_pattern = hls_index_pattern
45
- @max_lines = IO.console.winsize[0]
46
- @author_name = author_name
47
- @video_name = if @author_name then "#{@author_name}_#{video_name}" else nil end
48
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'playwright'
4
+ require 'tty-spinner'
5
+ require 'open3'
6
+ require 'm3u8'
7
+ require 'io/console'
8
+
9
+ module Vidload
10
+ module Mp2t
11
+ module Api
12
+ DEMUXER_PATH = "#{__dir__}/remuxer.sh"
13
+ VIDEO_DOWNLOADED_EVENT_QUEUE = Queue.new
14
+ VIDEO_INDEX_EVENT_QUEUE = Queue.new
15
+ ANSI_BOLD_WHITE = "\033[1;97m"
16
+ ANSI_LIGHT_GREY = "\033[37m"
17
+ ANSI_RESET = "\033[0m"
18
+
19
+ class DownloaderBuilder
20
+ REQUIRED_ARGS = %i[video_url hls_url master_playlist_name playwright_cli_path video_referer
21
+ ts_seg_pattern hls_index_pattern].freeze
22
+
23
+ def initialize
24
+ @kwargs = {}
25
+ end
49
26
 
50
- def self.from_argv
51
- new(
52
- video_url: ARGV[0],
53
- video_name: ARGV[1],
54
- hls_url: ARGV[2],
55
- master_playlist_name: ARGV[3],
56
- playwright_cli_path: ARGV[4],
57
- video_referer: ARGV[5],
58
- ts_seg_pattern: ARGV[6],
59
- hls_index_pattern: ARGV[7],
60
- author_name: ARGV[8],
61
- )
62
- end
27
+ def with_kwargs(**kwargs)
28
+ @kwargs = kwargs
29
+ self
30
+ end
63
31
 
64
- def self.from_hash(hash)
65
- new(
66
- video_url: hash[:video_url],
67
- author_name: hash[:author_name],
68
- video_name: hash[:video_name],
69
- hls_url: hash[:hls_url],
70
- master_playlist_name: hash[:master_playlist_name],
71
- playwright_cli_path: hash[:playwright_cli_path],
72
- video_referer: hash[:video_referer],
73
- ts_seg_pattern: hash[:ts_seg_pattern],
74
- hls_index_pattern: hash[:hls_index_pattern],
75
- )
76
- end
32
+ def with_video_url(video_url)
33
+ @kwargs[:video_url] = video_url
34
+ self
35
+ end
77
36
 
78
- # main func to be called in your own scripts defined under web/
79
- def download_video(video_starter_callbacks: [])
80
- Playwright.create(playwright_cli_executable_path: @playwright_cli_path) do |playwright|
81
- browser = playwright.chromium.launch
82
- page = browser.new_page
37
+ def with_video_name(video_name)
38
+ @kwargs[:video_name] = video_name
39
+ self
40
+ end
83
41
 
84
- manage_video_download(page, *video_starter_callbacks)
85
- wait_until_video_downloaded
42
+ def with_author_name(author_name)
43
+ @kwargs[:author_name] = author_name
44
+ self
45
+ end
86
46
 
87
- browser.close
88
- end
89
- end
47
+ def with_hls_url(hls_url)
48
+ @kwargs[:hls_url] = hls_url
49
+ self
50
+ end
90
51
 
91
- def display_calling_args
92
- puts "Constants:"
93
- puts "\tDEMUXER_PATH=#{DEMUXER_PATH}"
94
- puts "Called with:"
95
- puts "\tvideo_url=#{@video_url}"
96
- puts "\tvideo_name=#{@video_name}"
97
- puts "\thls_url=#{@hls_url}"
98
- puts "\tmaster_playlist_name=#{@master_playlist_name}"
99
- puts "\tplaywright_cli_path=#{@playwright_cli_path}"
100
- puts "\tvideo_referer=#{@video_referer}"
101
- puts "\tts_seg_pattern=#{@ts_seg_pattern}"
102
- puts "\thls_index_pattern=#{@hls_index_pattern}"
103
- puts "\tauthor_name=#{@author_name}"
104
- end
52
+ def with_master_playlist_name(master_playlist_name)
53
+ @kwargs[:master_playlist_name] = master_playlist_name
54
+ self
55
+ end
105
56
 
106
- def self.display_with_spinner(loading_msg = "Loading...")
107
- spinner = TTY::Spinner.new("[:spinner] #{loading_msg}")
108
- spinner.auto_spin
109
- yield
110
- spinner.success("(done)")
111
- end
57
+ def with_playwright_cli_path(playwright_cli_path)
58
+ @kwargs[:playwright_cli_path] = playwright_cli_path
59
+ self
60
+ end
112
61
 
113
- private
62
+ def with_video_referer(video_referer)
63
+ @kwargs[:video_referer] = video_referer
64
+ self
65
+ end
114
66
 
115
- def manage_video_download(page, *video_starter_callbacks)
116
- @seg_qty = nil
117
- @pending_hls_response = nil
118
- @lines = [""] * @max_lines
119
- page.on("response", -> (resp) { listen_to_video_starts(resp) })
120
- navigate_to_url(@video_url, page)
121
- video_starter_callbacks.each do |callback|
122
- res = callback.call(page)
123
- if !@video_name && res[:video_name]
124
- @video_name = res[:video_name]
67
+ def with_ts_seg_pattern(ts_seg_pattern)
68
+ @kwargs[:ts_seg_pattern] = ts_seg_pattern
69
+ self
125
70
  end
126
- if !@author_name && res[:author_name]
127
- @author_name = res[:author_name]
128
- @video_name = "#{@author_name}_#{@video_name}"
71
+
72
+ def with_hls_index_pattern(hls_index_pattern)
73
+ @kwargs[:hls_index_pattern] = hls_index_pattern
74
+ self
129
75
  end
130
- end
131
- end
132
76
 
133
- def wait_until_video_downloaded
134
- VIDEO_DOWNLOADED_EVENT_QUEUE.pop
135
- end
77
+ def with_output_dir(output_dir)
78
+ @kwargs[:output_dir] = output_dir
79
+ self
80
+ end
136
81
 
137
- def trigger_video_download(video_url, seg_qty)
138
- puts "Video starts. Starting download..."
139
- run_cmd(DEMUXER_PATH, video_url, @video_name, @video_referer) do |line|
140
- if (line.include?("hls @") || line.include?("https @")) && line.match?(/#{@ts_seg_pattern}/i)
141
- seg_nb = line.match(/#{@ts_seg_pattern}/i)[:seg_nb]
142
- add_line(line)
143
- progress_bar(seg_nb, seg_qty)
82
+ def build
83
+ REQUIRED_ARGS.each do |required_arg|
84
+ raise ArgumentError, "#{required_arg} must be provided" unless @kwargs[required_arg]
85
+ end
86
+
87
+ @kwargs[:output_dir] = './' unless @kwargs[:output_dir]
88
+ @kwargs[:video_name] = "#{@kwargs[:author_name]}_#{@kwargs[:video_name]}" if @kwargs[:author_name]
89
+
90
+ Downloader.new(**@kwargs)
144
91
  end
145
92
  end
146
- print "\r\e[2K"
147
- puts "✔ Video downloaded successfully! Available in ./#{@video_name}.mp4"
148
- VIDEO_DOWNLOADED_EVENT_QUEUE << true
149
- end
150
93
 
151
- def listen_to_video_starts(response)
152
- if response.url.start_with?(@hls_url) && response.url.match?(/#{@hls_index_pattern}/i)
153
- body = response.text
154
- playlist = M3u8::Playlist.read(body)
155
- last_item = playlist.items.last.segment
156
- match = last_item.match(/#{@ts_seg_pattern}/i)
157
- @seg_qty = match[:seg_nb].to_i
94
+ class Downloader
95
+ def initialize(**kwargs)
96
+ @max_lines = IO.console.winsize[0]
97
+ @kwargs = kwargs
98
+ end
158
99
 
159
- if @pending_hls_response
160
- trigger_video_download(@pending_hls_response.url, @seg_qty)
100
+ def self.builder
101
+ DownloaderBuilder.new
161
102
  end
162
- elsif response.url.start_with?(@hls_url) && response.url.include?(@master_playlist_name)
163
- if @seg_qty
164
- trigger_video_download(response.url, @seg_qty)
165
- else
166
- @pending_hls_response = response
103
+
104
+ def self.from_hash(hash)
105
+ builder.with_kwargs(**hash).build
167
106
  end
168
- end
169
- end
170
107
 
171
- def navigate_to_url(url, page)
172
- Downloader.display_with_spinner("Page #{url} loading...") do
173
- page.goto(url)
174
- end
175
- end
108
+ # main func to be called in your own scripts defined under web/
109
+ def download_video(video_starter_callbacks: [])
110
+ Playwright.create(playwright_cli_executable_path: @kwargs[:playwright_cli_path]) do |playwright|
111
+ browser = playwright.chromium.launch
112
+ page = browser.new_page
176
113
 
177
- def run_cmd(*cmd)
178
- Open3.popen2e(*cmd) do |_stdin, stdout_and_stderr, wait_thr|
179
- stdout_and_stderr.each_line do |line|
180
- yield line
114
+ manage_video_download(page, *video_starter_callbacks)
115
+ wait_until_video_downloaded
116
+
117
+ browser.close
118
+ end
181
119
  end
182
- end
183
- end
184
120
 
185
- def redraw_lines()
186
- return if @lines.empty?
121
+ def display_calling_args
122
+ puts 'Constants:'
123
+ puts "\tDEMUXER_PATH=#{DEMUXER_PATH}"
124
+ puts 'Called with:'
125
+ @kwargs.each do |key, value|
126
+ puts "\t#{key}=#{value}"
127
+ end
128
+ end
187
129
 
188
- printf "\e[H"
189
- printf "\e[0J"
130
+ def self.display_with_spinner(loading_msg = 'Loading...')
131
+ spinner = TTY::Spinner.new("[:spinner] #{loading_msg}")
132
+ spinner.auto_spin
133
+ yield
134
+ spinner.success('(done)')
135
+ end
190
136
 
191
- _rows, cols = IO.console.winsize
192
- @lines.each do |line|
193
- if line.length > cols
194
- puts "#{line.slice(0, cols - 3)}..."
195
- else
196
- puts line
137
+ private
138
+
139
+ def manage_video_download(page, *video_starter_callbacks)
140
+ @seg_qty = nil
141
+ @pending_hls_response = nil
142
+ @lines = [''] * @max_lines
143
+ page.on('response', ->(resp) { listen_to_video_starts(resp) })
144
+ navigate_to_url(@kwargs[:video_url], page)
145
+ video_starter_callbacks.each do |callback|
146
+ res = callback.call(page)
147
+ @kwargs[:video_name] = res[:video_name] if !@kwargs[:video_name] && res[:video_name]
148
+ if !@kwargs[:author_name] && res[:author_name]
149
+ @kwargs[:author_name] = res[:author_name]
150
+ @kwargs[:video_name] = "#{@kwargs[:author_name]}_#{@kwargs[:video_name]}"
151
+ end
152
+ end
197
153
  end
198
- end
199
- end
200
154
 
201
- def add_line(line)
202
- @lines << line
203
- @lines.shift if @lines.size > @max_lines
204
- redraw_lines()
205
- end
155
+ def wait_until_video_downloaded
156
+ VIDEO_DOWNLOADED_EVENT_QUEUE.pop
157
+ end
206
158
 
207
- def progress_bar(current, total, width: 40)
208
- ratio = current.to_f / total
209
- filled = (ratio * width).round
210
- empty = width - filled
159
+ def trigger_video_download(video_url, seg_qty)
160
+ puts 'Video starts. Starting download...'
161
+ run_cmd(DEMUXER_PATH, video_url, "#{@kwargs[:output_dir]}#{@kwargs[:video_name]}",
162
+ @kwargs[:video_referer]) do |line|
163
+ if (line.include?('hls @') || line.include?('https @')) && line.match?(/#{@kwargs[:ts_seg_pattern]}/i)
164
+ seg_nb = line.match(/#{@kwargs[:ts_seg_pattern]}/i)[:seg_nb]
165
+ add_line(line)
166
+ progress_bar(seg_nb, seg_qty)
167
+ end
168
+ end
169
+ print "\r\e[2K"
170
+ puts "✔ Video downloaded successfully! Available in #{@kwargs[:output_dir]}#{@kwargs[:video_name]}.mp4"
171
+ VIDEO_DOWNLOADED_EVENT_QUEUE << true
172
+ end
211
173
 
212
- bar = "█" * filled + "░" * empty
213
- percent = (ratio * 100).round(1)
174
+ def listen_to_video_starts(response)
175
+ if response.url.start_with?(@kwargs[:hls_url]) && response.url.match?(/#{@kwargs[:hls_index_pattern]}/i)
176
+ body = response.text
177
+ playlist = M3u8::Playlist.read(body)
178
+ last_item = playlist.items.last.segment
179
+ match = last_item.match(/#{@kwargs[:ts_seg_pattern]}/i)
180
+ @seg_qty = match[:seg_nb].to_i
181
+
182
+ trigger_video_download(@pending_hls_response.url, @seg_qty) if @pending_hls_response
183
+ elsif response.url.start_with?(@kwargs[:hls_url]) && response.url.include?(@kwargs[:master_playlist_name])
184
+ if @seg_qty
185
+ trigger_video_download(response.url, @seg_qty)
186
+ else
187
+ @pending_hls_response = response
188
+ end
189
+ end
190
+ end
214
191
 
215
- print "\r[#{bar}] #{percent}% (#{current}/#{total})"
192
+ def navigate_to_url(url, page)
193
+ Downloader.display_with_spinner("Page #{url} loading...") do
194
+ page.goto(url)
195
+ end
196
+ end
197
+
198
+ def run_cmd(*cmd, &block)
199
+ Open3.popen2e(*cmd) do |_stdin, stdout_and_stderr, _wait_thr|
200
+ stdout_and_stderr.each_line(&block)
201
+ end
202
+ end
203
+
204
+ def redraw_lines
205
+ return if @lines.empty?
206
+
207
+ printf "\e[H"
208
+ printf "\e[0J"
209
+
210
+ _rows, cols = IO.console.winsize
211
+ @lines.each do |line|
212
+ if line.length > cols
213
+ puts "#{line.slice(0, cols - 3)}..."
214
+ else
215
+ puts line
216
+ end
217
+ end
218
+ end
219
+
220
+ def add_line(line)
221
+ @lines << line
222
+ @lines.shift if @lines.size > @max_lines
223
+ redraw_lines
224
+ end
225
+
226
+ def progress_bar(current, total, width: 40)
227
+ ratio = current.to_f / total
228
+ filled = (ratio * width).round
229
+ empty = width - filled
230
+
231
+ bar = '█' * filled + '░' * empty
232
+ percent = (ratio * 100).round(1)
233
+
234
+ print "\r[#{bar}] #{percent}% (#{current}/#{total})"
235
+ end
236
+ end
216
237
  end
217
238
  end
218
239
  end
data/lib/vidload/mp2t.rb CHANGED
@@ -1,3 +1,7 @@
1
- module Vidload::Mp2t
2
- require_relative "mp2t/api"
1
+ # frozen_string_literal: true
2
+
3
+ module Vidload
4
+ module Mp2t
5
+ require_relative 'mp2t/api'
6
+ end
3
7
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Vidload
2
- VERSION = "0.1.11"
4
+ VERSION = '0.2.1'
3
5
  end
data/lib/vidload.rb CHANGED
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Vidload
2
- require_relative "vidload/version"
3
- require_relative "vidload/mp2t"
4
- require_relative "vidload/cli"
4
+ require_relative 'vidload/version'
5
+ require_relative 'vidload/mp2t'
6
+ require_relative 'vidload/cli'
5
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vidload
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.11
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patacode <pata.codegineer@gmail.com>
@@ -10,6 +10,20 @@ bindir: bin
10
10
  cert_chain: []
11
11
  date: 2026-03-13 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: m3u8
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.8'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.8'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: playwright-ruby-client
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -25,61 +39,61 @@ dependencies:
25
39
  - !ruby/object:Gem::Version
26
40
  version: '1.58'
27
41
  - !ruby/object:Gem::Dependency
28
- name: tty-spinner
42
+ name: thor
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - "~>"
32
46
  - !ruby/object:Gem::Version
33
- version: '0.9'
47
+ version: '1.5'
34
48
  type: :runtime
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
52
  - - "~>"
39
53
  - !ruby/object:Gem::Version
40
- version: '0.9'
54
+ version: '1.5'
41
55
  - !ruby/object:Gem::Dependency
42
- name: m3u8
56
+ name: tty-spinner
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - "~>"
46
60
  - !ruby/object:Gem::Version
47
- version: '1.8'
61
+ version: '0.9'
48
62
  type: :runtime
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
66
  - - "~>"
53
67
  - !ruby/object:Gem::Version
54
- version: '1.8'
68
+ version: '0.9'
55
69
  - !ruby/object:Gem::Dependency
56
- name: thor
70
+ name: rake
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
73
  - - "~>"
60
74
  - !ruby/object:Gem::Version
61
- version: '1.5'
62
- type: :runtime
75
+ version: '13.3'
76
+ type: :development
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
80
  - - "~>"
67
81
  - !ruby/object:Gem::Version
68
- version: '1.5'
82
+ version: '13.3'
69
83
  - !ruby/object:Gem::Dependency
70
- name: rake
84
+ name: rubocop
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
- - - ">="
87
+ - - "~>"
74
88
  - !ruby/object:Gem::Version
75
- version: '0'
89
+ version: '1.85'
76
90
  type: :development
77
91
  prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
- - - ">="
94
+ - - "~>"
81
95
  - !ruby/object:Gem::Version
82
- version: '0'
96
+ version: '1.85'
83
97
  description:
84
98
  email:
85
99
  executables: []