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 +4 -4
- data/Gemfile.lock +5 -1
- data/lib/sportsflix/cli.rb +13 -10
- data/lib/sportsflix/providers/arenavision.rb +11 -20
- data/lib/sportsflix/utils/http.rb +131 -0
- data/lib/sportsflix/version.rb +1 -1
- data/sportsflix.gemspec +1 -0
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8fe9cee194bc85a6907925f3fb937c9cce0bef68
|
4
|
+
data.tar.gz: 31b00a384f81ee0f9e7e03ae11ed4f25a5e184f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f3e58ddf0a78d689ff643d6586cf756b76b51d3b8bc2d6fbffd4fc6303369318a97aabf265060addd2deca97155c159578e3b2a19aadd641346f257f256e2f4a
|
7
|
+
data.tar.gz: f2262b2bef876b2b42cd475698678406bb9f97cbb81c28091e4cfe0a20b99e20695768a96fd883ac6b2b087e71472808309a5ebd22a3c821f887dd208b2cbb3f
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
sportsflix (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
|
data/lib/sportsflix/cli.rb
CHANGED
@@ -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', {
|
17
|
-
class_option('offset', {
|
18
|
-
class_option('video-format', {
|
19
|
-
class_option('club', {
|
20
|
-
class_option('video-player', {
|
21
|
-
class_option('video-player-path', {
|
22
|
-
class_option('interactive', {
|
23
|
-
class_option('server-only', {
|
24
|
-
class_option('proxy-delay', {
|
25
|
-
class_option('server-ip', {
|
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
|
-
|
9
|
-
|
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
|
19
|
-
schedule_path = home.css('
|
20
|
-
schedule
|
21
|
-
streams
|
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
|
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("#{
|
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
|
-
|
56
|
-
|
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
|
data/lib/sportsflix/version.rb
CHANGED
data/sportsflix.gemspec
CHANGED
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.
|
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-
|
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
|