sportsflix 1.1.1 → 1.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.
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