66require 'open-uri'
77require 'zip'
88
9+ if OpenSSL ::SSL . const_defined? ( :OP_IGNORE_UNEXPECTED_EOF )
10+ OpenSSL ::SSL ::SSLContext ::DEFAULT_PARAMS [ :options ] |= OpenSSL ::SSL ::OP_IGNORE_UNEXPECTED_EOF
11+ end
12+
913module TrainPortal ::Ice
1014 module Videos
1115 module_function
@@ -68,14 +72,16 @@ def download_video(manifests, file_name)
6872 # mpd_manifest = read_mpd_manifest(manifests.detect { |url| url.end_with?('manifest.mpd') })
6973 manifest_mpd_url = manifests . detect { |url | url . end_with? ( 'manifest.mpd' ) }
7074
71- file_name = full_path ( file_name )
72- # yt_dlp(manifest_mpd_url, file_name)
73- file_name = Dir . glob ( "#{ file_name } *.mp4" ) . first
75+ yt_dlp ( manifest_mpd_url , full_path ( file_name ) )
76+ file_name = full_path ( file_name , 'mp4' )
7477 mp4decode ( file_name , manifest_mpd_url )
7578 reencode ( file_name )
7679 end
7780
7881 def yt_dlp ( download_url , file_name )
82+ raise ArgumentError , 'file_name is required' if file_name . nil?
83+ return if full_path ( file_name , 'mp4' ) # file exists
84+
7985 unless system ( 'which yt-dlp > /dev/null 2>&1' )
8086 raise "yt-dlp is not installed or not found in PATH"
8187 end
@@ -126,15 +132,18 @@ def mp4decode(input_file, manifest_mpd_url)
126132 mp4decrypt_path = File . join ( tmp_dir , 'bin' , 'mp4decrypt' )
127133 download_mp4decrypt ( mp4decrypt_path ) unless File . exist? ( mp4decrypt_path )
128134 end
129- binding . irb
130135
131- # Extract the decryption key from the manifest.mpd
132136 manifest = Nokogiri ::XML ( URI . open ( manifest_mpd_url ) . read )
133- key_id = manifest . at_xpath ( '//cenc:default_KID' , 'cenc' => 'urn:mpeg:cenc:2013' ) . content
134- pssh = manifest . at_xpath ( '//cenc:pssh' , 'cenc' => 'urn:mpeg:cenc:2013' ) . content
137+ manifest . remove_namespaces!
138+ video_definitions = manifest . css ( 'AdaptationSet[contentType="video"]' )
139+ max_quality_video = video_definitions . max_by { |v | v [ 'maxWidth' ] }
140+ crypto_parameters = max_quality_video . css ( 'ContentProtection[default_KID]' ) . first
141+ key_id = crypto_parameters [ 'default_KID' ]
142+ crypto_mode = crypto_parameters [ 'value' ]
143+ scheme_id_uri = crypto_parameters [ 'schemeIdUri' ]
144+ pssh = max_quality_video . css ( 'ContentProtection pssh' ) . first . content
135145
136- # Here you would need to implement the logic to derive the key from the pssh
137- # For now, we'll assume you have a method to get the key
146+ binding . irb
138147 key = get_decryption_key ( pssh )
139148
140149 output_file = input_file . sub ( /\. \w +$/ , '_dec.mp4' )
@@ -166,9 +175,15 @@ def get_manifests(details) = details['video']['hydra:member'].flat_map { |vh| vh
166175 def read_m3u8 ( url ) = M3u8 ::Playlist . read ( URI . open ( url ) . read )
167176 def read_mpd_manifest ( url ) = Nokogiri ::XML ( URI . open ( url ) . read )
168177 def sanitize_string ( string ) = string . to_url ( force_downcase : false ) . gsub ( '-' , '_' )
169- def full_path ( sub_path ) = TrainPortal . download_directory ( File . join ( 'videos' , sub_path ) )
170178 def tmp_dir = TrainPortal . download_directory ( 'tmp' )
171179
180+ def full_path ( sub_path , extension = nil )
181+ result = TrainPortal . download_directory ( File . join ( 'videos' , sub_path ) )
182+ return result if extension . nil?
183+
184+ Dir . glob ( "#{ result } *.#{ extension } " ) . first
185+ end
186+
172187 def download_mp4decrypt ( destination_path )
173188 puts 'downloading mp4decrypt'
174189 # Download and unpack Bento4 SDK
@@ -188,7 +203,8 @@ def download_mp4decrypt(destination_path)
188203 zip_path = File . join ( tmp_dir , 'bento4.zip' )
189204
190205 File . open ( zip_path , 'wb' ) do |file |
191- file . write ( URI . open ( bento_url ) . open ( open_timeout : READ_TIMEOUT , read_timeout : READ_TIMEOUT ) . read )
206+ uri = URI . parse ( bento_url ) . open ( open_timeout : READ_TIMEOUT , read_timeout : READ_TIMEOUT )
207+ file . write ( uri . read )
192208 end
193209
194210 Zip ::File . open ( zip_path ) do |zip_file |
0 commit comments