Last active
November 14, 2018 19:52
-
-
Save rctay/9065133 to your computer and use it in GitHub Desktop.
SoundCloud set/playlist shuffle via Python + Requests + mpg123. Ctrl-C for next (press Ctrl-C in quick succession to quit), Ctrl-Z/fg to pause/resume
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import requests | |
| import soundcloud | |
| import random | |
| import socket | |
| import subprocess | |
| SOUNDCLOUD_API_KEY = '00000000000000000000000000000000' | |
| PLAYLIST_URL = 'https://soundcloud.com/nervomusic/sets/nervomusic-com' | |
| client = soundcloud.Client(client_id=SOUNDCLOUD_API_KEY) | |
| playlist = client.get('/resolve', url=PLAYLIST_URL) | |
| class Player(object): | |
| def __init__(self): | |
| self.p = None | |
| self.reset() | |
| def reset(self): | |
| if self.p: | |
| self.close() | |
| self.p = subprocess.Popen(['mpg123', '-'], stdin=subprocess.PIPE) | |
| def write(self, chunk): | |
| self.p.stdin.write(chunk) | |
| def close(self): | |
| self.p.stdin.close() | |
| self.p.wait() | |
| p = Player() | |
| CHUNK_SZ = 512 | |
| while True: | |
| try: | |
| random.shuffle(playlist.tracks) | |
| for track in playlist.tracks: | |
| print track['title'] | |
| done = False | |
| first = True | |
| pos, sz = None, None | |
| while not done: | |
| headers = {} | |
| if pos > 0: | |
| headers['Range'] = "bytes=%d-" % pos | |
| try: | |
| # go through soundcloud api always, in case stream url expires | |
| stream = client.get(track['stream_url'], allow_redirects=False) | |
| req = requests.get(stream.location, stream=True, headers=headers) | |
| except requests.exceptions.HTTPError as e: | |
| print "stream got error", str(e) | |
| done = True | |
| continue | |
| if first: | |
| first = False | |
| sz = int(req.headers['content-length']) | |
| pos = 0 | |
| try: | |
| for chunk in req.iter_content(CHUNK_SZ): | |
| pos += len(chunk) | |
| try: | |
| p.write(chunk) | |
| except IOError as e: | |
| import errno | |
| if e.errno in [errno.EPIPE, errno.EINVAL]: | |
| print "broken pipe" | |
| p.reset() | |
| else: | |
| print "io error", str(e) | |
| raise | |
| # in case the stream may be exhausted but we have not read the whole | |
| # response | |
| done = pos >= sz | |
| if not done: | |
| print "more to read, retrying" | |
| except socket.error as e: | |
| print str(e), "retrying..." | |
| done = False | |
| except KeyboardInterrupt: | |
| # behaves like a next | |
| done = True | |
| req.close() | |
| except KeyboardInterrupt: | |
| break | |
| p.close() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment