class Spotify(): def __init__(self, username, password): self.api = SpotifyAPI() self.api.connect(username, password) def logged_in(self): return self.api.logged_in def logout(self): self.api.disconnect() @Cache def getPlaylists(self, username = None): username = self.api.username if username == None else username playlist_uris = [] if username == self.api.username: playlist_uris += ["spotify:user:"******":starred"] playlist_uris += [playlist.uri for playlist in self.api.playlists_request(username).contents.items] return [self.objectFromURI(playlist_uri) for playlist_uri in playlist_uris] def search(self, query): return self.api.search_request(query) def objectFromInternalObj(self, object_type, objs, nameOnly = False): if nameOnly: return ", ".join([obj.name for obj in objs]) try: uris = [SpotifyUtil.gid2uri(object_type, obj.gid) for obj in objs] except: uris = SpotifyUtil.gid2uri(object_type, objs.gid) return self.objectFromURI(uris) def objectFromURI(self, uris): if self.logged_in() == False: return False uris = [uris] if type(uris) != list else uris uri_type = SpotifyUtil.get_uri_type(uris[0]) if uri_type == False: return None elif uri_type == "playlist": results = [SpotifyPlaylist(self, uri=uri) for uri in uris] elif uri_type in ["track", "album", "artist"]: uris = [uri for uri in uris if not SpotifyUtil.is_local(uri)] objs = self.api.metadata_request(uris) objs = [objs] if type(objs) != list else objs if uri_type == "track": results = [SpotifyTrack(self, obj=obj) for obj in objs] elif uri_type == "album": results = [SpotifyAlbum(self, obj=obj) for obj in objs] elif uri_type == "artist": results = [SpotifyArtist(self, obj=obj) for obj in objs] else: return None if len(results) == 1: return results[0] else: return results @staticmethod def imagesFromArray(image_objs): images = {} for image_obj in image_objs: size = str(image_obj.width) images[size] = "https://d3rt1990lpmkn.cloudfront.net/" + size + "/" + SpotifyUtil.gid2id(image_obj.file_id) return images
class Spotify(): AUTOREPLACE_TRACKS = True def __init__(self, username, password): self.api = SpotifyAPI() self.api.connect(username, password) def logged_in(self): return self.api.is_logged_in and not self.api.disconnecting def logout(self): self.api.disconnect() @Cache def getPlaylists(self, username = None): username = self.api.username if username == None else username playlist_uris = [] if username == self.api.username: playlist_uris += ["spotify:user:"******":starred"] playlist_uris += [playlist.uri for playlist in self.api.playlists_request(username).contents.items] return self.objectFromURI(playlist_uris) def newPlaylist(self, name): self._Cache__cache = {} uri = self.api.new_playlist(name) return SpotifyPlaylist(self, uri=uri) def removePlaylist(self, playlist): self._Cache__cache = {} return self.api.remove_playlist(playlist.getURI()) def getUserToplist(self, toplist_content_type = "track", username = None): return SpotifyToplist(self, toplist_content_type, "user", username, None) def getRegionToplist(self, toplist_content_type = "track", region = None): return SpotifyToplist(self, toplist_content_type, "region", None, region) def search(self, query, query_type = "all", max_results = 50, offset = 0): return SpotifySearch(self, query, query_type=query_type, max_results=max_results, offset=offset) def objectFromInternalObj(self, object_type, objs, nameOnly = False): if nameOnly: return ", ".join([obj.name for obj in objs]) try: uris = [SpotifyUtil.gid2uri(object_type, obj.gid) for obj in objs] except: uris = SpotifyUtil.gid2uri(object_type, objs.gid) return self.objectFromURI(uris, asArray = True) def objectFromID(self, object_type, ids): try: uris = [SpotifyUtil.id2uri(object_type, id) for id in ids] except: uris = SpotifyUtil.id2uri(object_type, ids) return self.objectFromURI(uris, asArray = True) @Cache def objectFromURI(self, uris, asArray = False): if self.logged_in() == False: return False uris = [uris] if type(uris) != list else uris if len(uris) == 0: return [] if asArray else None uri_type = SpotifyUtil.get_uri_type(uris[0]) if uri_type == False: return None elif uri_type == "playlist": if len(uris) == 1: results = [SpotifyPlaylist(self, uri=uris[0])] else: thread_results = {} jobs = [] for index in range(0, len(uris)): jobs.append((self, uris[index], thread_results, index)) def work_function(spotify, uri, results, index): results[index] = SpotifyPlaylist(spotify, uri=uri) Spotify.doWorkerQueue(work_function, jobs) results = [v for k, v in thread_results.items()] elif uri_type in ["track", "album", "artist"]: uris = [uri for uri in uris if not SpotifyUtil.is_local(uri)] objs = self.api.metadata_request(uris) objs = [objs] if type(objs) != list else objs failed_requests = len([obj for obj in objs if obj == False]) if failed_requests > 0: print failed_requests,"metadata requests failed" objs = [obj for obj in objs if obj != False] if uri_type == "track": tracks = [SpotifyTrack(self, obj=obj) for obj in objs] results = [track for track in tracks if self.AUTOREPLACE_TRACKS == False or track.isAvailable()] elif uri_type == "album": results = [SpotifyAlbum(self, obj=obj) for obj in objs] elif uri_type == "artist": results = [SpotifyArtist(self, obj=obj) for obj in objs] else: return None if asArray == False: if len(results) == 1: results = results[0] elif len(results) == 0: return None return results @staticmethod def doWorkerQueue(work_function, args, worker_thread_count = 5): def worker(): while not q.empty(): args = q.get() work_function(*args) q.task_done() q = Queue() for arg in args: q.put(arg) for i in range(worker_thread_count): t = Thread(target=worker) t.start() q.join() @staticmethod def imagesFromArray(image_objs): images = {} for image_obj in image_objs: size = str(image_obj.width) images[size] = "https://d3rt1990lpmkn.cloudfront.net/" + size + "/" + SpotifyUtil.gid2id(image_obj.file_id) return images
class AlbumCards: def kill_player(self): if self.mpg123: log("stopping playback") # sometimes takes two SIGINTs to promptly stop mpg123 # (and KILL/QUIT hands the RPi) self.mpg123.send_signal(signal.SIGINT) time.sleep(0.1) self.mpg123.send_signal(signal.SIGINT) self.mpg123.wait() self.mpg123 = None def kill_poller(self): if self.poller: self.poller.send_signal(signal.SIGINT) # KILL/QUIT hangs the RPi self.poller.wait() self.poller = None def play_stream(self, uri): log("playing %s" % uri) self.kill_player() self.mpg123 = subprocess.Popen(["mpg123", "-q", "-b", "0", "-a", self.config.defaults().get("audio"), uri]) def play_track_uri(self, track_uri): if track_uri.startswith("file://"): self.play_stream(track_uri[7:]) else: log("fetching metadata for %s" % track_uri) track = self.sp.metadata_request(track_uri) log("fetching playback URI for %s" % track.name) self.sp.track_uri(track, lambda sp, result: self.play_stream(result["uri"])) def play_tag(self, tag): if tag == "0": log("no tag present") else: log("tag %s present" % tag) if self.now_playing == tag: return self.now_playing = tag if tag == "0": self.kill_player() return try: track_uri = config.get("tags", tag) self.play_track_uri(track_uri) self.now_playing = tag except ConfigParser.Error: print "unknown tag: %s" % (tag) def poll_nfc(self): log("starting nfc-poll") self.poller = subprocess.Popen( self.config.defaults().get("poller"), stdout=subprocess.PIPE, stderr=subprocess.PIPE ) log("spawned nfc-poll as process %d" % self.poller.pid) while True: line = self.poller.stdout.readline() if line == None or line == "": break m = re.search("UID.*:(.*)", line) if m: self.play_tag(m.group(1).replace(" ", "")) self.poller.wait() log("nfc-poll exited with %s" % self.poller.returncode) def login_callback(self, sp, logged_in): if not logged_in: print "%s: error logging in" % sys.argv[0] sys.exit(1) def __init__(self, config): self.mpg123 = None self.poller = None self.config = config self.now_playing = None atexit.register(lambda: self.kill_player()) atexit.register(lambda: self.kill_poller()) def start(self): log("connecting...") # login_callback(None, True) self.sp = SpotifyAPI(lambda sp, logged_in: self.login_callback(sp, logged_in)) # self.sp.connect(config.defaults().get("username"), config.defaults().get("password")) self.poll_nfc() self.sp.disconnect()