Merge branch 'refs/heads/develop' into desktop

This commit is contained in:
Ruud committed 2013-01-14 19:54:22 +01:00
commit 178c8942c3
26 files changed
+347 -145

No files matched your search

+10 -8
View File
@@ -23,20 +23,22 @@ config = [{
'default': '', 'default': '',
'type': 'password', 'type': 'password',
}, },
{
'name': 'host',
'advanced': True,
'default': '0.0.0.0',
'hidden': True,
'label': 'IP',
'description': 'Host that I should listen to. "0.0.0.0" listens to all ips.',
},
{ {
'name': 'port', 'name': 'port',
'default': 5050, 'default': 5050,
'type': 'int', 'type': 'int',
'description': 'The port I should listen to.', 'description': 'The port I should listen to.',
}, },
{
'name': 'ssl_cert',
'description': 'Path to SSL server.crt',
'advanced': True,
},
{
'name': 'ssl_key',
'description': 'Path to SSL server.key',
'advanced': True,
},
{ {
'name': 'launch_browser', 'name': 'launch_browser',
'default': True, 'default': True,
+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')]))
@@ -33,6 +33,13 @@ config = [{
'default': 'Movies', 'default': 'Movies',
'description': 'The category CP places the nzb in. Like <strong>movies</strong> or <strong>couchpotato</strong>', 'description': 'The category CP places the nzb in. Like <strong>movies</strong> or <strong>couchpotato</strong>',
}, },
{
'name': 'priority',
'default': '0',
'type': 'dropdown',
'values': [('Very Low', -100), ('Low', -50), ('Normal', 0), ('High', 50), ('Very High', 100)],
'description': 'Only change this if you are using NZBget 9.0 or higher',
},
{ {
'name': 'manual', 'name': 'manual',
'default': 0, 'default': 0,
+9 -6
View File
@@ -1,7 +1,8 @@
from base64 import standard_b64encode from base64 import standard_b64encode
from couchpotato.core.downloaders.base import Downloader from couchpotato.core.downloaders.base import Downloader
from couchpotato.core.helpers.variable import tryInt
from couchpotato.core.logger import CPLog from couchpotato.core.logger import CPLog
from inspect import isfunction import re
import socket import socket
import traceback import traceback
import xmlrpclib import xmlrpclib
@@ -14,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())
@@ -44,7 +42,12 @@ class NZBGet(Downloader):
log.error('Protocol Error: %s', e) log.error('Protocol Error: %s', e)
return False return False
if rpc.append(nzb_name, self.conf('category'), False, standard_b64encode(filedata.strip())): if re.search(r"^0", rpc.version()):
xml_response = rpc.append(nzb_name, self.conf('category'), False, standard_b64encode(filedata.strip()))
else:
xml_response = rpc.append(nzb_name, self.conf('category'), tryInt(self.conf('priority')), False, standard_b64encode(filedata.strip()))
if xml_response:
log.info('NZB sent successfully to NZBGet') log.info('NZB sent successfully to NZBGet')
return True return True
else: else:
+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')))
@@ -1,6 +1,6 @@
from bencode import bencode, bdecode from bencode import bencode, bdecode
from couchpotato.core.downloaders.base import Downloader from couchpotato.core.downloaders.base import Downloader
from couchpotato.core.helpers.encoding import isInt from couchpotato.core.helpers.encoding import isInt, ss
from couchpotato.core.logger import CPLog from couchpotato.core.logger import CPLog
from hashlib import sha1 from hashlib import sha1
from multipartpost import MultipartPostHandler from multipartpost import MultipartPostHandler
@@ -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')))
@@ -125,7 +122,7 @@ class uTorrentAPI(object):
def add_torrent_file(self, filename, filedata): def add_torrent_file(self, filename, filedata):
action = "action=add-file" action = "action=add-file"
return self._request(action, {"torrent_file": (filename, filedata)}) return self._request(action, {"torrent_file": (ss(filename), filedata)})
def set_torrent(self, hash, params): def set_torrent(self, hash, params):
action = "action=setprops&hash=%s" % hash action = "action=setprops&hash=%s" % hash
+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
+5 -4
View File
@@ -13,6 +13,7 @@ class XBMC(Notification):
listen_to = ['renamer.after'] listen_to = ['renamer.after']
use_json_notifications = {} use_json_notifications = {}
couch_logo_url = 'https://raw.github.com/RuudBurger/CouchPotatoServer/master/couchpotato/static/images/xbmc-notify.png'
def notify(self, message = '', data = {}, listener = None): def notify(self, message = '', data = {}, listener = None):
if self.isDisabled(): return if self.isDisabled(): return
@@ -27,7 +28,7 @@ class XBMC(Notification):
if self.use_json_notifications.get(host): if self.use_json_notifications.get(host):
response = self.request(host, [ response = self.request(host, [
('GUI.ShowNotification', {'title':self.default_title, 'message':message}), ('GUI.ShowNotification', {'title': self.default_title, 'message': message, 'image': self.couch_logo_url}),
('VideoLibrary.Scan', {}), ('VideoLibrary.Scan', {}),
]) ])
else: else:
@@ -89,7 +90,7 @@ class XBMC(Notification):
self.use_json_notifications[host] = True self.use_json_notifications[host] = True
# send the text message # send the text message
resp = self.request(host, [('GUI.ShowNotification', {'title':self.default_title, 'message':message})]) resp = self.request(host, [('GUI.ShowNotification', {'title':self.default_title, 'message':message, 'image':self.couch_logo_url})])
for result in resp: for result in resp:
if (result.get('result') and result['result'] == 'OK'): if (result.get('result') and result['result'] == 'OK'):
log.debug('Message delivered successfully!') log.debug('Message delivered successfully!')
@@ -111,8 +112,8 @@ class XBMC(Notification):
server = 'http://%s/xbmcCmds/' % host server = 'http://%s/xbmcCmds/' % host
# title, message [, timeout , image #can be added!] # Notification(title, message [, timeout , image])
cmd = "xbmcHttp?command=ExecBuiltIn(Notification('%s','%s'))" % (urllib.quote(data['title']), urllib.quote(data['message'])) cmd = "xbmcHttp?command=ExecBuiltIn(Notification(%s,%s,'',%s))" % (urllib.quote(data['title']), urllib.quote(data['message']), urllib.quote(self.couch_logo_url))
server += cmd server += cmd
# I have no idea what to set to, just tried text/plain and seems to be working :) # I have no idea what to set to, just tried text/plain and seems to be working :)
-1
View File
@@ -135,7 +135,6 @@ class Manage(Plugin):
already_used = used_files.get(release_file['path']) already_used = used_files.get(release_file['path'])
if already_used: if already_used:
print already_used, release['id']
if already_used < release['id']: if already_used < release['id']:
fireEvent('release.delete', release['id'], single = True) # delete this one fireEvent('release.delete', release['id'], single = True) # delete this one
else: else:
+130 -64
View File
@@ -12,6 +12,7 @@ from couchpotato.environment import Env
from inspect import ismethod, isfunction from inspect import ismethod, isfunction
from sqlalchemy.exc import InterfaceError from sqlalchemy.exc import InterfaceError
import datetime import datetime
import random
import re import re
import time import time
import traceback import traceback
@@ -83,37 +84,51 @@ class Searcher(Plugin):
movies = db.query(Movie).filter( movies = db.query(Movie).filter(
Movie.status.has(identifier = 'active') Movie.status.has(identifier = 'active')
).all() ).all()
random.shuffle(movies)
self.in_progress = { self.in_progress = {
'total': len(movies), 'total': len(movies),
'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)
@@ -128,6 +143,8 @@ class Searcher(Plugin):
available_status = fireEvent('status.get', 'available', single = True) available_status = fireEvent('status.get', 'available', single = True)
ignored_status = fireEvent('status.get', 'ignored', single = True) ignored_status = fireEvent('status.get', 'ignored', single = True)
found_releases = []
default_title = getTitle(movie['library']) default_title = getTitle(movie['library'])
if not default_title: if not default_title:
log.error('No proper info found for movie, removing it from library to cause it from having more issues.') log.error('No proper info found for movie, removing it from library to cause it from having more issues.')
@@ -136,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):
@@ -155,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:
@@ -172,10 +194,13 @@ class Searcher(Plugin):
# Add them to this movie releases list # Add them to this movie releases list
for nzb in sorted_results: for nzb in sorted_results:
rls = db.query(Release).filter_by(identifier = md5(nzb['url'])).first() nzb_identifier = md5(nzb['url'])
found_releases.append(nzb_identifier)
rls = db.query(Release).filter_by(identifier = nzb_identifier).first()
if not rls: if not rls:
rls = Release( rls = Release(
identifier = md5(nzb['url']), identifier = nzb_identifier,
movie_id = movie.get('id'), movie_id = movie.get('id'),
quality_id = quality_type.get('quality_id'), quality_id = quality_type.get('quality_id'),
status_id = available_status.get('id') status_id = available_status.get('id')
@@ -223,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'])
@@ -238,61 +269,87 @@ class Searcher(Plugin):
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)
@@ -536,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
@@ -49,6 +49,7 @@ class Subtitle(Plugin):
available_languages = sum(group['subtitle_language'].itervalues(), []) available_languages = sum(group['subtitle_language'].itervalues(), [])
downloaded = [] downloaded = []
files = [toUnicode(x) for x in group['files']['movie']] files = [toUnicode(x) for x in group['files']['movie']]
log.debug('Searching for subtitles for: %s', files)
for lang in self.getLanguages(): for lang in self.getLanguages():
if lang not in available_languages: if lang not in available_languages:
@@ -57,6 +58,7 @@ class Subtitle(Plugin):
downloaded.extend(download[subtitle]) downloaded.extend(download[subtitle])
for d_sub in downloaded: for d_sub in downloaded:
log.info('Found subtitle (%s): %s', (d_sub.language.alpha2, files))
group['files']['subtitle'].add(d_sub.path) group['files']['subtitle'].add(d_sub.path)
group['subtitle_language'][d_sub.path] = [d_sub.language.alpha2] group['subtitle_language'][d_sub.path] = [d_sub.language.alpha2]
@@ -0,0 +1,28 @@
from .main import Rottentomatoes
def start():
return Rottentomatoes()
config = [{
'name': 'rottentomatoes',
'groups': [
{
'tab': 'automation',
'name': 'rottentomatoes_automation',
'label': 'Rottentomatoes',
'description': 'Imports movies from the rottentomatoes "in theaters"-feed.',
'options': [
{
'name': 'automation_enabled',
'default': False,
'type': 'enabler',
},
{
'name': 'tomatometer_percent',
'default': '80',
'label': 'Tomatometer'
}
],
},
],
}]
@@ -0,0 +1,48 @@
from couchpotato.core.helpers.rss import RSS
from couchpotato.core.helpers.variable import tryInt
from couchpotato.core.logger import CPLog
from couchpotato.core.providers.automation.base import Automation
from xml.etree.ElementTree import QName
import datetime
import re
log = CPLog(__name__)
class Rottentomatoes(Automation, RSS):
interval = 1800
urls = {
'namespace': 'http://www.rottentomatoes.com/xmlns/rtmovie/',
'theater': 'http://www.rottentomatoes.com/syndication/rss/in_theaters.xml',
}
def getIMDBids(self):
movies = []
rss_movies = self.getRSSData(self.urls['theater'])
rating_tag = str(QName(self.urls['namespace'], 'tomatometer_percent'))
for movie in rss_movies:
value = self.getTextElement(movie, "title")
result = re.search('(?<=%\s).*', value)
if result:
log.info2('Something smells...')
rating = tryInt(self.getTextElement(movie, rating_tag))
name = result.group(0)
if rating < tryInt(self.conf('tomatometer_percent')):
log.info2('%s seems to be rotten...' % name)
else:
log.info2('Found %s fresh enough movies, enqueuing: %s' % (rating, name))
year = datetime.datetime.now().strftime("%Y")
imdb = self.search(name, year)
if imdb:
movies.append(imdb['imdb'])
return movies
+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):
@@ -4,6 +4,7 @@ from couchpotato.core.logger import CPLog
from couchpotato.core.providers.nzb.base import NZBProvider from couchpotato.core.providers.nzb.base import NZBProvider
from couchpotato.environment import Env from couchpotato.environment import Env
from dateutil.parser import parse from dateutil.parser import parse
import json
import traceback import traceback
log = CPLog(__name__) log = CPLog(__name__)
@@ -15,7 +16,7 @@ class FTDWorld(NZBProvider):
'search': 'http://ftdworld.net/api/index.php?%s', 'search': 'http://ftdworld.net/api/index.php?%s',
'detail': 'http://ftdworld.net/spotinfo.php?id=%s', 'detail': 'http://ftdworld.net/spotinfo.php?id=%s',
'download': 'http://ftdworld.net/cgi-bin/nzbdown.pl?fileID=%s', 'download': 'http://ftdworld.net/cgi-bin/nzbdown.pl?fileID=%s',
'login': 'http://ftdworld.net/index.php', 'login': 'http://ftdworld.net/api/login.php',
} }
http_time_between_calls = 3 #seconds http_time_between_calls = 3 #seconds
@@ -56,6 +57,7 @@ class FTDWorld(NZBProvider):
'id': nzb_id, 'id': nzb_id,
'name': toUnicode(item.get('Title')), 'name': toUnicode(item.get('Title')),
'age': self.calculateAge(tryInt(item.get('Created'))), 'age': self.calculateAge(tryInt(item.get('Created'))),
'size': item.get('Size', 0),
'url': self.urls['download'] % nzb_id, 'url': self.urls['download'] % nzb_id,
'download': self.loginDownload, 'download': self.loginDownload,
'detail_url': self.urls['detail'] % nzb_id, 'detail_url': self.urls['detail'] % nzb_id,
@@ -73,4 +75,7 @@ class FTDWorld(NZBProvider):
}) })
def loginSuccess(self, output): def loginSuccess(self, output):
return 'password is incorrect' not in output try:
return json.loads(output).get('goodToGo', False)
except:
return False
+13 -2
View File
@@ -104,12 +104,23 @@ class Newznab(NZBProvider, RSS):
return result return result
def getUrl(self, host, type): def getUrl(self, host, type):
if '?page=newznabapi' in host:
return cleanHost(host)[:-1] + '&t=' + type
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):
@@ -10,7 +10,7 @@ config = [{
'tab': 'searcher', 'tab': 'searcher',
'subtab': 'nzb_providers', 'subtab': 'nzb_providers',
'name': 'OMGWTFNZBs', 'name': 'OMGWTFNZBs',
'description': 'See <a href="http://www.omgwtfnzbs.com/">OMGWTFNZBs</a>', 'description': 'See <a href="http://omgwtfnzbs.org/">OMGWTFNZBs</a>',
'options': [ 'options': [
{ {
'name': 'enabled', 'name': 'enabled',
@@ -59,3 +59,6 @@ class TorrentDay(TorrentProvider):
'password': self.conf('password'), 'password': self.conf('password'),
'submit': 'submit', 'submit': 'submit',
}) })
def loginSuccess(self, output):
return 'Password not correct' not in output
+13 -3
View File
@@ -4,6 +4,7 @@ from couchpotato.api import api, NonBlockHandler
from couchpotato.core.event import fireEventAsync, fireEvent from couchpotato.core.event import fireEventAsync, fireEvent
from couchpotato.core.helpers.variable import getDataDir, tryInt from couchpotato.core.helpers.variable import getDataDir, tryInt
from logging import handlers from logging import handlers
from tornado.httpserver import HTTPServer
from tornado.web import Application, FallbackHandler from tornado.web import Application, FallbackHandler
from tornado.wsgi import WSGIContainer from tornado.wsgi import WSGIContainer
from werkzeug.contrib.cache import FileSystemCache from werkzeug.contrib.cache import FileSystemCache
@@ -210,8 +211,9 @@ def runCouchPotato(options, base_path, args, data_dir = None, log_dir = None, En
# app.debug = development # app.debug = development
config = { config = {
'use_reloader': reloader, 'use_reloader': reloader,
'host': Env.setting('host', default = '0.0.0.0'), 'port': tryInt(Env.setting('port', default = 5000)),
'port': tryInt(Env.setting('port', default = 5000)) 'ssl_cert': Env.setting('ssl_cert', default = None),
'ssl_key': Env.setting('ssl_key', default = None),
} }
# Static path # Static path
@@ -243,12 +245,20 @@ def runCouchPotato(options, base_path, args, data_dir = None, log_dir = None, En
debug = config['use_reloader'] debug = config['use_reloader']
) )
if config['ssl_cert'] and config['ssl_key']:
server = HTTPServer(application, no_keep_alive = True, ssl_options = {
"certfile": config['ssl_cert'],
"keyfile": config['ssl_key'],
})
else:
server = HTTPServer(application, no_keep_alive = True)
try_restart = True try_restart = True
restart_tries = 5 restart_tries = 5
while try_restart: while try_restart:
try: try:
application.listen(config['port'], config['host'], no_keep_alive = True) server.listen(config['port'])
loop.start() loop.start()
except Exception, e: except Exception, e:
try: try:
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

+1 -1
View File
@@ -1311,7 +1311,7 @@ Option.Combined = new Class({
if(has_empty > 0) return; if(has_empty > 0) return;
self.add_empty_timeout = setTimeout(function(){ self.add_empty_timeout = setTimeout(function(){
self.createItem(false, null); self.createItem({'use': true});
}, 10); }, 10);
}, },