youtube_audio 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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