def extractMetadataHelper(self, mediawrapper, fieldMap, filename): ''' Retrieves artist, album, and track data, forcing it to unicode ''' artists = [] for artistField in self.artistFieldPref: if (fieldMap[artistField] in mediawrapper): tmpartist = mediawrapper[fieldMap[artistField]][0] if (not common.isempty(tmpartist)): artists.append(unicode(tmpartist).lower()) if (self.useBothArtistFields): continue break artists = set(artists).difference(self.meaninglessArtists) if (len(artists) == 0): common.safeStderr('No artist info found for [' + filename + ']') return None # album album = u'-unknown-' if (fieldMap['album'] in mediawrapper): tmpalbum = mediawrapper[fieldMap['album']][0] if (not common.isempty(tmpalbum)): album = unicode(tmpalbum).lower() # track track = None if (fieldMap['track'] in mediawrapper): tmptrack = mediawrapper[fieldMap['track']][0] if (not common.isempty(tmptrack)): track = unicode(tmptrack).lower() if (track is None): common.safeStderr('No track title found for [' + filename + ']') return None return {'artists': artists, 'album': album, 'track': track}
def addToMediaLibrary(self, artist, album, track, artistTags=None, trackTags=None): if (common.isempty(artist)): raise Exception('No artist info provided') elif (common.isempty(track)): raise Exception('No track title provided') elif (common.isempty(album)): raise Exception('No album title provided') if (artist not in self.mediaLibrary): self.mediaLibrary[artist] = { 'albums':{}, 'tags':artistTags } if (album not in self.mediaLibrary[artist]['albums']): self.mediaLibrary[artist]['albums'][album] = { 'tracks':{} } if (track not in self.mediaLibrary[artist]['albums'][album]['tracks']): self.mediaLibrary[artist]['albums'][album]['tracks'][track] = { 'tags':trackTags }
def updateTagsHelper(self, mediawrapper, tagPayload, fieldMap): ''' This version saves the tag data in Unicode encoding ''' retVal = False for bucket in tagPayload: if (bucket not in fieldMap): raise Exception('Unknown field type requested [' + bucket + ']') curField = fieldMap[bucket] # If we're not required to verwrite, check if we actually need to and should if (bucket not in self.forceOverwriteFields): # Is the payload empty? Don't update. if (len(tagPayload[bucket]) == 0): continue # Is there an existing value? Don't update if this isn't an overwritable field or if the current value is the same as the update value elif (curField in mediawrapper and not common.isempty(mediawrapper[curField][0])): if (bucket not in self.overwriteFields or unicode(mediawrapper[curField][0]) == unicode( tagPayload[bucket])): continue mediawrapper[curField] = unicode(tagPayload[bucket]) retVal = True if (retVal == True): mediawrapper.save() return retVal
def getPopularityWeight(self, tag): ''' Returns the popularity weight for the given tag, or 0 if the tag is empty or not present ''' if (common.isempty(tag)): return 0 key = tag.lower() if (self.localTagLibrary[key] is not None): return self.localTagLibrary[key]['lasthits'] return 0
def lookupSynonyms(self, tag): ''' Returns a set of synonyms for the given tag, or None if none exist ''' if (common.isempty(tag)): return None key = tag.lower() if (key in self.synonyms): return self.synonyms[key] return None
def getImageList(self, params): common.trace("Starting to search images using parameters: %s" % str(params), "theaudiodb") images = [] self._setFilepaths(params) url, url_params = self._getUrlDetails(params, self.URL_ARTISTSEARCH) if url: json_data = self._getData(self.ARTISTFILEPATH, self.CACHEFILEPATH, url, url_params) if json_data: content = json_data.get('artists') if content is not None: if "strMusicBrainzID" in content[0]: params["mbid"] = content[0].get("strMusicBrainzID") if "strArtistFanart" in content[0]: image = content[0].get('strArtistFanart') if image: images.append(image) if "strArtistFanart2" in content[0]: image = content[0].get('strArtistFanart2') if image: images.append(image) if "strArtistFanart3" in content[0]: image = content[0].get('strArtistFanart3') if image: images.append(image) if "strArtistThumb" in content[0]: image = content[0].get('strArtistWideThumb') if image: images.append(image) if "strArtistWideThumb" in content[0]: image = content[0].get('strArtistWideThumb') if image: images.append(image) if "strArtistClearart" in content[0]: image = content[0].get('strArtistClearart') if image: images.append(image) if "strArtistAlternate" in content[0] and not common.isempty(content[0].get('strArtistAlternate')): params['fullname'] = content[0].get('strArtistAlternate') if "strArtist" in content[0] and not common.isempty(content[0].get('strArtist')): params['alias'] = content[0].get('strArtist') if "strCountryCode" in content[0] and not common.isempty(content[0].get('strCountryCode')): params['location'] = content[0].get('strCountryCode') if not images: return [] else: return self._delExclusions(images, params.get('exclusionsfile', ''))
def loadSynonyms(self): synfile = self.config.get('tagSynonymsFile') if (common.isempty(synfile)): return if (not os.path.exists(synfile) or not os.access(synfile, os.R_OK)): common.safeStderr('Synonyms file either does not exist or cannot be accessed ['+synfile+']') # Read the synonmyms file. The expected format is: # original token(tab)replacement token[,replacement token]... # e.g. # rnb rhythm and blues, r&b # This would replace any instance of 'rnb' seen in the LastFM tag set with both 'rhythm and blues' and 'r&b' # We preserve order, for the replacement values (so you can order them as you would like them to be replaced) for line in fileinput.input(synfile): # Allow inline comments if ('#' in line): line = line.split('#')[0] line = line.strip() if (common.isempty(line)): continue if (isinstance(line, str)): line = unicode(line, 'latin1') synline = line.split('\t') if (len(synline) < 2): common.safeStderr('Invalid synonym file line: '+line) continue original = synline[0].lower() replacements = map(string.strip, synline[1].split(',')) if ('-none-' in map(lambda val: val.lower(), replacements)): self.synonyms[original] = [] elif (original in self.synonyms): self.synonyms[original] = common.distinctSeq(self.synonyms[original] + replacements) else: self.synonyms[original] = common.distinctSeq(replacements) #for syn in sorted(self.synonyms): # common.safeStdout('Synonyms: '+ syn + ' :: '+ ', '.join(sorted(self.synonyms[syn]))) if (self.config.getboolean('verbose')): print 'Loaded ['+str(len(self.synonyms.keys()))+'] tag synonyms'
def decode_string(self, str): if (common.isempty(str)): return '' if (str.lower() == 'space'): return ' ' elif (str.lower() == 'semi'): return ';' elif (str.lower() == 'hash'): return '#' elif (str.lower() == 'percent'): return '%' else: return str
def updateTagsHelperID3(self, mediawrapper, tagPayload, fieldMap): ''' ID3 requires uniquely encoded values, so this custom method is necessary to properly save the updated tags. If the comments field is used, values will be saved with an empty description and lang=eng. ''' retVal = False for bucket in tagPayload: if (bucket not in fieldMap): raise Exception('Unknown field type requested [' + bucket + ']') curField = fieldMap[bucket] # If we're not required to verwrite, check if we actually need to and should if (bucket not in self.forceOverwriteFields): # Is the payload empty? Don't update. if (len(tagPayload[bucket]) == 0): continue # Is there an existing value? Don't update if this isn't an overwritable field or if the current value is the same as the update value elif (curField in mediawrapper and not common.isempty(mediawrapper[curField][0])): if (bucket not in self.overwriteFields or unicode(mediawrapper[curField][0]) == unicode( tagPayload[bucket])): continue mediawrapper[curField] = self.id3FuncMap[bucket]( tagPayload[bucket]) retVal = True if (retVal == True): # There's an odd bug somewhere in the interaction between some set of Mutagen, iTunes, and/or WMP that causes # duplicate ID3v2 headers. This tends to break playback at least in iTunes. The following pre-save block makes a # copy of whatever the 'current' header is, deletes 'all' v2 headers, and then re-adds the current header frames. # We seem to end up with some unnecessary blank padding between the frames and content, though. curFrames = {} for key in mediawrapper.keys(): curFrames[key] = mediawrapper[key] mediawrapper.delete(delete_v2=True) for key in curFrames: mediawrapper[key] = curFrames[key] mediawrapper.save(v1=self.id3v1Handling) return retVal
def getImageList(self, params): common.trace("Starting to search images using parameters: %s" %str(params), "duckgo") images = [] if params.get('mbid', '') == '': common.warn("No artist identified over MusicBrainz, search stopped") return images if "fullname" in params and not common.isempty(params['fullname']): keywords = params['fullname'] + " AND (singer OR band)" elif "alias" in params and not common.isempty(params['alias']): keywords = params['alias'] + " AND (singer OR band)" elif "artist" in params and not common.isempty(params['artist']): keywords = params['artist'] + " AND (singer OR band)" else: keywords = None if keywords is not None and "location" in params and not common.isempty(params['location']): keywords = keywords + " AND " + params['location'] elif keywords is not None and "lang" in params and not common.isempty(params['lang']): keywords = keywords + " AND " + params['lang'] if keywords is not None: payload = {'q': keywords} common.trace("Hitting DuckDuckGo for token", "duckgo") data = common.urlcall(self.DDG_SEARCH_URL, "POST", payload=payload) searchObj = re.search(r'vqd=([\d-]+)\&', data, re.M | re.I) if not searchObj: common.error("Token parsing failed!", "duckgo") return images else: common.debug("Obtained token: %s" % searchObj.group(1), "duckgo") headers = { 'authority': 'duckduckgo.com', 'accept': 'application/json, text/javascript, */*; q=0.01', 'sec-fetch-dest': 'empty', 'x-requested-with': 'XMLHttpRequest', 'user-agent': common.agent(), 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'referer': 'https://duckduckgo.com/' } payload = { "q": keywords, "vqd": searchObj.group(1), "v7exp": "a", "o": "json", "l": "wt-wt", "f": ",,,", "p": '1' } data = None while True: try: data = common.urlcall(self.DDG_SEARCH_URL + "i.js", headers=headers, payload=payload, output='json') break except ValueError as e: common.trace("Calling url failure; sleep and retry", "duckgo") common.sleep(500) continue index = 0 max = common.any2int(params['limit'], 0) for obj in data["results"]: contextual = str(obj["title"].encode('utf-8')).lower().find(params['artist'].lower() + " ") >= 0 dimension = int(obj["width"]) >= 876 if common.any2bool(params.get('getall', 'false')) else int(obj["width"]) >= 1920 if contextual and dimension: index += 1 images.append(obj["image"]) if max > 0 and index >= max: break if not images: return [] else: return self._delExclusions(images, params.get('exclusionsfile'))