Ejemplo n.º 1
0
def freedesktop_thumbnail(filename, pixbuf=None):
    """Fetch or (re-)generate the thumbnail in $XDG_CACHE_HOME/thumbnails.

    If there is no thumbnail for the specified filename, a new
    thumbnail will be generated and stored according to the FDO spec.
    A thumbnail will also get regenerated if the MTimes (as in "modified")
    of thumbnail and original image do not match.

    When pixbuf is given, it will be scaled and used as thumbnail
    instead of reading the file itself. In this case the file is still
    accessed to get its mtime, so this method must not be called if
    the file is still open.

    Returns the large (256x256) thumbnail.
    """

    uri = lib.glib.filename_to_uri(os.path.abspath(filename))
    logger.debug("thumb: uri=%r", uri)
    file_hash = hashlib.md5(uri).hexdigest()

    cache_dir = lib.glib.get_user_cache_dir()
    base_directory = os.path.join(cache_dir, 'thumbnails')

    directory = os.path.join(base_directory, 'normal')
    tb_filename_normal = os.path.join(directory, file_hash) + '.png'

    if not os.path.exists(directory):
        os.makedirs(directory, 0700)
    directory = os.path.join(base_directory, 'large')
    tb_filename_large = os.path.join(directory, file_hash) + '.png'
    if not os.path.exists(directory):
        os.makedirs(directory, 0700)

    file_mtime = str(int(os.stat(filename).st_mtime))

    save_thumbnail = True

    if filename.lower().endswith('.ora'):
        # don't bother with normal (128x128) thumbnails when we can
        # get a large one (256x256) from the file in an instant
        acceptable_tb_filenames = [tb_filename_large]
    else:
        # prefer the large thumbnail, but accept the normal one if
        # available, for the sake of performance
        acceptable_tb_filenames = [tb_filename_large, tb_filename_normal]

    for fn in acceptable_tb_filenames:
        if not pixbuf and os.path.isfile(fn):
            # use the largest stored thumbnail that isn't obsolete
            pixbuf = GdkPixbuf.Pixbuf.new_from_file(fn)
            if file_mtime == pixbuf.get_option("tEXt::Thumb::MTime"):
                save_thumbnail = False
            else:
                pixbuf = None

    if not pixbuf:
        # try to load a pixbuf from the file
        pixbuf = get_pixbuf(filename)

    if pixbuf:
        pixbuf = scale_proportionally(pixbuf, 256, 256)
        if save_thumbnail:
            png_opts = {"tEXt::Thumb::MTime": file_mtime,
                        "tEXt::Thumb::URI": uri}
            logger.debug("thumb: png_opts=%r", png_opts)
            lib.pixbuf.save(
                pixbuf,
                tb_filename_large,
                type='png',
                **png_opts
            )
            logger.debug("thumb: saved large (256x256) thumbnail to %r",
                         tb_filename_large)
            # save normal size too, in case some implementations don't
            # bother with large thumbnails
            pixbuf_normal = scale_proportionally(pixbuf, 128, 128)
            lib.pixbuf.save(
                pixbuf_normal,
                tb_filename_normal,
                type='png',
                **png_opts
            )
            logger.debug("thumb: saved normal (128x128) thumbnail to %r",
                         tb_filename_normal)
    return pixbuf
Ejemplo n.º 2
0
def freedesktop_thumbnail(filename, pixbuf=None, force=False):
    """Fetch or (re-)generate the thumbnail in $XDG_CACHE_HOME/thumbnails.

    If there is no thumbnail for the specified filename, a new
    thumbnail will be generated and stored according to the FDO spec.
    A thumbnail will also get regenerated if the file modification times
    of thumbnail and original image do not match.

    :param GdkPixbuf.Pixbuf pixbuf: Thumbnail to save, optional.
    :param bool force: Force rengeneration (skip mtime checks).
    :returns: the large (256x256) thumbnail, or None.
    :rtype: GdkPixbuf.Pixbuf

    When pixbuf is given, it will be scaled and used as thumbnail
    instead of reading the file itself. In this case the file is still
    accessed to get its mtime, so this method must not be called if
    the file is still open.

    >>> image = "svg/thumbnail-test-input.svg"
    >>> p1 = freedesktop_thumbnail(image, force=True)
    >>> isinstance(p1, GdkPixbuf.Pixbuf)
    True
    >>> p2 = freedesktop_thumbnail(image)
    >>> isinstance(p2, GdkPixbuf.Pixbuf)
    True
    >>> p2.to_string() == p1.to_string()
    True
    >>> p2.get_width() == p2.get_height() == 256
    True

    """

    uri = lib.glib.filename_to_uri(os.path.abspath(filename))
    logger.debug("thumb: uri=%r", uri)
    if not isinstance(uri, bytes):
        uri = uri.encode("utf-8")
    file_hash = hashlib.md5(uri).hexdigest()

    cache_dir = lib.glib.get_user_cache_dir()
    base_directory = os.path.join(cache_dir, u'thumbnails')

    directory = os.path.join(base_directory, u'normal')
    tb_filename_normal = os.path.join(directory, file_hash) + u'.png'

    if not os.path.exists(directory):
        os.makedirs(directory, 0o700)
    directory = os.path.join(base_directory, u'large')
    tb_filename_large = os.path.join(directory, file_hash) + u'.png'
    if not os.path.exists(directory):
        os.makedirs(directory, 0o700)

    file_mtime = str(int(os.stat(filename).st_mtime))

    save_thumbnail = True

    if filename.lower().endswith(u'.ora'):
        # don't bother with normal (128x128) thumbnails when we can
        # get a large one (256x256) from the file in an instant
        acceptable_tb_filenames = [tb_filename_large]
    else:
        # prefer the large thumbnail, but accept the normal one if
        # available, for the sake of performance
        acceptable_tb_filenames = [tb_filename_large, tb_filename_normal]

    # Use the largest stored thumbnail that isn't obsolete,
    # Unless one was passed in,
    # or regeneration is being forced.
    for fn in acceptable_tb_filenames:
        if pixbuf or force or (not os.path.isfile(fn)):
            continue
        try:
            pixbuf = lib.pixbuf.load_from_file(fn)
        except Exception as e:
            logger.warning(
                u"thumb: cache file %r looks corrupt (%r). "
                u"It will be regenerated.",
                fn,
                unicode(e),
            )
            pixbuf = None
        else:
            assert pixbuf is not None
            if file_mtime == pixbuf.get_option("tEXt::Thumb::MTime"):
                save_thumbnail = False
                break
            else:
                pixbuf = None

    # Try to load a pixbuf from the file, if we still need one.
    if not pixbuf:
        pixbuf = get_pixbuf(filename)

    # Update the fd.o thumbs cache.
    if pixbuf:
        pixbuf = scale_proportionally(pixbuf, 256, 256)
        if save_thumbnail:
            png_opts = {
                "tEXt::Thumb::MTime": file_mtime,
                "tEXt::Thumb::URI": uri
            }
            logger.debug("thumb: png_opts=%r", png_opts)
            lib.pixbuf.save(pixbuf, tb_filename_large, type='png', **png_opts)
            logger.debug("thumb: saved large (256x256) thumbnail to %r",
                         tb_filename_large)
            # save normal size too, in case some implementations don't
            # bother with large thumbnails
            pixbuf_normal = scale_proportionally(pixbuf, 128, 128)
            lib.pixbuf.save(pixbuf_normal,
                            tb_filename_normal,
                            type='png',
                            **png_opts)
            logger.debug("thumb: saved normal (128x128) thumbnail to %r",
                         tb_filename_normal)

    # Return the 256x256 scaled version.
    return pixbuf
Ejemplo n.º 3
0
def freedesktop_thumbnail(filename, pixbuf=None, force=False):
    """Fetch or (re-)generate the thumbnail in $XDG_CACHE_HOME/thumbnails.

    If there is no thumbnail for the specified filename, a new
    thumbnail will be generated and stored according to the FDO spec.
    A thumbnail will also get regenerated if the file modification times
    of thumbnail and original image do not match.

    :param GdkPixbuf.Pixbuf pixbuf: Thumbnail to save, optional.
    :param bool force: Force rengeneration (skip mtime checks).
    :returns: the large (256x256) thumbnail, or None.
    :rtype: GdkPixbuf.Pixbuf

    When pixbuf is given, it will be scaled and used as thumbnail
    instead of reading the file itself. In this case the file is still
    accessed to get its mtime, so this method must not be called if
    the file is still open.

    >>> icon = "desktop/icons/hicolor/512x512/apps/mypaint.png"
    >>> p1 = freedesktop_thumbnail(icon, force=True)
    >>> isinstance(p1, GdkPixbuf.Pixbuf)
    True
    >>> p2 = freedesktop_thumbnail(icon)
    >>> isinstance(p2, GdkPixbuf.Pixbuf)
    True
    >>> p2.to_string() == p1.to_string()
    True
    >>> p2.get_width() == p2.get_height() == 256
    True

    """

    uri = lib.glib.filename_to_uri(os.path.abspath(filename))
    logger.debug("thumb: uri=%r", uri)
    if not isinstance(uri, bytes):
        uri = uri.encode("utf-8")
    file_hash = hashlib.md5(uri).hexdigest()

    cache_dir = lib.glib.get_user_cache_dir()
    base_directory = os.path.join(cache_dir, u'thumbnails')

    directory = os.path.join(base_directory, u'normal')
    tb_filename_normal = os.path.join(directory, file_hash) + u'.png'

    if not os.path.exists(directory):
        os.makedirs(directory, 0o700)
    directory = os.path.join(base_directory, u'large')
    tb_filename_large = os.path.join(directory, file_hash) + u'.png'
    if not os.path.exists(directory):
        os.makedirs(directory, 0o700)

    file_mtime = str(int(os.stat(filename).st_mtime))

    save_thumbnail = True

    if filename.lower().endswith(u'.ora'):
        # don't bother with normal (128x128) thumbnails when we can
        # get a large one (256x256) from the file in an instant
        acceptable_tb_filenames = [tb_filename_large]
    else:
        # prefer the large thumbnail, but accept the normal one if
        # available, for the sake of performance
        acceptable_tb_filenames = [tb_filename_large, tb_filename_normal]

    # Use the largest stored thumbnail that isn't obsolete,
    # Unless one was passed in,
    # or regeneration is being forced.
    for fn in acceptable_tb_filenames:
        if pixbuf or force or (not os.path.isfile(fn)):
            continue
        try:
            pixbuf = lib.pixbuf.load_from_file(fn)
        except Exception as e:
            logger.warning(
                u"thumb: cache file %r looks corrupt (%r). "
                u"It will be regenerated.",
                fn, unicode(e),
            )
            pixbuf = None
        else:
            assert pixbuf is not None
            if file_mtime == pixbuf.get_option("tEXt::Thumb::MTime"):
                save_thumbnail = False
                break
            else:
                pixbuf = None

    # Try to load a pixbuf from the file, if we still need one.
    if not pixbuf:
        pixbuf = get_pixbuf(filename)

    # Update the fd.o thumbs cache.
    if pixbuf:
        pixbuf = scale_proportionally(pixbuf, 256, 256)
        if save_thumbnail:
            png_opts = {"tEXt::Thumb::MTime": file_mtime,
                        "tEXt::Thumb::URI": uri}
            logger.debug("thumb: png_opts=%r", png_opts)
            lib.pixbuf.save(
                pixbuf,
                tb_filename_large,
                type='png',
                **png_opts
            )
            logger.debug("thumb: saved large (256x256) thumbnail to %r",
                         tb_filename_large)
            # save normal size too, in case some implementations don't
            # bother with large thumbnails
            pixbuf_normal = scale_proportionally(pixbuf, 128, 128)
            lib.pixbuf.save(
                pixbuf_normal,
                tb_filename_normal,
                type='png',
                **png_opts
            )
            logger.debug("thumb: saved normal (128x128) thumbnail to %r",
                         tb_filename_normal)

    # Return the 256x256 scaled version.
    return pixbuf
Ejemplo n.º 4
0
def freedesktop_thumbnail(filename, pixbuf=None):
    """Fetch or (re-)generate the thumbnail in $XDG_CACHE_HOME/thumbnails.

    If there is no thumbnail for the specified filename, a new
    thumbnail will be generated and stored according to the FDO spec.
    A thumbnail will also get regenerated if the file modification times
    of thumbnail and original image do not match.

    :param GdkPixbuf.Pixbuf pixbuf: Thumbnail to save, optional.
    :returns: the large (256x256) thumbnail, or None.
    :rtype: GdkPixbuf.Pixbuf

    When pixbuf is given, it will be scaled and used as thumbnail
    instead of reading the file itself. In this case the file is still
    accessed to get its mtime, so this method must not be called if
    the file is still open.

    """

    uri = lib.glib.filename_to_uri(os.path.abspath(filename))
    logger.debug("thumb: uri=%r", uri)
    file_hash = hashlib.md5(uri).hexdigest()

    cache_dir = lib.glib.get_user_cache_dir()
    base_directory = os.path.join(cache_dir, 'thumbnails')

    directory = os.path.join(base_directory, 'normal')
    tb_filename_normal = os.path.join(directory, file_hash) + '.png'

    if not os.path.exists(directory):
        os.makedirs(directory, 0o700)
    directory = os.path.join(base_directory, 'large')
    tb_filename_large = os.path.join(directory, file_hash) + '.png'
    if not os.path.exists(directory):
        os.makedirs(directory, 0o700)

    file_mtime = str(int(os.stat(filename).st_mtime))

    save_thumbnail = True

    if filename.lower().endswith('.ora'):
        # don't bother with normal (128x128) thumbnails when we can
        # get a large one (256x256) from the file in an instant
        acceptable_tb_filenames = [tb_filename_large]
    else:
        # prefer the large thumbnail, but accept the normal one if
        # available, for the sake of performance
        acceptable_tb_filenames = [tb_filename_large, tb_filename_normal]

    for fn in acceptable_tb_filenames:
        if pixbuf or not os.path.isfile(fn):
            continue
        try:
            # use the largest stored thumbnail that isn't obsolete
            pixbuf = lib.pixbuf.load_from_file(fn)
        except Exception as e:
            logger.warning(
                u"thumb: cache file %r looks corrupt (%r). "
                u"It will be regenerated.",
                fn,
                unicode(e),
            )
            pixbuf = None
        else:
            assert pixbuf is not None
            if file_mtime == pixbuf.get_option("tEXt::Thumb::MTime"):
                save_thumbnail = False
                break
            else:
                pixbuf = None

    if not pixbuf:
        # try to load a pixbuf from the file
        pixbuf = get_pixbuf(filename)

    if pixbuf:
        pixbuf = scale_proportionally(pixbuf, 256, 256)
        if save_thumbnail:
            png_opts = {
                "tEXt::Thumb::MTime": file_mtime,
                "tEXt::Thumb::URI": uri
            }
            logger.debug("thumb: png_opts=%r", png_opts)
            lib.pixbuf.save(pixbuf, tb_filename_large, type='png', **png_opts)
            logger.debug("thumb: saved large (256x256) thumbnail to %r",
                         tb_filename_large)
            # save normal size too, in case some implementations don't
            # bother with large thumbnails
            pixbuf_normal = scale_proportionally(pixbuf, 128, 128)
            lib.pixbuf.save(pixbuf_normal,
                            tb_filename_normal,
                            type='png',
                            **png_opts)
            logger.debug("thumb: saved normal (128x128) thumbnail to %r",
                         tb_filename_normal)
    return pixbuf