Exemple #1
0
    def test_write_delete_cover(self, writeable_track, writeable_track_name):
        if not writeable_track.has_cover:
            return

        if writeable_track.ext in ['aac', 'mp4']:
            from mutagen.mp4 import MP4Cover
            newcover = CoverImage(None, None, 'image/jpeg',
                                  MP4Cover(random_str()))
        else:
            newcover = CoverImage(3, 'cover', 'image/jpeg',
                                  bytes(random_str()))

        tr = track.Track(writeable_track_name)
        assert tr.get_tag_raw('cover') is None
        assert tr.get_tag_disk('cover') is not None

        # Can we delete a cover?
        tr.set_tag_disk('cover', None)
        assert tr.write_tags() is not False

        self.verify_tags_exist(tr, writeable_track, deleted='cover')

        # Can we write a new cover?
        tr.set_tag_disk('cover', newcover)
        assert tr.get_tag_raw('cover') is None
        assert tr.get_tag_disk('cover') == [newcover]

        # reading the tags shouldn't change anything, since we're reading from disk
        assert tr.read_tags() is not False
        assert tr.get_tag_raw('cover') is None
        assert tr.get_tag_disk('cover') == [newcover]

        self.verify_tags_exist(tr, writeable_track)
Exemple #2
0
    def __init__(self, all_button=True):
        Gtk.Box.__init__(self)
        self.init_template()

        self.parent_row = None
        self.all_func = None
        self.update_func = None
        # Prevents the update function from being called, make
        # sure you do that manually after the batch update
        self.batch_update = False

        self.pixbuf = None
        self.info = CoverImage(None, None, None, None)
        self.default_type = 3
        self.mime_info = {
            'image/jpeg': {
                # Title for display
                'title': _('JPEG image'),
                # Type and options for GDK Pixbuf saving
                'type': 'jpeg',
                'options': {
                    'quality': '90'
                },
            },
            'image/png': {
                'title': _('PNG image'),
                'type': 'png',
                'options': {}
            },
            'image/': {
                'title': _('Image'),
                # Store unknown images as JPEG
                'type': 'jpeg',
                'options': {
                    'quality': '90'
                },
            },
            # TODO: Handle linked images
            '-->': {
                'title': _('Linked image')
            },
        }

        self.button.drag_dest_set(Gtk.DestDefaults.ALL, [],
                                  Gdk.DragAction.COPY)
        self.button.drag_dest_add_uri_targets()

        self.type_selection.connect('scroll-event', dummy_scroll_handler)

        self.all_button = None
        if all_button:
            self.all_button = AllButton(self)
            self.pack_start(self.all_button, False, False, 0)
Exemple #3
0
    def __init__(self, all_button=True):
        Gtk.Box.__init__(self)
        self.init_template()

        self.parent_row = None
        self.all_func = None
        self.update_func = None
        # Prevents the update function from being called, make
        # sure you do that manually after the batch update
        self.batch_update = False

        self.pixbuf = None
        self.info = CoverImage(None, None, None, None)
        self.default_type = 3
        self.mime_info = {
            'image/jpeg': {
                # Title for display
                'title': _('JPEG image'),
                # Type and options for GDK Pixbuf saving
                'type': 'jpeg',
                'options': {'quality': '90'},
            },
            'image/png': {'title': _('PNG image'), 'type': 'png', 'options': {}},
            'image/': {
                'title': _('Image'),
                # Store unknown images as JPEG
                'type': 'jpeg',
                'options': {'quality': '90'},
            },
            # TODO: Handle linked images
            '-->': {'title': _('Linked image')},
        }

        self.button.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.COPY)
        self.button.drag_dest_add_uri_targets()

        self.type_selection.connect('scroll-event', dummy_scroll_handler)

        self.all_button = None
        if all_button:
            self.all_button = AllButton(self)
            self.pack_start(self.all_button, False, False, 0)
Exemple #4
0
class TagImageField(Gtk.Box):

    __gtype_name__ = 'TagImageField'

    (
        button,
        image,
        type_model,
        description_entry,
        type_selection,
        info_label,
    ) = GtkTemplate.Child.widgets(6)

    def __init__(self, all_button=True):
        Gtk.Box.__init__(self)
        self.init_template()

        self.parent_row = None
        self.all_func = None
        self.update_func = None
        # Prevents the update function from being called, make
        # sure you do that manually after the batch update
        self.batch_update = False

        self.pixbuf = None
        self.info = CoverImage(None, None, None, None)
        self.default_type = 3
        self.mime_info = {
            'image/jpeg': {
                # Title for display
                'title': _('JPEG image'),
                # Type and options for GDK Pixbuf saving
                'type': 'jpeg',
                'options': {
                    'quality': '90'
                },
            },
            'image/png': {
                'title': _('PNG image'),
                'type': 'png',
                'options': {}
            },
            'image/': {
                'title': _('Image'),
                # Store unknown images as JPEG
                'type': 'jpeg',
                'options': {
                    'quality': '90'
                },
            },
            # TODO: Handle linked images
            '-->': {
                'title': _('Linked image')
            },
        }

        self.button.drag_dest_set(Gtk.DestDefaults.ALL, [],
                                  Gdk.DragAction.COPY)
        self.button.drag_dest_add_uri_targets()

        self.type_selection.connect('scroll-event', dummy_scroll_handler)

        self.all_button = None
        if all_button:
            self.all_button = AllButton(self)
            self.pack_start(self.all_button, False, False, 0)

    def grab_focus(self):
        """
            Gives focus to the internal widget
        """
        self.image.grab_focus()

    def register_parent_row(self, parent_row):
        self.parent_row = parent_row

    def register_update_func(self, func):
        self.update_func = func

    def register_all_func(self, function):
        self.all_func = function

    def set_value(self, val, all_vals=None, doupdate=True):
        if doupdate:
            if val:
                loader = GdkPixbuf.PixbufLoader()

                try:
                    loader.write(val.data)
                    loader.close()
                except GLib.GError:
                    pass
                else:
                    self.batch_update = True
                    self.set_pixbuf(loader.get_pixbuf(), val.mime)

                    # some file types do not support multiple cover types
                    if val.type is not None:
                        self.type_selection.set_active(val.type)
                        self.type_selection.set_sensitive(True)
                    else:
                        self.type_selection.set_active(-1)
                        self.type_selection.set_sensitive(False)

                    if val.desc is not None:
                        self.description_entry.set_text(val.desc)
                        self.description_entry.set_sensitive(True)
                    else:
                        self.description_entry.set_text('')
                        self.description_entry.set_sensitive(False)

                    self.batch_update = False
            else:
                self.batch_update = True
                self.set_pixbuf(None)
                self.type_selection.set_active(-1)
                self.type_selection.set_sensitive(False)
                self.description_entry.set_text('')
                self.description_entry.set_sensitive(False)
                self.batch_update = False
                self.call_update_func()

        if None not in (all_vals, self.all_button):
            self.all_button.set_active(all(val == v for v in all_vals))

    def get_value(self):
        if not self.pixbuf:
            return None

        mime = self.mime_info[self.info.mime]
        # Retrieve proper image data
        writer = io.BytesIO()

        def gdk_pixbuf_save_func(buf, count, user_data):
            if writer.write(buf) == count:
                return True
            return False

        # workaround for some undocumented changes in GdkPixbuf API
        # see https://bugzilla.gnome.org/show_bug.cgi?id=670372
        # see https://github.com/mypaint/mypaint/issues/236
        try:
            save_to_callback_function = self.pixbuf.save_to_callbackv
        except AttributeError:
            save_to_callback_function = self.pixbuf.save_to_callback
        save_to_callback_function(
            gdk_pixbuf_save_func,
            None,
            mime['type'],
            list(mime['options'].keys()),  # must be a sequence (list)
            list(mime['options'].values()),  # must be a sequence (list)
        )

        # Move to the beginning of the buffer to allow read operations
        writer.seek(0)

        return self.info._replace(data=writer.read())

    def call_update_func(self):
        """
            Wrapper around the update function
        """
        if not self.update_func or self.batch_update:
            return

        self.update_func(self, self.parent_row.tag, self.parent_row.multi_id,
                         self.get_value)

    def set_pixbuf(self, pixbuf, mime=None):
        """
            Updates the displayed cover image and info values
        """
        self.pixbuf = pixbuf

        if pixbuf is None:
            self.image.set_from_icon_name('list-add', Gtk.IconSize.DIALOG)
            self.info_label.set_markup('')
        else:
            self.image.set_from_pixbuf(
                pixbuf.scale_simple(100, 100, GdkPixbuf.InterpType.BILINEAR))

            width, height = pixbuf.get_width(), pixbuf.get_height()
            if mime is None:
                # TRANSLATORS: do not translate 'width' and 'height'
                markup = _('{width}x{height} pixels').format(width=width,
                                                             height=height)
            else:
                # TRANSLATORS: do not translate 'format', 'width', and 'height'
                markup = _('{format} ({width}x{height} pixels)').format(
                    format=self.mime_info.get(
                        mime, self.mime_info['image/'])['title'],
                    width=width,
                    height=height,
                )
            self.info_label.set_markup(markup)

            self.info = self.info._replace(mime=mime)

    def _on_button_clicked(self, button):
        """
            Allows setting the cover image using a file selection dialog
        """
        dialog = dialogs.FileOperationDialog(
            title=_('Select image to set as cover'),
            parent=self.get_toplevel(),
            buttons=(
                Gtk.STOCK_CANCEL,
                Gtk.ResponseType.CANCEL,
                Gtk.STOCK_OK,
                Gtk.ResponseType.OK,
            ),
        )
        dialog.set_select_multiple(False)
        filefilter = Gtk.FileFilter()
        # Not using Gtk.FileFilter.add_pixbuf_formats since
        # not all image formats are supported in tags
        filefilter.set_name(_('Supported image formats'))
        filefilter.add_pattern('*.[jJ][pP][gG]')
        filefilter.add_pattern('*.[jJ][pP][eE][gG]')
        filefilter.add_pattern('*.[pP][nN][gG]')
        dialog.add_filter(filefilter)

        if dialog.run() == Gtk.ResponseType.OK:
            filename = dialog.get_filename()

            try:
                pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
                info = GdkPixbuf.Pixbuf.get_file_info(filename)[0]
            except TypeError:
                pass
            else:
                self.batch_update = True
                self.set_pixbuf(pixbuf, info.get_mime_types()[0])
                self.type_selection.set_active(self.default_type)
                self.type_selection.set_sensitive(True)
                self.description_entry.set_text(
                    os.path.basename(filename).rsplit('.', 1)[0])
                self.description_entry.set_sensitive(True)
                self.batch_update = False
                self.call_update_func()

        dialog.destroy()

    def _on_button_drag_data_received(self, widget, context, x, y, selection,
                                      info, time):
        """
            Allows setting the cover image via drag and drop
        """
        if selection.target.name() == 'text/uri-list':
            filename = Gio.File.new_for_uri(selection.get_uris()[0]).get_path()

            try:
                pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
                info = GdkPixbuf.Pixbuf.get_file_info(filename)[0]
            except TypeError:
                pass
            else:
                self.batch_update = True
                self.set_pixbuf(pixbuf, info['mime_types'][0])
                self.type_selection.set_active(self.default_type)
                self.description_entry.set_sensitive(True)
                self.description_entry.set_text(
                    os.path.basename(filename).rsplit('.', 1)[0])
                self.description_entry.set_sensitive(True)
                self.batch_update = False
                self.call_update_func()

    def _on_type_selection_changed(self, combobox):
        """
            Notifies about changes in the cover type
        """
        self.info = self.info._replace(
            type=self.type_model[combobox.get_active()][0])
        self.call_update_func()

    def _on_description_entry_changed(self, entry):
        """
            Notifies about changes in the cover description
        """
        self.info = self.info._replace(desc=entry.get_text())
        self.call_update_func()
Exemple #5
0
class TagImageField(Gtk.Box):

    __gtype_name__ = 'TagImageField'

    (
        button,
        image,
        type_model,
        description_entry,
        type_selection,
        info_label,
    ) = GtkTemplate.Child.widgets(6)

    def __init__(self, all_button=True):
        Gtk.Box.__init__(self)
        self.init_template()

        self.parent_row = None
        self.all_func = None
        self.update_func = None
        # Prevents the update function from being called, make
        # sure you do that manually after the batch update
        self.batch_update = False

        self.pixbuf = None
        self.info = CoverImage(None, None, None, None)
        self.default_type = 3
        self.mime_info = {
            'image/jpeg': {
                # Title for display
                'title': _('JPEG image'),
                # Type and options for GDK Pixbuf saving
                'type': 'jpeg',
                'options': {'quality': '90'},
            },
            'image/png': {'title': _('PNG image'), 'type': 'png', 'options': {}},
            'image/': {
                'title': _('Image'),
                # Store unknown images as JPEG
                'type': 'jpeg',
                'options': {'quality': '90'},
            },
            # TODO: Handle linked images
            '-->': {'title': _('Linked image')},
        }

        self.button.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.COPY)
        self.button.drag_dest_add_uri_targets()

        self.type_selection.connect('scroll-event', dummy_scroll_handler)

        self.all_button = None
        if all_button:
            self.all_button = AllButton(self)
            self.pack_start(self.all_button, False, False, 0)

    def grab_focus(self):
        """
            Gives focus to the internal widget
        """
        self.image.grab_focus()

    def register_parent_row(self, parent_row):
        self.parent_row = parent_row

    def register_update_func(self, func):
        self.update_func = func

    def register_all_func(self, function):
        self.all_func = function

    def set_value(self, val, all_vals=None, doupdate=True):
        if doupdate:
            if val:
                loader = GdkPixbuf.PixbufLoader()

                try:
                    loader.write(val.data)
                    loader.close()
                except GLib.GError:
                    pass
                else:
                    self.batch_update = True
                    self.set_pixbuf(loader.get_pixbuf(), val.mime)

                    # some file types do not support multiple cover types
                    if val.type is not None:
                        self.type_selection.set_active(val.type)
                        self.type_selection.set_sensitive(True)
                    else:
                        self.type_selection.set_active(-1)
                        self.type_selection.set_sensitive(False)

                    if val.desc is not None:
                        self.description_entry.set_text(val.desc)
                        self.description_entry.set_sensitive(True)
                    else:
                        self.description_entry.set_text('')
                        self.description_entry.set_sensitive(False)

                    self.batch_update = False
            else:
                self.batch_update = True
                self.set_pixbuf(None)
                self.type_selection.set_active(-1)
                self.type_selection.set_sensitive(False)
                self.description_entry.set_text('')
                self.description_entry.set_sensitive(False)
                self.batch_update = False
                self.call_update_func()

        if None not in (all_vals, self.all_button):
            self.all_button.set_active(all(val == v for v in all_vals))

    def get_value(self):
        if not self.pixbuf:
            return None

        mime = self.mime_info[self.info.mime]
        # Retrieve proper image data
        writer = io.BytesIO()

        def gdk_pixbuf_save_func(buf, count, user_data):
            if writer.write(buf) == count:
                return True
            return False

        # workaround for some undocumented changes in GdkPixbuf API
        # see https://bugzilla.gnome.org/show_bug.cgi?id=670372
        # see https://github.com/mypaint/mypaint/issues/236
        try:
            save_to_callback_function = self.pixbuf.save_to_callbackv
        except AttributeError:
            save_to_callback_function = self.pixbuf.save_to_callback
        save_to_callback_function(
            gdk_pixbuf_save_func,
            None,
            mime['type'],
            mime['options'].keys(),
            mime['options'].values(),
        )

        # Move to the beginning of the buffer to allow read operations
        writer.seek(0)

        return self.info._replace(data=writer.read())

    def call_update_func(self):
        """
            Wrapper around the update function
        """
        if not self.update_func or self.batch_update:
            return

        self.update_func(
            self, self.parent_row.tag, self.parent_row.multi_id, self.get_value
        )

    def set_pixbuf(self, pixbuf, mime=None):
        """
            Updates the displayed cover image and info values
        """
        self.pixbuf = pixbuf

        if pixbuf is None:
            self.image.set_from_icon_name('list-add', Gtk.IconSize.DIALOG)
            self.info_label.set_markup('')
        else:
            self.image.set_from_pixbuf(
                pixbuf.scale_simple(100, 100, GdkPixbuf.InterpType.BILINEAR)
            )

            width, height = pixbuf.get_width(), pixbuf.get_height()
            if mime is None:
                # TRANSLATORS: do not translate 'width' and 'height'
                markup = _('{width}x{height} pixels').format(width=width, height=height)
            else:
                # TRANSLATORS: do not translate 'format', 'width', and 'height'
                markup = _('{format} ({width}x{height} pixels)').format(
                    format=self.mime_info.get(mime, self.mime_info['image/'])['title'],
                    width=width,
                    height=height,
                )
            self.info_label.set_markup(markup)

            self.info = self.info._replace(mime=mime)

    def _on_button_clicked(self, button):
        """
            Allows setting the cover image using a file selection dialog
        """
        dialog = dialogs.FileOperationDialog(
            title=_('Select image to set as cover'),
            parent=self.get_toplevel(),
            buttons=(
                Gtk.STOCK_CANCEL,
                Gtk.ResponseType.CANCEL,
                Gtk.STOCK_OK,
                Gtk.ResponseType.OK,
            ),
        )
        dialog.set_select_multiple(False)
        filefilter = Gtk.FileFilter()
        # Not using Gtk.FileFilter.add_pixbuf_formats since
        # not all image formats are supported in tags
        filefilter.set_name(_('Supported image formats'))
        filefilter.add_pattern('*.[jJ][pP][gG]')
        filefilter.add_pattern('*.[jJ][pP][eE][gG]')
        filefilter.add_pattern('*.[pP][nN][gG]')
        dialog.add_filter(filefilter)

        if dialog.run() == Gtk.ResponseType.OK:
            filename = dialog.get_filename()

            try:
                pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
                info = GdkPixbuf.Pixbuf.get_file_info(filename)[0]
            except TypeError:
                pass
            else:
                self.batch_update = True
                self.set_pixbuf(pixbuf, info.get_mime_types()[0])
                self.type_selection.set_active(self.default_type)
                self.type_selection.set_sensitive(True)
                self.description_entry.set_text(
                    os.path.basename(filename).rsplit('.', 1)[0]
                )
                self.description_entry.set_sensitive(True)
                self.batch_update = False
                self.call_update_func()

        dialog.destroy()

    def _on_button_drag_data_received(
        self, widget, context, x, y, selection, info, time
    ):
        """
            Allows setting the cover image via drag and drop
        """
        if selection.target.name() == 'text/uri-list':
            filename = Gio.File.new_for_uri(selection.get_uris()[0]).get_path()

            try:
                pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
                info = GdkPixbuf.Pixbuf.get_file_info(filename)[0]
            except TypeError:
                pass
            else:
                self.batch_update = True
                self.set_pixbuf(pixbuf, info['mime_types'][0])
                self.type_selection.set_active(self.default_type)
                self.description_entry.set_sensitive(True)
                self.description_entry.set_text(
                    os.path.basename(filename).rsplit('.', 1)[0]
                )
                self.description_entry.set_sensitive(True)
                self.batch_update = False
                self.call_update_func()

    def _on_type_selection_changed(self, combobox):
        """
            Notifies about changes in the cover type
        """
        self.info = self.info._replace(type=self.type_model[combobox.get_active()][0])
        self.call_update_func()

    def _on_description_entry_changed(self, entry):
        """
            Notifies about changes in the cover description
        """
        self.info = self.info._replace(desc=entry.get_text())
        self.call_update_func()