diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index 7478e5ed..e2d994fd 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -516,6 +516,59 @@
Torrents +
+
+ +
+
+
+
+ + + e.g. http://localhost:9117/torznab/iptorrents +
+
+ + +
+
+ +
+
+ <% + torznab_number = 2 + %> + %for torznab in config['extra_torznabs']: + <% + if torznab[2] == '1' or torznab[2] == 1: + torznab_enabled = "checked" + else: + torznab_enabled = "" + %> +
+
+ + +
+
+ + +
+
+ +
+
+ +
+
+ <% + torznab_number += 1 + %> + %endfor + +
+
+
@@ -2152,11 +2205,18 @@ var deletedNewznabs = 0; - $(".remove").click(function() { + $(".removeNewznab").click(function() { $(this).parent().parent().remove(); deletedNewznabs = deletedNewznabs + 1; }); + var deletedTorznabs = 0; + + $(".removeTorznab").click(function() { + $(this).parent().parent().remove(); + deletedTorznabs = deletedTorznabs + 1; + }); + $("#modify_extras").click(openExtrasDialog); $("#include_extras").click(function(){ @@ -2179,7 +2239,7 @@ $("#add_newznab").click(function() { var intId = $("#newznab_providers > div").size() + deletedNewznabs + 1; var formfields = $("
"); - var removeButton = $("
"); + var removeButton = $("
"); removeButton.click(function() { $(this).parent().remove(); deletedNewznabs = deletedNewznabs + 1; @@ -2190,6 +2250,20 @@ $("#add_newznab").before(formfields); }); + $("#add_torznab").click(function() { + var intId = $("#torznab_providers > div").size() + deletedTorznabs + 1; + var formfields = $("
"); + var removeButton = $("
"); + removeButton.click(function() { + $(this).parent().remove(); + deletedTorznabs = deletedTorznabs + 1; + + }); + formfields.append(removeButton); + formfields.append("
"); + $("#add_torznab").before(formfields); + }); + $(".hpuser").keyup(function() { $(".hpuser").val($(this).val()); }); @@ -2206,6 +2280,7 @@ initConfigCheckbox("#use_newznab"); initConfigCheckbox("#use_nzbsorg"); initConfigCheckbox("#use_omgwtfnzbs"); + initConfigCheckbox("#use_torznab"); initConfigCheckbox("#use_kat"); initConfigCheckbox("#use_piratebay"); initConfigCheckbox("#use_oldpiratebay"); diff --git a/headphones/config.py b/headphones/config.py index 97ad7c62..67b7af72 100644 --- a/headphones/config.py +++ b/headphones/config.py @@ -81,6 +81,7 @@ _CONFIG_DEFINITIONS = { 'ENCODER_PATH': (str, 'General', ''), 'EXTRAS': (str, 'General', ''), 'EXTRA_NEWZNABS': (list, 'Newznab', ''), + 'EXTRA_TORZNABS': (list, 'Torznab', ''), 'FILE_FORMAT': (str, 'General', 'Track Artist - Album [Year] - Title'), 'FILE_PERMISSIONS': (str, 'General', '0644'), 'FILE_UNDERSCORES': (int, 'General', 0), @@ -140,6 +141,10 @@ _CONFIG_DEFINITIONS = { 'NEWZNAB_APIKEY': (str, 'Newznab', ''), 'NEWZNAB_ENABLED': (int, 'Newznab', 1), 'NEWZNAB_HOST': (str, 'Newznab', ''), + 'TORZNAB': (int, 'Torznab', 0), + 'TORZNAB_APIKEY': (str, 'Torznab', ''), + 'TORZNAB_ENABLED': (int, 'Torznab', 1), + 'TORZNAB_HOST': (str, 'Torznab', ''), 'NMA_APIKEY': (str, 'NMA', ''), 'NMA_ENABLED': (int, 'NMA', 0), 'NMA_ONSNATCH': (int, 'NMA', 0), @@ -351,6 +356,25 @@ class Config(object): extra_newznabs.append(item) self.EXTRA_NEWZNABS = extra_newznabs + def get_extra_torznabs(self): + """ Return the extra torznab tuples """ + extra_torznabs = list( + itertools.izip(*[itertools.islice(self.EXTRA_TORZNABS, i, None, 3) + for i in range(3)]) + ) + return extra_torznabs + + def clear_extra_torznabs(self): + """ Forget about the configured extra torznabs """ + self.EXTRA_TORZNABS = [] + + def add_extra_torznab(self, torznab): + """ Add a new extra torznab """ + extra_torznabs = self.EXTRA_TORZNABS + for item in torznab: + extra_torznabs.append(item) + self.EXTRA_TORZNABS = extra_torznabs + def __getattr__(self, name): """ Returns something from the ini unless it is a real property diff --git a/headphones/searcher.py b/headphones/searcher.py index a498d619..dbfab875 100644 --- a/headphones/searcher.py +++ b/headphones/searcher.py @@ -228,7 +228,7 @@ def do_sorted_search(album, new, losslessOnly, choose_specific_download=False): NZB_PROVIDERS = (headphones.CONFIG.HEADPHONES_INDEXER or headphones.CONFIG.NEWZNAB or headphones.CONFIG.NZBSORG or headphones.CONFIG.OMGWTFNZBS) NZB_DOWNLOADERS = (headphones.CONFIG.SAB_HOST or headphones.CONFIG.BLACKHOLE_DIR or headphones.CONFIG.NZBGET_HOST) - TORRENT_PROVIDERS = (headphones.CONFIG.KAT or headphones.CONFIG.PIRATEBAY or headphones.CONFIG.OLDPIRATEBAY or headphones.CONFIG.MININOVA or headphones.CONFIG.WAFFLES or headphones.CONFIG.RUTRACKER or headphones.CONFIG.WHATCD) + TORRENT_PROVIDERS = (headphones.CONFIG.TORZNAB or headphones.CONFIG.KAT or headphones.CONFIG.PIRATEBAY or headphones.CONFIG.OLDPIRATEBAY or headphones.CONFIG.MININOVA or headphones.CONFIG.WAFFLES or headphones.CONFIG.RUTRACKER or headphones.CONFIG.WHATCD) results = [] myDB = db.DBConnection() @@ -1086,6 +1086,68 @@ def searchTorrent(album, new=False, losslessOnly=False, albumlength=None, choose return proxy_url + if headphones.CONFIG.TORZNAB: + provider = "torznab" + torznab_hosts = [] + + if headphones.CONFIG.TORZNAB_HOST and headphones.CONFIG.TORZNAB_ENABLED: + torznab_hosts.append((headphones.CONFIG.TORZNAB_HOST, headphones.CONFIG.TORZNAB_APIKEY, headphones.CONFIG.TORZNAB_ENABLED)) + + for torznab_host in headphones.CONFIG.get_extra_torznabs(): + if torznab_host[2] == '1' or torznab_host[2] == 1: + torznab_hosts.append(torznab_host) + + if headphones.CONFIG.PREFERRED_QUALITY == 3 or losslessOnly: + categories = "3040" + elif headphones.CONFIG.PREFERRED_QUALITY == 1 or allow_lossless: + categories = "3040,3010" + else: + categories = "3010" + + if album['Type'] == 'Other': + categories = "3030" + logger.info("Album type is audiobook/spokenword. Using audiobook category") + + for torznab_host in torznab_hosts: + + provider = torznab_host[0] + + # Request results + logger.info('Parsing results from %s using search term: %s' % (torznab_host[0],term)) + + headers = {'User-Agent': USER_AGENT} + params = { + "t": "search", + "apikey": torznab_host[1], + "cat": categories, + "maxage": headphones.CONFIG.USENET_RETENTION, + "q": term + } + + data = request.request_feed( + url=torznab_host[0] + '/api?', + params=params, headers=headers + ) + + # Process feed + if data: + if not len(data.entries): + logger.info(u"No results found from %s for %s", torznab_host[0], term) + else: + for item in data.entries: + try: + url = item.link + title = item.title + size = int(item.links[1]['length']) + if all(word.lower() in title.lower() for word in term.split()): + logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size))) + resultlist.append((title, size, url, provider, 'torrent', True)) + else: + logger.info('Skipping %s, not all search term words found' % title) + + except Exception as e: + logger.exception("An unknown error occurred trying to parse the feed: %s" % e) + if headphones.CONFIG.KAT: provider = "Kick Ass Torrents" ka_term = term.replace("!", "") diff --git a/headphones/webserve.py b/headphones/webserve.py index bc6d4aa7..59418d28 100644 --- a/headphones/webserve.py +++ b/headphones/webserve.py @@ -1065,6 +1065,11 @@ class WebInterface(object): "newznab_apikey": headphones.CONFIG.NEWZNAB_APIKEY, "newznab_enabled": checked(headphones.CONFIG.NEWZNAB_ENABLED), "extra_newznabs": headphones.CONFIG.get_extra_newznabs(), + "use_torznab": checked(headphones.CONFIG.TORZNAB), + "torznab_host": headphones.CONFIG.TORZNAB_HOST, + "torznab_apikey": headphones.CONFIG.TORZNAB_APIKEY, + "torznab_enabled": checked(headphones.CONFIG.TORZNAB_ENABLED), + "extra_torznabs": headphones.CONFIG.get_extra_torznabs(), "use_nzbsorg": checked(headphones.CONFIG.NZBSORG), "nzbsorg_uid": headphones.CONFIG.NZBSORG_UID, "nzbsorg_hash": headphones.CONFIG.NZBSORG_HASH, @@ -1278,7 +1283,7 @@ class WebInterface(object): # Handle the variable config options. Note - keys with False values aren't getting passed checked_configs = [ - "launch_browser", "enable_https", "api_enabled", "use_blackhole", "headphones_indexer", "use_newznab", "newznab_enabled", + "launch_browser", "enable_https", "api_enabled", "use_blackhole", "headphones_indexer", "use_newznab", "newznab_enabled", "use_torznab", "torznab_enabled", "use_nzbsorg", "use_omgwtfnzbs", "use_kat", "use_piratebay", "use_oldpiratebay", "use_mininova", "use_waffles", "use_rutracker", "use_whatcd", "preferred_bitrate_allow_lossless", "detect_bitrate", "ignore_clean_releases", "freeze_db", "cue_split", "move_files", "rename_files", "correct_metadata", "cleanup_files", "keep_nfo", "add_album_art", "embed_album_art", "embed_lyrics", @@ -1316,6 +1321,21 @@ class WebInterface(object): del kwargs[key] extra_newznabs.append((newznab_host, newznab_api, newznab_enabled)) + extra_torznabs = [] + for kwarg in [x for x in kwargs if x.startswith('torznab_host')]: + torznab_host_key = kwarg + torznab_number = kwarg[12:] + if len(torznab_number): + torznab_api_key = 'torznab_api' + torznab_number + torznab_enabled_key = 'torznab_enabled' + torznab_number + torznab_host = kwargs.get(torznab_host_key, '') + torznab_api = kwargs.get(torznab_api_key, '') + torznab_enabled = int(kwargs.get(torznab_enabled_key, 0)) + for key in [torznab_host_key, torznab_api_key, torznab_enabled_key]: + if key in kwargs: + del kwargs[key] + extra_torznabs.append((torznab_host, torznab_api, torznab_enabled)) + # Convert the extras to list then string. Coming in as 0 or 1 (append new extras to the end) temp_extras_list = [] @@ -1346,6 +1366,11 @@ class WebInterface(object): for extra_newznab in extra_newznabs: headphones.CONFIG.add_extra_newznab(extra_newznab) + headphones.CONFIG.clear_extra_torznabs() + headphones.CONFIG.process_kwargs(kwargs) + for extra_torznab in extra_torznabs: + headphones.CONFIG.add_extra_torznab(extra_torznab) + # Sanity checking if headphones.CONFIG.SEARCH_INTERVAL and headphones.CONFIG.SEARCH_INTERVAL < 360: logger.info("Search interval too low. Resetting to 6 hour minimum")