forked from iambus/xunlei-lixian
-
Notifications
You must be signed in to change notification settings - Fork 0
/
lixian_nodes.py
149 lines (130 loc) · 4.12 KB
/
lixian_nodes.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import lixian_logging
import urllib2
import re
VOD_RANGE = '0-50'
def resolve_node_url(url, gdriveid, timeout=60):
request = urllib2.Request(url, headers={'Cookie': 'gdriveid=' + gdriveid})
response = urllib2.urlopen(request, timeout=timeout)
response.close()
return response.geturl()
def switch_node_in_url(node_url, node):
return re.sub(r'(http://)(vod\d+)(\.t\d+\.lixian\.vip\.xunlei\.com)', r'\1%s\3' % node, node_url)
def switch_node(url, node, gdriveid):
assert re.match(r'^vod\d+$', node)
logger = lixian_logging.get_logger()
logger.debug('Download URL: ' + url)
try:
url = resolve_node_url(url, gdriveid, timeout=60)
logger.debug('Resolved URL: ' + url)
except:
import traceback
logger.debug(traceback.format_exc())
return url
url = switch_node_in_url(url, node)
logger.debug('Switch to node URL: ' + url)
return url
def test_response_speed(response, max_size, max_duration):
import time
current_duration = 0
current_size = 0
start = time.clock()
while current_duration < max_duration and current_size < max_size:
data = response.read(max_size - current_size)
if not data:
# print "End of file"
break
current_size += len(data)
end = time.clock()
current_duration = end - start
if current_size < 1024:
raise Exception("Sample too small: %d" % current_size)
return current_size / current_duration, current_size, current_duration
def get_node_url_speed(url, gdriveid):
request = urllib2.Request(url, headers={'Cookie': 'gdriveid=' + gdriveid})
response = urllib2.urlopen(request, timeout=3)
speed, size, duration = test_response_speed(response, 2*1000*1000, 3)
response.close()
return speed
def parse_vod_nodes(vod_nodes):
if vod_nodes == 'all' or not vod_nodes:
vod_nodes = VOD_RANGE
nodes = []
# remove duplicate nodes
seen = set()
def add(node):
if node not in seen:
nodes.append(node)
seen.add(node)
for expr in re.split(r'\s*,\s*', vod_nodes):
if re.match(r'^\d+-\d+$', expr):
start, end = map(int, expr.split('-'))
if start <= end:
for i in range(start, end + 1):
add("vod%d" % i)
else:
for i in range(start, end - 1, -1):
add("vod%d" % i)
elif re.match(r'^\d+$', expr):
add('vod'+expr)
else:
raise Exception("Invalid vod expr: " + expr)
return nodes
def get_best_node_url_from(node_url, nodes, gdriveid):
best = None
best_speed = 0
logger = lixian_logging.get_logger()
for node in nodes:
url = switch_node_in_url(node_url, node)
try:
speed = get_node_url_speed(url, gdriveid)
logger.debug("%s speed: %s" % (node, speed))
if speed > best_speed:
best_speed = speed
best = url
except Exception, e:
logger.debug("%s error: %s" % (node, e))
return best
def get_good_node_url_from(node_url, nodes, acceptable_speed, gdriveid):
best = None
best_speed = 0
logger = lixian_logging.get_logger()
for node in nodes:
url = switch_node_in_url(node_url, node)
try:
speed = get_node_url_speed(url, gdriveid)
logger.debug("%s speed: %s" % (node, speed))
if speed > acceptable_speed:
return url
elif speed > best_speed:
best_speed = speed
best = url
except Exception, e:
logger.debug("%s error: %s" % (node, e))
return best
def use_node_by_policy(url, vod_nodes, gdriveid, policy):
nodes = parse_vod_nodes(vod_nodes)
assert nodes
logger = lixian_logging.get_logger()
logger.debug('Download URL: ' + url)
try:
node_url = resolve_node_url(url, gdriveid, timeout=60)
logger.debug('Resolved URL: ' + node_url)
except:
import traceback
logger.debug(traceback.format_exc())
return url
default_node = re.match(r'http://(vod\d+)\.', node_url).group(1)
if default_node not in nodes:
nodes.insert(0, default_node)
chosen = policy(node_url, nodes, gdriveid)
if chosen:
logger.debug('Switch to URL: ' + chosen)
return chosen
else:
return node_url
def use_fastest_node(url, vod_nodes, gdriveid):
return use_node_by_policy(url, vod_nodes, gdriveid, get_best_node_url_from)
def use_fast_node(url, vod_nodes, acceptable_speed, gdriveid):
def policy(url, vod_nodes, gdriveid):
return get_good_node_url_from(url, vod_nodes, acceptable_speed, gdriveid)
return use_node_by_policy(url, vod_nodes, gdriveid, policy)