|
@@ -1,5 +1,5 @@
|
1
|
1
|
#!/usr/bin/python
|
2
|
|
-"""
|
|
2
|
+"""
|
3
|
3
|
StreamProxy daemon (based on Livestream daemon)
|
4
|
4
|
Ensures persistent cookies, User-Agents and others tricks to play protected HLS/DASH streams
|
5
|
5
|
"""
|
|
@@ -41,10 +41,10 @@ class StreamHandler(BaseHTTPRequestHandler):
|
41
|
41
|
SPLIT_CODE = "%7E"
|
42
|
42
|
EQ_CODE = "%3D"
|
43
|
43
|
COL_CODE = "%3A"
|
44
|
|
-
|
|
44
|
+
|
45
|
45
|
p = self.path.split("~")
|
46
|
46
|
url = urllib.unquote(p[0][1:])
|
47
|
|
- url = url.replace(COL_CODE, ":")
|
|
47
|
+ url = url.replace(COL_CODE, ":")
|
48
|
48
|
headers = headers2dict("""
|
49
|
49
|
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 8_0_2 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12A366 Safari/600.1.4
|
50
|
50
|
""")
|
|
@@ -56,7 +56,7 @@ class StreamHandler(BaseHTTPRequestHandler):
|
56
|
56
|
self.fetch_url2(self.wfile, url, headers)
|
57
|
57
|
except Exception as e:
|
58
|
58
|
print "Got Exception: ", str(e)
|
59
|
|
-
|
|
59
|
+
|
60
|
60
|
def fetch_offline(self,wfile):
|
61
|
61
|
self.send_response(200)
|
62
|
62
|
self.send_header("Server", "StreamProxy")
|
|
@@ -64,18 +64,18 @@ class StreamHandler(BaseHTTPRequestHandler):
|
64
|
64
|
self.end_headers()
|
65
|
65
|
self.wfile.write(open("offline.mp4", "rb").read())
|
66
|
66
|
self.wfile.close()
|
67
|
|
-
|
68
|
|
-
|
|
67
|
+
|
|
68
|
+
|
69
|
69
|
def fetch_url2(self, wfile, url, headers):
|
70
|
70
|
if DEBUG: print "\n***********************************************************"
|
71
|
71
|
self.log_message("fetch_url: %s", url)
|
72
|
72
|
#self.log_message("headers: %s", headers)
|
73
|
|
-
|
|
73
|
+
|
74
|
74
|
base_url = "/".join(url.split("/")[0:-1])
|
75
|
75
|
if base_url not in sessions:
|
76
|
76
|
sessions[base_url] = requests.Session()
|
77
|
77
|
#cj = cookielib.CookieJar()
|
78
|
|
- #sessions[base_url] = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
|
|
78
|
+ #sessions[base_url] = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
|
79
|
79
|
else:
|
80
|
80
|
pass
|
81
|
81
|
ses = sessions[base_url]
|
|
@@ -90,7 +90,7 @@ class StreamHandler(BaseHTTPRequestHandler):
|
90
|
90
|
code = r.status_code #r.status_code
|
91
|
91
|
if DEBUG: print "**Response:", code #r.status_code
|
92
|
92
|
if DEBUG: print "**Response headers: "
|
93
|
|
- for h in r.headers:
|
|
93
|
+ for h in r.headers:
|
94
|
94
|
if DEBUG: print h,"=",r.headers[h]
|
95
|
95
|
self.send_response(code)
|
96
|
96
|
if DEBUG: print "**Return headers:"
|
|
@@ -102,7 +102,7 @@ class StreamHandler(BaseHTTPRequestHandler):
|
102
|
102
|
self.send_header(h, r.headers[h])
|
103
|
103
|
if DEBUG:print h,"=",r.headers[h]
|
104
|
104
|
self.end_headers()
|
105
|
|
-
|
|
105
|
+
|
106
|
106
|
CHUNK_SIZE = 4 * 1024
|
107
|
107
|
if code == 200:
|
108
|
108
|
#while True:
|
|
@@ -115,20 +115,20 @@ class StreamHandler(BaseHTTPRequestHandler):
|
115
|
115
|
for chunk in r.iter_content(1024):
|
116
|
116
|
try:
|
117
|
117
|
#print "#",
|
118
|
|
- wfile.write(chunk)
|
|
118
|
+ wfile.write(chunk)
|
119
|
119
|
except Exception as e:
|
120
|
120
|
print "Exception: ", str(e)
|
121
|
121
|
return
|
122
|
122
|
if DEBUG: print " = file downloaded = "
|
123
|
123
|
time.sleep(2)
|
124
|
124
|
#self.wfile.close()
|
125
|
|
-
|
126
|
|
-
|
|
125
|
+
|
|
126
|
+
|
127
|
127
|
else:
|
128
|
128
|
print code
|
129
|
129
|
self.fetch_offline(wfile)
|
130
|
130
|
pass
|
131
|
|
-
|
|
131
|
+
|
132
|
132
|
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
|
133
|
133
|
"""Handle requests in a separate thread."""
|
134
|
134
|
|
|
@@ -156,33 +156,33 @@ class Daemon:
|
156
|
156
|
|
157
|
157
|
def daemonize(self):
|
158
|
158
|
"""
|
159
|
|
- do the UNIX double-fork magic, see Stevens' "Advanced
|
|
159
|
+ do the UNIX double-fork magic, see Stevens' "Advanced
|
160
|
160
|
Programming in the UNIX Environment" for details (ISBN 0201563177)
|
161
|
161
|
http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
|
162
|
162
|
"""
|
163
|
|
- try:
|
164
|
|
- pid = os.fork()
|
|
163
|
+ try:
|
|
164
|
+ pid = os.fork()
|
165
|
165
|
if pid > 0:
|
166
|
166
|
# exit first parent
|
167
|
|
- sys.exit(0)
|
168
|
|
- except OSError, e:
|
|
167
|
+ sys.exit(0)
|
|
168
|
+ except OSError, e:
|
169
|
169
|
sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
|
170
|
170
|
sys.exit(1)
|
171
|
171
|
|
172
|
172
|
# decouple from parent environment
|
173
|
|
- os.chdir("/")
|
174
|
|
- os.setsid()
|
175
|
|
- os.umask(0)
|
|
173
|
+ os.chdir("/")
|
|
174
|
+ os.setsid()
|
|
175
|
+ os.umask(0)
|
176
|
176
|
|
177
|
177
|
# do second fork
|
178
|
|
- try:
|
179
|
|
- pid = os.fork()
|
|
178
|
+ try:
|
|
179
|
+ pid = os.fork()
|
180
|
180
|
if pid > 0:
|
181
|
181
|
# exit from second parent
|
182
|
|
- sys.exit(0)
|
183
|
|
- except OSError, e:
|
|
182
|
+ sys.exit(0)
|
|
183
|
+ except OSError, e:
|
184
|
184
|
sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
|
185
|
|
- sys.exit(1)
|
|
185
|
+ sys.exit(1)
|
186
|
186
|
|
187
|
187
|
# redirect standard file descriptors
|
188
|
188
|
sys.stdout.flush()
|
|
@@ -240,7 +240,7 @@ class Daemon:
|
240
|
240
|
sys.stderr.write(message % self.pidfile)
|
241
|
241
|
return # not an error in a restart
|
242
|
242
|
|
243
|
|
- # Try killing the daemon process
|
|
243
|
+ # Try killing the daemon process
|
244
|
244
|
try:
|
245
|
245
|
while 1:
|
246
|
246
|
os.kill(pid, SIGTERM)
|
|
@@ -272,7 +272,7 @@ class ProxyDaemon(Daemon):
|
272
|
272
|
start()
|
273
|
273
|
|
274
|
274
|
if __name__ == "__main__":
|
275
|
|
- daemon = ProxyDaemon("/var/run/streamproxy.pid")
|
|
275
|
+ daemon = ProxyDaemon("/var/run/playstreamproxy.pid")
|
276
|
276
|
if len(sys.argv) == 2:
|
277
|
277
|
if "start" == sys.argv[1]:
|
278
|
278
|
daemon.start()
|