Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
rembo10 committed Jan 22, 2022
2 parents b319960 + 0182be2 commit 3a9b749
Show file tree
Hide file tree
Showing 1,422 changed files with 151,464 additions and 66,878 deletions.
4 changes: 4 additions & 0 deletions Headphones.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
import os
import sys

if sys.version_info <= (3, 5):
sys.stdout.write("Headphones requires Python >= 3.5\n")
sys.exit(1)

# Ensure lib added to path, before any other imports
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'lib/'))

Expand Down
4 changes: 2 additions & 2 deletions contrib/sni_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# Ensure that we use the Headphones provided libraries.
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../lib"))

import urlparse
import urllib.parse


def can_import(module):
Expand Down Expand Up @@ -89,7 +89,7 @@ def main():
url = sys.argv[1]

# Check if it is a HTTPS website.
parts = urlparse.urlparse(url)
parts = urllib.parse.urlparse(url)

if parts.scheme.lower() != "https":
sys.stderr.write(
Expand Down
4 changes: 2 additions & 2 deletions data/interfaces/default/config.html
Original file line number Diff line number Diff line change
Expand Up @@ -1274,7 +1274,7 @@ <h1 class="clearfix"><i class="fa fa-gear"></i> Settings</h1>
<input type="checkbox" name="synoindex_enabled" id="synoindex" value="1" ${config['synoindex_enabled']} /><label for="synoindex"><span class="option">Synology NAS</span></label>
</div>
</fieldset>

<!--
<fieldset>
<div class="row checkbox left">
<input type="checkbox" class="bigcheck" name="twitter_enabled" id="twitter" value="1" ${config['twitter_enabled']} /><label for="twitter"><span class="option">Twitter</span></label>
Expand All @@ -1295,7 +1295,7 @@ <h1 class="clearfix"><i class="fa fa-gear"></i> Settings</h1>
</div>
</div>
</fieldset>

-->
<fieldset>
<div class="row checkbox left">
<input type="checkbox" class="bigcheck" name="slack_enabled" id="slack" value="1" ${config['slack_enabled']} /><label for="slack"><span class="option">Slack</span></label>
Expand Down
8 changes: 4 additions & 4 deletions data/interfaces/default/history.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<%inherit file="base.html"/>
<%!
from headphones import helpers
import cgi
from html import escape as html_escape
%>

<%def name="headerIncludes()">
Expand Down Expand Up @@ -62,11 +62,11 @@ <h1 class="clearfix"><i class="fa fa-calendar"></i> History</h1>
%>
<tr class="grade${grade}">
<td id="dateadded">${item['DateAdded']}</td>
<td id="filename">${cgi.escape(item['Title'], quote=True)} [<a href="${item['URL']}">${fileid}</a>]<a href="albumPage?AlbumID=${item['AlbumID']}">[album page]</a></td>
<td id="filename">${html_escape(item['Title'], quote=True)} [<a href="${item['URL']}">${fileid}</a>]<a href="albumPage?AlbumID=${item['AlbumID']}">[album page]</a></td>
<td id="size">${helpers.bytes_to_mb(item['Size'])}</td>
<td title="${folder}" id="status">${item['Status']}</td>
<td id="action">[<a href="javascript:void(0)" onclick="doAjaxCall('queueAlbum?AlbumID=${item['AlbumID']}&redirect=history', $(this),'table')" data-success="Retrying download of '${cgi.escape(item['Title'], quote=True)}'">retry</a>][<a href="javascript:void(0)" onclick="doAjaxCall('queueAlbum?AlbumID=${item['AlbumID']}&new=True&redirect=history',$(this),'table')" data-success="Looking for a new version of '${cgi.escape(item['Title'], quote=True)}'">new</a>]</td>
<td id="delete"><a href="javascript:void(0)" onclick="doAjaxCall('clearhistory?date_added=${item['DateAdded']}&title=${cgi.escape(item['Title'], quote=True)}',$(this),'table')" data-success="${cgi.escape(item['Title'], quote=True)} cleared from history"><img src="interfaces/default/images/trashcan.png" height="18" width="18" id="trashcan" title="Clear this item from the history"></a>
<td id="action">[<a href="javascript:void(0)" onclick="doAjaxCall('queueAlbum?AlbumID=${item['AlbumID']}&redirect=history', $(this),'table')" data-success="Retrying download of '${html_escape(item['Title'], quote=True)}'">retry</a>][<a href="javascript:void(0)" onclick="doAjaxCall('queueAlbum?AlbumID=${item['AlbumID']}&new=True&redirect=history',$(this),'table')" data-success="Looking for a new version of '${html_escape(item['Title'], quote=True)}'">new</a>]</td>
<td id="delete"><a href="javascript:void(0)" onclick="doAjaxCall('clearhistory?date_added=${item['DateAdded']}&title=${html_escape(item['Title'], quote=True)}',$(this),'table')" data-success="${html_escape(item['Title'], quote=True)} cleared from history"><img src="interfaces/default/images/trashcan.png" height="18" width="18" id="trashcan" title="Clear this item from the history"></a>
</tr>
%endfor
</tbody>
Expand Down
8 changes: 4 additions & 4 deletions headphones/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ def daemonize():
pid = os.fork() # @UndefinedVariable - only available in UNIX
if pid != 0:
sys.exit(0)
except OSError, e:
except OSError as e:
raise RuntimeError("1st fork failed: %s [%d]", e.strerror, e.errno)

os.setsid()
Expand All @@ -232,10 +232,10 @@ def daemonize():
pid = os.fork() # @UndefinedVariable - only available in UNIX
if pid != 0:
sys.exit(0)
except OSError, e:
except OSError as e:
raise RuntimeError("2nd fork failed: %s [%d]", e.strerror, e.errno)

dev_null = file('/dev/null', 'r')
dev_null = open('/dev/null', 'r')
os.dup2(dev_null.fileno(), sys.stdin.fileno())

si = open('/dev/null', "r")
Expand All @@ -251,7 +251,7 @@ def daemonize():

if CREATEPID:
logger.info("Writing PID %d to %s", pid, PIDFILE)
with file(PIDFILE, 'w') as fp:
with open(PIDFILE, 'w') as fp:
fp.write("%s\n" % pid)


Expand Down
14 changes: 7 additions & 7 deletions headphones/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def fetchData(self):
methodToCall = getattr(self, "_" + self.cmd)
methodToCall(**self.kwargs)
if 'callback' not in self.kwargs:
if isinstance(self.data, basestring):
if isinstance(self.data, str):
return self.data
else:
return json.dumps(self.data)
Expand All @@ -106,7 +106,7 @@ def _dic_from_query(self, query):
rows_as_dic = []

for row in rows:
row_as_dic = dict(zip(row.keys(), row))
row_as_dic = dict(list(zip(list(row.keys()), row)))
rows_as_dic.append(row_as_dic)

return rows_as_dic
Expand Down Expand Up @@ -474,17 +474,17 @@ def _download_specific_release(self, **kwargs):
# Handle situations where the torrent url contains arguments that are
# parsed
if kwargs:
import urllib
import urllib2
url = urllib2.quote(
url, safe=":?/=&") + '&' + urllib.urlencode(kwargs)
import urllib.request, urllib.parse, urllib.error
import urllib.request, urllib.error, urllib.parse
url = urllib.parse.quote(
url, safe=":?/=&") + '&' + urllib.parse.urlencode(kwargs)

try:
result = [(title, int(size), url, provider, kind)]
except ValueError:
result = [(title, float(size), url, provider, kind)]

logger.info(u"Making sure we can download the chosen result")
logger.info("Making sure we can download the chosen result")
(data, bestqual) = searcher.preprocess(result)

if data and bestqual:
Expand Down
4 changes: 2 additions & 2 deletions headphones/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ def get_image_links(self, ArtistID=None, AlbumID=None):

# fallback to 1st album cover if none of the above
elif 'albums' in data:
for mbid, art in data.get('albums', dict()).items():
for mbid, art in list(data.get('albums', dict()).items()):
if 'albumcover' in art:
image_url = art['albumcover'][0]['url']
break
Expand Down Expand Up @@ -352,7 +352,7 @@ def _update_cache(self):

# fallback to 1st album cover if none of the above
elif 'albums' in data:
for mbid, art in data.get('albums', dict()).items():
for mbid, art in list(data.get('albums', dict()).items()):
if 'albumcover' in art:
image_url = art['albumcover'][0]['url']
break
Expand Down
8 changes: 4 additions & 4 deletions headphones/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@
#######################################


import urllib
import urllib.request, urllib.parse, urllib.error

from common import USER_AGENT
from .common import USER_AGENT


class HeadphonesURLopener(urllib.FancyURLopener):
class HeadphonesURLopener(urllib.request.FancyURLopener):
version = USER_AGENT


Expand All @@ -44,7 +44,7 @@ def __init__(self, user, pw):
self.numTries = 0

# call the base class
urllib.FancyURLopener.__init__(self)
urllib.request.FancyURLopener.__init__(self)

def prompt_user_passwd(self, host, realm):
"""
Expand Down
13 changes: 7 additions & 6 deletions headphones/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import os
import re
from headphones import version
from functools import reduce


# Identify Our Application
Expand Down Expand Up @@ -74,7 +75,7 @@ class Quality:
@staticmethod
def _getStatusStrings(status):
toReturn = {}
for x in Quality.qualityStrings.keys():
for x in list(Quality.qualityStrings.keys()):
toReturn[Quality.compositeStatus(status, x)] = Quality.statusPrefixes[status] + " (" + \
Quality.qualityStrings[x] + ")"
return toReturn
Expand All @@ -93,7 +94,7 @@ def combineQualities(anyQualities, bestQualities):
def splitQuality(quality):
anyQualities = []
bestQualities = []
for curQual in Quality.qualityStrings.keys():
for curQual in list(Quality.qualityStrings.keys()):
if curQual & quality:
anyQualities.append(curQual)
if curQual << 16 & quality:
Expand Down Expand Up @@ -151,7 +152,7 @@ def qualityDownloaded(status):
@staticmethod
def splitCompositeStatus(status):
"""Returns a tuple containing (status, quality)"""
for x in sorted(Quality.qualityStrings.keys(), reverse=True):
for x in sorted(list(Quality.qualityStrings.keys()), reverse=True):
if status > x * 100:
return (status - x * 100, x)

Expand All @@ -169,10 +170,10 @@ def statusFromName(name, assume=True):
SNATCHED_PROPER = None


Quality.DOWNLOADED = [Quality.compositeStatus(DOWNLOADED, x) for x in Quality.qualityStrings.keys()]
Quality.SNATCHED = [Quality.compositeStatus(SNATCHED, x) for x in Quality.qualityStrings.keys()]
Quality.DOWNLOADED = [Quality.compositeStatus(DOWNLOADED, x) for x in list(Quality.qualityStrings.keys())]
Quality.SNATCHED = [Quality.compositeStatus(SNATCHED, x) for x in list(Quality.qualityStrings.keys())]
Quality.SNATCHED_PROPER = [Quality.compositeStatus(SNATCHED_PROPER, x) for x in
Quality.qualityStrings.keys()]
list(Quality.qualityStrings.keys())]

MP3 = Quality.combineQualities([Quality.B192, Quality.B256, Quality.B320, Quality.VBR], [])
LOSSLESS = Quality.combineQualities([Quality.FLAC], [])
Expand Down
56 changes: 35 additions & 21 deletions headphones/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@

import os
import re
import ast
from configparser import ConfigParser
import headphones.logger
from configobj import ConfigObj


def bool_int(value):
"""
Casts a config value into a 0 or 1
"""
if isinstance(value, basestring):
if isinstance(value, str):
if value.lower() in ('', '0', 'false', 'f', 'no', 'n', 'off'):
value = 0
return int(bool(value))
Expand Down Expand Up @@ -326,8 +327,9 @@ class Config(object):
def __init__(self, config_file):
""" Initialize the config with values from a file """
self._config_file = config_file
self._config = ConfigObj(self._config_file, encoding='utf-8')
for key in _CONFIG_DEFINITIONS.keys():
self._config = ConfigParser()
self._config.read(self._config_file)
for key in list(_CONFIG_DEFINITIONS.keys()):
self.check_setting(key)
self.ENCODER_MULTICORE_COUNT = max(0, self.ENCODER_MULTICORE_COUNT)
self._upgrade()
Expand All @@ -344,7 +346,7 @@ def _define(self, name):

def check_section(self, section):
""" Check if INI section exists, if not create it """
if section not in self._config:
if not self._config.has_section(section):
self._config[section] = {}
return True
else:
Expand All @@ -354,28 +356,38 @@ def check_setting(self, key):
""" Cast any value in the config to the right type or use the default """
key, definition_type, section, ini_key, default = self._define(key)
self.check_section(section)

# ConfigParser values are strings, so need to convert to actual list
if definition_type == list:
definition_type = ast.literal_eval

try:
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
# some_key = ''
if my_val == '""' or my_val == "''":
my_val = ''
except Exception:
my_val = definition_type(default)
self._config[section][ini_key] = my_val
my_val = default
self._config[section][ini_key] = str(my_val)
return my_val

def write(self):
""" Make a copy of the stored config and write it to the configured file """
new_config = ConfigObj(encoding="UTF-8")
new_config.filename = self._config_file
new_config = ConfigParser()

# first copy over everything from the old config, even if it is not
# correctly defined to keep from losing data
for key, subkeys in self._config.items():
for key, subkeys in list(self._config.items()):
if key not in new_config:
new_config[key] = {}
for subkey, value in subkeys.items():
for subkey, value in list(subkeys.items()):
new_config[key][subkey] = value

# next make sure that everything we expect to have defined is so
for key in _CONFIG_DEFINITIONS.keys():
for key in list(_CONFIG_DEFINITIONS.keys()):
key, definition_type, section, ini_key, default = self._define(key)
self.check_setting(key)
if section not in new_config:
Expand All @@ -386,14 +398,15 @@ def write(self):
headphones.logger.info("Writing configuration to file")

try:
new_config.write()
with open(self._config_file, 'w') as configfile:
new_config.write(configfile)
except IOError as e:
headphones.logger.error("Error writing configuration file: %s", e)

def get_extra_newznabs(self):
""" Return the extra newznab tuples """
extra_newznabs = list(
itertools.izip(*[itertools.islice(self.EXTRA_NEWZNABS, i, None, 3)
zip(*[itertools.islice(self.EXTRA_NEWZNABS, i, None, 3)
for i in range(3)])
)
return extra_newznabs
Expand All @@ -412,7 +425,7 @@ def add_extra_newznab(self, newznab):
def get_extra_torznabs(self):
""" Return the extra torznab tuples """
extra_torznabs = list(
itertools.izip(*[itertools.islice(self.EXTRA_TORZNABS, i, None, 4)
zip(*[itertools.islice(self.EXTRA_TORZNABS, i, None, 4)
for i in range(4)])
)
return extra_torznabs
Expand Down Expand Up @@ -448,20 +461,21 @@ def __setattr__(self, name, value):
return value
else:
key, definition_type, section, ini_key, default = self._define(name)
self._config[section][ini_key] = definition_type(value)
self._config[section][ini_key] = str(value)
return self._config[section][ini_key]

def process_kwargs(self, kwargs):
"""
Given a big bunch of key value pairs, apply them to the ini.
"""
for name, value in kwargs.items():
for name, value in list(kwargs.items()):
key, definition_type, section, ini_key, default = self._define(name)
self._config[section][ini_key] = definition_type(value)
self._config[section][ini_key] = str(value)

def _upgrade(self):
"""
Bring old configs up to date
Bring old configs up to date. Although this is kind of a dumb
way to do it because it doesn't handle multi-step upgrades
"""
if self.CONFIG_VERSION == '2':
# Update the config to use direct path to the encoder rather than the encoder folder
Expand All @@ -488,12 +502,12 @@ def _upgrade(self):
# Add Seed Ratio to Torznabs
if self.EXTRA_TORZNABS:
extra_torznabs = list(
itertools.izip(*[itertools.islice(self.EXTRA_TORZNABS, i, None, 3)
zip(*[itertools.islice(self.EXTRA_TORZNABS, i, None, 3)
for i in range(3)])
)
new_torznabs = []
for torznab in extra_torznabs:
new_torznabs.extend([torznab[0], torznab[1], u'', torznab[2]])
new_torznabs.extend([torznab[0], torznab[1], '', torznab[2]])
if new_torznabs:
self.EXTRA_TORZNABS = new_torznabs

Expand Down
Loading

0 comments on commit 3a9b749

Please sign in to comment.