diff --git a/README.md b/README.md
index 60116063..168a76c4 100644
--- a/README.md
+++ b/README.md
@@ -39,6 +39,6 @@ Album Page with track overview:

-If you run into any issues, visit http://headphones.codeshy.com/forum and report an issue.
+If you run into any issues, use the GitHub issue tracker at: http://github.com/rembo10/headphones/issues.
This is free software under the GPL v3 open source license - so feel free to do with it what you wish.
diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html
index 3e0641a6..07cdae9c 100644
--- a/data/interfaces/default/config.html
+++ b/data/interfaces/default/config.html
@@ -20,7 +20,7 @@
Settings
@@ -828,21 +842,21 @@
<%def name="javascriptIncludes()">
%def>
diff --git a/headphones/__init__.py b/headphones/__init__.py
index 965c5e1b..4df3318d 100644
--- a/headphones/__init__.py
+++ b/headphones/__init__.py
@@ -136,6 +136,8 @@ NZBGET_PASSWORD = None
NZBGET_CATEGORY = None
NZBGET_HOST = None
+HEADPHONES_INDEXER = False
+
NZBMATRIX = False
NZBMATRIX_USERNAME = None
NZBMATRIX_APIKEY = None
@@ -158,8 +160,6 @@ NZBSRUS = False
NZBSRUS_UID = None
NZBSRUS_APIKEY = None
-NZBX = False
-
PREFERRED_WORDS = None
IGNORED_WORDS = None
REQUIRED_WORDS = None
@@ -293,8 +293,8 @@ def initialize():
TORRENTBLACKHOLE_DIR, NUMBEROFSEEDERS, ISOHUNT, KAT, MININOVA, WAFFLES, WAFFLES_UID, WAFFLES_PASSKEY, \
RUTRACKER, RUTRACKER_USER, RUTRACKER_PASSWORD, WHATCD, WHATCD_USERNAME, WHATCD_PASSWORD, DOWNLOAD_TORRENT_DIR, \
LIBRARYSCAN, LIBRARYSCAN_INTERVAL, DOWNLOAD_SCAN_INTERVAL, SAB_HOST, SAB_USERNAME, SAB_PASSWORD, SAB_APIKEY, SAB_CATEGORY, \
- NZBGET_USERNAME, NZBGET_PASSWORD, NZBGET_CATEGORY, NZBGET_HOST, NZBMATRIX, NZBMATRIX_USERNAME, NZBMATRIX_APIKEY, NEWZNAB, NEWZNAB_HOST, NEWZNAB_APIKEY, NEWZNAB_ENABLED, EXTRA_NEWZNABS, \
- NZBSORG, NZBSORG_UID, NZBSORG_HASH, NEWZBIN, NEWZBIN_UID, NEWZBIN_PASSWORD, NZBSRUS, NZBSRUS_UID, NZBSRUS_APIKEY, NZBX, \
+ NZBGET_USERNAME, NZBGET_PASSWORD, NZBGET_CATEGORY, NZBGET_HOST, HEADPHONES_INDEXER, NZBMATRIX, NZBMATRIX_USERNAME, NZBMATRIX_APIKEY, NEWZNAB, NEWZNAB_HOST, NEWZNAB_APIKEY, NEWZNAB_ENABLED, EXTRA_NEWZNABS, \
+ NZBSORG, NZBSORG_UID, NZBSORG_HASH, NEWZBIN, NEWZBIN_UID, NEWZBIN_PASSWORD, NZBSRUS, NZBSRUS_UID, NZBSRUS_APIKEY, \
NZB_DOWNLOADER, PREFERRED_WORDS, REQUIRED_WORDS, IGNORED_WORDS, \
LASTFM_USERNAME, INTERFACE, FOLDER_PERMISSIONS, ENCODERFOLDER, ENCODER_PATH, ENCODER, XLDPROFILE, BITRATE, SAMPLINGFREQUENCY, \
MUSIC_ENCODER, ADVANCEDENCODER, ENCODEROUTPUTFORMAT, ENCODERQUALITY, ENCODERVBRCBR, ENCODERLOSSLESS, DELETE_LOSSLESS_FILES, \
@@ -311,11 +311,11 @@ def initialize():
CheckSection('General')
CheckSection('SABnzbd')
CheckSection('NZBget')
+ CheckSection('Headphones')
CheckSection('NZBMatrix')
CheckSection('Newznab')
CheckSection('NZBsorg')
CheckSection('NZBsRus')
- CheckSection('nzbX')
CheckSection('Newzbin')
CheckSection('Waffles')
CheckSection('Rutracker')
@@ -421,7 +421,9 @@ def initialize():
NZBGET_PASSWORD = check_setting_str(CFG, 'NZBget', 'nzbget_password', '')
NZBGET_CATEGORY = check_setting_str(CFG, 'NZBget', 'nzbget_category', '')
NZBGET_HOST = check_setting_str(CFG, 'NZBget', 'nzbget_host', '')
-
+
+ HEADPHONES_INDEXER = bool(check_setting_int(CFG, 'Headphones', 'headphones_indexer', 0))
+
NZBMATRIX = bool(check_setting_int(CFG, 'NZBMatrix', 'nzbmatrix', 0))
NZBMATRIX_USERNAME = check_setting_str(CFG, 'NZBMatrix', 'nzbmatrix_username', '')
NZBMATRIX_APIKEY = check_setting_str(CFG, 'NZBMatrix', 'nzbmatrix_apikey', '')
@@ -446,9 +448,7 @@ def initialize():
NZBSRUS = bool(check_setting_int(CFG, 'NZBsRus', 'nzbsrus', 0))
NZBSRUS_UID = check_setting_str(CFG, 'NZBsRus', 'nzbsrus_uid', '')
NZBSRUS_APIKEY = check_setting_str(CFG, 'NZBsRus', 'nzbsrus_apikey', '')
-
- NZBX = bool(check_setting_int(CFG, 'nzbX', 'nzbx', 0))
-
+
PREFERRED_WORDS = check_setting_str(CFG, 'General', 'preferred_words', '')
IGNORED_WORDS = check_setting_str(CFG, 'General', 'ignored_words', '')
REQUIRED_WORDS = check_setting_str(CFG, 'General', 'required_words', '')
@@ -554,12 +554,18 @@ def initialize():
if ENCODERFOLDER:
ENCODER_PATH = os.path.join(ENCODERFOLDER, ENCODER)
CONFIG_VERSION = '3'
-
+
if CONFIG_VERSION == '3':
#Update the BLACKHOLE option to the NZB_DOWNLOADER format
if BLACKHOLE:
NZB_DOWNLOADER = 2
CONFIG_VERSION = '4'
+
+ # Enable Headphones Indexer if they have a VIP account
+ if CONFIG_VERSION == '4':
+ if HPUSER and HPPASS:
+ HEADPHONES_INDEXER = True
+ CONFIG_VERSION = '5'
if not LOG_DIR:
LOG_DIR = os.path.join(DATA_DIR, 'logs')
@@ -763,12 +769,15 @@ def config_write():
new_config['SABnzbd']['sab_password'] = SAB_PASSWORD
new_config['SABnzbd']['sab_apikey'] = SAB_APIKEY
new_config['SABnzbd']['sab_category'] = SAB_CATEGORY
-
+
new_config['NZBget'] = {}
new_config['NZBget']['nzbget_username'] = NZBGET_USERNAME
new_config['NZBget']['nzbget_password'] = NZBGET_PASSWORD
new_config['NZBget']['nzbget_category'] = NZBGET_CATEGORY
new_config['NZBget']['nzbget_host'] = NZBGET_HOST
+
+ new_config['Headphones'] = {}
+ new_config['Headphones']['headphones_indexer'] = int(HEADPHONES_INDEXER)
new_config['NZBMatrix'] = {}
new_config['NZBMatrix']['nzbmatrix'] = int(NZBMATRIX)
@@ -802,10 +811,7 @@ def config_write():
new_config['NZBsRus']['nzbsrus'] = int(NZBSRUS)
new_config['NZBsRus']['nzbsrus_uid'] = NZBSRUS_UID
new_config['NZBsRus']['nzbsrus_apikey'] = NZBSRUS_APIKEY
-
- new_config['nzbX'] = {}
- new_config['nzbX']['nzbx'] = int(NZBX)
-
+
new_config['General']['preferred_words'] = PREFERRED_WORDS
new_config['General']['ignored_words'] = IGNORED_WORDS
new_config['General']['required_words'] = REQUIRED_WORDS
@@ -1069,17 +1075,17 @@ def dbcheck():
for artist in artists:
if artist[1]:
c.execute('UPDATE artists SET Extras=? WHERE ArtistID=?', ("1,2,3,4,5,6,7,8", artist[0]))
-
+
try:
c.execute('SELECT Kind from snatched')
except sqlite3.OperationalError:
c.execute('ALTER TABLE snatched ADD COLUMN Kind TEXT DEFAULT NULL')
-
+
try:
c.execute('SELECT SearchTerm from albums')
except sqlite3.OperationalError:
c.execute('ALTER TABLE albums ADD COLUMN SearchTerm TEXT DEFAULT NULL')
-
+
conn.commit()
c.close()
diff --git a/headphones/searcher.py b/headphones/searcher.py
index 8ec70247..c46504bf 100644
--- a/headphones/searcher.py
+++ b/headphones/searcher.py
@@ -25,7 +25,7 @@ from xml.dom import minidom
from xml.parsers.expat import ExpatError
import lib.simplejson as json
from StringIO import StringIO
-import gzip
+import gzip, base64
import os, re, time
import string
@@ -80,7 +80,7 @@ def getNewzbinURL(url):
f.close()
return data
-
+
def url_fix(s, charset='utf-8'):
if isinstance(s, unicode):
s = s.encode(charset, 'ignore')
@@ -88,7 +88,7 @@ def url_fix(s, charset='utf-8'):
path = urllib.quote(path, '/%')
qs = urllib.quote_plus(qs, ':&=')
return urlparse.urlunsplit((scheme, netloc, path, qs, anchor))
-
+
def patch_http_response_read(func):
def inner(*args):
try:
@@ -98,20 +98,20 @@ def patch_http_response_read(func):
return inner
httplib.HTTPResponse.read = patch_http_response_read(httplib.HTTPResponse.read)
-
-
+
+
def searchforalbum(albumid=None, new=False, lossless=False):
-
+
if not albumid:
myDB = db.DBConnection()
-
+
results = myDB.select('SELECT AlbumID, Status from albums WHERE Status="Wanted" OR Status="Wanted Lossless"')
new = True
-
+
for result in results:
foundNZB = "none"
- if (headphones.NEWZNAB or headphones.NZBSORG or headphones.NZBX or headphones.NZBSRUS) and (headphones.SAB_HOST or headphones.BLACKHOLE_DIR or headphones.NZBGET_HOST):
+ if (headphones.HEADPHONES_INDEXER or headphones.NEWZNAB or headphones.NZBSORG or headphones.NZBSRUS) and (headphones.SAB_HOST or headphones.BLACKHOLE_DIR or headphones.NZBGET_HOST):
if result['Status'] == "Wanted Lossless":
foundNZB = searchNZB(result['AlbumID'], new, losslessOnly=True)
else:
@@ -123,11 +123,11 @@ def searchforalbum(albumid=None, new=False, lossless=False):
searchTorrent(result['AlbumID'], new, losslessOnly=True)
else:
searchTorrent(result['AlbumID'], new)
-
- else:
-
+
+ else:
+
foundNZB = "none"
- if (headphones.NZBMATRIX or headphones.NEWZNAB or headphones.NZBSORG or headphones.NEWZBIN or headphones.NZBX or headphones.NZBSRUS) and (headphones.SAB_HOST or headphones.BLACKHOLE_DIR or headphones.NZBGET_HOST):
+ if (headphones.HEADPHONES_INDEXER or headphones.NEWZNAB or headphones.NZBSORG or headphones.NZBSRUS) and (headphones.SAB_HOST or headphones.BLACKHOLE_DIR or headphones.NZBGET_HOST):
foundNZB = searchNZB(albumid, new, lossless)
if (headphones.KAT or headphones.ISOHUNT or headphones.MININOVA or headphones.WAFFLES or headphones.RUTRACKER or headphones.WHATCD) and foundNZB == "none":
@@ -136,32 +136,32 @@ def searchforalbum(albumid=None, new=False, lossless=False):
def searchNZB(albumid=None, new=False, losslessOnly=False):
myDB = db.DBConnection()
-
+
if albumid:
results = myDB.select('SELECT ArtistName, AlbumTitle, AlbumID, ReleaseDate, Type, SearchTerm from albums WHERE AlbumID=?', [albumid])
else:
results = myDB.select('SELECT ArtistName, AlbumTitle, AlbumID, ReleaseDate, Type, SearchTerm from albums WHERE Status="Wanted" OR Status="Wanted Lossless"')
new = True
-
+
for albums in results:
-
+
albumid = albums[2]
reldate = albums[3]
-
+
try:
year = reldate[:4]
except TypeError:
year = ''
-
+
dic = {'...':'', ' & ':' ', ' = ': ' ', '?':'', '$':'s', ' + ':' ', '"':'', ',':'', '*':'', '.':'', ':':''}
cleanalbum = helpers.latinToAscii(helpers.replace_all(albums[1], dic)).strip()
cleanartist = helpers.latinToAscii(helpers.replace_all(albums[0], dic)).strip()
-
+
# Use the provided search term if available, otherwise build a search term
if albums[5]:
term = albums[5]
-
+
else:
# FLAC usually doesn't have a year for some reason so I'll leave it out
# Various Artist albums might be listed as VA, so I'll leave that out too
@@ -172,24 +172,84 @@ def searchNZB(albumid=None, new=False, losslessOnly=False):
term = cleanalbum + ' ' + year
else:
term = cleanartist + ' ' + cleanalbum
-
+
# Replace bad characters in the term and unicode it
term = re.sub('[\.\-\/]', ' ', term).encode('utf-8')
-
+
artistterm = re.sub('[\.\-\/]', ' ', cleanartist).encode('utf-8')
-
+
logger.info("Searching for %s since it was marked as wanted" % term)
-
+
resultlist = []
+
+ if headphones.HEADPHONES_INDEXER:
+
+ provider = "headphones"
+ if headphones.PREFERRED_QUALITY == 3 or losslessOnly:
+ categories = "3040"
+ elif headphones.PREFERRED_QUALITY:
+ categories = "3040,3010"
+ else:
+ categories = "3010"
+
+ if albums['Type'] == 'Other':
+ categories = "3030"
+ logger.info("Album type is audiobook/spokenword. Using audiobook category")
+
+ params = { "t": "search",
+ "cat": categories,
+ "apikey": '89edf227c1de9b3de50383fff11466c6',
+ "maxage": headphones.USENET_RETENTION,
+ "q": term
+ }
+
+ searchURL = 'http://headphones.codeshy.com/newznab/api?' + urllib.urlencode(params)
+
+ # Add a user-agent
+ request = urllib2.Request(searchURL)
+ request.add_header('User-Agent', 'headphones/0.0 +https://github.com/rembo10/headphones')
+ base64string = base64.encodestring('%s:%s' % (headphones.HPUSER, headphones.HPPASS)).replace('\n', '')
+ request.add_header("Authorization", "Basic %s" % base64string)
+ opener = urllib2.build_opener()
+
+ logger.info(u'Parsing results from %s' % (searchURL, 'Headphones Index'))
+
+ try:
+ data = opener.open(request).read()
+ except Exception, e:
+ logger.warn('Error fetching data from %s: %s' % ('Headphones Index', e))
+ data = False
+
+ if data:
+
+ d = feedparser.parse(data)
+
+ if not len(d.entries):
+ logger.info(u"No results found from %s for %s" % ('Headphones Index', term))
+ pass
+
+ else:
+ for item in d.entries:
+ try:
+ url = item.link
+ title = item.title
+ size = int(item.links[1]['length'])
+
+ resultlist.append((title, size, url, provider))
+ logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size)))
+
+ except Exception, e:
+ logger.error(u"An unknown error occurred trying to parse the feed: %s" % e)
+
if headphones.NEWZNAB:
-
+
newznab_hosts = [(headphones.NEWZNAB_HOST, headphones.NEWZNAB_APIKEY, headphones.NEWZNAB_ENABLED)]
-
+
for newznab_host in headphones.EXTRA_NEWZNABS:
if newznab_host[2] == '1' or newznab_host[2] == 1:
newznab_hosts.append(newznab_host)
-
+
provider = "newznab"
if headphones.PREFERRED_QUALITY == 3 or losslessOnly:
categories = "3040"
@@ -197,13 +257,13 @@ def searchNZB(albumid=None, new=False, losslessOnly=False):
categories = "3040,3010"
else:
categories = "3010"
-
+
if albums['Type'] == 'Other':
categories = "3030"
logger.info("Album type is audiobook/spokenword. Using audiobook category")
-
+
for newznab_host in newznab_hosts:
-
+
# Add a little mod for kere.ws
if newznab_host[0] == "http://kere.ws":
if categories == "3040":
@@ -221,43 +281,43 @@ def searchNZB(albumid=None, new=False, losslessOnly=False):
"maxage": headphones.USENET_RETENTION,
"q": term
}
-
+
searchURL = newznab_host[0] + '/api?' + urllib.urlencode(params)
-
+
# Add a user-agent
request = urllib2.Request(searchURL)
request.add_header('User-Agent', 'headphones/0.0 +https://github.com/rembo10/headphones')
opener = urllib2.build_opener()
-
+
logger.info(u'Parsing results from %s' % (searchURL, newznab_host[0]))
-
+
try:
data = opener.open(request).read()
except Exception, e:
logger.warn('Error fetching data from %s: %s' % (newznab_host[0], e))
data = False
-
+
if data:
-
+
d = feedparser.parse(data)
-
+
if not len(d.entries):
logger.info(u"No results found from %s for %s" % (newznab_host[0], term))
pass
-
+
else:
for item in d.entries:
try:
url = item.link
title = item.title
size = int(item.links[1]['length'])
-
+
resultlist.append((title, size, url, provider))
- logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size)))
-
+ logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size)))
+
except Exception, e:
logger.error(u"An unknown error occurred trying to parse the feed: %s" % e)
-
+
if headphones.NZBSORG:
provider = "nzbsorg"
if headphones.PREFERRED_QUALITY == 3 or losslessOnly:
@@ -266,7 +326,7 @@ def searchNZB(albumid=None, new=False, losslessOnly=False):
categories = "3040,3010"
else:
categories = "3010"
-
+
if albums['Type'] == 'Other':
categories = "3030"
logger.info("Album type is audiobook/spokenword. Using audiobook category")
@@ -277,50 +337,50 @@ def searchNZB(albumid=None, new=False, losslessOnly=False):
"maxage": headphones.USENET_RETENTION,
"q": term
}
-
+
searchURL = 'http://beta.nzbs.org/api?' + urllib.urlencode(params)
-
+
logger.info(u'Parsing results from nzbs.org' % searchURL)
-
+
try:
data = urllib2.urlopen(searchURL, timeout=20).read()
except urllib2.URLError, e:
logger.warn('Error fetching data from nzbs.org: %s' % e)
data = False
-
+
if data:
-
+
d = feedparser.parse(data)
-
+
if not len(d.entries):
logger.info(u"No results found from nzbs.org for %s" % term)
pass
-
+
else:
for item in d.entries:
try:
url = item.link
title = item.title
size = int(item.links[1]['length'])
-
+
resultlist.append((title, size, url, provider))
logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size)))
-
+
except Exception, e:
logger.error(u"An unknown error occurred trying to parse the feed: %s" % e)
-
+
if headphones.NZBSRUS:
-
+
provider = "nzbsrus"
categories = "54"
-
+
if headphones.PREFERRED_QUALITY == 3 or losslessOnly:
sub = "16"
elif headphones.PREFERRED_QUALITY:
sub = ""
else:
sub = "15"
-
+
if albums['Type'] == 'Other':
sub = ""
logger.info("Album type is audiobook/spokenword. Searching all music categories")
@@ -332,89 +392,40 @@ def searchNZB(albumid=None, new=False, losslessOnly=False):
"age": headphones.USENET_RETENTION,
"searchtext": term
}
-
+
searchURL = 'https://www.nzbsrus.com/api.php?' + urllib.urlencode(params)
-
+
# Add a user-agent
request = urllib2.Request(searchURL)
request.add_header('User-Agent', 'headphones/0.0 +https://github.com/rembo10/headphones')
opener = urllib2.build_opener()
-
+
logger.info(u'Parsing results from NZBsRus' % searchURL)
-
+
try:
data = opener.open(request).read()
except Exception, e:
logger.warn('Error fetching data from NZBsRus: %s' % e)
data = False
-
+
if data:
-
+
d = json.loads(data)
-
+
if d['matches'] <= 0:
logger.info(u"No results found from NZBsRus for %s" % term)
pass
-
+
else:
for item in d['results']:
try:
url = "http://www.nzbsrus.com/nzbdownload_rss.php/" + item['id'] + "/" + headphones.NZBSRUS_UID + "/" + item['key']
title = item['name']
size = int(item['size'])
-
- resultlist.append((title, size, url, provider))
- logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size)))
-
- except Exception, e:
- logger.error(u"An unknown error occurred trying to parse the feed: %s" % e)
-
- if headphones.NZBX:
- provider = "nzbx"
- if headphones.PREFERRED_QUALITY == 3 or losslessOnly:
- categories = "3040"
- elif headphones.PREFERRED_QUALITY:
- categories = "3040,3010"
- else:
- categories = "3010"
-
- if albums['Type'] == 'Other':
- categories = "3030"
- logger.info("Album type is audiobook/spokenword. Using audiobook category")
- params = { "source" : "headphones",
- "cat": categories,
- "q": term
- }
-
- searchURL = 'https://nzbx.co/api/search?' + urllib.urlencode(params)
-
- logger.info(u'Parsing results from nzbx.co' % searchURL)
-
- try:
- data = urllib2.urlopen(searchURL, timeout=20).read()
- except urllib2.URLError, e:
- logger.warn('Error fetching data from nzbx.co: %s' % str(e))
- data = False
-
- if data:
-
- d = json.loads(data)
-
- if not len(d):
- logger.info(u"No results found from nzbx.co for %s" % term)
- pass
-
- else:
- for item in d:
- try:
- url = item['nzb']
- title = item['name']
- size = item['size']
-
resultlist.append((title, size, url, provider))
logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size)))
-
+
except Exception, e:
logger.error(u"An unknown error occurred trying to parse the feed: %s" % e)
@@ -427,9 +438,9 @@ def searchNZB(albumid=None, new=False, losslessOnly=False):
if len(resultlist):
resultlist[:] = [result for result in resultlist if verifyresult(result[0], artistterm, term, losslessOnly)]
-
- if len(resultlist):
-
+
+ if len(resultlist):
+
# Add a priority if it has any of the preferred words
temp_list = []
for result in resultlist:
@@ -437,9 +448,9 @@ def searchNZB(albumid=None, new=False, losslessOnly=False):
temp_list.append((result[0],result[1],result[2],result[3],1))
else:
temp_list.append((result[0],result[1],result[2],result[3],0))
-
+
resultlist = temp_list
-
+
if headphones.PREFERRED_QUALITY == 2 and headphones.PREFERRED_BITRATE:
logger.debug('Target bitrate: %s kbps' % headphones.PREFERRED_BITRATE)
@@ -450,16 +461,16 @@ def searchNZB(albumid=None, new=False, losslessOnly=False):
albumlength = sum([pair[0] for pair in tracks])
targetsize = albumlength/1000 * int(headphones.PREFERRED_BITRATE) * 128
-
+
if not targetsize:
logger.info('No track information for %s - %s. Defaulting to highest quality' % (albums[0], albums[1]))
nzblist = sorted(resultlist, key=lambda title: (-title[4] , -title[1]))
-
+
else:
logger.info('Target size: %s' % helpers.bytes_to_mb(targetsize))
newlist = []
flac_list = []
-
+
if headphones.PREFERRED_BITRATE_HIGH_BUFFER:
high_size_limit = targetsize * int(headphones.PREFERRED_BITRATE_HIGH_BUFFER)/100
else:
@@ -468,69 +479,69 @@ def searchNZB(albumid=None, new=False, losslessOnly=False):
low_size_limit = targetsize * int(headphones.PREFERRED_BITRATE_LOW_BUFFER)/100
else:
low_size_limit = None
-
+
for result in resultlist:
-
+
if high_size_limit and (result[1] > high_size_limit):
logger.info(result[0] + " is too large for this album - not considering it. (Size: " + helpers.bytes_to_mb(result[1]) + ", Maxsize: " + helpers.bytes_to_mb(high_size_limit))
-
+
# Add lossless nzbs to the "flac list" which we can use if there are no good lossy matches
if 'flac' in result[0].lower():
flac_list.append((result[0], result[1], result[2], result[3], result[4]))
-
+
continue
-
+
if low_size_limit and (result[1] < low_size_limit):
logger.info(result[0] + " is too small for this album - not considering it. (Size: " + helpers.bytes_to_mb(result[1]) + ", Minsize: " + helpers.bytes_to_mb(low_size_limit))
continue
-
+
delta = abs(targetsize - result[1])
newlist.append((result[0], result[1], result[2], result[3], result[4], delta))
-
+
nzblist = sorted(newlist, key=lambda title: (-title[4], title[5]))
-
+
if not len(nzblist) and len(flac_list) and headphones.PREFERRED_BITRATE_ALLOW_LOSSLESS:
logger.info("Since there were no appropriate lossy matches (and at least one lossless match), going to use lossless instead")
nzblist = sorted(flac_list, key=lambda title: (-title[4], -title[1]))
-
+
except Exception, e:
-
+
logger.debug('Error: %s' % str(e))
logger.info('No track information for %s - %s. Defaulting to highest quality' % (albums[0], albums[1]))
-
+
nzblist = sorted(resultlist, key=lambda title: (-title[4], -title[1]))
-
+
else:
-
+
nzblist = sorted(resultlist, key=lambda title: (-title[4], -title[1]))
-
-
+
+
if new:
-
+
while True:
-
+
if len(nzblist):
-
+
alreadydownloaded = myDB.select('SELECT * from snatched WHERE URL=?', [nzblist[0][2]])
-
+
if len(alreadydownloaded):
logger.info('%s has already been downloaded. Skipping.' % nzblist[0][0])
nzblist.pop(0)
-
+
else:
break
else:
logger.info('No more results found for %s' % term)
return "none"
-
+
if not len(nzblist):
logger.info('No appropriate matches found for %s' % term)
return "none"
logger.info(u"Pre-processing result")
-
+
(data, bestqual) = preprocess(nzblist)
-
+
if data and bestqual:
logger.info(u'Found best result: %s - %s' % (bestqual[2], bestqual[0], helpers.bytes_to_mb(bestqual[1])))
# Get rid of any dodgy chars here so we can prevent sab from renaming our downloads
@@ -548,7 +559,7 @@ def searchNZB(albumid=None, new=False, losslessOnly=False):
nzb.extraInfo.append(data)
nzb.name = nzb_folder_name
sab.sendNZB(nzb)
-
+
# If we sent the file to sab, we can check how it was renamed and insert that into the snatched table
(replace_spaces, replace_dots) = sab.checkConfig()
@@ -558,7 +569,7 @@ def searchNZB(albumid=None, new=False, losslessOnly=False):
nzb_folder_name = helpers.sab_replace_spaces(nzb_folder_name)
else:
-
+
nzb_name = nzb_folder_name + '.nzb'
download_path = os.path.join(headphones.BLACKHOLE_DIR, nzb_name)
try:
@@ -571,23 +582,23 @@ def searchNZB(albumid=None, new=False, losslessOnly=False):
except Exception, e:
logger.error('Couldn\'t write NZB file: %s' % e)
break
-
+
myDB.action('UPDATE albums SET status = "Snatched" WHERE AlbumID=?', [albums[2]])
myDB.action('INSERT INTO snatched VALUES( ?, ?, ?, ?, DATETIME("NOW", "localtime"), ?, ?, ?)', [albums[2], bestqual[0], bestqual[1], bestqual[2], "Snatched", nzb_folder_name, "nzb"])
return "found"
else:
return "none"
- else:
+ else:
return "none"
def verifyresult(title, artistterm, term, lossless):
-
+
title = re.sub('[\.\-\/\_]', ' ', title)
-
+
#if artistterm != 'Various Artists':
- #
+ #
# if not re.search('^' + re.escape(artistterm), title, re.IGNORECASE):
# #logger.info("Removed from results: " + title + " (artist not at string start).")
# #return False
@@ -599,29 +610,29 @@ def verifyresult(title, artistterm, term, lossless):
# return False
#another attempt to weed out substrings. We don't want "Vol III" when we were looking for "Vol II"
-
+
# Filter out remix search results (if we're not looking for it)
if 'remix' not in term.lower() and 'remix' in title.lower():
logger.info("Removed " + title + " from results because it's a remix album and we're not looking for a remix album right now")
return False
-
+
# Filter out FLAC if we're not specifically looking for it
if headphones.PREFERRED_QUALITY == (0 or '0') and 'flac' in title.lower() and not lossless:
logger.info("Removed " + title + " from results because it's a lossless album and we're not looking for a lossless album right now")
return False
-
+
if headphones.IGNORED_WORDS:
for each_word in helpers.split_string(headphones.IGNORED_WORDS):
if each_word.lower() in title.lower():
logger.info("Removed " + title + " from results because it contains ignored word: '" + each_word + "'")
return False
-
+
if headphones.REQUIRED_WORDS:
for each_word in helpers.split_string(headphones.REQUIRED_WORDS):
if each_word.lower() not in title.lower():
logger.info("Removed " + title + " from results because it doesn't contain required word: '" + each_word + "'")
return False
-
+
tokens = re.split('\W', term, re.IGNORECASE | re.UNICODE)
for token in tokens:
@@ -637,13 +648,13 @@ def verifyresult(title, artistterm, term, lossless):
if not not re.search('(?:\W|^)+' + dumbtoken + '(?:\W|$)+', title, re.IGNORECASE | re.UNICODE):
logger.info("Removed from results: " + title + " (missing tokens: " + token + " and " + cleantoken + ")")
return False
-
+
return True
def getresultNZB(result):
-
+
nzb = None
-
+
if result[3] == 'newzbin':
params = urllib.urlencode({"username": headphones.NEWZBIN_UID, "password": headphones.NEWZBIN_PASSWORD, "reportid": result[2]})
url = "https://www.newzbin2.es" + "/api/dnzb/"
@@ -658,28 +669,40 @@ def getresultNZB(result):
getresultNZB(result)
except AttributeError:
logger.warn("AttributeError in getresultNZB.")
- else:
+ elif result[3] == 'headphones':
request = urllib2.Request(result[2])
request.add_header('User-Agent', 'headphones/0.0 +https://github.com/rembo10/headphones')
+ base64string = base64.encodestring('%s:%s' % (headphones.HPUSER, headphones.HPPASS)).replace('\n', '')
+ request.add_header("Authorization", "Basic %s" % base64string)
+
opener = urllib2.build_opener()
+ try:
+ nzb = opener.open(request).read()
+ except Exception, e:
+ logger.warn('Error fetching nzb from url: ' + result[2] + ' %s' % e)
+ else:
+ request = urllib2.Request(result[2])
+ request.add_header('User-Agent', 'headphones/0.0 +https://github.com/rembo10/headphones')
+ opener = urllib2.build_opener()
+
try:
nzb = opener.open(request).read()
except Exception, e:
logger.warn('Error fetching nzb from url: ' + result[2] + ' %s' % e)
return nzb
-
+
def preprocess(resultlist):
if not headphones.USENET_RETENTION:
usenet_retention = 2000
else:
usenet_retention = int(headphones.USENET_RETENTION)
-
+
for result in resultlist:
nzb = getresultNZB(result)
if nzb:
- try:
+ try:
d = minidom.parseString(nzb)
node = d.documentElement
nzbfiles = d.getElementsByTagName("file")
@@ -707,42 +730,42 @@ def preprocess(resultlist):
def searchTorrent(albumid=None, new=False, losslessOnly=False):
myDB = db.DBConnection()
-
+
if albumid:
results = myDB.select('SELECT ArtistName, AlbumTitle, AlbumID, ReleaseDate, SearchTerm from albums WHERE AlbumID=?', [albumid])
else:
results = myDB.select('SELECT ArtistName, AlbumTitle, AlbumID, ReleaseDate, SearchTerm from albums WHERE Status="Wanted" OR Status="Wanted Lossless"')
new = True
-
+
# rutracker login
-
+
if headphones.RUTRACKER and results:
rulogin = rutracker.login(headphones.RUTRACKER_USER, headphones.RUTRACKER_PASSWORD)
if not rulogin:
logger.info(u'Could not login to rutracker, search results will exclude this provider')
-
+
for albums in results:
-
+
albumid = albums[2]
reldate = albums[3]
-
+
try:
year = reldate[:4]
except TypeError:
year = ''
-
+
dic = {'...':'', ' & ':' ', ' = ': ' ', '?':'', '$':'s', ' + ':' ', '"':'', ',':' ', '*':''}
semi_cleanalbum = helpers.replace_all(albums[1], dic)
cleanalbum = helpers.latinToAscii(semi_cleanalbum)
semi_cleanartist = helpers.replace_all(albums[0], dic)
cleanartist = helpers.latinToAscii(semi_cleanartist)
-
+
# Use provided term if available, otherwise build our own (this code needs to be cleaned up since a lot
# of these torrent providers are just using cleanartist/cleanalbum terms
if albums[4]:
term = albums[4]
-
+
else:
# FLAC usually doesn't have a year for some reason so I'll leave it out
# Various Artist albums might be listed as VA, so I'll leave that out too
@@ -768,7 +791,7 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
albumterm = re.sub('[\.\-\/]', ' ', cleanalbum).encode('utf-8', 'replace')
logger.info("Searching torrents for %s since it was marked as wanted" % term)
-
+
resultlist = []
pre_sorted_results = False
minimumseeders = int(headphones.NUMBEROFSEEDERS) - 1
@@ -787,32 +810,32 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
else:
categories = "7" #music
format = "8" #mp3
- maxsize = 300000000
+ maxsize = 300000000
- params = {
+ params = {
"categories[0]": "music",
"field": "seeders",
"sorder": "desc",
"rss": "1"
}
searchURL = providerurl + "/?%s" % urllib.urlencode(params)
-
+
try:
data = urllib2.urlopen(searchURL, timeout=20)
except urllib2.URLError, e:
logger.warn('Error fetching data from %s: %s' % (provider, e))
data = False
-
+
if data:
-
+
logger.info(u'Parsing results from KAT' % searchURL)
-
+
d = feedparser.parse(data)
if not len(d.entries):
logger.info(u"No results found from %s for %s" % (provider, term))
pass
-
+
else:
for item in d.entries:
try:
@@ -841,8 +864,8 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
resultlist.append((title, size, url, provider))
logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size)))
else:
- logger.info('%s is larger than the maxsize, the wrong format or has too little seeders for this category, skipping. (Size: %i bytes, Seeders: %i, Format: %s)' % (title, size, int(seeders), rightformat))
-
+ logger.info('%s is larger than the maxsize, the wrong format or has too little seeders for this category, skipping. (Size: %i bytes, Seeders: %i, Format: %s)' % (title, size, int(seeders), rightformat))
+
except Exception, e:
logger.error(u"An unknown error occurred in the KAT parser: %s" % e)
@@ -894,9 +917,9 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
data = False
if data:
-
+
logger.info(u'Parsing results from Waffles.fm' % searchURL)
-
+
d = feedparser.parse(data)
if not len(d.entries):
logger.info(u"No results found from %s for %s" % (provider, term))
@@ -918,21 +941,21 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size)))
except Exception, e:
logger.error(u"An error occurred while trying to parse the response from Waffles.fm: %s" % e)
-
+
# rutracker.org
-
+
if headphones.RUTRACKER and rulogin:
-
+
provider = "rutracker.org"
-
+
# Ignore if release date not specified, results too unpredictable
-
+
if not year and not usersearchterm:
logger.info(u'Release date not specified, ignoring for rutracker.org')
else:
-
+
bitrate = False
-
+
if headphones.PREFERRED_QUALITY == 3 or losslessOnly:
format = 'lossless'
maxsize = 10000000000
@@ -944,7 +967,7 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
maxsize = 300000000
if headphones.PREFERRED_QUALITY == 2 and headphones.PREFERRED_BITRATE:
bitrate = True
-
+
# build search url based on above
if not usersearchterm:
@@ -953,13 +976,13 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
searchURL = rutracker.searchurl(usersearchterm, ' ', ' ', format)
logger.info(u'Parsing results from rutracker.org' % searchURL)
-
+
# parse results and get best match
-
+
rulist = rutracker.search(searchURL, maxsize, minimumseeders, albumid, bitrate)
-
+
# add best match to overall results list
-
+
if rulist:
for ru in rulist:
title = ru[0].decode('utf-8')
@@ -1046,7 +1069,7 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
provider))
if headphones.ISOHUNT:
- provider = "isoHunt"
+ provider = "isoHunt"
providerurl = url_fix("http://isohunt.com/js/rss/" + term)
if headphones.PREFERRED_QUALITY == 3 or losslessOnly:
categories = "7" #music
@@ -1059,29 +1082,29 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
else:
categories = "7" #music
format = "8" #mp3
- maxsize = 300000000
+ maxsize = 300000000
- params = {
+ params = {
"iht": "2",
"sort": "seeds"
}
searchURL = providerurl + "?%s" % urllib.urlencode(params)
-
+
try:
data = urllib2.urlopen(searchURL, timeout=20).read()
except urllib2.URLError, e:
logger.warn('Error fetching data from %s: %s' % (provider, e))
data = False
-
+
if data:
-
+
logger.info(u'Parsing results from isoHunt' % searchURL)
-
+
d = feedparser.parse(data)
if not len(d.entries):
logger.info(u"No results found from %s for %s" % (provider, term))
pass
-
+
else:
for item in d.entries:
try:
@@ -1114,13 +1137,13 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
resultlist.append((title, size, url, provider))
logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size)))
else:
- logger.info('%s is larger than the maxsize, the wrong format or has too little seeders for this category, skipping. (Size: %i bytes, Seeders: %i, Format: %s)' % (title, size, int(seeds), rightformat))
-
+ logger.info('%s is larger than the maxsize, the wrong format or has too little seeders for this category, skipping. (Size: %i bytes, Seeders: %i, Format: %s)' % (title, size, int(seeds), rightformat))
+
except Exception, e:
logger.error(u"An unknown error occurred in the isoHunt parser: %s" % e)
if headphones.MININOVA:
- provider = "Mininova"
+ provider = "Mininova"
providerurl = url_fix("http://www.mininova.org/rss/" + term + "/5")
if headphones.PREFERRED_QUALITY == 3 or losslessOnly:
categories = "7" #music
@@ -1133,25 +1156,25 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
else:
categories = "7" #music
format = "8" #mp3
- maxsize = 300000000
+ maxsize = 300000000
+
+ searchURL = providerurl
- searchURL = providerurl
-
try:
data = urllib2.urlopen(searchURL, timeout=20).read()
except urllib2.URLError, e:
logger.warn('Error fetching data from %s: %s' % (provider, e))
data = False
-
+
if data:
-
+
logger.info(u'Parsing results from Mininova' % searchURL)
-
+
d = feedparser.parse(data)
if not len(d.entries):
logger.info(u"No results found from %s for %s" % (provider, term))
pass
-
+
else:
for item in d.entries:
try:
@@ -1183,8 +1206,8 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
resultlist.append((title, size, url, provider))
logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size)))
else:
- logger.info('%s is larger than the maxsize, the wrong format or has too little seeders for this category, skipping. (Size: %i bytes, Seeders: %i, Format: %s)' % (title, size, int(seeds), rightformat))
-
+ logger.info('%s is larger than the maxsize, the wrong format or has too little seeders for this category, skipping. (Size: %i bytes, Seeders: %i, Format: %s)' % (title, size, int(seeds), rightformat))
+
except Exception, e:
logger.error(u"An unknown error occurred in the Mininova Parser: %s" % e)
@@ -1195,9 +1218,9 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
#this should be less of an issue when it isn't a self-titled album so we'll only check vs artist
if len(resultlist):
resultlist[:] = [result for result in resultlist if verifyresult(result[0], artistterm, term, losslessOnly)]
-
- if len(resultlist):
-
+
+ if len(resultlist):
+
if headphones.PREFERRED_QUALITY == 2 and headphones.PREFERRED_BITRATE and not pre_sorted_results:
logger.debug('Target bitrate: %s kbps' % headphones.PREFERRED_BITRATE)
@@ -1209,43 +1232,43 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
targetsize = albumlength/1000 * int(headphones.PREFERRED_BITRATE) * 128
logger.info('Target size: %s' % helpers.bytes_to_mb(targetsize))
-
+
newlist = []
for result in resultlist:
delta = abs(targetsize - result[1])
newlist.append((result[0], result[1], result[2], result[3], delta))
-
+
torrentlist = sorted(newlist, key=lambda title: title[4])
-
+
except Exception, e:
-
+
logger.debug('Error: %s' % str(e))
logger.info('No track information for %s - %s. Defaulting to highest quality' % (albums[0], albums[1]))
-
+
torrentlist = sorted(resultlist, key=lambda title: title[1], reverse=True)
elif pre_sorted_results:
torrentlist = resultlist
-
+
else:
-
+
torrentlist = sorted(resultlist, key=lambda title: title[1], reverse=True)
-
-
+
+
if new:
-
+
while True:
-
+
if len(torrentlist):
-
+
alreadydownloaded = myDB.select('SELECT * from snatched WHERE URL=?', [torrentlist[0][2]])
-
+
if len(alreadydownloaded):
logger.info('%s has already been downloaded. Skipping.' % torrentlist[0][0])
torrentlist.pop(0)
-
+
else:
break
else:
@@ -1253,12 +1276,12 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
return
logger.info(u"Pre-processing result")
-
+
(data, bestqual) = preprocesstorrent(torrentlist, pre_sorted_results)
-
+
if data and bestqual:
logger.info(u'Found best result from %s: %s - %s' % (bestqual[3], bestqual[2], bestqual[0], helpers.bytes_to_mb(bestqual[1])))
- torrent_folder_name = '%s - %s [%s]' % (helpers.latinToAscii(albums[0]).encode('UTF-8').replace('/', '_'), helpers.latinToAscii(albums[1]).encode('UTF-8').replace('/', '_'), year)
+ torrent_folder_name = '%s - %s [%s]' % (helpers.latinToAscii(albums[0]).encode('UTF-8').replace('/', '_'), helpers.latinToAscii(albums[1]).encode('UTF-8').replace('/', '_'), year)
if headphones.TORRENTBLACKHOLE_DIR == "sendtracker":
torrent = classes.TorrentDataSearchResult()
@@ -1267,7 +1290,7 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
sab.sendTorrent(torrent)
elif headphones.TORRENTBLACKHOLE_DIR != "":
-
+
# Get torrent name from .torrent, this is usually used by the torrent client as the folder name
torrent_name = torrent_folder_name + '.torrent'
@@ -1277,14 +1300,14 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
download_path = rutracker.get_torrent(bestqual[2], headphones.TORRENTBLACKHOLE_DIR)
if not download_path:
break
- else:
+ else:
#Write the torrent file to a path derived from the TORRENTBLACKHOLE_DIR and file name.
prev = os.umask(headphones.UMASK)
torrent_file = open(download_path, 'wb')
torrent_file.write(data)
torrent_file.close()
os.umask(prev)
-
+
#Open the fresh torrent file again so we can extract the proper torrent name
#Used later in post-processing.
torrent_file = open(download_path, 'rb')
@@ -1295,7 +1318,7 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
except Exception, e:
logger.error('Couldn\'t get name from Torrent file: %s' % e)
break
-
+
myDB.action('UPDATE albums SET status = "Snatched" WHERE AlbumID=?', [albums[2]])
myDB.action('INSERT INTO snatched VALUES( ?, ?, ?, ?, DATETIME("NOW", "localtime"), ?, ?, ?)', [albums[2], bestqual[0], bestqual[1], bestqual[2], "Snatched", torrent_folder_name, "torrent"])
@@ -1306,12 +1329,12 @@ def preprocesstorrent(resultlist, pre_sorted_list=False):
selresult = result
elif int(selresult[1]) < int(result[1]): # if size is lower than new result replace previous selected result (bigger size = better quality?)
selresult = result
-
+
# get outta here if rutracker
-
+
if selresult[3] == 'rutracker.org':
return True, selresult
-
+
if pre_sorted_list:
selresult = resultlist[0]
else:
@@ -1324,10 +1347,10 @@ def preprocesstorrent(resultlist, pre_sorted_list=False):
try:
request = urllib2.Request(selresult[2])
request.add_header('Accept-encoding', 'gzip')
-
+
if selresult[3] == 'Kick Ass Torrent':
request.add_header('Referer', 'http://kat.ph/')
-
+
response = urllib2.urlopen(request)
if response.info().get('Content-Encoding') == 'gzip':
buf = StringIO(response.read())
@@ -1337,5 +1360,5 @@ def preprocesstorrent(resultlist, pre_sorted_list=False):
torrent = response.read()
except ExpatError:
logger.error('Unable to torrent %s' % selresult[2])
-
+
return torrent, selresult
diff --git a/headphones/webserve.py b/headphones/webserve.py
index 1f8167ab..31d3f26d 100644
--- a/headphones/webserve.py
+++ b/headphones/webserve.py
@@ -40,17 +40,17 @@ def serve_template(templatename, **kwargs):
interface_dir = os.path.join(str(headphones.PROG_DIR), 'data/interfaces/')
template_dir = os.path.join(str(interface_dir), headphones.INTERFACE)
-
+
_hplookup = TemplateLookup(directories=[template_dir])
-
+
try:
template = _hplookup.get_template(templatename)
return template.render(**kwargs)
except:
return exceptions.html_error_template().render()
-
+
class WebInterface(object):
-
+
def index(self):
raise cherrypy.HTTPRedirect("home")
index.exposed=True
@@ -65,11 +65,11 @@ class WebInterface(object):
myDB = db.DBConnection()
artist = myDB.action('SELECT * FROM artists WHERE ArtistID=?', [ArtistID]).fetchone()
albums = myDB.select('SELECT * from albums WHERE ArtistID=? order by ReleaseDate DESC', [ArtistID])
-
- # Don't redirect to the artist page until it has the bare minimum info inserted
- # Redirect to the home page if we still can't get it after 5 seconds
+
+ # Don't redirect to the artist page until it has the bare minimum info inserted
+ # Redirect to the home page if we still can't get it after 5 seconds
retry = 0
-
+
while retry < 5:
if not artist:
time.sleep(1)
@@ -77,19 +77,19 @@ class WebInterface(object):
retry += 1
else:
break
-
+
if not artist:
raise cherrypy.HTTPRedirect("home")
-
+
# Serve the extras up as a dict to make things easier for new templates
extras_list = ["single", "ep", "compilation", "soundtrack", "live", "remix", "spokenword", "audiobook"]
extras_dict = {}
-
+
if not artist['Extras']:
artist_extras = ""
else:
artist_extras = artist['Extras']
-
+
i = 1
for extra in extras_list:
if str(i) in artist_extras:
@@ -100,8 +100,8 @@ class WebInterface(object):
return serve_template(templatename="artist.html", title=artist['ArtistName'], artist=artist, albums=albums, extras=extras_dict)
artistPage.exposed = True
-
-
+
+
def albumPage(self, AlbumID):
myDB = db.DBConnection()
album = myDB.action('SELECT * from albums WHERE AlbumID=?', [AlbumID]).fetchone()
@@ -110,8 +110,8 @@ class WebInterface(object):
title = album['ArtistName'] + ' - ' + album['AlbumTitle']
return serve_template(templatename="album.html", title=title, album=album, tracks=tracks, description=description)
albumPage.exposed = True
-
-
+
+
def search(self, name, type):
if len(name) == 0:
raise cherrypy.HTTPRedirect("home")
@@ -127,7 +127,7 @@ class WebInterface(object):
threading.Thread(target=lastfm.getSimilar).start()
raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % artistid)
addArtist.exposed = True
-
+
def getExtras(self, ArtistID, newstyle=False, **kwargs):
# if calling this function without the newstyle, they're using the old format
# which doesn't separate extras, so we'll grab all of them
@@ -144,7 +144,7 @@ class WebInterface(object):
temp_extras_list.append(i)
i += 1
extras = ','.join(str(n) for n in temp_extras_list)
-
+
myDB = db.DBConnection()
controlValueDict = {'ArtistID': ArtistID}
newValueDict = {'IncludeExtras': 1,
@@ -153,7 +153,7 @@ class WebInterface(object):
threading.Thread(target=importer.addArtisttoDB, args=[ArtistID, True]).start()
raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID)
getExtras.exposed = True
-
+
def removeExtras(self, ArtistID):
myDB = db.DBConnection()
controlValueDict = {'ArtistID': ArtistID}
@@ -165,7 +165,7 @@ class WebInterface(object):
myDB.action('DELETE from albums WHERE ArtistID=? AND AlbumID=?', [ArtistID, album['AlbumID']])
raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID)
removeExtras.exposed = True
-
+
def pauseArtist(self, ArtistID):
logger.info(u"Pausing artist: " + ArtistID)
myDB = db.DBConnection()
@@ -174,7 +174,7 @@ class WebInterface(object):
myDB.upsert("artists", newValueDict, controlValueDict)
raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID)
pauseArtist.exposed = True
-
+
def resumeArtist(self, ArtistID):
logger.info(u"Resuming artist: " + ArtistID)
myDB = db.DBConnection()
@@ -183,7 +183,7 @@ class WebInterface(object):
myDB.upsert("artists", newValueDict, controlValueDict)
raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID)
resumeArtist.exposed = True
-
+
def deleteArtist(self, ArtistID):
logger.info(u"Deleting all traces of artist: " + ArtistID)
myDB = db.DBConnection()
@@ -195,7 +195,7 @@ class WebInterface(object):
myDB.action('INSERT OR REPLACE into blacklist VALUES (?)', [ArtistID])
raise cherrypy.HTTPRedirect("home")
deleteArtist.exposed = True
-
+
def deleteEmptyArtists(self):
logger.info(u"Deleting all empty artists")
myDB = db.DBConnection()
@@ -208,13 +208,13 @@ class WebInterface(object):
myDB.action('DELETE from allalbums WHERE ArtistID=?', [ArtistID])
myDB.action('DELETE from alltracks WHERE ArtistID=?', [ArtistID])
myDB.action('INSERT OR REPLACE into blacklist VALUES (?)', [ArtistID])
- deleteEmptyArtists.exposed = True
+ deleteEmptyArtists.exposed = True
def refreshArtist(self, ArtistID):
- threading.Thread(target=importer.addArtisttoDB, args=[ArtistID]).start()
+ threading.Thread(target=importer.addArtisttoDB, args=[ArtistID]).start()
raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID)
- refreshArtist.exposed=True
-
+ refreshArtist.exposed=True
+
def markAlbums(self, ArtistID=None, action=None, **args):
myDB = db.DBConnection()
if action == 'WantedNew' or action == 'WantedLossless':
@@ -237,12 +237,12 @@ class WebInterface(object):
else:
raise cherrypy.HTTPRedirect("upcoming")
markAlbums.exposed = True
-
+
def addArtists(self, **args):
threading.Thread(target=importer.artistlist_to_mbids, args=[args, True]).start()
raise cherrypy.HTTPRedirect("home")
addArtists.exposed = True
-
+
def queueAlbum(self, AlbumID, ArtistID=None, new=False, redirect=None, lossless=False):
logger.info(u"Marking album: " + AlbumID + " as wanted...")
myDB = db.DBConnection()
@@ -250,7 +250,7 @@ class WebInterface(object):
if lossless:
newValueDict = {'Status': 'Wanted Lossless'}
logger.info("...lossless only!")
- else:
+ else:
newValueDict = {'Status': 'Wanted'}
myDB.upsert("albums", newValueDict, controlValueDict)
searcher.searchforalbum(AlbumID, new)
@@ -268,7 +268,7 @@ class WebInterface(object):
myDB.upsert("albums", newValueDict, controlValueDict)
raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID)
unqueueAlbum.exposed = True
-
+
def deleteAlbum(self, AlbumID, ArtistID=None):
logger.info(u"Deleting all traces of album: " + AlbumID)
myDB = db.DBConnection()
@@ -279,7 +279,7 @@ class WebInterface(object):
else:
raise cherrypy.HTTPRedirect("home")
deleteAlbum.exposed = True
-
+
def switchAlbum(self, AlbumID, ReleaseID):
'''
Take the values from allalbums/alltracks (based on the ReleaseID) and swap it into the album & track tables
@@ -288,7 +288,7 @@ class WebInterface(object):
albumswitcher.switch(AlbumID, ReleaseID)
raise cherrypy.HTTPRedirect("albumPage?AlbumID=%s" % AlbumID)
switchAlbum.exposed = True
-
+
def editSearchTerm(self, AlbumID, SearchTerm):
logger.info(u"Updating search term for albumid: " + AlbumID)
myDB = db.DBConnection()
@@ -304,19 +304,19 @@ class WebInterface(object):
wanted = myDB.select("SELECT * from albums WHERE Status='Wanted'")
return serve_template(templatename="upcoming.html", title="Upcoming", upcoming=upcoming, wanted=wanted)
upcoming.exposed = True
-
+
def manage(self):
myDB = db.DBConnection()
emptyArtists = myDB.select("SELECT * FROM artists WHERE LatestAlbum IS NULL")
return serve_template(templatename="manage.html", title="Manage", emptyArtists=emptyArtists)
manage.exposed = True
-
+
def manageArtists(self):
myDB = db.DBConnection()
artists = myDB.select('SELECT * from artists order by ArtistSortName COLLATE NOCASE')
return serve_template(templatename="manageartists.html", title="Manage Artists", artists=artists)
manageArtists.exposed = True
-
+
def manageAlbums(self, Status=None):
myDB = db.DBConnection()
if Status == "Upcoming":
@@ -327,13 +327,13 @@ class WebInterface(object):
albums = myDB.select('SELECT * from albums')
return serve_template(templatename="managealbums.html", title="Manage Albums", albums=albums)
manageAlbums.exposed = True
-
+
def manageNew(self):
myDB = db.DBConnection()
newartists = myDB.select('SELECT * from newartists')
return serve_template(templatename="managenew.html", title="Manage New Artists", newartists=newartists)
- manageNew.exposed = True
-
+ manageNew.exposed = True
+
def markArtists(self, action=None, **args):
myDB = db.DBConnection()
artistsToAdd = []
@@ -350,7 +350,7 @@ class WebInterface(object):
elif action == 'resume':
controlValueDict = {'ArtistID': ArtistID}
newValueDict = {'Status': 'Active'}
- myDB.upsert("artists", newValueDict, controlValueDict)
+ myDB.upsert("artists", newValueDict, controlValueDict)
else:
artistsToAdd.append(ArtistID)
if len(artistsToAdd) > 0:
@@ -358,14 +358,14 @@ class WebInterface(object):
threading.Thread(target=importer.addArtistIDListToDB, args=[artistsToAdd]).start()
raise cherrypy.HTTPRedirect("home")
markArtists.exposed = True
-
+
def importLastFM(self, username):
headphones.LASTFM_USERNAME = username
headphones.config_write()
threading.Thread(target=lastfm.getArtists).start()
raise cherrypy.HTTPRedirect("home")
importLastFM.exposed = True
-
+
def importLastFMTag(self, tag, limit):
threading.Thread(target=lastfm.getTagTopArtists, args=(tag, limit)).start()
raise cherrypy.HTTPRedirect("home")
@@ -378,14 +378,14 @@ class WebInterface(object):
time.sleep(10)
raise cherrypy.HTTPRedirect("home")
importItunes.exposed = True
-
+
def musicScan(self, path, scan=0, redirect=None, autoadd=0, libraryscan=0):
headphones.LIBRARYSCAN = libraryscan
headphones.ADD_ARTISTS = autoadd
headphones.MUSIC_DIR = path
- headphones.config_write()
+ headphones.config_write()
if scan:
- try:
+ try:
threading.Thread(target=librarysync.libraryScan).start()
except Exception, e:
logger.error('Unable to complete the scan: %s' % e)
@@ -394,41 +394,41 @@ class WebInterface(object):
else:
raise cherrypy.HTTPRedirect("home")
musicScan.exposed = True
-
+
def forceUpdate(self):
from headphones import updater
threading.Thread(target=updater.dbUpdate).start()
raise cherrypy.HTTPRedirect("home")
forceUpdate.exposed = True
-
+
def forceSearch(self):
from headphones import searcher
threading.Thread(target=searcher.searchforalbum).start()
raise cherrypy.HTTPRedirect("home")
forceSearch.exposed = True
-
+
def forcePostProcess(self):
from headphones import postprocessor
threading.Thread(target=postprocessor.forcePostProcess).start()
raise cherrypy.HTTPRedirect("home")
forcePostProcess.exposed = True
-
+
def checkGithub(self):
from headphones import versioncheck
versioncheck.checkGithub()
raise cherrypy.HTTPRedirect("home")
checkGithub.exposed = True
-
+
def history(self):
myDB = db.DBConnection()
history = myDB.select('''SELECT * from snatched order by DateAdded DESC''')
return serve_template(templatename="history.html", title="History", history=history)
history.exposed = True
-
+
def logs(self):
return serve_template(templatename="logs.html", title="Log", lineList=headphones.LOG_LIST)
logs.exposed = True
-
+
def getLog(self,iDisplayStart=0,iDisplayLength=100,iSortCol_0=0,sSortDir_0="desc",sSearch="",**kwargs):
@@ -446,7 +446,7 @@ class WebInterface(object):
sortcolumn = 2
elif iSortCol_0 == '2':
sortcolumn = 1
- filtered.sort(key=lambda x:x[sortcolumn],reverse=sSortDir_0 == "desc")
+ filtered.sort(key=lambda x:x[sortcolumn],reverse=sSortDir_0 == "desc")
rows = filtered[iDisplayStart:(iDisplayStart+iDisplayLength)]
rows = [[row[0],row[2],row[1]] for row in rows]
@@ -463,10 +463,10 @@ class WebInterface(object):
iDisplayStart = int(iDisplayStart)
iDisplayLength = int(iDisplayLength)
filtered = []
- totalcount = 0
+ totalcount = 0
myDB = db.DBConnection()
-
-
+
+
sortcolumn = 'ArtistSortName'
sortbyhavepercent = False
if iSortCol_0 == '2':
@@ -477,9 +477,9 @@ class WebInterface(object):
sortbyhavepercent = True
if sSearch == "":
- query = 'SELECT * from artists order by %s COLLATE NOCASE %s' % (sortcolumn,sSortDir_0)
+ query = 'SELECT * from artists order by %s COLLATE NOCASE %s' % (sortcolumn,sSortDir_0)
filtered = myDB.select(query)
- totalcount = len(filtered)
+ totalcount = len(filtered)
else:
query = 'SELECT * from artists WHERE ArtistSortName LIKE "%' + sSearch + '%" OR LatestAlbum LIKE "%' + sSearch +'%"' + 'ORDER BY %s COLLATE NOCASE %s' % (sortcolumn,sSortDir_0)
filtered = myDB.select(query)
@@ -488,11 +488,11 @@ class WebInterface(object):
if sortbyhavepercent:
filtered.sort(key=lambda x:(float(x['HaveTracks'])/x['TotalTracks'] if x['TotalTracks'] > 0 else 0.0,x['HaveTracks'] if x['HaveTracks'] else 0.0),reverse=sSortDir_0 == "asc")
- #can't figure out how to change the datatables default sorting order when its using an ajax datasource so ill
+ #can't figure out how to change the datatables default sorting order when its using an ajax datasource so ill
#just reverse it here and the first click on the "Latest Album" header will sort by descending release date
if sortcolumn == 'ReleaseDate':
filtered.reverse()
-
+
artists = filtered[iDisplayStart:(iDisplayStart+iDisplayLength)]
rows = []
@@ -502,7 +502,7 @@ class WebInterface(object):
"Status":artist["Status"],
"TotalTracks":artist["TotalTracks"],
"HaveTracks":artist["HaveTracks"],
- "LatestAlbum":"",
+ "LatestAlbum":"",
"ReleaseDate":"",
"ReleaseInFuture":"False",
"AlbumID":"",
@@ -520,7 +520,7 @@ class WebInterface(object):
row['ReleaseDate'] = ''
row['LatestAlbum'] = artist['LatestAlbum']
row['AlbumID'] = artist['AlbumID']
-
+
rows.append(row)
@@ -543,23 +543,23 @@ class WebInterface(object):
myDB.action('DELETE from snatched WHERE Status=?', [type])
raise cherrypy.HTTPRedirect("history")
clearhistory.exposed = True
-
+
def generateAPI(self):
import hashlib, random
-
+
apikey = hashlib.sha224( str(random.getrandbits(256)) ).hexdigest()[0:32]
logger.info("New API generated")
return apikey
-
+
generateAPI.exposed = True
-
+
def config(self):
-
+
interface_dir = os.path.join(headphones.PROG_DIR, 'data/interfaces/')
interface_list = [ name for name in os.listdir(interface_dir) if os.path.isdir(os.path.join(interface_dir, name)) ]
- config = {
+ config = {
"http_host" : headphones.HTTP_HOST,
"http_user" : headphones.HTTP_USERNAME,
"http_port" : headphones.HTTP_PORT,
@@ -586,6 +586,7 @@ class WebInterface(object):
"use_blackhole" : checked(headphones.BLACKHOLE),
"blackhole_dir" : headphones.BLACKHOLE_DIR,
"usenet_retention" : headphones.USENET_RETENTION,
+ "use_headphones_indexer" : checked(headphones.HEADPHONES_INDEXER),
"use_newznab" : checked(headphones.NEWZNAB),
"newznab_host" : headphones.NEWZNAB_HOST,
"newznab_api" : headphones.NEWZNAB_APIKEY,
@@ -597,7 +598,6 @@ class WebInterface(object):
"use_nzbsrus" : checked(headphones.NZBSRUS),
"nzbsrus_uid" : headphones.NZBSRUS_UID,
"nzbsrus_apikey" : headphones.NZBSRUS_APIKEY,
- "use_nzbx" : checked(headphones.NZBX),
"preferred_words" : headphones.PREFERRED_WORDS,
"ignored_words" : headphones.IGNORED_WORDS,
"required_words" : headphones.REQUIRED_WORDS,
@@ -684,11 +684,11 @@ class WebInterface(object):
"hppass": headphones.HPPASS,
"cache_sizemb":headphones.CACHE_SIZEMB,
}
-
+
# Need to convert EXTRAS to a dictionary we can pass to the config: it'll come in as a string like 2,5,6,8
extras_list = ["single", "ep", "compilation", "soundtrack", "live", "remix", "spokenword", "audiobook"]
extras_dict = {}
-
+
i = 1
for extra in extras_list:
if str(i) in headphones.EXTRAS:
@@ -696,26 +696,27 @@ class WebInterface(object):
else:
extras_dict[extra] = ""
i+=1
-
+
config["extras"] = extras_dict
-
- return serve_template(templatename="config.html", title="Settings", config=config)
+
+ return serve_template(templatename="config.html", title="Settings", config=config)
config.exposed = True
- def configUpdate(self, http_host='0.0.0.0', http_username=None, http_port=8181, http_password=None, launch_browser=0, api_enabled=0, api_key=None,
- download_scan_interval=None, nzb_search_interval=None, libraryscan_interval=None, sab_host=None, sab_username=None, sab_apikey=None, sab_password=None,
- sab_category=None, nzbget_host=None, nzbget_username='nzbget', nzbget_password=None, nzbget_category=None, nzb_downloader=0, download_dir=None, blackhole=0, blackhole_dir=None, usenet_retention=None, newznab=0, newznab_host=None, newznab_apikey=None,
- newznab_enabled=0, nzbsorg=0, nzbsorg_uid=None, nzbsorg_hash=None, nzbsrus=0, nzbsrus_uid=None, nzbsrus_apikey=None, nzbx=0, preferred_words=None, required_words=None, ignored_words=None,
- preferred_quality=0, preferred_bitrate=None, detect_bitrate=0, move_files=0, torrentblackhole_dir=None, download_torrent_dir=None,
+ def configUpdate(self, http_host='0.0.0.0', http_username=None, http_port=8181, http_password=None, launch_browser=0, api_enabled=0, api_key=None,
+ download_scan_interval=None, nzb_search_interval=None, libraryscan_interval=None, sab_host=None, sab_username=None, sab_apikey=None, sab_password=None,
+ sab_category=None, nzbget_host=None, nzbget_username='nzbget', nzbget_password=None, nzbget_category=None, nzb_downloader=0, download_dir=None, blackhole=0, blackhole_dir=None, usenet_retention=None,
+ use_headphones_indexer=0,newznab=0, newznab_host=None, newznab_apikey=None,
+ newznab_enabled=0, nzbsorg=0, nzbsorg_uid=None, nzbsorg_hash=None, nzbsrus=0, nzbsrus_uid=None, nzbsrus_apikey=None, preferred_words=None, required_words=None, ignored_words=None,
+ preferred_quality=0, preferred_bitrate=None, detect_bitrate=0, move_files=0, torrentblackhole_dir=None, download_torrent_dir=None,
numberofseeders=10, use_isohunt=0, use_kat=0, use_mininova=0, waffles=0, waffles_uid=None, waffles_passkey=None, whatcd=0, whatcd_username=None, whatcd_password=None,
- rutracker=0, rutracker_user=None, rutracker_password=None, rename_files=0, correct_metadata=0, cleanup_files=0, add_album_art=0, album_art_format=None, embed_album_art=0, embed_lyrics=0,
+ rutracker=0, rutracker_user=None, rutracker_password=None, rename_files=0, correct_metadata=0, cleanup_files=0, add_album_art=0, album_art_format=None, embed_album_art=0, embed_lyrics=0,
destination_dir=None, lossless_destination_dir=None, folder_format=None, file_format=None, include_extras=0, single=0, ep=0, compilation=0, soundtrack=0, live=0,
remix=0, spokenword=0, audiobook=0, autowant_upcoming=False, autowant_all=False, keep_torrent_files=False, interface=None, log_dir=None, cache_dir=None, music_encoder=0, encoder=None, xldprofile=None,
- bitrate=None, samplingfrequency=None, encoderfolder=None, advancedencoder=None, encoderoutputformat=None, encodervbrcbr=None, encoderquality=None, encoderlossless=0,
- delete_lossless_files=0, prowl_enabled=0, prowl_onsnatch=0, prowl_keys=None, prowl_priority=0, xbmc_enabled=0, xbmc_host=None, xbmc_username=None, xbmc_password=None,
- xbmc_update=0, xbmc_notify=0, nma_enabled=False, nma_apikey=None, nma_priority=0, nma_onsnatch=0, synoindex_enabled=False,
- pushover_enabled=0, pushover_onsnatch=0, pushover_keys=None, pushover_priority=0, mirror=None, customhost=None, customport=None,
+ bitrate=None, samplingfrequency=None, encoderfolder=None, advancedencoder=None, encoderoutputformat=None, encodervbrcbr=None, encoderquality=None, encoderlossless=0,
+ delete_lossless_files=0, prowl_enabled=0, prowl_onsnatch=0, prowl_keys=None, prowl_priority=0, xbmc_enabled=0, xbmc_host=None, xbmc_username=None, xbmc_password=None,
+ xbmc_update=0, xbmc_notify=0, nma_enabled=False, nma_apikey=None, nma_priority=0, nma_onsnatch=0, synoindex_enabled=False,
+ pushover_enabled=0, pushover_onsnatch=0, pushover_keys=None, pushover_priority=0, mirror=None, customhost=None, customport=None,
customsleep=None, hpuser=None, hppass=None, preferred_bitrate_high_buffer=None, preferred_bitrate_low_buffer=None, preferred_bitrate_allow_lossless=0, cache_sizemb=None, **kwargs):
headphones.HTTP_HOST = http_host
@@ -730,7 +731,7 @@ class WebInterface(object):
headphones.LIBRARYSCAN_INTERVAL = libraryscan_interval
headphones.SAB_HOST = sab_host
headphones.SAB_USERNAME = sab_username
- headphones.SAB_PASSWORD = sab_password
+ headphones.SAB_PASSWORD = sab_password
headphones.SAB_APIKEY = sab_apikey
headphones.SAB_CATEGORY = sab_category
headphones.NZBGET_HOST = nzbget_host
@@ -742,6 +743,7 @@ class WebInterface(object):
headphones.BLACKHOLE = blackhole
headphones.BLACKHOLE_DIR = blackhole_dir
headphones.USENET_RETENTION = usenet_retention
+ headphones.HEADPHONES_INDEXER = use_headphones_indexer
headphones.NEWZNAB = newznab
headphones.NEWZNAB_HOST = newznab_host
headphones.NEWZNAB_APIKEY = newznab_apikey
@@ -752,7 +754,6 @@ class WebInterface(object):
headphones.NZBSRUS = nzbsrus
headphones.NZBSRUS_UID = nzbsrus_uid
headphones.NZBSRUS_APIKEY = nzbsrus_apikey
- headphones.NZBX = nzbx
headphones.PREFERRED_WORDS = preferred_words
headphones.IGNORED_WORDS = ignored_words
headphones.REQUIRED_WORDS = required_words
@@ -836,9 +837,9 @@ class WebInterface(object):
headphones.CACHE_SIZEMB = int(cache_sizemb)
# Handle the variable config options. Note - keys with False values aren't getting passed
-
+
headphones.EXTRA_NEWZNABS = []
-
+
for kwarg in kwargs:
if kwarg.startswith('newznab_host'):
newznab_number = kwarg[12:]
@@ -848,26 +849,26 @@ class WebInterface(object):
newznab_enabled = int(kwargs['newznab_enabled' + newznab_number])
except KeyError:
newznab_enabled = 0
-
+
headphones.EXTRA_NEWZNABS.append((newznab_host, newznab_api, newznab_enabled))
-
+
# Convert the extras to list then string. Coming in as 0 or 1
temp_extras_list = []
extras_list = [single, ep, compilation, soundtrack, live, remix, spokenword, audiobook]
-
+
i = 1
for extra in extras_list:
if extra:
temp_extras_list.append(i)
i+=1
-
- headphones.EXTRAS = ','.join(str(n) for n in temp_extras_list)
-
+
+ headphones.EXTRAS = ','.join(str(n) for n in temp_extras_list)
+
# Sanity checking
if headphones.SEARCH_INTERVAL < 360:
logger.info("Search interval too low. Resetting to 6 hour minimum")
headphones.SEARCH_INTERVAL = 360
-
+
# Write the config
headphones.config_write()
@@ -875,7 +876,7 @@ class WebInterface(object):
mb.startmb()
raise cherrypy.HTTPRedirect("config")
-
+
configUpdate.exposed = True
def shutdown(self):
@@ -891,14 +892,14 @@ class WebInterface(object):
message = 'Restarting...'
return serve_template(templatename="shutdown.html", title="Restarting", message=message, timer=30)
restart.exposed = True
-
+
def update(self):
headphones.SIGNAL = 'update'
message = 'Updating...'
return serve_template(templatename="shutdown.html", title="Updating", message=message, timer=120)
return page
update.exposed = True
-
+
def extras(self):
myDB = db.DBConnection()
cloudlist = myDB.select('SELECT * from lastfmcloud')
@@ -910,59 +911,59 @@ class WebInterface(object):
threading.Thread(target=importer.addReleaseById, args=[rid]).start()
raise cherrypy.HTTPRedirect("home")
addReleaseById.exposed = True
-
+
def updateCloud(self):
-
+
lastfm.getSimilar()
raise cherrypy.HTTPRedirect("extras")
-
+
updateCloud.exposed = True
-
+
def api(self, *args, **kwargs):
-
+
from headphones.api import Api
-
+
a = Api()
-
+
a.checkParams(*args, **kwargs)
-
+
data = a.fetchData()
-
+
return data
-
+
api.exposed = True
-
+
def getInfo(self, ArtistID=None, AlbumID=None):
-
+
from headphones import cache
info_dict = cache.getInfo(ArtistID, AlbumID)
-
+
return simplejson.dumps(info_dict)
-
+
getInfo.exposed = True
-
+
def getArtwork(self, ArtistID=None, AlbumID=None):
-
+
from headphones import cache
return cache.getArtwork(ArtistID, AlbumID)
-
+
getArtwork.exposed = True
-
+
def getThumb(self, ArtistID=None, AlbumID=None):
-
+
from headphones import cache
return cache.getThumb(ArtistID, AlbumID)
-
+
getThumb.exposed = True
-
+
# If you just want to get the last.fm image links for an album, make sure to pass a releaseid and not a releasegroupid
def getImageLinks(self, ArtistID=None, AlbumID=None):
-
+
from headphones import cache
image_dict = cache.getImageLinks(ArtistID, AlbumID)
-
+
return simplejson.dumps(image_dict)
-
+
getImageLinks.exposed = True
class Artwork(object):
@@ -978,7 +979,7 @@ class Artwork(object):
ArtistID = ID
elif ArtistOrAlbum == "album":
AlbumID = ID
-
+
relpath = cache.getArtwork(ArtistID,AlbumID)
if not relpath:
@@ -1011,7 +1012,7 @@ class Artwork(object):
ArtistID = ID
elif ArtistOrAlbum == "album":
AlbumID = ID
-
+
relpath = cache.getThumb(ArtistID,AlbumID)
if not relpath:
@@ -1031,8 +1032,8 @@ class Artwork(object):
f = open(path,'rb')
return f.read()
default.exposed = True
-
+
thumbs = Thumbs()
-
-
+
+
WebInterface.artwork = Artwork()