Make sure downloaders and providers match

Remove releases not found anymore on new searches
This commit is contained in:
Ruud
2013-01-12 16:07:11 +01:00
parent a0dc5c075a
commit 71b22345bc
13 changed files with 200 additions and 124 deletions
+35 -11
View File
@@ -33,17 +33,36 @@ class Downloader(Provider):
] ]
def __init__(self): def __init__(self):
addEvent('download', self.download) addEvent('download', self._download)
addEvent('download.status', self.getAllDownloadStatus) addEvent('download.enabled', self._isEnabled)
addEvent('download.remove_failed', self.removeFailed) addEvent('download.enabled_types', self.getEnabledDownloadType)
addEvent('download.status', self._getAllDownloadStatus)
addEvent('download.remove_failed', self._removeFailed)
def download(self, data = {}, movie = {}, manual = False, filedata = None): def getEnabledDownloadType(self):
pass for download_type in self.type:
if self.isEnabled(manual = True, data = {'type': download_type}):
return self.type
def getAllDownloadStatus(self): return []
def _download(self, data = {}, movie = {}, manual = False, filedata = None):
if self.isDisabled(manual, data):
return
return self.download(data = data, movie = movie, filedata = filedata)
def _getAllDownloadStatus(self):
if self.isDisabled(manual = True, data = {}):
return
return self.getAllDownloadStatus()
def _removeFailed(self, item):
if self.conf('delete_failed', default = True):
return self.removeFailed(self, item)
return False return False
def removeFailed(self, name = {}, nzo_id = {}): def removeFailed(self, item):
return False return False
def isCorrectType(self, item_type): def isCorrectType(self, item_type):
@@ -77,9 +96,14 @@ class Downloader(Provider):
log.error('Failed converting magnet url to torrent: %s', (torrent_hash)) log.error('Failed converting magnet url to torrent: %s', (torrent_hash))
return False return False
def isDisabled(self, manual): def isDisabled(self, manual, data):
return not self.isEnabled(manual) return not self.isEnabled(manual, data)
def isEnabled(self, manual): def _isEnabled(self, manual, data = {}):
if not self.isEnabled(manual, data):
return
return True
def isEnabled(self, manual, data = {}):
d_manual = self.conf('manual', default = False) d_manual = self.conf('manual', default = False)
return super(Downloader, self).isEnabled() and ((d_manual and manual) or (d_manual is False)) return super(Downloader, self).isEnabled() and ((d_manual and manual) or (d_manual is False)) and self.isCorrectType(data.get('type'))
+14 -5
View File
@@ -10,11 +10,7 @@ class Blackhole(Downloader):
type = ['nzb', 'torrent', 'torrent_magnet'] type = ['nzb', 'torrent', 'torrent_magnet']
def download(self, data = {}, movie = {}, manual = False, filedata = None): def download(self, data = {}, movie = {}, filedata = None):
if self.isDisabled(manual) or \
(not self.isCorrectType(data.get('type')) or \
(not self.conf('use_for') in ['both', 'torrent' if 'torrent' in data.get('type') else data.get('type')])):
return
directory = self.conf('directory') directory = self.conf('directory')
if not directory or not os.path.isdir(directory): if not directory or not os.path.isdir(directory):
@@ -52,4 +48,17 @@ class Blackhole(Downloader):
except: except:
log.info('Failed to download file %s: %s', (data.get('name'), traceback.format_exc())) log.info('Failed to download file %s: %s', (data.get('name'), traceback.format_exc()))
return False return False
return False return False
def getEnabledDownloadType(self):
if self.conf('use_for') == 'both':
return super(Blackhole, self).getEnabledDownloadType()
elif self.conf('use_for') == 'torrent':
return ['torrent', 'torrent_magnet']
else:
return ['nzb']
def isEnabled(self, manual, data = {}):
return super(Blackhole, self).isEnabled(manual, data) and \
((self.conf('use_for') in ['both', 'torrent' if 'torrent' in data.get('type') else data.get('type')]))
+1 -4
View File
@@ -15,10 +15,7 @@ class NZBGet(Downloader):
url = 'http://nzbget:%(password)s@%(host)s/xmlrpc' url = 'http://nzbget:%(password)s@%(host)s/xmlrpc'
def download(self, data = {}, movie = {}, manual = False, filedata = None): def download(self, data = {}, movie = {}, filedata = None):
if self.isDisabled(manual) or not self.isCorrectType(data.get('type')):
return
if not filedata: if not filedata:
log.error('Unable to get NZB file: %s', traceback.format_exc()) log.error('Unable to get NZB file: %s', traceback.format_exc())
+4 -10
View File
@@ -22,10 +22,7 @@ class NZBVortex(Downloader):
api_level = None api_level = None
session_id = None session_id = None
def download(self, data = {}, movie = {}, manual = False, filedata = None): def download(self, data = {}, movie = {}, filedata = None):
if self.isDisabled(manual) or not self.isCorrectType(data.get('type')) or not self.getApiLevel():
return
# Send the nzb # Send the nzb
try: try:
@@ -39,9 +36,6 @@ class NZBVortex(Downloader):
def getAllDownloadStatus(self): def getAllDownloadStatus(self):
if self.isDisabled(manual = True):
return False
raw_statuses = self.call('nzb') raw_statuses = self.call('nzb')
statuses = [] statuses = []
@@ -66,9 +60,6 @@ class NZBVortex(Downloader):
def removeFailed(self, item): def removeFailed(self, item):
if not self.conf('delete_failed', default = True):
return False
log.info('%s failed downloading, deleting...', item['name']) log.info('%s failed downloading, deleting...', item['name'])
try: try:
@@ -153,6 +144,9 @@ class NZBVortex(Downloader):
return self.api_level return self.api_level
def isEnabled(self, manual, data):
return super(NZBVortex, self).isEnabled(manual, data) and self.getApiLevel()
class HTTPSConnection(httplib.HTTPSConnection): class HTTPSConnection(httplib.HTTPSConnection):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@@ -11,9 +11,7 @@ class Pneumatic(Downloader):
type = ['nzb'] type = ['nzb']
strm_syntax = 'plugin://plugin.program.pneumatic/?mode=strm&type=add_file&nzb=%s&nzbname=%s' strm_syntax = 'plugin://plugin.program.pneumatic/?mode=strm&type=add_file&nzb=%s&nzbname=%s'
def download(self, data = {}, movie = {}, manual = False, filedata = None): def download(self, data = {}, movie = {}, filedata = None):
if self.isDisabled(manual) or (not self.isCorrectType(data.get('type'))):
return
directory = self.conf('directory') directory = self.conf('directory')
if not directory or not os.path.isdir(directory): if not directory or not os.path.isdir(directory):
+1 -9
View File
@@ -12,10 +12,7 @@ class Sabnzbd(Downloader):
type = ['nzb'] type = ['nzb']
def download(self, data = {}, movie = {}, manual = False, filedata = None): def download(self, data = {}, movie = {}, filedata = None):
if self.isDisabled(manual) or not self.isCorrectType(data.get('type')):
return
log.info('Sending "%s" to SABnzbd.', data.get('name')) log.info('Sending "%s" to SABnzbd.', data.get('name'))
@@ -65,8 +62,6 @@ class Sabnzbd(Downloader):
return False return False
def getAllDownloadStatus(self): def getAllDownloadStatus(self):
if self.isDisabled(manual = True):
return False
log.debug('Checking SABnzbd download status.') log.debug('Checking SABnzbd download status.')
@@ -122,9 +117,6 @@ class Sabnzbd(Downloader):
def removeFailed(self, item): def removeFailed(self, item):
if not self.conf('delete_failed', default = True):
return False
log.info('%s failed downloading, deleting...', item['name']) log.info('%s failed downloading, deleting...', item['name'])
try: try:
@@ -14,10 +14,7 @@ class Synology(Downloader):
type = ['torrent_magnet'] type = ['torrent_magnet']
log = CPLog(__name__) log = CPLog(__name__)
def download(self, data, movie, manual = False, filedata = None): def download(self, data, movie, filedata = None):
if self.isDisabled(manual) or not self.isCorrectType(data.get('type')):
return
log.error('Sending "%s" (%s) to Synology.', (data.get('name'), data.get('type'))) log.error('Sending "%s" (%s) to Synology.', (data.get('name'), data.get('type')))
@@ -16,10 +16,7 @@ class Transmission(Downloader):
type = ['torrent', 'torrent_magnet'] type = ['torrent', 'torrent_magnet']
log = CPLog(__name__) log = CPLog(__name__)
def download(self, data, movie, manual = False, filedata = None): def download(self, data, movie, filedata = None):
if self.isDisabled(manual) or not self.isCorrectType(data.get('type')):
return
log.debug('Sending "%s" (%s) to Transmission.', (data.get('name'), data.get('type'))) log.debug('Sending "%s" (%s) to Transmission.', (data.get('name'), data.get('type')))
@@ -20,10 +20,7 @@ class uTorrent(Downloader):
type = ['torrent', 'torrent_magnet'] type = ['torrent', 'torrent_magnet']
utorrent_api = None utorrent_api = None
def download(self, data, movie, manual = False, filedata = None): def download(self, data, movie, filedata = None):
if self.isDisabled(manual) or not self.isCorrectType(data.get('type')):
return
log.debug('Sending "%s" (%s) to uTorrent.', (data.get('name'), data.get('type'))) log.debug('Sending "%s" (%s) to uTorrent.', (data.get('name'), data.get('type')))
+2 -1
View File
@@ -115,7 +115,8 @@ def fireEvent(name, *args, **kwargs):
elif isinstance(results[0], list): elif isinstance(results[0], list):
merged = [] merged = []
for result in results: for result in results:
merged += result if result not in merged:
merged += result
results = merged results = merged
+121 -67
View File
@@ -91,31 +91,44 @@ class Searcher(Plugin):
'to_go': len(movies), 'to_go': len(movies),
} }
for movie in movies: try:
movie_dict = movie.to_dict({ search_types = self.getSearchTypes()
'profile': {'types': {'quality': {}}},
'releases': {'status': {}, 'quality': {}},
'library': {'titles': {}, 'files':{}},
'files': {}
})
try: for movie in movies:
self.single(movie_dict) movie_dict = movie.to_dict({
except IndexError: 'profile': {'types': {'quality': {}}},
log.error('Forcing library update for %s, if you see this often, please report: %s', (movie_dict['library']['identifier'], traceback.format_exc())) 'releases': {'status': {}, 'quality': {}},
fireEvent('library.update', movie_dict['library']['identifier'], force = True) 'library': {'titles': {}, 'files':{}},
except: 'files': {}
log.error('Search failed for %s: %s', (movie_dict['library']['identifier'], traceback.format_exc())) })
self.in_progress['to_go'] -= 1 try:
self.single(movie_dict, search_types)
except IndexError:
log.error('Forcing library update for %s, if you see this often, please report: %s', (movie_dict['library']['identifier'], traceback.format_exc()))
fireEvent('library.update', movie_dict['library']['identifier'], force = True)
except:
log.error('Search failed for %s: %s', (movie_dict['library']['identifier'], traceback.format_exc()))
# Break if CP wants to shut down self.in_progress['to_go'] -= 1
if self.shuttingDown():
break # Break if CP wants to shut down
if self.shuttingDown():
break
except SearchSetupError:
pass
self.in_progress = False self.in_progress = False
def single(self, movie): def single(self, movie, search_types = None):
# Find out search type
try:
if not search_types:
search_types = self.getSearchTypes()
except SearchSetupError:
return
done_status = fireEvent('status.get', 'done', single = True) done_status = fireEvent('status.get', 'done', single = True)
@@ -140,6 +153,7 @@ class Searcher(Plugin):
fireEvent('notify.frontend', type = 'searcher.started.%s' % movie['id'], data = True, message = 'Searching for "%s"' % default_title) fireEvent('notify.frontend', type = 'searcher.started.%s' % movie['id'], data = True, message = 'Searching for "%s"' % default_title)
ret = False ret = False
for quality_type in movie['profile']['types']: for quality_type in movie['profile']['types']:
if not self.couldBeReleased(quality_type['quality']['identifier'], release_dates, pre_releases): if not self.couldBeReleased(quality_type['quality']['identifier'], release_dates, pre_releases):
@@ -159,7 +173,11 @@ class Searcher(Plugin):
log.info('Search for %s in %s', (default_title, quality_type['quality']['label'])) log.info('Search for %s in %s', (default_title, quality_type['quality']['label']))
quality = fireEvent('quality.single', identifier = quality_type['quality']['identifier'], single = True) quality = fireEvent('quality.single', identifier = quality_type['quality']['identifier'], single = True)
results = fireEvent('yarr.search', movie, quality, merge = True) results = []
for search_type in search_types:
type_results = fireEvent('%s.search' % search_type, movie, quality, merge = True)
if type_results:
results += type_results
sorted_results = sorted(results, key = lambda k: k['score'], reverse = True) sorted_results = sorted(results, key = lambda k: k['score'], reverse = True)
if len(sorted_results) == 0: if len(sorted_results) == 0:
@@ -230,6 +248,12 @@ class Searcher(Plugin):
break break
elif downloaded != 'try_next': elif downloaded != 'try_next':
break break
# Remove releases that aren't found anymore
for release in movie.get('releases', []):
if release.get('identifier') not in found_releases:
fireEvent('release.delete', release.get('id'), single = True)
else: else:
log.info('Better quality (%s) already available or snatched for %s', (quality_type['quality']['label'], default_title)) log.info('Better quality (%s) already available or snatched for %s', (quality_type['quality']['label'], default_title))
fireEvent('movie.restatus', movie['id']) fireEvent('movie.restatus', movie['id'])
@@ -239,72 +263,93 @@ class Searcher(Plugin):
if self.shuttingDown() or ret: if self.shuttingDown() or ret:
break break
# Remove releases that aren't found anymore
for release in movie.get('releases', []):
if release.get('identifier') not in found_releases:
fireEvent('release.delete', release.get('id'), single = True)
fireEvent('notify.frontend', type = 'searcher.ended.%s' % movie['id'], data = True) fireEvent('notify.frontend', type = 'searcher.ended.%s' % movie['id'], data = True)
return ret return ret
def download(self, data, movie, manual = False): def download(self, data, movie, manual = False):
snatched_status = fireEvent('status.get', 'snatched', single = True) # Test to see if any downloaders are enabled for this type
downloader_enabled = fireEvent('download.enabled', manual, data, single = True)
# Download movie to temp if downloader_enabled:
filedata = None
if data.get('download') and (ismethod(data.get('download')) or isfunction(data.get('download'))):
filedata = data.get('download')(url = data.get('url'), nzb_id = data.get('id'))
if filedata == 'try_next':
return filedata
successful = fireEvent('download', data = data, movie = movie, manual = manual, filedata = filedata, single = True) snatched_status = fireEvent('status.get', 'snatched', single = True)
if successful: # Download movie to temp
filedata = None
if data.get('download') and (ismethod(data.get('download')) or isfunction(data.get('download'))):
filedata = data.get('download')(url = data.get('url'), nzb_id = data.get('id'))
if filedata == 'try_next':
return filedata
try: successful = fireEvent('download', data = data, movie = movie, manual = manual, filedata = filedata, single = True)
# Mark release as snatched
db = get_session()
rls = db.query(Release).filter_by(identifier = md5(data['url'])).first()
if rls:
rls.status_id = snatched_status.get('id')
db.commit()
log_movie = '%s (%s) in %s' % (getTitle(movie['library']), movie['library']['year'], rls.quality.label) if successful:
snatch_message = 'Snatched "%s": %s' % (data.get('name'), log_movie)
log.info(snatch_message)
fireEvent('movie.snatched', message = snatch_message, data = rls.to_dict())
# If renamer isn't used, mark movie done try:
if not Env.setting('enabled', 'renamer'): # Mark release as snatched
active_status = fireEvent('status.get', 'active', single = True) db = get_session()
done_status = fireEvent('status.get', 'done', single = True) rls = db.query(Release).filter_by(identifier = md5(data['url'])).first()
try: if rls:
if movie['status_id'] == active_status.get('id'): rls.status_id = snatched_status.get('id')
for profile_type in movie['profile']['types']: db.commit()
if rls and profile_type['quality_id'] == rls.quality.id and profile_type['finish']:
log.info('Renamer disabled, marking movie as finished: %s', log_movie)
# Mark release done log_movie = '%s (%s) in %s' % (getTitle(movie['library']), movie['library']['year'], rls.quality.label)
rls.status_id = done_status.get('id') snatch_message = 'Snatched "%s": %s' % (data.get('name'), log_movie)
db.commit() log.info(snatch_message)
fireEvent('movie.snatched', message = snatch_message, data = rls.to_dict())
# Mark movie done # If renamer isn't used, mark movie done
mvie = db.query(Movie).filter_by(id = movie['id']).first() if not Env.setting('enabled', 'renamer'):
mvie.status_id = done_status.get('id') active_status = fireEvent('status.get', 'active', single = True)
db.commit() done_status = fireEvent('status.get', 'done', single = True)
except: try:
log.error('Failed marking movie finished, renamer disabled: %s', traceback.format_exc()) if movie['status_id'] == active_status.get('id'):
for profile_type in movie['profile']['types']:
if rls and profile_type['quality_id'] == rls.quality.id and profile_type['finish']:
log.info('Renamer disabled, marking movie as finished: %s', log_movie)
except: # Mark release done
log.error('Failed marking movie finished: %s', traceback.format_exc()) rls.status_id = done_status.get('id')
db.commit()
return True # Mark movie done
mvie = db.query(Movie).filter_by(id = movie['id']).first()
mvie.status_id = done_status.get('id')
db.commit()
except:
log.error('Failed marking movie finished, renamer disabled: %s', traceback.format_exc())
except:
log.error('Failed marking movie finished: %s', traceback.format_exc())
return True
log.info('Tried to download, but none of the "%s" downloaders are enabled', (data.get('type', '')))
log.info('Tried to download, but none of the downloaders are enabled')
return False return False
def getSearchTypes(self):
download_types = fireEvent('download.enabled_types', merge = True)
provider_types = fireEvent('provider.enabled_types', merge = True)
if download_types and len(list(set(provider_types) & set(download_types))) == 0:
log.error('There aren\'t any providers enabled for your downloader (%s). Check your settings.', ','.join(download_types))
raise NoProviders
for useless_provider in list(set(provider_types) - set(download_types)):
log.debug('Provider for "%s" enabled, but no downloader.', useless_provider)
search_types = download_types
if len(search_types) == 0:
log.error('There aren\'t any downloaders enabled. Please pick one in settings.')
raise NoDownloaders
return search_types
def correctMovie(self, nzb = {}, movie = {}, quality = {}, **kwargs): def correctMovie(self, nzb = {}, movie = {}, quality = {}, **kwargs):
imdb_results = kwargs.get('imdb_results', False) imdb_results = kwargs.get('imdb_results', False)
@@ -548,3 +593,12 @@ class Searcher(Plugin):
except: except:
log.error('Failed searching for next release: %s', traceback.format_exc()) log.error('Failed searching for next release: %s', traceback.format_exc())
return False return False
class SearchSetupError(Exception):
pass
class NoDownloaders(SearchSetupError):
pass
class NoProviders(SearchSetupError):
pass
+8
View File
@@ -84,8 +84,16 @@ class YarrProvider(Provider):
login_opener = None login_opener = None
def __init__(self): def __init__(self):
addEvent('provider.enabled_types', self.getEnabledProviderType)
addEvent('provider.belongs_to', self.belongsTo) addEvent('provider.belongs_to', self.belongsTo)
addEvent('yarr.search', self.search) addEvent('yarr.search', self.search)
addEvent('%s.search' % self.type, self.search)
def getEnabledProviderType(self):
if self.isEnabled():
return self.type
else:
return []
def login(self): def login(self):
+10 -2
View File
@@ -109,10 +109,18 @@ class Newznab(NZBProvider, RSS):
return cleanHost(host) + 'api?t=' + type return cleanHost(host) + 'api?t=' + type
def isDisabled(self, host): def isDisabled(self, host = None):
return not self.isEnabled(host) return not self.isEnabled(host)
def isEnabled(self, host): def isEnabled(self, host = None):
# Return true if at least one is enabled and no host is given
if host is None:
for host in self.getHosts():
if self.isEnabled(host):
return True
return False
return NZBProvider.isEnabled(self) and host['host'] and host['api_key'] and int(host['use']) return NZBProvider.isEnabled(self) and host['host'] and host['api_key'] and int(host['use'])
def getApiExt(self, host): def getApiExt(self, host):