Advertisement
Guest User

Untitled

a guest
Jun 1st, 2017
388
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.40 KB | None | 0 0
  1. #!/usr/bin/env python
  2. #
  3. # Copyright (c) 2015 andrew0
  4. #
  5. # This software is provided 'as-is', without any express or implied
  6. # warranty. In no event will the authors be held liable for any damages
  7. # arising from the use of this software.
  8. #
  9. # Permission is granted to anyone to use this software for any purpose,
  10. # including commercial applications, and to alter it and redistribute it
  11. # freely, subject to the following restrictions:
  12. #
  13. # 1. The origin of this software must not be misrepresented; you must not
  14. # claim that you wrote the original software. If you use this software
  15. # in a product, an acknowledgment in the product documentation would be
  16. # appreciated but is not required.
  17. #
  18. # 2. Altered source versions must be plainly marked as such, and must not be
  19. # misrepresented as being the original software.
  20. #
  21. # 3. This notice may not be removed or altered from any source distribution.
  22. #
  23.  
  24. import sys
  25. import time
  26. import BaseHTTPServer
  27. import requests
  28. import base64
  29. import json
  30. import datetime
  31. from channels import *
  32.  
  33. SIRIUS_USERNAME = ''
  34. SIRIUS_PASSWORD = ''
  35.  
  36. class SiriusXM:
  37. BASE_URL = 'https://player.siriusxm.com/rest/v1/experience/modules'
  38. HLS_BASE_URL = 'http://primary.hls-streaming.production.streaming.siriusxm.com/AAC_Data'
  39. USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36'
  40.  
  41. def __init__(self, username, password):
  42. self.session = requests.Session()
  43. self.session.headers.update({'User-Agent': self.USER_AGENT})
  44. self.username = username
  45. self.password = password
  46. # self.login()
  47.  
  48. def login(self):
  49. url = '%s/modify/authentication' % (self.BASE_URL)
  50. postdata = {
  51. 'moduleList': {
  52. 'modules': [{
  53. 'moduleRequest': {
  54. 'resultTemplate': 'web',
  55. 'deviceInfo': {
  56. 'osVersion': 'Mac',
  57. 'platform': 'Web',
  58. 'sxmAppVersion': 'v.0316',
  59. 'browser': 'Chrome',
  60. 'browserVersion': '41.0.2272.101',
  61. 'deviceModel': 'K2WebClient',
  62. 'appRegion': 'US',
  63. 'clientDeviceId': 'null',
  64. },
  65. 'standardAuth': {
  66. 'username': self.username,
  67. 'password': self.password,
  68. },
  69. },
  70. }],
  71. },
  72. }
  73. res = self.session.post(url, data=json.dumps(postdata))
  74.  
  75. if res.status_code != 200:
  76. print '%s: Received status code %d ' % (sys._getframe().f_code.co_name, res.status_code)
  77. return False
  78.  
  79. try:
  80. return res.json()['ModuleListResponse']['status'] == 1
  81. except:
  82. return False
  83.  
  84. def get_auth_token(self, channel_number):
  85. channel_id = get_channel_id(channel_number)
  86. if channel_id is None:
  87. return False
  88.  
  89. ts = int(round(time.time() * 1000))
  90. rfc3339 = datetime.datetime.utcnow().isoformat('T') + 'Z'
  91. url = '%s/tune/now-playing-live?ccRequestType=AUDIO_VIDEO&hls_output_mode=custom&id=%s&marker_mode=all_separate_cue_points&result-template=web&time=%d&timestamp=%s' % (self.BASE_URL, channel_id, ts, rfc3339)
  92. res = self.session.get(url)
  93.  
  94. if res.status_code != 200:
  95. print '%s: Received status code %d ' % (sys._getframe().f_code.co_name, res.status_code)
  96. return False
  97.  
  98. # login if session expired
  99. try:
  100. if res.json()['ModuleListResponse']['status'] == 0:
  101. print 'Session expired, logging in'
  102. if self.login():
  103. print 'Successfully logged in'
  104. return self.get_auth_token(channel_number)
  105. else:
  106. print 'Unable to login'
  107. return False
  108. except:
  109. return False
  110.  
  111. return 'SXMAKTOKEN' in res.cookies
  112.  
  113. def get_playlist(self, channel_number):
  114. channel_id = get_channel_id(channel_number)
  115. if channel_id is None:
  116. return False
  117.  
  118. url = '%s/%s/HLS_%s_256k_v3/%s_256k_large_v3.m3u8' % (self.HLS_BASE_URL, channel_id, channel_id, channel_id)
  119. res = self.session.get(url)
  120. if res.status_code == 200:
  121. return res
  122. elif res.status_code == 403:
  123. print 'Received 403 Forbidden, renewing token'
  124. if self.get_auth_token(channel_number):
  125. return self.get_playlist(channel_number)
  126. else:
  127. return None
  128. elif res.status_code == 503:
  129. time.sleep(3)
  130. return self.get_playlist(channel_number)
  131. else:
  132. return None
  133.  
  134. def get_segment(self, name):
  135. channel_id = name.split('_', 1)[0]
  136. url = '%s/%s/HLS_%s_256k_v3/%s' % (self.HLS_BASE_URL, channel_id, channel_id, name)
  137. res = self.session.get(url, stream=True)
  138. if res.status_code == 200:
  139. return res
  140. elif res.status_code == 403:
  141. print 'Received 403 Forbidden, renewing token'
  142. if self.get_auth_token(get_channel_number(channel_id)):
  143. return self.get_segment(name)
  144. else:
  145. return None
  146. elif res.status_code == 503:
  147. time.sleep(3)
  148. return self.get_segment(name)
  149. else:
  150. return None
  151.  
  152. class SiriusHandler(BaseHTTPServer.BaseHTTPRequestHandler):
  153. HLS_AES_KEY = base64.b64decode('0Nsco7MAgxowGvkUT8aYag==')
  154. sxm = SiriusXM(SIRIUS_USERNAME, SIRIUS_PASSWORD)
  155.  
  156. def handle_key(self):
  157. self.send_response(200)
  158. self.send_header('Content-Type', 'text/plain')
  159. self.end_headers()
  160. self.wfile.write(self.HLS_AES_KEY)
  161.  
  162. def handle_playlist(self):
  163. channel_number = self.path.split('/')[-1][:-5]
  164. res = self.sxm.get_playlist(channel_number)
  165.  
  166. if res is not None and res.status_code == 404:
  167. print 'Channel %d not found' % (channel_number)
  168. self.send_response(404)
  169. self.end_headers()
  170. return True
  171. elif res is not None and res.status_code == 200:
  172. self.send_response(200)
  173. self.send_header('Content-Type', 'application/x-mpegURL')
  174. self.end_headers()
  175. self.wfile.write(res.text)
  176. return True
  177.  
  178. return False
  179.  
  180. def handle_segment(self):
  181. res = None
  182. data = None
  183.  
  184. for attempts in range(3):
  185. try:
  186. res = self.sxm.get_segment(self.path[1:])
  187. data = res.raw.read() if res is not None and res.status_code == 200 else None
  188. break
  189. except:
  190. # try one more time
  191. print 'Received IncompleteRead exception, trying again'
  192.  
  193. if res is None or data is None:
  194. return False
  195.  
  196. if res.status_code == 404:
  197. print 'Segment %s not found' % (self.path[1:])
  198. self.send_response(404)
  199. self.end_headers()
  200. return True
  201. elif res.status_code == 200:
  202. self.send_response(200)
  203. self.send_header('Content-Type', 'audio/x-aac')
  204. self.end_headers()
  205. self.wfile.write(data)
  206. return True
  207.  
  208. return False
  209.  
  210. def do_GET(self):
  211. '''Respond to a GET request.'''
  212. try:
  213. if self.path.find('/key/') != -1:
  214. self.handle_key()
  215. return
  216. elif self.path.endswith('.m3u8'):
  217. if self.handle_playlist():
  218. return
  219. elif self.path.endswith('.aac'):
  220. if self.handle_segment():
  221. return
  222. else:
  223. self.send_response(404)
  224. self.end_headers()
  225. return
  226.  
  227. self.send_response(500)
  228. self.end_headers()
  229. except:
  230. print 'Received broken pipe, ignoring...'
  231.  
  232. if __name__ == '__main__':
  233. httpd = BaseHTTPServer.HTTPServer(('', 9001), SiriusHandler)
  234. try:
  235. httpd.serve_forever()
  236. except KeyboardInterrupt:
  237. pass
  238. httpd.server_close()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement