Standardized checking snatched
This commit is contained in:
@@ -1,10 +1,7 @@
|
||||
from base64 import b32decode, b16encode
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.helpers.encoding import toSafeString
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.environment import Env
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
|
||||
@@ -23,34 +20,18 @@ class Downloader(Plugin):
|
||||
|
||||
def __init__(self):
|
||||
addEvent('download', self.download)
|
||||
addEvent('download.status', self.getDownloadStatus)
|
||||
addEvent('download.remove', self.remove)
|
||||
addEvent('download.status', self.getAllDownloadStatus)
|
||||
addEvent('download.remove_failed', self.removeFailed)
|
||||
|
||||
def download(self, data = {}, movie = {}, manual = False, filedata = None):
|
||||
pass
|
||||
|
||||
def getDownloadStatus(self, data = {}, movie = {}):
|
||||
def getAllDownloadStatus(self):
|
||||
return False
|
||||
|
||||
def remove(self, name = {}, nzo_id = {}):
|
||||
def removeFailed(self, name = {}, nzo_id = {}):
|
||||
return False
|
||||
|
||||
def createNzbName(self, data, 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))
|
||||
if data.get('type') == 'nzb' and 'DOCTYPE nzb' not in filedata and '</nzb>' not in filedata:
|
||||
return '%s.%s' % (name, 'rar')
|
||||
return '%s.%s' % (name, data.get('type'))
|
||||
|
||||
def cpTag(self, movie):
|
||||
if Env.setting('enabled', 'renamer'):
|
||||
return '.cp(' + movie['library'].get('identifier') + ')' if movie['library'].get('identifier') else ''
|
||||
|
||||
return ''
|
||||
|
||||
def isCorrectType(self, item_type):
|
||||
is_correct = item_type in self.type
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from couchpotato.core.downloaders.base import Downloader
|
||||
from couchpotato.core.helpers.encoding import tryUrlencode
|
||||
from couchpotato.core.helpers.variable import cleanHost
|
||||
from couchpotato.core.helpers.variable import cleanHost, mergeDicts
|
||||
from couchpotato.core.logger import CPLog
|
||||
import json
|
||||
import traceback
|
||||
@@ -63,86 +63,92 @@ class Sabnzbd(Downloader):
|
||||
log.error("Unknown error: " + result[:40])
|
||||
return False
|
||||
|
||||
def getDownloadStatus(self, data = {}, movie = {}):
|
||||
if self.isDisabled(manual = True) or not self.isCorrectType(data.get('type')):
|
||||
return
|
||||
|
||||
log.info('Checking SABnzbd download status.')
|
||||
|
||||
# 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('Failed checking status: %s', traceback.format_exc())
|
||||
def getAllDownloadStatus(self):
|
||||
if self.isDisabled(manual = False):
|
||||
return False
|
||||
|
||||
log.debug('Checking SABnzbd download status.')
|
||||
|
||||
# Go through Queue
|
||||
try:
|
||||
queue = json.loads(sab)
|
||||
queue = self.call({
|
||||
'mode': 'queue',
|
||||
})
|
||||
except:
|
||||
log.debug("Result text from SAB: " + sab[:40])
|
||||
log.error('Failed parsing json status: %s', traceback.format_exc())
|
||||
log.error('Failed getting queue: %s', traceback.format_exc())
|
||||
return False
|
||||
|
||||
# Go through history items
|
||||
params = {
|
||||
'apikey': self.conf('api_key'),
|
||||
'mode': 'history',
|
||||
'limit': 15,
|
||||
'output': 'json'
|
||||
}
|
||||
url = cleanHost(self.conf('host')) + "api?" + tryUrlencode(params)
|
||||
|
||||
try:
|
||||
sab = self.urlopen(url, timeout = 60, show_error = False)
|
||||
history = self.call({
|
||||
'mode': 'history',
|
||||
'limit': 15,
|
||||
})
|
||||
except:
|
||||
log.error('Failed getting history: %s', traceback.format_exc())
|
||||
log.error('Failed getting history json: %s', traceback.format_exc())
|
||||
return False
|
||||
|
||||
try:
|
||||
history = json.loads(sab)
|
||||
except:
|
||||
log.debug("Result text from SAB: " + sab[:40])
|
||||
log.error('Failed parsing history json: %s', traceback.format_exc())
|
||||
statuses = []
|
||||
|
||||
# Get busy releases
|
||||
for item in queue.get('slots', []):
|
||||
statuses.append({
|
||||
'id': item['nzo_id'],
|
||||
'name': item['filename'],
|
||||
'status': 'busy',
|
||||
'original_status': item['status'],
|
||||
'timeleft': item['timeleft'] if not queue['paused'] else -1,
|
||||
})
|
||||
|
||||
# Get old releases
|
||||
for item in history.get('slots', []):
|
||||
|
||||
status = 'busy'
|
||||
if item['status'] == 'Failed' or (item['status'] == 'Completed' and item['fail_message'].strip()):
|
||||
status = 'failed'
|
||||
elif item['status'] == 'Completed':
|
||||
status = 'completed'
|
||||
|
||||
statuses.append({
|
||||
'id': item['nzo_id'],
|
||||
'name': item['name'],
|
||||
'status': status,
|
||||
'original_status': item['status'],
|
||||
'timeleft': 0,
|
||||
})
|
||||
|
||||
return statuses
|
||||
|
||||
def removeFailed(self, item):
|
||||
|
||||
if not self.conf('delete_failed', default = True):
|
||||
return False
|
||||
|
||||
return queue, history
|
||||
log.info('%s failed downloading, deleting...', item['name'])
|
||||
|
||||
def remove(self, name = {}, nzo_id = {}):
|
||||
# Delete failed download
|
||||
if self.conf('delete_failed', default = True):
|
||||
|
||||
log.info('%s failed downloading, deleting...', name)
|
||||
params = {
|
||||
'apikey': self.conf('api_key'),
|
||||
try:
|
||||
self.call({
|
||||
'mode': 'history',
|
||||
'name': 'delete',
|
||||
'del_files': '1',
|
||||
'value': nzo_id
|
||||
}
|
||||
url = cleanHost(self.conf('host')) + "api?" + tryUrlencode(params)
|
||||
'value': item['id']
|
||||
}, use_json = False)
|
||||
except:
|
||||
log.error('Failed deleting: %s', traceback.format_exc())
|
||||
return False
|
||||
|
||||
try:
|
||||
sab = self.urlopen(url, timeout = 60, show_error = False)
|
||||
except:
|
||||
log.error('Failed deleting: %s', traceback.format_exc())
|
||||
return False
|
||||
return True
|
||||
|
||||
result = sab.strip()
|
||||
if not result:
|
||||
log.error("SABnzbd didn't return anything.")
|
||||
def call(self, params, use_json = True):
|
||||
|
||||
url = cleanHost(self.conf('host')) + "api?" + tryUrlencode(mergeDicts(params, {
|
||||
'apikey': self.conf('api_key'),
|
||||
'output': 'json'
|
||||
}))
|
||||
|
||||
data = self.urlopen(url, timeout = 60, show_error = False)
|
||||
if use_json:
|
||||
return json.loads(data)[params['mode']]
|
||||
else:
|
||||
return data
|
||||
|
||||
log.debug("Result text from SAB: " + result[:40])
|
||||
if result == "ok":
|
||||
log.info('SabNZBd deleted failed release %s successfully.', name)
|
||||
elif result == "Missing authentication":
|
||||
log.error("Incorrect username/password or API?.")
|
||||
else:
|
||||
log.error("Unknown error: " + result[:40])
|
||||
return
|
||||
|
||||
@@ -118,8 +118,16 @@ def getTitle(library_dict):
|
||||
try:
|
||||
return library_dict['titles'][0]['title']
|
||||
except:
|
||||
log.error('Could not get title for %s', library_dict['identifier'])
|
||||
return None
|
||||
try:
|
||||
for title in library_dict.titles:
|
||||
if title.default:
|
||||
return title.title
|
||||
except:
|
||||
log.error('Could not get title for %s', library_dict.identifier)
|
||||
return None
|
||||
|
||||
log.error('Could not get title for %s', library_dict['identifier'])
|
||||
return None
|
||||
except:
|
||||
log.error('Could not get title for library item: %s', library_dict)
|
||||
return None
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from StringIO import StringIO
|
||||
from couchpotato import addView
|
||||
from couchpotato.core.event import fireEvent, addEvent
|
||||
from couchpotato.core.helpers.encoding import tryUrlencode, simplifyString, ss
|
||||
from couchpotato.core.helpers.encoding import tryUrlencode, simplifyString, ss, \
|
||||
toSafeString
|
||||
from couchpotato.core.helpers.variable import getExt
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.environment import Env
|
||||
@@ -245,6 +246,22 @@ class Plugin(object):
|
||||
Env.get('cache').set(cache_key, value, timeout)
|
||||
return value
|
||||
|
||||
def createNzbName(self, data, 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))
|
||||
if data.get('type') == 'nzb' and 'DOCTYPE nzb' not in filedata and '</nzb>' not in filedata:
|
||||
return '%s.%s' % (name, 'rar')
|
||||
return '%s.%s' % (name, data.get('type'))
|
||||
|
||||
def cpTag(self, movie):
|
||||
if Env.setting('enabled', 'renamer'):
|
||||
return '.cp(' + movie['library'].get('identifier') + ')' if movie['library'].get('identifier') else ''
|
||||
|
||||
return ''
|
||||
|
||||
def isDisabled(self):
|
||||
return not self.isEnabled()
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ log = CPLog(__name__)
|
||||
class Renamer(Plugin):
|
||||
|
||||
renaming_started = False
|
||||
checking_snatched = False
|
||||
|
||||
def __init__(self):
|
||||
|
||||
@@ -33,6 +34,7 @@ class Renamer(Plugin):
|
||||
addEvent('app.load', self.scan)
|
||||
|
||||
fireEvent('schedule.interval', 'renamer.check_snatched', self.checkSnatched, minutes = self.conf('run_every'))
|
||||
fireEvent('schedule.interval', 'renamer.check_snatched_forced', self.scan, hours = 2)
|
||||
|
||||
def scanView(self):
|
||||
|
||||
@@ -495,6 +497,11 @@ class Renamer(Plugin):
|
||||
loge('Couldn\'t remove empty directory %s: %s', (folder, traceback.format_exc()))
|
||||
|
||||
def checkSnatched(self):
|
||||
if self.checking_snatched:
|
||||
log.debug('Already checking snatched')
|
||||
|
||||
self.checking_snatched = True
|
||||
|
||||
snatched_status = fireEvent('status.get', 'snatched', single = True)
|
||||
ignored_status = fireEvent('status.get', 'ignored', single = True)
|
||||
failed_status = fireEvent('status.get', 'failed', single = True)
|
||||
@@ -504,81 +511,75 @@ class Renamer(Plugin):
|
||||
db = get_session()
|
||||
rels = db.query(Release).filter_by(status_id = snatched_status.get('id')).all()
|
||||
|
||||
scan_required = False
|
||||
|
||||
if rels:
|
||||
self.checking_snatched = True
|
||||
log.debug('Checking status snatched releases...')
|
||||
# get queue and history (once) from SABnzbd
|
||||
queue, history = fireEvent('download.status', data = {}, movie = {}, single = True)
|
||||
|
||||
scan_required = False
|
||||
|
||||
for rel in rels:
|
||||
|
||||
# Get current selected title
|
||||
default_title = ''
|
||||
for title in rel.movie.library.titles:
|
||||
if title.default: default_title = title.title
|
||||
|
||||
# Check if movie has already completed and is manage tab (legacy db correction)
|
||||
if rel.movie.status_id == done_status.get('id'):
|
||||
log.debug('Found a completed movie with a snatched release : %s. Setting release status to ignored...' , default_title)
|
||||
rel.status_id = ignored_status.get('id')
|
||||
db.commit()
|
||||
continue
|
||||
|
||||
item = {}
|
||||
for info in rel.info:
|
||||
item[info.identifier] = info.value
|
||||
|
||||
movie_dict = fireEvent('movie.get', rel.movie_id, single = True)
|
||||
|
||||
# check status
|
||||
nzbname = self.createNzbName(item, movie_dict)
|
||||
try:
|
||||
for slot in queue['queue']['slots']:
|
||||
log.debug('Found %s in SabNZBd queue, which is %s, with %s left', (slot['filename'], slot['status'], slot['timeleft']))
|
||||
if slot['filename'] == nzbname:
|
||||
downloadstatus =['status'].lower()
|
||||
except:
|
||||
log.debug('No items in queue: %s', (traceback.format_exc()))
|
||||
try:
|
||||
for slot in history['history']['slots']:
|
||||
log.debug('Found %s in SabNZBd history, which has %s', (slot['name'], slot['status']))
|
||||
if slot['name'] == nzbname:
|
||||
# Note: if post process even if failed is on in SabNZBd, it will complete with a fail message
|
||||
if slot['status'] == 'Failed' or (slot['status'] == 'Completed' and slot['fail_message'].strip()):
|
||||
|
||||
# Delete failed download
|
||||
rel_remove = fireEvent('download.remove', name = slot['name'], nzo_id = slot['nzo_id'], single = True)
|
||||
downloadstatus = 'failed'
|
||||
else:
|
||||
downloadstatus = slot['status'].lower()
|
||||
except:
|
||||
log.debug('No items in history: %s', (traceback.format_exc()))
|
||||
|
||||
if not downloadstatus:
|
||||
statuses = fireEvent('download.status', merge = True)
|
||||
if not statuses:
|
||||
log.debug('Download status functionality is not implemented for active downloaders.')
|
||||
scan_required = True
|
||||
else:
|
||||
log.debug('Download status: %s' , downloadstatus)
|
||||
|
||||
if downloadstatus == 'failed':
|
||||
if self.conf('next_on_failed'):
|
||||
fireEvent('searcher.try_next_release', movie_id = rel.movie_id)
|
||||
else:
|
||||
rel.status_id = failed_status.get('id')
|
||||
try:
|
||||
for rel in rels:
|
||||
rel_dict = rel.to_dict({'info': {}})
|
||||
|
||||
# Get current selected title
|
||||
default_title = getTitle(rel.movie.library)
|
||||
|
||||
# Check if movie has already completed and is manage tab (legacy db correction)
|
||||
if rel.movie.status_id == done_status.get('id'):
|
||||
log.debug('Found a completed movie with a snatched release : %s. Setting release status to ignored...' , default_title)
|
||||
rel.status_id = ignored_status.get('id')
|
||||
db.commit()
|
||||
continue
|
||||
|
||||
movie_dict = fireEvent('movie.get', rel.movie_id, single = True)
|
||||
|
||||
# check status
|
||||
nzbname = self.createNzbName(rel_dict['info'], movie_dict)
|
||||
|
||||
found = False
|
||||
for item in statuses:
|
||||
if item['name'] == nzbname:
|
||||
|
||||
timeleft = 'N/A' if item['timeleft'] == -1 else item['timeleft']
|
||||
log.debug('Found %s: %s, time to go: %s', (item['name'], item['status'].upper(), timeleft))
|
||||
|
||||
if item['status'] == 'busy':
|
||||
pass
|
||||
elif item['status'] == 'failed':
|
||||
if item['delete']:
|
||||
fireEvent('download.remove_failed', item, single = True)
|
||||
|
||||
if self.conf('next_on_failed'):
|
||||
fireEvent('searcher.try_next_release', movie_id = rel.movie_id)
|
||||
else:
|
||||
rel.status_id = failed_status.get('id')
|
||||
db.commit()
|
||||
elif item['status'] == 'completed':
|
||||
log.info('Download of %s completed!', item['name'])
|
||||
scan_required = True
|
||||
|
||||
found = True
|
||||
break
|
||||
|
||||
if not found:
|
||||
log.info('%s not found in downloaders', nzbname)
|
||||
rel.status_id = ignored_status.get('id')
|
||||
db.commit()
|
||||
|
||||
log.info('Download of %s failed.', item['name'])
|
||||
if self.conf('next_on_failed'):
|
||||
fireEvent('searcher.try_next_release', movie_id = rel.movie_id)
|
||||
|
||||
elif downloadstatus == 'completed':
|
||||
log.info('Download of %s completed!', item['name'])
|
||||
scan_required = True
|
||||
except:
|
||||
log.error('Failed checking for release in downloader: %s', traceback.format_exc())
|
||||
|
||||
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
|
||||
if scan_required:
|
||||
fireEvent('renamer.scan')
|
||||
|
||||
self.checking_snatched = False
|
||||
|
||||
return True
|
||||
|
||||
Reference in New Issue
Block a user