Example #1
0
File: file.py Project: Freso/picard
 def _move_additional_files(self, old_filename, new_filename):
     """Move extra files, like playlists..."""
     old_path = encode_filename(os.path.dirname(old_filename))
     new_path = encode_filename(os.path.dirname(new_filename))
     patterns = encode_filename(config.setting["move_additional_files_pattern"])
     patterns = filter(bool, [p.strip() for p in patterns.split()])
     try:
         names = os.listdir(old_path)
     except os.error:
         log.error("Error: {} directory not found".format(old_path))
         return
     filtered_names = filter(lambda x: x[0] != '.', names)
     for pattern in patterns:
         pattern_regex = re.compile(fnmatch.translate(pattern), re.IGNORECASE)
         file_names = names
         if pattern[0] != '.':
             file_names = filtered_names
         for old_file in file_names:
             if pattern_regex.match(old_file):
                 new_file = os.path.join(new_path, old_file)
                 old_file = os.path.join(old_path, old_file)
                 # FIXME we shouldn't do this from a thread!
                 if self.tagger.files.get(decode_filename(old_file)):
                     log.debug("File loaded in the tagger, not moving %r", old_file)
                     continue
                 log.debug("Moving %r to %r", old_file, new_file)
                 shutil.move(old_file, new_file)
Example #2
0
    def _save(self, filename, metadata):
        """Save metadata to the file."""
        log.debug("Saving file %r", filename)
        try:
            tags = mutagen.apev2.APEv2(encode_filename(filename))
        except mutagen.apev2.APENoHeaderError:
            tags = mutagen.apev2.APEv2()
        if config.setting["clear_existing_tags"]:
            tags.clear()
        elif metadata.images_to_be_saved_to_tags:
            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
        for image in metadata.images_to_be_saved_to_tags:
            cover_filename = 'Cover Art (Front)'
            cover_filename += image.extension
            tags['Cover Art (Front)'] = mutagen.apev2.APEValue(cover_filename + '\0' + image.data, mutagen.apev2.BINARY)
            break
            # can't save more than one item with the same name
            # (mp3tags does this, but it's against the specs)

        self._remove_deleted_tags(metadata, tags)

        tags.save(encode_filename(filename))
Example #3
0
 def _save(self, filename, metadata):
     """Save metadata to the file."""
     log.debug("Saving file %r", filename)
     try:
         tags = mutagen.apev2.APEv2(encode_filename(filename))
     except mutagen.apev2.APENoHeaderError:
         tags = mutagen.apev2.APEv2()
     if config.setting["clear_existing_tags"]:
         tags.clear()
     elif metadata.images_to_be_saved_to_tags:
         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
     for image in metadata.images_to_be_saved_to_tags:
         cover_filename = "Cover Art (Front)"
         cover_filename += image.extension
         tags["Cover Art (Front)"] = mutagen.apev2.APEValue(cover_filename + "\0" + image.data, mutagen.apev2.BINARY)
         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))
Example #4
0
 def _next_filename(self, filename, counters):
     if counters[filename]:
         new_filename = "%s (%d)" % (decode_filename(filename), counters[filename])
     else:
         new_filename = filename
     counters[filename] += 1
     return encode_filename(new_filename)
Example #5
0
 def drop_urls(urls, target):
     files = []
     new_files = []
     for url in urls:
         if url.scheme() == "file" or not url.scheme():
             # Dropping a file from iTunes gives a filename with a NULL terminator
             filename = os.path.normpath(
                 os.path.realpath(unicode(url.toLocalFile()).rstrip("\0")))
             file = BaseTreeView.tagger.files.get(filename)
             if file:
                 files.append(file)
             elif os.path.isdir(encode_filename(filename)):
                 BaseTreeView.tagger.add_directory(filename)
             else:
                 new_files.append(filename)
         elif url.scheme() in ("http", "https"):
             path = unicode(url.path())
             match = re.search(r"/(release|recording)/([0-9a-z\-]{36})",
                               path)
             if match:
                 entity = match.group(1)
                 mbid = match.group(2)
                 if entity == "release":
                     BaseTreeView.tagger.load_album(mbid)
                 elif entity == "recording":
                     BaseTreeView.tagger.load_nat(mbid)
     if files:
         BaseTreeView.tagger.move_files(files, target)
     if new_files:
         BaseTreeView.tagger.add_files(new_files, target=target)
Example #6
0
 def drop_urls(urls, target):
     files = []
     new_files = []
     for url in urls:
         log.debug("Dropped the URL: %r", url.toString(QtCore.QUrl.RemoveUserInfo))
         if url.scheme() == "file" or not url.scheme():
             filename = os.path.normpath(os.path.realpath(url.toLocalFile().rstrip("\0")))
             file = BaseTreeView.tagger.files.get(filename)
             if file:
                 files.append(file)
             elif os.path.isdir(encode_filename(filename)):
                 BaseTreeView.tagger.add_directory(filename)
             else:
                 new_files.append(filename)
         elif url.scheme() in ("http", "https"):
             path = url.path()
             match = re.search(r"/(release|recording)/([0-9a-z\-]{36})", path)
             if match:
                 entity = match.group(1)
                 mbid = match.group(2)
                 if entity == "release":
                     BaseTreeView.tagger.load_album(mbid)
                 elif entity == "recording":
                     BaseTreeView.tagger.load_nat(mbid)
     if files:
         BaseTreeView.tagger.move_files(files, target)
     if new_files:
         BaseTreeView.tagger.add_files(new_files, target=target)
Example #7
0
def format_file_info(file_):
    info = []
    info.append((_('Filename:'), file_.filename))
    if '~format' in file_.orig_metadata:
        info.append((_('Format:'), file_.orig_metadata['~format']))
    try:
        size = os.path.getsize(encode_filename(file_.filename))
        sizestr = "%s (%s)" % (bytes1human.decimal(size),
                               bytes2human.binary(size))
        info.append((_('Size:'), sizestr))
    except:
        pass
    if file_.orig_metadata.length:
        info.append((_('Length:'), format_time(file_.orig_metadata.length)))
    if '~bitrate' in file_.orig_metadata:
        info.append(
            (_('Bitrate:'), '%s kbps' % file_.orig_metadata['~bitrate']))
    if '~sample_rate' in file_.orig_metadata:
        info.append(
            (_('Sample rate:'), '%s Hz' % file_.orig_metadata['~sample_rate']))
    if '~bits_per_sample' in file_.orig_metadata:
        info.append((_('Bits per sample:'),
                     str(file_.orig_metadata['~bits_per_sample'])))
    if '~channels' in file_.orig_metadata:
        ch = file_.orig_metadata['~channels']
        if ch == '1':
            ch = _('Mono')
        elif ch == '2':
            ch = _('Stereo')
        info.append((_('Channels:'), ch))
    return '<br/>'.join(
        map(lambda i: '<b>%s</b> %s' % (htmlescape(i[0]), htmlescape(i[1])),
            info))
Example #8
0
 def _save_images(self, filename, metadata, settings):
     """Save the cover images to disk."""
     if not metadata.images:
         return
     overwrite = settings["save_images_overwrite"]
     image_filename = self._script_to_filename(
         settings["cover_image_filename"], metadata, settings)
     if not image_filename:
         image_filename = "cover"
     if os.path.isabs(image_filename):
         filename = image_filename
     else:
         filename = os.path.join(os.path.dirname(filename), image_filename)
     if settings['windows_compatible_filenames'] or sys.platform == 'win32':
         filename = filename.replace('./', '_/').replace('.\\', '_\\')
     filename = encode_filename(filename)
     i = 0
     for mime, data in metadata.images:
         image_filename = filename
         ext = mimetype.get_extension(mime, ".jpg")
         if i > 0:
             image_filename = "%s (%d)" % (filename, i)
         i += 1
         while os.path.exists(image_filename + ext) and not overwrite:
             if os.path.getsize(image_filename + ext) == len(data):
                 self.log.debug("Identical file size, not saving %r",
                                image_filename)
                 break
             image_filename = "%s (%d)" % (filename, i)
             i += 1
         else:
             self.log.debug("Saving cover images to %r", image_filename)
             f = open(image_filename + ext, "wb")
             f.write(data)
             f.close()
Example #9
0
    def _save(self, filename, metadata):
        log.debug("Saving file %r", filename)
        file = ASF(encode_filename(filename))

        if config.setting["clear_existing_tags"]:
            file.tags.clear()
        if config.setting["save_images_to_tags"]:
            cover = []
            for image in metadata.images:
                if not save_this_image_to_tags(image):
                    continue
                tag_data = pack_image(image["mime"], image["data"], image_type_as_id3_num(image["type"]), image["desc"])
                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 / (config.setting["rating_steps"] - 1)
            if name not in self.__TRANS:
                continue
            name = self.__TRANS[name]
            file.tags[name] = map(unicode, values)
        file.save()
Example #10
0
 def _display_info_tab(self):
     file = self.obj
     info = []
     info.append((_('Filename:'), file.filename))
     if '~format' in file.orig_metadata:
         info.append((_('Format:'), file.orig_metadata['~format']))
     try:
         size = os.path.getsize(encode_filename(file.filename))
         if size < 1024:
             size = '%d B' % size
         elif size < 1024 * 1024:
             size = '%0.1f kB' % (size / 1024.0)
         else:
             size = '%0.1f MB' % (size / 1024.0 / 1024.0)
         info.append((_('Size:'), size))
     except:
         pass
     if file.orig_metadata.length:
         info.append((_('Length:'), format_time(file.orig_metadata.length)))
     if '~bitrate' in file.orig_metadata:
         info.append((_('Bitrate:'), '%s kbps' % file.orig_metadata['~bitrate']))
     if '~sample_rate' in file.orig_metadata:
         info.append((_('Sample rate:'), '%s Hz' % file.orig_metadata['~sample_rate']))
     if '~bits_per_sample' in file.orig_metadata:
         info.append((_('Bits per sample:'), str(file.orig_metadata['~bits_per_sample'])))
     if '~channels' in file.orig_metadata:
         ch = file.orig_metadata['~channels']
         if ch == 1: ch = _('Mono')
         elif ch == 2: ch = _('Stereo')
         else: ch = str(ch)
         info.append((_('Channels:'), ch))
     text = '<br/>'.join(map(lambda i: '<b>%s</b><br/>%s' % i, info))
     self.ui.info.setText(text)
Example #11
0
    def _save(self, filename, metadata):
        log.debug("Saving file %r", filename)
        file = ASF(encode_filename(filename))
        tags = file.tags

        if config.setting['clear_existing_tags']:
            tags.clear()
        cover = []
        for image in metadata.images_to_be_saved_to_tags:
            tag_data = pack_image(image.mimetype, image.data,
                                  image_type_as_id3_num(image.maintype),
                                  image.comment)
            cover.append(ASFByteArrayAttribute(tag_data))
        if cover:
            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 // (config.setting['rating_steps'] - 1)
            elif name == 'discnumber' and 'totaldiscs' in metadata:
                values[0] = '%s/%s' % (metadata['discnumber'],
                                       metadata['totaldiscs'])
            if name not in self.__TRANS:
                continue
            name = self.__TRANS[name]
            tags[name] = values

        self._remove_deleted_tags(metadata, tags)

        file.save()
Example #12
0
def format_file_info(file_):
    info = []
    info.append((_('Filename:'), file_.filename))
    if '~format' in file_.orig_metadata:
        info.append((_('Format:'), file_.orig_metadata['~format']))
    try:
        size = os.path.getsize(encode_filename(file_.filename))
        sizestr = "%s (%s)" % (bytes1human.decimal(size), bytes2human.binary(size))
        info.append((_('Size:'), sizestr))
    except BaseException:
        pass
    if file_.orig_metadata.length:
        info.append((_('Length:'), format_time(file_.orig_metadata.length)))
    if '~bitrate' in file_.orig_metadata:
        info.append((_('Bitrate:'), '%s kbps' % file_.orig_metadata['~bitrate']))
    if '~sample_rate' in file_.orig_metadata:
        info.append((_('Sample rate:'), '%s Hz' % file_.orig_metadata['~sample_rate']))
    if '~bits_per_sample' in file_.orig_metadata:
        info.append((_('Bits per sample:'), str(file_.orig_metadata['~bits_per_sample'])))
    if '~channels' in file_.orig_metadata:
        ch = file_.orig_metadata['~channels']
        if ch == '1':
            ch = _('Mono')
        elif ch == '2':
            ch = _('Stereo')
        info.append((_('Channels:'), ch))
    return '<br/>'.join(map(lambda i: '<b>%s</b> %s' %
                            (htmlescape(i[0]),
                             htmlescape(i[1])), info))
Example #13
0
 def drop_urls(urls, target):
     files = []
     new_files = []
     for url in urls:
         log.debug("Dropped the URL: %r", url.toString(QtCore.QUrl.RemoveUserInfo))
         if url.scheme() == "file" or not url.scheme():
             filename = os.path.normpath(os.path.realpath(url.toLocalFile().rstrip("\0")))
             file = BaseTreeView.tagger.files.get(filename)
             if file:
                 files.append(file)
             elif os.path.isdir(encode_filename(filename)):
                 BaseTreeView.tagger.add_directory(filename)
             else:
                 new_files.append(filename)
         elif url.scheme() in ("http", "https"):
             path = url.path()
             match = re.search(r"/(release|recording)/([0-9a-z\-]{36})", path)
             if match:
                 entity = match.group(1)
                 mbid = match.group(2)
                 if entity == "release":
                     BaseTreeView.tagger.load_album(mbid)
                 elif entity == "recording":
                     BaseTreeView.tagger.load_nat(mbid)
     if files:
         BaseTreeView.tagger.move_files(files, target)
     if new_files:
         BaseTreeView.tagger.add_files(new_files, target=target)
Example #14
0
 def _next_filename(self, filename, counters):
     if counters[filename]:
         new_filename = "%s (%d)" % (decode_filename(filename), counters[filename])
     else:
         new_filename = filename
     counters[filename] += 1
     return encode_filename(new_filename)
Example #15
0
    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 image in metadata.images:
                if self.config.setting["save_only_front_images_to_tags"] and image["type"] != "front":
                    continue
                imagetype = ID3_IMAGE_TYPE_MAP.get(image["type"], 0)
                tag_data = pack_image(image["mime"], image["data"], imagetype,
                                      image["description"])
                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()
Example #16
0
def _shorten_to_bytes_length(text, length):
    """Truncates a unicode object to the given number of bytes it would take
    when encoded in the "filesystem encoding".
    """
    assert isinstance(text, str), "This function only works on unicode"
    raw = encode_filename(text)
    # maybe there's no need to truncate anything
    if len(raw) <= length:
        return text
    # or maybe there's nothing multi-byte here
    if len(raw) == len(text):
        return text[:length]
    # if we're dealing with utf-8, we can use an efficient algorithm
    # to deal with character boundaries
    if _re_utf8.match(_io_encoding):
        i = length
        # a UTF-8 intermediate byte starts with the bits 10xxxxxx,
        # so ord(char) & 0b11000000 = 0b10000000
        while i > 0 and (raw[i] & 0xC0) == 0x80:
            i -= 1
        return decode_filename(raw[:i])
    # finally, a brute force approach
    i = length
    while i > 0:
        try:
            return decode_filename(raw[:i])
        except UnicodeDecodeError:
            pass
        i -= 1
    # hmm. we got here?
    return ""
Example #17
0
    def _load(self, filename):
        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, description) = unpack_image(image.value)
                    try:
                        coverartimage = TagCoverArtImage(
                            file=filename,
                            tag=name,
                            types=types_from_id3(type),
                            comment=description,
                            support_types=True,
                            data=data,
                        )
                    except CoverArtImageError as e:
                        log.error('Cannot load image from %r: %s' %
                                  (filename, e))
                    else:
                        metadata.append_image(coverartimage)

                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 * (config.setting['rating_steps'] - 1)))
            name = self.__RTRANS[name]
            values = filter(bool, map(unicode, values))
            if values:
                metadata[name] = values
        self._info(metadata, file)
        return metadata
Example #18
0
    def _save(self, filename, metadata):
        log.debug("Saving file %r", filename)
        file = ASF(encode_filename(filename))

        if config.setting['clear_existing_tags']:
            file.tags.clear()
        cover = []
        for image in metadata.images_to_be_saved_to_tags:
            tag_data = pack_image(image.mimetype, image.data,
                                    image_type_as_id3_num(image.maintype),
                                    image.comment)
            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 / (config.setting['rating_steps'] - 1)
            elif name == 'discnumber' and 'totaldiscs' in metadata:
                values[0] = '%s/%s' % (metadata['discnumber'], metadata['totaldiscs'])
            if name not in self.__TRANS:
                continue
            name = self.__TRANS[name]
            file.tags[name] = map(unicode, values)
        file.save()
Example #19
0
 def _load(self, filename):
     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, description) = unpack_image(image.value)
                 extras = {
                     'desc': description,
                     'type': image_type_from_id3_num(type)
                 }
                 metadata.add_image(mime, data, extras=extras)
             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 * (config.setting['rating_steps'] - 1)))
         name = self.__RTRANS[name]
         values = filter(bool, map(unicode, values))
         if values:
             metadata[name] = values
     self._info(metadata, file)
     return metadata
Example #20
0
 def _save_images(self, filename, metadata, settings):
     """Save the cover images to disk."""
     if not metadata.images:
         return
     overwrite = settings["save_images_overwrite"]
     image_filename = self._script_to_filename(
         settings["cover_image_filename"], metadata, settings)
     if not image_filename:
         image_filename = "cover"
     if os.path.isabs(image_filename):
         filename = image_filename
     else:
         filename = os.path.join(os.path.dirname(filename), image_filename)
     if settings['windows_compatible_filenames'] or sys.platform == 'win32':
         filename = filename.replace('./', '_/').replace('.\\', '_\\')
     filename = encode_filename(filename)
     i = 0
     for mime, data in metadata.images:
         image_filename = filename
         ext = mimetype.get_extension(mime, ".jpg")
         if i > 0:
             image_filename = "%s (%d)" % (filename, i)
         i += 1
         while os.path.exists(image_filename + ext) and not overwrite:
             if os.path.getsize(image_filename + ext) == len(data):
                 self.log.debug("Identical file size, not saving %r", image_filename)
                 break
             image_filename = "%s (%d)" % (filename, i)
             i += 1
         else:
             self.log.debug("Saving cover images to %r", image_filename)
             f = open(image_filename + ext, "wb")
             f.write(data)
             f.close()
Example #21
0
 def _display_info_tab(self):
     file = self.obj
     info = []
     info.append((_('Filename:'), file.filename))
     if '~format' in file.orig_metadata:
         info.append((_('Format:'), file.orig_metadata['~format']))
     try:
         size = os.path.getsize(encode_filename(file.filename))
         sizestr = "%s (%s)" % (bytes2human.decimal(size), bytes2human.binary(size))
         info.append((_('Size:'), sizestr))
     except:
         pass
     if file.orig_metadata.length:
         info.append((_('Length:'), format_time(file.orig_metadata.length)))
     if '~bitrate' in file.orig_metadata:
         info.append((_('Bitrate:'), '%s kbps' % file.orig_metadata['~bitrate']))
     if '~sample_rate' in file.orig_metadata:
         info.append((_('Sample rate:'), '%s Hz' % file.orig_metadata['~sample_rate']))
     if '~bits_per_sample' in file.orig_metadata:
         info.append((_('Bits per sample:'), str(file.orig_metadata['~bits_per_sample'])))
     if '~channels' in file.orig_metadata:
         ch = file.orig_metadata['~channels']
         if ch == 1:
             ch = _('Mono')
         elif ch == 2:
             ch = _('Stereo')
         else:
             ch = str(ch)
         info.append((_('Channels:'), ch))
     text = '<br/>'.join(map(lambda i: '<b>%s</b><br/>%s' %
                             (cgi.escape(i[0]),
                              cgi.escape(i[1])), info))
     self.ui.info.setText(text)
Example #22
0
 def drop_urls(urls, target):
     files = []
     new_files = []
     for url in urls:
         if url.scheme() == "file" or not url.scheme():
             # Dropping a file from iTunes gives a filename with a NULL terminator
             filename = os.path.normpath(os.path.realpath(unicode(url.toLocalFile()).rstrip("\0")))
             file = BaseTreeView.tagger.files.get(filename)
             if file:
                 files.append(file)
             elif os.path.isdir(encode_filename(filename)):
                 BaseTreeView.tagger.add_directory(filename)
             else:
                 new_files.append(filename)
         elif url.scheme() in ("http", "https"):
             path = unicode(url.path())
             match = re.search(r"/(release|recording)/([0-9a-z\-]{36})", path)
             if match:
                 entity = match.group(1)
                 mbid = match.group(2)
                 if entity == "release":
                     BaseTreeView.tagger.load_album(mbid)
                 elif entity == "recording":
                     BaseTreeView.tagger.load_nat(mbid)
     if files:
         BaseTreeView.tagger.move_files(files, target)
     if new_files:
         BaseTreeView.tagger.add_files(new_files, target=target)
Example #23
0
File: asf.py Project: m42i/picard
    def _save(self, filename, metadata):
        log.debug("Saving file %r", filename)
        file = ASF(encode_filename(filename))

        if config.setting['clear_existing_tags']:
            file.tags.clear()
        if config.setting['save_images_to_tags']:
            cover = []
            for image in metadata.images:
                if not save_this_image_to_tags(image):
                    continue
                tag_data = pack_image(image.mimetype, image.data,
                                      image_type_as_id3_num(image.maintype()),
                                      image.description)
                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 / (config.setting['rating_steps'] - 1)
            if name not in self.__TRANS:
                continue
            name = self.__TRANS[name]
            file.tags[name] = map(unicode, values)
        file.save()
Example #24
0
def _shorten_to_bytes_length(text, length):
    """Truncates a unicode object to the given number of bytes it would take
    when encoded in the "filesystem encoding".
    """
    assert isinstance(text, unicode), "This function only works on unicode"
    raw = encode_filename(text)
    # maybe there's no need to truncate anything
    if len(raw) <= length:
        return text
    # or maybe there's nothing multi-byte here
    if len(raw) == len(text):
        return text[:length]
    # if we're dealing with utf-8, we can use an efficient algorithm
    # to deal with character boundaries
    if _re_utf8.match(_io_encoding):
        i = length
        # a UTF-8 intermediate byte starts with the bits 10xxxxxx,
        # so ord(char) & 0b11000000 = 0b10000000
        while i > 0 and (ord(raw[i]) & 0xC0) == 0x80:
            i -= 1
        return decode_filename(raw[:i])
    # finally, a brute force approach
    i = length
    while i > 0:
        try:
            return decode_filename(raw[:i])
        except UnicodeDecodeError:
            pass
        i -= 1
    # hmm. we got here?
    return u""
Example #25
0
    def _save(self, filename, metadata):
        log.debug("Saving file %r", filename)
        file = MP4(encode_filename(self.filename))
        if file.tags is None:
            file.add_tags()

        if config.setting["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 config.setting['save_images_to_tags']:
            covr = []
            for image in metadata.images:
                if not save_this_image_to_tags(image):
                    continue
                mime = image["mime"]
                if mime == "image/jpeg":
                    covr.append(MP4Cover(image["data"], MP4Cover.FORMAT_JPEG))
                elif mime == "image/png":
                    covr.append(MP4Cover(image["data"], MP4Cover.FORMAT_PNG))
            if covr:
                file.tags["covr"] = covr

        file.save()
Example #26
0
    def _save(self, filename, metadata):
        log.debug("Saving file %r", filename)
        file = MP4(encode_filename(self.filename))
        tags = file.tags
        if tags is None:
            file.add_tags()

        if config.setting["clear_existing_tags"]:
            tags.clear()

        for name, values in metadata.rawitems():
            if name.startswith('lyrics:'):
                name = 'lyrics'
            if name in self.__r_text_tags:
                tags[self.__r_text_tags[name]] = values
            elif name in self.__r_bool_tags:
                tags[self.__r_bool_tags[name]] = (values[0] == '1')
            elif name in self.__r_int_tags:
                try:
                    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]
                tags[self.__r_freeform_tags[name]] = values
            elif name == "musicip_fingerprint":
                tags["----:com.apple.iTunes:fingerprint"] = [
                    b"MusicMagic Fingerprint%s" % v.encode('ascii')
                    for v in values
                ]

        if "tracknumber" in metadata:
            if "totaltracks" in metadata:
                tags["trkn"] = [(int(metadata["tracknumber"]),
                                 int(metadata["totaltracks"]))]
            else:
                tags["trkn"] = [(int(metadata["tracknumber"]), 0)]

        if "discnumber" in metadata:
            if "totaldiscs" in metadata:
                tags["disk"] = [(int(metadata["discnumber"]),
                                 int(metadata["totaldiscs"]))]
            else:
                tags["disk"] = [(int(metadata["discnumber"]), 0)]

        covr = []
        for image in metadata.images_to_be_saved_to_tags:
            if image.mimetype == "image/jpeg":
                covr.append(MP4Cover(image.data, MP4Cover.FORMAT_JPEG))
            elif image.mimetype == "image/png":
                covr.append(MP4Cover(image.data, MP4Cover.FORMAT_PNG))
        if covr:
            tags["covr"] = covr

        self._remove_deleted_tags(metadata, tags)

        file.save()
Example #27
0
 def _move_additional_files(self, old_filename, new_filename):
     """Move extra files, like playlists..."""
     old_path = encode_filename(os.path.dirname(old_filename))
     new_path = encode_filename(os.path.dirname(new_filename))
     patterns = encode_filename(config.setting["move_additional_files_pattern"])
     patterns = filter(bool, [p.strip() for p in patterns.split()])
     for pattern in patterns:
         # FIXME glob1 is not documented, maybe we need our own implemention?
         for old_file in glob.glob1(old_path, pattern):
             new_file = os.path.join(new_path, old_file)
             old_file = os.path.join(old_path, old_file)
             # FIXME we shouldn't do this from a thread!
             if self.tagger.files.get(decode_filename(old_file)):
                 log.debug("File loaded in the tagger, not moving %r", old_file)
                 continue
             log.debug("Moving %r to %r", old_file, new_file)
             shutil.move(old_file, new_file)
Example #28
0
 def callback(self, objs):
     files = [o for o in objs if isinstance(o, File)]
     for file in files:
         if self.fix(encode_filename(file.filename)):
             self.log.info("fix_mp4_meta: %s - Fixed", file.filename)
         else:
             self.log.info("fix_mp4_meta: %s - Not needed", file.filename)
         QtCore.QCoreApplication.processEvents()
Example #29
0
 def _rename(self, old_filename, metadata):
     new_filename, ext = os.path.splitext(self._make_filename(old_filename, metadata))
     if old_filename != new_filename + ext:
         new_dirname = os.path.dirname(new_filename)
         if not os.path.isdir(encode_filename(new_dirname)):
             os.makedirs(new_dirname)
         tmp_filename = new_filename
         i = 1
         while not pathcmp(old_filename, new_filename + ext) and os.path.exists(encode_filename(new_filename + ext)):
             new_filename = "%s (%d)" % (tmp_filename, i)
             i += 1
         new_filename = new_filename + ext
         log.debug("Moving file %r => %r", old_filename, new_filename)
         shutil.move(encode_filename(old_filename), encode_filename(new_filename))
         return new_filename
     else:
         return old_filename
Example #30
0
 def callback(self, objs):
     files = [o for o in objs if isinstance(o, File)]
     for file in files:
         if self.fix(encode_filename(file.filename)):
             self.log.info("fix_mp4_meta: %s - Fixed", file.filename)
         else:
             self.log.info("fix_mp4_meta: %s - Not needed", file.filename)
         QtCore.QCoreApplication.processEvents()
Example #31
0
 def _save(self, filename, metadata):
     if config.setting['aac_save_ape']:
         super()._save(filename, metadata)
     elif config.setting['remove_ape_from_aac']:
         try:
             mutagen.apev2.delete(encode_filename(filename))
         except BaseException:
             log.exception('Error removing APEv2 tags from %s', filename)
Example #32
0
 def _move_additional_files(self, old_filename, new_filename):
     """Move extra files, like playlists..."""
     old_path = encode_filename(os.path.dirname(old_filename))
     new_path = encode_filename(os.path.dirname(new_filename))
     patterns = encode_filename(config.setting["move_additional_files_pattern"])
     patterns = filter(bool, [p.strip() for p in patterns.split()])
     for pattern in patterns:
         # FIXME glob1 is not documented, maybe we need our own implemention?
         for old_file in glob.glob1(old_path, pattern):
             new_file = os.path.join(new_path, old_file)
             old_file = os.path.join(old_path, old_file)
             # FIXME we shouldn't do this from a thread!
             if self.tagger.files.get(decode_filename(old_file)):
                 log.debug("File loaded in the tagger, not moving %r", old_file)
                 continue
             log.debug("Moving %r to %r", old_file, new_file)
             shutil.move(old_file, new_file)
Example #33
0
 def _save_and_rename(self, old_filename, metadata):
     """Save the metadata."""
     # Check that file has not been removed since thread was queued
     # Also don't save if we are stopping.
     if self.state == File.REMOVED:
         log.debug("File not saved because it was removed: %r", self.filename)
         return None
     if self.tagger.stopping:
         log.debug("File not saved because %s is stopping: %r", PICARD_APP_NAME, self.filename)
         return None
     new_filename = old_filename
     if not config.setting["dont_write_tags"]:
         encoded_old_filename = encode_filename(old_filename)
         info = os.stat(encoded_old_filename)
         self._save(old_filename, metadata)
         if config.setting["preserve_timestamps"]:
             try:
                 os.utime(encoded_old_filename, (info.st_atime, info.st_mtime))
             except OSError:
                 log.warning("Couldn't preserve timestamp for %r", old_filename)
     # Rename files
     if config.setting["rename_files"] or config.setting["move_files"]:
         new_filename = self._rename(old_filename, metadata)
     # Move extra files (images, playlists, etc.)
     if config.setting["move_files"] and config.setting["move_additional_files"]:
         self._move_additional_files(old_filename, new_filename)
     # Delete empty directories
     if config.setting["delete_empty_dirs"]:
         dirname = encode_filename(os.path.dirname(old_filename))
         try:
             self._rmdir(dirname)
             head, tail = os.path.split(dirname)
             if not tail:
                 head, tail = os.path.split(head)
             while head and tail:
                 try:
                     self._rmdir(head)
                 except:
                     break
                 head, tail = os.path.split(head)
         except EnvironmentError:
             pass
     # Save cover art images
     if config.setting["save_images_to_files"]:
         self._save_images(os.path.dirname(new_filename), metadata)
     return new_filename
Example #34
0
    def _save(self, filename, metadata):
        """Save metadata to the file."""
        log.debug("Saving file %r", filename)
        try:
            tags = mutagen.apev2.APEv2(encode_filename(filename))
        except mutagen.apev2.APENoHeaderError:
            tags = mutagen.apev2.APEv2()
        if config.setting["clear_existing_tags"]:
            tags.clear()
        elif metadata.images_to_be_saved_to_tags:
            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("~") or not self.supports_tag(name):
                continue
            real_name = self._get_tag_name(name)
            # tracknumber/totaltracks => Track
            if name == 'tracknumber':
                if 'totaltracks' in metadata:
                    value = '%s/%s' % (value, metadata['totaltracks'])
            # discnumber/totaldiscs => Disc
            elif name == 'discnumber':
                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)
                if desc:
                    value += ' (%s)' % desc
            temp.setdefault(real_name, []).append(value)
        for name, values in temp.items():
            tags[name] = values
        for image in metadata.images_to_be_saved_to_tags:
            cover_filename = 'Cover Art (Front)'
            cover_filename += image.extension
            tags['Cover Art (Front)'] = mutagen.apev2.APEValue(cover_filename.encode('ascii') + b'\0' + image.data, mutagen.apev2.BINARY)
            break
            # can't save more than one item with the same name
            # (mp3tags does this, but it's against the specs)

        self._remove_deleted_tags(metadata, tags)
        tags.save(encode_filename(filename))
Example #35
0
File: asf.py Project: ruipin/picard
    def _load(self, filename):
        log.debug("Loading file %r", filename)
        config = get_config()
        self.__casemap = {}
        file = ASF(encode_filename(filename))
        metadata = Metadata()
        for name, values in file.tags.items():
            if name == 'WM/Picture':
                for image in values:
                    try:
                        (mime, data, image_type,
                         description) = unpack_image(image.value)
                    except ValueError as e:
                        log.warning('Cannot unpack image from %r: %s',
                                    filename, e)
                        continue
                    try:
                        coverartimage = TagCoverArtImage(
                            file=filename,
                            tag=name,
                            types=types_from_id3(image_type),
                            comment=description,
                            support_types=True,
                            data=data,
                            id3_type=image_type,
                        )
                    except CoverArtImageError as e:
                        log.error('Cannot load image from %r: %s' %
                                  (filename, e))
                    else:
                        metadata.images.append(coverartimage)

                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(str(values[0])) / 99.0 *
                        (config.setting['rating_steps'] - 1)))
            elif name == 'WM/PartOfSet':
                disc = str(values[0]).split("/")
                if len(disc) > 1:
                    metadata["totaldiscs"] = disc[1]
                    values[0] = disc[0]
            name_lower = name.lower()
            if name in self.__RTRANS:
                name = self.__RTRANS[name]
            elif name_lower in self.__RTRANS_CI:
                orig_name = name
                name = self.__RTRANS_CI[name_lower]
                self.__casemap[name] = orig_name
            else:
                continue
            values = [str(value) for value in values if value]
            if values:
                metadata[name] = values
        self._info(metadata, file)
        return metadata
Example #36
0
 def _save_and_rename(self, old_filename, metadata):
     """Save the metadata."""
     new_filename = old_filename
     if not config.setting["dont_write_tags"]:
         encoded_old_filename = encode_filename(old_filename)
         info = os.stat(encoded_old_filename)
         self._save(old_filename, metadata)
         if config.setting["preserve_timestamps"]:
             try:
                 os.utime(encoded_old_filename,
                          (info.st_atime, info.st_mtime))
             except OSError:
                 log.warning("Couldn't preserve timestamp for %r",
                             old_filename)
     # Rename files
     if config.setting["rename_files"] or config.setting["move_files"]:
         new_filename = self._rename(old_filename, metadata)
     # Move extra files (images, playlists, etc.)
     if config.setting["move_files"] and config.setting[
             "move_additional_files"]:
         self._move_additional_files(old_filename, new_filename)
     #changes, delete extra files
     if config.setting["move_files"] and config.setting[
             "delete_additional_files"]:
         self._delete_additional_files(old_filename)
     # Delete empty directories
     if config.setting["delete_empty_dirs"]:
         dirname = encode_filename(os.path.dirname(old_filename))
         try:
             self._rmdir(dirname)
             head, tail = os.path.split(dirname)
             if not tail:
                 head, tail = os.path.split(head)
             while head and tail:
                 try:
                     self._rmdir(head)
                 except:
                     break
                 head, tail = os.path.split(head)
         except EnvironmentError:
             pass
     # Save cover art images
     if config.setting["save_images_to_files"]:
         self._save_images(os.path.dirname(new_filename), metadata)
     return new_filename
Example #37
0
    def _rename(self, old_filename, metadata):
        old_dir = os.path.dirname(self.filename)
        new_dir = os.path.dirname(encode_filename(
                                    self._make_filename(old_filename,
                                                        metadata
                                                        )))
        filename = os.path.basename(old_filename)
        new_filename = os.path.join(new_dir, filename)
        audiofile = " ".join(self.fileline.split()[0:-1]).strip('"')

        old_audiofile = os.path.join(old_dir, audiofile)
        new_audiofile = os.path.join(new_dir, audiofile)

        if not os.path.isdir(new_dir):
            os.makedirs(new_dir)

        if os.path.exists(old_filename):
            self.log.debug("Moving file %r => %r", old_filename, new_filename)
            shutil.move(encode_filename(old_filename), encode_filename(new_filename))
        if os.path.exists(old_audiofile):
            self.log.debug("Moving file %r => %r", old_audiofile, new_audiofile)
            shutil.move(encode_filename(old_audiofile), encode_filename(new_audiofile))
        # wavpack
        wvc_filename = old_audiofile.replace(".wv", ".wvc")
        if os.path.isfile(wvc_filename):
            new_wvc_filename = new_audiofile.replace(".wv", ".wvc")
            shutil.move(encode_filename(wvc_filename),
                        encode_filename(new_wvc_filename))
        return new_filename
Example #38
0
 def add_directory(self, path):
     path = encode_filename(path)
     self.other_queue.put(
         (
             partial(os.listdir, path),
             partial(self.process_directory_listing, path, deque()),
             QtCore.Qt.LowEventPriority,
         )
     )
Example #39
0
 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 = value.rfind(' (')
                     if start > 0:
                         name += value[start + 2:-1]
                         value = value[:start]
             elif name.startswith('rating'):
                 try:
                     name, email = name.split(':', 1)
                 except ValueError:
                     email = ''
                 if email != self.config.setting['rating_user_email']:
                     continue
                 name = '~rating'
                 value = unicode(
                     int(
                         round(
                             (float(value) *
                              (self.config.setting['rating_steps'] - 1)))))
             elif name == "fingerprint" and value.startswith(
                     "MusicMagic Fingerprint"):
                 name = "musicip_fingerprint"
                 value = value[22:]
             elif name == "tracktotal" and "totaltracks" not in file.tags:
                 name = "totaltracks"
             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
Example #40
0
 def write(self):
     b_lines = []
     for header in self.headers:
         b_lines.append(header.encode("utf-8"))
     for entry in self.entries:
         for row in entry:
             b_lines.append(unicode(row).encode("utf-8"))
     with open(encode_filename(self.filename), "wt") as f:
         f.writelines(b_lines)
Example #41
0
 def write(self):
     b_lines = []
     for header in self.headers:
         b_lines.append(header.encode("utf-8"))
     for entry in self.entries:
         for row in entry:
             b_lines.append(unicode(row).encode("utf-8"))
     with open(encode_filename(self.filename), "wt") as f:
         f.writelines(b_lines)
Example #42
0
    def _load(self, filename):
        log.debug("Loading file %r", filename)
        self.__casemap = {}
        file = self._File(encode_filename(filename))
        metadata = Metadata()
        if file.tags:
            for origname, values in file.tags.items():
                name_lower = origname.lower()
                if (values.kind == mutagen.apev2.BINARY
                        and name_lower.startswith("cover art")):
                    if b'\0' in values.value:
                        descr, data = values.value.split(b'\0', 1)
                        try:
                            coverartimage = TagCoverArtImage(
                                file=filename,
                                tag=name_lower,
                                data=data,
                            )
                        except CoverArtImageError as e:
                            log.error('Cannot load image from %r: %s' %
                                      (filename, e))
                        else:
                            metadata.images.append(coverartimage)

                # skip EXTERNAL and BINARY values
                if values.kind != mutagen.apev2.TEXT:
                    continue
                for value in values:
                    name = name_lower
                    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 + ':'
                        if value.endswith(')'):
                            start = value.rfind(' (')
                            if start > 0:
                                name += value[start + 2:-1]
                                value = value[:start]
                    elif name in self.__rtranslate:
                        name = self.__rtranslate[name]
                    self.__casemap[name] = origname
                    metadata.add(name, value)
        self._info(metadata, file)
        return metadata
Example #43
0
File: mp4.py Project: skyme5/picard
    def _load(self, filename):
        log.debug("Loading file %r", filename)
        self.__casemap = {}
        file = MP4(encode_filename(filename))
        tags = file.tags or {}
        metadata = Metadata()
        for name, values in tags.items():
            name_lower = name.lower()
            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], str(value))
            elif name in self.__freeform_tags:
                for value in values:
                    value = value.decode("utf-8", "replace").strip("\x00")
                    metadata.add(self.__freeform_tags[name], value)
            elif name_lower in self.__freeform_tags_ci:
                for value in values:
                    value = value.decode("utf-8", "replace").strip("\x00")
                    tag_name = self.__freeform_tags_ci[name_lower]
                    metadata.add(tag_name, value)
                    self.__casemap[tag_name] = name
            elif name == "----:com.apple.iTunes:fingerprint":
                for value in values:
                    value = value.decode("utf-8", "replace").strip("\x00")
                    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.imageformat not in (value.FORMAT_JPEG,
                                                 value.FORMAT_PNG):
                        continue
                    try:
                        coverartimage = TagCoverArtImage(
                            file=filename,
                            tag=name,
                            data=value,
                        )
                    except CoverArtImageError as e:
                        log.error('Cannot load image from %r: %s' %
                                  (filename, e))
                    else:
                        metadata.images.append(coverartimage)

        self._info(metadata, file)
        return metadata
Example #44
0
 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'
     return metadata
Example #45
0
 def run(self):
   device = self.config.setting["cd_lookup_device"].split(",", 1)[0]
   disc = Disc()
   disc.read(encode_filename(device))
   self.discid = disc.id
   self.log.debug('CD has discid: %s', disc.id)
   self.rip()
   result = self.widget.exec_()
   if result == self.widget.Rejected:
     self._cleanup()
Example #46
0
 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'
     return metadata
Example #47
0
    def _save(self, filename, metadata):
        log.debug("Saving file %r", filename)
        file = MP4(encode_filename(self.filename))
        if file.tags is None:
            file.add_tags()

        if config.setting["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 config.setting['save_images_to_tags']:
            covr = []
            for image in metadata.images:
                if not save_this_image_to_tags(image):
                    continue
                mime = image["mime"]
                if mime == "image/jpeg":
                    covr.append(MP4Cover(image["data"], MP4Cover.FORMAT_JPEG))
                elif mime == "image/png":
                    covr.append(MP4Cover(image["data"], MP4Cover.FORMAT_PNG))
            if covr:
                file.tags["covr"] = covr

        file.save()
Example #48
0
    def _load(self, filename):
        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)
                        try:
                            coverartimage = TagCoverArtImage(
                                file=filename,
                                tag=origname,
                                data=data,
                            )
                        except CoverArtImageError as e:
                            log.error('Cannot load image from %r: %s' %
                                      (filename, e))
                        else:
                            metadata.append_image(coverartimage)

                # 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
Example #49
0
 def _make_image_filename(self, image_filename, dirname, metadata):
     image_filename = self._script_to_filename(image_filename, metadata)
     if not image_filename:
         image_filename = "cover"
     if os.path.isabs(image_filename):
         filename = image_filename
     else:
         filename = os.path.join(dirname, image_filename)
     if config.setting['windows_compatibility'] or sys.platform == 'win32':
         filename = filename.replace('./', '_/').replace('.\\', '_\\')
     return encode_filename(filename)
Example #50
0
 def _rename(self, old_filename, metadata, settings):
     new_filename, ext = os.path.splitext(
         self._make_filename(old_filename, metadata, settings))
     if old_filename != new_filename + ext:
         new_dirname = os.path.dirname(new_filename)
         if not os.path.isdir(encode_filename(new_dirname)):
             os.makedirs(new_dirname)
         tmp_filename = new_filename
         i = 1
         while (not pathcmp(old_filename, new_filename + ext)
                and os.path.exists(encode_filename(new_filename + ext))):
             new_filename = "%s (%d)" % (tmp_filename, i)
             i += 1
         new_filename = new_filename + ext
         self.log.debug("Moving file %r => %r", old_filename, new_filename)
         shutil.move(encode_filename(old_filename),
                     encode_filename(new_filename))
         return new_filename
     else:
         return old_filename
Example #51
0
 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 = value.rfind(' (')
                     if start > 0:
                         name += value[start + 2:-1]
                         value = value[:start]
             elif name.startswith('rating'):
                 try: name, email = name.split(':', 1)
                 except ValueError: email = ''
                 if email != self.config.setting['rating_user_email']:
                     continue
                 name = '~rating'
                 value = unicode(int(round((float(value) * (self.config.setting['rating_steps'] - 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
Example #52
0
 def _make_image_filename(self, image_filename, dirname, metadata):
     image_filename = self._script_to_filename(image_filename, metadata)
     if not image_filename:
         image_filename = "cover"
     if os.path.isabs(image_filename):
         filename = image_filename
     else:
         filename = os.path.join(dirname, image_filename)
     if config.setting['windows_compatible_filenames'] or sys.platform == 'win32':
         filename = filename.replace('./', '_/').replace('.\\', '_\\')
     return encode_filename(filename)
Example #53
0
 def _delete_additional_files(self, old_filename):
     """Delete all other files of type that are not being saved.."""
     """Retrieve path of saved file"""
     old_path = encode_filename(os.path.dirname(old_filename))
     """Retrieve patterns(types) to be deleted"""
     patterns = encode_filename(
         config.setting["delete_additional_files_pattern"])
     patterns = filter(bool, [p.strip() for p in patterns.split()])
     for pattern in patterns:
         # FIXME glob1 is not documented, maybe we need our own implementation?
         for old_file in glob.glob1(old_path, pattern):
             old_file = os.path.join(old_path, old_file)
             # FIXME we shouldn't do this from a thread!
             if self.tagger.files.get(decode_filename(old_file)):
                 """Ensures file being saved is not deleted"""
                 log.debug("File loaded in the tagger, not deleting %r",
                           old_file)
                 continue
             log.debug("Deleting %r", old_file)
             os.remove(old_file)
Example #54
0
 def _make_image_filename(self, image_filename, dirname, metadata):
     image_filename = self._script_to_filename(image_filename, metadata)
     if not image_filename:
         image_filename = "cover"
     if os.path.isabs(image_filename):
         filename = image_filename
     else:
         filename = os.path.join(dirname, image_filename)
     if config.setting["windows_compatible_filenames"] or sys.platform == "win32":
         filename = filename.replace("./", "_/").replace(".\\", "_\\")
     return encode_filename(filename)
Example #55
0
def calculate_replay_gain_for_files(files, format, tagger):
    """Calculates the replay gain for a list of files in album mode."""
    file_list = ['%s' % encode_filename(f.filename) for f in files]

    if format in REPLAYGAIN_COMMANDS \
        and tagger.config.setting[REPLAYGAIN_COMMANDS[format][0]]:
        command = tagger.config.setting[REPLAYGAIN_COMMANDS[format][0]]
        options = tagger.config.setting[REPLAYGAIN_COMMANDS[format][1]].split(' ')
        tagger.log.debug('%s %s %s' % (command, ' '.join(options), decode_filename(' '.join(file_list))))
        check_call([command] + options + file_list)
    else:
        raise Exception('ReplayGain: Unsupported format %s' % (format))
Example #56
0
    def lookup_cd(self, action=None):
        """Reads CD from the selected drive and tries to lookup the DiscID on MusicBrainz."""
        if action is None:
            device = self.config.setting["cd_lookup_device"].split(",", 1)[0]
        else:
            device = unicode(action.text())

        disc = Disc()
        self.set_wait_cursor()
        self.other_queue.put((partial(disc.read, encode_filename(device)),
                              partial(self._lookup_disc,
                                      disc), QtCore.Qt.LowEventPriority))
Example #57
0
 def install_plugin(self, path):
     path = encode_filename(path)
     file = os.path.basename(path)
     dest = os.path.join(self.tagger.user_plugin_dir, file)
     if os.path.exists(dest):
         msgbox = QtGui.QMessageBox(self)
         msgbox.setText("A plugin named %s is already installed." % file)
         msgbox.setInformativeText("Do you want to overwrite the existing plugin?")
         msgbox.setStandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
         msgbox.setDefaultButton(QtGui.QMessageBox.No)
         if msgbox.exec_() == QtGui.QMessageBox.No:
             return
     self.tagger.pluginmanager.install_plugin(path, dest)
Example #58
0
 def drop_urls(urls, target):
     files = []
     new_files = []
     for url in urls:
         log.debug("Dropped the URL: %r",
                   url.toString(QtCore.QUrl.RemoveUserInfo))
         if url.scheme() == "file" or not url.scheme():
             # Workaround for https://bugreports.qt.io/browse/QTBUG-40449
             # OSX Urls follow the NSURL scheme and need to be converted
             if sys.platform == 'darwin' and unicode(
                     url.path()).startswith('/.file/id='):
                 if NSURL_IMPORTED:
                     filename = os.path.normpath(
                         os.path.realpath(
                             unicode(
                                 NSURL.URLWithString_(str(url.toString())).
                                 filePathURL().path()).rstrip("\0")))
                     log.debug(
                         'OSX NSURL path detected. Dropped File is: %r',
                         filename)
                 else:
                     log.error("Unable to get appropriate file path for %r",
                               url.toString(QtCore.QUrl.RemoveUserInfo))
                     continue
             else:
                 # Dropping a file from iTunes gives a filename with a NULL terminator
                 filename = os.path.normpath(
                     os.path.realpath(
                         unicode(url.toLocalFile()).rstrip("\0")))
             file = BaseTreeView.tagger.files.get(filename)
             if file:
                 files.append(file)
             elif os.path.isdir(encode_filename(filename)):
                 BaseTreeView.tagger.add_directory(filename)
             else:
                 new_files.append(filename)
         elif url.scheme() in ("http", "https"):
             path = unicode(url.path())
             match = re.search(r"/(release|recording)/([0-9a-z\-]{36})",
                               path)
             if match:
                 entity = match.group(1)
                 mbid = match.group(2)
                 if entity == "release":
                     BaseTreeView.tagger.load_album(mbid)
                 elif entity == "recording":
                     BaseTreeView.tagger.load_nat(mbid)
     if files:
         BaseTreeView.tagger.move_files(files, target)
     if new_files:
         BaseTreeView.tagger.add_files(new_files, target=target)
Example #59
0
    def lookup_cd(self, action):
        """Reads CD from the selected drive and tries to lookup the DiscID on MusicBrainz."""
        if isinstance(action, QtWidgets.QAction):
            device = action.text()
        elif config.setting["cd_lookup_device"] != '':
            device = config.setting["cd_lookup_device"].split(",", 1)[0]
        else:
            # rely on python-discid auto detection
            device = None

        disc = Disc()
        self.set_wait_cursor()
        thread.run_task(partial(disc.read, encode_filename(device)),
                        partial(self._lookup_disc, disc))
Example #60
0
 def write(self):
     lines = []
     for track in self.tracks:
         num = track.index
         for line in track:
             indent = 0
             if num > 0:
                 if line[0] == "TRACK":
                     indent = 2
                 elif line[0] != "FILE":
                     indent = 4
             line2 = u" ".join([self.quote(s) for s in line])
             lines.append(" " * indent + line2.encode("UTF-8") + "\n")
     with open(encode_filename(self.filename), "wt") as f:
         f.writelines(lines)