2
from flexget.entry import Entry
3
from flexget.plugin import internet, register_plugin
4
from flexget.utils.tools import urlopener
8
socket.setdefaulttimeout(timeout)
10
log = logging.getLogger('nzbmatrix')
13
class NzbMatrix(object):
14
"""NZBMatrix search plugin."""
17
from flexget import validator
18
nzbmatrix = validator.factory('dict')
19
nzbmatrix.accept('integer', key='catid')
20
nzbmatrix.accept('integer', key='num')
21
nzbmatrix.accept('integer', key='age')
22
nzbmatrix.accept('choice', key='region').accept_choices(
23
['1', '2', '3', 'PAL', 'NTSC', 'FREE'], ignore_case=True)
24
nzbmatrix.accept('text', key='group')
25
nzbmatrix.accept('text', key='username', required=True)
26
nzbmatrix.accept('text', key='apikey', required=True)
27
nzbmatrix.accept('integer', key='larger')
28
nzbmatrix.accept('integer', key='smaller')
29
nzbmatrix.accept('integer', key='minhits')
30
nzbmatrix.accept('integer', key='maxhits')
31
nzbmatrix.accept('integer', key='maxage')
32
nzbmatrix.accept('boolean', key='englishonly')
33
# TODO: I should overwrite this below. If there's an IMDB ID, I should
34
# search on it via weblink
35
nzbmatrix.accept('choice', key='searchin').accept_choices(
36
['name', 'subject', 'weblink'], ignore_case=True)
40
def search(self, query, comparator, config=None):
41
# TODO: Implement comparator matching
43
params = self.getparams(config)
44
params['search'] = self.clean(query)
45
search_url = 'https://api.nzbmatrix.com/v1.1/search.php?' + urllib.urlencode(params)
46
results = self.nzbid_from_search(search_url, params['search'], query)
51
for result in results:
53
entry['title'] = result['NZBNAME']
54
download_params = {"username": params['username'], 'apikey': params['apikey'], 'id': result['NZBID']}
55
entry['url'] = "http://api.nzbmatrix.com/v1.1/download.php?" + urllib.urlencode(download_params)
59
def getparams(self, config):
60
# keeping vars separate, for code readability. Config entries are
61
# identical to params passed.
63
if 'searchin' in params:
64
params['searchin'] = params['searchin'].lower()
65
if 'region' in params:
66
if params['region'].lower() == 'pal':
68
if params['region'].lower() == 'ntsc':
70
if params['region'].lower() == 'free':
72
if 'englishonly' in params:
73
if params['englishonly']:
74
params['englishonly'] = 1
76
del params['englishonly']
80
"""clean the title name for search"""
82
return s.replace('.', ' ').replace('_', ' ').replace(',', '')\
83
.replace('-', ' ').strip().lower()
86
def nzbid_from_search(self, url, name, query):
87
"""Parses nzb download url from api results"""
91
log.debug("Sleeping to respect nzbmatrix rules about hammering the API")
93
apireturn = self.parse_nzb_matrix_api(urlopener(url, log).read(),
99
for result in apireturn:
100
names.append(result["NZBNAME"])
101
matches = difflib.get_close_matches(name, names, 1, 0.3)
102
if len(matches) == 0:
105
for result in apireturn:
106
if result["NZBNAME"] == matches[0]:
108
for match in matches: # Already sorted
109
for result in apireturn:
110
if result.get(match, False):
111
matched_results.append(result)
112
return matched_results
114
def parse_nzb_matrix_api(self, apireturn, title):
116
apireturn = str(apireturn)
117
if (apireturn == "error:nothing_found" or
118
apireturn == "error:no_nzb_found"):
119
log.debug("Nothing found from nzbmatrix for search on %s" % title)
121
elif apireturn[:6] == 'error:':
122
log.error("Error recieved from nzbmatrix API: %s" % apireturn[6:])
126
apire = re.compile(r"([A-Z_]+):(.+);$")
127
for line in apireturn.splitlines():
128
match = apire.match(line)
129
if not match and line == "|" and api_result != {}:
130
#not an empty api result
131
results.append(api_result)
134
api_result[match.group(1)] = match.group(2)
136
log.debug("Recieved non-matching line in nzbmatrix API search: "
139
results.append(api_result)
142
register_plugin(NzbMatrix, 'nzbmatrix', groups=['search'])