Ejemplo n.º 1
0
    def test_thumb(s):
        thumb = thumbnails.get_thumbnail(s.filename, (50, 60))

        #check for right scaling
        s.failUnless(thumb)
        s.failUnlessEqual((thumb.get_width(), thumb.get_height()), (50, 3))

        #test the thumbnail filename
        uri = "file://" + pathname2url(s.filename)
        name = hash.md5(uri).hexdigest() + ".png"

        path = thumbnails.get_thumbnail_folder()
        path = os.path.join(path, "normal", name)

        s.failUnless(os.path.isfile(path))

        #check for metadata
        thumb_pb = GdkPixbuf.Pixbuf.new_from_file(path)
        meta_mtime = thumb_pb.get_option("tEXt::Thumb::MTime")
        meta_uri = thumb_pb.get_option("tEXt::Thumb::URI")

        s.failUnlessEqual(int(meta_mtime), int(mtime(s.filename)))
        s.failUnlessEqual(meta_uri, uri)

        #check rights
        if os.name != "nt":
            s.failUnlessEqual(os.stat(path).st_mode, 33152)
Ejemplo n.º 2
0
    def __init__(self, name, module, deps, path):
        self.name = name
        self.module = module
        self.path = path

        self.deps = {}
        for dep in deps:
            self.deps[dep] = mtime(dep)
Ejemplo n.º 3
0
    def __init__(self, name, module, deps, path):
        self.name = name
        self.module = module
        self.path = path

        self.deps = {}
        for dep in deps:
            self.deps[dep] = mtime(dep)
Ejemplo n.º 4
0
    def has_changed(self, dep_paths):
        if set(self.deps.keys()) != set(dep_paths):
            return True

        for path, old_mtime in iteritems(self.deps):
            if mtime(path) != old_mtime:
                return True

        return False
Ejemplo n.º 5
0
    def has_changed(self, dep_paths):
        if set(self.deps.keys()) != set(dep_paths):
            return True

        for path, old_mtime in self.deps.items():
            if mtime(path) != old_mtime:
                return True

        return False
Ejemplo n.º 6
0
    def copy(self, parent_widget, song):
        if not self.__pattern:
            self.__set_pattern()

        target = strip_win32_incompat_from_path(self.__pattern.format(song))
        dirname = os.path.dirname(target)

        if os.path.exists(target):
            dialog = ConfirmFileReplace(parent_widget, target)
            resp = dialog.run()
            if resp == ConfirmFileReplace.RESPONSE_REPLACE:
                try:
                    # Remove the current song
                    self.__library.remove([self.__library[target]])
                except KeyError:
                    pass
            else:
                return False

        try:
            if not os.path.isdir(dirname):
                os.makedirs(dirname)
            shutil.copyfile(song['~filename'], target)

            if self['covers']:
                coverfile = os.path.join(dirname, 'folder.jpg')
                cover = app.cover_manager.get_cover(song)
                if cover and mtime(cover.name) > mtime(coverfile):
                    image = GdkPixbuf.Pixbuf.new_from_file_at_size(
                        cover.name, 200, 200)
                    image.savev(coverfile, "jpeg", [], [])

            song = copy.deepcopy(song)
            song.sanitize(target)
            self.__library.add([song])
            return song
        except (OSError, IOError, GLib.GError) as exc:
            encoding = util.get_locale_encoding()
            return str(exc).decode(encoding, 'replace')
Ejemplo n.º 7
0
    def copy(self, parent_widget, song):
        if not self.__pattern:
            self.__set_pattern()

        target = strip_win32_incompat_from_path(self.__pattern.format(song))
        dirname = os.path.dirname(target)

        if os.path.exists(target):
            dialog = ConfirmFileReplace(parent_widget, target)
            resp = dialog.run()
            if resp == ConfirmFileReplace.RESPONSE_REPLACE:
                try:
                    # Remove the current song
                    self.__library.remove([self.__library[target]])
                except KeyError:
                    pass
            else:
                return False

        try:
            if not os.path.isdir(dirname):
                os.makedirs(dirname)
            shutil.copyfile(song['~filename'], target)

            if self['covers']:
                coverfile = os.path.join(dirname, 'folder.jpg')
                cover = app.cover_manager.get_cover(song)
                if cover and mtime(cover.name) > mtime(coverfile):
                    image = GdkPixbuf.Pixbuf.new_from_file_at_size(
                        cover.name, 200, 200)
                    image.savev(coverfile, "jpeg", [], [])

            song = copy.deepcopy(song)
            song.sanitize(target)
            self.__library.add([song])
            return song
        except (OSError, IOError, GLib.GError) as exc:
            encoding = util.get_locale_encoding()
            return str(exc).decode(encoding, 'replace')
Ejemplo n.º 8
0
def save(save_period=None):
    """Save all registered libraries that have a filename and are marked dirty.

    If `save_period` (seconds) is given the library will only be saved if
    it hasn't been in the last `save_period` seconds.
    """

    print_d("Saving all libraries...")

    librarian = SongFileLibrary.librarian
    for lib in librarian.libraries.values():
        filename = lib.filename
        if not filename or not lib.dirty:
            continue

        if not save_period or abs(time.time() - mtime(filename)) > save_period:
            lib.save()
Ejemplo n.º 9
0
    def __changed(self, selector, selection, label):
        model, rows = selection.get_selected_rows()
        files = []

        if len(rows) < 2:
            count = len(model or [])
        else:
            count = len(rows)
        label.set_text(numeric_phrase("%d song", "%d songs", count))

        for row in rows:
            filename = model[row][0]
            if not os.path.exists(filename):
                pass
            elif filename in self.__library:
                song = self.__library[filename]
                if song("~#mtime") + 1. < mtime(filename):
                    try:
                        song.reload()
                    except AudioFileError:
                        pass
                files.append(song)
            else:
                files.append(formats.MusicFile(filename))
        files = list(filter(None, files))
        if len(files) == 0:
            self.set_title("Ex Falso")
        elif len(files) == 1:
            self.set_title("%s - Ex Falso" % files[0].comma("title"))
        else:
            params = ({
                'title': files[0].comma("title"),
                'count': format_int_locale(len(files) - 1)
            })
            self.set_title("%s - Ex Falso" % (ngettext(
                "%(title)s and %(count)s more", "%(title)s and %(count)s more",
                len(files) - 1) % params))
        self.__library.add(files)
        self.emit('changed', files)
Ejemplo n.º 10
0
    def __changed(self, selector, selection, label):
        model, rows = selection.get_selected_rows()
        files = []

        if len(rows) < 2:
            count = len(model or [])
        else:
            count = len(rows)
        label.set_text(numeric_phrase("%d song", "%d songs", count))

        for row in rows:
            filename = model[row][0]
            if not os.path.exists(filename):
                pass
            elif filename in self.__library:
                song = self.__library[filename]
                if song("~#mtime") + 1. < mtime(filename):
                    try:
                        song.reload()
                    except AudioFileError:
                        pass
                files.append(song)
            else:
                files.append(formats.MusicFile(filename))
        files = list(filter(None, files))
        if len(files) == 0:
            self.set_title("Ex Falso")
        elif len(files) == 1:
            self.set_title("%s - Ex Falso" % files[0].comma("title"))
        else:
            params = ({'title': files[0].comma("title"),
                       'count': format_int_locale(len(files) - 1)})
            self.set_title(
                "%s - Ex Falso" %
                (ngettext("%(title)s and %(count)s more",
                          "%(title)s and %(count)s more", len(files) - 1)
                 % params))
        self.__library.add(files)
        self.emit('changed', files)
Ejemplo n.º 11
0
    def __changed(self, selector, selection, label):
        model, rows = selection.get_selected_rows()
        files = []

        if len(rows) < 2:
            count = len(model or [])
        else:
            count = len(rows)
        label.set_text(ngettext("%d song", "%d songs", count) % count)

        for row in rows:
            filename = model[row][0]
            if not os.path.exists(filename):
                pass
            elif filename in self.__library:
                file = self.__library[filename]
                if file("~#mtime") + 1. < mtime(filename):
                    try:
                        file.reload()
                    except StandardError:
                        pass
                files.append(file)
            else:
                files.append(formats.MusicFile(filename))
        files = filter(None, files)
        if len(files) == 0:
            self.set_title("Ex Falso")
        elif len(files) == 1:
            self.set_title("%s - Ex Falso" % files[0].comma("title"))
        else:
            self.set_title(
                "%s - Ex Falso" %
                (ngettext("%(title)s and %(count)d more",
                          "%(title)s and %(count)d more",
                          len(files) - 1) % (
                {'title': files[0].comma("title"), 'count': len(files) - 1})))
        self.__library.add(files)
        self.emit('changed', files)
    def __changed(self, selector, selection, label):
        model, rows = selection.get_selected_rows()
        files = []

        if len(rows) < 2:
            count = len(model or [])
        else:
            count = len(rows)
        label.set_text(ngettext("%d song", "%d songs", count) % count)

        for row in rows:
            filename = util.fsnative(model[row][0])
            if not os.path.exists(filename):
                pass
            elif filename in self.__library:
                file = self.__library[filename]
                if file("~#mtime") + 1. < mtime(filename):
                    try:
                        file.reload()
                    except StandardError:
                        pass
                files.append(file)
            else:
                files.append(formats.MusicFile(filename))
        files = filter(None, files)
        if len(files) == 0:
            self.set_title("Ex Falso")
        elif len(files) == 1:
            self.set_title("%s - Ex Falso" % files[0].comma("title"))
        else:
            self.set_title("%s - Ex Falso" % (ngettext(
                "%(title)s and %(count)d more", "%(title)s and %(count)d more",
                len(files) - 1) % ({
                    'title': files[0].comma("title"),
                    'count': len(files) - 1
                })))
        self.__library.add(files)
        self.emit('changed', files)
Ejemplo n.º 13
0
def save(force=False):
    """Save all registered libraries that have a filename and are marked dirty.

    If force = True save all of them blocking, else save non-blocking and
    only if they were last saved more than LIBRARY_SAVE_PERIOD_SECONDS ago.
    """

    print_d("Saving all libraries...")

    librarian = SongFileLibrary.librarian
    for lib in librarian.libraries.values():
        filename = lib.filename
        if not filename or not lib.dirty:
            continue

        if force:
            try:
                lib.save()
            except EnvironmentError:
                pass
            lib.destroy()
        elif time.time() - mtime(filename) > LIBRARY_SAVE_PERIOD_SECONDS:
            threading.Thread(target=lib.save).run()
Ejemplo n.º 14
0
def save(force=False):
    """Save all registered libraries that have a filename and are marked dirty.

    If force = True save all of them blocking, else save non-blocking and
    only if they were last saved more than LIBRARY_SAVE_PERIOD_SECONDS ago.
    """

    print_d("Saving all libraries...")

    librarian = SongFileLibrary.librarian
    for lib in librarian.libraries.values():
        filename = lib.filename
        if not filename or not lib.dirty:
            continue

        if force:
            try:
                lib.save()
            except EnvironmentError:
                pass
            lib.destroy()
        elif time.time() - mtime(filename) > LIBRARY_SAVE_PERIOD_SECONDS:
            threading.Thread(target=lib.save).run()
Ejemplo n.º 15
0
def get_thumbnail(path, boundary):
    """Get a thumbnail pixbuf of an image at `path`.

    Will create/use a thumbnail in the user's thumbnail directory if possible.
    Follows the Free Desktop specification:

    http://specifications.freedesktop.org/thumbnail-spec/

    Can raise GLib.GError. Thread-safe.
    """

    width, height = boundary
    new_from_file_at_size = GdkPixbuf.Pixbuf.new_from_file_at_size

    # larger than thumbnails, load directly
    if width > ThumbSize.LARGEST or height > ThumbSize.LARGEST:
        return new_from_file_at_size(path, width, height)

    path_mtime = mtime(path)
    if path_mtime == 0:
        return new_from_file_at_size(path, width, height)

    # embedded thumbnails come from /tmp/
    # FIXME: move this to another layer
    if path.startswith(tempfile.gettempdir()):
        return new_from_file_at_size(path, width, height)

    thumb_path, thumb_size = get_cache_info(path, boundary)
    cache_dir = os.path.dirname(thumb_path)
    try:
        mkdir(cache_dir, 0o700)
    except OSError:
        return new_from_file_at_size(path, width, height)

    try:
        pb = new_from_file_at_size(thumb_path, width, height)
    except GLib.GError:
        # in case it fails to load, we recreate it
        pass
    else:
        meta_mtime = pb.get_option("tEXt::Thumb::MTime")
        if meta_mtime is not None:
            try:
                meta_mtime = int(meta_mtime)
            except ValueError:
                pass
            else:
                if meta_mtime == int(path_mtime):
                    return pb

    info, pw, ph = GdkPixbuf.Pixbuf.get_file_info(path)

    # Too small picture, no thumbnail needed
    if pw < thumb_size and ph < thumb_size:
        return new_from_file_at_size(path, width, height)

    thumb_pb = new_from_file_at_size(path, thumb_size, thumb_size)

    uri = "file://" + pathname2url(path)
    mime = info.get_mime_types()[0]
    options = {
        "tEXt::Thumb::Image::Width": str(pw),
        "tEXt::Thumb::Image::Height": str(ph),
        "tEXt::Thumb::URI": uri,
        "tEXt::Thumb::MTime": str(int(path_mtime)),
        "tEXt::Thumb::Size": str(os.path.getsize(path)),
        "tEXt::Thumb::Mimetype": mime,
        "tEXt::Software": "QuodLibet"
    }

    thumb_pb.savev(thumb_path, "png", options.keys(), options.values())
    try:
        os.chmod(thumb_path, 0o600)
    except OSError:
        pass

    return scale(thumb_pb, boundary)
Ejemplo n.º 16
0
def get_thumbnail(path, boundary):
    """Get a thumbnail pixbuf of an image at `path`.

    Will create/use a thumbnail in the user's thumbnail directory if possible.
    Follows the Free Desktop specification:

    http://specifications.freedesktop.org/thumbnail-spec/

    Can raise GLib.GError.
    """

    width, height = boundary

    # embedded thumbnails come from /tmp/
    # and too big thumbnails make no sense
    if path.startswith(tempfile.gettempdir()) or \
            width > 256 or height > 256 or mtime(path) == 0:
        return GdkPixbuf.Pixbuf.new_from_file_at_size(path, width, height)

    if width <= 128 and height <= 128:
        size_name = "normal"
        thumb_size = 128
    else:
        size_name = "large"
        thumb_size = 256

    thumb_folder = get_thumbnail_folder()
    cache_dir = os.path.join(thumb_folder, size_name)
    try:
        mkdir(cache_dir, 0700)
    except OSError:
        return GdkPixbuf.Pixbuf.new_from_file_at_size(path, width, height)

    bytes_ = path
    if isinstance(path, unicode):
        bytes_ = path.encode("utf-8")
    uri = "file://" + pathname2url(bytes_)
    thumb_name = hashlib.md5(uri).hexdigest() + ".png"

    thumb_path = os.path.join(cache_dir, thumb_name)

    pb = meta_mtime = None
    if os.path.exists(thumb_path):
        pb = GdkPixbuf.Pixbuf.new_from_file(thumb_path)
        meta_mtime = pb.get_option("tEXt::Thumb::MTime")
        meta_mtime = meta_mtime and int(meta_mtime)

    if not pb or meta_mtime != int(mtime(path)):
        pb = GdkPixbuf.Pixbuf.new_from_file(path)

        #Too small picture, no thumbnail needed
        if pb.get_width() < thumb_size and pb.get_height() < thumb_size:
            return scale(pb, boundary)

        info = GdkPixbuf.Pixbuf.get_file_info(path)[0]
        mime = info.get_mime_types()[0]
        options = {
            "tEXt::Thumb::Image::Width": str(pb.get_width()),
            "tEXt::Thumb::Image::Height": str(pb.get_height()),
            "tEXt::Thumb::URI": uri,
            "tEXt::Thumb::MTime": str(int(mtime(path))),
            "tEXt::Thumb::Size": str(os.path.getsize(fsnative(path))),
            "tEXt::Thumb::Mimetype": mime,
            "tEXt::Software": "QuodLibet"
        }

        pb = scale(pb, (thumb_size, thumb_size))
        pb.savev(thumb_path, "png", options.keys(), options.values())
        try:
            os.chmod(thumb_path, 0600)
        except OSError:
            pass

    return scale(pb, boundary)
Ejemplo n.º 17
0
    def copy(self, songlist, song):
        if self.__load_db() is None:
            return False
        track = gpod.itdb_track_new()

        # All values should be utf-8 encoded strings
        # Filepaths should be encoded with the fs encoding

        # Either combine tags with comma, or only take the first value
        if self['all_tags']:
            tag = song.comma
        else:
            tag = lambda key: (song.list(key) or ('', ))[0]

        title = tag('title')
        if self['title_version'] and song('version'):
            title = " - ".join([title, song('version')])
        track.title = encode(title)

        album = tag('album')
        if self['album_part'] and song('discsubtitle'):
            album = " - ".join([album, song('discsubtitle')])
        track.album = encode(album)

        # String keys
        for key in ['artist', 'genre', 'grouping', 'composer', 'albumartist']:
            if hasattr(track, key):  # albumartist since libgpod-0.4.2
                setattr(track, key, encode(tag(key)))
        # Sort keys (since libgpod-0.5.0)
        for key in ['artist', 'album', 'albumartist']:
            if hasattr(track, 'sort_' + key):
                setattr(track, 'sort_' + key, encode(tag(key + 'sort')))
        # Numeric keys
        for key in ['bitrate', 'playcount', 'year']:
            try:
                setattr(track, key, int(song('~#' + key)))
            except ValueError:
                continue
        # Numeric keys where the names differ
        for key, value in {
                'cd_nr': song('~#disc'),
                'cds': song('~#discs'),
                'rating': min(100,
                              song('~#rating') * 100),
                'time_added': self.__mactime(time.time()),
                'time_modified': self.__mactime(mtime(song('~filename'))),
                'track_nr': song('~#track'),
                'tracklen': song('~#length') * 1000,
                'tracks': song('~#tracks'),
                'size': filesize(song('~filename')),
                'soundcheck': self.__soundcheck(song),
        }.items():
            try:
                setattr(track, key, int(value))
            except ValueError:
                continue

        track.filetype = song('~format')
        track.comment = encode(fsdecode(song('~filename')))

        # Associate a cover with the track
        if self['covers']:
            cover = song.find_cover()
            if cover:
                # libgpod will copy the file later when the iTunesDB
                # is saved, so we have to keep a reference around in
                # case the cover is a temporary file.
                self.__covers.append(cover)
                gpod.itdb_track_set_thumbnails(track, fsencode(cover.name))

        # Add the track to the master playlist
        gpod.itdb_track_add(self.__itdb, track, -1)
        master = gpod.itdb_playlist_mpl(self.__itdb)
        gpod.itdb_playlist_add_track(master, track, -1)

        # Copy the actual file
        if gpod.itdb_cp_track_to_ipod(track, song['~filename'], None) == 1:
            return IPodSong(track)
        else:
            return False
Ejemplo n.º 18
0
    def copy(self, parent_widget, song):
        if self.__load_db() is None:
            return False
        track = gpod.itdb_track_new()

        # All values should be utf-8 encoded strings
        # Filepaths should be encoded with the fs encoding

        # Either combine tags with comma, or only take the first value
        if self['all_tags']:
            tag = song.comma
        else:
            tag = lambda key: (song.list(key) or ('',))[0]

        title = tag('title')
        if self['title_version'] and song('version'):
            title = " - ".join([title, song('version')])
        track.title = encode(title)

        album = tag('album')
        if self['album_part'] and song('discsubtitle'):
            album = " - ".join([album, song('discsubtitle')])
        track.album = encode(album)

        # String keys
        for key in ['artist', 'genre', 'grouping', 'composer', 'albumartist']:
            if hasattr(track, key):  # albumartist since libgpod-0.4.2
                setattr(track, key, encode(tag(key)))
        # Sort keys (since libgpod-0.5.0)
        for key in ['artist', 'album', 'albumartist']:
            if hasattr(track, 'sort_' + key):
                setattr(track, 'sort_' + key, encode(tag(key + 'sort')))
        # Numeric keys
        for key in ['bitrate', 'playcount', 'year']:
            try:
                setattr(track, key, int(song('~#' + key)))
            except ValueError:
                continue
        # Numeric keys where the names differ
        for key, value in {
            'cd_nr': song('~#disc'),
            'cds': song('~#discs'),
            'rating': min(100, song('~#rating') * 100),
            'time_added': self.__mactime(time.time()),
            'time_modified': self.__mactime(mtime(song('~filename'))),
            'track_nr': song('~#track'),
            'tracklen': song('~#length') * 1000,
            'tracks': song('~#tracks'),
            'size': filesize(song('~filename')),
            'soundcheck': self.__soundcheck(song),
        }.items():
            try:
                setattr(track, key, int(value))
            except ValueError:
                continue

        track.filetype = encode(song('~format'))
        track.comment = encode(fsdecode(song('~filename')))

        # Associate a cover with the track
        if self['covers']:
            cover = app.cover_manager.get_cover(song)
            if cover:
                # libgpod will copy the file later when the iTunesDB
                # is saved, so we have to keep a reference around in
                # case the cover is a temporary file.
                self.__covers.append(cover)
                gpod.itdb_track_set_thumbnails(
                    track, fsnative2glib(cover.name))

        # Add the track to the master playlist
        gpod.itdb_track_add(self.__itdb, track, -1)
        master = gpod.itdb_playlist_mpl(self.__itdb)
        gpod.itdb_playlist_add_track(master, track, -1)

        # Copy the actual file
        if gpod.itdb_cp_track_to_ipod(track, song['~filename'], None) == 1:
            return IPodSong(track)
        else:
            return False
Ejemplo n.º 19
0
 def valid(self):
     """Return true if the file cache is up-to-date (checked via
     mtime), or we can't tell."""
     return (bool(self.get("~#mtime", 0)) and
             self["~#mtime"] == mtime(self["~filename"]))
Ejemplo n.º 20
0
 def valid(self):
     """Return true if the file cache is up-to-date (checked via
     mtime), or we can't tell."""
     return (bool(self.get("~#mtime", 0))
             and self["~#mtime"] == mtime(self["~filename"]))
Ejemplo n.º 21
0
 def test_bad(self):
     self.failIf(os.path.exists("/dev/doesnotexist"))
     self.failUnlessEqual(mtime("/dev/doesnotexist"), 0)
Ejemplo n.º 22
0
 def test_equal(self):
     self.failUnlessEqual(mtime("."), os.path.getmtime("."))
Ejemplo n.º 23
0
 def test_bad(self):
     self.failIf(os.path.exists("/dev/doesnotexist"))
     self.failUnlessEqual(mtime("/dev/doesnotexist"), 0)
Ejemplo n.º 24
0
 def test_equal(self):
     self.failUnlessEqual(mtime("."), os.path.getmtime("."))
Ejemplo n.º 25
0
    def _execute(self, options, args):
        if len(args) < 1:
            raise CommandError(_("Not enough arguments"))
        elif len(args) > 1:
            raise CommandError(_("Too many arguments"))

        song = self.load_song(args[0])
        dump = self._song_to_text(song).encode("utf-8")

        # write to tmp file
        fd, path = tempfile.mkstemp(suffix=".txt")

        # XXX: copy mtime here so we can test for changes in tests by
        # setting a out of date mtime on the source song file
        copy_mtime(args[0], path)

        try:
            try:
                os.write(fd, dump)
            finally:
                os.close(fd)

            # only parse the result if the editor returns 0 and the mtime has
            # changed
            old_mtime = mtime(path)

            editor_args = get_editor_args()
            self.log("Using editor: %r" % editor_args)

            try:
                subprocess.check_call(editor_args + [path])
            except subprocess.CalledProcessError as e:
                self.log(text_type(e))
                raise CommandError(_("Editing aborted"))
            except OSError as e:
                self.log(text_type(e))
                raise CommandError(
                    _("Starting text editor '%(editor-name)s' failed.") % {
                        "editor-name": editor_args[0]})

            was_changed = mtime(path) != old_mtime
            if not was_changed:
                raise CommandError(_("No changes detected"))

            with open(path, "rb") as h:
                data = h.read()

        finally:
            os.unlink(path)

        try:
            text = data.decode("utf-8")
        except ValueError as e:
            raise CommandError("Invalid data: %r" % e)

        if options.dry_run:
            self.verbose = True
        self._text_to_song(text, song)

        if not options.dry_run:
            self.save_songs([song])
Ejemplo n.º 26
0
    def _execute(self, options, args):
        if len(args) < 1:
            raise CommandError(_("Not enough arguments"))
        elif len(args) > 1:
            raise CommandError(_("Too many arguments"))

        song = self.load_song(args[0])
        dump = self._song_to_text(song).encode("utf-8")

        # write to tmp file
        fd, path = tempfile.mkstemp(suffix=".txt")

        # XXX: copy mtime here so we can test for changes in tests by
        # setting a out of date mtime on the source song file
        copy_mtime(args[0], path)

        try:
            try:
                os.write(fd, dump)
            finally:
                os.close(fd)

            # only parse the result if the editor returns 0 and the mtime has
            # changed
            old_mtime = mtime(path)

            editor_args = get_editor_args()
            self.log("Using editor: %r" % editor_args)

            try:
                subprocess.check_call(editor_args + [path])
            except subprocess.CalledProcessError as e:
                self.log(str(e))
                raise CommandError(_("Editing aborted"))
            except OSError as e:
                self.log(str(e))
                raise CommandError(
                    _("Starting text editor '%(editor-name)s' failed.") %
                    {"editor-name": editor_args[0]})

            was_changed = mtime(path) != old_mtime
            if not was_changed:
                raise CommandError(_("No changes detected"))

            with open(path, "rb") as h:
                data = h.read()

        finally:
            os.unlink(path)

        try:
            text = data.decode("utf-8")
        except ValueError as e:
            raise CommandError("Invalid data: %r" % e)

        if options.dry_run:
            self.verbose = True
        self._text_to_song(text, song)

        if not options.dry_run:
            self.save_songs([song])
Ejemplo n.º 27
0
    def copy(self, parent_widget, song):
        if self.__load_db() is None:
            return False
        track = gpod.itdb_track_new()

        # All values should be utf-8 encoded strings
        # Filepaths should be encoded with the fs encoding

        # Either combine tags with comma, or only take the first value
        if self["all_tags"]:
            tag = song.comma
        else:
            tag = lambda key: (song.list(key) or ("",))[0]

        title = tag("title")
        if self["title_version"] and song("version"):
            title = " - ".join([title, song("version")])
        track.title = encode(title)

        album = tag("album")
        if self["album_part"] and song("discsubtitle"):
            album = " - ".join([album, song("discsubtitle")])
        track.album = encode(album)

        # String keys
        for key in ["artist", "genre", "grouping", "composer", "albumartist"]:
            if hasattr(track, key):  # albumartist since libgpod-0.4.2
                setattr(track, key, encode(tag(key)))
        # Sort keys (since libgpod-0.5.0)
        for key in ["artist", "album", "albumartist"]:
            if hasattr(track, "sort_" + key):
                setattr(track, "sort_" + key, encode(tag(key + "sort")))
        # Numeric keys
        for key in ["bitrate", "playcount", "year"]:
            try:
                setattr(track, key, int(song("~#" + key)))
            except ValueError:
                continue
        # Numeric keys where the names differ
        for key, value in {
            "cd_nr": song("~#disc"),
            "cds": song("~#discs"),
            "rating": min(100, song("~#rating") * 100),
            "time_added": self.__mactime(time.time()),
            "time_modified": self.__mactime(mtime(song("~filename"))),
            "track_nr": song("~#track"),
            "tracklen": song("~#length") * 1000,
            "tracks": song("~#tracks"),
            "size": filesize(song("~filename")),
            "soundcheck": self.__soundcheck(song),
        }.items():
            try:
                setattr(track, key, int(value))
            except ValueError:
                continue

        track.filetype = encode(song("~format"))
        track.comment = encode(fsdecode(song("~filename")))

        # Associate a cover with the track
        if self["covers"]:
            cover = app.cover_manager.get_cover(song)
            if cover:
                # libgpod will copy the file later when the iTunesDB
                # is saved, so we have to keep a reference around in
                # case the cover is a temporary file.
                self.__covers.append(cover)
                gpod.itdb_track_set_thumbnails(track, fsnative2glib(cover.name))

        # Add the track to the master playlist
        gpod.itdb_track_add(self.__itdb, track, -1)
        master = gpod.itdb_playlist_mpl(self.__itdb)
        gpod.itdb_playlist_add_track(master, track, -1)

        # Copy the actual file
        if gpod.itdb_cp_track_to_ipod(track, song["~filename"], None) == 1:
            return IPodSong(track)
        else:
            return False