Skip to content
This repository has been archived by the owner on Jul 6, 2021. It is now read-only.

Commit

Permalink
fixed status code check (thanks @alexj212)
Browse files Browse the repository at this point in the history
refactored confusing code
added option to list channels
  • Loading branch information
andrew authored and andrew committed Dec 7, 2018
1 parent e472905 commit 0726400
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 29 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
# SiriusXM

This script creates a server that serves HLS streams for SiriusXM channels. To use it, pass your SiriusXM username and password and a port to run the server on. For example, you start the server by running:
`python sxm.py myuser mypassword 8888`
`python3 sxm.py myuser mypassword -p 8888`

You can see a list of the channels by setting the -l or --list flag:
`python3 sxm.py myuser mypassword -l`

Then in a player that supports HLS (QuickTime, VLC, ffmpeg, etc) you can access a channel at http://127.0.0.1:8888/channel.m3u8 where "channel" is the channel name, ID, or Sirius channel number.

Expand Down
78 changes: 50 additions & 28 deletions sxm.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import argparse
import requests
import base64
import urllib.parse
Expand Down Expand Up @@ -185,7 +186,7 @@ def get_playlist_url(self, guid, channel_id, use_cache=True, max_attempts=5):
else:
self.log('Reached max attempts for playlist')
return None
elif status == 0:
elif message_code != 100:
self.log('Received error {} {}'.format(message_code, message))
return None

Expand Down Expand Up @@ -214,9 +215,13 @@ def get_playlist_variant_url(self, url):
if res.status_code != 200:
self.log('Received status code {} on playlist variant retrieval'.format(res.status_code))
return None

variant = next(filter(lambda x: x.endswith('.m3u8'), map(lambda x: x.rstrip(), res.text.split('\n'))), None)
return '{}/{}'.format(url.rsplit('/', 1)[0], variant) if variant else None

for x in res.text.split('\n'):
if x.rstrip().endswith('.m3u8'):
# first variant should be 256k one
return '{}/{}'.format(url.rsplit('/', 1)[0], x.rstrip())

return None

def get_playlist(self, name, use_cache=True):
guid, channel_id = self.get_channel(name)
Expand All @@ -241,13 +246,12 @@ def get_playlist(self, name, use_cache=True):
return None

# add base path to segments
lines = list(map(lambda x: x.rstrip(), res.text.split('\n')))
base_url = url.rsplit('/', 1)[0]
base_path = base_url[8:].split('/', 1)[1]
lines = res.text.split('\n')
for x in range(len(lines)):
line = lines[x].rstrip()
if line.endswith('.aac'):
base_url = url.rsplit('/', 1)[0]
base_path = base_url[8:].split('/', 1)[1]
lines[x] = '{}/{}'.format(base_path, line)
if lines[x].rstrip().endswith('.aac'):
lines[x] = '{}/{}'.format(base_path, lines[x])
return '\n'.join(lines)

def get_segment(self, path, max_attempts=5):
Expand All @@ -273,8 +277,8 @@ def get_segment(self, path, max_attempts=5):
return None

return res.content

def get_channel(self, name):
def get_channels(self):
# download channel list if necessary
if not self.channels:
postdata = {
Expand All @@ -300,22 +304,23 @@ def get_channel(self, name):
self.channels = data['ModuleListResponse']['moduleList']['modules'][0]['moduleResponse']['contentData']['channelListing']['channels']
except (KeyError, IndexError):
self.log('Error parsing json response for channels')
return (None, None)
return []
return self.channels

def get_channel(self, name):
name = name.lower()
for x in self.channels:
for x in self.get_channels():
if x.get('name', '').lower() == name or x.get('channelId', '').lower() == name or x.get('siriusChannelNumber') == name:
return (x['channelGuid'], x['channelId'])
return (None, None)

def make_sirius_handler(username, password):
def make_sirius_handler(sxm):
class SiriusHandler(BaseHTTPRequestHandler):
HLS_AES_KEY = base64.b64decode('0Nsco7MAgxowGvkUT8aYag==')
sxm = SiriusXM(username, password)

def do_GET(self):
if self.path.endswith('.m3u8'):
data = self.sxm.get_playlist(self.path.rsplit('/', 1)[1][:-5])
data = sxm.get_playlist(self.path.rsplit('/', 1)[1][:-5])
if data:
self.send_response(200)
self.send_header('Content-Type', 'application/x-mpegURL')
Expand All @@ -325,7 +330,7 @@ def do_GET(self):
self.send_response(500)
self.end_headers()
elif self.path.endswith('.aac'):
data = self.sxm.get_segment(self.path[1:])
data = sxm.get_segment(self.path[1:])
if data:
self.send_response(200)
self.send_header('Content-Type', 'audio/x-aac')
Expand All @@ -345,13 +350,30 @@ def do_GET(self):
return SiriusHandler

if __name__ == '__main__':
if len(sys.argv) < 4:
print('usage: python sxm.py [username] [password] [port]')
sys.exit(1)

httpd = HTTPServer(('', int(sys.argv[3])), make_sirius_handler(sys.argv[1], sys.argv[2]))
try:
httpd.serve_forever()
except KeyboardInterrupt:
pass
httpd.server_close()
parser = argparse.ArgumentParser(description='SiriusXM proxy')
parser.add_argument('username')
parser.add_argument('password')
parser.add_argument('-l', '--list', required=False, action='store_true', default=False)
parser.add_argument('-p', '--port', required=False, default=9999, type=int)
args = vars(parser.parse_args())

sxm = SiriusXM(args['username'], args['password'])
if args['list']:
channels = list(sorted(sxm.get_channels(), key=lambda x: (not x.get('isFavorite', False), int(x.get('siriusChannelNumber', 9999)))))

l1 = max(len(x.get('channelId', '')) for x in channels)
l2 = max(len(str(x.get('siriusChannelNumber', 0))) for x in channels)
l3 = max(len(x.get('name', '')) for x in channels)
print('{} | {} | {}'.format('ID'.ljust(l1), 'Num'.ljust(l2), 'Name'.ljust(l3)))
for channel in channels:
cid = channel.get('channelId', '').ljust(l1)[:l1]
cnum = str(channel.get('siriusChannelNumber', '??')).ljust(l2)[:l2]
cname = channel.get('name', '??').ljust(l3)[:l3]
print('{} | {} | {}'.format(cid, cnum, cname))
else:
httpd = HTTPServer(('', args['port']), make_sirius_handler(sxm))
try:
httpd.serve_forever()
except KeyboardInterrupt:
pass
httpd.server_close()

1 comment on commit 0726400

@derttent
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dear andrew! How to connected with u to email?

Please # to comment.