diff --git a/couchpotato/core/downloaders/putio/__init__.py b/couchpotato/core/downloaders/putio/__init__.py index 60ccad58..48794d9d 100644 --- a/couchpotato/core/downloaders/putio/__init__.py +++ b/couchpotato/core/downloaders/putio/__init__.py @@ -30,7 +30,7 @@ config = [{ }, { 'name': 'folder', - 'description': ('The folder on putio where you want the upload to go','Must be a folder in the root directory'), + 'description': ('The folder on putio where you want the upload to go','Will find the first first folder that matches this name'), 'default': 0, }, { diff --git a/couchpotato/core/downloaders/putio/main.py b/couchpotato/core/downloaders/putio/main.py index ce58ff7c..c5685821 100644 --- a/couchpotato/core/downloaders/putio/main.py +++ b/couchpotato/core/downloaders/putio/main.py @@ -28,16 +28,25 @@ class PutIO(DownloaderBase): return super(PutIO, self).__init__() + # This is a recusive function to check for the folders + def recursionFolder(self, client, folder = 0, tfolder = ''): + files = client.File.list(folder) + for f in files: + if f.content_type == 'application/x-directory': + if f.name == tfolder: + return f.id + else: + result = self.recursionFolder(client, f.id, tfolder) + if result != 0: + return result + return 0 + + # This will check the root for the folder, and kick of recusively checking sub folder def convertFolder(self, client, folder): if folder == 0: return 0 else: - files = client.File.list() - for f in files: - if f.name == folder and f.content_type == "application/x-directory": - return f.id - #If we get through the whole list and don't get a match we will use the root - return 0 + return self.recursionFolder(client, 0, folder) def download(self, data = None, media = None, filedata = None): if not media: media = {} diff --git a/couchpotato/core/downloaders/synology.py b/couchpotato/core/downloaders/synology.py index b5327ccb..8368eb1f 100644 --- a/couchpotato/core/downloaders/synology.py +++ b/couchpotato/core/downloaders/synology.py @@ -137,7 +137,7 @@ class SynologyRPC(object): def _req(self, url, args, files = None): response = {'success': False} try: - req = requests.post(url, data = args, files = files) + req = requests.post(url, data = args, files = files, verify = False) req.raise_for_status() response = json.loads(req.text) if response['success']: diff --git a/couchpotato/core/media/_base/providers/nzb/omgwtfnzbs.py b/couchpotato/core/media/_base/providers/nzb/omgwtfnzbs.py index ea5f90f7..3d002fb8 100644 --- a/couchpotato/core/media/_base/providers/nzb/omgwtfnzbs.py +++ b/couchpotato/core/media/_base/providers/nzb/omgwtfnzbs.py @@ -18,20 +18,13 @@ class Base(NZBProvider, RSS): http_time_between_calls = 1 # Seconds cat_ids = [ - ([15], ['dvdrip']), + ([15], ['dvdrip', 'scr', 'r5', 'tc', 'ts', 'cam']), ([15, 16], ['brrip']), ([16], ['720p', '1080p', 'bd50']), ([17], ['dvdr']), ] cat_backup_id = 'movie' - def search(self, movie, quality): - - if quality['identifier'] in fireEvent('quality.pre_releases', single = True): - return [] - - return super(Base, self).search(movie, quality) - def _searchOnTitle(self, title, movie, quality, results): q = '%s %s' % (title, movie['info']['year']) diff --git a/couchpotato/core/media/_base/providers/torrent/hdaccess.py b/couchpotato/core/media/_base/providers/torrent/hdaccess.py new file mode 100644 index 00000000..1a6b0edd --- /dev/null +++ b/couchpotato/core/media/_base/providers/torrent/hdaccess.py @@ -0,0 +1,130 @@ +import re +import traceback + +from couchpotato.core.helpers.variable import tryInt, getIdentifier +from couchpotato.core.logger import CPLog +from couchpotato.core.media._base.providers.torrent.base import TorrentProvider + + +log = CPLog(__name__) + + +class Base(TorrentProvider): + + urls = { + 'test': 'https://hdaccess.net/', + 'detail': 'https://hdaccess.net/details.php?id=%s', + 'search': 'https://hdaccess.net/searchapi.php?apikey=%s&username=%s&imdbid=%s&internal=%s', + 'download': 'https://hdaccess.net/grab.php?torrent=%s&apikey=%s', + } + + http_time_between_calls = 1 # Seconds + + def _search(self, movie, quality, results): + data = self.getJsonData(self.urls['search'] % (self.conf('apikey'), self.conf('username'), getIdentifier(movie), self.conf('internal_only'))) + + if data: + try: + #for result in data[]: + for key, result in data.iteritems(): + if tryInt(result['total_results']) == 0: + return + torrentscore = self.conf('extra_score') + releasegroup = result['releasegroup'] + resolution = result['resolution'] + encoding = result['encoding'] + freeleech = tryInt(result['freeleech']) + seeders = tryInt(result['seeders']) + torrent_desc = '/ %s / %s / %s / %s seeders' % (releasegroup, resolution, encoding, seeders) + + if freeleech > 0 and self.conf('prefer_internal'): + torrent_desc += '/ Internal' + torrentscore += 200 + + if seeders == 0: + torrentscore = 0 + + name = result['release_name'] + year = tryInt(result['year']) + + results.append({ + 'id': tryInt(result['torrentid']), + 'name': re.sub('[^A-Za-z0-9\-_ \(\).]+', '', '%s (%s) %s' % (name, year, torrent_desc)), + 'url': self.urls['download'] % (result['torrentid'], self.conf('apikey')), + 'detail_url': self.urls['detail'] % result['torrentid'], + 'size': tryInt(result['size']), + 'seeders': tryInt(result['seeders']), + 'leechers': tryInt(result['leechers']), + 'age': tryInt(result['age']), + 'score': torrentscore + }) + except: + log.error('Failed getting results from %s: %s', (self.getName(), traceback.format_exc())) +config = [{ + 'name': 'hdaccess', + 'groups': [ + { + 'tab': 'searcher', + 'list': 'torrent_providers', + 'name': 'HDAccess', + 'wizard': True, + 'description': 'HDAccess', + 'icon': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAADuUlEQVQ4yz3T209bdQAH8O/vnNNzWno5FIpAKZdSLi23gWMDtumWuSXOyzJj9M1kyIOPS1xiYuKe9GUPezZZnGIiMTqTxS1bdIuYkG2MWKBAKYVszOgKFkrbA+259HfO+fli/PwPHzI+Pg5CCEAI2VcUlEsl1tHdU7P5bGOkWChEaaUCwvHpmkD93POn6bwgCMQGAMYYYwyCruuQnE7SPzjIstvb8l+bm5fXkokJSmlQEkUQAIpSRH5vd0tyum7I/sA1Z5VH2ctmiGWZjHw4McE1NAZtQ9fD25kXt1VN7es7dNjuGRjiJFeVpWo6slsZPhF/Ys/PPeIs2056ff7zIOS5rpU5/viJEwwEnu3Mi18dojjw0aWP6amz57h9RSE/35zinq2nuGjvIQwOj7K2SKeZWkk0auXSSZ+/ZopSy+CbW1pQKpWu6Jr2/qVPPqWRjm6HWi6Tm999g3RyGbndLCqGgVBrO3F7fHykK0YX47NNtGLYlBq/c+H2iD+3k704dHQUDcFmQVXLyP6zhfTqCl45fQYjx17FemoJunoAk1bQFGoVhkdPwNC0ix2dMT+3llodM02rKdo7gN3dHAEhuH/vNgDg3Pl3cPaNt2GZJpYX5lBbFwClBukfGobL5WrayW6NccVCISY4HIQxYts2Q3J5CXOPHuLlo6NoCoXQ2hbG0JFRpJYWcVDIQ5ZlyL5qW5b9hNlWjKsYBgzDgKppMCoGHty7A0orOHbyNNweL+obGnDm9TdhWSYS8Vn4a2shOZ0QJRGSKIHjeGGtWNhjqqpyG+k04k8eozPai9ZwByavf4kfpyZxZGwMfYOHsbwQx34hB5dL4syKweRq/xpXHwzNapqWSSYWMDszzYqFPEaOn4KiKJiZfoCZ6d8Am+GtC++iXCpjaf4P9vefT8HzfKarp3eWRKMxCILwuWXSz977YIK2RTodDoGH1+OG1+tDlbsKkuiAJEngeWBjNUUnv7rucIiOLyzTvMKJTgnVtbVXLctK3L31g+NAUajL5bEptaDpOnTdgGkzVHl9drms0ju3fnJIkphoaQtfbQiFwAcCAY5wnCE5Xff3i8XX4o9nGksH+8zl9hAGZlWMCivkc9z0L3fZ999+LTCGZKi55YJTFHfye3sc6e/vB88LpK6+iWlqSS4WcpcNXZtwOp3B6mo/REmCSSkEgd+qq3vpRkt75Fp9Y1BZWZwnhq4zEovF/u/MATAti4U7umvyu9kR27aikihC9vvTnV2xufVUMu/2uIksy/9tZvgX49fLmAMx3bsAAAAASUVORK5CYII=', + 'options': [ + { + 'name': 'enabled', + 'type': 'enabler', + 'default': False, + }, + { + 'name': 'username', + 'default': '', + 'description': 'Enter your site username.', + }, + { + 'name': 'apikey', + 'default': '', + 'label': 'API Key', + 'description': 'Enter your site api key. This can be find on Profile Security', + }, + { + 'name': 'seed_ratio', + 'label': 'Seed ratio', + 'type': 'float', + 'default': 0, + 'description': 'Will not be (re)moved until this seed ratio is met. HDAccess minimum is 1:1.', + }, + { + 'name': 'seed_time', + 'label': 'Seed time', + 'type': 'int', + 'default': 0, + 'description': 'Will not be (re)moved until this seed time (in hours) is met. HDAccess minimum is 48 hours.', + }, + { + 'name': 'prefer_internal', + 'advanced': True, + 'type': 'bool', + 'default': 1, + 'description': 'Favors internal releases over non-internal releases.', + }, + { + 'name': 'internal_only', + 'advanced': True, + 'label': 'Internal Only', + 'type': 'bool', + 'default': False, + 'description': 'Only download releases marked as HDAccess internal', + }, + { + 'name': 'extra_score', + 'advanced': True, + 'label': 'Extra Score', + 'type': 'int', + 'default': 0, + 'description': 'Starting score for each release found via this provider.', + } + ], + }, + ], +}] diff --git a/couchpotato/core/media/_base/providers/torrent/iptorrents.py b/couchpotato/core/media/_base/providers/torrent/iptorrents.py index 0915ca31..61ced9c9 100644 --- a/couchpotato/core/media/_base/providers/torrent/iptorrents.py +++ b/couchpotato/core/media/_base/providers/torrent/iptorrents.py @@ -14,11 +14,11 @@ log = CPLog(__name__) class Base(TorrentProvider): urls = { - 'test': 'https://www.iptorrents.com/', - 'base_url': 'https://www.iptorrents.com', - 'login': 'https://www.iptorrents.com/torrents/', - 'login_check': 'https://www.iptorrents.com/inbox.php', - 'search': 'https://www.iptorrents.com/torrents/?%s%%s&q=%s&qf=ti&p=%%d', + 'test': 'https://iptorrents.eu/', + 'base_url': 'https://iptorrents.eu', + 'login': 'https://iptorrents.eu/torrents/', + 'login_check': 'https://iptorrents.eu/inbox.php', + 'search': 'https://iptorrents.eu/torrents/?%s%%s&q=%s&qf=ti&p=%%d', } http_time_between_calls = 1 # Seconds @@ -120,7 +120,7 @@ config = [{ 'tab': 'searcher', 'list': 'torrent_providers', 'name': 'IPTorrents', - 'description': 'IPTorrents', + 'description': 'IPTorrents', 'wizard': True, 'icon': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABRklEQVR42qWQO0vDUBiG8zeKY3EqQUtNO7g0J6ZJ1+ifKIIFQXAqDYKCyaaYxM3udrZLHdRFhXrZ6liCW6mubfk874EESgqaeOCF7/Y8hEh41aq6yZi2nyZgBGya9XKtZs4No05pAkZV2YbEmyMMsoSxLQeC46wCTdPPY4HruPQyGIhF97qLWsS78Miydn4XdK46NJ9OsQAYBzMIMf8MQ9wtCnTdWCaIDx/u7uljOIQEe0hiIWPamSTLay3+RxOCSPI9+RJAo7Er9r2bnqjBFAqyK+VyK4f5/Cr5ni8OFKVCz49PFI5GdNvvU7ttE1M1zMU+8AMqFksEhrMnQsBDzqmDAwzx2ehRLwT7yyCI+vSC99c3mozH1NxrJgWWtR1BOECfEJSVCm6WCzJGCA7+IWhBsM4zywDPwEp4vCjx2DzBH2ODAfsDb33Ps6dQwJgAAAAASUVORK5CYII=', 'options': [ diff --git a/couchpotato/core/media/_base/providers/torrent/kickasstorrents.py b/couchpotato/core/media/_base/providers/torrent/kickasstorrents.py index fb58814d..d6e3ee72 100644 --- a/couchpotato/core/media/_base/providers/torrent/kickasstorrents.py +++ b/couchpotato/core/media/_base/providers/torrent/kickasstorrents.py @@ -30,7 +30,7 @@ class Base(TorrentMagnetProvider): cat_backup_id = None proxy_list = [ - 'https://kickass.so', + 'https://kickass.to', 'http://kickass.pw', 'http://kickassto.come.in', 'http://katproxy.ws', diff --git a/couchpotato/core/media/_base/providers/torrent/torrentbytes.py b/couchpotato/core/media/_base/providers/torrent/torrentbytes.py index fadd2ea0..32221d83 100644 --- a/couchpotato/core/media/_base/providers/torrent/torrentbytes.py +++ b/couchpotato/core/media/_base/providers/torrent/torrentbytes.py @@ -56,7 +56,7 @@ class Base(TorrentProvider): full_id = link['href'].replace('details.php?id=', '') torrent_id = full_id[:6] - name = toUnicode(link.contents[0].encode('ISO-8859-1')).strip() + name = toUnicode(link.get('title', link.contents[0]).encode('ISO-8859-1')).strip() results.append({ 'id': torrent_id, diff --git a/couchpotato/core/media/_base/providers/torrent/torrentday.py b/couchpotato/core/media/_base/providers/torrent/torrentday.py index a3e9b78c..73014339 100644 --- a/couchpotato/core/media/_base/providers/torrent/torrentday.py +++ b/couchpotato/core/media/_base/providers/torrent/torrentday.py @@ -1,3 +1,4 @@ +import re from couchpotato.core.helpers.variable import tryInt from couchpotato.core.logger import CPLog from couchpotato.core.media._base.providers.torrent.base import TorrentProvider @@ -8,12 +9,12 @@ log = CPLog(__name__) class Base(TorrentProvider): urls = { - 'test': 'http://www.td.af/', - 'login': 'http://www.td.af/torrents/', - 'login_check': 'http://www.torrentday.com/userdetails.php', - 'detail': 'http://www.td.af/details.php?id=%s', - 'search': 'http://www.td.af/V3/API/API.php', - 'download': 'http://www.td.af/download.php/%s/%s', + 'test': 'https://torrentday.eu/', + 'login': 'https://torrentday.eu/torrents/', + 'login_check': 'https://torrentday.eu/userdetails.php', + 'detail': 'https://torrentday.eu/details.php?id=%s', + 'search': 'https://torrentday.eu/V3/API/API.php', + 'download': 'https://torrentday.eu/download.php/%s/%s', } http_time_between_calls = 1 # Seconds @@ -55,6 +56,10 @@ class Base(TorrentProvider): } def loginSuccess(self, output): + often = re.search('You tried too often, please wait .*', output) + if often: + raise Exception(often.group(0)[:-6].strip()) + return 'Password not correct' not in output def loginCheckSuccess(self, output): @@ -68,7 +73,7 @@ config = [{ 'tab': 'searcher', 'list': 'torrent_providers', 'name': 'TorrentDay', - 'description': 'TorrentDay', + 'description': 'TorrentDay', 'wizard': True, 'icon': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAC5ElEQVQ4y12TXUgUURTH//fO7Di7foeQJH6gEEEIZZllVohfSG/6UA+RSFAQQj74VA8+Bj30lmAlRVSEvZRfhNhaka5ZUG1paKaW39tq5O6Ou+PM3M4o6m6X+XPPzD3zm/+dcy574r515WfIW8CZBM4YAA5Gc/aQC3yd7oXYEONcsISE5dTDh91HS0t7FEWhBUAeN9ynV/d9qJAgE4AECURAcVsGlCCnly26LMA0IQwTa52dje3d3e3hcPi8qqrrMjcVYI3EHCQZlkFOHBwR2QHh2ASAAIJxWGAQEDxjePhs3527XjJwnb37OHBq0T+Tyyjh+9KnEzNJ7nouc1Q/3A3HGsOvnJy+PSUlj81w2Lny9WuJ6+3AmTjD4HOcrdR2dWXLRQePvyaSLfQOPMPC8mC9iHCsOxSyzJCelzdSXlNzD5ujpb25Wbfc/XXJemTXF4+nnCNq+AMLe50uFfEJTiw4GXSFtiHL0SnIq66+p0kSArqO+eH3RdsAv9+f5vW7L7GICq6rmM8XBCAXlBw90rOyxibn5yzfkg/L09M52/jxqdESaIrBXHYZZbB1GX8cEpySxKIB8S5XcOnvqpli1zuwmrTtoLjw5LOK/eeuWsE4JH5IRPaPZKiKigmPp+5pa+u1aEjIMhEgrRkmi9mgxGUhM7LNJSzOzsE3+cOeExovXOjdytE0LV4zqNZUtV0uZzAGoGkhDH/2YHZiErmv4uyWQnZZWc+hoqL3WzlTExN5hhA8IEwkZWZOxwB++30YG/9GkYCPvqAaHAW5uWPROW86OmqCprUR7z1yZDAGQNuCvkoB/baIKUBWMTYymv+gra3eJNvjXu+B562tFyXqTJ6YuHK8rKwvBmC3vR7cOCPQLWFz8LnfXWUrJo9U19BwMyUlJRjTSMJ2ENxUiGxq9KXQfwqYlnWstvbR5aamG9g0uzM8Q4OFt++3NNixQ2NgYmeN03FOTUv7XVpV9aKisvLl1vN/WVhNc/Fi1NEAAAAASUVORK5CYII=', 'options': [ diff --git a/couchpotato/core/media/_base/providers/torrent/torrentz.py b/couchpotato/core/media/_base/providers/torrent/torrentz.py index 8a5455c9..a5c1ed05 100644 --- a/couchpotato/core/media/_base/providers/torrent/torrentz.py +++ b/couchpotato/core/media/_base/providers/torrent/torrentz.py @@ -22,12 +22,12 @@ class Base(TorrentMagnetProvider, RSS): http_time_between_calls = 0 - def _search(self, media, quality, results): + def _searchOnTitle(self, title, media, quality, results): search_url = self.urls['verified_search'] if self.conf('verified_only') else self.urls['search'] # Create search parameters - search_params = self.buildUrl(media) + search_params = self.buildUrl(title, media, quality) smin = quality.get('size_min') smax = quality.get('size_max') diff --git a/couchpotato/core/media/_base/providers/torrent/yify.py b/couchpotato/core/media/_base/providers/torrent/yify.py index 725aabbc..492eeb65 100644 --- a/couchpotato/core/media/_base/providers/torrent/yify.py +++ b/couchpotato/core/media/_base/providers/torrent/yify.py @@ -2,27 +2,25 @@ import traceback from couchpotato.core.helpers.variable import tryInt, getIdentifier from couchpotato.core.logger import CPLog -from couchpotato.core.media._base.providers.torrent.base import TorrentMagnetProvider +from couchpotato.core.media._base.providers.torrent.base import TorrentProvider log = CPLog(__name__) -class Base(TorrentMagnetProvider): +class Base(TorrentProvider): urls = { - 'test': '%s/api', - 'search': '%s/api/list.json?keywords=%s', - 'detail': '%s/api/movie.json?id=%s' + 'test': '%s/api/v2', + 'search': '%s/api/v2/list_movies.json?limit=50&query_term=%s' } http_time_between_calls = 1 # seconds proxy_list = [ 'https://yts.re', - 'http://ytsproxy.come.in', - 'http://yts.im', - 'http://yify-torrents.im', + 'https://yts.wf', + 'https://yts.im', ] def search(self, movie, quality): @@ -41,25 +39,28 @@ class Base(TorrentMagnetProvider): search_url = self.urls['search'] % (domain, getIdentifier(movie)) data = self.getJsonData(search_url) + data = data.get('data') - if data and data.get('MovieList'): + if data and data.get('movies'): try: - for result in data.get('MovieList'): + for result in data.get('movies'): - if result['Quality'] and result['Quality'] not in result['MovieTitle']: - title = result['MovieTitle'] + ' BrRip ' + result['Quality'] - else: - title = result['MovieTitle'] + ' BrRip' + for release in result.get('torrents', []): - results.append({ - 'id': result['MovieID'], - 'name': title, - 'url': result['TorrentMagnetUrl'], - 'detail_url': self.urls['detail'] % (domain, result['MovieID']), - 'size': self.parseSize(result['Size']), - 'seeders': tryInt(result['TorrentSeeds']), - 'leechers': tryInt(result['TorrentPeers']), - }) + if release['quality'] and release['quality'] not in result['title_long']: + title = result['title_long'] + ' BRRip ' + release['quality'] + else: + title = result['title_long'] + ' BRRip' + + results.append({ + 'id': release['hash'], + 'name': title, + 'url': release['url'], + 'detail_url': result['url'], + 'size': self.parseSize(release['size']), + 'seeders': tryInt(release['seeds']), + 'leechers': tryInt(release['peers']), + }) except: log.error('Failed getting results from %s: %s', (self.getName(), traceback.format_exc())) diff --git a/couchpotato/core/media/movie/providers/automation/crowdai.py b/couchpotato/core/media/movie/providers/automation/crowdai.py new file mode 100644 index 00000000..56f1fb27 --- /dev/null +++ b/couchpotato/core/media/movie/providers/automation/crowdai.py @@ -0,0 +1,89 @@ +import re + +from couchpotato.core.helpers.rss import RSS +from couchpotato.core.helpers.variable import tryInt, splitString +from couchpotato.core.logger import CPLog +from couchpotato.core.media.movie.providers.automation.base import Automation + + +log = CPLog(__name__) + +autoload = 'CrowdAI' + + +class CrowdAI(Automation, RSS): + + interval = 1800 + + def getIMDBids(self): + + movies = [] + + urls = dict(zip(splitString(self.conf('automation_urls')), [tryInt(x) for x in splitString(self.conf('automation_urls_use'))])) + + for url in urls: + + if not urls[url]: + continue + + rss_movies = self.getRSSData(url) + + for movie in rss_movies: + + description = self.getTextElement(movie, 'description') + grabs = 0 + + for item in movie: + if item.attrib.get('name') == 'grabs': + grabs = item.attrib.get('value') + break + + if int(grabs) > tryInt(self.conf('number_grabs')): + title = re.match(r'.*Title: .a href.*/">(.*) \(\d{4}\).*', description).group(1) + log.info2('%s grabs for movie: %s, enqueue...', (grabs, title)) + year = re.match(r'.*Year: (\d{4}).*', description).group(1) + imdb = self.search(title, year) + + if imdb and self.isMinimalMovie(imdb): + movies.append(imdb['imdb']) + + return movies + + +config = [{ + 'name': 'crowdai', + 'groups': [ + { + 'tab': 'automation', + 'list': 'automation_providers', + 'name': 'crowdai_automation', + 'label': 'CrowdAI', + 'description': 'Imports from any newznab powered NZB providers RSS feed depending on the number of grabs per movie. Go to your newznab site and find the RSS section. Then copy the copy paste the link under "Movies > x264 feed" here.', + 'options': [ + { + 'name': 'automation_enabled', + 'default': False, + 'type': 'enabler', + }, + { + 'name': 'automation_urls_use', + 'label': 'Use', + 'default': '1', + }, + { + 'name': 'automation_urls', + 'label': 'url', + 'type': 'combined', + 'combine': ['automation_urls_use', 'automation_urls'], + 'default': 'http://YOUR_PROVIDER/rss?t=THE_MOVIE_CATEGORY&i=YOUR_USER_ID&r=YOUR_API_KEY&res=2&rls=2&num=100', + }, + { + 'name': 'number_grabs', + 'default': '500', + 'label': 'Grab threshold', + 'description': 'Number of grabs required', + }, + ], + }, + ], +}] diff --git a/couchpotato/core/media/movie/providers/automation/letterboxd.py b/couchpotato/core/media/movie/providers/automation/letterboxd.py index e9fc8741..d43821c0 100644 --- a/couchpotato/core/media/movie/providers/automation/letterboxd.py +++ b/couchpotato/core/media/movie/providers/automation/letterboxd.py @@ -48,11 +48,12 @@ class Letterboxd(Automation): soup = BeautifulSoup(self.getHTMLData(self.url % username)) - for movie in soup.find_all('a', attrs = {'class': 'frame'}): - match = removeEmpty(self.pattern.split(movie['title'])) + for movie in soup.find_all('li', attrs = {'class': 'poster-container'}): + img = movie.find('img', movie) + title = img.get('alt') + movies.append({ - 'title': match[0], - 'year': match[1] + 'title': title }) return movies diff --git a/couchpotato/core/media/movie/providers/automation/rottentomatoes.py b/couchpotato/core/media/movie/providers/automation/rottentomatoes.py index a01f76d2..65d54f13 100644 --- a/couchpotato/core/media/movie/providers/automation/rottentomatoes.py +++ b/couchpotato/core/media/movie/providers/automation/rottentomatoes.py @@ -39,15 +39,14 @@ class Rottentomatoes(Automation, RSS): if result: - log.info2('Something smells...') rating = tryInt(self.getTextElement(movie, rating_tag)) name = result.group(0) + print rating, tryInt(self.conf('tomatometer_percent')) 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)) + log.info2('Found %s with fresh rating %s', (name, rating)) year = datetime.datetime.now().strftime("%Y") imdb = self.search(name, year) diff --git a/couchpotato/core/media/movie/providers/torrent/bitsoup.py b/couchpotato/core/media/movie/providers/torrent/bitsoup.py index e9d69fe5..b0c8eded 100644 --- a/couchpotato/core/media/movie/providers/torrent/bitsoup.py +++ b/couchpotato/core/media/movie/providers/torrent/bitsoup.py @@ -11,7 +11,7 @@ autoload = 'Bitsoup' class Bitsoup(MovieProvider, Base): cat_ids = [ ([17], ['3d']), - ([41], ['720p', '1080p']), + ([80], ['720p', '1080p']), ([20], ['dvdr']), ([19], ['brrip', 'dvdrip']), ] diff --git a/couchpotato/core/media/movie/providers/torrent/hdaccess.py b/couchpotato/core/media/movie/providers/torrent/hdaccess.py new file mode 100644 index 00000000..fae2cf54 --- /dev/null +++ b/couchpotato/core/media/movie/providers/torrent/hdaccess.py @@ -0,0 +1,11 @@ +from couchpotato.core.logger import CPLog +from couchpotato.core.media._base.providers.torrent.hdaccess import Base +from couchpotato.core.media.movie.providers.base import MovieProvider + +log = CPLog(__name__) + +autoload = 'HDAccess' + + +class HDAccess(MovieProvider, Base): + pass diff --git a/couchpotato/core/media/movie/providers/torrent/torrentz.py b/couchpotato/core/media/movie/providers/torrent/torrentz.py index 742554c4..011ec430 100644 --- a/couchpotato/core/media/movie/providers/torrent/torrentz.py +++ b/couchpotato/core/media/movie/providers/torrent/torrentz.py @@ -1,6 +1,5 @@ from couchpotato.core.helpers.encoding import tryUrlencode from couchpotato.core.logger import CPLog -from couchpotato.core.event import fireEvent from couchpotato.core.media._base.providers.torrent.torrentz import Base from couchpotato.core.media.movie.providers.base import MovieProvider @@ -11,5 +10,5 @@ autoload = 'Torrentz' class Torrentz(MovieProvider, Base): - def buildUrl(self, media): - return tryUrlencode('"%s"' % fireEvent('library.query', media, single = True)) + def buildUrl(self, title, media, quality): + return tryUrlencode('"%s %s"' % (title, media['info']['year'])) \ No newline at end of file diff --git a/couchpotato/core/notifications/plex/__init__.py b/couchpotato/core/notifications/plex/__init__.py index 4a64ec5b..957369b7 100755 --- a/couchpotato/core/notifications/plex/__init__.py +++ b/couchpotato/core/notifications/plex/__init__.py @@ -23,6 +23,26 @@ config = [{ 'default': 'localhost', 'description': 'Hostname/IP, default localhost' }, + { + 'name': 'username', + 'label': 'Username', + 'default': '', + 'description': 'Required for myPlex' + }, + { + 'name': 'password', + 'label': 'Password', + 'default': '', + 'type': 'password', + 'description': 'Required for myPlex' + }, + { + 'name': 'auth_token', + 'label': 'Auth Token', + 'default': '', + 'advanced': True, + 'description': 'Required for myPlex' + }, { 'name': 'clients', 'default': '', diff --git a/couchpotato/core/notifications/plex/server.py b/couchpotato/core/notifications/plex/server.py index cd11f49b..4b8ea05f 100644 --- a/couchpotato/core/notifications/plex/server.py +++ b/couchpotato/core/notifications/plex/server.py @@ -35,11 +35,46 @@ class PlexServer(object): if path.startswith('/'): path = path[1:] - data = self.plex.urlopen('%s/%s' % ( - self.createHost(self.plex.conf('media_server'), port = 32400), - path - )) + #Maintain support for older Plex installations without myPlex + if not self.plex.conf('auth_token') and not self.plex.conf('username') and not self.plex.conf('password'): + data = self.plex.urlopen('%s/%s' % ( + self.createHost(self.plex.conf('media_server'), port = 32400), + path + )) + else: + #Fetch X-Plex-Token if it doesn't exist but a username/password do + if not self.plex.conf('auth_token') and (self.plex.conf('username') and self.plex.conf('password')): + import urllib2, base64 + log.info("Fetching a new X-Plex-Token from plex.tv") + username = self.plex.conf('username') + password = self.plex.conf('password') + req = urllib2.Request("https://plex.tv/users/sign_in.xml", data="") + authheader = "Basic %s" % base64.encodestring('%s:%s' % (username, password))[:-1] + req.add_header("Authorization", authheader) + req.add_header("X-Plex-Product", "Couchpotato Notifier") + req.add_header("X-Plex-Client-Identifier", "b3a6b24dcab2224bdb101fc6aa08ea5e2f3147d6") + req.add_header("X-Plex-Version", "1.0") + + try: + response = urllib2.urlopen(req) + except urllib2.URLError, e: + log.info("Error fetching token from plex.tv") + + try: + auth_tree = etree.parse(response) + token = auth_tree.findall(".//authentication-token")[0].text + self.plex.conf('auth_token', token) + except (ValueError, IndexError) as e: + log.info("Error parsing plex.tv response: " + ex(e)) + + #Add X-Plex-Token header for myPlex support workaround + data = self.plex.urlopen('%s/%s?X-Plex-Token=%s' % ( + self.createHost(self.plex.conf('media_server'), port = 32400), + path, + self.plex.conf('auth_token') + )) + if data_type == 'xml': return etree.fromstring(data) else: diff --git a/couchpotato/core/notifications/webhook.py b/couchpotato/core/notifications/webhook.py new file mode 100644 index 00000000..8dc63291 --- /dev/null +++ b/couchpotato/core/notifications/webhook.py @@ -0,0 +1,68 @@ +import traceback + +from couchpotato.core.helpers.encoding import toUnicode +from couchpotato.core.helpers.variable import getIdentifier +from couchpotato.core.logger import CPLog +from couchpotato.core.notifications.base import Notification + + +log = CPLog(__name__) + +autoload = 'Webhook' + +class Webhook(Notification): + + def notify(self, message = '', data = None, listener = None): + if not data: data = {} + + post_data = { + 'message': toUnicode(message) + } + + if getIdentifier(data): + post_data.update({ + 'imdb_id': getIdentifier(data) + }) + + headers = { + 'Content-type': 'application/x-www-form-urlencoded' + } + + try: + self.urlopen(self.conf('url'), headers = headers, data = post_data, show_error = False) + return True + except: + log.error('Webhook notification failed: %s', traceback.format_exc()) + + return False + + +config = [{ + 'name': 'webhook', + 'groups': [ + { + 'tab': 'notifications', + 'list': 'notification_providers', + 'name': 'webhook', + 'label': 'Webhook', + 'options': [ + { + 'name': 'enabled', + 'default': 0, + 'type': 'enabler', + }, + { + 'name': 'url', + 'description': 'The URL to send notification data to when ' + }, + { + 'name': 'on_snatch', + 'default': 0, + 'type': 'bool', + 'advanced': True, + 'description': 'Also send message when movie is snatched.', + } + ] + } + ] +}] diff --git a/couchpotato/core/plugins/base.py b/couchpotato/core/plugins/base.py index c02e8f75..e4b27c9b 100644 --- a/couchpotato/core/plugins/base.py +++ b/couchpotato/core/plugins/base.py @@ -206,7 +206,7 @@ class Plugin(object): if self.http_failed_disabled[host] > (time.time() - 900): log.info2('Disabled calls to %s for 15 minutes because so many failed requests.', host) if not show_error: - raise Exception('Disabled calls to %s for 15 minutes because so many failed requests') + raise Exception('Disabled calls to %s for 15 minutes because so many failed requests' % host) else: return '' else: diff --git a/couchpotato/core/plugins/browser.py b/couchpotato/core/plugins/browser.py index 632375df..660070a2 100644 --- a/couchpotato/core/plugins/browser.py +++ b/couchpotato/core/plugins/browser.py @@ -87,6 +87,7 @@ class FileBrowser(Plugin): try: dirs = self.getDirectories(path = path, show_hidden = show_hidden) except: + log.error('Failed getting directory "%s" : %s', (path, traceback.format_exc())) dirs = [] parent = os.path.dirname(path.rstrip(os.path.sep)) diff --git a/couchpotato/core/plugins/renamer.py b/couchpotato/core/plugins/renamer.py index d0720d0b..60b4f8b6 100755 --- a/couchpotato/core/plugins/renamer.py +++ b/couchpotato/core/plugins/renamer.py @@ -885,7 +885,9 @@ Remove it if you want it to be renamed (again, or at least let it try again) #If information is not available, we don't want the tag in the filename replaced = replaced.replace('<' + x + '>', '') - replaced = self.replaceDoubles(replaced.lstrip('. ')) + if self.conf('replace_doubles'): + replaced = self.replaceDoubles(replaced.lstrip('. ')) + for x, r in replacements.items(): if x in ['thename', 'namethe']: replaced = replaced.replace(six.u('<%s>') % toUnicode(x), toUnicode(r)) @@ -1342,6 +1344,14 @@ config = [{ 'type': 'choice', 'options': rename_options }, + { + 'advanced': True, + 'name': 'replace_doubles', + 'type': 'bool', + 'label': 'Clean Name', + 'description': ('Attempt to clean up double separaters due to missing data for fields.','Sometimes this eliminates wanted white space (see #2782).'), + 'default': True + }, { 'name': 'unrar', 'type': 'bool', diff --git a/init/ubuntu b/init/ubuntu index a21e2d34..cbe20e08 100755 --- a/init/ubuntu +++ b/init/ubuntu @@ -95,6 +95,8 @@ fi case "$1" in start) + touch $PID_FILE + chown $RUN_AS $PID_FILE echo "Starting $DESC" start-stop-daemon -d $APP_PATH -c $RUN_AS $EXTRA_SSD_OPTS --start --pidfile $PID_FILE --exec $DAEMON -- $DAEMON_OPTS ;;