sportsflix 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ab875fb4cea428df2326513d643cfe01d4a5d408
4
- data.tar.gz: 1ddc915f45c262ee95b6b71a21bb5a42edc69d3b
3
+ metadata.gz: 8fe9cee194bc85a6907925f3fb937c9cce0bef68
4
+ data.tar.gz: 31b00a384f81ee0f9e7e03ae11ed4f25a5e184f9
5
5
  SHA512:
6
- metadata.gz: dd303bd3c51135c388a91899a730fea4fc497e776f4c5a877144eca9f6c064733e5f14fa618e83ab69abb8486406f3ce0e00f0bf6d3c0112a25fafd4064c75dc
7
- data.tar.gz: c0ecd3bf0e9ff1c37296a2adb3727bc2f4fcf626a2cfaaf7f7623854d05467baa95ddfd3830a923fd1efc6e2c86755b7a89c56f65d36aa22f8e5270e1cc0522c
6
+ metadata.gz: f3e58ddf0a78d689ff643d6586cf756b76b51d3b8bc2d6fbffd4fc6303369318a97aabf265060addd2deca97155c159578e3b2a19aadd641346f257f256e2f4a
7
+ data.tar.gz: f2262b2bef876b2b42cd475698678406bb9f97cbb81c28091e4cfe0a20b99e20695768a96fd883ac6b2b087e71472808309a5ebd22a3c821f887dd208b2cbb3f
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sportsflix (1.1.1)
4
+ sportsflix (1.2.0)
5
+ execjs-fastnode (~> 0.2.0)
5
6
  oga (~> 2.8)
6
7
  thor (~> 0.19.4)
7
8
 
@@ -14,6 +15,9 @@ GEM
14
15
  simplecov
15
16
  diff-lcs (1.3)
16
17
  docile (1.1.5)
18
+ execjs (2.7.0)
19
+ execjs-fastnode (0.2.0)
20
+ execjs (~> 2.0)
17
21
  json (2.0.3)
18
22
  oga (2.10)
19
23
  ast
@@ -1,5 +1,6 @@
1
1
  require 'thor'
2
2
  require 'sportsflix'
3
+ require 'sportsflix/utils/http'
3
4
 
4
5
  module Sportsflix
5
6
 
@@ -13,21 +14,23 @@ module Sportsflix
13
14
  DEFAULT_VIDEO_PLAYER_PATH = DEFAULT_VIDEO_PLAYER
14
15
  DEFAULT_PROXY_DELAY = 10
15
16
 
16
- class_option('verbose', { :type => :boolean, :default => false })
17
- class_option('offset', { :aliases => :o, :type => :numeric, :default => DEFAULT_OFFSET })
18
- class_option('video-format', { :aliases => :f, :type => :string, :default => DEFAULT_VIDEO_FORMAT })
19
- class_option('club', { :aliases => :c, :type => :string })
20
- class_option('video-player', { :aliases => :p, :type => :string, :default => DEFAULT_VIDEO_PLAYER })
21
- class_option('video-player-path', { :type => :string, :default => DEFAULT_VIDEO_PLAYER_PATH })
22
- class_option('interactive', { :type => :boolean, :default => false })
23
- class_option('server-only', { :aliases => :s, :type => :boolean, :default => false })
24
- class_option('proxy-delay', { :type => :numeric, :default => DEFAULT_PROXY_DELAY })
25
- class_option('server-ip', { :type => :string })
17
+ class_option('verbose', {:type => :boolean, :default => false})
18
+ class_option('offset', {:aliases => :o, :type => :numeric, :default => DEFAULT_OFFSET})
19
+ class_option('video-format', {:aliases => :f, :type => :string, :default => DEFAULT_VIDEO_FORMAT})
20
+ class_option('club', {:aliases => :c, :type => :string})
21
+ class_option('video-player', {:aliases => :p, :type => :string, :default => DEFAULT_VIDEO_PLAYER})
22
+ class_option('video-player-path', {:type => :string, :default => DEFAULT_VIDEO_PLAYER_PATH})
23
+ class_option('interactive', {:type => :boolean, :default => false})
24
+ class_option('server-only', {:aliases => :s, :type => :boolean, :default => false})
25
+ class_option('proxy-delay', {:type => :numeric, :default => DEFAULT_PROXY_DELAY})
26
+ class_option('server-ip', {:type => :string})
26
27
 
27
28
  desc('watch', 'watch stream in the chosen player')
29
+
28
30
  def watch
29
31
  api = API.new(options)
30
32
  api.watch
31
33
  end
34
+
32
35
  end
33
36
  end
@@ -5,22 +5,23 @@ module Sportsflix
5
5
  module Providers
6
6
  module Arenavision
7
7
  class Client
8
- ARENAVISION_BASE_URL = ['http://arenavision.in', 'http://arenavision.ru']
9
- ARENAVISION_DEFAULT_URL_IDX = 0
8
+ BASE_URLS = ['https://arenavision.in', 'https://arenavision.ru']
9
+ BASE_URL = BASE_URLS.sample
10
10
 
11
11
  def initialize(options)
12
12
  @verbose = options[:verbose]
13
13
  @club_name = options[:club]
14
14
  @server_only = options['server-only']
15
+ @http = Sportsflix::Utils::HTTP.new
15
16
  end
16
17
 
17
18
  def list_streams
18
- home = get_page_contents("#{ARENAVISION_BASE_URL[ARENAVISION_DEFAULT_URL_IDX]}/")
19
- schedule_path = home.css('.menu li a').select {|item| item.text.include?('EVENTS GUIDE')}.first.get('href')
20
- schedule = get_page_contents("#{ARENAVISION_BASE_URL[ARENAVISION_DEFAULT_URL_IDX]}#{schedule_path}")
21
- streams = schedule.css('table tr')
19
+ home = get_page_contents("#{BASE_URL}/")
20
+ schedule_path = home.css('a').select {|item| item.text.include?('EVENTS GUIDE')}.first.get('href')
21
+ schedule = get_page_contents("#{BASE_URL}#{schedule_path}")
22
+ streams = schedule.css('table tr')
22
23
  # Remove first element
23
- streams = streams.drop(1)
24
+ streams = streams.drop(1)
24
25
  # Remove last element
25
26
  streams.pop(2)
26
27
 
@@ -46,24 +47,14 @@ module Sportsflix
46
47
  end
47
48
 
48
49
  def get_stream_uri(stream_nr)
49
- stream_raw = get_page_contents("#{ARENAVISION_BASE_URL[ARENAVISION_DEFAULT_URL_IDX]}/av#{stream_nr}")
50
+ stream_raw = get_page_contents("#{BASE_URL}/av#{stream_nr}")
50
51
  stream_raw.css('p[class="auto-style1"] a').first.get('href')
51
52
  end
52
53
 
53
54
  private
54
55
  def get_page_contents(raw_url)
55
- url = URI.parse(raw_url)
56
- enum = Enumerator.new do |yielder|
57
- Net::HTTP.start(url.host, url.port) do |http|
58
- http.request_get(url.path, {'Cookie' => 'beget=begetok;'}) do |response|
59
- response.read_body do |chunk|
60
- yielder << chunk
61
- end
62
- end
63
- end
64
- end
65
-
66
- Oga.parse_xml(enum)
56
+ html_str = @http.get(raw_url, {}, {Cookie: 'beget=begetok;'}).body
57
+ Oga.parse_xml(html_str)
67
58
  end
68
59
 
69
60
  def parse_stream_ids(raw_stream)
@@ -0,0 +1,131 @@
1
+ require 'net/http'
2
+ require 'execjs'
3
+
4
+ module Sportsflix
5
+ module Utils
6
+ class HTTP
7
+
8
+ DEFAULT_USER_AGENTS = [
9
+ 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36',
10
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36',
11
+ 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36',
12
+ 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0',
13
+ 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:41.0) Gecko/20100101 Firefox/41.0',
14
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3099.0 Safari/537.36'
15
+ ]
16
+
17
+ DEFAULT_HEADERS = {
18
+ 'User-Agent' => DEFAULT_USER_AGENTS.sample
19
+ }
20
+
21
+ def initialize
22
+ @headers = DEFAULT_HEADERS
23
+ @params = {}
24
+ end
25
+
26
+ def get(raw_url, extra_params = {}, extra_headers = {})
27
+ uri = URI.parse(raw_url)
28
+ uri.query = URI.encode_www_form(@params.merge(extra_params))
29
+ req = Net::HTTP::Get.new(uri, @headers.merge(extra_headers))
30
+
31
+ res = Net::HTTP.start(uri.hostname, uri.port) {|http|
32
+ http.request(req)
33
+ }
34
+
35
+ get_lambda = lambda {|url| get(url, extra_params, extra_headers)}
36
+
37
+ with_cf_bypass(res, get_lambda)
38
+ end
39
+
40
+ def post(raw_url, body, extra_params = {}, extra_headers = {})
41
+ uri = URI.parse(raw_url)
42
+ uri.query = URI.encode_www_form(@params.merge(extra_params))
43
+ merged_headers = @headers
44
+ .merge({'Content-Type' => 'application/x-www-form-urlencoded'})
45
+ .merge(extra_headers)
46
+ req = Net::HTTP::Post.new(uri, merged_headers)
47
+
48
+ res = Net::HTTP.start(uri.hostname, uri.port) {|http|
49
+ http.request(req)
50
+ }
51
+
52
+ post_lambda = lambda {|url| post(url, body, extra_params, extra_headers)}
53
+
54
+ bypassed_res = with_cf_bypass(res, post_lambda)
55
+ bypassed_res.body
56
+ end
57
+
58
+ private
59
+ def with_cf_bypass(res, req_lambda)
60
+ if res.is_a?(Net::HTTPRedirection)
61
+ puts "Redirecting from #{res.uri.to_s} to #{res['location']}."
62
+ res
63
+ elsif res.is_a?(Net::HTTPServiceUnavailable) &&
64
+ res['Server'] == 'cloudflare-nginx' &&
65
+ res.body.include?('jschl_vc') &&
66
+ res.body.include?('jschl_answer')
67
+
68
+ url = "#{res.uri.scheme}://#{res.uri.hostname}"
69
+ if res.uri.port
70
+ url = "#{url}:#{res.uri.port}"
71
+ end
72
+
73
+ url = "#{url}/cdn-cgi/l/chk_jschl"
74
+
75
+ @headers = @headers.merge({Referer: res.uri.to_s})
76
+ @params = @params.merge(
77
+ {
78
+ jschl_vc: /name="jschl_vc" value="(\w+)"/.match(res.body),
79
+ pass: /name="pass" value="(.+?)"/.match(res.body),
80
+ jschl_answer: "#{solve_challenge(res.body)}#{res.hostname.size}"
81
+ }
82
+ )
83
+
84
+ redirect = req_lambda.call(url)
85
+ req_lambda.call(redirect['location'])
86
+ else
87
+ res
88
+ end
89
+ end
90
+
91
+ def solve_challenge(body)
92
+ begin
93
+ js = /setTimeout\(function\(\){\s+(var s,t,o,p,b,r,e,a,k,i,n,g,f.+?\r?\n[\s\S]+?a\.value =.+?)\r?\n/.match(body)
94
+ rescue
95
+ puts 'Unable to identify Cloudflare IUAM Javascript on website.'
96
+ exit(1)
97
+ end
98
+
99
+ js = js.gsub("a\.value = (parseInt\(.+?\)).+", "\1")
100
+ js = js.gsub("\s{3,}[a-z](?: = |\.).+", '')
101
+
102
+ # Strip characters that could be used to exit the string context
103
+ # These characters are not currently used in Cloudflare's arithmetic snippet
104
+ js = js.gsub("[\n\\']", '')
105
+
106
+ unless js.include?('parseInt')
107
+ puts 'Error parsing Cloudflare IUAM Javascript challenge.'
108
+ exit(1)
109
+ end
110
+
111
+ begin
112
+ js = "return require('vm').runInNewContext('#{js}');"
113
+ result = ExecJS.eval(js)
114
+ rescue
115
+ puts 'Error executing Cloudflare IUAM Javascript.'
116
+ exit(1)
117
+ end
118
+
119
+ begin
120
+ result = result.to_i
121
+ rescue
122
+ puts 'Cloudflare IUAM challenge returned unexpected value.'
123
+ exit(1)
124
+ end
125
+
126
+ result.to_s
127
+ end
128
+
129
+ end
130
+ end
131
+ end
@@ -1,3 +1,3 @@
1
1
  module Sportsflix
2
- VERSION = '1.1.1'
2
+ VERSION = '1.2.0'
3
3
  end
@@ -47,4 +47,5 @@ Gem::Specification.new do |spec|
47
47
  # Runtime
48
48
  spec.add_runtime_dependency 'thor', ['~> 0.19.4']
49
49
  spec.add_runtime_dependency 'oga', ['~> 2.8']
50
+ spec.add_runtime_dependency 'execjs-fastnode', ['~> 0.2.0']
50
51
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sportsflix
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Fernandes
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-05-15 00:00:00.000000000 Z
11
+ date: 2017-06-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -136,6 +136,20 @@ dependencies:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
138
  version: '2.8'
139
+ - !ruby/object:Gem::Dependency
140
+ name: execjs-fastnode
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: 0.2.0
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: 0.2.0
139
153
  description: "\n Watch the best sports stream in HD from the command line.\n Using
140
154
  arenavision streams.\n "
141
155
  email:
@@ -166,6 +180,7 @@ files:
166
180
  - lib/sportsflix/players/vlc.rb
167
181
  - lib/sportsflix/providers/arenavision.rb
168
182
  - lib/sportsflix/utils/exec.rb
183
+ - lib/sportsflix/utils/http.rb
169
184
  - lib/sportsflix/version.rb
170
185
  - sportsflix.gemspec
171
186
  homepage: https://github.com/rtfpessoa/sportsflix