vidload 0.1.8 → 0.1.10

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: 3416ba597c831a7d6e1460bc0447fee8577e758279e036e820ccd967d93d46ef
4
+ data.tar.gz: a6d4c4c9e5bc8447cf68335029d60ca156c98ef6fc7bacd066b2335ca0e6966b
5
5
  SHA512:
6
- metadata.gz: eaa42d64362b16ea42006bc1e245bd425c00025f3e626d11c848319884f500148f33ffab790bb2df4585fff331e6f476f3940dda2b9c9e77e7cfdda0e105777c
7
- data.tar.gz: 784c87e22bbb5340608c305e31b928650361fa228bf7788d551ea0065f3c1015b0294dba755503d563a12778a46f8eddfc7b3056060817ae57cd38ba374cd9bf
6
+ metadata.gz: '0891fcda0f3d59ea64ce309f35fea44456185bf0c6d56765e3c6d6ca60160819d19428e2b065e55c5c14efd3a7ef5dffa712ae02861e4e098c2d6e8bb8fdef75'
7
+ data.tar.gz: d5406c2f3e81b23038402e9f249c2359a96dc89edeba437e8274a2c4ada88bf6f1207373ab3205a2c9604bcc88bf0af8d8443e2f483cf93647c5934d780df466
@@ -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,21 +22,29 @@ 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:,
28
+ author_name:
19
29
  )
20
30
  raise ArgumentError, "video_url must be provided" unless video_url
21
- raise ArgumentError, "video_name must be provided" unless video_name
22
31
  raise ArgumentError, "hls_url must be provided" unless hls_url
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
- @video_name = video_name
29
39
  @hls_url = hls_url
30
40
  @master_playlist_name = master_playlist_name
31
41
  @playwright_cli_path = playwright_cli_path
32
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
33
48
  end
34
49
 
35
50
  def self.from_argv
@@ -39,7 +54,10 @@ module Vidload::Mp2t::Api
39
54
  hls_url: ARGV[2],
40
55
  master_playlist_name: ARGV[3],
41
56
  playwright_cli_path: ARGV[4],
42
- video_referer: ARGV[5]
57
+ video_referer: ARGV[5],
58
+ ts_seg_pattern: ARGV[6],
59
+ hls_index_pattern: ARGV[7],
60
+ author_name: ARGV[8],
43
61
  )
44
62
  end
45
63
 
@@ -66,6 +84,9 @@ module Vidload::Mp2t::Api
66
84
  puts "\tmaster_playlist_name=#{@master_playlist_name}"
67
85
  puts "\tplaywright_cli_path=#{@playwright_cli_path}"
68
86
  puts "\tvideo_referer=#{@video_referer}"
87
+ puts "\tts_seg_pattern=#{@ts_seg_pattern}"
88
+ puts "\thls_index_pattern=#{@hls_index_pattern}"
89
+ puts "\tauthor_name=#{@author_name}"
69
90
  end
70
91
 
71
92
  def self.display_with_spinner(loading_msg = "Loading...")
@@ -78,10 +99,20 @@ module Vidload::Mp2t::Api
78
99
  private
79
100
 
80
101
  def manage_video_download(page, *video_starter_callbacks)
81
- page.on("request", -> (req) { listen_to_video_starts(req) })
102
+ @seg_qty = nil
103
+ @pending_hls_response = nil
104
+ @lines = [""] * @max_lines
105
+ page.on("response", -> (resp) { listen_to_video_starts(resp) })
82
106
  navigate_to_url(@video_url, page)
83
107
  video_starter_callbacks.each do |callback|
84
- callback.call(page)
108
+ res = callback.call(page)
109
+ if !@video_name && res[:video_name]
110
+ @video_name = res[:video_name]
111
+ end
112
+ if !@author_name && res[:author_name]
113
+ @author_name = res[:author_name]
114
+ @video_name = "#{@author_name}_#{@video_name}"
115
+ end
85
116
  end
86
117
  end
87
118
 
@@ -89,12 +120,37 @@ module Vidload::Mp2t::Api
89
120
  VIDEO_DOWNLOADED_EVENT_QUEUE.pop
90
121
  end
91
122
 
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
123
+ def trigger_video_download(video_url, seg_qty)
124
+ puts "Video starts. Starting download..."
125
+ run_cmd(DEMUXER_PATH, video_url, @video_name, @video_referer) do |line|
126
+ if (line.include?("hls @") || line.include?("https @")) && line.match?(/#{@ts_seg_pattern}/i)
127
+ seg_nb = line.match(/#{@ts_seg_pattern}/i)[:seg_nb]
128
+ add_line(line)
129
+ progress_bar(seg_nb, seg_qty)
130
+ end
131
+ end
132
+ print "\r\e[2K"
133
+ puts "✔ Video downloaded successfully! Available in ./#{@video_name}.mp4"
134
+ VIDEO_DOWNLOADED_EVENT_QUEUE << true
135
+ end
136
+
137
+ def listen_to_video_starts(response)
138
+ if response.url.start_with?(@hls_url) && response.url.match?(/#{@hls_index_pattern}/i)
139
+ body = response.text
140
+ playlist = M3u8::Playlist.read(body)
141
+ last_item = playlist.items.last.segment
142
+ match = last_item.match(/#{@ts_seg_pattern}/i)
143
+ @seg_qty = match[:seg_nb].to_i
144
+
145
+ if @pending_hls_response
146
+ trigger_video_download(@pending_hls_response.url, @seg_qty)
147
+ end
148
+ elsif response.url.start_with?(@hls_url) && response.url.include?(@master_playlist_name)
149
+ if @seg_qty
150
+ trigger_video_download(response.url, @seg_qty)
151
+ else
152
+ @pending_hls_response = response
153
+ end
98
154
  end
99
155
  end
100
156
 
@@ -103,5 +159,46 @@ module Vidload::Mp2t::Api
103
159
  page.goto(url)
104
160
  end
105
161
  end
162
+
163
+ def run_cmd(*cmd)
164
+ Open3.popen2e(*cmd) do |_stdin, stdout_and_stderr, wait_thr|
165
+ stdout_and_stderr.each_line do |line|
166
+ yield line
167
+ end
168
+ end
169
+ end
170
+
171
+ def redraw_lines()
172
+ return if @lines.empty?
173
+
174
+ printf "\e[H"
175
+ printf "\e[0J"
176
+
177
+ _rows, cols = IO.console.winsize
178
+ @lines.each do |line|
179
+ if line.length > cols
180
+ puts "#{line.slice(0, cols - 3)}..."
181
+ else
182
+ puts line
183
+ end
184
+ end
185
+ end
186
+
187
+ def add_line(line)
188
+ @lines << line
189
+ @lines.shift if @lines.size > @max_lines
190
+ redraw_lines()
191
+ end
192
+
193
+ def progress_bar(current, total, width: 40)
194
+ ratio = current.to_f / total
195
+ filled = (ratio * width).round
196
+ empty = width - filled
197
+
198
+ bar = "█" * filled + "░" * empty
199
+ percent = (ratio * 100).round(1)
200
+
201
+ print "\r[#{bar}] #{percent}% (#{current}/#{total})"
202
+ end
106
203
  end
107
204
  end
@@ -1,3 +1,3 @@
1
1
  module Vidload
2
- VERSION = "0.1.8"
2
+ VERSION = "0.1.10"
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.10
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