From 9ccb0f85dc109cbb105690dd9cccd296aa29f9ba Mon Sep 17 00:00:00 2001 From: Ade Date: Sat, 11 Mar 2017 12:19:53 +1300 Subject: [PATCH 01/25] Use CAA artwork for post processing Preference is: CAA Amazon last.fm --- headphones/albumart.py | 56 +++++++++++++++++++++++++++++++++++-- headphones/postprocessor.py | 20 +++++-------- 2 files changed, 61 insertions(+), 15 deletions(-) diff --git a/headphones/albumart.py b/headphones/albumart.py index 8bbbd425..803d469a 100644 --- a/headphones/albumart.py +++ b/headphones/albumart.py @@ -17,12 +17,64 @@ from headphones import request, db, logger def getAlbumArt(albumid): + + artwork_path = None + artwork = None + + # 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: + logger.info("Artwork found at CAA") + return artwork_path, artwork + + # Amazon myDB = db.DBConnection() asin = myDB.action( 'SELECT AlbumASIN from albums WHERE AlbumID=?', [albumid]).fetchone()[0] - if asin: - return 'http://ec1.images-amazon.com/images/P/%s.01.LZZZZZZZ.jpg' % 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: + logger.info("Artwork found at Amazon") + return artwork_path, artwork + + # last.fm + from headphones import lastfm + + myDB = db.DBConnection() + dbalbum = myDB.action( + 'SELECT ArtistName, AlbumTitle, ReleaseID FROM albums WHERE AlbumID=?', + [albumid]).fetchone() + if dbalbum['ReleaseID'] != albumid: + data = lastfm.request_lastfm("album.getinfo", mbid=dbalbum['ReleaseID']) + if not data: + data = lastfm.request_lastfm("album.getinfo", artist=dbalbum['ArtistName'], + album=dbalbum['AlbumTitle']) + else: + data = lastfm.request_lastfm("album.getinfo", artist=dbalbum['ArtistName'], + album=dbalbum['AlbumTitle']) + + if data: + try: + images = data['album']['image'] + for image in images: + if image['size'] == 'extralarge': + artwork_path = image['#text'] + elif image['size'] == 'mega': + artwork_path = image['#text'] + break + except KeyError: + artwork_path = None + + if artwork_path: + artwork = request.request_content(artwork_path, timeout=20) + if artwork and len(artwork) >= 100: + logger.info("Artwork found at last.fm") + return artwork_path, artwork + + logger.info("No suitable album art found.") + return None, None def getCachedArt(albumid): diff --git a/headphones/postprocessor.py b/headphones/postprocessor.py index 9a7a4e9c..39393e58 100755 --- a/headphones/postprocessor.py +++ b/headphones/postprocessor.py @@ -410,21 +410,15 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list, shutil.rmtree(new_folder) return + # get artwork and path + album_art_path = None artwork = None - album_art_path = albumart.getAlbumArt(albumid) - if headphones.CONFIG.EMBED_ALBUM_ART or headphones.CONFIG.ADD_ALBUM_ART: + 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 CONFIG.XBMC_NOTIFY): - if album_art_path: - artwork = request.request_content(album_art_path) - else: - artwork = None - - if not album_art_path or not artwork or len(artwork) < 100: - logger.info("No suitable album art found from Amazon. Checking Last.FM....") - artwork = albumart.getCachedArt(albumid) - if not artwork or len(artwork) < 100: - artwork = False - logger.info("No suitable album art found from Last.FM. Not adding album art") + logger.info('Searching for artwork') + album_art_path, artwork = albumart.getAlbumArt(albumid) if headphones.CONFIG.EMBED_ALBUM_ART and artwork: embedAlbumArt(artwork, downloaded_track_list) From 7797cd0681757672888f6808f7d6a5fb0265002c Mon Sep 17 00:00:00 2001 From: Ade Date: Sat, 11 Mar 2017 12:37:13 +1300 Subject: [PATCH 02/25] pep8 --- headphones/postprocessor.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/headphones/postprocessor.py b/headphones/postprocessor.py index 39393e58..7727fd82 100755 --- a/headphones/postprocessor.py +++ b/headphones/postprocessor.py @@ -414,9 +414,8 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list, album_art_path = None artwork = None 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 CONFIG.XBMC_NOTIFY): - + (headphones.CONFIG.PLEX_ENABLED and headphones.CONFIG.PLEX_NOTIFY) or \ + (headphones.CONFIG.XBMC_ENABLED and CONFIG.XBMC_NOTIFY): logger.info('Searching for artwork') album_art_path, artwork = albumart.getAlbumArt(albumid) From a9bbd34a52d31a34f707e24050c355321c313df0 Mon Sep 17 00:00:00 2001 From: Ade Date: Sat, 11 Mar 2017 12:44:13 +1300 Subject: [PATCH 03/25] more pep8 --- headphones/postprocessor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/headphones/postprocessor.py b/headphones/postprocessor.py index 7727fd82..620ff748 100755 --- a/headphones/postprocessor.py +++ b/headphones/postprocessor.py @@ -28,7 +28,7 @@ from beets.mediafile import MediaFile, FileTypeError, UnreadableFileError from beetsplug import lyrics as beetslyrics from headphones import notifiers, utorrent, transmission, deluge, qbittorrent from headphones import db, albumart, librarysync -from headphones import logger, helpers, request, mb, music_encoder +from headphones import logger, helpers, mb, music_encoder from headphones import metadata postprocessor_lock = threading.Lock() @@ -415,7 +415,7 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list, artwork = None 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 CONFIG.XBMC_NOTIFY): + (headphones.CONFIG.XBMC_ENABLED and headphones.CONFIG.XBMC_NOTIFY): logger.info('Searching for artwork') album_art_path, artwork = albumart.getAlbumArt(albumid) From e415de769849b1414e8011827b8deeab73aece09 Mon Sep 17 00:00:00 2001 From: Noam Date: Sat, 11 Mar 2017 15:38:12 +0200 Subject: [PATCH 04/25] Added JSON-related HTTP headers --- headphones/deluge.py | 49 ++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/headphones/deluge.py b/headphones/deluge.py index 5f16a2ba..cfab6355 100644 --- a/headphones/deluge.py +++ b/headphones/deluge.py @@ -49,6 +49,7 @@ delugeweb_auth = {} delugeweb_url = '' deluge_verify_cert = False scrub_logs = True +headers = {'Accept': 'application/json', 'Content-Type': 'application/json'} def _scrubber(text): @@ -104,11 +105,11 @@ def addTorrent(link, data=None, name=None): # return addTorrent(local_torrent_path) else: user_agent = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2243.2 Safari/537.36' - headers = {'User-Agent': user_agent} + get_headers = {'User-Agent': user_agent} torrentfile = '' logger.debug('Deluge: Trying to download (GET)') try: - r = requests.get(link, headers=headers) + r = requests.get(link, headers=get_headers) if r.status_code == 200: logger.debug('Deluge: 200 OK') # .text will ruin the encoding for some torrents @@ -202,7 +203,7 @@ def getTorrentFolder(result): ], "id": 21}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert) + verify=deluge_verify_cert, headers=headers) result['total_done'] = json.loads(response.text)['result']['total_done'] tries = 0 @@ -210,7 +211,7 @@ def getTorrentFolder(result): tries += 1 time.sleep(5) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert) + verify=deluge_verify_cert, headers=headers) result['total_done'] = json.loads(response.text)['result']['total_done'] post_data = json.dumps({"method": "web.get_torrent_status", @@ -229,7 +230,7 @@ def getTorrentFolder(result): "id": 23}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert) + verify=deluge_verify_cert, headers=headers) result['save_path'] = json.loads(response.text)['result']['save_path'] result['name'] = json.loads(response.text)['result']['name'] @@ -253,7 +254,7 @@ def removeTorrent(torrentid, remove_data=False): ], "id": 25}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert) + verify=deluge_verify_cert, headers=headers) result = json.loads(response.text)['result'] return result @@ -296,12 +297,12 @@ def _get_auth(): "id": 1}) try: response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert) + verify=deluge_verify_cert, headers=headers) except requests.ConnectionError: try: logger.debug('Deluge: Connection failed, let\'s try HTTPS just in case') response = requests.post(delugeweb_url.replace('http:', 'https:'), data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert) + verify=deluge_verify_cert, headers=headers) # If the previous line didn't fail, change delugeweb_url for the rest of this session logger.error('Deluge: Switching to HTTPS, but certificate won\'t be verified because NO CERTIFICATE WAS CONFIGURED!') delugeweb_url = delugeweb_url.replace('http:', 'https:') @@ -326,7 +327,7 @@ def _get_auth(): "id": 10}) try: response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert) + verify=deluge_verify_cert, headers=headers) except Exception as e: logger.error('Deluge: Authentication failed: %s' % str(e)) formatted_lines = traceback.format_exc().splitlines() @@ -343,7 +344,7 @@ def _get_auth(): "id": 11}) try: response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert) + verify=deluge_verify_cert, headers=headers) except Exception as e: logger.error('Deluge: Authentication failed: %s' % str(e)) formatted_lines = traceback.format_exc().splitlines() @@ -361,7 +362,7 @@ def _get_auth(): try: response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert) + verify=deluge_verify_cert, headers=headers) except Exception as e: logger.error('Deluge: Authentication failed: %s' % str(e)) formatted_lines = traceback.format_exc().splitlines() @@ -374,7 +375,7 @@ def _get_auth(): try: response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert) + verify=deluge_verify_cert, headers=headers) except Exception as e: logger.error('Deluge: Authentication failed: %s' % str(e)) formatted_lines = traceback.format_exc().splitlines() @@ -399,7 +400,7 @@ def _add_torrent_magnet(result): "params": [result['url'], {}], "id": 2}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert) + verify=deluge_verify_cert, headers=headers) result['hash'] = json.loads(response.text)['result'] logger.debug('Deluge: Response was %s' % str(json.loads(response.text))) return json.loads(response.text)['result'] @@ -419,7 +420,7 @@ def _add_torrent_url(result): "params": [result['url'], {}], "id": 32}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert) + verify=deluge_verify_cert, headers=headers) result['location'] = json.loads(response.text)['result'] logger.debug('Deluge: Response was %s' % str(json.loads(response.text))) return json.loads(response.text)['result'] @@ -440,7 +441,7 @@ def _add_torrent_file(result): "params": [result['name'] + '.torrent', b64encode(result['content'].encode('utf8')), {}], "id": 2}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert) + verify=deluge_verify_cert, headers=headers) result['hash'] = json.loads(response.text)['result'] logger.debug('Deluge: Response was %s' % str(json.loads(response.text))) return json.loads(response.text)['result'] @@ -453,7 +454,7 @@ def _add_torrent_file(result): "params": [result['name'] + '.torrent', b64encode(result['content']), {}], "id": 22}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert) + verify=deluge_verify_cert, headers=headers) result['hash'] = json.loads(response.text)['result'] logger.debug('Deluge: Response was %s' % str(json.loads(response.text))) return json.loads(response.text)['result'] @@ -485,7 +486,7 @@ def setTorrentLabel(result): "params": [], "id": 3}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert) + verify=deluge_verify_cert, headers=headers) labels = json.loads(response.text)['result'] if labels is not None: @@ -496,7 +497,7 @@ def setTorrentLabel(result): "params": [label], "id": 4}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert) + verify=deluge_verify_cert, headers=headers) logger.debug('Deluge: %s label added to Deluge' % label) except Exception as e: logger.error('Deluge: Setting label failed: %s' % str(e)) @@ -508,7 +509,7 @@ def setTorrentLabel(result): "params": [result['hash'], label], "id": 5}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert) + verify=deluge_verify_cert, headers=headers) logger.debug('Deluge: %s label added to torrent' % label) else: logger.debug('Deluge: Label plugin not detected') @@ -532,12 +533,12 @@ def setSeedRatio(result): "params": [result['hash'], True], "id": 5}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert) + verify=deluge_verify_cert, headers=headers) post_data = json.dumps({"method": "core.set_torrent_stop_ratio", "params": [result['hash'], float(ratio)], "id": 6}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert) + verify=deluge_verify_cert, headers=headers) return not json.loads(response.text)['error'] @@ -560,7 +561,7 @@ def setTorrentPath(result): "params": [result['hash'], True], "id": 7}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert) + verify=deluge_verify_cert, headers=headers) if headphones.CONFIG.DELUGE_DONE_DIRECTORY: move_to = headphones.CONFIG.DELUGE_DONE_DIRECTORY @@ -574,7 +575,7 @@ def setTorrentPath(result): "params": [result['hash'], move_to], "id": 8}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert) + verify=deluge_verify_cert, headers=headers) return not json.loads(response.text)['error'] @@ -597,7 +598,7 @@ def setTorrentPause(result): "params": [[result['hash']]], "id": 9}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert) + verify=deluge_verify_cert, headers=headers) return not json.loads(response.text)['error'] From 578ae2c2d8246e20498e05310144e58287f153e1 Mon Sep 17 00:00:00 2001 From: Ade Date: Sun, 12 Mar 2017 12:47:12 +1300 Subject: [PATCH 05/25] More post processing artwork changes - Added min/max pixel width config options - Artwork < min width is ignored - Artwork > max width is downsized to max width using a proxy web service --- data/interfaces/default/config.html | 66 +++++++++++--- headphones/albumart.py | 129 ++++++++++++++++++++++++++-- headphones/config.py | 2 + headphones/postprocessor.py | 1 - headphones/webserve.py | 2 + 5 files changed, 181 insertions(+), 19 deletions(-) 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 @@ +
+ +
+ + +
+ +
+
+
as .jpg
Use $Artist/$artist, $Album/$album, $Year/$year, put optional variables in curly braces, use single-quote marks to escape curly braces literally ('{', '}').
-
- -
-
- + +
+
+ Album art min width \ + Album art max width +
+