Standardized checking snatched
This commit is contained in:
@@ -1,10 +1,7 @@
|
|||||||
from base64 import b32decode, b16encode
|
from base64 import b32decode, b16encode
|
||||||
from couchpotato.core.event import addEvent
|
from couchpotato.core.event import addEvent
|
||||||
from couchpotato.core.helpers.encoding import toSafeString
|
|
||||||
from couchpotato.core.logger import CPLog
|
from couchpotato.core.logger import CPLog
|
||||||
from couchpotato.core.plugins.base import Plugin
|
from couchpotato.core.plugins.base import Plugin
|
||||||
from couchpotato.environment import Env
|
|
||||||
import os
|
|
||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
|
|
||||||
@@ -23,34 +20,18 @@ class Downloader(Plugin):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
addEvent('download', self.download)
|
addEvent('download', self.download)
|
||||||
addEvent('download.status', self.getDownloadStatus)
|
addEvent('download.status', self.getAllDownloadStatus)
|
||||||
addEvent('download.remove', self.remove)
|
addEvent('download.remove_failed', self.removeFailed)
|
||||||
|
|
||||||
def download(self, data = {}, movie = {}, manual = False, filedata = None):
|
def download(self, data = {}, movie = {}, manual = False, filedata = None):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def getDownloadStatus(self, data = {}, movie = {}):
|
def getAllDownloadStatus(self):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def remove(self, name = {}, nzo_id = {}):
|
def removeFailed(self, name = {}, nzo_id = {}):
|
||||||
return False
|
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):
|
def isCorrectType(self, item_type):
|
||||||
is_correct = item_type in self.type
|
is_correct = item_type in self.type
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from couchpotato.core.downloaders.base import Downloader
|
from couchpotato.core.downloaders.base import Downloader
|
||||||
from couchpotato.core.helpers.encoding import tryUrlencode
|
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
|
from couchpotato.core.logger import CPLog
|
||||||
import json
|
import json
|
||||||
import traceback
|
import traceback
|
||||||
@@ -63,86 +63,92 @@ class Sabnzbd(Downloader):
|
|||||||
log.error("Unknown error: " + result[:40])
|
log.error("Unknown error: " + result[:40])
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def getDownloadStatus(self, data = {}, movie = {}):
|
def getAllDownloadStatus(self):
|
||||||
if self.isDisabled(manual = True) or not self.isCorrectType(data.get('type')):
|
if self.isDisabled(manual = False):
|
||||||
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())
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
log.debug('Checking SABnzbd download status.')
|
||||||
|
|
||||||
|
# Go through Queue
|
||||||
try:
|
try:
|
||||||
queue = json.loads(sab)
|
queue = self.call({
|
||||||
|
'mode': 'queue',
|
||||||
|
})
|
||||||
except:
|
except:
|
||||||
log.debug("Result text from SAB: " + sab[:40])
|
log.error('Failed getting queue: %s', traceback.format_exc())
|
||||||
log.error('Failed parsing json status: %s', traceback.format_exc())
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Go through history items
|
# 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:
|
try:
|
||||||
sab = self.urlopen(url, timeout = 60, show_error = False)
|
history = self.call({
|
||||||
|
'mode': 'history',
|
||||||
|
'limit': 15,
|
||||||
|
})
|
||||||
except:
|
except:
|
||||||
log.error('Failed getting history: %s', traceback.format_exc())
|
log.error('Failed getting history json: %s', traceback.format_exc())
|
||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
statuses = []
|
||||||
history = json.loads(sab)
|
|
||||||
except:
|
# Get busy releases
|
||||||
log.debug("Result text from SAB: " + sab[:40])
|
for item in queue.get('slots', []):
|
||||||
log.error('Failed parsing history json: %s', traceback.format_exc())
|
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 False
|
||||||
|
|
||||||
return queue, history
|
log.info('%s failed downloading, deleting...', item['name'])
|
||||||
|
|
||||||
def remove(self, name = {}, nzo_id = {}):
|
try:
|
||||||
# Delete failed download
|
self.call({
|
||||||
if self.conf('delete_failed', default = True):
|
|
||||||
|
|
||||||
log.info('%s failed downloading, deleting...', name)
|
|
||||||
params = {
|
|
||||||
'apikey': self.conf('api_key'),
|
|
||||||
'mode': 'history',
|
'mode': 'history',
|
||||||
'name': 'delete',
|
'name': 'delete',
|
||||||
'del_files': '1',
|
'del_files': '1',
|
||||||
'value': nzo_id
|
'value': item['id']
|
||||||
}
|
}, use_json = False)
|
||||||
url = cleanHost(self.conf('host')) + "api?" + tryUrlencode(params)
|
except:
|
||||||
|
log.error('Failed deleting: %s', traceback.format_exc())
|
||||||
|
return False
|
||||||
|
|
||||||
try:
|
return True
|
||||||
sab = self.urlopen(url, timeout = 60, show_error = False)
|
|
||||||
except:
|
|
||||||
log.error('Failed deleting: %s', traceback.format_exc())
|
|
||||||
return False
|
|
||||||
|
|
||||||
result = sab.strip()
|
def call(self, params, use_json = True):
|
||||||
if not result:
|
|
||||||
log.error("SABnzbd didn't return anything.")
|
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:
|
try:
|
||||||
return library_dict['titles'][0]['title']
|
return library_dict['titles'][0]['title']
|
||||||
except:
|
except:
|
||||||
log.error('Could not get title for %s', library_dict['identifier'])
|
try:
|
||||||
return None
|
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:
|
except:
|
||||||
log.error('Could not get title for library item: %s', library_dict)
|
log.error('Could not get title for library item: %s', library_dict)
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
from couchpotato import addView
|
from couchpotato import addView
|
||||||
from couchpotato.core.event import fireEvent, addEvent
|
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.helpers.variable import getExt
|
||||||
from couchpotato.core.logger import CPLog
|
from couchpotato.core.logger import CPLog
|
||||||
from couchpotato.environment import Env
|
from couchpotato.environment import Env
|
||||||
@@ -245,6 +246,22 @@ class Plugin(object):
|
|||||||
Env.get('cache').set(cache_key, value, timeout)
|
Env.get('cache').set(cache_key, value, timeout)
|
||||||
return value
|
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):
|
def isDisabled(self):
|
||||||
return not self.isEnabled()
|
return not self.isEnabled()
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ log = CPLog(__name__)
|
|||||||
class Renamer(Plugin):
|
class Renamer(Plugin):
|
||||||
|
|
||||||
renaming_started = False
|
renaming_started = False
|
||||||
|
checking_snatched = False
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
@@ -33,6 +34,7 @@ class Renamer(Plugin):
|
|||||||
addEvent('app.load', self.scan)
|
addEvent('app.load', self.scan)
|
||||||
|
|
||||||
fireEvent('schedule.interval', 'renamer.check_snatched', self.checkSnatched, minutes = self.conf('run_every'))
|
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):
|
def scanView(self):
|
||||||
|
|
||||||
@@ -495,6 +497,11 @@ class Renamer(Plugin):
|
|||||||
loge('Couldn\'t remove empty directory %s: %s', (folder, traceback.format_exc()))
|
loge('Couldn\'t remove empty directory %s: %s', (folder, traceback.format_exc()))
|
||||||
|
|
||||||
def checkSnatched(self):
|
def checkSnatched(self):
|
||||||
|
if self.checking_snatched:
|
||||||
|
log.debug('Already checking snatched')
|
||||||
|
|
||||||
|
self.checking_snatched = True
|
||||||
|
|
||||||
snatched_status = fireEvent('status.get', 'snatched', single = True)
|
snatched_status = fireEvent('status.get', 'snatched', single = True)
|
||||||
ignored_status = fireEvent('status.get', 'ignored', single = True)
|
ignored_status = fireEvent('status.get', 'ignored', single = True)
|
||||||
failed_status = fireEvent('status.get', 'failed', single = True)
|
failed_status = fireEvent('status.get', 'failed', single = True)
|
||||||
@@ -504,81 +511,75 @@ class Renamer(Plugin):
|
|||||||
db = get_session()
|
db = get_session()
|
||||||
rels = db.query(Release).filter_by(status_id = snatched_status.get('id')).all()
|
rels = db.query(Release).filter_by(status_id = snatched_status.get('id')).all()
|
||||||
|
|
||||||
|
scan_required = False
|
||||||
|
|
||||||
if rels:
|
if rels:
|
||||||
|
self.checking_snatched = True
|
||||||
log.debug('Checking status snatched releases...')
|
log.debug('Checking status snatched releases...')
|
||||||
# get queue and history (once) from SABnzbd
|
# get queue and history (once) from SABnzbd
|
||||||
queue, history = fireEvent('download.status', data = {}, movie = {}, single = True)
|
statuses = fireEvent('download.status', merge = True)
|
||||||
|
if not statuses:
|
||||||
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:
|
|
||||||
log.debug('Download status functionality is not implemented for active downloaders.')
|
log.debug('Download status functionality is not implemented for active downloaders.')
|
||||||
scan_required = True
|
scan_required = True
|
||||||
else:
|
|
||||||
log.debug('Download status: %s' , downloadstatus)
|
|
||||||
|
|
||||||
if downloadstatus == 'failed':
|
try:
|
||||||
if self.conf('next_on_failed'):
|
for rel in rels:
|
||||||
fireEvent('searcher.try_next_release', movie_id = rel.movie_id)
|
rel_dict = rel.to_dict({'info': {}})
|
||||||
else:
|
|
||||||
rel.status_id = failed_status.get('id')
|
# 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()
|
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':
|
except:
|
||||||
log.info('Download of %s completed!', item['name'])
|
log.error('Failed checking for release in downloader: %s', traceback.format_exc())
|
||||||
scan_required = True
|
|
||||||
|
|
||||||
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:
|
if scan_required:
|
||||||
fireEvent('renamer.scan')
|
fireEvent('renamer.scan')
|
||||||
|
|
||||||
|
self.checking_snatched = False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|||||||
Reference in New Issue
Block a user