From 05f6d57ea342f4adb5074d7cf23b06f561f3aff5 Mon Sep 17 00:00:00 2001 From: Ruud Date: Tue, 12 Apr 2011 10:43:37 +0200 Subject: [PATCH] NZB/Torrent provider base --- couchpotato/core/downloaders/sabnzbd/__init__.py | 6 ++ couchpotato/core/notifications/base.py | 6 -- couchpotato/core/plugins/base.py | 11 +- couchpotato/core/plugins/renamer/__init__.py | 71 +++++++------ couchpotato/core/plugins/renamer/main.py | 10 ++ couchpotato/core/providers/base.py | 22 ++++ couchpotato/core/providers/imdb/main.py | 6 +- couchpotato/core/providers/newzbin/__init__.py | 29 ++++++ couchpotato/core/providers/newzbin/main.py | 8 ++ couchpotato/core/providers/newznab/__init__.py | 30 ++++++ couchpotato/core/providers/newznab/main.py | 8 ++ couchpotato/core/providers/nzbmatrix/__init__.py | 29 ++++++ couchpotato/core/providers/nzbmatrix/main.py | 116 +++++++++++++++++++++ couchpotato/core/providers/nzbs/__init__.py | 30 ++++++ couchpotato/core/providers/nzbs/main.py | 8 ++ couchpotato/core/providers/themoviedb/main.py | 5 +- .../core/providers/thepiratebay/__init__.py | 24 +++++ couchpotato/core/providers/thepiratebay/main.py | 8 ++ couchpotato/static/scripts/couchpotato.js | 1 + couchpotato/static/scripts/page.js | 13 +-- couchpotato/static/scripts/page/settings.js | 25 ++--- 21 files changed, 394 insertions(+), 72 deletions(-) create mode 100644 couchpotato/core/plugins/renamer/main.py create mode 100644 couchpotato/core/providers/newzbin/__init__.py create mode 100644 couchpotato/core/providers/newzbin/main.py create mode 100644 couchpotato/core/providers/newznab/__init__.py create mode 100644 couchpotato/core/providers/newznab/main.py create mode 100644 couchpotato/core/providers/nzbmatrix/__init__.py create mode 100644 couchpotato/core/providers/nzbmatrix/main.py create mode 100644 couchpotato/core/providers/nzbs/__init__.py create mode 100644 couchpotato/core/providers/nzbs/main.py create mode 100644 couchpotato/core/providers/thepiratebay/__init__.py create mode 100644 couchpotato/core/providers/thepiratebay/main.py diff --git a/couchpotato/core/downloaders/sabnzbd/__init__.py b/couchpotato/core/downloaders/sabnzbd/__init__.py index 470a2f4..4d49c4d 100644 --- a/couchpotato/core/downloaders/sabnzbd/__init__.py +++ b/couchpotato/core/downloaders/sabnzbd/__init__.py @@ -26,6 +26,12 @@ config = [{ 'label': 'Api Key', 'description': 'Used for all calls to Sabnzbd.', }, + { + 'advanced': True, + 'name': 'pp_directory', + 'type': 'directory', + 'description': 'Your Post-Processing Script directory, set in Sabnzbd > Config > Directories.', + }, ], } ], diff --git a/couchpotato/core/notifications/base.py b/couchpotato/core/notifications/base.py index d45c085..ea577ab 100644 --- a/couchpotato/core/notifications/base.py +++ b/couchpotato/core/notifications/base.py @@ -14,12 +14,6 @@ class Notification(Plugin): def notify(self, message = '', data = {}): pass - def isDisabled(self): - return not self.isEnabled() - - def isEnabled(self): - return self.conf('enabled', True) - def test(self): success = self.notify(message = self.test_message) diff --git a/couchpotato/core/plugins/base.py b/couchpotato/core/plugins/base.py index 8b4484a..3c2bfe6 100644 --- a/couchpotato/core/plugins/base.py +++ b/couchpotato/core/plugins/base.py @@ -7,7 +7,10 @@ import os.path class Plugin(): def conf(self, attr): - return Env.setting(attr, self.__class__.__name__.lower()) + return Env.setting(attr, self.getName().lower()) + + def getName(self): + return self.__class__.__name__ def registerStatic(self, file_path): @@ -25,3 +28,9 @@ class Plugin(): dir = os.path.join(plugin_dir, 'static') return send_from_directory(dir, file) + + def isDisabled(self): + return not self.isEnabled() + + def isEnabled(self): + return self.conf('enabled', True) diff --git a/couchpotato/core/plugins/renamer/__init__.py b/couchpotato/core/plugins/renamer/__init__.py index 49296d4..5d8aa22 100644 --- a/couchpotato/core/plugins/renamer/__init__.py +++ b/couchpotato/core/plugins/renamer/__init__.py @@ -1,33 +1,42 @@ +from couchpotato.core.plugins.renamer.main import Renamer + def start(): - pass + return Renamer() -#config = [{ -# 'name': 'Renamer', -# 'tab': 'renaming', -# 'options': { -# 'enabled': { -# 'default': False, -# 'type': 'bool', -# 'description': 'Enable renaming', -# }, -# 'from': { -# 'default': '', -# 'type': 'directory', -# 'label': 'From', -# 'description': 'Folder where the movies are downloaded to.', -# }, -# 'to': { -# 'default': '', -# 'type': 'directory', -# 'label': 'To', -# 'description': 'Folder where the movies will be moved to.', -# }, -# 'run_every': { -# 'default': 1, -# 'type': 'int', -# 'unit': 'min(s)', -# 'description': 'Search for new movies inside the folder every X minutes.', -# } -# } -#}] -config = [] +config = [{ + 'name': 'renamer', + 'groups': [ + { + 'tab': 'renamer', + 'name': 'tmdb', + 'label': 'TheMovieDB', + 'advanced': True, + 'description': 'Move and rename your downloaded movies to your movie directory.', + 'options': [ + { + 'name': 'enabled', + 'default': False, + 'type': 'enabler', + }, + { + 'name': 'from', + 'type': 'directory', + 'description': 'Folder where the movies are downloaded to.', + }, + { + 'name': 'to', + 'type': 'directory', + 'description': 'Folder where the movies will be moved to.', + }, + { + 'name': 'run_every', + 'label': 'Run every', + 'default': 1, + 'type': 'int', + 'unit': 'min(s)', + 'description': 'Search for new movies inside the folder every X minutes.', + } + ], + }, + ], +}] diff --git a/couchpotato/core/plugins/renamer/main.py b/couchpotato/core/plugins/renamer/main.py new file mode 100644 index 0000000..5576178 --- /dev/null +++ b/couchpotato/core/plugins/renamer/main.py @@ -0,0 +1,10 @@ +from couchpotato.core.logger import CPLog +from couchpotato.core.plugins.base import Plugin + +log = CPLog(__name__) + + +class Renamer(Plugin): + + def __init__(self): + pass diff --git a/couchpotato/core/providers/base.py b/couchpotato/core/providers/base.py index 23e15eb..7367bf2 100644 --- a/couchpotato/core/providers/base.py +++ b/couchpotato/core/providers/base.py @@ -8,3 +8,25 @@ class Provider(Plugin): type = None # movie, nzb, torrent, subtitle, trailer timeout = 10 # Default timeout for url requests + + +class MovieProvider(Provider): + type = 'movie' + + +class NZBProvider(Provider): + type = 'nzb' + + time_between_searches = 10 # Seconds + + +class TorrentProvider(Provider): + type = 'torrent' + + +class SubtitleProvider(Provider): + type = 'subtitle' + + +class TrailerProvider(Provider): + type = 'trailer' diff --git a/couchpotato/core/providers/imdb/main.py b/couchpotato/core/providers/imdb/main.py index 2d330c0..3eb7cac 100644 --- a/couchpotato/core/providers/imdb/main.py +++ b/couchpotato/core/providers/imdb/main.py @@ -1,14 +1,12 @@ from couchpotato.core.event import addEvent from couchpotato.core.logger import CPLog -from couchpotato.core.providers.base import Provider +from couchpotato.core.providers.base import MovieProvider from imdb import IMDb log = CPLog(__name__) -class IMDB(Provider): - - type = 'movie' +class IMDB(MovieProvider): def __init__(self): diff --git a/couchpotato/core/providers/newzbin/__init__.py b/couchpotato/core/providers/newzbin/__init__.py new file mode 100644 index 0000000..0b411bf --- /dev/null +++ b/couchpotato/core/providers/newzbin/__init__.py @@ -0,0 +1,29 @@ +from .main import Newzbin + +def start(): + return Newzbin() + +config = [{ + 'name': 'newzbin', + 'groups': [ + { + 'tab': 'providers', + 'name': 'newzbin', + 'options': [ + { + 'name': 'enabled', + 'type': 'enabler', + }, + { + 'name': 'username', + 'default': '', + }, + { + 'name': 'password', + 'default': '', + 'type': 'password', + }, + ], + }, + ], +}] diff --git a/couchpotato/core/providers/newzbin/main.py b/couchpotato/core/providers/newzbin/main.py new file mode 100644 index 0000000..3fe3a29 --- /dev/null +++ b/couchpotato/core/providers/newzbin/main.py @@ -0,0 +1,8 @@ +from couchpotato.core.logger import CPLog +from couchpotato.core.providers.base import NZBProvider + +log = CPLog(__name__) + + +class Newzbin(NZBProvider): + pass diff --git a/couchpotato/core/providers/newznab/__init__.py b/couchpotato/core/providers/newznab/__init__.py new file mode 100644 index 0000000..6587764 --- /dev/null +++ b/couchpotato/core/providers/newznab/__init__.py @@ -0,0 +1,30 @@ +from .main import Newznab + +def start(): + return Newznab() + +config = [{ + 'name': 'newznab', + 'groups': [ + { + 'tab': 'providers', + 'name': 'newznab', + 'options': [ + { + 'name': 'enabled', + 'type': 'enabler', + }, + { + 'name': 'host', + 'default': 'http://nzb.su', + }, + { + 'name': 'api_key', + 'default': '', + 'label': 'Api Key', + 'description': 'Can be found after login on the "API" page, bottom left. The string after "&apikey=".', + }, + ], + }, + ], +}] diff --git a/couchpotato/core/providers/newznab/main.py b/couchpotato/core/providers/newznab/main.py new file mode 100644 index 0000000..0e2f30d --- /dev/null +++ b/couchpotato/core/providers/newznab/main.py @@ -0,0 +1,8 @@ +from couchpotato.core.logger import CPLog +from couchpotato.core.providers.base import NZBProvider + +log = CPLog(__name__) + + +class Newznab(NZBProvider): + pass diff --git a/couchpotato/core/providers/nzbmatrix/__init__.py b/couchpotato/core/providers/nzbmatrix/__init__.py new file mode 100644 index 0000000..201cfcc --- /dev/null +++ b/couchpotato/core/providers/nzbmatrix/__init__.py @@ -0,0 +1,29 @@ +from .main import NZBMatrix + +def start(): + return NZBMatrix() + +config = [{ + 'name': 'nzbmatrix', + 'groups': [ + { + 'tab': 'providers', + 'name': 'nzbmatrix', + 'label': 'NZBMatrix', + 'options': [ + { + 'name': 'enabled', + 'type': 'enabler', + }, + { + 'name': 'username', + }, + { + 'name': 'api_key', + 'default': '9b939aee0aaafc12a65bf448e4af9543', + 'label': 'Api Key', + }, + ], + }, + ], +}] diff --git a/couchpotato/core/providers/nzbmatrix/main.py b/couchpotato/core/providers/nzbmatrix/main.py new file mode 100644 index 0000000..fe7d80a --- /dev/null +++ b/couchpotato/core/providers/nzbmatrix/main.py @@ -0,0 +1,116 @@ +from couchpotato.core.logger import CPLog +from couchpotato.core.providers.base import NZBProvider + +log = CPLog(__name__) + + +class NZBMatrix(NZBProvider): + + urls = { + 'download': 'https://api.nzbmatrix.com/v1.1/download.php?id=%s%s', + 'detail': 'https://nzbmatrix.com/nzb-details.php?id=%s&hit=1', + 'search': 'http://rss.nzbmatrix.com/rss.php', + } + + cat_ids = [ + ([42], ['720p', '1080p']), + ([2], ['cam', 'ts', 'dvdrip', 'tc', 'r5', 'scr']), + ([54], ['brrip']), + ([1], ['dvdr']), + ] + cat_backup_id = 2 + + def __init__(self, config): + log.info('Using NZBMatrix provider') + + self.config = config + + def find(self, movie, quality, type, retry = False): + + self.cleanCache(); + + results = [] + if not self.enabled() or not self.isAvailable(self.searchUrl): + return results + + catId = self.getCatId(type) + arguments = urlencode({ + 'term': movie.imdb, + 'subcat': catId, + 'username': self.conf('username'), + 'apikey': self.conf('apikey'), + 'searchin': 'weblink', + 'english': 1 if self.conf('english') else 0, + }) + url = "%s?%s" % (self.searchUrl, arguments) + cacheId = str(movie.imdb) + '-' + str(catId) + singleCat = (len(self.catIds.get(catId)) == 1 and catId != self.catBackupId) + + try: + cached = False + if(self.cache.get(cacheId)): + data = True + cached = True + log.info('Getting RSS from cache: %s.' % cacheId) + else: + log.info('Searching: %s' % url) + data = self.urlopen(url) + self.cache[cacheId] = { + 'time': time.time() + } + + except (IOError, URLError): + log.error('Failed to open %s.' % url) + return results + + if data: + try: + try: + if cached: + xml = self.cache[cacheId]['xml'] + else: + xml = self.getItems(data) + self.cache[cacheId]['xml'] = xml + except: + log.debug('No valid xml or to many requests.. You never know with %s.' % self.name) + return results + + for nzb in xml: + + title = self.gettextelement(nzb, "title") + if 'error' in title.lower(): continue + + id = int(self.gettextelement(nzb, "link").split('&')[0].partition('id=')[2]) + size = self.gettextelement(nzb, "description").split('
')[2].split('> ')[1] + date = str(self.gettextelement(nzb, "description").split('
')[3].partition('Added: ')[2]) + + new = self.feedItem() + new.id = id + new.type = 'nzb' + new.name = title + new.date = int(time.mktime(parse(date).timetuple())) + new.size = self.parseSize(size) + new.url = self.downloadLink(id) + new.detailUrl = self.detailLink(id) + new.content = self.gettextelement(nzb, "description") + new.score = self.calcScore(new, movie) + new.checkNZB = True + + if new.date > time.time() - (int(self.config.get('NZB', 'retention')) * 24 * 60 * 60): + if self.isCorrectMovie(new, movie, type, imdbResults = True, singleCategory = singleCat): + results.append(new) + log.info('Found: %s' % new.name) + else: + log.info('Found outside retention: %s' % new.name) + + return results + except SyntaxError: + log.error('Failed to parse XML response from NZBMatrix.com') + + return results + + def getApiExt(self): + return '&username=%s&apikey=%s' % (self.conf('username'), self.conf('apikey')) + + def isEnabled(self): + return self.conf('enabled') and self.conf('username') and self.conf('apikey') diff --git a/couchpotato/core/providers/nzbs/__init__.py b/couchpotato/core/providers/nzbs/__init__.py new file mode 100644 index 0000000..1fe5a64 --- /dev/null +++ b/couchpotato/core/providers/nzbs/__init__.py @@ -0,0 +1,30 @@ +from .main import Nzbs + +def start(): + return Nzbs() + +config = [{ + 'name': 'nzbs', + 'groups': [ + { + 'tab': 'providers', + 'name': 'nzbs', + 'options': [ + { + 'name': 'enabled', + 'type': 'enabler', + }, + { + 'name': 'id', + 'label': 'Id', + 'description': 'Can be found here, the number after "&i="', + }, + { + 'name': 'api_key', + 'label': 'Api Key', + 'description': 'Can be found here, the string after "&h="' + }, + ], + }, + ], +}] diff --git a/couchpotato/core/providers/nzbs/main.py b/couchpotato/core/providers/nzbs/main.py new file mode 100644 index 0000000..fb4742c --- /dev/null +++ b/couchpotato/core/providers/nzbs/main.py @@ -0,0 +1,8 @@ +from couchpotato.core.logger import CPLog +from couchpotato.core.providers.base import NZBProvider + +log = CPLog(__name__) + + +class Nzbs(NZBProvider): + pass diff --git a/couchpotato/core/providers/themoviedb/main.py b/couchpotato/core/providers/themoviedb/main.py index bc077cf..bd9e0ad 100644 --- a/couchpotato/core/providers/themoviedb/main.py +++ b/couchpotato/core/providers/themoviedb/main.py @@ -2,16 +2,15 @@ from __future__ import with_statement from couchpotato.core.event import addEvent from couchpotato.core.helpers.encoding import simplifyString, toUnicode from couchpotato.core.logger import CPLog -from couchpotato.core.providers.base import Provider +from couchpotato.core.providers.base import MovieProvider from libs.themoviedb import tmdb log = CPLog(__name__) -class TheMovieDb(Provider): +class TheMovieDb(MovieProvider): """Api for theMovieDb""" - type = 'movie' apiUrl = 'http://api.themoviedb.org/2.1' imageUrl = 'http://hwcdn.themoviedb.org' diff --git a/couchpotato/core/providers/thepiratebay/__init__.py b/couchpotato/core/providers/thepiratebay/__init__.py new file mode 100644 index 0000000..1e154ce --- /dev/null +++ b/couchpotato/core/providers/thepiratebay/__init__.py @@ -0,0 +1,24 @@ +from .main import ThePirateBay + +def start(): + return ThePirateBay() + +config = [{ + 'name': 'themoviedb', + 'groups': [ + { + 'tab': 'providers', + 'name': 'tmdb', + 'label': 'TheMovieDB', + 'advanced': True, + 'description': 'Used for all calls to TheMovieDB.', + 'options': [ + { + 'name': 'api_key', + 'default': '9b939aee0aaafc12a65bf448e4af9543', + 'label': 'Api Key', + }, + ], + }, + ], +}] diff --git a/couchpotato/core/providers/thepiratebay/main.py b/couchpotato/core/providers/thepiratebay/main.py new file mode 100644 index 0000000..2c2ce6f --- /dev/null +++ b/couchpotato/core/providers/thepiratebay/main.py @@ -0,0 +1,8 @@ +from couchpotato.core.logger import CPLog +from couchpotato.core.providers.base import TorrentProvider + +log = CPLog(__name__) + + +class ThePirateBay(TorrentProvider): + pass diff --git a/couchpotato/static/scripts/couchpotato.js b/couchpotato/static/scripts/couchpotato.js index b335b12..d299f77 100644 --- a/couchpotato/static/scripts/couchpotato.js +++ b/couchpotato/static/scripts/couchpotato.js @@ -92,6 +92,7 @@ var CouchPotato = new Class({ getPage: function(name){ return this.pages[name] } + }); window.App = new CouchPotato(); diff --git a/couchpotato/static/scripts/page.js b/couchpotato/static/scripts/page.js index dd4d4b0..f178da0 100644 --- a/couchpotato/static/scripts/page.js +++ b/couchpotato/static/scripts/page.js @@ -8,18 +8,17 @@ var PageBase = new Class({ has_tab: true, - initialize: function(parent, options) { + initialize: function(options) { var self = this; self.setOptions(options) - self.setParent(parent) // Create main page container self.el = new Element('div.page.'+self.name); // Create tab for page if(self.has_tab){ - var nav = self.getParent().getBlock('navigation'); + var nav = App.getBlock('navigation'); self.tab = nav.addTab({ 'href': '/'+self.name, 'title': self.title, @@ -49,14 +48,6 @@ var PageBase = new Class({ getName: function(){ return this.name }, - - setParent: function(parent){ - this.app = parent - }, - - getParent: function(){ - return this.app - }, show: function(){ this.el.addClass('active'); diff --git a/couchpotato/static/scripts/page/settings.js b/couchpotato/static/scripts/page/settings.js index b5c619f..0759133 100644 --- a/couchpotato/static/scripts/page/settings.js +++ b/couchpotato/static/scripts/page/settings.js @@ -6,18 +6,10 @@ Page.Settings = new Class({ title: 'Change settings.', tabs: { - 'general': { - 'label': 'General' - }, - 'providers': { - 'label': 'Providers' - }, - 'downloaders': { - 'label': 'Downloaders' - }, - 'notifications': { - 'label': 'Notifications' - } + 'general': {}, + 'providers': {}, + 'downloaders': {}, + 'notifications': {} }, open: function(action, params){ @@ -150,16 +142,17 @@ Page.Settings = new Class({ if(self.tabs[tab_name] && self.tabs[tab_name].tab) return self.tabs[tab_name].tab + var label = (tab.label || tab.name).capitalize() var tab_el = new Element('li').adopt( new Element('a', { 'href': '/'+self.name+'/'+tab_name+'/', - 'text': (tab.label || tab.name).capitalize() + 'text': label }) ).inject(self.tabs_container); if(!self.tabs[tab_name]) self.tabs[tab_name] = { - 'label': tab.label || tab.name + 'label': label } self.tabs[tab_name] = Object.merge(self.tabs[tab_name], { @@ -179,7 +172,7 @@ Page.Settings = new Class({ 'class': group.advanced ? 'inlineLabels advanced' : 'inlineLabels' }).adopt( new Element('h2', { - 'text': group.label || group.name.capitalize() + 'text': (group.label || group.name).capitalize() }).adopt( new Element('span.hint', { 'text': group.description @@ -236,7 +229,7 @@ var OptionBase = new Class({ createLabel: function(){ var self = this; return new Element('label', { - 'text': self.options.label || self.options.name.capitalize() + 'text': (self.options.label || self.options.name).capitalize() }) },