From 64c044acc1789ce1a7f48f0c84a5f9e294e6eba2 Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Sun, 15 Jul 2012 19:09:48 +0200 Subject: [PATCH 01/42] Shutdown the logger before restarting CPS. Fixes issue #587 --- CouchPotato.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CouchPotato.py b/CouchPotato.py index 21b208a2..b1620c0c 100755 --- a/CouchPotato.py +++ b/CouchPotato.py @@ -9,7 +9,7 @@ import socket import subprocess import sys import traceback - +import time # Root path base_path = dirname(os.path.abspath(__file__)) @@ -96,6 +96,10 @@ class Loader(object): except: self.log.critical(traceback.format_exc()) + # Release log files and shutdown logger + logging.shutdown() + time.sleep(3) + args = [sys.executable] + [os.path.join(base_path, __file__)] + sys.argv[1:] subprocess.Popen(args) except: From 108044948fef08006d43acdabd1fb2d3e0a816a3 Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Tue, 24 Jul 2012 22:49:49 +0200 Subject: [PATCH 02/42] Fix XBMC and NMA Notification tests --- .../core/notifications/notifymyandroid/main.py | 5 ++++- couchpotato/core/notifications/xbmc/main.py | 12 ++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/couchpotato/core/notifications/notifymyandroid/main.py b/couchpotato/core/notifications/notifymyandroid/main.py index a6a316da..52e21413 100644 --- a/couchpotato/core/notifications/notifymyandroid/main.py +++ b/couchpotato/core/notifications/notifymyandroid/main.py @@ -21,8 +21,11 @@ class NotifyMyAndroid(Notification): response = nma.push(self.default_title, self.event , message, self.conf('priority'), batch_mode = len(keys) > 1) + successful = 0 for key in keys: if not response[str(key)]['code'] == u'200': log.error('Could not send notification to NotifyMyAndroid (%s). %s', (key, response[key]['message'])) + else: + successful += 1 - return response + return successful == len(keys) \ No newline at end of file diff --git a/couchpotato/core/notifications/xbmc/main.py b/couchpotato/core/notifications/xbmc/main.py index 4f6e62a7..5f16bdb8 100644 --- a/couchpotato/core/notifications/xbmc/main.py +++ b/couchpotato/core/notifications/xbmc/main.py @@ -13,11 +13,15 @@ class XBMC(Notification): def notify(self, message = '', data = {}, listener = None): if self.isDisabled(): return - for host in [x.strip() for x in self.conf('host').split(",")]: - self.send({'command': 'ExecBuiltIn', 'parameter': 'Notification(CouchPotato, %s)' % message}, host) - self.send({'command': 'ExecBuiltIn', 'parameter': 'XBMC.updatelibrary(video)'}, host) + hosts = [x.strip() for x in self.conf('host').split(",")] + successful = 0 + for host in hosts: + if self.send({'command': 'ExecBuiltIn', 'parameter': 'Notification(CouchPotato, %s)' % message}, host): + success += 1 + if self.send({'command': 'ExecBuiltIn', 'parameter': 'XBMC.updatelibrary(video)'}, host): + success += 1 - return True + return successful == len(hosts)*2 def send(self, command, host): From 80929b5806d498241e404cd352aecfc53e64bfe4 Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Tue, 24 Jul 2012 22:55:53 +0200 Subject: [PATCH 03/42] small fix to xbmc notification test --- couchpotato/core/notifications/xbmc/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/couchpotato/core/notifications/xbmc/main.py b/couchpotato/core/notifications/xbmc/main.py index 5f16bdb8..1c83bdd5 100644 --- a/couchpotato/core/notifications/xbmc/main.py +++ b/couchpotato/core/notifications/xbmc/main.py @@ -17,9 +17,9 @@ class XBMC(Notification): successful = 0 for host in hosts: if self.send({'command': 'ExecBuiltIn', 'parameter': 'Notification(CouchPotato, %s)' % message}, host): - success += 1 + successful += 1 if self.send({'command': 'ExecBuiltIn', 'parameter': 'XBMC.updatelibrary(video)'}, host): - success += 1 + successful += 1 return successful == len(hosts)*2 From 2406e9ef8554532849b48a9b8644fce243e4f13d Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Tue, 24 Jul 2012 23:27:26 +0200 Subject: [PATCH 04/42] Fixed NMJ test --- couchpotato/core/notifications/nmj/main.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/couchpotato/core/notifications/nmj/main.py b/couchpotato/core/notifications/nmj/main.py index 8f0e201d..1a794fbe 100644 --- a/couchpotato/core/notifications/nmj/main.py +++ b/couchpotato/core/notifications/nmj/main.py @@ -19,7 +19,7 @@ class NMJ(Notification): def __init__(self): addEvent('renamer.after', self.addToLibrary) - + addApiView(self.testNotifyName(), self.test) addApiView('notify.nmj.auto_config', self.autoConfig) def autoConfig(self): @@ -76,7 +76,7 @@ class NMJ(Notification): mount = self.conf('mount') database = self.conf('database') - if self.mount: + if mount: log.debug('Try to mount network drive via url: %s', (mount)) try: data = self.urlopen(mount) @@ -114,3 +114,8 @@ class NMJ(Notification): def failed(self): return jsonified({'success': False}) + + def test(self): + return jsonified({'success': self.addToLibrary()}) + + From 46c73dc43cdce4a4b474b26661bc83ee090fdaec Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Wed, 25 Jul 2012 00:09:13 +0200 Subject: [PATCH 05/42] Fix hint being added to the notification test button texts --- couchpotato/core/notifications/core/static/notification.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/couchpotato/core/notifications/core/static/notification.js b/couchpotato/core/notifications/core/static/notification.js index 595a39ed..db9db843 100644 --- a/couchpotato/core/notifications/core/static/notification.js +++ b/couchpotato/core/notifications/core/static/notification.js @@ -216,7 +216,7 @@ var NotificationBase = new Class({ }, testButtonName: function(fieldset){ - var name = fieldset.getElement('h2').get('text'); + var name = String(fieldset.getElement('h2').innerHTML).substring(0,String(fieldset.getElement('h2').innerHTML).indexOf(" Date: Sun, 29 Jul 2012 18:56:05 +0200 Subject: [PATCH 06/42] Added failed download handling for sabnzbd. It will check for failed downloads from the sabnzbd api and then set the failed release to ignore and try the search again. --- couchpotato/core/downloaders/base.py | 5 ++- couchpotato/core/downloaders/sabnzbd/main.py | 43 ++++++++++++++++++ couchpotato/core/plugins/renamer/main.py | 46 +++++++++++++++++++- 3 files changed, 92 insertions(+), 2 deletions(-) diff --git a/couchpotato/core/downloaders/base.py b/couchpotato/core/downloaders/base.py index 74e04de6..e0dad913 100644 --- a/couchpotato/core/downloaders/base.py +++ b/couchpotato/core/downloaders/base.py @@ -7,17 +7,20 @@ import os log = CPLog(__name__) - class Downloader(Plugin): type = [] def __init__(self): addEvent('download', self.download) + addEvent('getdownloadfailed', self.getdownloadfailed) def download(self, data = {}): pass + def getdownloadfailed(self, data = {}): + pass + def createNzbName(self, data, movie): return '%s%s' % (toSafeString(data.get('name')), self.cpTag(movie)) diff --git a/couchpotato/core/downloaders/sabnzbd/main.py b/couchpotato/core/downloaders/sabnzbd/main.py index 64b24509..8983c1ad 100644 --- a/couchpotato/core/downloaders/sabnzbd/main.py +++ b/couchpotato/core/downloaders/sabnzbd/main.py @@ -3,6 +3,8 @@ from couchpotato.core.helpers.encoding import tryUrlencode from couchpotato.core.helpers.variable import cleanHost from couchpotato.core.logger import CPLog import traceback +import urllib2 +import json log = CPLog(__name__) @@ -61,3 +63,44 @@ class Sabnzbd(Downloader): else: log.error("Unknown error: " + result[:40]) return False + + def getdownloadfailed(self, data = {}, movie = {}, manual = False): + if self.isDisabled(manual) or not self.isCorrectType(data.get('type')): + return + + log.info('Checking download status of "%s" at SABnzbd.', data.get('name')) + + params = { + 'apikey': self.conf('api_key'), + 'mode': 'history', + 'ouput': 'json' + } + url = cleanHost(self.conf('host')) + "api?" + tryUrlencode(params) + log.debug('Opening: %s', url) + history = json.load(urllib2.urlopen(url)) + + nzbname = self.createNzbName(data, movie) + + # Go through history items + for slot in history['history']['slots']: + log.debug('Found %s in SabNZBd history, which has %s', (slot['name'], slot['status'])) + if slot['name'] == nzbname and slot['status'] == 'Failed': + log.debug('%s failed downloading, deleting...', slot['name']) + + # Delete failed download + params = { + 'apikey': self.conf('api_key'), + 'mode': 'history', + 'name': 'delete', + 'value' : slot['id'] + } + url = cleanHost(self.conf('host')) + "api?" + tryUrlencode(params) + try: + data = self.urlopen(url, timeout = 60, show_error = False) + except: + log.error(traceback.format_exc()) + + # Return failed + return True + + return False diff --git a/couchpotato/core/plugins/renamer/main.py b/couchpotato/core/plugins/renamer/main.py index 4c6e6b89..c8a76944 100644 --- a/couchpotato/core/plugins/renamer/main.py +++ b/couchpotato/core/plugins/renamer/main.py @@ -6,7 +6,7 @@ from couchpotato.core.helpers.request import jsonified from couchpotato.core.helpers.variable import getExt, mergeDicts, getTitle from couchpotato.core.logger import CPLog from couchpotato.core.plugins.base import Plugin -from couchpotato.core.settings.model import Library, File, Profile +from couchpotato.core.settings.model import Library, File, Profile, Release as Relea from couchpotato.environment import Env import os import re @@ -48,6 +48,8 @@ class Renamer(Plugin): log.info('Renamer is disabled to avoid infinite looping of the same error.') return + self.checkSnatchedStatusses() + # Check to see if the "to" folder is inside the "from" folder. if not os.path.isdir(self.conf('from')) or not os.path.isdir(self.conf('to')): log.debug('"To" and "From" have to exist.') @@ -471,3 +473,45 @@ class Renamer(Plugin): os.rmdir(folder) except: log.error('Couldn\'t remove empty directory %s: %s', (folder, traceback.format_exc())) + + def checkSnatchedStatusses(self): + snatched_status = fireEvent('status.get', 'snatched', single = True) + ignored_status = fireEvent('status.get', 'ignored', single = True) + + db = get_session() + rels = db.query(Relea).filter_by(status_id = snatched_status.get('id')) + + log.debug('Checking snatched releases... %s', 'ops') + + for rel in rels: + log.debug('Checking snatched release: %s' , rel.movie.library.titles[0].title) + item = {} + for info in rel.info: + item[info.identifier] = info.value + + log.debug('Checking status snatched release: %s' , item.get('name')) + + mymovie = rel.movie.to_dict({ + 'profile': {'types': {'quality': {}}}, + 'releases': {'status': {}, 'quality': {}}, + 'library': {'titles': {}, 'files':{}}, + 'files': {} + }) + + log.debug('Checking status snatched release: %s' , mymovie['library'].get('identifier')) + + # check status + downloadfailed = fireEvent('getdownloadfailed', data = item, movie = mymovie) + + if downloadfailed: + log.debug('Download of %s failed', item['name']) + + # if failed set status to ignored + rel.status_id = ignored_status.get('id') + db.commit() + + # search/download again + log.info('Download of %s failed, trying next release...', item['name']) + fireEvent('searcher.single', rel.movie) + + return From 684ef44f841a9f39b07e9a724c550e688bf89a19 Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Sun, 29 Jul 2012 20:09:11 +0200 Subject: [PATCH 07/42] Fixed typo and added some error handling --- couchpotato/core/downloaders/sabnzbd/main.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/couchpotato/core/downloaders/sabnzbd/main.py b/couchpotato/core/downloaders/sabnzbd/main.py index 8983c1ad..45b4e25b 100644 --- a/couchpotato/core/downloaders/sabnzbd/main.py +++ b/couchpotato/core/downloaders/sabnzbd/main.py @@ -4,6 +4,7 @@ from couchpotato.core.helpers.variable import cleanHost from couchpotato.core.logger import CPLog import traceback import urllib2 +import requests import json log = CPLog(__name__) @@ -73,11 +74,16 @@ class Sabnzbd(Downloader): params = { 'apikey': self.conf('api_key'), 'mode': 'history', - 'ouput': 'json' + 'output': 'json' } url = cleanHost(self.conf('host')) + "api?" + tryUrlencode(params) log.debug('Opening: %s', url) - history = json.load(urllib2.urlopen(url)) + + try: + history = json.load(urllib2.urlopen(url)) + except: + log.error(traceback.format_exc()) + return False nzbname = self.createNzbName(data, movie) From 2884488338d5bd98a98b431c80eb17eb509802de Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Sun, 29 Jul 2012 22:02:51 +0200 Subject: [PATCH 08/42] Removed double logging, bug fixes --- couchpotato/core/plugins/renamer/main.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/couchpotato/core/plugins/renamer/main.py b/couchpotato/core/plugins/renamer/main.py index c8a76944..f0887acf 100644 --- a/couchpotato/core/plugins/renamer/main.py +++ b/couchpotato/core/plugins/renamer/main.py @@ -481,37 +481,32 @@ class Renamer(Plugin): db = get_session() rels = db.query(Relea).filter_by(status_id = snatched_status.get('id')) - log.debug('Checking snatched releases... %s', 'ops') - for rel in rels: log.debug('Checking snatched release: %s' , rel.movie.library.titles[0].title) + item = {} for info in rel.info: item[info.identifier] = info.value log.debug('Checking status snatched release: %s' , item.get('name')) - mymovie = rel.movie.to_dict({ + mov = rel.movie.to_dict({ 'profile': {'types': {'quality': {}}}, 'releases': {'status': {}, 'quality': {}}, 'library': {'titles': {}, 'files':{}}, 'files': {} }) - log.debug('Checking status snatched release: %s' , mymovie['library'].get('identifier')) - # check status - downloadfailed = fireEvent('getdownloadfailed', data = item, movie = mymovie) + downloadfailed = fireEvent('getdownloadfailed', data = item, movie = mov) if downloadfailed: - log.debug('Download of %s failed', item['name']) - # if failed set status to ignored rel.status_id = ignored_status.get('id') db.commit() # search/download again log.info('Download of %s failed, trying next release...', item['name']) - fireEvent('searcher.single', rel.movie) + fireEvent('searcher.single', mov) return From b262ed59a8d6f07a882da79c09bc049e4e5dd265 Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Mon, 30 Jul 2012 19:15:25 +0200 Subject: [PATCH 09/42] Fixed sab api bug --- couchpotato/core/downloaders/sabnzbd/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/couchpotato/core/downloaders/sabnzbd/main.py b/couchpotato/core/downloaders/sabnzbd/main.py index 45b4e25b..10180991 100644 --- a/couchpotato/core/downloaders/sabnzbd/main.py +++ b/couchpotato/core/downloaders/sabnzbd/main.py @@ -98,7 +98,8 @@ class Sabnzbd(Downloader): 'apikey': self.conf('api_key'), 'mode': 'history', 'name': 'delete', - 'value' : slot['id'] + 'del_files': '1', + 'value': slot['nzo_id'] } url = cleanHost(self.conf('host')) + "api?" + tryUrlencode(params) try: From 4aae3c45eadc76ee276a305d26e9e862ace2eff2 Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Wed, 1 Aug 2012 02:38:00 +0200 Subject: [PATCH 10/42] Bug fixes, it should actually restart the download now --- couchpotato/core/downloaders/base.py | 4 ++-- couchpotato/core/downloaders/sabnzbd/main.py | 4 ++-- couchpotato/core/plugins/renamer/main.py | 20 +++++++++++++++++++- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/couchpotato/core/downloaders/base.py b/couchpotato/core/downloaders/base.py index e0dad913..07536a49 100644 --- a/couchpotato/core/downloaders/base.py +++ b/couchpotato/core/downloaders/base.py @@ -15,10 +15,10 @@ class Downloader(Plugin): addEvent('download', self.download) addEvent('getdownloadfailed', self.getdownloadfailed) - def download(self, data = {}): + def download(self, data = {}, movie = {}, manual = False, filedata = None): pass - def getdownloadfailed(self, data = {}): + def getdownloadfailed(self, data = {}, movie = {}): pass def createNzbName(self, data, movie): diff --git a/couchpotato/core/downloaders/sabnzbd/main.py b/couchpotato/core/downloaders/sabnzbd/main.py index 10180991..010c1be2 100644 --- a/couchpotato/core/downloaders/sabnzbd/main.py +++ b/couchpotato/core/downloaders/sabnzbd/main.py @@ -65,8 +65,8 @@ class Sabnzbd(Downloader): log.error("Unknown error: " + result[:40]) return False - def getdownloadfailed(self, data = {}, movie = {}, manual = False): - if self.isDisabled(manual) or not self.isCorrectType(data.get('type')): + def getdownloadfailed(self, data = {}, movie = {}): + if self.isDisabled(manual = True) or not self.isCorrectType(data.get('type')): return log.info('Checking download status of "%s" at SABnzbd.', data.get('name')) diff --git a/couchpotato/core/plugins/renamer/main.py b/couchpotato/core/plugins/renamer/main.py index f0887acf..8855df4d 100644 --- a/couchpotato/core/plugins/renamer/main.py +++ b/couchpotato/core/plugins/renamer/main.py @@ -482,7 +482,13 @@ class Renamer(Plugin): rels = db.query(Relea).filter_by(status_id = snatched_status.get('id')) for rel in rels: - log.debug('Checking snatched release: %s' , rel.movie.library.titles[0].title) + + # Get current selected title + default_title = '' + for title in rel.movie.library.titles: + if title.default: default_title = title.title + + log.debug('Checking snatched movie: %s' , default_title) item = {} for info in rel.info: @@ -506,6 +512,18 @@ class Renamer(Plugin): db.commit() # search/download again + # if downloaded manually: # this is currently not stored... + # log.info('Download of %s failed...', item['name']) + # return + + #update movie to reflect release status update + mov = rel.movie.to_dict({ + 'profile': {'types': {'quality': {}}}, + 'releases': {'status': {}, 'quality': {}}, + 'library': {'titles': {}, 'files':{}}, + 'files': {} + }) + log.info('Download of %s failed, trying next release...', item['name']) fireEvent('searcher.single', mov) From db2c9e51565cd71605ba8484edf35c6f68c53ccf Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Wed, 1 Aug 2012 03:09:34 +0200 Subject: [PATCH 11/42] Added config options --- .../core/downloaders/sabnzbd/__init__.py | 16 ++++++++- couchpotato/core/downloaders/sabnzbd/main.py | 36 ++++++++++--------- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/couchpotato/core/downloaders/sabnzbd/__init__.py b/couchpotato/core/downloaders/sabnzbd/__init__.py index ac0ce05c..938c6748 100644 --- a/couchpotato/core/downloaders/sabnzbd/__init__.py +++ b/couchpotato/core/downloaders/sabnzbd/__init__.py @@ -35,11 +35,25 @@ config = [{ }, { 'name': 'manual', - 'default': 0, + 'default': False, 'type': 'bool', 'advanced': True, 'description': 'Disable this downloader for automated searches, but use it when I manually send a release.', }, + { + 'name': 'download failed', + 'default': True, + 'type': 'bool', + 'advanced': True, + 'description': 'Try next the next best release for a movie after a download failed.', + }, + { + 'name': 'delete failed', + 'default': True, + 'type': 'bool', + 'advanced': True, + 'description': 'Delete a release after it\'s download failed.', + }, ], } ], diff --git a/couchpotato/core/downloaders/sabnzbd/main.py b/couchpotato/core/downloaders/sabnzbd/main.py index 010c1be2..bb7a2eb8 100644 --- a/couchpotato/core/downloaders/sabnzbd/main.py +++ b/couchpotato/core/downloaders/sabnzbd/main.py @@ -3,8 +3,6 @@ from couchpotato.core.helpers.encoding import tryUrlencode from couchpotato.core.helpers.variable import cleanHost from couchpotato.core.logger import CPLog import traceback -import urllib2 -import requests import json log = CPLog(__name__) @@ -69,6 +67,9 @@ class Sabnzbd(Downloader): if self.isDisabled(manual = True) or not self.isCorrectType(data.get('type')): return + if not self.conf('download failed', default = True): + return False + log.info('Checking download status of "%s" at SABnzbd.', data.get('name')) params = { @@ -80,7 +81,7 @@ class Sabnzbd(Downloader): log.debug('Opening: %s', url) try: - history = json.load(urllib2.urlopen(url)) + history = json.load(self.urlopen(url)) except: log.error(traceback.format_exc()) return False @@ -91,23 +92,24 @@ class Sabnzbd(Downloader): for slot in history['history']['slots']: log.debug('Found %s in SabNZBd history, which has %s', (slot['name'], slot['status'])) if slot['name'] == nzbname and slot['status'] == 'Failed': - log.debug('%s failed downloading, deleting...', slot['name']) # Delete failed download - params = { - 'apikey': self.conf('api_key'), - 'mode': 'history', - 'name': 'delete', - 'del_files': '1', - 'value': slot['nzo_id'] - } - url = cleanHost(self.conf('host')) + "api?" + tryUrlencode(params) - try: - data = self.urlopen(url, timeout = 60, show_error = False) - except: - log.error(traceback.format_exc()) + if self.conf('delete failed', default = True): + log.info('%s failed downloading, deleting...', slot['name']) + params = { + 'apikey': self.conf('api_key'), + 'mode': 'history', + 'name': 'delete', + 'del_files': '1', + 'value': slot['nzo_id'] + } + url = cleanHost(self.conf('host')) + "api?" + tryUrlencode(params) + try: + data = self.urlopen(url, timeout = 60, show_error = False) + except: + log.error(traceback.format_exc()) - # Return failed + # Return download failed return True return False From 100b563e107ec789de93c2ba417eca18da59dfa8 Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Wed, 1 Aug 2012 03:11:19 +0200 Subject: [PATCH 12/42] typo --- couchpotato/core/downloaders/sabnzbd/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/couchpotato/core/downloaders/sabnzbd/__init__.py b/couchpotato/core/downloaders/sabnzbd/__init__.py index 938c6748..822ba61f 100644 --- a/couchpotato/core/downloaders/sabnzbd/__init__.py +++ b/couchpotato/core/downloaders/sabnzbd/__init__.py @@ -45,7 +45,7 @@ config = [{ 'default': True, 'type': 'bool', 'advanced': True, - 'description': 'Try next the next best release for a movie after a download failed.', + 'description': 'Try the next best release for a movie after a download failed.', }, { 'name': 'delete failed', From 4223ed4b5aa97ee5aa0871e51a61af3e6c234b57 Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Thu, 2 Aug 2012 18:59:04 +0200 Subject: [PATCH 13/42] Put the renamer as a function of a download status checker in the searcher class The changed functionality is as follows: - the renamer is not on scheduled interval anymore - the download status checker checks the status of all snatched releases every x minutes - if a release has downloaded it fires up the renamer (if enabled) - if it failed, it sets the release to ignored and snatches the next best release With these additions the renamer wont scan your hd anymore when it is not required, and will retry failed downloads with new releases. To do: - the only downloader implemented is SabNZBd, for the others it defaults to the old behavior when releases are snatched (I think!?) - a button to scan manually: Items added to the renamer folder are only picked up after a download completed --- couchpotato/core/downloaders/base.py | 6 +- .../core/downloaders/sabnzbd/__init__.py | 7 -- couchpotato/core/downloaders/sabnzbd/main.py | 109 ++++++++++++------ couchpotato/core/plugins/renamer/__init__.py | 9 -- couchpotato/core/plugins/renamer/main.py | 57 +-------- couchpotato/core/plugins/searcher/__init__.py | 16 +++ couchpotato/core/plugins/searcher/main.py | 76 ++++++++++++ 7 files changed, 172 insertions(+), 108 deletions(-) diff --git a/couchpotato/core/downloaders/base.py b/couchpotato/core/downloaders/base.py index 07536a49..1be9dccc 100644 --- a/couchpotato/core/downloaders/base.py +++ b/couchpotato/core/downloaders/base.py @@ -13,16 +13,16 @@ class Downloader(Plugin): def __init__(self): addEvent('download', self.download) - addEvent('getdownloadfailed', self.getdownloadfailed) + addEvent('getdownloadstatus', self.getdownloadstatus) def download(self, data = {}, movie = {}, manual = False, filedata = None): pass - def getdownloadfailed(self, data = {}, movie = {}): + def getdownloadstatus(self, data = {}, movie = {}): pass def createNzbName(self, data, movie): - return '%s%s' % (toSafeString(data.get('name')), self.cpTag(movie)) + return '%s%s' % (toSafeString(data.get('name')[:40]), self.cpTag(movie)) def createFileName(self, data, filedata, movie): name = os.path.join(self.createNzbName(data, movie)) diff --git a/couchpotato/core/downloaders/sabnzbd/__init__.py b/couchpotato/core/downloaders/sabnzbd/__init__.py index 822ba61f..1baaea0d 100644 --- a/couchpotato/core/downloaders/sabnzbd/__init__.py +++ b/couchpotato/core/downloaders/sabnzbd/__init__.py @@ -40,13 +40,6 @@ config = [{ 'advanced': True, 'description': 'Disable this downloader for automated searches, but use it when I manually send a release.', }, - { - 'name': 'download failed', - 'default': True, - 'type': 'bool', - 'advanced': True, - 'description': 'Try the next best release for a movie after a download failed.', - }, { 'name': 'delete failed', 'default': True, diff --git a/couchpotato/core/downloaders/sabnzbd/main.py b/couchpotato/core/downloaders/sabnzbd/main.py index bb7a2eb8..4144b279 100644 --- a/couchpotato/core/downloaders/sabnzbd/main.py +++ b/couchpotato/core/downloaders/sabnzbd/main.py @@ -40,14 +40,14 @@ class Sabnzbd(Downloader): try: if params.get('mode') is 'addfile': - data = self.urlopen(url, timeout = 60, params = {"nzbfile": (nzb_filename, filedata)}, multipart = True, show_error = False) + sab = self.urlopen(url, timeout = 60, params = {"nzbfile": (nzb_filename, filedata)}, multipart = True, show_error = False) else: - data = self.urlopen(url, timeout = 60, show_error = False) + sab = self.urlopen(url, timeout = 60, show_error = False) except: log.error(traceback.format_exc()) return False - result = data.strip() + result = sab.strip() if not result: log.error("SABnzbd didn't return anything.") return False @@ -63,53 +63,96 @@ class Sabnzbd(Downloader): log.error("Unknown error: " + result[:40]) return False - def getdownloadfailed(self, data = {}, movie = {}): + def getdownloadstatus(self, data = {}, movie = {}): if self.isDisabled(manual = True) or not self.isCorrectType(data.get('type')): return - if not self.conf('download failed', default = True): - return False + nzbname = self.createNzbName(data, movie) + log.info('Checking download status of "%s" at SABnzbd.', nzbname) - log.info('Checking download status of "%s" at SABnzbd.', data.get('name')) + # Go through Queue + params = { + 'apikey': self.conf('api_key'), + 'mode': 'queue', + 'output': 'json' + } + url = cleanHost(self.conf('host')) + "api?" + tryUrlencode(params) + try: + sab = self.urlopen(url, timeout = 60, show_error = False) + except: + log.error(traceback.format_exc()) + return + try: + history = json.loads(sab) + except: + log.debug("Result text from SAB: " + sab[:40]) + log.error(traceback.format_exc()) + return + + for slot in history['queue']['slots']: + if slot['cat'] == self.conf('category'): + log.debug('Found %s in SabNZBd queue, which is %s, with %s left', (slot['filename'], slot['status'], slot['timeleft'])) + if slot['filename'] == nzbname: + return slot['status'] + + # Go through history items params = { 'apikey': self.conf('api_key'), 'mode': 'history', 'output': 'json' } url = cleanHost(self.conf('host')) + "api?" + tryUrlencode(params) - log.debug('Opening: %s', url) try: - history = json.load(self.urlopen(url)) + sab = self.urlopen(url, timeout = 60, show_error = False) except: log.error(traceback.format_exc()) - return False + return + try: + history = json.loads(sab) + except: + log.debug("Result text from SAB: " + sab[:40]) + log.error(traceback.format_exc()) + return - nzbname = self.createNzbName(data, movie) - - # Go through history items for slot in history['history']['slots']: - log.debug('Found %s in SabNZBd history, which has %s', (slot['name'], slot['status'])) - if slot['name'] == nzbname and slot['status'] == 'Failed': + if slot['category'] == self.conf('category'): + log.debug('Found %s in SabNZBd history, which has %s', (slot['name'], slot['status'])) + if slot['name'] == nzbname: + if slot['status'] == 'Failed' or 'fail' in slot['fail_message'].lower(): - # Delete failed download - if self.conf('delete failed', default = True): - log.info('%s failed downloading, deleting...', slot['name']) - params = { - 'apikey': self.conf('api_key'), - 'mode': 'history', - 'name': 'delete', - 'del_files': '1', - 'value': slot['nzo_id'] - } - url = cleanHost(self.conf('host')) + "api?" + tryUrlencode(params) - try: - data = self.urlopen(url, timeout = 60, show_error = False) - except: - log.error(traceback.format_exc()) + # Delete failed download + if self.conf('delete failed', default = True): + log.info('%s failed downloading, deleting...', slot['name']) + params = { + 'apikey': self.conf('api_key'), + 'mode': 'history', + 'name': 'delete', + 'del_files': '1', + 'value': slot['nzo_id'] + } + url = cleanHost(self.conf('host')) + "api?" + tryUrlencode(params) + try: + sab = self.urlopen(url, timeout = 60, show_error = False) + except: + log.error(traceback.format_exc()) + return False - # Return download failed - return True + result = sab.strip() + if not result: + log.error("SABnzbd didn't return anything.") - return False + log.debug("Result text from SAB: " + result[:40]) + if result == "ok": + log.info('SabNZBd deleted failed release %s successfully.', slot['name']) + elif result == "Missing authentication": + log.error("Incorrect username/password.") + else: + log.error("Unknown error: " + result[:40]) + + return 'Failed' + else: + return slot['status'] + + return 'Not found' diff --git a/couchpotato/core/plugins/renamer/__init__.py b/couchpotato/core/plugins/renamer/__init__.py index 59439544..21076d68 100644 --- a/couchpotato/core/plugins/renamer/__init__.py +++ b/couchpotato/core/plugins/renamer/__init__.py @@ -86,15 +86,6 @@ config = [{ 'label': 'Separator', 'description': 'Replace all the spaces with a character. Example: ".", "-" (without quotes). Leave empty to use spaces.', }, - { - 'advanced': True, - 'name': 'run_every', - 'label': 'Run every', - 'default': 1, - 'type': 'int', - 'unit': 'min(s)', - 'description': 'Search for new movies inside the folder every X minutes.', - }, ], }, { 'tab': 'renamer', diff --git a/couchpotato/core/plugins/renamer/main.py b/couchpotato/core/plugins/renamer/main.py index 8855df4d..4c80fb08 100644 --- a/couchpotato/core/plugins/renamer/main.py +++ b/couchpotato/core/plugins/renamer/main.py @@ -29,7 +29,7 @@ class Renamer(Plugin): addEvent('renamer.scan', self.scan) addEvent('app.load', self.scan) - fireEvent('schedule.interval', 'renamer.scan', self.scan, minutes = self.conf('run_every')) + #fireEvent('schedule.interval', 'renamer.scan', self.scan, minutes = self.conf('run_every')) def scanView(self): @@ -48,8 +48,6 @@ class Renamer(Plugin): log.info('Renamer is disabled to avoid infinite looping of the same error.') return - self.checkSnatchedStatusses() - # Check to see if the "to" folder is inside the "from" folder. if not os.path.isdir(self.conf('from')) or not os.path.isdir(self.conf('to')): log.debug('"To" and "From" have to exist.') @@ -474,57 +472,4 @@ class Renamer(Plugin): except: log.error('Couldn\'t remove empty directory %s: %s', (folder, traceback.format_exc())) - def checkSnatchedStatusses(self): - snatched_status = fireEvent('status.get', 'snatched', single = True) - ignored_status = fireEvent('status.get', 'ignored', single = True) - db = get_session() - rels = db.query(Relea).filter_by(status_id = snatched_status.get('id')) - - for rel in rels: - - # Get current selected title - default_title = '' - for title in rel.movie.library.titles: - if title.default: default_title = title.title - - log.debug('Checking snatched movie: %s' , default_title) - - item = {} - for info in rel.info: - item[info.identifier] = info.value - - log.debug('Checking status snatched release: %s' , item.get('name')) - - mov = rel.movie.to_dict({ - 'profile': {'types': {'quality': {}}}, - 'releases': {'status': {}, 'quality': {}}, - 'library': {'titles': {}, 'files':{}}, - 'files': {} - }) - - # check status - downloadfailed = fireEvent('getdownloadfailed', data = item, movie = mov) - - if downloadfailed: - # if failed set status to ignored - rel.status_id = ignored_status.get('id') - db.commit() - - # search/download again - # if downloaded manually: # this is currently not stored... - # log.info('Download of %s failed...', item['name']) - # return - - #update movie to reflect release status update - mov = rel.movie.to_dict({ - 'profile': {'types': {'quality': {}}}, - 'releases': {'status': {}, 'quality': {}}, - 'library': {'titles': {}, 'files':{}}, - 'files': {} - }) - - log.info('Download of %s failed, trying next release...', item['name']) - fireEvent('searcher.single', mov) - - return diff --git a/couchpotato/core/plugins/searcher/__init__.py b/couchpotato/core/plugins/searcher/__init__.py index f499e2bd..2d82b61c 100644 --- a/couchpotato/core/plugins/searcher/__init__.py +++ b/couchpotato/core/plugins/searcher/__init__.py @@ -39,6 +39,22 @@ config = [{ 'type': 'dropdown', 'values': [('usenet & torrents', 'both'), ('usenet', 'nzb'), ('torrents', 'torrent')], }, + { + 'advanced': True, + 'name': 'run_every', + 'label': 'Run every', + 'default': 1, + 'type': 'int', + 'unit': 'min(s)', + 'description': 'Detect movie status every X minutes. Will start the renamer if movie is completed or handle failed download if these options are enabled', + }, + { + 'name': 'failed download', + 'default': True, + 'type': 'bool', + 'advanced': True, + 'description': 'Try the next best release for a movie after a download failed.', + }, ], }, { 'tab': 'searcher', diff --git a/couchpotato/core/plugins/searcher/main.py b/couchpotato/core/plugins/searcher/main.py index 03eb6cfb..b1b285ef 100644 --- a/couchpotato/core/plugins/searcher/main.py +++ b/couchpotato/core/plugins/searcher/main.py @@ -25,9 +25,12 @@ class Searcher(Plugin): addEvent('searcher.single', self.single) addEvent('searcher.correct_movie', self.correctMovie) addEvent('searcher.download', self.download) + addEvent('searcher.checksnatched', self.checksnatched) # Schedule cronjob fireEvent('schedule.cron', 'searcher.all', self.all_movies, day = self.conf('cron_day'), hour = self.conf('cron_hour'), minute = self.conf('cron_minute')) + fireEvent('schedule.interval', 'searcher.checksnatched', self.checksnatched, minutes = self.conf('run_every')) + def all_movies(self): @@ -439,3 +442,76 @@ class Searcher(Plugin): return False + + def checksnatched(self): + snatched_status = fireEvent('status.get', 'snatched', single = True) + ignored_status = fireEvent('status.get', 'ignored', single = True) + + db = get_session() + rels = db.query(Release).filter_by(status_id = snatched_status.get('id')) + + log.info('Checking snatched releases...') + + for rel in rels: + + # Get current selected title + default_title = '' + for title in rel.movie.library.titles: + if title.default: default_title = title.title + + log.debug('Checking snatched movie: %s' , default_title) + + item = {} + for info in rel.info: + item[info.identifier] = info.value + + movie = rel.movie.to_dict({ + 'profile': {'types': {'quality': {}}}, + 'releases': {'status': {}, 'quality': {}}, + 'library': {'titles': {}, 'files':{}}, + 'files': {} + }) + + # check status + downloadstatus = fireEvent('getdownloadstatus', data = item, movie = movie) + log.debug('Download staus: %s' , downloadstatus[0]) + + if downloadstatus[0] == 'Failed': + # if failed set status to ignored + rel.status_id = ignored_status.get('id') + db.commit() + + # search/download again + # if downloaded manually: # this is currently not stored... + # log.info('Download of %s failed...', item['name']) + # return + + if self.conf('failed download', default = True): + + #update movie to reflect release status update + movie = rel.movie.to_dict({ + 'profile': {'types': {'quality': {}}}, + 'releases': {'status': {}, 'quality': {}}, + 'library': {'titles': {}, 'files':{}}, + 'files': {} + }) + log.info('Download of %s failed, trying next release...', item['name']) + fireEvent('searcher.single', movie) + else: + log.info('Download of %s failed.', item['name']) + + elif downloadstatus[0] == 'Completed': + log.info('Download of %s completed!', item['name']) + fireEvent('renamer.scan') + + elif downloadstatus[0] == 'Not found': + log.info('%s not found in SabNZBd', item['name']) + rel.status_id = ignored_status.get('id') + db.commit() + + elif downloadstatus[0] == None: # Downloader not compatible with download status or + fireEvent('renamer.scan') + + # Note that Queued, Downloading, Paused, Repairn and Unpackimg are also available as status + + return From 928c440f90b77a793ad64368afcb8cd31a3a883f Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Thu, 2 Aug 2012 19:01:19 +0200 Subject: [PATCH 14/42] renamer cleanup --- couchpotato/core/plugins/renamer/main.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/couchpotato/core/plugins/renamer/main.py b/couchpotato/core/plugins/renamer/main.py index 4c80fb08..a7744993 100644 --- a/couchpotato/core/plugins/renamer/main.py +++ b/couchpotato/core/plugins/renamer/main.py @@ -6,7 +6,7 @@ from couchpotato.core.helpers.request import jsonified from couchpotato.core.helpers.variable import getExt, mergeDicts, getTitle from couchpotato.core.logger import CPLog from couchpotato.core.plugins.base import Plugin -from couchpotato.core.settings.model import Library, File, Profile, Release as Relea +from couchpotato.core.settings.model import Library, File, Profile from couchpotato.environment import Env import os import re @@ -471,5 +471,3 @@ class Renamer(Plugin): os.rmdir(folder) except: log.error('Couldn\'t remove empty directory %s: %s', (folder, traceback.format_exc())) - - From 0193f7ad8116f681d4666247a34f546f564bff5d Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Thu, 2 Aug 2012 20:05:31 +0200 Subject: [PATCH 15/42] scan only once when multiple releases are snatched --- couchpotato/core/plugins/searcher/main.py | 68 ++++++++++++----------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/couchpotato/core/plugins/searcher/main.py b/couchpotato/core/plugins/searcher/main.py index b1b285ef..2d2fdac6 100644 --- a/couchpotato/core/plugins/searcher/main.py +++ b/couchpotato/core/plugins/searcher/main.py @@ -450,7 +450,8 @@ class Searcher(Plugin): db = get_session() rels = db.query(Release).filter_by(status_id = snatched_status.get('id')) - log.info('Checking snatched releases...') + log.info('Checking status snatched releases...') + scanrequired = False for rel in rels: @@ -474,44 +475,47 @@ class Searcher(Plugin): # check status downloadstatus = fireEvent('getdownloadstatus', data = item, movie = movie) - log.debug('Download staus: %s' , downloadstatus[0]) + if downloadstatus == None: # Downloader not compatible with download status + scanrequired = True - if downloadstatus[0] == 'Failed': - # if failed set status to ignored - rel.status_id = ignored_status.get('id') - db.commit() + else: + log.debug('Download staus: %s' , downloadstatus[0]) - # search/download again - # if downloaded manually: # this is currently not stored... - # log.info('Download of %s failed...', item['name']) - # return + if downloadstatus[0] == 'Failed': + # if failed set status to ignored + rel.status_id = ignored_status.get('id') + db.commit() - if self.conf('failed download', default = True): + # search/download again + # if downloaded manually: # this is currently not stored... + # log.info('Download of %s failed...', item['name']) + # return - #update movie to reflect release status update - movie = rel.movie.to_dict({ - 'profile': {'types': {'quality': {}}}, - 'releases': {'status': {}, 'quality': {}}, - 'library': {'titles': {}, 'files':{}}, - 'files': {} - }) - log.info('Download of %s failed, trying next release...', item['name']) - fireEvent('searcher.single', movie) - else: - log.info('Download of %s failed.', item['name']) + if self.conf('failed download', default = True): - elif downloadstatus[0] == 'Completed': - log.info('Download of %s completed!', item['name']) - fireEvent('renamer.scan') + #update movie to reflect release status update + movie = rel.movie.to_dict({ + 'profile': {'types': {'quality': {}}}, + 'releases': {'status': {}, 'quality': {}}, + 'library': {'titles': {}, 'files':{}}, + 'files': {} + }) + log.info('Download of %s failed, trying next release...', item['name']) + fireEvent('searcher.single', movie) + else: + log.info('Download of %s failed.', item['name']) - elif downloadstatus[0] == 'Not found': - log.info('%s not found in SabNZBd', item['name']) - rel.status_id = ignored_status.get('id') - db.commit() + elif downloadstatus[0] == 'Completed': + log.info('Download of %s completed!', item['name']) + scanrequired = True - elif downloadstatus[0] == None: # Downloader not compatible with download status or - fireEvent('renamer.scan') + elif downloadstatus[0] == 'Not found': + log.info('%s not found in downloaders', item['name']) + rel.status_id = ignored_status.get('id') + db.commit() - # Note that Queued, Downloading, Paused, Repairn and Unpackimg are also available as status + # Note that Queued, Downloading, Paused, Repairn and Unpackimg are also available as status + if scanrequired: + fireEvent('renamer.scan') return From 71c181379a9008f14b3c416494d34ea71b05d86a Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Thu, 2 Aug 2012 20:56:05 +0200 Subject: [PATCH 16/42] Fixed behavior for not compatible downloaders + typo's --- couchpotato/core/plugins/searcher/main.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/couchpotato/core/plugins/searcher/main.py b/couchpotato/core/plugins/searcher/main.py index 2d2fdac6..fbfe0fa9 100644 --- a/couchpotato/core/plugins/searcher/main.py +++ b/couchpotato/core/plugins/searcher/main.py @@ -475,11 +475,12 @@ class Searcher(Plugin): # check status downloadstatus = fireEvent('getdownloadstatus', data = item, movie = movie) - if downloadstatus == None: # Downloader not compatible with download status + if not downloadstatus: # Downloader not compatible with download status + log.debug('Download status functionality is not implemented for active downloaders.') scanrequired = True else: - log.debug('Download staus: %s' , downloadstatus[0]) + log.debug('Download status: %s' , downloadstatus[0]) if downloadstatus[0] == 'Failed': # if failed set status to ignored @@ -514,7 +515,7 @@ class Searcher(Plugin): rel.status_id = ignored_status.get('id') db.commit() - # Note that Queued, Downloading, Paused, Repairn and Unpackimg are also available as status + # Note that Queued, Downloading, Paused, Repair and Unpackimg are also available as status for SabNZBd if scanrequired: fireEvent('renamer.scan') From 334a5725e1bc3c935f62352f14909bc9ba987049 Mon Sep 17 00:00:00 2001 From: Ruud Date: Thu, 9 Aug 2012 16:00:41 +0200 Subject: [PATCH 17/42] Use magnet links from publichd. fix #682 --- .../core/providers/torrent/publichd/main.py | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/couchpotato/core/providers/torrent/publichd/main.py b/couchpotato/core/providers/torrent/publichd/main.py index c962f935..14bfab59 100644 --- a/couchpotato/core/providers/torrent/publichd/main.py +++ b/couchpotato/core/providers/torrent/publichd/main.py @@ -15,19 +15,9 @@ class PublicHD(TorrentProvider): urls = { 'test': 'http://publichd.eu', - 'download': 'http://publichd.eu/%s', 'detail': 'http://publichd.eu/index.php?page=torrent-details&id=%s', 'search': 'http://publichd.eu/index.php', } - - cat_ids = [ - ([9], ['bd50']), - ([5], ['1080p']), - ([2], ['720p']), - ([15, 16], ['brrip']), - ] - - cat_backup_id = 0 http_time_between_calls = 0 def search(self, movie, quality): @@ -39,9 +29,8 @@ class PublicHD(TorrentProvider): params = tryUrlencode({ 'page':'torrents', - 'search': getTitle(movie['library']) + ' ' + quality['identifier'], + 'search': '%s %s' % (getTitle(movie['library']), movie['library']['year']), 'active': 1, - 'category': self.getCatId(quality['identifier'])[0] }) url = '%s?%s' % (self.urls['search'], params) @@ -58,7 +47,7 @@ class PublicHD(TorrentProvider): for result in entries[2:len(entries) - 1]: info_url = result.find(href = re.compile('torrent-details')) - download = result.find(href = re.compile('\.torrent')) + download = result.find(href = re.compile('magnet:')) if info_url and download: @@ -67,12 +56,11 @@ class PublicHD(TorrentProvider): new = { 'id': url['id'][0], 'name': info_url.string, - 'type': 'torrent', + 'type': 'torrent_magnet', 'check_nzb': False, 'description': '', 'provider': self.getName(), - 'download': self.download, - 'url': self.urls['download'] % download['href'], + 'url': download['href'], 'detail_url': self.urls['detail'] % url['id'][0], 'size': self.parseSize(result.find_all('td')[7].string), 'seeders': tryInt(result.find_all('td')[4].string), From 28f75617b2f1046b1fbc72905c49f7efc96db4b1 Mon Sep 17 00:00:00 2001 From: Ruud Date: Thu, 9 Aug 2012 16:23:05 +0200 Subject: [PATCH 18/42] Use magnet link for kat.ph. fix #644 --- .../providers/torrent/kickasstorrents/main.py | 26 ++++--------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/couchpotato/core/providers/torrent/kickasstorrents/main.py b/couchpotato/core/providers/torrent/kickasstorrents/main.py index c57ae8a6..e1440c2a 100644 --- a/couchpotato/core/providers/torrent/kickasstorrents/main.py +++ b/couchpotato/core/providers/torrent/kickasstorrents/main.py @@ -3,8 +3,6 @@ from couchpotato.core.event import fireEvent from couchpotato.core.helpers.variable import tryInt from couchpotato.core.logger import CPLog from couchpotato.core.providers.torrent.base import TorrentProvider -import StringIO -import gzip import re import traceback @@ -14,10 +12,9 @@ log = CPLog(__name__) class KickAssTorrents(TorrentProvider): urls = { - 'test': 'http://www.kat.ph/', - 'detail': 'http://www.kat.ph/%s-t%s.html', - 'search': 'http://www.kat.ph/i%s/', - 'download': 'http://torcache.net/', + 'test': 'http://kat.ph/', + 'detail': 'http://kat.ph/%s-t%s.html', + 'search': 'http://kat.ph/i%s/', } cat_ids = [ @@ -60,11 +57,10 @@ class KickAssTorrents(TorrentProvider): continue new = { - 'type': 'torrent', + 'type': 'torrent_magnet', 'check_nzb': False, 'description': '', 'provider': self.getName(), - 'download': self.download, 'score': 0, } @@ -77,9 +73,7 @@ class KickAssTorrents(TorrentProvider): link = td.find('div', {'class': 'torrentname'}).find_all('a')[1] new['id'] = temp.get('id')[-8:] new['name'] = link.text - new['url'] = td.find_all('a', 'idownload')[1]['href'] - if new['url'][:2] == '//': - new['url'] = 'http:%s' % new['url'] + new['url'] = td.find('a', 'imagnet')['href'] new['score'] = 20 if td.find('a', 'iverif') else 0 elif column_name is 'size': new['size'] = self.parseSize(td.text) @@ -129,13 +123,3 @@ class KickAssTorrents(TorrentProvider): age += tryInt(nr) * mult return tryInt(age) - - def download(self, url = '', nzb_id = ''): - compressed_data = self.urlopen(url = url, headers = {'Referer': 'http://kat.ph/'}) - - compressedstream = StringIO.StringIO(compressed_data) - gzipper = gzip.GzipFile(fileobj = compressedstream) - data = gzipper.read() - - return data - From da69c2840ac5390000af212e83737c5239e99ef6 Mon Sep 17 00:00:00 2001 From: Ruud Date: Thu, 9 Aug 2012 22:11:29 +0200 Subject: [PATCH 19/42] Wrong content tag in search bugs Opera. fix #696 --- couchpotato/core/plugins/movie/static/search.css | 1 - 1 file changed, 1 deletion(-) diff --git a/couchpotato/core/plugins/movie/static/search.css b/couchpotato/core/plugins/movie/static/search.css index 4f43ffe1..76130cb0 100644 --- a/couchpotato/core/plugins/movie/static/search.css +++ b/couchpotato/core/plugins/movie/static/search.css @@ -186,7 +186,6 @@ .movie_result .info h2 span { padding: 0 5px; - content: ")"; } .movie_result .info h2 span:before { content: "("; } From 4d90b1873b829b7609ddfc9afdd95a3f2b9896f0 Mon Sep 17 00:00:00 2001 From: Ruud Date: Thu, 9 Aug 2012 22:23:09 +0200 Subject: [PATCH 20/42] Add SD documentary category NZBMatrix. fix #698 --- couchpotato/core/providers/nzb/nzbmatrix/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/couchpotato/core/providers/nzb/nzbmatrix/main.py b/couchpotato/core/providers/nzb/nzbmatrix/main.py index c6c5be91..09514a4c 100644 --- a/couchpotato/core/providers/nzb/nzbmatrix/main.py +++ b/couchpotato/core/providers/nzb/nzbmatrix/main.py @@ -22,7 +22,7 @@ class NZBMatrix(NZBProvider, RSS): cat_ids = [ ([50], ['bd50']), ([42, 53], ['720p', '1080p']), - ([2], ['cam', 'ts', 'dvdrip', 'tc', 'r5', 'scr']), + ([2, 9], ['cam', 'ts', 'dvdrip', 'tc', 'r5', 'scr']), ([54], ['brrip']), ([1], ['dvdr']), ] From 627314dbe1f23355eaac8c794f85c9ef95b7075c Mon Sep 17 00:00:00 2001 From: Ruud Date: Fri, 10 Aug 2012 20:29:42 +0200 Subject: [PATCH 21/42] Change release info API structure --- couchpotato/core/settings/model.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/couchpotato/core/settings/model.py b/couchpotato/core/settings/model.py index 553c0357..2ecb48ae 100644 --- a/couchpotato/core/settings/model.py +++ b/couchpotato/core/settings/model.py @@ -103,6 +103,22 @@ class Release(Entity): files = ManyToMany('File', cascade = 'all, delete-orphan', single_parent = True) info = OneToMany('ReleaseInfo', cascade = 'all, delete-orphan') + def to_dict(self, deep = {}, exclude = []): + orig_dict = super(Release, self).to_dict(deep = deep, exclude = exclude) + + new_info = {} + for info in orig_dict.get('info', []): + + value = info['value'] + try: value = int(info['value']) + except: pass + + new_info[info['identifier']] = value + + orig_dict['info'] = new_info + + return orig_dict + class ReleaseInfo(Entity): """Properties that can be bound to a file for off-line usage""" From 9b6261f0b3da9fcd9d6f91910bc9a106b827cda6 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sun, 12 Aug 2012 15:17:11 +0200 Subject: [PATCH 22/42] Always check for correct quality --- couchpotato/core/plugins/searcher/main.py | 8 ++------ couchpotato/core/providers/nzb/mysterbin/main.py | 2 +- couchpotato/core/providers/nzb/newznab/main.py | 7 +++---- couchpotato/core/providers/nzb/nzbclub/main.py | 2 +- couchpotato/core/providers/nzb/nzbindex/main.py | 2 +- couchpotato/core/providers/nzb/nzbmatrix/main.py | 3 +-- .../core/providers/torrent/kickasstorrents/main.py | 2 +- couchpotato/core/providers/torrent/publichd/main.py | 2 +- couchpotato/core/providers/torrent/sceneaccess/main.py | 2 +- couchpotato/core/providers/torrent/scenehd/main.py | 2 +- couchpotato/core/providers/torrent/thepiratebay/main.py | 2 +- couchpotato/core/providers/torrent/torrentleech/main.py | 2 +- 12 files changed, 15 insertions(+), 21 deletions(-) diff --git a/couchpotato/core/plugins/searcher/main.py b/couchpotato/core/plugins/searcher/main.py index 6fc6fe4c..16e3e5ad 100644 --- a/couchpotato/core/plugins/searcher/main.py +++ b/couchpotato/core/plugins/searcher/main.py @@ -242,7 +242,6 @@ class Searcher(Plugin): def correctMovie(self, nzb = {}, movie = {}, quality = {}, **kwargs): imdb_results = kwargs.get('imdb_results', False) - single_category = kwargs.get('single_category', False) retention = Env.setting('retention', section = 'nzb') if nzb.get('seeds') is None and retention < nzb.get('age', 0): @@ -275,7 +274,7 @@ class Searcher(Plugin): preferred_quality = fireEvent('quality.single', identifier = quality['identifier'], single = True) # Contains lower quality string - if self.containsOtherQuality(nzb, movie_year = movie['library']['year'], preferred_quality = preferred_quality, single_category = single_category): + if self.containsOtherQuality(nzb, movie_year = movie['library']['year'], preferred_quality = preferred_quality): log.info('Wrong: %s, looking for %s', (nzb['name'], quality['label'])) return False @@ -327,7 +326,7 @@ class Searcher(Plugin): log.info("Wrong: %s, undetermined naming. Looking for '%s (%s)'" % (nzb['name'], movie_name, movie['library']['year'])) return False - def containsOtherQuality(self, nzb, movie_year = None, preferred_quality = {}, single_category = False): + def containsOtherQuality(self, nzb, movie_year = None, preferred_quality = {}): name = nzb['name'] size = nzb.get('size', 0) @@ -358,9 +357,6 @@ class Searcher(Plugin): if found.get(allowed): del found[allowed] - if (len(found) == 0 and single_category): - return False - return not (found.get(preferred_quality['identifier']) and len(found) == 1) def checkIMDB(self, haystack, imdbId): diff --git a/couchpotato/core/providers/nzb/mysterbin/main.py b/couchpotato/core/providers/nzb/mysterbin/main.py index 008f24f8..edf2c70e 100644 --- a/couchpotato/core/providers/nzb/mysterbin/main.py +++ b/couchpotato/core/providers/nzb/mysterbin/main.py @@ -88,7 +88,7 @@ class Mysterbin(NZBProvider): new['score'] = fireEvent('score.calculate', new, movie, single = True) is_correct_movie = fireEvent('searcher.correct_movie', nzb = new, movie = movie, quality = quality, - imdb_results = False, single_category = False, single = True) + imdb_results = False, single = True) if is_correct_movie: results.append(new) self.found(new) diff --git a/couchpotato/core/providers/nzb/newznab/main.py b/couchpotato/core/providers/nzb/newznab/main.py index ff2aba86..5485c63b 100644 --- a/couchpotato/core/providers/nzb/newznab/main.py +++ b/couchpotato/core/providers/nzb/newznab/main.py @@ -96,13 +96,12 @@ class Newznab(NZBProvider, RSS): url = "%s&%s" % (self.getUrl(host['host'], self.urls['search']), arguments) cache_key = 'newznab.%s.%s.%s' % (host['host'], movie['library']['identifier'], cat_id[0]) - single_cat = (len(cat_id) == 1 and cat_id[0] != self.cat_backup_id) - results = self.createItems(url, cache_key, host, single_cat = single_cat, movie = movie, quality = quality) + results = self.createItems(url, cache_key, host, movie = movie, quality = quality) return results - def createItems(self, url, cache_key, host, single_cat = False, movie = None, quality = None, for_feed = False): + def createItems(self, url, cache_key, host, movie = None, quality = None, for_feed = False): results = [] data = self.getCache(cache_key, url, cache_timeout = 1800, headers = {'User-Agent': Env.getIdentifier()}) @@ -146,7 +145,7 @@ class Newznab(NZBProvider, RSS): if not for_feed: is_correct_movie = fireEvent('searcher.correct_movie', nzb = new, movie = movie, quality = quality, - imdb_results = True, single_category = single_cat, single = True) + imdb_results = True, single = True) if is_correct_movie: new['score'] = fireEvent('score.calculate', new, movie, single = True) diff --git a/couchpotato/core/providers/nzb/nzbclub/main.py b/couchpotato/core/providers/nzb/nzbclub/main.py index dedddb85..d047a8a8 100644 --- a/couchpotato/core/providers/nzb/nzbclub/main.py +++ b/couchpotato/core/providers/nzb/nzbclub/main.py @@ -86,7 +86,7 @@ class NZBClub(NZBProvider, RSS): is_correct_movie = fireEvent('searcher.correct_movie', nzb = new, movie = movie, quality = quality, - imdb_results = False, single_category = False, single = True) + imdb_results = False, single = True) if is_correct_movie: new['score'] = fireEvent('score.calculate', new, movie, single = True) diff --git a/couchpotato/core/providers/nzb/nzbindex/main.py b/couchpotato/core/providers/nzb/nzbindex/main.py index 3d6384c0..ee2092c2 100644 --- a/couchpotato/core/providers/nzb/nzbindex/main.py +++ b/couchpotato/core/providers/nzb/nzbindex/main.py @@ -93,7 +93,7 @@ class NzbIndex(NZBProvider, RSS): is_correct_movie = fireEvent('searcher.correct_movie', nzb = new, movie = movie, quality = quality, - imdb_results = False, single_category = False, single = True) + imdb_results = False, single = True) if is_correct_movie: new['score'] = fireEvent('score.calculate', new, movie, single = True) diff --git a/couchpotato/core/providers/nzb/nzbmatrix/main.py b/couchpotato/core/providers/nzb/nzbmatrix/main.py index 09514a4c..9539eac1 100644 --- a/couchpotato/core/providers/nzb/nzbmatrix/main.py +++ b/couchpotato/core/providers/nzb/nzbmatrix/main.py @@ -49,7 +49,6 @@ class NZBMatrix(NZBProvider, RSS): url = "%s?%s" % (self.urls['search'], arguments) cache_key = 'nzbmatrix.%s.%s' % (movie['library'].get('identifier'), cat_ids) - single_cat = True data = self.getCache(cache_key, url, cache_timeout = 1800, headers = {'User-Agent': Env.getIdentifier()}) if data: @@ -86,7 +85,7 @@ class NZBMatrix(NZBProvider, RSS): is_correct_movie = fireEvent('searcher.correct_movie', nzb = new, movie = movie, quality = quality, - imdb_results = True, single_category = single_cat, single = True) + imdb_results = True, single = True) if is_correct_movie: new['score'] = fireEvent('score.calculate', new, movie, single = True) diff --git a/couchpotato/core/providers/torrent/kickasstorrents/main.py b/couchpotato/core/providers/torrent/kickasstorrents/main.py index e1440c2a..84a679f2 100644 --- a/couchpotato/core/providers/torrent/kickasstorrents/main.py +++ b/couchpotato/core/providers/torrent/kickasstorrents/main.py @@ -89,7 +89,7 @@ class KickAssTorrents(TorrentProvider): new['score'] += fireEvent('score.calculate', new, movie, single = True) is_correct_movie = fireEvent('searcher.correct_movie', nzb = new, movie = movie, quality = quality, - imdb_results = True, single_category = False, single = True) + imdb_results = True, single = True) if is_correct_movie: results.append(new) self.found(new) diff --git a/couchpotato/core/providers/torrent/publichd/main.py b/couchpotato/core/providers/torrent/publichd/main.py index 14bfab59..cf0e6878 100644 --- a/couchpotato/core/providers/torrent/publichd/main.py +++ b/couchpotato/core/providers/torrent/publichd/main.py @@ -70,7 +70,7 @@ class PublicHD(TorrentProvider): new['score'] = fireEvent('score.calculate', new, movie, single = True) is_correct_movie = fireEvent('searcher.correct_movie', nzb = new, movie = movie, quality = quality, - imdb_results = False, single_category = False, single = True) + imdb_results = False, single = True) if is_correct_movie: results.append(new) diff --git a/couchpotato/core/providers/torrent/sceneaccess/main.py b/couchpotato/core/providers/torrent/sceneaccess/main.py index 62cd7dcd..39598d61 100644 --- a/couchpotato/core/providers/torrent/sceneaccess/main.py +++ b/couchpotato/core/providers/torrent/sceneaccess/main.py @@ -86,7 +86,7 @@ class SceneAccess(TorrentProvider): new['score'] = fireEvent('score.calculate', new, movie, single = True) is_correct_movie = fireEvent('searcher.correct_movie', nzb = new, movie = movie, quality = quality, - imdb_results = False, single_category = False, single = True) + imdb_results = False, single = True) if is_correct_movie: results.append(new) diff --git a/couchpotato/core/providers/torrent/scenehd/main.py b/couchpotato/core/providers/torrent/scenehd/main.py index 89d847d2..596cb5bb 100644 --- a/couchpotato/core/providers/torrent/scenehd/main.py +++ b/couchpotato/core/providers/torrent/scenehd/main.py @@ -79,7 +79,7 @@ class SceneHD(TorrentProvider): new['score'] = fireEvent('score.calculate', new, movie, single = True) is_correct_movie = fireEvent('searcher.correct_movie', nzb = new, movie = movie, quality = quality, - imdb_results = imdb_results, single_category = False, single = True) + imdb_results = imdb_results, single = True) if is_correct_movie: results.append(new) diff --git a/couchpotato/core/providers/torrent/thepiratebay/main.py b/couchpotato/core/providers/torrent/thepiratebay/main.py index cdb86a75..7b102958 100644 --- a/couchpotato/core/providers/torrent/thepiratebay/main.py +++ b/couchpotato/core/providers/torrent/thepiratebay/main.py @@ -120,7 +120,7 @@ class ThePirateBay(TorrentProvider): new['score'] = fireEvent('score.calculate', new, movie, single = True) is_correct_movie = fireEvent('searcher.correct_movie', nzb = new, movie = movie, quality = quality, - imdb_results = False, single_category = False, single = True) + imdb_results = False, single = True) if is_correct_movie: results.append(new) diff --git a/couchpotato/core/providers/torrent/torrentleech/main.py b/couchpotato/core/providers/torrent/torrentleech/main.py index c174a3d4..0cfc7ce3 100644 --- a/couchpotato/core/providers/torrent/torrentleech/main.py +++ b/couchpotato/core/providers/torrent/torrentleech/main.py @@ -80,7 +80,7 @@ class TorrentLeech(TorrentProvider): new['score'] = fireEvent('score.calculate', new, movie, single = True) is_correct_movie = fireEvent('searcher.correct_movie', nzb = new, movie = movie, quality = quality, - imdb_results = imdb_results, single_category = False, single = True) + imdb_results = imdb_results, single = True) if is_correct_movie: results.append(new) From f63e332861bdf149b56198c567a9b6b8a3f6b6ef Mon Sep 17 00:00:00 2001 From: Ruud Date: Sun, 12 Aug 2012 23:53:53 +0200 Subject: [PATCH 23/42] Cleanup of automated failed download detection. --- couchpotato/core/downloaders/base.py | 7 +- .../core/downloaders/sabnzbd/__init__.py | 5 +- couchpotato/core/downloaders/sabnzbd/main.py | 34 ++--- .../core/plugins/movie/static/movie.css | 26 ++++ .../core/plugins/movie/static/movie.js | 121 +++++++++++++++--- couchpotato/core/plugins/searcher/__init__.py | 3 +- couchpotato/core/plugins/searcher/main.py | 96 ++++++++------ couchpotato/core/plugins/status/main.py | 1 + couchpotato/static/scripts/couchpotato.js | 21 +-- couchpotato/static/scripts/page/wanted.js | 3 +- 10 files changed, 228 insertions(+), 89 deletions(-) diff --git a/couchpotato/core/downloaders/base.py b/couchpotato/core/downloaders/base.py index 1be9dccc..d5f1c422 100644 --- a/couchpotato/core/downloaders/base.py +++ b/couchpotato/core/downloaders/base.py @@ -13,16 +13,17 @@ class Downloader(Plugin): def __init__(self): addEvent('download', self.download) - addEvent('getdownloadstatus', self.getdownloadstatus) + addEvent('download.status', self.getDownloadStatus) def download(self, data = {}, movie = {}, manual = False, filedata = None): pass - def getdownloadstatus(self, data = {}, movie = {}): + def getDownloadStatus(self, data = {}, movie = {}): pass def createNzbName(self, data, movie): - return '%s%s' % (toSafeString(data.get('name')[:40]), self.cpTag(movie)) + tag = self.cpTag(movie) + return '%s%s' % (toSafeString(data.get('name')[:127 - len(tag)]), tag) def createFileName(self, data, filedata, movie): name = os.path.join(self.createNzbName(data, movie)) diff --git a/couchpotato/core/downloaders/sabnzbd/__init__.py b/couchpotato/core/downloaders/sabnzbd/__init__.py index 1baaea0d..927a9ff2 100644 --- a/couchpotato/core/downloaders/sabnzbd/__init__.py +++ b/couchpotato/core/downloaders/sabnzbd/__init__.py @@ -41,11 +41,10 @@ config = [{ 'description': 'Disable this downloader for automated searches, but use it when I manually send a release.', }, { - 'name': 'delete failed', + 'name': 'delete_failed', 'default': True, 'type': 'bool', - 'advanced': True, - 'description': 'Delete a release after it\'s download failed.', + 'description': 'Delete a release after the download has failed.', }, ], } diff --git a/couchpotato/core/downloaders/sabnzbd/main.py b/couchpotato/core/downloaders/sabnzbd/main.py index 4144b279..e174259d 100644 --- a/couchpotato/core/downloaders/sabnzbd/main.py +++ b/couchpotato/core/downloaders/sabnzbd/main.py @@ -44,7 +44,7 @@ class Sabnzbd(Downloader): else: sab = self.urlopen(url, timeout = 60, show_error = False) except: - log.error(traceback.format_exc()) + log.error('Failed sending release: %s', traceback.format_exc()) return False result = sab.strip() @@ -63,7 +63,7 @@ class Sabnzbd(Downloader): log.error("Unknown error: " + result[:40]) return False - def getdownloadstatus(self, data = {}, movie = {}): + def getDownloadStatus(self, data = {}, movie = {}): if self.isDisabled(manual = True) or not self.isCorrectType(data.get('type')): return @@ -81,20 +81,21 @@ class Sabnzbd(Downloader): try: sab = self.urlopen(url, timeout = 60, show_error = False) except: - log.error(traceback.format_exc()) - return + log.error('Failed checking status: %s', traceback.format_exc()) + return False + try: history = json.loads(sab) except: log.debug("Result text from SAB: " + sab[:40]) - log.error(traceback.format_exc()) - return + log.error('Failed parsing json status: %s', traceback.format_exc()) + return False for slot in history['queue']['slots']: if slot['cat'] == self.conf('category'): log.debug('Found %s in SabNZBd queue, which is %s, with %s left', (slot['filename'], slot['status'], slot['timeleft'])) if slot['filename'] == nzbname: - return slot['status'] + return slot['status'].lower() # Go through history items params = { @@ -107,13 +108,14 @@ class Sabnzbd(Downloader): try: sab = self.urlopen(url, timeout = 60, show_error = False) except: - log.error(traceback.format_exc()) + log.error('Failed getting history: %s', traceback.format_exc()) return + try: history = json.loads(sab) except: log.debug("Result text from SAB: " + sab[:40]) - log.error(traceback.format_exc()) + log.error('Failed parsing history json: %s', traceback.format_exc()) return for slot in history['history']['slots']: @@ -123,7 +125,8 @@ class Sabnzbd(Downloader): if slot['status'] == 'Failed' or 'fail' in slot['fail_message'].lower(): # Delete failed download - if self.conf('delete failed', default = True): + if self.conf('delete_failed', default = True): + log.info('%s failed downloading, deleting...', slot['name']) params = { 'apikey': self.conf('api_key'), @@ -133,10 +136,11 @@ class Sabnzbd(Downloader): 'value': slot['nzo_id'] } url = cleanHost(self.conf('host')) + "api?" + tryUrlencode(params) + try: sab = self.urlopen(url, timeout = 60, show_error = False) except: - log.error(traceback.format_exc()) + log.error('Failed deleting: %s', traceback.format_exc()) return False result = sab.strip() @@ -147,12 +151,12 @@ class Sabnzbd(Downloader): if result == "ok": log.info('SabNZBd deleted failed release %s successfully.', slot['name']) elif result == "Missing authentication": - log.error("Incorrect username/password.") + log.error("Incorrect username/password or API?.") else: log.error("Unknown error: " + result[:40]) - return 'Failed' + return 'failed' else: - return slot['status'] + return slot['status'].lower() - return 'Not found' + return 'not_found' diff --git a/couchpotato/core/plugins/movie/static/movie.css b/couchpotato/core/plugins/movie/static/movie.css index c30ee99a..f809c579 100644 --- a/couchpotato/core/plugins/movie/static/movie.css +++ b/couchpotato/core/plugins/movie/static/movie.css @@ -340,6 +340,32 @@ .movies .movie .hide_trailer.hide { top: -30px; } + + .movies .movie .try_container { + padding: 5px 10px; + text-align: center; + } + + .movies .movie .try_container a { + margin: 0 5px; + padding: 2px 5px; + } + + .movies .movie .releases .next_release { + border-left: 6px solid #2aa300; + } + + .movies .movie .releases .next_release > :first-child { + margin-left: -6px; + } + + .movies .movie .releases .last_release { + border-left: 6px solid #ffa200; + } + + .movies .movie .releases .last_release > :first-child { + margin-left: -6px; + } .movies .load_more { display: block; diff --git a/couchpotato/core/plugins/movie/static/movie.js b/couchpotato/core/plugins/movie/static/movie.js index 92a66efd..2182b887 100644 --- a/couchpotato/core/plugins/movie/static/movie.js +++ b/couchpotato/core/plugins/movie/static/movie.js @@ -146,7 +146,7 @@ var Movie = new Class({ }); // Add done releases - Array.each(self.data.releases, function(release){ + self.data.releases.each(function(release){ var q = self.quality.getElement('.q_id'+ release.quality_id), status = Status.get(release.status_id); @@ -159,9 +159,9 @@ var Movie = new Class({ }); Object.each(self.options.actions, function(action, key){ - self.actions.adopt( - self.action[key.toLowerCase()] = new self.options.actions[key](self) - ) + self.action[key.toLowerCase()] = action = new self.options.actions[key](self) + if(action.el) + self.actions.adopt(action) }); if(!self.data.library.rating) @@ -280,6 +280,31 @@ var MovieAction = new Class({ this.el.removeClass('disable') }, + createMask: function(){ + var self = this; + self.mask = new Element('div.mask', { + 'styles': { + 'z-index': '1' + } + }).inject(self.movie, 'top').fade('hide'); + self.positionMask(); + }, + + positionMask: function(){ + var self = this, + movie = $(self.movie), + s = movie.getSize() + + return; + + return self.mask.setStyles({ + 'width': s.x, + 'height': s.y + }).position({ + 'relativeTo': movie + }) + }, + toElement: function(){ return this.el || null } @@ -318,13 +343,10 @@ var IMDBAction = new Class({ var ReleaseAction = new Class({ Extends: MovieAction, - id: null, create: function(){ var self = this; - self.id = self.movie.get('identifier'); - self.el = new Element('a.releases.icon.download', { 'title': 'Show the releases that are available for ' + self.movie.getTitle(), 'events': { @@ -332,15 +354,33 @@ var ReleaseAction = new Class({ } }); + var buttons_done = false; + + self.movie.data.releases.sortBy('-info.score').each(function(release){ + if(buttons_done) return; + + var status = Status.get(release.status_id); + + if((status.identifier == 'ignored' || status.identifier == 'failed') || (!self.next_release && status.identifier == 'available')){ + self.hide_on_click = false; + self.show(); + buttons_done = true; + } + + }); + }, show: function(e){ var self = this; - (e).preventDefault(); + if(e) + (e).preventDefault(); if(!self.options_container){ self.options_container = new Element('div.options').adopt( - self.release_container = new Element('div.releases.table') + self.release_container = new Element('div.releases.table').adopt( + self.trynext_container = new Element('div.buttons.try_container') + ) ).inject(self.movie, 'top'); // Header @@ -354,7 +394,7 @@ var ReleaseAction = new Class({ new Element('span.provider', {'text': 'Provider'}) ).inject(self.release_container) - Array.each(self.movie.data.releases, function(release){ + self.movie.data.releases.sortBy('-info.score').each(function(release){ var status = Status.get(release.status_id), quality = Quality.getProfile(release.quality_id) || {}, @@ -364,8 +404,18 @@ var ReleaseAction = new Class({ var details_url = info.filter(function(item){ return item.identifier == 'detail_url' }).pick().value; } catch(e){} + if( status.identifier == 'ignored' || status.identifier == 'failed'){ + self.last_release = release; + } + else if(!self.next_release && status.identifier == 'available'){ + self.next_release = release; + } + + // Create release new Element('div', { - 'class': 'item '+status.identifier, + 'class': 'item '+status.identifier + + (self.next_release && self.next_release.id == release.id ? ' next_release' : '') + + (self.last_release && self.last_release.id == release.id ? ' last_release' : ''), 'id': 'release_'+release.id }).adopt( new Element('span.name', {'text': self.get(release, 'name'), 'title': self.get(release, 'name')}), @@ -400,17 +450,37 @@ var ReleaseAction = new Class({ ).inject(self.release_container) }); + self.trynext_container.adopt( + new Element('span.or', { + 'text': 'Download' + }), + self.last_release ? new Element('a.button.orange', { + 'text': 'the same release again', + 'events': { + 'click': self.trySameRelease.bind(self) + } + }) : null, + self.next_release && self.last_release ? new Element('span.or', { + 'text': 'or' + }) : null, + self.next_release ? [new Element('a.button.green', { + 'text': self.last_release ? 'another release' : 'the best release', + 'events': { + 'click': self.tryNextRelease.bind(self) + } + }), + new Element('span.or', { + 'text': 'or pick one below' + })] : null + ) + } self.movie.slide('in', self.options_container); }, get: function(release, type){ - var self = this; - - return (release.info.filter(function(info){ - return type == info.identifier - }).pick() || {}).value || 'n/a' + return release.info[type] || 'n/a' }, download: function(release){ @@ -444,6 +514,25 @@ var ReleaseAction = new Class({ } }) + }, + + tryNextRelease: function(movie_id){ + var self = this; + + if(self.last_release) + self.ignore(self.last_release); + + if(self.next_release) + self.download(self.next_release); + + }, + + trySameRelease: function(movie_id){ + var self = this; + + if(self.last_release) + self.download(self.last_release); + } }); diff --git a/couchpotato/core/plugins/searcher/__init__.py b/couchpotato/core/plugins/searcher/__init__.py index 2d82b61c..e2c2bb83 100644 --- a/couchpotato/core/plugins/searcher/__init__.py +++ b/couchpotato/core/plugins/searcher/__init__.py @@ -49,10 +49,9 @@ config = [{ 'description': 'Detect movie status every X minutes. Will start the renamer if movie is completed or handle failed download if these options are enabled', }, { - 'name': 'failed download', + 'name': 'next_on_failed', 'default': True, 'type': 'bool', - 'advanced': True, 'description': 'Try the next best release for a movie after a download failed.', }, ], diff --git a/couchpotato/core/plugins/searcher/main.py b/couchpotato/core/plugins/searcher/main.py index 16e3e5ad..1d966ee8 100644 --- a/couchpotato/core/plugins/searcher/main.py +++ b/couchpotato/core/plugins/searcher/main.py @@ -1,6 +1,8 @@ from couchpotato import get_session +from couchpotato.api import addApiView from couchpotato.core.event import addEvent, fireEvent from couchpotato.core.helpers.encoding import simplifyString, toUnicode +from couchpotato.core.helpers.request import jsonified, getParam from couchpotato.core.helpers.variable import md5, getImdb, getTitle from couchpotato.core.logger import CPLog from couchpotato.core.plugins.base import Plugin @@ -25,11 +27,15 @@ class Searcher(Plugin): addEvent('searcher.single', self.single) addEvent('searcher.correct_movie', self.correctMovie) addEvent('searcher.download', self.download) - addEvent('searcher.checksnatched', self.checksnatched) + addEvent('searcher.check_snatched', self.checkSnatched) + + addApiView('searcher.try_next', self.tryNextReleaseView, docs = { + 'desc': 'Try next best release', + }) # Schedule cronjob fireEvent('schedule.cron', 'searcher.all', self.all_movies, day = self.conf('cron_day'), hour = self.conf('cron_hour'), minute = self.conf('cron_minute')) - fireEvent('schedule.interval', 'searcher.checksnatched', self.checksnatched, minutes = self.conf('run_every')) + fireEvent('schedule.interval', 'searcher.check_snatched', self.checkSnatched, minutes = self.conf('run_every')) def all_movies(self): @@ -439,14 +445,17 @@ class Searcher(Plugin): return False - def checksnatched(self): + def checkSnatched(self): snatched_status = fireEvent('status.get', 'snatched', single = True) ignored_status = fireEvent('status.get', 'ignored', single = True) + failed_status = fireEvent('status.get', 'failed', single = True) db = get_session() rels = db.query(Release).filter_by(status_id = snatched_status.get('id')) - log.info('Checking status snatched releases...') + if rels: + log.debug('Checking status snatched releases...') + scanrequired = False for rel in rels: @@ -462,57 +471,66 @@ class Searcher(Plugin): for info in rel.info: item[info.identifier] = info.value - movie = rel.movie.to_dict({ - 'profile': {'types': {'quality': {}}}, - 'releases': {'status': {}, 'quality': {}}, - 'library': {'titles': {}, 'files':{}}, - 'files': {} - }) + movie_dict = fireEvent('movie.get', rel.movie_id, single = True) # check status - downloadstatus = fireEvent('getdownloadstatus', data = item, movie = movie) - if not downloadstatus: # Downloader not compatible with download status + downloadstatus = fireEvent('download.status', data = item, movie = movie_dict, single = True) + if not downloadstatus: log.debug('Download status functionality is not implemented for active downloaders.') scanrequired = True - else: - log.debug('Download status: %s' , downloadstatus[0]) + log.debug('Download status: %s' , downloadstatus) - if downloadstatus[0] == 'Failed': - # if failed set status to ignored - rel.status_id = ignored_status.get('id') - db.commit() - - # search/download again - # if downloaded manually: # this is currently not stored... - # log.info('Download of %s failed...', item['name']) - # return - - if self.conf('failed download', default = True): - - #update movie to reflect release status update - movie = rel.movie.to_dict({ - 'profile': {'types': {'quality': {}}}, - 'releases': {'status': {}, 'quality': {}}, - 'library': {'titles': {}, 'files':{}}, - 'files': {} - }) - log.info('Download of %s failed, trying next release...', item['name']) - fireEvent('searcher.single', movie) + if downloadstatus == 'failed': + if self.conf('next_on_failed'): + self.tryNextRelease(rel.movie_id) else: + rel.status_id = failed_status.get('id') + db.commit() + log.info('Download of %s failed.', item['name']) - elif downloadstatus[0] == 'Completed': + elif downloadstatus == 'completed': log.info('Download of %s completed!', item['name']) scanrequired = True - elif downloadstatus[0] == 'Not found': + elif downloadstatus == 'not_found': log.info('%s not found in downloaders', item['name']) rel.status_id = ignored_status.get('id') db.commit() - # Note that Queued, Downloading, Paused, Repair and Unpackimg are also available as status for SabNZBd + # Note that Queued, Downloading, Paused, Repair and Unpackimg are also available as status for SabNZBd if scanrequired: fireEvent('renamer.scan') - return + def tryNextReleaseView(self): + + trynext = self.tryNextRelease(getParam('id')) + + return jsonified({ + 'success': trynext + }) + + def tryNextRelease(self, movie_id, manual = False): + + snatched_status = fireEvent('status.get', 'snatched', single = True) + ignored_status = fireEvent('status.get', 'ignored', single = True) + + try: + movie_dict = fireEvent('movie.get', movie_id, single = True) + + db = get_session() + rels = db.query(Release).filter_by(status_id = snatched_status.get('id')) + + for rel in rels: + rel.status_id = ignored_status.get('id') + db.commit() + + log.info('Trying next release for', getTitle(movie_dict['library'])) + fireEvent('searcher.single', movie_dict) + + return True + + except: + log.error('Failed searching for next release: %s', traceback.format_exc()) + return False diff --git a/couchpotato/core/plugins/status/main.py b/couchpotato/core/plugins/status/main.py index af2e8792..91c2858f 100644 --- a/couchpotato/core/plugins/status/main.py +++ b/couchpotato/core/plugins/status/main.py @@ -19,6 +19,7 @@ class StatusPlugin(Plugin): 'downloaded': 'Downloaded', 'wanted': 'Wanted', 'snatched': 'Snatched', + 'failed': 'Failed', 'deleted': 'Deleted', 'ignored': 'Ignored', } diff --git a/couchpotato/static/scripts/couchpotato.js b/couchpotato/static/scripts/couchpotato.js index c0860a99..b99e6c7e 100644 --- a/couchpotato/static/scripts/couchpotato.js +++ b/couchpotato/static/scripts/couchpotato.js @@ -419,15 +419,18 @@ function randomString(length, extra) { return 0; }; - Array.implement('sortBy', function(){ - keyPaths.empty(); - Array.each(arguments, function(argument) { - switch (typeOf(argument)) { - case "array": saveKeyPath(argument); break; - case "string": saveKeyPath(argument.match(/[+-]|[^.]+/g)); break; - } - }); - return this.sort(comparer); + Array.implement({ + sortBy: function(){ + keyPaths.empty(); + + Array.each(arguments, function(argument) { + switch (typeOf(argument)) { + case "array": saveKeyPath(argument); break; + case "string": saveKeyPath(argument.match(/[+-]|[^.]+/g)); break; + } + }); + return this.sort(comparer); + } }); })(); diff --git a/couchpotato/static/scripts/page/wanted.js b/couchpotato/static/scripts/page/wanted.js index 5f2dcf7e..2ce6c14c 100644 --- a/couchpotato/static/scripts/page/wanted.js +++ b/couchpotato/static/scripts/page/wanted.js @@ -31,7 +31,6 @@ window.addEvent('domready', function(){ 'IMDB': IMDBAction ,'Trailer': TrailerAction ,'Releases': ReleaseAction - ,'Edit': new Class({ Extends: MovieAction, @@ -170,7 +169,7 @@ window.addEvent('domready', function(){ (e).preventDefault(); if(!self.delete_container){ - self.delete_container = new Element('div.delete_container').adopt( + self.delete_container = new Element('div.buttons.delete_container').adopt( new Element('a.cancel', { 'text': 'Cancel', 'events': { From de3832b7b68cc3265aa05149728e88945af2c4e3 Mon Sep 17 00:00:00 2001 From: Ruud Date: Mon, 13 Aug 2012 21:50:31 +0200 Subject: [PATCH 24/42] Filter try_next on movie. --- couchpotato/core/plugins/searcher/main.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/couchpotato/core/plugins/searcher/main.py b/couchpotato/core/plugins/searcher/main.py index 1d966ee8..01358ee8 100644 --- a/couchpotato/core/plugins/searcher/main.py +++ b/couchpotato/core/plugins/searcher/main.py @@ -30,7 +30,10 @@ class Searcher(Plugin): addEvent('searcher.check_snatched', self.checkSnatched) addApiView('searcher.try_next', self.tryNextReleaseView, docs = { - 'desc': 'Try next best release', + 'desc': 'Marks the snatched results as ignored and try the next best release', + 'params': { + 'id': {'desc': 'The id of the movie'}, + }, }) # Schedule cronjob @@ -520,13 +523,16 @@ class Searcher(Plugin): movie_dict = fireEvent('movie.get', movie_id, single = True) db = get_session() - rels = db.query(Release).filter_by(status_id = snatched_status.get('id')) + rels = db.query(Release).filter_by( + status_id = snatched_status.get('id'), + movie_id = movie_id + ).all() for rel in rels: rel.status_id = ignored_status.get('id') - db.commit() + db.commit() - log.info('Trying next release for', getTitle(movie_dict['library'])) + log.info('Trying next release for: %s', getTitle(movie_dict['library'])) fireEvent('searcher.single', movie_dict) return True From 2d6c31af970bddcfb3e6bb60d41816f1ba33fdba Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Sat, 18 Aug 2012 19:52:07 +0200 Subject: [PATCH 25/42] Some failure messages do not contain 'fail'. No message at all means success. --- couchpotato/core/downloaders/sabnzbd/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/couchpotato/core/downloaders/sabnzbd/main.py b/couchpotato/core/downloaders/sabnzbd/main.py index e174259d..86d5459d 100644 --- a/couchpotato/core/downloaders/sabnzbd/main.py +++ b/couchpotato/core/downloaders/sabnzbd/main.py @@ -122,7 +122,7 @@ class Sabnzbd(Downloader): if slot['category'] == self.conf('category'): log.debug('Found %s in SabNZBd history, which has %s', (slot['name'], slot['status'])) if slot['name'] == nzbname: - if slot['status'] == 'Failed' or 'fail' in slot['fail_message'].lower(): + if slot['status'] == 'Failed' or slot['fail_message'].strip(): # Delete failed download if self.conf('delete_failed', default = True): From a9545e4fa50017be6423cdbfddc009e031f6ce47 Mon Sep 17 00:00:00 2001 From: Ruud Date: Fri, 24 Aug 2012 16:08:01 +0200 Subject: [PATCH 26/42] Remove paypal link --- couchpotato/static/scripts/page/about.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/couchpotato/static/scripts/page/about.js b/couchpotato/static/scripts/page/about.js index 93687b49..0b346799 100644 --- a/couchpotato/static/scripts/page/about.js +++ b/couchpotato/static/scripts/page/about.js @@ -100,16 +100,7 @@ var AboutSettingTab = new Class({ new Element('li[text=No speed or download limits]'), new Element('li[text=Free SSL Encrypted connections]') ) - ), - new Element('div.donate', { - 'html': - 'Or, buy me a (24 pack) Pepsi, for while I\'m coding ;)' + - '
' + - '' + - '' + - '' + - '
' - }) + ) ); }, From c423cd1f79988bbe190c22d6548647469794a1ef Mon Sep 17 00:00:00 2001 From: Ruud Date: Fri, 24 Aug 2012 16:23:42 +0200 Subject: [PATCH 27/42] Load new "thanks" frame in about page --- couchpotato/static/scripts/page/about.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/couchpotato/static/scripts/page/about.js b/couchpotato/static/scripts/page/about.js index 0b346799..0a966672 100644 --- a/couchpotato/static/scripts/page/about.js +++ b/couchpotato/static/scripts/page/about.js @@ -100,7 +100,12 @@ var AboutSettingTab = new Class({ new Element('li[text=No speed or download limits]'), new Element('li[text=Free SSL Encrypted connections]') ) - ) + ), + new Element('div.donate', { + 'html': + 'Or support me via:' + + '' + }) ); }, From 6e2380f203669f4abb46202d93ef74e0a8bf1cc2 Mon Sep 17 00:00:00 2001 From: Ruud Date: Fri, 24 Aug 2012 16:35:50 +0200 Subject: [PATCH 28/42] NMA fix Thanks @Mochaka --- .../core/notifications/notifymyandroid/main.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/couchpotato/core/notifications/notifymyandroid/main.py b/couchpotato/core/notifications/notifymyandroid/main.py index a6a316da..7ac5936c 100644 --- a/couchpotato/core/notifications/notifymyandroid/main.py +++ b/couchpotato/core/notifications/notifymyandroid/main.py @@ -15,11 +15,16 @@ class NotifyMyAndroid(Notification): nma.addkey(keys) nma.developerkey(self.conf('dev_key')) - # hacky fix for the event type + # hacky fix for the event type # as it seems to be part of the message now self.event = message.split(' ')[0] - - response = nma.push(self.default_title, self.event , message, self.conf('priority'), batch_mode = len(keys) > 1) + response = nma.push( + application = self.default_title, + event = self.event, + description = message, + priority = self.conf('priority'), + batch_mode = len(keys) > 1 + ) for key in keys: if not response[str(key)]['code'] == u'200': From 7ee04e3d18113fb95471d27960e645e510779e6e Mon Sep 17 00:00:00 2001 From: Ruud Date: Fri, 24 Aug 2012 17:41:16 +0200 Subject: [PATCH 29/42] nzbrus: Found some dvdrips in the misc category so adding to lookup --- .../core/providers/nzb/nzbsrus/__init__.py | 39 ++++++ .../core/providers/nzb/nzbsrus/main.py | 111 ++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 couchpotato/core/providers/nzb/nzbsrus/__init__.py create mode 100644 couchpotato/core/providers/nzb/nzbsrus/main.py diff --git a/couchpotato/core/providers/nzb/nzbsrus/__init__.py b/couchpotato/core/providers/nzb/nzbsrus/__init__.py new file mode 100644 index 00000000..96322e18 --- /dev/null +++ b/couchpotato/core/providers/nzb/nzbsrus/__init__.py @@ -0,0 +1,39 @@ +from .main import Nzbsrus + +def start(): + return Nzbsrus() + +config = [{ + 'name': 'nzbsrus', + 'groups': [ + { + 'tab': 'searcher', + 'subtab': 'providers', + 'name': 'nzbsrus', + 'label': 'Nzbsrus', + 'wizard': True, + 'options': [ + { + 'name': 'enabled', + 'type': 'enabler', + }, + { + 'name': 'userid', + 'label': 'User ID', + }, + { + 'name': 'api_key', + 'default': '', + 'label': 'Api Key', + }, + { + 'name': 'english_only', + 'default': 1, + 'type': 'bool', + 'label': 'English only', + 'description': 'Only search for English spoken movies on Nzbsrus', + }, + ], + }, + ], +}] diff --git a/couchpotato/core/providers/nzb/nzbsrus/main.py b/couchpotato/core/providers/nzb/nzbsrus/main.py new file mode 100644 index 00000000..25836919 --- /dev/null +++ b/couchpotato/core/providers/nzb/nzbsrus/main.py @@ -0,0 +1,111 @@ +from couchpotato.core.event import fireEvent +from couchpotato.core.helpers.encoding import tryUrlencode +from couchpotato.core.helpers.rss import RSS +from couchpotato.core.logger import CPLog +from couchpotato.core.providers.nzb.base import NZBProvider +from couchpotato.environment import Env +import time +import xml.etree.ElementTree as XMLTree + +log = CPLog(__name__) + +# See the api http://www.nzbsrus.com/NzbsrusAPI.pdf +class Nzbsrus(NZBProvider, RSS): + + urls = { + 'download': 'https://www.nzbsrus.com/nzbdownload_rss.php/%s', + 'detail': 'https://www.nzbsrus.com/nzbdetails.php?id=%s', + 'search': 'https://www.nzbsrus.com/api.php?extended=1&xml=1&listname={date,grabs}', + } + + cat_ids = [ + ([90,45,51], ['720p', '1080p','brrip','bd50','dvdr']), + ([48,51], ['cam', 'ts', 'dvdrip', 'tc', 'r5', 'scr']), + + + + ] + cat_backup_id = 240 + + def search(self, movie, quality): + + results = [] + + if self.isDisabled(): + return results + + cat_id_string = '&'.join(['c%s=1' % x for x in self.getCatId(quality.get('identifier'))]) + + arguments = tryUrlencode({ + 'searchtext': 'imdb:'+movie['library']['identifier'][2:], + 'uid': self.conf('userid'), + 'key': self.conf('api_key'), + 'age': Env.setting('retention', section = 'nzb'), + + }) + # check for english_only + if self.conf('english_only'): + arguments += "&lang0=1&lang3=1&lang1=1" + + url = "%s&%s&%s" % (self.urls['search'], arguments ,cat_id_string) + + cache_key = 'nzbsrus_1.%s.%s' % (movie['library'].get('identifier'), cat_id_string) + single_cat = True + + data = self.getCache(cache_key, url, cache_timeout = 1800, headers = {'User-Agent': Env.getIdentifier()}) + if data: + try: + try: + data = XMLTree.fromstring(data) + nzbs = self.getElements(data, 'results/result') + except Exception, e: + log.debug('%s, %s', (self.getName(), e)) + return results + + for nzb in nzbs: + + title = self.getTextElement(nzb, "name") + if 'error' in title.lower(): continue + + id = self.getTextElement(nzb, "id") + size = int(round(int(self.getTextElement(nzb, "size")) / 1048576)) + age = int(round(( time.time() - int(self.getTextElement(nzb, "postdate")) ) / 86400 )) + + + new = { + 'id': id, + 'type': 'nzb', + 'provider': self.getName(), + 'name': title, + 'age': age, + 'size': size, + 'url': self.urls['download'] % id + self.getApiExt() + self.getTextElement(nzb, "key"), + 'download': self.download, + 'detail_url': self.urls['detail'] % id, + 'description': self.getTextElement(nzb, "addtext"), + 'check_nzb': True, + } + + is_correct_movie = fireEvent('searcher.correct_movie', + nzb = new, movie = movie, quality = quality, + imdb_results = True, single_category = single_cat, single = True) + + if is_correct_movie: + new['score'] = fireEvent('score.calculate', new, movie, single = True) + results.append(new) + self.found(new) + + return results + except SyntaxError: + log.error('Failed to parse XML response from Nzbsrus.com') + + return results + + def download(self, url = '', nzb_id = ''): + return self.urlopen(url, headers = {'User-Agent': Env.getIdentifier()}) + + def getApiExt(self): + return '/%s/' % (self.conf('userid')) + + def isEnabled(self): + return NZBProvider.isEnabled(self) and self.conf('userid') and self.conf('api_key') From 3f35cba818f211f50b8e5c481872a5f8db2e5cad Mon Sep 17 00:00:00 2001 From: Ruud Date: Fri, 24 Aug 2012 17:57:56 +0200 Subject: [PATCH 30/42] NZBsRus cleanup --- .../core/providers/nzb/nzbsrus/__init__.py | 1 + .../core/providers/nzb/nzbsrus/main.py | 21 +++++++------------ 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/couchpotato/core/providers/nzb/nzbsrus/__init__.py b/couchpotato/core/providers/nzb/nzbsrus/__init__.py index 96322e18..70f3e3f0 100644 --- a/couchpotato/core/providers/nzb/nzbsrus/__init__.py +++ b/couchpotato/core/providers/nzb/nzbsrus/__init__.py @@ -11,6 +11,7 @@ config = [{ 'subtab': 'providers', 'name': 'nzbsrus', 'label': 'Nzbsrus', + 'description': 'See NZBsRus', 'wizard': True, 'options': [ { diff --git a/couchpotato/core/providers/nzb/nzbsrus/main.py b/couchpotato/core/providers/nzb/nzbsrus/main.py index 25836919..20a51d56 100644 --- a/couchpotato/core/providers/nzb/nzbsrus/main.py +++ b/couchpotato/core/providers/nzb/nzbsrus/main.py @@ -9,7 +9,6 @@ import xml.etree.ElementTree as XMLTree log = CPLog(__name__) -# See the api http://www.nzbsrus.com/NzbsrusAPI.pdf class Nzbsrus(NZBProvider, RSS): urls = { @@ -19,11 +18,8 @@ class Nzbsrus(NZBProvider, RSS): } cat_ids = [ - ([90,45,51], ['720p', '1080p','brrip','bd50','dvdr']), - ([48,51], ['cam', 'ts', 'dvdrip', 'tc', 'r5', 'scr']), - - - + ([90, 45, 51], ['720p', '1080p', 'brrip', 'bd50', 'dvdr']), + ([48, 51], ['cam', 'ts', 'dvdrip', 'tc', 'r5', 'scr']), ] cat_backup_id = 240 @@ -37,17 +33,18 @@ class Nzbsrus(NZBProvider, RSS): cat_id_string = '&'.join(['c%s=1' % x for x in self.getCatId(quality.get('identifier'))]) arguments = tryUrlencode({ - 'searchtext': 'imdb:'+movie['library']['identifier'][2:], + 'searchtext': 'imdb:' + movie['library']['identifier'][2:], 'uid': self.conf('userid'), 'key': self.conf('api_key'), 'age': Env.setting('retention', section = 'nzb'), }) + # check for english_only if self.conf('english_only'): arguments += "&lang0=1&lang3=1&lang1=1" - url = "%s&%s&%s" % (self.urls['search'], arguments ,cat_id_string) + url = "%s&%s&%s" % (self.urls['search'], arguments , cat_id_string) cache_key = 'nzbsrus_1.%s.%s' % (movie['library'].get('identifier'), cat_id_string) single_cat = True @@ -69,9 +66,8 @@ class Nzbsrus(NZBProvider, RSS): id = self.getTextElement(nzb, "id") size = int(round(int(self.getTextElement(nzb, "size")) / 1048576)) - age = int(round(( time.time() - int(self.getTextElement(nzb, "postdate")) ) / 86400 )) + age = int(round((time.time() - int(self.getTextElement(nzb, "postdate"))) / 86400)) - new = { 'id': id, 'type': 'nzb', @@ -88,7 +84,7 @@ class Nzbsrus(NZBProvider, RSS): is_correct_movie = fireEvent('searcher.correct_movie', nzb = new, movie = movie, quality = quality, - imdb_results = True, single_category = single_cat, single = True) + imdb_results = True, single = True) if is_correct_movie: new['score'] = fireEvent('score.calculate', new, movie, single = True) @@ -106,6 +102,3 @@ class Nzbsrus(NZBProvider, RSS): def getApiExt(self): return '/%s/' % (self.conf('userid')) - - def isEnabled(self): - return NZBProvider.isEnabled(self) and self.conf('userid') and self.conf('api_key') From e33c6b3a5fd40430f9df1396082c36b539279b0c Mon Sep 17 00:00:00 2001 From: Ruud Date: Fri, 24 Aug 2012 22:11:13 +0200 Subject: [PATCH 31/42] Added urls to providers --- couchpotato/core/providers/nzb/mysterbin/__init__.py | 2 +- couchpotato/core/providers/nzb/newzbin/__init__.py | 1 + couchpotato/core/providers/nzb/nzbclub/__init__.py | 2 +- couchpotato/core/providers/nzb/nzbindex/__init__.py | 2 +- couchpotato/core/providers/nzb/nzbmatrix/__init__.py | 1 + .../core/providers/torrent/kickasstorrents/__init__.py | 1 + couchpotato/core/providers/torrent/publichd/__init__.py | 4 ++-- couchpotato/core/providers/torrent/sceneaccess/__init__.py | 1 + couchpotato/core/providers/torrent/scenehd/__init__.py | 1 + couchpotato/core/providers/torrent/thepiratebay/__init__.py | 2 +- couchpotato/core/providers/torrent/torrentleech/__init__.py | 1 + 11 files changed, 12 insertions(+), 6 deletions(-) diff --git a/couchpotato/core/providers/nzb/mysterbin/__init__.py b/couchpotato/core/providers/nzb/mysterbin/__init__.py index 0c759555..3f0d1d33 100644 --- a/couchpotato/core/providers/nzb/mysterbin/__init__.py +++ b/couchpotato/core/providers/nzb/mysterbin/__init__.py @@ -10,7 +10,7 @@ config = [{ 'tab': 'searcher', 'subtab': 'providers', 'name': 'Mysterbin', - 'description': '', + 'description': 'Free provider, less accurate. See Mysterbin', 'options': [ { 'name': 'enabled', diff --git a/couchpotato/core/providers/nzb/newzbin/__init__.py b/couchpotato/core/providers/nzb/newzbin/__init__.py index 4ebd849d..06b25486 100644 --- a/couchpotato/core/providers/nzb/newzbin/__init__.py +++ b/couchpotato/core/providers/nzb/newzbin/__init__.py @@ -10,6 +10,7 @@ config = [{ 'tab': 'searcher', 'subtab': 'providers', 'name': 'newzbin', + 'description': 'See Newzbin', 'wizard': True, 'options': [ { diff --git a/couchpotato/core/providers/nzb/nzbclub/__init__.py b/couchpotato/core/providers/nzb/nzbclub/__init__.py index 9c14e10f..e3387a3e 100644 --- a/couchpotato/core/providers/nzb/nzbclub/__init__.py +++ b/couchpotato/core/providers/nzb/nzbclub/__init__.py @@ -10,7 +10,7 @@ config = [{ 'tab': 'searcher', 'subtab': 'providers', 'name': 'NZBClub', - 'description': '', + 'description': 'Free provider, less accurate. See NZBClub', 'options': [ { 'name': 'enabled', diff --git a/couchpotato/core/providers/nzb/nzbindex/__init__.py b/couchpotato/core/providers/nzb/nzbindex/__init__.py index 8a3261bf..59b2730d 100644 --- a/couchpotato/core/providers/nzb/nzbindex/__init__.py +++ b/couchpotato/core/providers/nzb/nzbindex/__init__.py @@ -10,7 +10,7 @@ config = [{ 'tab': 'searcher', 'subtab': 'providers', 'name': 'nzbindex', - 'description': 'Free provider, but less accurate.', + 'description': 'Free provider, less accurate. See NZBIndex', 'options': [ { 'name': 'enabled', diff --git a/couchpotato/core/providers/nzb/nzbmatrix/__init__.py b/couchpotato/core/providers/nzb/nzbmatrix/__init__.py index 82b6ef6e..8fc6b408 100644 --- a/couchpotato/core/providers/nzb/nzbmatrix/__init__.py +++ b/couchpotato/core/providers/nzb/nzbmatrix/__init__.py @@ -11,6 +11,7 @@ config = [{ 'subtab': 'providers', 'name': 'nzbmatrix', 'label': 'NZBMatrix', + 'description': 'See NZBMatrix', 'wizard': True, 'options': [ { diff --git a/couchpotato/core/providers/torrent/kickasstorrents/__init__.py b/couchpotato/core/providers/torrent/kickasstorrents/__init__.py index 6514643e..2401f95a 100644 --- a/couchpotato/core/providers/torrent/kickasstorrents/__init__.py +++ b/couchpotato/core/providers/torrent/kickasstorrents/__init__.py @@ -10,6 +10,7 @@ config = [{ 'tab': 'searcher', 'subtab': 'providers', 'name': 'KickAssTorrents', + 'description': 'See KickAssTorrents', 'options': [ { 'name': 'enabled', diff --git a/couchpotato/core/providers/torrent/publichd/__init__.py b/couchpotato/core/providers/torrent/publichd/__init__.py index c28781e3..94d0825e 100644 --- a/couchpotato/core/providers/torrent/publichd/__init__.py +++ b/couchpotato/core/providers/torrent/publichd/__init__.py @@ -10,7 +10,7 @@ config = [{ 'tab': 'searcher', 'subtab': 'providers', 'name': 'PublicHD', - 'description': 'Public Torrent site with only HD content.', + 'description': 'Public Torrent site with only HD content. See PublicHD', 'options': [ { 'name': 'enabled', @@ -20,4 +20,4 @@ config = [{ ], }, ], -}] \ No newline at end of file +}] diff --git a/couchpotato/core/providers/torrent/sceneaccess/__init__.py b/couchpotato/core/providers/torrent/sceneaccess/__init__.py index 3a128638..28b7ca33 100644 --- a/couchpotato/core/providers/torrent/sceneaccess/__init__.py +++ b/couchpotato/core/providers/torrent/sceneaccess/__init__.py @@ -10,6 +10,7 @@ config = [{ 'tab': 'searcher', 'subtab': 'providers', 'name': 'SceneAccess', + 'description': 'See SceneAccess', 'options': [ { 'name': 'enabled', diff --git a/couchpotato/core/providers/torrent/scenehd/__init__.py b/couchpotato/core/providers/torrent/scenehd/__init__.py index d4b7a0b8..c9f18be8 100644 --- a/couchpotato/core/providers/torrent/scenehd/__init__.py +++ b/couchpotato/core/providers/torrent/scenehd/__init__.py @@ -10,6 +10,7 @@ config = [{ 'tab': 'searcher', 'subtab': 'providers', 'name': 'SceneHD', + 'description': 'See SceneHD', 'options': [ { 'name': 'enabled', diff --git a/couchpotato/core/providers/torrent/thepiratebay/__init__.py b/couchpotato/core/providers/torrent/thepiratebay/__init__.py index 8aa49111..2f8872e4 100644 --- a/couchpotato/core/providers/torrent/thepiratebay/__init__.py +++ b/couchpotato/core/providers/torrent/thepiratebay/__init__.py @@ -9,7 +9,7 @@ config = [{ 'tab': 'searcher', 'subtab': 'providers', 'name': 'ThePirateBay', - 'description': 'The world\'s largest bittorrent tracker.', + 'description': 'The world\'s largest bittorrent tracker. See ThePirateBay', 'options': [ { 'name': 'enabled', diff --git a/couchpotato/core/providers/torrent/torrentleech/__init__.py b/couchpotato/core/providers/torrent/torrentleech/__init__.py index 19627e11..482dfda7 100644 --- a/couchpotato/core/providers/torrent/torrentleech/__init__.py +++ b/couchpotato/core/providers/torrent/torrentleech/__init__.py @@ -10,6 +10,7 @@ config = [{ 'tab': 'searcher', 'subtab': 'providers', 'name': 'TorrentLeech', + 'description': 'See TorrentLeech', 'options': [ { 'name': 'enabled', From d817179747bbd3cb7a5cc7e3c8ab6a82384d7494 Mon Sep 17 00:00:00 2001 From: Ruud Date: Fri, 24 Aug 2012 22:46:32 +0200 Subject: [PATCH 32/42] De-referer external urls. --- couchpotato/core/plugins/movie/static/movie.js | 11 +---------- couchpotato/static/scripts/couchpotato.js | 12 ++++++++++++ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/couchpotato/core/plugins/movie/static/movie.js b/couchpotato/core/plugins/movie/static/movie.js index 2182b887..dbb28aa5 100644 --- a/couchpotato/core/plugins/movie/static/movie.js +++ b/couchpotato/core/plugins/movie/static/movie.js @@ -323,19 +323,10 @@ var IMDBAction = new Class({ self.el = new Element('a.imdb', { 'title': 'Go to the IMDB page of ' + self.movie.getTitle(), - 'events': { - 'click': self.gotoIMDB.bind(self) - } + 'href': 'http://www.imdb.com/title/'+self.id+'/' }); if(!self.id) self.disable(); - }, - - gotoIMDB: function(e){ - var self = this; - (e).preventDefault(); - - window.open('http://www.imdb.com/title/'+self.id+'/'); } }); diff --git a/couchpotato/static/scripts/couchpotato.js b/couchpotato/static/scripts/couchpotato.js index b99e6c7e..9e66e342 100644 --- a/couchpotato/static/scripts/couchpotato.js +++ b/couchpotato/static/scripts/couchpotato.js @@ -29,6 +29,7 @@ var CouchPotato = new Class({ History.addEvent('change', self.openPage.bind(self)); self.c.addEvent('click:relay(a[href^=/]:not([target]))', self.pushState.bind(self)); + self.c.addEvent('click:relay(a[href^=http])', self.openDerefered.bind(self)); }, getOption: function(name){ @@ -269,6 +270,17 @@ var CouchPotato = new Class({ createUrl: function(action, params){ return this.options.base_url + (action ? action+'/' : '') + (params ? '?'+Object.toQueryString(params) : '') + }, + + openDerefered: function(e, el){ + (e).stop(); + + var url = 'http://www.dereferer.org/?' + el.get('href'); + + if(el.get('target') == '_blank' || (e.meta && Browser.Platform.mac) || (e.control && !Browser.Platform.mac)) + window.open(url); + else + window.location = url; } }); From bdfb073bca972f7933b4cda246941cfe29fc830b Mon Sep 17 00:00:00 2001 From: Ruud Date: Sun, 26 Aug 2012 14:14:57 +0200 Subject: [PATCH 33/42] Catch filetime error. fix #741 --- couchpotato/core/plugins/scanner/main.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/couchpotato/core/plugins/scanner/main.py b/couchpotato/core/plugins/scanner/main.py index 7e47ed96..0bc47681 100644 --- a/couchpotato/core/plugins/scanner/main.py +++ b/couchpotato/core/plugins/scanner/main.py @@ -303,7 +303,15 @@ class Scanner(Plugin): break if file_too_new: - log.info('Files seem to be still unpacking or just unpacked (created on %s), ignoring for now: %s', (time.ctime(file_time[0]), identifier)) + try: + time_string = time.ctime(file_time[0]) + except: + try: + time_string = time.ctime(file_time[1]) + except: + time_string = 'unknown' + + log.info('Files seem to be still unpacking or just unpacked (created on %s), ignoring for now: %s', (time_string, identifier)) # Delete the unsorted list del group['unsorted_files'] From b599bc384bb2e4675ec7d2dce48138416493365c Mon Sep 17 00:00:00 2001 From: Ruud Date: Sun, 26 Aug 2012 14:23:14 +0200 Subject: [PATCH 34/42] Add updater type to about page --- couchpotato/static/scripts/page/about.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/couchpotato/static/scripts/page/about.js b/couchpotato/static/scripts/page/about.js index 0a966672..8ac38ba6 100644 --- a/couchpotato/static/scripts/page/about.js +++ b/couchpotato/static/scripts/page/about.js @@ -58,6 +58,8 @@ var AboutSettingTab = new Class({ } } }), + new Element('dt[text=Updater]'), + self.updater_type = new Element('dd.updater'), new Element('dt[text=ID]'), new Element('dd', {'text': App.getOption('pid')}), new Element('dt[text=Directories]'), @@ -115,6 +117,7 @@ var AboutSettingTab = new Class({ var self = this; var date = new Date(json.version.date * 1000); self.version_text.set('text', json.version.hash + ' ('+date.toUTCString()+')'); + self.updater_type.set('text', json.version.type); } }); From ada4710a0a51abebe5621dbcf2abcad0d07d968b Mon Sep 17 00:00:00 2001 From: Ruud Date: Sun, 26 Aug 2012 14:31:38 +0200 Subject: [PATCH 35/42] Correctly select edit quality. fix #736 --- couchpotato/static/scripts/page/wanted.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/couchpotato/static/scripts/page/wanted.js b/couchpotato/static/scripts/page/wanted.js index 2ce6c14c..3ef10d7a 100644 --- a/couchpotato/static/scripts/page/wanted.js +++ b/couchpotato/static/scripts/page/wanted.js @@ -73,20 +73,23 @@ window.addEvent('domready', function(){ new Element('option', { 'text': alt.title }).inject(self.title_select); - + if(alt['default']) self.title_select.set('value', alt.title); }); Quality.getActiveProfiles().each(function(profile){ + + var profile_id = profile.id ? profile.id : profile.data.id; + new Element('option', { - 'value': profile.id ? profile.id : profile.data.id, + 'value': profile_id, 'text': profile.label ? profile.label : profile.data.label }).inject(self.profile_select); - if(self.movie.profile) - self.profile_select.set('value', profile.id ? profile.id : profile.data.id); + if(self.movie.profile && self.movie.profile.data.id == profile_id) + self.profile_select.set('value', profile_id); }); } From cf86948c138bf1e266d095b01c3c60f99bf55130 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sun, 26 Aug 2012 15:42:26 +0200 Subject: [PATCH 36/42] Remove empty folder when found better quality. fix #735 --- couchpotato/core/plugins/renamer/main.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/couchpotato/core/plugins/renamer/main.py b/couchpotato/core/plugins/renamer/main.py index a2882eae..9a443ed2 100644 --- a/couchpotato/core/plugins/renamer/main.py +++ b/couchpotato/core/plugins/renamer/main.py @@ -327,6 +327,11 @@ class Renamer(Plugin): try: if os.path.isfile(src): os.remove(src) + + parent_dir = os.path.normpath(os.path.dirname(src)) + if os.path.isdir(parent_dir) and destination != parent_dir: + self.deleteEmptyFolder(parent_dir, show_error = False) + except: log.error('Failed removing %s: %s', (src, traceback.format_exc())) self.tagDir(group, 'failed_remove') @@ -464,8 +469,9 @@ class Renamer(Plugin): def replaceDoubles(self, string): return string.replace(' ', ' ').replace(' .', '.') - def deleteEmptyFolder(self, folder): + def deleteEmptyFolder(self, folder, show_error = True): + loge = log.error if show_error else log.debug for root, dirs, files in os.walk(folder): for dir_name in dirs: @@ -474,9 +480,9 @@ class Renamer(Plugin): try: os.rmdir(full_path) except: - log.error('Couldn\'t remove empty directory %s: %s', (full_path, traceback.format_exc())) + loge('Couldn\'t remove empty directory %s: %s', (full_path, traceback.format_exc())) try: os.rmdir(folder) except: - log.error('Couldn\'t remove empty directory %s: %s', (folder, traceback.format_exc())) + loge('Couldn\'t remove empty directory %s: %s', (folder, traceback.format_exc())) From 4275be005902ad64d45d6541b9c25415521af119 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sun, 26 Aug 2012 17:06:49 +0200 Subject: [PATCH 37/42] Add some tooltips to quality. closes #725 --- couchpotato/core/plugins/movie/static/movie.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/couchpotato/core/plugins/movie/static/movie.js b/couchpotato/core/plugins/movie/static/movie.js index dbb28aa5..4ddac680 100644 --- a/couchpotato/core/plugins/movie/static/movie.js +++ b/couchpotato/core/plugins/movie/static/movie.js @@ -140,8 +140,10 @@ var Movie = new Class({ self.profile.getTypes().each(function(type){ var q = self.addQuality(type.quality_id || type.get('quality_id')); - if(type.finish == true || type.get('finish')) + if((type.finish == true || type.get('finish')) && !q.hasClass('finish')){ q.addClass('finish'); + q.set('title', q.get('title') + ' Will finish searching for this movie if this quality is found.') + } }); @@ -153,8 +155,11 @@ var Movie = new Class({ if(!q && (status.identifier == 'snatched' || status.identifier == 'done')) var q = self.addQuality(release.quality_id) - if (status && q) + + if (status && q && !q.hasClass(status.identifier)){ q.addClass(status.identifier); + q.set('title', (q.get('title') ? q.get('title') : '') + ' status: '+ status.label) + } }); @@ -175,7 +180,8 @@ var Movie = new Class({ var q = Quality.getQuality(quality_id); return new Element('span', { 'text': q.label, - 'class': 'q_'+q.identifier + ' q_id' + q.id + 'class': 'q_'+q.identifier + ' q_id' + q.id, + 'title': '' }).inject(self.quality); }, From 442444a95a408cff8106b7e212eee8a7c45f2578 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sun, 26 Aug 2012 17:36:10 +0200 Subject: [PATCH 38/42] Update readme. closes #717 --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2eb0f2f3..b0e4222d 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,12 @@ Once a movie is found, it will send it to SABnzbd or download the torrent to a s ## Running from Source -CouchPotatoServer can be run from source. This will use *git* as updater, so make sure that is installed also. +CouchPotatoServer can be run from source. This will use *git* as updater, so make sure that is installed also. -Windows: +Windows, see [the CP forum](http://couchpota.to/forum/showthread.php?tid=14) for more details: -* Install [PyWin32 2.7](http://sourceforge.net/projects/pywin32/files/pywin32/Build%20217/) and [GIT](http://git-scm.com/) +* Install [Python 2.7](http://www.python.org/download/releases/2.7.3/) +* Then install [PyWin32 2.7](http://sourceforge.net/projects/pywin32/files/pywin32/Build%20217/) and [GIT](http://git-scm.com/) * If you come and ask on the forums 'why directory selection no work?', I will kill a kitten, also this is because you need PyWin32 * Open up `Git Bash` (or CMD) and go to the folder you want to install CP. Something like Program Files. * Run `git clone https://github.com/RuudBurger/CouchPotatoServer.git`. From d64150d6a7fd2a250c59d5ad7fbb8595399015d8 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sun, 26 Aug 2012 22:43:00 +0200 Subject: [PATCH 39/42] Magnet link improvements --- couchpotato/core/downloaders/base.py | 14 ++++++++++++++ couchpotato/core/downloaders/blackhole/main.py | 14 +++++++++++--- couchpotato/core/plugins/release/main.py | 4 +++- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/couchpotato/core/downloaders/base.py b/couchpotato/core/downloaders/base.py index d5f1c422..c910edd2 100644 --- a/couchpotato/core/downloaders/base.py +++ b/couchpotato/core/downloaders/base.py @@ -4,6 +4,8 @@ from couchpotato.core.logger import CPLog from couchpotato.core.plugins.base import Plugin from couchpotato.environment import Env import os +import re +import traceback log = CPLog(__name__) @@ -45,6 +47,18 @@ class Downloader(Plugin): return is_correct + def magnetToTorrent(self, magnet_link): + torrent_hash = re.findall('urn:btih:([\w]{40})', magnet_link)[0] + url = 'http://torrage.com/torrent/%s.torrent' % torrent_hash + + try: + filedata = self.urlopen(url) + return filedata + except: + log.error('Failed converting magnet url to torrent: %s, %s', (url, traceback.format_exc())) + + return False + def isDisabled(self, manual): return not self.isEnabled(manual) diff --git a/couchpotato/core/downloaders/blackhole/main.py b/couchpotato/core/downloaders/blackhole/main.py index a3f03617..4713adf7 100644 --- a/couchpotato/core/downloaders/blackhole/main.py +++ b/couchpotato/core/downloaders/blackhole/main.py @@ -8,7 +8,7 @@ log = CPLog(__name__) class Blackhole(Downloader): - type = ['nzb', 'torrent'] + type = ['nzb', 'torrent', 'torrent_magnet'] def download(self, data = {}, movie = {}, manual = False, filedata = None): if self.isDisabled(manual) or (not self.isCorrectType(data.get('type')) or (not self.conf('use_for') in ['both', data.get('type')])): @@ -20,8 +20,16 @@ class Blackhole(Downloader): else: try: if not filedata or len(filedata) < 50: - log.error('No nzb/torrent available!') - return False + try: + if data.get('type') == 'torrent_magnet': + filedata = self.magnetToTorrent(data.get('url')) + data['type'] = 'torrent' + except: + log.error('Failed download torrent via magnet url: %s', traceback.format_exc()) + + if not filedata or len(filedata) < 50: + log.error('No nzb/torrent available: %s', data.get('url')) + return False fullPath = os.path.join(directory, self.createFileName(data, filedata, movie)) diff --git a/couchpotato/core/plugins/release/main.py b/couchpotato/core/plugins/release/main.py index 0abc065e..9046034c 100644 --- a/couchpotato/core/plugins/release/main.py +++ b/couchpotato/core/plugins/release/main.py @@ -170,7 +170,9 @@ class Release(Plugin): # Get matching provider provider = fireEvent('provider.belongs_to', item['url'], provider = item.get('provider'), single = True) - item['download'] = provider.download + + if item['type'] != 'torrent_magnet': + item['download'] = provider.download success = fireEvent('searcher.download', data = item, movie = rel.movie.to_dict({ 'profile': {'types': {'quality': {}}}, From b8c6393409321eda8fe17a75a3f003acbe545342 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sun, 26 Aug 2012 22:43:14 +0200 Subject: [PATCH 40/42] Quality and filesize fix in release list. --- couchpotato/core/plugins/movie/static/movie.js | 4 ++-- couchpotato/core/plugins/searcher/main.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/couchpotato/core/plugins/movie/static/movie.js b/couchpotato/core/plugins/movie/static/movie.js index 4ddac680..f8af5c09 100644 --- a/couchpotato/core/plugins/movie/static/movie.js +++ b/couchpotato/core/plugins/movie/static/movie.js @@ -417,8 +417,8 @@ var ReleaseAction = new Class({ }).adopt( new Element('span.name', {'text': self.get(release, 'name'), 'title': self.get(release, 'name')}), new Element('span.status', {'text': status.identifier, 'class': 'release_status '+status.identifier}), - new Element('span.quality', {'text': quality.label || 'n/a'}), - new Element('span.size', {'text': (self.get(release, 'size'))}), + new Element('span.quality', {'text': quality.get('label') || 'n/a'}), + new Element('span.size', {'text': release.info['size'] ? Math.floor(self.get(release, 'size')) : 'n/a'}), new Element('span.age', {'text': self.get(release, 'age')}), new Element('span.score', {'text': self.get(release, 'score')}), new Element('span.provider', {'text': self.get(release, 'provider')}), diff --git a/couchpotato/core/plugins/searcher/main.py b/couchpotato/core/plugins/searcher/main.py index 01358ee8..afabeae8 100644 --- a/couchpotato/core/plugins/searcher/main.py +++ b/couchpotato/core/plugins/searcher/main.py @@ -150,7 +150,7 @@ class Searcher(Plugin): for info in nzb: try: - if not isinstance(nzb[info], (str, unicode, int, long)): + if not isinstance(nzb[info], (str, unicode, int, long, float)): continue rls_info = ReleaseInfo( From 5df95d4132f1a585aac7f19f1bbcf970f531788d Mon Sep 17 00:00:00 2001 From: Ruud Date: Sun, 26 Aug 2012 22:44:11 +0200 Subject: [PATCH 41/42] Fixed spelling in please wait message. --- couchpotato/static/scripts/couchpotato.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/couchpotato/static/scripts/couchpotato.js b/couchpotato/static/scripts/couchpotato.js index 9e66e342..61ee45bd 100644 --- a/couchpotato/static/scripts/couchpotato.js +++ b/couchpotato/static/scripts/couchpotato.js @@ -188,7 +188,7 @@ var CouchPotato = new Class({ restart: function(message, title){ var self = this; - self.blockPage(message || 'Restarting... please wait. If this takes to long, something must have gone wrong.', title); + self.blockPage(message || 'Restarting... please wait. If this takes too long, something must have gone wrong.', title); Api.request('app.restart'); self.checkAvailable(1000); }, @@ -217,7 +217,7 @@ var CouchPotato = new Class({ Updater.check(onComplete) - self.blockPage('Please wait. If this takes to long, something must have gone wrong.', 'Checking for updates'); + self.blockPage('Please wait. If this takes too long, something must have gone wrong.', 'Checking for updates'); self.checkAvailable(3000); }, From 9a0a829802fcfc940dff2e3e0f8205f317bfcdd1 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sun, 26 Aug 2012 22:59:40 +0200 Subject: [PATCH 42/42] Show info link again. fix #706 --- couchpotato/core/plugins/movie/static/movie.js | 8 ++------ .../core/providers/torrent/kickasstorrents/main.py | 3 ++- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/couchpotato/core/plugins/movie/static/movie.js b/couchpotato/core/plugins/movie/static/movie.js index f8af5c09..17234b92 100644 --- a/couchpotato/core/plugins/movie/static/movie.js +++ b/couchpotato/core/plugins/movie/static/movie.js @@ -397,10 +397,6 @@ var ReleaseAction = new Class({ quality = Quality.getProfile(release.quality_id) || {}, info = release.info; - try { - var details_url = info.filter(function(item){ return item.identifier == 'detail_url' }).pick().value; - } catch(e){} - if( status.identifier == 'ignored' || status.identifier == 'failed'){ self.last_release = release; } @@ -422,8 +418,8 @@ var ReleaseAction = new Class({ new Element('span.age', {'text': self.get(release, 'age')}), new Element('span.score', {'text': self.get(release, 'score')}), new Element('span.provider', {'text': self.get(release, 'provider')}), - details_url ? new Element('a.info.icon', { - 'href': details_url, + release.info['detail_url'] ? new Element('a.info.icon', { + 'href': release.info['detail_url'], 'target': '_blank' }) : null, new Element('a.download.icon', { diff --git a/couchpotato/core/providers/torrent/kickasstorrents/main.py b/couchpotato/core/providers/torrent/kickasstorrents/main.py index 84a679f2..6c157a6b 100644 --- a/couchpotato/core/providers/torrent/kickasstorrents/main.py +++ b/couchpotato/core/providers/torrent/kickasstorrents/main.py @@ -13,7 +13,7 @@ class KickAssTorrents(TorrentProvider): urls = { 'test': 'http://kat.ph/', - 'detail': 'http://kat.ph/%s-t%s.html', + 'detail': 'http://kat.ph/%s', 'search': 'http://kat.ph/i%s/', } @@ -74,6 +74,7 @@ class KickAssTorrents(TorrentProvider): new['id'] = temp.get('id')[-8:] new['name'] = link.text new['url'] = td.find('a', 'imagnet')['href'] + new['detail_url'] = self.urls['detail'] % link['href'][1:] new['score'] = 20 if td.find('a', 'iverif') else 0 elif column_name is 'size': new['size'] = self.parseSize(td.text)