def do_scan(): yield "%i | Begin.\n" % int(time.time()) all_files = {} for root, dirs, files in os.walk(unicode(app.config["MUSIC_PATH"])): if len(files) != 0: yield "%i | Scanning [%s].\n" % (int(time.time()), encode_filename(root)) for name in files: name = encode_filename(os.path.join(root, name)) all_files[name] = True song = Song.query.get(name) if song != None: stat = os.stat(name) if song.lastmodified == stat.st_mtime and song.filesize == stat.st_size: continue else: song = Song() try: tags = readtags(name) except: tags = None if tags == None: yield "%i | Skipping [%s].\n" % (int(time.time()), name) continue song.sync_picard(tags) db.session.add(song) yield "%i | Adding [%s].\n" % (int(time.time()), encode_filename(song.filename)) for song in db.session.query(Song.filename): if song.filename not in all_files: Song.query.filter(Song.filename == song.filename).delete(False) yield "%i | Removing [%s].\n" % (int(time.time()), encode_filename(song.filename)) db.session.commit() yield "%i | Done.\n" % int(time.time())
def _save(self, filename, metadata, settings): """Save metadata to the file.""" self.log.debug("Saving file %r", filename) try: tags = mutagen.apev2.APEv2(encode_filename(filename)) except mutagen.apev2.APENoHeaderError: tags = mutagen.apev2.APEv2() if settings["clear_existing_tags"]: tags.clear() elif settings['save_images_to_tags'] and metadata.images: for name, value in tags.items(): if name.lower().startswith('cover art') and value.kind == mutagen.apev2.BINARY: del tags[name] temp = {} for name, value in metadata.items(): if name.startswith("~"): continue if name.startswith('lyrics:'): name = 'Lyrics' elif name == "date": name = "Year" # tracknumber/totaltracks => Track elif name == 'tracknumber': name = 'Track' if 'totaltracks' in metadata: value = '%s/%s' % (value, metadata['totaltracks']) # discnumber/totaldiscs => Disc elif name == 'discnumber': name = 'Disc' if 'totaldiscs' in metadata: value = '%s/%s' % (value, metadata['totaldiscs']) elif name in ('totaltracks', 'totaldiscs'): continue # "performer:Piano=Joe Barr" => "Performer=Joe Barr (Piano)" elif name.startswith('performer:') or name.startswith('comment:'): name, desc = name.split(':', 1) name = name.title() if desc: value += ' (%s)' % desc elif name in self.__rtranslate: name = self.__rtranslate[name] else: name = name.title() temp.setdefault(name, []).append(value) for name, values in temp.items(): tags[str(name)] = values if settings['save_images_to_tags']: for mime, data, _fname in metadata.images: cover_filename = 'Cover Art (Front)' cover_filename += mimetype.get_extension(mime, '.jpg') tags['Cover Art (Front)'] = cover_filename + '\0' + data break # can't save more than one item with the same name # (mp3tags does this, but it's against the specs) tags.save(encode_filename(filename))
def _save(self, filename, metadata, settings): self.log.debug("Saving file %r", filename) file = MP4(encode_filename(self.filename)) if file.tags is None: file.add_tags() if settings["clear_existing_tags"]: file.tags.clear() for name, values in metadata.rawitems(): if name.startswith('lyrics:'): name = 'lyrics' if name in self.__r_text_tags: file.tags[self.__r_text_tags[name]] = values elif name in self.__r_bool_tags: file.tags[self.__r_bool_tags[name]] = (values[0] == '1') elif name in self.__r_int_tags: try: file.tags[self.__r_int_tags[name]] = [int(value) for value in values] except ValueError: pass elif name in self.__r_freeform_tags: values = [v.encode("utf-8") for v in values] file.tags[self.__r_freeform_tags[name]] = values elif name == "musicip_fingerprint": file.tags["----:com.apple.iTunes:fingerprint"] = ["MusicMagic Fingerprint%s" % str(v) for v in values] if "tracknumber" in metadata: if "totaltracks" in metadata: file.tags["trkn"] = [(int(metadata["tracknumber"]), int(metadata["totaltracks"]))] else: file.tags["trkn"] = [(int(metadata["tracknumber"]), 0)] if "discnumber" in metadata: if "totaldiscs" in metadata: file.tags["disk"] = [(int(metadata["discnumber"]), int(metadata["totaldiscs"]))] else: file.tags["disk"] = [(int(metadata["discnumber"]), 0)] if settings['save_images_to_tags']: covr = [] for mime, data, _fname in metadata.images: if mime == "image/jpeg": covr.append(MP4Cover(data, MP4Cover.FORMAT_JPEG)) elif mime == "image/png": covr.append(MP4Cover(data, MP4Cover.FORMAT_PNG)) if covr: file.tags["covr"] = covr file.save()
def _load(self, filename): self.log.debug("Loading file %r", filename) f = wave.open(encode_filename(filename), "rb") metadata = Metadata() metadata['~#channels'] = f.getnchannels() metadata['~#bits_per_sample'] = f.getsampwidth() * 8 metadata['~#sample_rate'] = f.getframerate() metadata.length = 1000 * f.getnframes() / f.getframerate() metadata['~format'] = 'Microsoft WAVE' file = Wave(f) self._info(metadata, file) return metadata
def _load(self, filename): self.log.debug("Loading file %r", filename) file = self._File(encode_filename(filename)) metadata = Metadata() if file.tags: for origname, values in file.tags.items(): if origname.lower().startswith("cover art") and values.kind == mutagen.apev2.BINARY: if '\0' in values.value: descr, data = values.value.split('\0', 1) mime = mimetype.get_from_data(data, descr, 'image/jpeg') metadata.add_image(mime, data) # skip EXTERNAL and BINARY values if values.kind != mutagen.apev2.TEXT: continue for value in values: name = origname if name == "Year": name = "date" value = sanitize_date(value) elif name == "Track": name = "tracknumber" track = value.split("/") if len(track) > 1: metadata["totaltracks"] = track[1] value = track[0] elif name == "Disc": name = "discnumber" disc = value.split("/") if len(disc) > 1: metadata["totaldiscs"] = disc[1] value = disc[0] elif name == 'Performer' or name == 'Comment': name = name.lower() + ':' if value.endswith(')'): start = value.rfind(' (') if start > 0: name += value[start + 2:-1] value = value[:start] elif name in self.__translate: name = self.__translate[name] else: name = name.lower() metadata.add(name, value) self._info(metadata, file) return metadata
def _load(self, filename): self.log.debug("Loading file %r", filename) file = MP4(encode_filename(filename)) if file.tags is None: file.add_tags() metadata = Metadata() for name, values in file.tags.items(): if name in self.__text_tags: for value in values: metadata.add(self.__text_tags[name], value) elif name in self.__bool_tags: metadata.add(self.__bool_tags[name], values and '1' or '0') elif name in self.__int_tags: for value in values: metadata.add(self.__int_tags[name], unicode(value)) elif name in self.__freeform_tags: for value in values: value = value.strip("\x00").decode("utf-8", "replace") metadata.add(self.__freeform_tags[name], value) elif name == "----:com.apple.iTunes:fingerprint": for value in values: value = value.strip("\x00").decode("utf-8", "replace") if value.startswith("MusicMagic Fingerprint"): metadata.add("musicip_fingerprint", value[22:]) elif name == "trkn": metadata["tracknumber"] = str(values[0][0]) metadata["totaltracks"] = str(values[0][1]) elif name == "disk": metadata["discnumber"] = str(values[0][0]) metadata["totaldiscs"] = str(values[0][1]) elif name == "covr": for value in values: if value.format == value.FORMAT_JPEG: metadata.add_image("image/jpeg", value) elif value.format == value.FORMAT_PNG: metadata.add_image("image/png", value) self._info(metadata, file) return metadata
def _load(self, filename): self.log.debug("Loading file %r", filename) file = ASF(encode_filename(filename)) metadata = Metadata() for name, values in file.tags.items(): if name == 'WM/Picture': for image in values: (mime, data, type) = unpack_image(image.value) if type == 3: # Only cover images metadata.add_image(mime, data) continue elif name not in self.__RTRANS: continue elif name == 'WM/SharedUserRating': # Rating in WMA ranges from 0 to 99, normalize this to the range 0 to 5 values[0] = int(round(int(unicode(values[0])) / 99.0 * (5 - 1))) name = self.__RTRANS[name] values = filter(bool, map(unicode, values)) if values: metadata[name] = values self._info(metadata, file) return metadata
def _save(self, filename, metadata, settings): self.log.debug("Saving file %r", filename) file = ASF(encode_filename(filename)) if settings['clear_existing_tags']: file.tags.clear() if settings['save_images_to_tags']: cover = [] for mime, data, _fname in metadata.images: tag_data = pack_image(mime, data, 3) cover.append(ASFByteArrayAttribute(tag_data)) if cover: file.tags['WM/Picture'] = cover for name, values in metadata.rawitems(): if name.startswith('lyrics:'): name = 'lyrics' elif name == '~rating': values[0] = int(values[0]) * 99 / (settings['rating_steps'] - 1) if name not in self.__TRANS: continue name = self.__TRANS[name] file.tags[name] = map(unicode, values) file.save()
def _save(self, filename, metadata, settings): """Save metadata to the file.""" self.log.debug("Saving file %r", filename) file = self._File(encode_filename(filename)) if file.tags is None: file.add_tags() if settings["clear_existing_tags"]: file.tags.clear() if self._File == mutagen.flac.FLAC and ( settings["clear_existing_tags"] or (settings['save_images_to_tags'] and metadata.images)): file.clear_pictures() tags = {} for name, value in metadata.items(): if name == '~rating': # Save rating according to http://code.google.com/p/quodlibet/wiki/Specs_VorbisComments if settings['rating_user_email']: name = 'rating:%s' % settings['rating_user_email'] else: name = 'rating' value = unicode(float(value) / (settings['rating_steps'] - 1)) # don't save private tags elif name.startswith("~"): continue if name.startswith('lyrics:'): name = 'lyrics' elif name == "date" or name == "originaldate": # YYYY-00-00 => YYYY value = sanitize_date(value) elif name.startswith('performer:') or name.startswith('comment:'): # transform "performer:Piano=Joe Barr" to "performer=Joe Barr (Piano)" name, desc = name.split(':', 1) if desc: value += ' (%s)' % desc elif name == "musicip_fingerprint": name = "fingerprint" value = "MusicMagic Fingerprint%s" % value tags.setdefault(name.upper().encode('utf-8'), []).append(value) if "totaltracks" in metadata: tags.setdefault(u"TRACKTOTAL", []).append(metadata["totaltracks"]) if "totaldiscs" in metadata: tags.setdefault(u"DISCTOTAL", []).append(metadata["totaldiscs"]) if settings['save_images_to_tags']: for mime, data, filename in metadata.images: image = mutagen.flac.Picture() image.type = 3 # Cover image image.data = data image.mime = mime if self._File == mutagen.flac.FLAC: file.add_picture(image) else: tags.setdefault(u"METADATA_BLOCK_PICTURE", []).append( base64.standard_b64encode(image.write())) file.tags.update(tags) kwargs = {} if self._File == mutagen.flac.FLAC and settings["remove_id3_from_flac"]: kwargs["deleteid3"] = True try: file.save(**kwargs) except TypeError: file.save()
def _load(self, filename): self.log.debug("Loading file %r", filename) file = self._File(encode_filename(filename)) file.tags = file.tags or {} metadata = Metadata() for origname, values in file.tags.items(): for value in values: name = origname if name == "date" or name == "originaldate": # YYYY-00-00 => YYYY value = sanitize_date(value) elif name == 'performer' or name == 'comment': # transform "performer=Joe Barr (Piano)" to "performer:Piano=Joe Barr" name += ':' if value.endswith(')'): start = len(value) - 2 count = 1 while count > 0 and start > 0: if value[start] == ')': count += 1 elif value[start] == '(': count -= 1 start -= 1 if start > 0: name += value[start + 2:-1] value = value[:start] elif name.startswith('rating'): try: name, email = name.split(':', 1) except ValueError: email = '' continue if email != self.config.setting['rating_user_email']: continue name = '~rating' value = unicode(int(round((float(value) * (5 - 1))))) elif name == "fingerprint" and value.startswith("MusicMagic Fingerprint"): name = "musicip_fingerprint" value = value[22:] elif name == "tracktotal": if "totaltracks" in file.tags: continue name = "totaltracks" elif name == "disctotal": if "totaldiscs" in file.tags: continue name = "totaldiscs" elif name == "metadata_block_picture": image = mutagen.flac.Picture(base64.standard_b64decode(value)) metadata.add_image(image.mime, image.data) continue metadata.add(name, value) if self._File == mutagen.flac.FLAC: for image in file.pictures: metadata.add_image(image.mime, image.data) # Read the unofficial COVERART tags, for backward compatibillity only if not "metadata_block_picture" in file.tags: try: for index, data in enumerate(file["COVERART"]): metadata.add_image(file["COVERARTMIME"][index], base64.standard_b64decode(data)) except KeyError: pass self._info(metadata, file) return metadata
def _save(self, filename, metadata, settings): """Save metadata to the file.""" self.log.debug("Saving file %r", filename) try: tags = compatid3.CompatID3(encode_filename(filename)) except mutagen.id3.ID3NoHeaderError: tags = compatid3.CompatID3() if settings['clear_existing_tags']: tags.clear() if settings['save_images_to_tags'] and metadata.images: tags.delall('APIC') if settings['write_id3v1']: v1 = 2 else: v1 = 0 encoding = {'utf-8': 3, 'utf-16': 1}.get(settings['id3v2_encoding'], 0) if 'tracknumber' in metadata: if 'totaltracks' in metadata: text = '%s/%s' % (metadata['tracknumber'], metadata['totaltracks']) else: text = metadata['tracknumber'] tags.add(id3.TRCK(encoding=0, text=text)) if 'discnumber' in metadata: if 'totaldiscs' in metadata: text = '%s/%s' % (metadata['discnumber'], metadata['totaldiscs']) else: text = metadata['discnumber'] tags.add(id3.TPOS(encoding=0, text=text)) if settings['save_images_to_tags']: for mime, data, _fname in metadata.images: tags.add(id3.APIC(encoding=0, mime=mime, type=3, desc='', data=data)) tmcl = mutagen.id3.TMCL(encoding=encoding, people=[]) tipl = mutagen.id3.TIPL(encoding=encoding, people=[]) tags.delall('TCMP') for name, values in metadata.rawitems(): if name.startswith('performer:'): role = name.split(':', 1)[1] for value in values: tmcl.people.append([role, value]) elif name.startswith('comment:'): desc = name.split(':', 1)[1] if desc.lower()[:4]=="itun": tags.delall('COMM:' + desc) tags.add(id3.COMM(encoding=0, desc=desc, lang='eng', text=[v+u'\x00' for v in values])) else: tags.add(id3.COMM(encoding=encoding, desc=desc, lang='eng', text=values)) elif name.startswith('lyrics:') or name == 'lyrics': if ':' in name: desc = name.split(':', 1)[1] else: desc = '' for value in values: tags.add(id3.USLT(encoding=encoding, desc=desc, text=value)) elif name in self.__rtipl_roles: for value in values: tipl.people.append([self.__rtipl_roles[name], value]) elif name == 'musicbrainz_trackid': tags.add(id3.UFID(owner='http://musicbrainz.org', data=str(values[0]))) elif name == '~rating': # Search for an existing POPM frame to get the current playcount for frame in tags.values(): if frame.FrameID == 'POPM' and frame.email == settings['rating_user_email']: count = getattr(frame, 'count', 0) break else: count = 0 # Convert rating to range between 0 and 255 rating = int(round(float(values[0]) * 255 / (settings['rating_steps'] - 1))) tags.add(id3.POPM(email=settings['rating_user_email'], rating=rating, count=count)) elif name in self.__rtranslate: frameid = self.__rtranslate[name] if frameid.startswith('W'): valid_urls = all([all(urlparse(v)[:2]) for v in values]) if frameid == 'WCOP': # Only add WCOP if there is only one license URL, otherwise use TXXX:LICENSE if len(values) > 1 or not valid_urls: tags.add(id3.TXXX(encoding=encoding, desc=self.__rtranslate_freetext[name], text=values)) else: tags.add(id3.WCOP(url=values[0])) elif frameid == 'WOAR' and valid_urls: for url in values: tags.add(id3.WOAR(url=url)) elif frameid.startswith('T'): tags.add(getattr(id3, frameid)(encoding=encoding, text=values)) if frameid == 'TSOA': tags.delall('XSOA') elif frameid == 'TSOP': tags.delall('XSOP') elif frameid == 'TSO2': tags.delall('TXXX:ALBUMARTISTSORT') elif name in self.__rtranslate_freetext: tags.add(id3.TXXX(encoding=encoding, desc=self.__rtranslate_freetext[name], text=values)) elif name.startswith('~id3:'): name = name[5:] if name.startswith('TXXX:'): tags.add(id3.TXXX(encoding=encoding, desc=name[5:], text=values)) else: frameclass = getattr(id3, name[:4], None) if frameclass: tags.add(frameclass(encoding=encoding, text=values)) # don't save private / already stored tags elif not name.startswith("~") and not name in self.__other_supported_tags: tags.add(id3.TXXX(encoding=encoding, desc=name, text=values)) if tmcl.people: tags.add(tmcl) if tipl.people: tags.add(tipl) if settings['write_id3v23']: tags.update_to_v23() tags.save(encode_filename(filename), v2=3, v1=v1) else: tags.update_to_v24() tags.save(encode_filename(filename), v2=4, v1=v1) if self._IsMP3 and settings["remove_ape_from_mp3"]: try: mutagen.apev2.delete(encode_filename(filename)) except: pass
def _load(self, filename): self.log.debug("Loading file %r", filename) file = self._File(encode_filename(filename), ID3=compatid3.CompatID3) tags = file.tags or {} # upgrade custom 2.3 frames to 2.4 for old, new in self.__upgrade.items(): if old in tags and new not in tags: f = tags.pop(old) tags.add(getattr(id3, new)(encoding=f.encoding, text=f.text)) metadata = Metadata() for frame in tags.values(): frameid = frame.FrameID if frameid in self.__translate: name = self.__translate[frameid] if frameid.startswith('T'): for text in frame.text: if text: metadata.add(name, unicode(text)) elif frameid == 'COMM': for text in frame.text: if text: metadata.add('%s:%s' % (name, frame.desc), unicode(text)) else: metadata.add(name, unicode(frame)) elif frameid == "TMCL": for role, name in frame.people: if role or name: metadata.add('performer:%s' % role, name) elif frameid == "TIPL": for role, name in frame.people: if role in self.__tipl_roles and name: metadata.add(self.__tipl_roles[role], name) elif frameid == 'TXXX': if frame.desc in self.__translate_freetext: name = self.__translate_freetext[frame.desc] else: name = str(frame.desc.lower()) for text in frame.text: metadata.add(name, unicode(text)) elif frameid == 'USLT': name = 'lyrics' if frame.desc: name += ':%s' % frame.desc metadata.add(name, unicode(frame.text)) elif frameid == 'UFID' and frame.owner == 'http://musicbrainz.org': metadata['musicbrainz_trackid'] = unicode(frame.data) elif frameid == 'TRCK': value = frame.text[0].split('/') if len(value) > 1: metadata['tracknumber'], metadata['totaltracks'] = value[:2] else: metadata['tracknumber'] = value[0] elif frameid == 'TPOS': value = frame.text[0].split('/') if len(value) > 1: metadata['discnumber'], metadata['totaldiscs'] = value[:2] else: metadata['discnumber'] = value[0] elif frameid == 'APIC': metadata.add_image(frame.mime, frame.data) elif frameid == 'POPM': # Rating in ID3 ranges from 0 to 255, normalize this to the range 0 to 5 if False: rating = unicode(int(round(frame.rating / 255.0 * (5 - 1)))) metadata.add('~rating', rating) if 'date' in metadata: metadata['date'] = sanitize_date(metadata.getall('date')[0]) self._info(metadata, file) return metadata