mirror of
https://github.com/clinton-hall/nzbToMedia.git
synced 2025-08-20 05:13:16 -07:00
Update beets to 1.3.18:
Dependencies: * PyYAML 3.11 * Unidecode 0.4.19 * beets 1.3.18 * colorama 0.3.7 * enum34 1.1.6 * jellyfish 0.5.4 * munkres 1.0.7 * musicbrainzngs 0.6 * mutagen 1.32
This commit is contained in:
parent
f093fafd8d
commit
1111074dc3
165 changed files with 48385 additions and 7424 deletions
|
@ -1,5 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# This file is part of beets.
|
||||
# Copyright 2013, Fabrice Laporte
|
||||
# Copyright 2016, Fabrice Laporte
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
|
@ -15,11 +16,15 @@
|
|||
"""Abstraction layer to resize images using PIL, ImageMagick, or a
|
||||
public resizing proxy if neither is available.
|
||||
"""
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import urllib
|
||||
import subprocess
|
||||
import os
|
||||
import re
|
||||
from tempfile import NamedTemporaryFile
|
||||
import logging
|
||||
|
||||
from beets import logging
|
||||
from beets import util
|
||||
|
||||
# Resizing methods
|
||||
|
@ -37,8 +42,8 @@ def resize_url(url, maxwidth):
|
|||
maxwidth (preserving aspect ratio).
|
||||
"""
|
||||
return '{0}?{1}'.format(PROXY_URL, urllib.urlencode({
|
||||
'url': url.replace('http://',''),
|
||||
'w': str(maxwidth),
|
||||
'url': url.replace('http://', ''),
|
||||
'w': bytes(maxwidth),
|
||||
}))
|
||||
|
||||
|
||||
|
@ -57,9 +62,8 @@ def pil_resize(maxwidth, path_in, path_out=None):
|
|||
"""
|
||||
path_out = path_out or temp_file_for(path_in)
|
||||
from PIL import Image
|
||||
log.debug(u'artresizer: PIL resizing {0} to {1}'.format(
|
||||
util.displayable_path(path_in), util.displayable_path(path_out)
|
||||
))
|
||||
log.debug(u'artresizer: PIL resizing {0} to {1}',
|
||||
util.displayable_path(path_in), util.displayable_path(path_out))
|
||||
|
||||
try:
|
||||
im = Image.open(util.syspath(path_in))
|
||||
|
@ -68,20 +72,18 @@ def pil_resize(maxwidth, path_in, path_out=None):
|
|||
im.save(path_out)
|
||||
return path_out
|
||||
except IOError:
|
||||
log.error(u"PIL cannot create thumbnail for '{0}'".format(
|
||||
util.displayable_path(path_in)
|
||||
))
|
||||
log.error(u"PIL cannot create thumbnail for '{0}'",
|
||||
util.displayable_path(path_in))
|
||||
return path_in
|
||||
|
||||
|
||||
def im_resize(maxwidth, path_in, path_out=None):
|
||||
"""Resize using ImageMagick's ``convert`` tool.
|
||||
tool. Return the output path of resized image.
|
||||
Return the output path of resized image.
|
||||
"""
|
||||
path_out = path_out or temp_file_for(path_in)
|
||||
log.debug(u'artresizer: ImageMagick resizing {0} to {1}'.format(
|
||||
util.displayable_path(path_in), util.displayable_path(path_out)
|
||||
))
|
||||
log.debug(u'artresizer: ImageMagick resizing {0} to {1}',
|
||||
util.displayable_path(path_in), util.displayable_path(path_out))
|
||||
|
||||
# "-resize widthxheight>" shrinks images with dimension(s) larger
|
||||
# than the corresponding width and/or height dimension(s). The >
|
||||
|
@ -89,13 +91,13 @@ def im_resize(maxwidth, path_in, path_out=None):
|
|||
# compatibility.
|
||||
try:
|
||||
util.command_output([
|
||||
'convert', util.syspath(path_in),
|
||||
'-resize', '{0}x^>'.format(maxwidth), path_out
|
||||
b'convert', util.syspath(path_in, prefix=False),
|
||||
b'-resize', b'{0}x^>'.format(maxwidth),
|
||||
util.syspath(path_out, prefix=False),
|
||||
])
|
||||
except subprocess.CalledProcessError:
|
||||
log.warn(u'artresizer: IM convert failed for {0}'.format(
|
||||
util.displayable_path(path_in)
|
||||
))
|
||||
log.warn(u'artresizer: IM convert failed for {0}',
|
||||
util.displayable_path(path_in))
|
||||
return path_in
|
||||
return path_out
|
||||
|
||||
|
@ -106,21 +108,56 @@ BACKEND_FUNCS = {
|
|||
}
|
||||
|
||||
|
||||
def pil_getsize(path_in):
|
||||
from PIL import Image
|
||||
try:
|
||||
im = Image.open(util.syspath(path_in))
|
||||
return im.size
|
||||
except IOError as exc:
|
||||
log.error(u"PIL could not read file {}: {}",
|
||||
util.displayable_path(path_in), exc)
|
||||
|
||||
|
||||
def im_getsize(path_in):
|
||||
cmd = [b'identify', b'-format', b'%w %h',
|
||||
util.syspath(path_in, prefix=False)]
|
||||
try:
|
||||
out = util.command_output(cmd)
|
||||
except subprocess.CalledProcessError as exc:
|
||||
log.warn(u'ImageMagick size query failed')
|
||||
log.debug(
|
||||
u'`convert` exited with (status {}) when '
|
||||
u'getting size with command {}:\n{}',
|
||||
exc.returncode, cmd, exc.output.strip()
|
||||
)
|
||||
return
|
||||
try:
|
||||
return tuple(map(int, out.split(b' ')))
|
||||
except IndexError:
|
||||
log.warn(u'Could not understand IM output: {0!r}', out)
|
||||
|
||||
|
||||
BACKEND_GET_SIZE = {
|
||||
PIL: pil_getsize,
|
||||
IMAGEMAGICK: im_getsize,
|
||||
}
|
||||
|
||||
|
||||
class Shareable(type):
|
||||
"""A pseudo-singleton metaclass that allows both shared and
|
||||
non-shared instances. The ``MyClass.shared`` property holds a
|
||||
lazily-created shared instance of ``MyClass`` while calling
|
||||
``MyClass()`` to construct a new object works as usual.
|
||||
"""
|
||||
def __init__(cls, name, bases, dict):
|
||||
super(Shareable, cls).__init__(name, bases, dict)
|
||||
cls._instance = None
|
||||
def __init__(self, name, bases, dict):
|
||||
super(Shareable, self).__init__(name, bases, dict)
|
||||
self._instance = None
|
||||
|
||||
@property
|
||||
def shared(cls):
|
||||
if cls._instance is None:
|
||||
cls._instance = cls()
|
||||
return cls._instance
|
||||
def shared(self):
|
||||
if self._instance is None:
|
||||
self._instance = self()
|
||||
return self._instance
|
||||
|
||||
|
||||
class ArtResizer(object):
|
||||
|
@ -128,12 +165,12 @@ class ArtResizer(object):
|
|||
"""
|
||||
__metaclass__ = Shareable
|
||||
|
||||
def __init__(self, method=None):
|
||||
"""Create a resizer object for the given method or, if none is
|
||||
specified, with an inferred method.
|
||||
def __init__(self):
|
||||
"""Create a resizer object with an inferred method.
|
||||
"""
|
||||
self.method = method or self._guess_method()
|
||||
log.debug(u"artresizer: method is {0}".format(self.method))
|
||||
self.method = self._check_method()
|
||||
log.debug(u"artresizer: method is {0}", self.method)
|
||||
self.can_compare = self._can_compare()
|
||||
|
||||
def resize(self, maxwidth, path_in, path_out=None):
|
||||
"""Manipulate an image file according to the method, returning a
|
||||
|
@ -141,7 +178,7 @@ class ArtResizer(object):
|
|||
temporary file. For WEBPROXY, returns `path_in` unmodified.
|
||||
"""
|
||||
if self.local:
|
||||
func = BACKEND_FUNCS[self.method]
|
||||
func = BACKEND_FUNCS[self.method[0]]
|
||||
return func(maxwidth, path_in, path_out)
|
||||
else:
|
||||
return path_in
|
||||
|
@ -159,30 +196,63 @@ class ArtResizer(object):
|
|||
@property
|
||||
def local(self):
|
||||
"""A boolean indicating whether the resizing method is performed
|
||||
locally (i.e., PIL or IMAGEMAGICK).
|
||||
locally (i.e., PIL or ImageMagick).
|
||||
"""
|
||||
return self.method in BACKEND_FUNCS
|
||||
return self.method[0] in BACKEND_FUNCS
|
||||
|
||||
def get_size(self, path_in):
|
||||
"""Return the size of an image file as an int couple (width, height)
|
||||
in pixels.
|
||||
|
||||
Only available locally
|
||||
"""
|
||||
if self.local:
|
||||
func = BACKEND_GET_SIZE[self.method[0]]
|
||||
return func(path_in)
|
||||
|
||||
def _can_compare(self):
|
||||
"""A boolean indicating whether image comparison is available"""
|
||||
|
||||
return self.method[0] == IMAGEMAGICK and self.method[1] > (6, 8, 7)
|
||||
|
||||
@staticmethod
|
||||
def _guess_method():
|
||||
"""Determine which resizing method to use. Returns PIL,
|
||||
IMAGEMAGICK, or WEBPROXY depending on available dependencies.
|
||||
"""
|
||||
# Try importing PIL.
|
||||
try:
|
||||
__import__('PIL', fromlist=['Image'])
|
||||
return PIL
|
||||
except ImportError:
|
||||
pass
|
||||
def _check_method():
|
||||
"""Return a tuple indicating an available method and its version."""
|
||||
version = get_im_version()
|
||||
if version:
|
||||
return IMAGEMAGICK, version
|
||||
|
||||
# Try invoking ImageMagick's "convert".
|
||||
try:
|
||||
out = util.command_output(['convert', '--version'])
|
||||
if 'imagemagick' in out.lower():
|
||||
# system32/convert.exe may be interfering
|
||||
return IMAGEMAGICK
|
||||
except (subprocess.CalledProcessError, OSError):
|
||||
pass
|
||||
version = get_pil_version()
|
||||
if version:
|
||||
return PIL, version
|
||||
|
||||
# Fall back to Web proxy method.
|
||||
return WEBPROXY
|
||||
return WEBPROXY, (0)
|
||||
|
||||
|
||||
def get_im_version():
|
||||
"""Return Image Magick version or None if it is unavailable
|
||||
Try invoking ImageMagick's "convert"."""
|
||||
try:
|
||||
out = util.command_output([b'identify', b'--version'])
|
||||
|
||||
if 'imagemagick' in out.lower():
|
||||
pattern = r".+ (\d+)\.(\d+)\.(\d+).*"
|
||||
match = re.search(pattern, out)
|
||||
if match:
|
||||
return (int(match.group(1)),
|
||||
int(match.group(2)),
|
||||
int(match.group(3)))
|
||||
return (0,)
|
||||
|
||||
except (subprocess.CalledProcessError, OSError):
|
||||
return None
|
||||
|
||||
|
||||
def get_pil_version():
|
||||
"""Return Image Magick version or None if it is unavailable
|
||||
Try importing PIL."""
|
||||
try:
|
||||
__import__('PIL', fromlist=[str('Image')])
|
||||
return (0,)
|
||||
except ImportError:
|
||||
return None
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue