def parse_line(line): orig_line = line try: line = line.split() if line[0] == "GET" and "?" in line[1]: parsed = urlparse(line[1]) args = parse_qs(parsed.query) if 'id' in args and args['id']: mbid = args['id'][0] if not mbid_validate(mbid): log.error("Browser integration failed: bad mbid %r", mbid) return False def load_it(loader): self.tagger.bring_tagger_front() loader(mbid) return True action = parsed.path if action == '/openalbum': return load_it(self.tagger.load_album) elif action == '/opennat': return load_it(self.tagger.load_nat) except Exception as e: log.error("Browser integration failed with %r on line %r", e, orig_line) return False log.error("Browser integration failed: cannot parse %r", orig_line) return False
def _release_request_finished(self, document, http, error): if self.load_task is None: return self.load_task = None parsed = False try: if error: self.log.error("%r", unicode(http.errorString())) # Fix for broken NAT releases if error == QtNetwork.QNetworkReply.ContentNotFoundError: nats = False nat_name = self.config.setting["nat_name"] files = list(self.unmatched_files.files) for file in files: trackid = file.metadata["musicbrainz_trackid"] if mbid_validate(trackid) and file.metadata["album"] == nat_name: nats = True self.tagger.move_file_to_nat(file, trackid) self.tagger.nats.update() if nats and not self.get_num_unmatched_files(): self.tagger.remove_album(self) error = False else: try: parsed = self._parse_release(document) except: error = True self.log.error(traceback.format_exc()) finally: self._requests -= 1 if parsed or error: self._finalize_loading(error)
def _file_loaded(self, result=None, error=None): file = result if file is not None and error is None and not file.has_error(): puid = file.metadata['musicip_puid'] trackid = file.metadata['musicbrainz_trackid'] albumid = file.metadata['musicbrainz_albumid'] self.puidmanager.add(puid, trackid) if mbid_validate(albumid): if mbid_validate(trackid): self.move_file_to_track(file, albumid, trackid) else: self.move_file_to_album(file, albumid) elif mbid_validate(trackid): self.move_file_to_nat(file, trackid) elif self.config.setting['analyze_new_files']: self.analyze([file])
def _file_loaded(self, file, target=None): if file is not None and not file.has_error(): trackid = file.metadata['musicbrainz_trackid'] if target is not None: self.move_files([file], target) elif not config.setting["ignore_file_mbids"]: albumid = file.metadata['musicbrainz_albumid'] if mbid_validate(albumid): if mbid_validate(trackid): self.move_file_to_track(file, albumid, trackid) else: self.move_file_to_album(file, albumid) elif mbid_validate(trackid): self.move_file_to_nat(file, trackid) elif config.setting['analyze_new_files'] and file.can_analyze(): self.analyze([file]) elif config.setting['analyze_new_files'] and file.can_analyze(): self.analyze([file])
def test_ok(self): self.assertTrue(util.mbid_validate('2944824d-4c26-476f-a981-be849081942f')) self.assertTrue(util.mbid_validate('2944824D-4C26-476F-A981-be849081942f')) self.assertFalse(util.mbid_validate('')) self.assertFalse(util.mbid_validate('Z944824d-4c26-476f-a981-be849081942f')) self.assertFalse(util.mbid_validate('22944824d-4c26-476f-a981-be849081942f')) self.assertFalse(util.mbid_validate('2944824d-4c26-476f-a981-be849081942ff')) self.assertFalse(util.mbid_validate('2944824d-4c26.476f-a981-be849081942f'))
def _file_loaded(self, result=None, error=None): file = result if file is not None and error is None and not file.has_error(): puid = file.metadata['musicip_puid'] trackid = file.metadata['musicbrainz_trackid'] self.puidmanager.add(puid, trackid) if not self.config.setting["ignore_file_mbids"]: albumid = file.metadata['musicbrainz_albumid'] if mbid_validate(albumid): if mbid_validate(trackid): self.move_file_to_track(file, albumid, trackid) else: self.move_file_to_album(file, albumid) elif mbid_validate(trackid): self.move_file_to_nat(file, trackid) elif self.config.setting['analyze_new_files']: self.analyze([file]) elif self.config.setting['analyze_new_files']: self.analyze([file])
def _file_loaded(self, file, target=None): if file is not None and not file.has_error(): recordingid = file.metadata['musicbrainz_recordingid'] if target is not None: self.move_files([file], target) elif not config.setting["ignore_file_mbids"]: albumid = file.metadata['musicbrainz_albumid'] if mbid_validate(albumid): if mbid_validate(recordingid): self.move_file_to_track(file, albumid, recordingid) else: self.move_file_to_album(file, albumid) elif mbid_validate(recordingid): self.move_file_to_nat(file, recordingid) elif config.setting['analyze_new_files'] and file.can_analyze( ): self.analyze([file]) elif config.setting['analyze_new_files'] and file.can_analyze(): self.analyze([file])
def _load_mbid(self, type, args): if 'id' in args and args['id']: mbid = args['id'][0] if not mbid_validate(mbid): self._response(400, '"id" is not a valid MBID.') else: tagger = QtCore.QCoreApplication.instance() to_main(tagger.load_mbid, type, mbid) self._response(200, 'MBID "%s" loaded' % mbid) else: self._response(400, 'Missing parameter "id".')
def _file_loaded(self, file, target=None): if file is not None and not file.has_error(): recordingid = ( file.metadata.getall("musicbrainz_recordingid")[0] if "musicbrainz_recordingid" in file.metadata else "" ) if target is not None: self.move_files([file], target) elif not config.setting["ignore_file_mbids"]: albumid = ( file.metadata.getall("musicbrainz_albumid")[0] if "musicbrainz_albumid" in file.metadata else "" ) if mbid_validate(albumid): if mbid_validate(recordingid): self.move_file_to_track(file, albumid, recordingid) else: self.move_file_to_album(file, albumid) elif mbid_validate(recordingid): self.move_file_to_nat(file, recordingid) elif config.setting["analyze_new_files"] and file.can_analyze(): self.analyze([file]) elif config.setting["analyze_new_files"] and file.can_analyze(): self.analyze([file])
def _file_loaded(self, file, target=None): if file is None or file.has_error(): return if target is not None: self.move_files([file], target) return if not config.setting["ignore_file_mbids"]: recordingid = file.metadata['musicbrainz_recordingid'] is_valid_recordingid = mbid_validate(recordingid) albumid = file.metadata['musicbrainz_albumid'] is_valid_albumid = mbid_validate(albumid) if is_valid_albumid and is_valid_recordingid: log.debug( "%r has release (%s) and recording (%s) MBIDs, moving to track...", file, albumid, recordingid) self.move_file_to_track(file, albumid, recordingid) return if is_valid_albumid: log.debug("%r has only release MBID (%s), moving to album...", file, albumid) self.move_file_to_album(file, albumid) return if is_valid_recordingid: log.debug( "%r has only recording MBID (%s), moving to non-album track...", file, recordingid) self.move_file_to_nat(file, recordingid) return # fallback on analyze if nothing else worked if config.setting['analyze_new_files'] and file.can_analyze(): log.debug("Trying to analyze %r ...", file) self.analyze([file])
def _match_files(self, files, recordingid=None, threshold=0): """Match files to tracks on this album, based on metadata similarity or recordingid.""" tracks_cache = defaultdict(lambda: None) def build_tracks_cache(): for track in self.tracks: tm_recordingid = track.orig_metadata['musicbrainz_recordingid'] tm_tracknumber = track.orig_metadata['tracknumber'] tm_discnumber = track.orig_metadata['discnumber'] for tup in ( (tm_recordingid, tm_tracknumber, tm_discnumber), (tm_recordingid, tm_tracknumber), (tm_recordingid, )): tracks_cache[tup] = track SimMatchAlbum = namedtuple('SimMatchAlbum', 'similarity track') for file in list(files): if file.state == File.REMOVED: continue # if we have a recordingid to match against, use that in priority recid = recordingid or file.metadata['musicbrainz_recordingid'] if recid and mbid_validate(recid): if not tracks_cache: build_tracks_cache() tracknumber = file.metadata['tracknumber'] discnumber = file.metadata['discnumber'] track = (tracks_cache[(recid, tracknumber, discnumber)] or tracks_cache[(recid, tracknumber)] or tracks_cache[(recid, )]) if track: yield (file, track) continue # try to match by similarity def candidates(): for track in self.tracks: yield SimMatchAlbum( similarity=track.metadata.compare(file.orig_metadata), track=track ) QtCore.QCoreApplication.processEvents() no_match = SimMatchAlbum(similarity=-1, track=self.unmatched_files) best_match = find_best_match(candidates, no_match) if best_match.similarity < threshold: yield (file, no_match.track) else: yield (file, best_match.result.track)
def _file_loaded(self, file, target=None): if file is None or file.has_error(): return if target is not None: self.move_files([file], target) return if not config.setting["ignore_file_mbids"]: recordingid = file.metadata['musicbrainz_recordingid'] is_valid_recordingid = mbid_validate(recordingid) albumid = file.metadata['musicbrainz_albumid'] is_valid_albumid = mbid_validate(albumid) if is_valid_albumid and is_valid_recordingid: log.debug("%r has release (%s) and recording (%s) MBIDs, moving to track...", file, albumid, recordingid) self.move_file_to_track(file, albumid, recordingid) return if is_valid_albumid: log.debug("%r has only release MBID (%s), moving to album...", file, albumid) self.move_file_to_album(file, albumid) return if is_valid_recordingid: log.debug("%r has only recording MBID (%s), moving to non-album track...", file, recordingid) self.move_file_to_nat(file, recordingid) return # fallback on analyze if nothing else worked if config.setting['analyze_new_files'] and file.can_analyze(): log.debug("Trying to analyze %r ...", file) self.analyze([file])
def _match_files(self, files, recordingid=None, threshold=0): """Match files to tracks on this album, based on metadata similarity or recordingid.""" tracks_cache = defaultdict(lambda: None) def build_tracks_cache(): for track in self.tracks: tm_recordingid = track.orig_metadata['musicbrainz_recordingid'] tm_tracknumber = track.orig_metadata['tracknumber'] tm_discnumber = track.orig_metadata['discnumber'] for tup in ( (tm_recordingid, tm_tracknumber, tm_discnumber), (tm_recordingid, tm_tracknumber), (tm_recordingid, )): tracks_cache[tup] = track SimMatchAlbum = namedtuple('SimMatchAlbum', 'similarity track') for file in list(files): if file.state == File.REMOVED: continue # if we have a recordingid to match against, use that in priority recid = recordingid or file.metadata['musicbrainz_recordingid'] if recid and mbid_validate(recid): if not tracks_cache: build_tracks_cache() tracknumber = file.metadata['tracknumber'] discnumber = file.metadata['discnumber'] track = (tracks_cache[(recid, tracknumber, discnumber)] or tracks_cache[(recid, tracknumber)] or tracks_cache[(recid, )]) if track: yield (file, track) continue # try to match by similarity def candidates(): for track in self.tracks: yield SimMatchAlbum( similarity=track.metadata.compare(file.orig_metadata), track=track ) no_match = SimMatchAlbum(similarity=-1, track=self.unmatched_files) best_match = find_best_match(candidates, no_match) if best_match.similarity < threshold: yield (file, no_match.track) else: yield (file, best_match.result.track)
def match_files(self, files, use_trackid=True): """Match files to tracks on this album, based on metadata similarity or trackid.""" for file in list(files): matches = [] trackid = file.metadata['musicbrainz_trackid'] if use_trackid and mbid_validate(trackid): matches = self._get_trackid_matches(file, trackid) if not matches: for track in self.tracks: sim = track.metadata.compare(file.orig_metadata) if sim >= self.config.setting['track_matching_threshold']: matches.append((sim, track)) if matches: matches.sort(reverse=True) file.move(matches[0][1]) else: file.move(self.unmatched_files)
def match_files(self, files, use_recordingid=True): """Match files to tracks on this album, based on metadata similarity or recordingid.""" for file in list(files): if file.state == File.REMOVED: continue matches = [] recordingid = file.metadata['musicbrainz_recordingid'] if use_recordingid and mbid_validate(recordingid): matches = self._get_recordingid_matches(file, recordingid) if not matches: for track in self.tracks: sim = track.metadata.compare(file.orig_metadata) if sim >= config.setting['track_matching_threshold']: matches.append((sim, track)) if matches: matches.sort(reverse=True) file.move(matches[0][1]) else: file.move(self.unmatched_files)
def match_files(self, files, use_recordingid=True): """Match files to tracks on this album, based on metadata similarity or recordingid.""" for file in list(files): if file.state == File.REMOVED: continue matches = [] recordingid = file.metadata['musicbrainz_recordingid'] if use_recordingid and mbid_validate(recordingid): matches = self._get_recordingid_matches(file, recordingid) if not matches: for track in self.tracks: sim = track.metadata.compare(file.orig_metadata) if sim >= config.setting['track_matching_threshold']: matches.append((sim, track)) if matches: matches.sort(key=itemgetter(0), reverse=True) file.move(matches[0][1]) else: file.move(self.unmatched_files)
def _match_files(self, files, recordingid=None, threshold=0): """Match files to tracks on this album, based on metadata similarity or recordingid.""" tracks_cache = defaultdict(lambda: None) def build_tracks_cache(): for track in self.tracks: tm_recordingid = track.orig_metadata['musicbrainz_recordingid'] tm_tracknumber = track.orig_metadata['tracknumber'] tm_discnumber = track.orig_metadata['discnumber'] for tup in ( (tm_recordingid, tm_tracknumber, tm_discnumber), (tm_recordingid, tm_tracknumber), (tm_recordingid, )): tracks_cache[tup] = track for file in list(files): if file.state == File.REMOVED: continue # if we have a recordingid to match against, use that in priority recid = recordingid or file.metadata['musicbrainz_recordingid'] if recid and mbid_validate(recid): if not tracks_cache: build_tracks_cache() tracknumber = file.metadata['tracknumber'] discnumber = file.metadata['discnumber'] track = (tracks_cache[(recid, tracknumber, discnumber)] or tracks_cache[(recid, tracknumber)] or tracks_cache[(recid, )]) if track: yield (file, track) continue # try to match by similarity # TODO: find a way to speed up this part, it needs to iterate all # tracks for each file, and metadata.compare() is rather complex best_track = None best_score = -1 for track in self.tracks: sim = track.metadata.compare(file.orig_metadata) if sim >= threshold and sim > best_score: best_track, best_score = track, sim yield (file, best_track or self.unmatched_files)
def _release_request_finished(self, document, http, error): if self.load_task is None: return self.load_task = None parsed = False try: if error: self.error_append(http.errorString()) # Fix for broken NAT releases if error == QtNetwork.QNetworkReply.NetworkError.ContentNotFoundError: config = get_config() nats = False nat_name = config.setting["nat_name"] files = list(self.unmatched_files.files) for file in files: recordingid = file.metadata["musicbrainz_recordingid"] if mbid_validate(recordingid) and file.metadata[ "album"] == nat_name: nats = True self.tagger.move_file_to_nat(file, recordingid) self.tagger.nats.update() if nats and not self.get_num_unmatched_files(): self.tagger.remove_album(self) error = False else: try: parsed = self._parse_release(document) config = get_config() if not parsed and config.setting['track_ars']: log.debug( 'Recording relationships not loaded in initial request for %r, issuing separate requests' % self) self._request_recording_relationships(config=config) except Exception: error = True self.error_append(traceback.format_exc()) finally: self._requests -= 1 if parsed or error: self._finalize_loading(error)
def _file_loaded(self, file, target=None, remove_file=False, unmatched_files=None): config = get_config() self._pending_files_count -= 1 if self._pending_files_count == 0: self.window.set_sorting(True) if remove_file: file.remove() return if file is None: return if file.has_error(): self.unclustered_files.add_file(file) return file_moved = False if not config.setting["ignore_file_mbids"]: recordingid = file.metadata.getall('musicbrainz_recordingid') recordingid = recordingid[0] if recordingid else '' is_valid_recordingid = mbid_validate(recordingid) albumid = file.metadata.getall('musicbrainz_albumid') albumid = albumid[0] if albumid else '' is_valid_albumid = mbid_validate(albumid) if is_valid_albumid and is_valid_recordingid: log.debug( "%r has release (%s) and recording (%s) MBIDs, moving to track...", file, albumid, recordingid) self.move_file_to_track(file, albumid, recordingid) file_moved = True elif is_valid_albumid: log.debug("%r has only release MBID (%s), moving to album...", file, albumid) self.move_file_to_album(file, albumid) file_moved = True elif is_valid_recordingid: log.debug( "%r has only recording MBID (%s), moving to non-album track...", file, recordingid) self.move_file_to_nat(file, recordingid) file_moved = True if not file_moved: target = self.move_file(file, target) if target and target != self.unclustered_files: file_moved = True if not file_moved and unmatched_files is not None: unmatched_files.append(file) # fallback on analyze if nothing else worked if not file_moved and config.setting[ 'analyze_new_files'] and file.can_analyze(): log.debug("Trying to analyze %r ...", file) self.analyze([file]) # Auto cluster newly added files if they are not explicitly moved elsewhere if self._pending_files_count == 0 and unmatched_files and config.setting[ "cluster_new_files"]: self.cluster(unmatched_files)