diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html
index a69e582d..a1f8ba64 100644
--- a/data/interfaces/default/config.html
+++ b/data/interfaces/default/config.html
@@ -949,30 +949,42 @@
+
-
- Embed lyrics
-
-
+
+
+
Destination Directory:
@@ -1903,9 +1915,36 @@
}
});
+ if ($("#embed_album_art").is(":checked"))
+ {
+ $("#album_art_size_options").show();
+ }
+ else
+ {
+ if (!$("#add_album_art").is(":checked"))
+ {
+ $("#album_art_size_options").hide();
+ }
+ }
+
+ $("#embed_album_art").click(function(){
+ if ($("#embed_album_art").is(":checked"))
+ {
+ $("#album_art_size_options").slideDown();
+ }
+ else
+ {
+ if (!$("#add_album_art").is(":checked"))
+ {
+ $("#album_art_size_options").slideUp();
+ }
+ }
+ });
+
if ($("#add_album_art").is(":checked"))
{
$("#album_art_options").show();
+ $("#album_art_size_options").show();
}
else
{
@@ -1916,10 +1955,15 @@
if ($("#add_album_art").is(":checked"))
{
$("#album_art_options").slideDown();
+ $("#album_art_size_options").slideDown();
}
else
{
$("#album_art_options").slideUp();
+ if (!$("#embed_album_art").is(":checked"))
+ {
+ $("#album_art_size_options").slideUp();
+ }
}
});
diff --git a/headphones/albumart.py b/headphones/albumart.py
index 803d469a..6ed73194 100644
--- a/headphones/albumart.py
+++ b/headphones/albumart.py
@@ -13,8 +13,14 @@
# You should have received a copy of the GNU General Public License
# along with Headphones. If not, see .
-from headphones import request, db, logger
+import StringIO
+import struct
+from contextlib import closing
+from six.moves.urllib.parse import urlencode
+import requests as requests
+import headphones
+from headphones import db, request, logger
def getAlbumArt(albumid):
@@ -22,26 +28,29 @@ def getAlbumArt(albumid):
artwork = None
# CAA
+ logger.info("Searching for artwork at CAA")
artwork_path = 'http://coverartarchive.org/release-group/%s/front' % albumid
- artwork = request.request_content(artwork_path, timeout=20)
- if artwork and len(artwork) >= 100:
+ artwork = getartwork(artwork_path)
+ if artwork:
logger.info("Artwork found at CAA")
return artwork_path, artwork
# Amazon
+ logger.info("Searching for artwork at Amazon")
myDB = db.DBConnection()
asin = myDB.action(
'SELECT AlbumASIN from albums WHERE AlbumID=?', [albumid]).fetchone()[0]
if asin:
artwork_path = 'http://ec1.images-amazon.com/images/P/%s.01.LZZZZZZZ.jpg' % asin
- artwork = request.request_content(artwork_path, timeout=20)
- if artwork and len(artwork) >= 100:
+ artwork = getartwork(artwork_path)
+ if artwork:
logger.info("Artwork found at Amazon")
return artwork_path, artwork
# last.fm
from headphones import lastfm
+ logger.info("Searching for artwork at last.fm")
myDB = db.DBConnection()
dbalbum = myDB.action(
'SELECT ArtistName, AlbumTitle, ReleaseID FROM albums WHERE AlbumID=?',
@@ -68,8 +77,8 @@ def getAlbumArt(albumid):
artwork_path = None
if artwork_path:
- artwork = request.request_content(artwork_path, timeout=20)
- if artwork and len(artwork) >= 100:
+ artwork = getartwork(artwork_path)
+ if artwork:
logger.info("Artwork found at last.fm")
return artwork_path, artwork
@@ -95,3 +104,109 @@ def getCachedArt(albumid):
else:
with open(artwork_path, "r") as fp:
return fp.read()
+
+
+def getartwork(artwork_path):
+
+ artwork = None
+ minwidth = headphones.CONFIG.ALBUM_ART_MIN_WIDTH
+ maxwidth = headphones.CONFIG.ALBUM_ART_MAX_WIDTH
+ useproxy = False
+
+ with closing(requests.get(artwork_path, stream=True)) as resp:
+
+ # Get 1st block of artwork
+ data = resp.iter_content(chunk_size=1024)
+ artwork = b''
+ for chunk in data:
+ artwork += chunk
+ if len(artwork) >= 32:
+ break
+ else:
+ artwork = None
+
+ # Check image and size
+ if artwork:
+ img_type, img_width, img_height = getImageInfo(artwork)
+
+ if not img_type or minwidth and img_width < minwidth:
+ logger.info("Artwork is not suitable or too small. Type: %s. Width: %s. Height: %s",
+ img_type, img_width, img_height)
+ artwork = None
+ elif maxwidth and img_width > maxwidth:
+ useproxy = True
+ else:
+ # Get rest of artwork
+ for chunk in data:
+ artwork += chunk
+
+ # Downsize using proxy service to max width
+ if useproxy:
+ logger.info("Artwork is greater than the maximum width, downsizing using proxy service")
+ artwork_path = '{0}?{1}'.format('http://images.weserv.nl/', urlencode({
+ 'url': artwork_path.replace('http://', ''),
+ 'w': maxwidth,
+ }))
+ artwork = request.request_content(artwork_path, timeout=20)
+
+ return artwork
+
+
+def getImageInfo(data):
+ data = str(data)
+ size = len(data)
+ height = -1
+ width = -1
+ content_type = None
+
+ # handle GIFs
+ if (size >= 10) and data[:6] in ('GIF87a', 'GIF89a'):
+ # Check to see if content_type is correct
+ content_type = 'image/gif'
+ w, h = struct.unpack("= 24) and data.startswith('\211PNG\r\n\032\n')
+ and (data[12:16] == 'IHDR')):
+ content_type = 'image/png'
+ w, h = struct.unpack(">LL", data[16:24])
+ width = int(w)
+ height = int(h)
+
+ # Maybe this is for an older PNG version.
+ elif (size >= 16) and data.startswith('\211PNG\r\n\032\n'):
+ # Check to see if we have the right content type
+ content_type = 'image/png'
+ w, h = struct.unpack(">LL", data[8:16])
+ width = int(w)
+ height = int(h)
+
+ # handle JPEGs
+ elif (size >= 2) and data.startswith('\377\330'):
+ content_type = 'image/jpeg'
+ jpeg = StringIO.StringIO(data)
+ jpeg.read(2)
+ b = jpeg.read(1)
+ try:
+ while (b and ord(b) != 0xDA):
+ while (ord(b) != 0xFF): b = jpeg.read(1)
+ while (ord(b) == 0xFF): b = jpeg.read(1)
+ if (ord(b) >= 0xC0 and ord(b) <= 0xC3):
+ jpeg.read(3)
+ h, w = struct.unpack(">HH", jpeg.read(4))
+ break
+ else:
+ jpeg.read(int(struct.unpack(">H", jpeg.read(2))[0])-2)
+ b = jpeg.read(1)
+ width = int(w)
+ height = int(h)
+ except struct.error:
+ pass
+ except ValueError:
+ pass
+
+ return content_type, width, height
diff --git a/headphones/config.py b/headphones/config.py
index 28f82a05..69f33b58 100644
--- a/headphones/config.py
+++ b/headphones/config.py
@@ -34,6 +34,8 @@ _CONFIG_DEFINITIONS = {
'ADD_ALBUM_ART': (int, 'General', 0),
'ADVANCEDENCODER': (str, 'General', ''),
'ALBUM_ART_FORMAT': (str, 'General', 'folder'),
+ 'ALBUM_ART_MIN_WIDTH': (int, 'General', 0),
+ 'ALBUM_ART_MAX_WIDTH': (int, 'General', 0),
# This is used in importer.py to determine how complete an album needs to
# be - to be considered "downloaded". Percentage from 0-100
'ALBUM_COMPLETION_PCT': (int, 'Advanced', 80),
diff --git a/headphones/postprocessor.py b/headphones/postprocessor.py
index 620ff748..5e0962ed 100755
--- a/headphones/postprocessor.py
+++ b/headphones/postprocessor.py
@@ -416,7 +416,6 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list,
if headphones.CONFIG.EMBED_ALBUM_ART or headphones.CONFIG.ADD_ALBUM_ART or \
(headphones.CONFIG.PLEX_ENABLED and headphones.CONFIG.PLEX_NOTIFY) or \
(headphones.CONFIG.XBMC_ENABLED and headphones.CONFIG.XBMC_NOTIFY):
- logger.info('Searching for artwork')
album_art_path, artwork = albumart.getAlbumArt(albumid)
if headphones.CONFIG.EMBED_ALBUM_ART and artwork:
diff --git a/headphones/webserve.py b/headphones/webserve.py
index fbad5daf..742b6767 100644
--- a/headphones/webserve.py
+++ b/headphones/webserve.py
@@ -1267,6 +1267,8 @@ class WebInterface(object):
"keep_nfo": checked(headphones.CONFIG.KEEP_NFO),
"add_album_art": checked(headphones.CONFIG.ADD_ALBUM_ART),
"album_art_format": headphones.CONFIG.ALBUM_ART_FORMAT,
+ "album_art_min_width": headphones.CONFIG.ALBUM_ART_MIN_WIDTH,
+ "album_art_max_width": headphones.CONFIG.ALBUM_ART_MAX_WIDTH,
"embed_album_art": checked(headphones.CONFIG.EMBED_ALBUM_ART),
"embed_lyrics": checked(headphones.CONFIG.EMBED_LYRICS),
"replace_existing_folders": checked(headphones.CONFIG.REPLACE_EXISTING_FOLDERS),