def handle_request(self, request, params): self.ensure_request_possible(request, params[0]) logging.debug("DBClient: handling %s request params %s", request, str(params)) if request == "metadata": return self.query_list(*params[:2], None, [params[2]], "metadata_request") elif request == "root_menu": return self.query_list(*params, None, [], "root_menu_request") elif request == "title": return self.query_list(*params, [], "title_request") elif request == "title_by_album": return self.query_list(*params, "title_by_album_request") elif request == "title_by_artist_album": return self.query_list(*params, "title_by_artist_album_request") elif request == "title_by_genre_artist_album": return self.query_list(*params, "title_by_genre_artist_album_request") elif request == "artist": return self.query_list(*params, None, [], "artist_request") elif request == "artist_by_genre": return self.query_list(*params[:2], None, params[2], "artist_by_genre_request") elif request == "album": return self.query_list(*params, None, [], "album_request") elif request == "album_by_artist": return self.query_list(*params[:2], None, params[2], "album_by_artist_request") elif request == "album_by_genre_artist": return self.query_list(*params[:2], None, params[2], "album_by_genre_artist_request") elif request == "genre": return self.query_list(*params, None, [], "genre_request") elif request == "playlist_folder": return self.query_list(*params[:2], None, [params[2], 0], "playlist_request") elif request == "playlist": return self.query_list(*params[:3], [0, params[3]], "playlist_request") elif request == "artwork": return self.query_blob(*params, "artwork_request") elif request == "waveform": waveform = self.query_blob(*params, "waveform_request", 1) return None if waveform is None else waveform[20:] elif request == "preview_waveform": return self.query_blob(*params, "preview_waveform_request") elif request == "color_waveform": blob = self.query_blob(*params, "color_waveform_request", 1) return None if blob is None else AnlzTag.parse(blob[4:]).content.entries elif request == "color_preview_waveform": blob = self.query_blob(*params, "color_preview_waveform_request") return None if blob is None else AnlzTag.parse(blob[4:]).content.entries elif request == "beatgrid": reply = self.query_blob(*params, "beatgrid_request") if reply is None: return None try: # pre-parse beatgrid data (like metadata) for easier access return packets.Beatgrid.parse(reply)["beats"] except (RangeError, FieldError) as e: raise dataprovider.FatalQueryError("DBClient: failed to parse beatgrid data: {}".format(e)) elif request == "mount_info": return self.query_list(*params[:2], None, [params[2]], "mount_info_request") elif request == "track_info": return self.query_list(*params[:2], None, [params[2]], "track_info_request") else: raise dataprovider.FatalQueryError("DBClient: invalid request type {}".format(request))
def download_and_parse_pdb(self, player_number, slot): filename = self.download_pdb(player_number, slot) db = PDBDatabase() try: db.load_file(filename) except RuntimeError as e: raise dataprovider.FatalQueryError( "PDBFile: failed to parse \"{}\": {}".format(filename, e)) return db
def download_pdb(self, player_number, slot): player = self.prodj.cl.getClient(player_number) if player is None: raise dataprovider.FatalQueryError( "PDBProvider: player {} not found in clientlist".format( player_number)) filename = "databases/player-{}-{}.pdb".format(player_number, slot) self.delete_pdb(filename) try: self.prodj.nfs.enqueue_download(player.ip_addr, slot, "/PIONEER/rekordbox/export.pdb", filename, sync=True) except RuntimeError as e: raise dataprovider.FatalQueryError( "PDBProvider: database download from player {} failed: {}". format(player_number, e)) return filename
def get_artwork(self, player_number, slot, artwork_id): player = self.prodj.cl.getClient(player_number) if player is None: raise dataprovider.FatalQueryError( "PDBProvider: player {} not found in clientlist".format( player_number)) db = self.get_db(player_number, slot) artwork = db.get_artwork(artwork_id) return self.prodj.nfs.enqueue_buffer_download(player.ip_addr, slot, artwork.path)
def socksnd(self, sock, data): try: sock.send(data) except BrokenPipeError as e: player_number = next((n for n, d in self.socks.items() if d[0] == sock), None) if player_number is None: raise dataprovider.FatalQueryError("socksnd failed with unknown sock") else: self.closeSocket(player_number) raise dataprovider.TemporaryQueryError("Connection to player {} lost".format(player_number))
def convert_and_sort_track_list(self, db, track_list, sort_mode): converted = [] # we do not know the default sort mode from pdb, thus fall back to title if sort_mode in ["title", "default"]: col2_name = "artist" else: col2_name = sort_mode for track in track_list: if col2_name in ["title", "artist"]: col2_item = db.get_artist( track.artist_id).name if track.artist_id > 0 else "" elif col2_name == "album": col2_item = db.get_album( track.album_id).name if track.album_id > 0 else "" elif col2_name == "genre": col2_item = db.get_genre( track.genre_id).name if track.genre_id > 0 else "" elif col2_name == "label": col2_item = db.get_label( track.label_id).name if track.label_id > 0 else "" elif col2_name == "original_artist": col2_item = db.get_artist( track.original_artist_id ).name if track.original_artist_id > 0 else "" elif col2_name == "remixer": col2_item = db.get_artist( track.remixer_id).name if track.remixer_id > 0 else "" elif col2_name == "key": col2_item = db.get_key( track.key_id).name if track.key_id > 0 else "" elif col2_name == "bpm": col2_item = track.bpm_100 / 100 elif col2_name in [ "rating", "comment", "duration", "bitrate", "play_count" ]: # 1:1 mappings col2_item = track[col2_name] else: raise dataprovider.FatalQueryError( "PDBProvider: unknown sort mode {}".format(sort_mode)) converted += [{ "title": track.title, col2_name: col2_item, "track_id": track.id, "artist_id": track.artist_id, "album_id": track.album_id, "artwork_id": track.artwork_id, "genre_id": track.genre_id }] if sort_mode == "default": return converted else: return sorted(converted, key=lambda key: key[sort_mode], reverse=sort_mode == "rating")
def download_and_parse_usbanlz(self, player_number, slot, anlz_path): player = self.prodj.cl.getClient(player_number) if player is None: raise dataprovider.FatalQueryError( "PDBProvider: player {} not found in clientlist".format( player_number)) dat = self.prodj.nfs.enqueue_buffer_download(player.ip_addr, slot, anlz_path) ext = self.prodj.nfs.enqueue_buffer_download( player.ip_addr, slot, anlz_path.replace("DAT", "EXT")) db = UsbAnlzDatabase() db.load_dat_buffer(dat) db.load_ext_buffer(ext) return db
def handle_request(self, request, params): logging.debug("PDBProvider: handling %s request params %s", request, str(params)) if request == "metadata": return self.get_metadata(*params) elif request == "root_menu": return self.get_root_menu() elif request == "title": return self.get_titles(*params) elif request == "title_by_album": return self.get_titles(*params) elif request == "title_by_artist_album": return self.get_titles(*params) elif request == "title_by_genre_artist_album": return self.get_titles(*params) elif request == "artist": return self.get_artists(*params) elif request == "artist_by_genre": return self.get_artists(*params) elif request == "album": return self.get_albums(*params) elif request == "album_by_artist": return self.get_albums(*params) elif request == "album_by_genre_artist": return self.get_albums(*params) elif request == "genre": return self.get_genres(*params) elif request == "playlist_folder": return self.get_playlists(*params) elif request == "playlist": return self.get_playlist(*params) elif request == "artwork": return self.get_artwork(*params) elif request == "waveform": return self.get_waveform(*params) elif request == "preview_waveform": return self.get_preview_waveform(*params) elif request == "color_waveform": return self.get_color_waveform(*params) elif request == "color_preview_waveform": return self.get_color_preview_waveform(*params) elif request == "beatgrid": return self.get_beatgrid(*params) elif request == "mount_info": return self.get_mount_info(*params) else: raise dataprovider.FatalQueryError( "PDBProvider: invalid request type {}".format(request))
def get_artwork(self, player_number, slot, artwork_id): player = self.prodj.cl.getClient(player_number) if player is None: raise dataprovider.FatalQueryError( "PDBProvider: player {} not found in clientlist".format( player_number)) db = self.get_db(player_number, slot) try: artwork = db.get_artwork(artwork_id) except KeyError as e: logging.warning( "PDBProvider: No artwork for {}, returning empty data".format( (player_number, slot, artwork_id))) return None return self.prodj.nfs.enqueue_buffer_download(player.ip_addr, slot, artwork.path)
def query_list(self, player_number, slot, sort_mode, id_list, request_type): sock = self.getSocket(player_number) slot_id = byte2int( packets.PlayerSlot.build(slot)) if slot is not None else 0 if sort_mode is None: sort_id = 0 # 0 for root_menu, playlist folders else: if sort_mode not in sort_types: logging.warning("DBClient: unknown sort mode %s", sort_mode) return None sort_id = sort_types[sort_mode] query = { "transaction_id": self.getTransactionId(player_number), "type": request_type, "args": [{ "type": "int32", "value": self.own_player_number << 24 | 1 << 16 | slot_id << 8 | 1 }] } # request-specific argument agumentations if request_type == "root_menu_request": query["args"].append({"type": "int32", "value": 0}) query["args"].append({"type": "int32", "value": 0xffffff}) elif request_type in [ "metadata_request", "track_data_request", "track_info_request" ]: query["args"].append({"type": "int32", "value": id_list[0]}) elif request_type == "playlist_request": query["args"].append({"type": "int32", "value": sort_id}) query["args"].append({ "type": "int32", "value": id_list[1] if id_list[1] > 0 else id_list[0] }) query["args"].append({ "type": "int32", "value": 0 if id_list[1] > 0 else 1 }) # 1 -> get folder, 0 -> get playlist else: # for any (non-playlist) "*_by_*_request" query["args"].append({"type": "int32", "value": sort_id}) for item_id in id_list: if item_id == 0: # we use id 0 for "ALL", dbserver expects all bits set item_id = 0xffffffff query["args"].append({"type": "int32", "value": item_id}) data = packets.DBMessage.build(query) logging.debug("DBClient: query_list request: {}".format(query)) self.socksnd(sock, data) try: reply = self.receive_dbmessage(sock) except (RangeError, MappingError, KeyError) as e: logging.error( "DBClient: parsing %s query failed on player %d failed: %s", query["type"], player_number, str(e)) return None if reply is None or reply["type"] != "success": logging.error("DBClient: %s failed on player %d (got %s)", query["type"], player_number, "NONE" if reply is None else reply["type"]) return None entry_count = reply["args"][1]["value"] if entry_count == 0: logging.warning("DBClient: %s empty (0 entries)", request_type) return [] logging.debug("DBClient: query_list %s: %d entries available", request_type, entry_count) # i could successfully receive hundreds of entries at once on xdj 1000 # thus i do not fragment render requests here query = { "transaction_id": self.getTransactionId(player_number), "type": "render", "args": [ { "type": "int32", "value": self.own_player_number << 24 | 1 << 16 | slot_id << 8 | 1 }, { "type": "int32", "value": 0 }, # entry offset { "type": "int32", "value": entry_count }, # entry count { "type": "int32", "value": 0 }, { "type": "int32", "value": entry_count }, # entry count { "type": "int32", "value": 0 } ] } data = packets.DBMessage.build(query) logging.debug("DBClient: render query {}".format(query)) self.socksnd(sock, data) parse_errors = 0 receive_timeouts = 0 data = b"" while parse_errors < self.parse_error_count and receive_timeouts < self.receive_timeout_count: new_data = sockrcv(sock, 4096, 1) if len(new_data) == 0: receive_timeouts += 1 continue data += new_data try: reply = packets.ManyDBMessages.parse(data) except (RangeError, FieldError, MappingError, KeyError, TypeError) as e: logging.debug( "DBClient: failed to parse %s render reply (%d bytes), trying to receive more", request_type, len(data)) parse_errors += 1 else: if reply[-1]["type"] != "menu_footer": logging.debug( "DBClient: %s rendering without menu_footer @ %d bytes, trying to receive more", request_type, len(data)) parse_errors += 1 else: break if parse_errors >= self.parse_error_count or receive_timeouts >= self.receive_timeout_count: raise dataprovider.FatalQueryError( "DBClient: Failed to receive {} render reply after {} timeouts, {} parse errors" .format(request_type, receive_timeouts, parse_errors)) # basically, parse_metadata returns a single dict whereas parse_list returns a list of dicts if request_type in [ "metadata_request", "mount_info_request", "track_info_request" ]: parsed = self.parse_metadata(reply) else: parsed = self.parse_list(reply) return parsed