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