diff --git a/headphones/api.py b/headphones/api.py index 025f12cf..63f9500a 100644 --- a/headphones/api.py +++ b/headphones/api.py @@ -474,8 +474,12 @@ class Api(object): # Handle situations where the torrent url contains arguments that are # parsed if kwargs: - import urllib.request, urllib.parse, urllib.error - import urllib.request, urllib.error, urllib.parse + import urllib.request + import urllib.parse + import urllib.error + import urllib.request + import urllib.error + import urllib.parse url = urllib.parse.quote( url, safe=":?/=&") + '&' + urllib.parse.urlencode(kwargs) diff --git a/headphones/cache.py b/headphones/cache.py index b478123c..b47fb5f5 100644 --- a/headphones/cache.py +++ b/headphones/cache.py @@ -388,9 +388,9 @@ class Cache(object): else: if dbalbum['Type'] != "part of": data = lastfm.request_lastfm("album.getinfo", - artist=helpers.clean_musicbrainz_name(dbalbum['ArtistName']), - album=helpers.clean_musicbrainz_name(dbalbum['AlbumTitle']), - api_key=LASTFM_API_KEY) + artist=helpers.clean_musicbrainz_name(dbalbum['ArtistName']), + album=helpers.clean_musicbrainz_name(dbalbum['AlbumTitle']), + api_key=LASTFM_API_KEY) else: # Series, use actual artist for the release-group @@ -484,7 +484,7 @@ class Cache(object): self.id + '_fanart_' + '.' + helpers.today() + ext) else: artwork_path = os.path.join(self.path_to_art_cache, - self.id + '.' + helpers.today() + ext) + self.id + '.' + helpers.today() + ext) try: with open(artwork_path, 'wb') as f: f.write(artwork) diff --git a/headphones/classes.py b/headphones/classes.py index a06f4f82..63c17f98 100644 --- a/headphones/classes.py +++ b/headphones/classes.py @@ -18,7 +18,9 @@ ####################################### -import urllib.request, urllib.parse, urllib.error +import urllib.request +import urllib.parse +import urllib.error from .common import USER_AGENT diff --git a/headphones/common.py b/headphones/common.py index a311860a..7b8477ec 100644 --- a/headphones/common.py +++ b/headphones/common.py @@ -77,7 +77,7 @@ class Quality: toReturn = {} for x in list(Quality.qualityStrings.keys()): toReturn[Quality.compositeStatus(status, x)] = Quality.statusPrefixes[status] + " (" + \ - Quality.qualityStrings[x] + ")" + Quality.qualityStrings[x] + ")" return toReturn @staticmethod diff --git a/headphones/config.py b/headphones/config.py index 69fdff08..64e478c2 100644 --- a/headphones/config.py +++ b/headphones/config.py @@ -31,6 +31,7 @@ class path(str): def __repr__(self): return 'headphones.config.path(%s)' % self + _CONFIG_DEFINITIONS = { 'ADD_ALBUM_ART': (int, 'General', 0), 'ADVANCEDENCODER': (str, 'General', ''), @@ -365,7 +366,7 @@ class Config(object): my_val = definition_type(self._config[section][ini_key]) # ConfigParser interprets empty strings in the config # literally, so we need to sanitize it. It's not really - # a config upgrade, since a user can at any time put + # a config upgrade, since a user can at any time put # some_key = '' if my_val == '""' or my_val == "''": my_val = '' @@ -407,7 +408,7 @@ class Config(object): """ Return the extra newznab tuples """ extra_newznabs = list( zip(*[itertools.islice(self.EXTRA_NEWZNABS, i, None, 3) - for i in range(3)]) + for i in range(3)]) ) return extra_newznabs @@ -426,7 +427,7 @@ class Config(object): """ Return the extra torznab tuples """ extra_torznabs = list( zip(*[itertools.islice(self.EXTRA_TORZNABS, i, None, 4) - for i in range(4)]) + for i in range(4)]) ) return extra_torznabs @@ -503,7 +504,7 @@ class Config(object): if self.EXTRA_TORZNABS: extra_torznabs = list( zip(*[itertools.islice(self.EXTRA_TORZNABS, i, None, 3) - for i in range(3)]) + for i in range(3)]) ) new_torznabs = [] for torznab in extra_torznabs: diff --git a/headphones/db.py b/headphones/db.py index e0b36afe..39940726 100644 --- a/headphones/db.py +++ b/headphones/db.py @@ -18,7 +18,6 @@ ################################### - import time import sqlite3 diff --git a/headphones/deluge.py b/headphones/deluge.py index 578adc6d..aa01dedc 100644 --- a/headphones/deluge.py +++ b/headphones/deluge.py @@ -35,7 +35,6 @@ # along with SickRage. If not, see . - from headphones import logger import time @@ -89,7 +88,7 @@ def addTorrent(link, data=None, name=None): if link.lower().startswith('magnet:'): logger.debug('Deluge: Got a magnet link: %s' % _scrubber(link)) result = {'type': 'magnet', - 'url': link} + 'url': link} retid = _add_torrent_magnet(result) elif link.lower().startswith('http://') or link.lower().startswith('https://'): @@ -143,8 +142,8 @@ def addTorrent(link, data=None, name=None): except: logger.debug('Deluge: Sending Deluge torrent with problematic name and some content') result = {'type': 'torrent', - 'name': name, - 'content': torrentfile} + 'name': name, + 'content': torrentfile} retid = _add_torrent_file(result) # elif link.endswith('.torrent') or data: @@ -175,8 +174,8 @@ def addTorrent(link, data=None, name=None): except UnicodeDecodeError: logger.debug('Deluge: Sending Deluge torrent with name %s and content [%s...]' % (name.decode('utf-8'), str(torrentfile)[:40])) result = {'type': 'torrent', - 'name': name, - 'content': torrentfile} + 'name': name, + 'content': torrentfile} retid = _add_torrent_file(result) else: @@ -208,7 +207,7 @@ def getTorrentFolder(result): ], "id": 21}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert, headers=headers) + verify=deluge_verify_cert, headers=headers) result['total_done'] = json.loads(response.text)['result']['total_done'] tries = 0 @@ -216,7 +215,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, headers=headers) + 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", @@ -235,7 +234,7 @@ def getTorrentFolder(result): "id": 23}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert, headers=headers) + verify=deluge_verify_cert, headers=headers) result['save_path'] = json.loads(response.text)['result']['save_path'] result['name'] = json.loads(response.text)['result']['name'] @@ -264,7 +263,7 @@ def removeTorrent(torrentid, remove_data=False): "id": 26}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert, headers=headers) + verify=deluge_verify_cert, headers=headers) try: state = json.loads(response.text)['result']['state'] @@ -283,10 +282,10 @@ def removeTorrent(torrentid, remove_data=False): "params": [ torrentid, remove_data - ], + ], "id": 25}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert, headers=headers) + verify=deluge_verify_cert, headers=headers) result = json.loads(response.text)['result'] return result @@ -329,12 +328,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, headers=headers) + 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, headers=headers) + 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:') @@ -359,7 +358,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, headers=headers) + verify=deluge_verify_cert, headers=headers) except Exception as e: logger.error('Deluge: Authentication failed: %s' % str(e)) formatted_lines = traceback.format_exc().splitlines() @@ -376,7 +375,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, headers=headers) + verify=deluge_verify_cert, headers=headers) except Exception as e: logger.error('Deluge: Authentication failed: %s' % str(e)) formatted_lines = traceback.format_exc().splitlines() @@ -395,7 +394,7 @@ def _get_auth(): try: response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert, headers=headers) + verify=deluge_verify_cert, headers=headers) except Exception as e: logger.error('Deluge: Authentication failed: %s' % str(e)) formatted_lines = traceback.format_exc().splitlines() @@ -408,7 +407,7 @@ def _get_auth(): try: response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert, headers=headers) + verify=deluge_verify_cert, headers=headers) except Exception as e: logger.error('Deluge: Authentication failed: %s' % str(e)) formatted_lines = traceback.format_exc().splitlines() @@ -433,7 +432,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, headers=headers) + 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 +452,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, headers=headers) + 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'] @@ -472,10 +471,10 @@ def _add_torrent_file(result): # content is torrent file contents that needs to be encoded to base64 post_data = json.dumps({"method": "core.add_torrent_file", "params": [result['name'] + '.torrent', - b64encode(result['content']).decode(), {}], + b64encode(result['content']).decode(), {}], "id": 2}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, - verify=deluge_verify_cert, headers=headers) + 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'] @@ -502,7 +501,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, headers=headers) + verify=deluge_verify_cert, headers=headers) labels = json.loads(response.text)['result'] if labels is not None: @@ -513,7 +512,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, headers=headers) + 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)) @@ -525,7 +524,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, headers=headers) + verify=deluge_verify_cert, headers=headers) logger.debug('Deluge: %s label added to torrent' % label) else: logger.debug('Deluge: Label plugin not detected') @@ -549,12 +548,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, headers=headers) + 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, headers=headers) + verify=deluge_verify_cert, headers=headers) return not json.loads(response.text)['error'] @@ -577,7 +576,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, headers=headers) + verify=deluge_verify_cert, headers=headers) if headphones.CONFIG.DELUGE_DONE_DIRECTORY: move_to = headphones.CONFIG.DELUGE_DONE_DIRECTORY @@ -591,7 +590,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, headers=headers) + verify=deluge_verify_cert, headers=headers) return not json.loads(response.text)['error'] @@ -614,7 +613,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, headers=headers) + verify=deluge_verify_cert, headers=headers) return not json.loads(response.text)['error'] diff --git a/headphones/helpers.py b/headphones/helpers.py index a1f216ec..7fcfede6 100644 --- a/headphones/helpers.py +++ b/headphones/helpers.py @@ -42,6 +42,7 @@ RE_FEATURING = re.compile(r"[fF]t\.|[fF]eaturing|[fF]eat\.|\b[wW]ith\b|&|vs\.") RE_CD_ALBUM = re.compile(r"\(?((CD|disc)\s*[0-9]+)\)?", re.I) RE_CD = re.compile(r"^(CD|dics)\s*[0-9]+$", re.I) + def cmp(x, y): """ Replacement for built-in function cmp that was removed in Python 3 @@ -54,6 +55,7 @@ def cmp(x, y): """ return (x > y) - (x < y) + def multikeysort(items, columns): comparers = [ ((itemgetter(col[1:].strip()), -1) if col.startswith('-') else (itemgetter(col.strip()), 1)) @@ -232,7 +234,7 @@ def pattern_substitute(pattern, dic, normalize=False): j = unicodedata.normalize('NFC', j) except TypeError: j = unicodedata.normalize('NFC', - j.decode(headphones.SYS_ENCODING, 'replace')) + j.decode(headphones.SYS_ENCODING, 'replace')) new_dic[i] = j dic = new_dic return pathrender.render(pattern, dic)[0] @@ -277,7 +279,7 @@ _XLATE_GRAPHICAL_AND_DIACRITICAL = { 'Ǥ': 'G', 'ǥ': 'g', 'DZ': 'DZ', 'Dz': 'Dz', 'dz': 'dz', 'Ȥ': 'Z', 'ȥ': 'z', '№': 'No.', 'º': 'o.', # normalize Nº abbrev (popular w/ classical music), - # this is 'masculine ordering indicator', not degree + # this is 'masculine ordering indicator', not degree } _XLATE_SPECIAL = { @@ -882,7 +884,7 @@ def smartMove(src, dest, delete=True): shutil.copy(source_path, dest_path) return True except Exception as e: - logger.warn(f"Error copying {filename}: {e}") + logger.warn(f"Error copying {filename}: {e}") def walk_directory(basedir, followlinks=True): diff --git a/headphones/helpers_test.py b/headphones/helpers_test.py index 3fd09aa4..b7cf09e9 100644 --- a/headphones/helpers_test.py +++ b/headphones/helpers_test.py @@ -14,9 +14,9 @@ class HelpersTest(TestCase): 'Symphonęy Nº9': 'Symphoney No.9', 'ÆæßðÞIJij': 'AeaessdThIJıj', 'Obsessió (Cerebral Apoplexy remix)': 'obsessio cerebral ' - 'apoplexy remix', + 'apoplexy remix', 'Doktór Hałabała i siedmiu zbojów': 'doktor halabala i siedmiu ' - 'zbojow', + 'zbojow', 'Arbetets Söner och Döttrar': 'arbetets soner och dottrar', 'Björk Guðmundsdóttir': 'bjork gudmundsdottir', 'L\'Arc~en~Ciel': 'larc en ciel', diff --git a/headphones/importer.py b/headphones/importer.py index b2459787..f8d834e6 100644 --- a/headphones/importer.py +++ b/headphones/importer.py @@ -39,7 +39,7 @@ def is_exists(artistid): if any(artistid in x for x in artistlist): logger.info(artistlist[0][ - 1] + " is already in the database. Updating 'have tracks', but not artist information") + 1] + " is already in the database. Updating 'have tracks', but not artist information") return True else: return False diff --git a/headphones/librarysync.py b/headphones/librarysync.py index 13619d37..c5d746da 100644 --- a/headphones/librarysync.py +++ b/headphones/librarysync.py @@ -152,7 +152,7 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, # track_list.append(track_dict) check_exist_track = myDB.action("SELECT * FROM have WHERE Location=?", - [track_path]).fetchone() + [track_path]).fetchone() # Only attempt to match tracks that are new, haven't yet been matched, or metadata has changed. if not check_exist_track: # This is a new track @@ -167,7 +167,7 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, if f_artist and f_artist != check_exist_track['ArtistName']: new_artists.append(f_artist) elif f_artist and f_artist == check_exist_track['ArtistName'] and \ - check_exist_track['Matched'] != "Ignored": + check_exist_track['Matched'] != "Ignored": new_artists.append(f_artist) else: continue @@ -191,26 +191,23 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, # Now we start track matching logger.info(f"{new_track_count} new/modified tracks found and added to the database") dbtracks = myDB.action( - "SELECT * FROM have WHERE Matched IS NULL AND LOCATION LIKE ?", - [f"{dir}%"] - ) + "SELECT * FROM have WHERE Matched IS NULL AND LOCATION LIKE ?", + [f"{dir}%"] + ) dbtracks_count = myDB.action( - "SELECT COUNT(*) FROM have WHERE Matched IS NULL AND LOCATION LIKE ?", - [f"{dir}%"] - ).fetchone()[0] + "SELECT COUNT(*) FROM have WHERE Matched IS NULL AND LOCATION LIKE ?", + [f"{dir}%"] + ).fetchone()[0] logger.info(f"Found {dbtracks_count} new/modified tracks in `{dir}`") logger.info("Matching tracks to the appropriate releases....") - - # Sort the track_list by most vague (e.g. no trackid or releaseid) # to most specific (both trackid & releaseid) - # When we insert into the database, the tracks with the most + # When we insert into the database, the tracks with the most # specific information will overwrite the more general matches sorted_dbtracks = helpers.multikeysort(dbtracks, ['ArtistName', 'AlbumTitle']) - # We'll use this to give a % completion, just because the # track matching might take a while tracks_completed = 0 @@ -227,8 +224,8 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, tracks_completed += 1 completion_percentage = math.floor( - float(tracks_completed) / dbtracks_count * 1000 - ) / 10 + float(tracks_completed) / dbtracks_count * 1000 + ) / 10 if completion_percentage >= (last_completion_percentage + 10): logger.info("Track matching is " + str(completion_percentage) + "% complete") diff --git a/headphones/metadata.py b/headphones/metadata.py index 20bdddd4..2585188b 100644 --- a/headphones/metadata.py +++ b/headphones/metadata.py @@ -38,6 +38,7 @@ class MetadataDict(dict): lowercase) in member variable self._lower. If case-sensitive lookup fails, another case-insensitive attempt is made. """ + def __setitem__(self, key, value): super(MetadataDict, self).__setitem__(key, value) self._lower.__setitem__(key.lower(), value) diff --git a/headphones/notifiers.py b/headphones/notifiers.py index 95ac9cc9..ec4294b5 100644 --- a/headphones/notifiers.py +++ b/headphones/notifiers.py @@ -1,5 +1,7 @@ from urllib.parse import urlencode, quote_plus -import urllib.request, urllib.parse, urllib.error +import urllib.request +import urllib.parse +import urllib.error import subprocess import json from email.mime.text import MIMEText @@ -7,7 +9,9 @@ import smtplib import email.utils from http.client import HTTPSConnection from urllib.parse import parse_qsl -import urllib.request, urllib.error, urllib.parse +import urllib.request +import urllib.error +import urllib.parse import requests as requests import os.path @@ -17,7 +21,7 @@ import cherrypy import headphones import gntp.notifier #import oauth2 as oauth -import twitter +import twitter class GROWL(object): @@ -246,7 +250,7 @@ class XBMC(object): if version < 12: # Eden notification = header + "," + message + "," + time + \ - "," + albumartpath + "," + albumartpath notifycommand = {'command': 'ExecBuiltIn', 'parameter': 'Notification(' + notification + ')'} @@ -440,7 +444,7 @@ class Plex(object): if version < 12: # Eden notification = header + "," + message + "," + time + \ - "," + albumartpath + "," + albumartpath notifycommand = {'command': 'ExecBuiltIn', 'parameter': 'Notification(' + notification + ')'} @@ -604,12 +608,12 @@ class JOIN(object): self.url += '&deviceId={deviceid}' response = urllib.request.urlopen(self.url.format(apikey=self.apikey, - title=quote_plus(event), - text=quote_plus( - message.encode( - "utf-8")), - icon=icon, - deviceid=self.deviceid)) + title=quote_plus(event), + text=quote_plus( + message.encode( + "utf-8")), + icon=icon, + deviceid=self.deviceid)) if response: logger.info("Join notifications sent.") @@ -733,8 +737,8 @@ class TwitterNotifier(object): def notify_download(self, title): if headphones.CONFIG.TWITTER_ENABLED: self._notifyTwitter(common.notifyStrings[ - common.NOTIFY_DOWNLOAD] + ': ' + - title + ' at ' + helpers.now()) + common.NOTIFY_DOWNLOAD] + ': ' + + title + ' at ' + helpers.now()) def test_notify(self): return self._notifyTwitter( @@ -798,7 +802,7 @@ class TwitterNotifier(object): if resp['status'] != '200': logger.info('The request for a token with did not succeed: ' + str( resp['status']), - logger.ERROR) + logger.ERROR) return False else: logger.info('Your Twitter Access Token key: %s' % access_token[ @@ -1020,7 +1024,7 @@ class TELEGRAM(object): # MusicBrainz link if rgid: message += '\n\n MusicBrainz' % rgid + 'release-group/%s">MusicBrainz' % rgid # Send image response = None diff --git a/headphones/pathrender.py b/headphones/pathrender.py index a456c059..a996220d 100644 --- a/headphones/pathrender.py +++ b/headphones/pathrender.py @@ -38,6 +38,7 @@ __author__ = "Andrzej Ciarkowski " class _PatternElement(object): '''ABC for hierarchy of path name renderer pattern elements.''' + def render(self, replacement): # type: (Mapping[str,str]) -> str '''Format this _PatternElement into string using provided substitution dictionary.''' @@ -55,6 +56,7 @@ class _Generator(_PatternElement): class _Replacement(_Generator): '''Replacement variable, eg. $title.''' + def __init__(self, pattern): # type: (str) self._pattern = pattern @@ -81,6 +83,7 @@ class _Replacement(_Generator): class _LiteralText(_PatternElement): '''Just a plain piece of text to be rendered "as is".''' + def __init__(self, text): # type: (str) self._text = text diff --git a/headphones/postprocessor.py b/headphones/postprocessor.py index 875bad80..607d5ba4 100755 --- a/headphones/postprocessor.py +++ b/headphones/postprocessor.py @@ -342,6 +342,7 @@ def verify(albumid, albumpath, Kind=None, forced=False, keep_original_folder=Fal logger.warn(f"Could not identify {albumpath}. It may not be the intended album") markAsUnprocessed(albumid, albumpath, keep_original_folder) + def markAsUnprocessed(albumid, albumpath, keep_original_folder=False): myDB = db.DBConnection() myDB.action( @@ -419,7 +420,7 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list, logger.debug("Write check exact error: %s", e) logger.error( f"`{downloaded_track}` is not writable. This is required " - "for some post processing steps. Not continuing." + "for some post processing steps. Not continuing." ) if new_folder: shutil.rmtree(new_folder) @@ -595,7 +596,7 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list, logger.info("Twitter notifications temporarily disabled") #logger.info("Sending Twitter notification") #twitter = notifiers.TwitterNotifier() - #twitter.notify_download(pushmessage) + # twitter.notify_download(pushmessage) if headphones.CONFIG.OSX_NOTIFY_ENABLED: from headphones import cache @@ -786,7 +787,7 @@ def moveFiles(albumpath, release, metadata_dict): newfolder = temp_folder + '[%i]' % i lossless_destination_path = os.path.normpath( os.path.join( - headphones.CONFIG.LOSSLESS_DESTINATION_DIR, + headphones.CONFIG.LOSSLESS_DESTINATION_DIR, newfolder ) ) @@ -828,7 +829,7 @@ def moveFiles(albumpath, release, metadata_dict): newfolder = temp_folder + '[%i]' % i lossy_destination_path = os.path.normpath( os.path.join( - headphones.CONFIG.DESTINATION_DIR, + headphones.CONFIG.DESTINATION_DIR, newfolder ) ) @@ -877,7 +878,7 @@ def moveFiles(albumpath, release, metadata_dict): os.remove(file_to_move) except Exception as e: logger.error( - f"Error deleting `{file_to_move}` from source directory") + f"Error deleting `{file_to_move}` from source directory") else: logger.error( f"Error copying `{file_to_move}`. " @@ -1135,6 +1136,7 @@ def updateFilePermissions(albumpaths): logger.error(f"Could not change permissions for `{full_path}`") continue + def renameUnprocessedFolder(path, tag): """ Rename a unprocessed folder to a new unique name to indicate a certain diff --git a/headphones/qbittorrent.py b/headphones/qbittorrent.py index 236c9b00..1c270eaa 100755 --- a/headphones/qbittorrent.py +++ b/headphones/qbittorrent.py @@ -13,8 +13,12 @@ # You should have received a copy of the GNU General Public License # along with Headphones. If not, see . -import urllib.request, urllib.parse, urllib.error -import urllib.request, urllib.error, urllib.parse +import urllib.request +import urllib.parse +import urllib.error +import urllib.request +import urllib.error +import urllib.parse import http.cookiejar import json import time @@ -81,7 +85,7 @@ class qbittorrentclient(object): logger.debug('Error getting SID. qBittorrent responded with error: ' + str(err.reason)) return for cookie in self.cookiejar: - logger.debug('login cookie: ' + cookie.name + ', value: ' + cookie.value) + logger.debug('login cookie: ' + cookie.name + ', value: ' + cookie.value) return def _command(self, command, args=None, content_type=None, files=None): diff --git a/headphones/request.py b/headphones/request.py index 6b8a3630..fbf52cdb 100644 --- a/headphones/request.py +++ b/headphones/request.py @@ -220,7 +220,7 @@ def server_message(response): # First attempt is to 'read' the response as HTML if response.headers.get("content-type") and \ - "text/html" in response.headers.get("content-type"): + "text/html" in response.headers.get("content-type"): try: soup = BeautifulSoup(response.content, "html.parser") except Exception: diff --git a/headphones/rutracker.py b/headphones/rutracker.py index a2985981..35fcd91b 100644 --- a/headphones/rutracker.py +++ b/headphones/rutracker.py @@ -1,6 +1,8 @@ #!/usr/bin/env python -import urllib.request, urllib.parse, urllib.error +import urllib.request +import urllib.parse +import urllib.error import time from urllib.parse import urlparse import re diff --git a/headphones/sab.py b/headphones/sab.py index 01ef6e21..3fe69e8d 100644 --- a/headphones/sab.py +++ b/headphones/sab.py @@ -30,7 +30,7 @@ def sab_api_call(request_type=None, params={}, **kwargs): if headphones.CONFIG.SAB_HOST.endswith('/'): headphones.CONFIG.SAB_HOST = headphones.CONFIG.SAB_HOST[ - 0:len(headphones.CONFIG.SAB_HOST) - 1] + 0:len(headphones.CONFIG.SAB_HOST) - 1] url = headphones.CONFIG.SAB_HOST + "/" + "api?" diff --git a/headphones/searcher.py b/headphones/searcher.py index 5a7ff973..71652896 100644 --- a/headphones/searcher.py +++ b/headphones/searcher.py @@ -19,7 +19,9 @@ from base64 import b16encode, b32decode from hashlib import sha1 import string import random -import urllib.request, urllib.parse, urllib.error +import urllib.request +import urllib.parse +import urllib.error import datetime import subprocess import unicodedata @@ -1081,7 +1083,7 @@ def send_to_downloader(data, bestqual, album): logger.info("Twitter notifications temporarily disabled") #logger.info("Sending Twitter notification") #twitter = notifiers.TwitterNotifier() - #twitter.notify_snatch(name) + # twitter.notify_snatch(name) if headphones.CONFIG.NMA_ENABLED and headphones.CONFIG.NMA_ONSNATCH: logger.info("Sending NMA notification") nma = notifiers.NMA() @@ -1503,8 +1505,8 @@ def searchTorrent(album, new=False, losslessOnly=False, albumlength=None, try: logger.info("Attempting to log in to Orpheus.network...") orpheusobj = gazelleapi.GazelleAPI(headphones.CONFIG.ORPHEUS_USERNAME, - headphones.CONFIG.ORPHEUS_PASSWORD, - headphones.CONFIG.ORPHEUS_URL) + headphones.CONFIG.ORPHEUS_PASSWORD, + headphones.CONFIG.ORPHEUS_URL) orpheusobj._login() except Exception as e: orpheusobj = None @@ -1550,13 +1552,13 @@ def searchTorrent(album, new=False, losslessOnly=False, albumlength=None, if usersearchterm: all_torrents.extend( orpheusobj.search_torrents(searchstr=usersearchterm, format=search_format, - encoding=bitrate_string, releasetype=album_type)['results']) + encoding=bitrate_string, releasetype=album_type)['results']) else: all_torrents.extend(orpheusobj.search_torrents(artistname=semi_clean_artist_term, - groupname=semi_clean_album_term, - format=search_format, - encoding=bitrate_string, - releasetype=album_type)['results']) + groupname=semi_clean_album_term, + format=search_format, + encoding=bitrate_string, + releasetype=album_type)['results']) # filter on format, size, and num seeders logger.info("Filtering torrents by format, maximum size, and minimum seeders...") @@ -1634,8 +1636,8 @@ def searchTorrent(album, new=False, losslessOnly=False, albumlength=None, try: logger.info("Attempting to log in to Redacted...") redobj = gazelleapi.GazelleAPI(headphones.CONFIG.REDACTED_USERNAME, - headphones.CONFIG.REDACTED_PASSWORD, - providerurl) + headphones.CONFIG.REDACTED_PASSWORD, + providerurl) redobj._login() except Exception as e: redobj = None @@ -1649,12 +1651,12 @@ def searchTorrent(album, new=False, losslessOnly=False, albumlength=None, if usersearchterm: all_torrents.extend( redobj.search_torrents(searchstr=usersearchterm, format=search_format, - encoding=bitrate_string)['results']) + encoding=bitrate_string)['results']) else: all_torrents.extend(redobj.search_torrents(artistname=semi_clean_artist_term, - groupname=semi_clean_album_term, - format=search_format, - encoding=bitrate_string)['results']) + groupname=semi_clean_album_term, + format=search_format, + encoding=bitrate_string)['results']) # filter on format, size, and num seeders logger.info("Filtering torrents by format, maximum size, and minimum seeders...") @@ -1791,7 +1793,7 @@ def searchTorrent(album, new=False, losslessOnly=False, albumlength=None, headers = { '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'} provider_url = fix_url(headphones.CONFIG.OLDPIRATEBAY_URL) + \ - "/search.php?" + urllib.parse.urlencode({"q": tpb_term, "iht": 6}) + "/search.php?" + urllib.parse.urlencode({"q": tpb_term, "iht": 6}) data = request.request_soup(url=provider_url, headers=headers) diff --git a/headphones/transmission.py b/headphones/transmission.py index a787344c..096ccfbc 100644 --- a/headphones/transmission.py +++ b/headphones/transmission.py @@ -183,15 +183,15 @@ def torrentAction(method, arguments): if _session_id is not None: headers = {'x-transmission-session-id': _session_id} response = request.request_response(host, method="POST", - data=data_json, headers=headers, auth=auth, - whitelist_status_code=[200, 401, 409]) + data=data_json, headers=headers, auth=auth, + whitelist_status_code=[200, 401, 409]) else: response = request.request_response(host, auth=auth, - whitelist_status_code=[401, 409]) + whitelist_status_code=[401, 409]) if response.status_code == 401: if auth: logger.error("Username and/or password not accepted by " - "Transmission") + "Transmission") else: logger.error("Transmission authorization required") return diff --git a/headphones/utorrent.py b/headphones/utorrent.py index 1b4b2ab2..456d5db6 100644 --- a/headphones/utorrent.py +++ b/headphones/utorrent.py @@ -13,11 +13,15 @@ # You should have received a copy of the GNU General Public License # along with Headphones. If not, see . -import urllib.request, urllib.parse, urllib.error +import urllib.request +import urllib.parse +import urllib.error import json import time from collections import namedtuple -import urllib.request, urllib.error, urllib.parse +import urllib.request +import urllib.error +import urllib.parse import urllib.parse import http.cookiejar diff --git a/headphones/webserve.py b/headphones/webserve.py index 458da435..81119d0a 100644 --- a/headphones/webserve.py +++ b/headphones/webserve.py @@ -19,12 +19,16 @@ from operator import itemgetter import threading import secrets import random -import urllib.request, urllib.parse, urllib.error +import urllib.request +import urllib.parse +import urllib.error import json import time import sys from html import escape as html_escape -import urllib.request, urllib.error, urllib.parse +import urllib.request +import urllib.error +import urllib.parse import os import re @@ -785,7 +789,7 @@ class WebInterface(object): track_title = tracks['TrackTitle'] if tracks['CleanName'] != original_clean: artist_id_check = myDB.action('SELECT ArtistID FROM tracks WHERE CleanName = ?', - [tracks['CleanName']]).fetchone() + [tracks['CleanName']]).fetchone() if artist_id_check: artist_id = artist_id_check[0] myDB.action( @@ -1074,7 +1078,7 @@ class WebInterface(object): data[counter] = album['AlbumTitle'] counter += 1 - return data + return data @cherrypy.expose @cherrypy.tools.json_out()