youtube_audio 0.1.0 → 0.2.0

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.
@@ -2,34 +2,36 @@
2
2
 
3
3
  module YoutubeAudio
4
4
  class Decipher
5
+ attr_reader :script_player_url
6
+ URL = 'https://www.youtube.com'
7
+
8
+ def initialize(script_player_url)
9
+ @script_player_url = script_player_url
10
+ end
11
+
5
12
  # @param cipher [string] youtube signature
6
13
  def decipher(cipher)
7
- decipher = cipher.split('')
8
- pb(decipher, 35)
9
- pb(decipher, 30)
10
- wh(decipher)
11
- pb(decipher, 2)
12
- decipher = p7(decipher, 3)
13
- wh(decipher)
14
- pb(decipher, 8)
15
- decipher = p7(decipher, 2)
16
- decipher.join('')
14
+ klass = extract_decode_function_handler
15
+ miniracer_klass = miniracer_context
16
+
17
+ miniracer_klass.eval(klass.cipher_helpers_object)
18
+ miniracer_klass.eval("var #{klass.decipher_function}")
19
+ miniracer_klass.eval("#{klass.decryption_function}('#{cipher}')")
17
20
  end
18
21
 
19
- def pb(a, b)
20
- c = a[0]
21
- a[0] = a[b % a.length]
22
- a[b % a.length] = c
22
+ private
23
+
24
+ def extract_decode_function_handler
25
+ @extract_decode_function_handler ||=
26
+ ExtractDecodeFunction.new(script_player_content)
23
27
  end
24
28
 
25
- def wh(a)
26
- result = a.reverse!
27
- result
29
+ def miniracer_context
30
+ MiniRacer::Context.new
28
31
  end
29
32
 
30
- def p7(a, b)
31
- result = a.slice(b, a.length)
32
- result
33
+ def script_player_content
34
+ @script_player_content ||= Net::HTTP.get(URI(URL + script_player_url))
33
35
  end
34
36
  end
35
37
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YoutubeAudio
4
+ class ExtractDecodeFunction
5
+ attr_reader :player_script
6
+ CIPHER_TEST = Regexp.new('([\\w$]+)\\s*=\\s*function\\((\\w+)\\)\\{\\s*\\2=\\s*\\2\\.split\\(""\\)\\s*;')
7
+
8
+ def initialize(player_script)
9
+ @player_script = player_script
10
+ end
11
+
12
+ def cipher_helper_object_name
13
+ @cipher_helper_object_name ||=
14
+ decipher_function.scan(/\;([a-zA-Z0-9]{0,255})\./).flatten.first
15
+ end
16
+
17
+ def cipher_helpers_object
18
+ object_decibper_index = player_script.index("var #{cipher_helper_object_name}={")
19
+ object_decibper_index_end = player_script[object_decibper_index..-1].index('};')
20
+ player_script[object_decibper_index..object_decibper_index + object_decibper_index_end]
21
+ end
22
+
23
+ def decipher_function
24
+ @decipher_function ||=
25
+ player_script.scan(Regexp.new(function_pattern)).flatten.first
26
+ end
27
+
28
+ def decryption_function
29
+ @decryption_function ||= player_script.scan(CIPHER_TEST).flatten.first
30
+ end
31
+
32
+ private
33
+
34
+ def function_pattern
35
+ @function_pattern ||=
36
+ '(' + decryption_function + '=function\\([a-zA-Z0-9_]+\\)\\{.+?\\})'
37
+ end
38
+ end
39
+ end
@@ -2,7 +2,10 @@
2
2
 
3
3
  module YoutubeAudio
4
4
  class Format
5
- def initialize(response_raw)
5
+ attr_reader :script_player_url
6
+
7
+ def initialize(response_raw, script_player_url: nil)
8
+ @script_player_url = script_player_url
6
9
  @response_raw = response_raw
7
10
  end
8
11
 
@@ -11,7 +14,12 @@ module YoutubeAudio
11
14
  end
12
15
 
13
16
  def url
14
- return UrlDecipher.new(cipher).decipher if cipher
17
+ if cipher
18
+ return UrlDecipher.new(
19
+ cipher,
20
+ script_player_url: script_player_url
21
+ ).decipher
22
+ end
15
23
 
16
24
  @response_raw&.dig('url')
17
25
  end
@@ -2,9 +2,12 @@
2
2
 
3
3
  module YoutubeAudio
4
4
  class PlayerResponse
5
+ attr_reader :script_player_url
6
+
5
7
  # @response_raw url [Hash]
6
- def initialize(response_raw)
8
+ def initialize(response_raw, script_player_url: nil)
7
9
  @response_raw = response_raw
10
+ @script_player_url = script_player_url
8
11
  end
9
12
 
10
13
  def formats
@@ -14,7 +17,7 @@ module YoutubeAudio
14
17
  end
15
18
 
16
19
  def to_youtube_format(format_raw)
17
- Format.new(format_raw)
20
+ Format.new(format_raw, script_player_url: script_player_url)
18
21
  end
19
22
  end
20
23
  end
@@ -16,7 +16,11 @@ module YoutubeAudio
16
16
  mini_racer_context.eval(@script)
17
17
  # get player response ...
18
18
  response = mini_racer_context.eval('ytInitialPlayerConfig')
19
- PlayerResponse.new(JSON.parse(response['args']['player_response']))
19
+
20
+ PlayerResponse.new(
21
+ JSON.parse(response['args']['player_response']),
22
+ script_player_url: response['assets']['js']
23
+ )
20
24
  end
21
25
  end
22
26
  end
@@ -2,17 +2,19 @@
2
2
 
3
3
  module YoutubeAudio
4
4
  class UrlDecipher
5
- attr_reader :cipher
5
+ attr_reader :cipher, :script_player_url
6
6
 
7
- def initialize(cipher)
7
+ def initialize(cipher, script_player_url:)
8
8
  @cipher = cipher
9
+ @script_player_url = script_player_url
9
10
  end
10
11
 
11
12
  def decipher
12
13
  decoded = CGI.parse(@cipher)
13
14
 
14
- signature = Decipher.new.decipher(decoded.dig('s').first)
15
- decoded.dig('url').first + '&' + decoded.dig('sp').first + "=#{signature}"
15
+ sign = Decipher.new(script_player_url).decipher(decoded.dig('s').first)
16
+
17
+ decoded.dig('url').first + '&' + decoded.dig('sp').first + "=#{sign}"
16
18
  end
17
19
  end
18
20
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module YoutubeAudio
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
data/lib/youtube_audio.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require 'mechanize'
4
4
  require 'mini_racer'
5
5
  require 'cgi'
6
+ require 'net/http'
6
7
  require 'youtube_audio/version'
7
8
  require 'youtube_audio/url_decipher'
8
9
  require 'youtube_audio/download'
@@ -13,6 +14,7 @@ require 'youtube_audio/script_parser'
13
14
  require 'youtube_audio/decipher'
14
15
  require 'youtube_audio/search'
15
16
  require 'youtube_audio/search_item'
17
+ require 'youtube_audio/extract_decode_function'
16
18
 
17
19
  module YoutubeAudio
18
20
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: youtube_audio
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jorge Hernandez
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-09-03 00:00:00.000000000 Z
11
+ date: 2019-09-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -157,10 +157,12 @@ files:
157
157
  - bin/console
158
158
  - bin/setup
159
159
  - fixtures/vcr_cassettes/search/results.yml
160
+ - fixtures/vcr_cassettes/youtube/player_base_js.yml
160
161
  - fixtures/vcr_cassettes/youtube/xoWRkd3oGcs.yml
161
162
  - lib/youtube_audio.rb
162
163
  - lib/youtube_audio/decipher.rb
163
164
  - lib/youtube_audio/download.rb
165
+ - lib/youtube_audio/extract_decode_function.rb
164
166
  - lib/youtube_audio/format.rb
165
167
  - lib/youtube_audio/formats.rb
166
168
  - lib/youtube_audio/player_response.rb