vidload 0.1.8 → 0.1.9

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: 4517bef7a27856859c3259c01245ac3304490d7abe9ebf6297bb0138b730ab91
4
- data.tar.gz: 20aaddca48db238d263c60afecd990b14017f3595cce15a163e0157a03c2fb53
3
+ metadata.gz: 1e31985b06dadaf2785ef93d95c31d4be1344f7009c3aa8249e82b98f595c241
4
+ data.tar.gz: a4fbe5dc7da663e70a3cdc6d7cd0642e0229d993f918b6d5e8a619b9716ec037
5
5
  SHA512:
6
- metadata.gz: eaa42d64362b16ea42006bc1e245bd425c00025f3e626d11c848319884f500148f33ffab790bb2df4585fff331e6f476f3940dda2b9c9e77e7cfdda0e105777c
7
- data.tar.gz: 784c87e22bbb5340608c305e31b928650361fa228bf7788d551ea0065f3c1015b0294dba755503d563a12778a46f8eddfc7b3056060817ae57cd38ba374cd9bf
6
+ metadata.gz: ea9730f179412b53445d348768212ca95d16cf921c674c0b00e9c370e36ed80ec710cba6a3fdbac4f312f86faed169e619b5608b1f2429cd0fc358e9dde802ed
7
+ data.tar.gz: ff0ee458009eeb3ee81a063dbcc7f14909e7d57a5aed3a802c8cf952ba3f9e0223bd837128e650c2bd97feb73e808b1848489250b05aeb7edc2246dfd1d4a684
@@ -1,10 +1,17 @@
1
1
  require "playwright"
2
2
  require "thread"
3
3
  require "tty-spinner"
4
+ require "open3"
5
+ require "m3u8"
6
+ require "io/console"
4
7
 
5
8
  module Vidload::Mp2t::Api
6
9
  DEMUXER_PATH = "#{__dir__}/remuxer.sh"
7
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"
8
15
 
9
16
  class Downloader
10
17
  attr_reader :video_url, :video_name, :hls_url, :master_playlist_name, :playwright_cli_path, :video_referer
@@ -15,7 +22,9 @@ module Vidload::Mp2t::Api
15
22
  hls_url:,
16
23
  master_playlist_name:,
17
24
  playwright_cli_path:,
18
- video_referer:
25
+ video_referer:,
26
+ ts_seg_pattern:,
27
+ hls_index_pattern:
19
28
  )
20
29
  raise ArgumentError, "video_url must be provided" unless video_url
21
30
  raise ArgumentError, "video_name must be provided" unless video_name
@@ -23,6 +32,8 @@ module Vidload::Mp2t::Api
23
32
  raise ArgumentError, "master_playlist_name must be provided" unless master_playlist_name
24
33
  raise ArgumentError, "playwright_cli_path must be provided" unless playwright_cli_path
25
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
26
37
 
27
38
  @video_url = video_url
28
39
  @video_name = video_name
@@ -30,6 +41,9 @@ module Vidload::Mp2t::Api
30
41
  @master_playlist_name = master_playlist_name
31
42
  @playwright_cli_path = playwright_cli_path
32
43
  @video_referer = video_referer
44
+ @ts_seg_pattern = ts_seg_pattern
45
+ @hls_index_pattern = hls_index_pattern
46
+ @max_lines = IO.console.winsize[0]
33
47
  end
34
48
 
35
49
  def self.from_argv
@@ -39,7 +53,9 @@ module Vidload::Mp2t::Api
39
53
  hls_url: ARGV[2],
40
54
  master_playlist_name: ARGV[3],
41
55
  playwright_cli_path: ARGV[4],
42
- video_referer: ARGV[5]
56
+ video_referer: ARGV[5],
57
+ ts_seg_pattern: ARGV[6],
58
+ hls_index_pattern: ARGV[7],
43
59
  )
44
60
  end
45
61
 
@@ -78,7 +94,10 @@ module Vidload::Mp2t::Api
78
94
  private
79
95
 
80
96
  def manage_video_download(page, *video_starter_callbacks)
81
- page.on("request", -> (req) { listen_to_video_starts(req) })
97
+ @seg_qty = nil
98
+ @pending_hls_response = nil
99
+ @lines = [""] * @max_lines
100
+ page.on("response", -> (resp) { listen_to_video_starts(resp) })
82
101
  navigate_to_url(@video_url, page)
83
102
  video_starter_callbacks.each do |callback|
84
103
  callback.call(page)
@@ -89,12 +108,37 @@ module Vidload::Mp2t::Api
89
108
  VIDEO_DOWNLOADED_EVENT_QUEUE.pop
90
109
  end
91
110
 
92
- def listen_to_video_starts(request)
93
- if request.url.start_with?(@hls_url) && request.url.include?(@master_playlist_name)
94
- puts "Video starts. Starting download..."
95
- system(DEMUXER_PATH, request.url, @video_name, @video_referer)
96
- puts "✔ Video downloaded successfully! Available in ./#{@video_name}.mp4"
97
- VIDEO_DOWNLOADED_EVENT_QUEUE << true
111
+ def trigger_video_download(video_url, seg_qty)
112
+ puts "Video starts. Starting download..."
113
+ run_cmd(DEMUXER_PATH, video_url, @video_name, @video_referer) do |line|
114
+ if (line.include?("hls @") || line.include?("https @")) && line.match?(/#{@ts_seg_pattern}/i)
115
+ seg_nb = line.match(/#{@ts_seg_pattern}/i)[:seg_nb]
116
+ add_line(line)
117
+ progress_bar(seg_nb, seg_qty)
118
+ end
119
+ end
120
+ print "\r\e[2K"
121
+ puts "✔ Video downloaded successfully! Available in ./#{@video_name}.mp4"
122
+ VIDEO_DOWNLOADED_EVENT_QUEUE << true
123
+ end
124
+
125
+ def listen_to_video_starts(response)
126
+ if response.url.start_with?(@hls_url) && response.url.match?(/#{@hls_index_pattern}/i)
127
+ body = response.text
128
+ playlist = M3u8::Playlist.read(body)
129
+ last_item = playlist.items.last.segment
130
+ match = last_item.match(/#{@ts_seg_pattern}/i)
131
+ @seg_qty = match[:seg_nb].to_i
132
+
133
+ if @pending_hls_response
134
+ trigger_video_download(@pending_hls_response.url, @seg_qty)
135
+ end
136
+ elsif response.url.start_with?(@hls_url) && response.url.include?(@master_playlist_name)
137
+ if @seg_qty
138
+ trigger_video_download(response.url, @seg_qty)
139
+ else
140
+ @pending_hls_response = response
141
+ end
98
142
  end
99
143
  end
100
144
 
@@ -103,5 +147,46 @@ module Vidload::Mp2t::Api
103
147
  page.goto(url)
104
148
  end
105
149
  end
150
+
151
+ def run_cmd(*cmd)
152
+ Open3.popen2e(*cmd) do |_stdin, stdout_and_stderr, wait_thr|
153
+ stdout_and_stderr.each_line do |line|
154
+ yield line
155
+ end
156
+ end
157
+ end
158
+
159
+ def redraw_lines()
160
+ return if @lines.empty?
161
+
162
+ printf "\e[H"
163
+ printf "\e[0J"
164
+
165
+ _rows, cols = IO.console.winsize
166
+ @lines.each do |line|
167
+ if line.length > cols
168
+ puts "#{line.slice(0, cols - 3)}..."
169
+ else
170
+ puts line
171
+ end
172
+ end
173
+ end
174
+
175
+ def add_line(line)
176
+ @lines << line
177
+ @lines.shift if @lines.size > @max_lines
178
+ redraw_lines()
179
+ end
180
+
181
+ def progress_bar(current, total, width: 40)
182
+ ratio = current.to_f / total
183
+ filled = (ratio * width).round
184
+ empty = width - filled
185
+
186
+ bar = "█" * filled + "░" * empty
187
+ percent = (ratio * 100).round(1)
188
+
189
+ print "\r[#{bar}] #{percent}% (#{current}/#{total})"
190
+ end
106
191
  end
107
192
  end
@@ -1,3 +1,3 @@
1
1
  module Vidload
2
- VERSION = "0.1.8"
2
+ VERSION = "0.1.9"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vidload
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patacode <pata.codegineer@gmail.com>
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-03-12 00:00:00.000000000 Z
11
+ date: 2026-03-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: playwright-ruby-client
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0.9'
41
+ - !ruby/object:Gem::Dependency
42
+ name: m3u8
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.8'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.8'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rake
43
57
  requirement: !ruby/object:Gem::Requirement