diff --git a/contributing.md b/contributing.md index d5db0b4..0d82d71 100644 --- a/contributing.md +++ b/contributing.md @@ -1,25 +1,36 @@ -## Got a issue/feature request or submitting a pull request? +# Contributing to CouchPotatoServer -Make sure you think of the following things: +1. [Contributing](#contributing) +2. [Submitting an Issue](#issues) +3. [Submitting a Pull Request](#pull-requests) -## Issue - * Search through the existing (and closed) issues first, see if you can get your answer there. - * Double check the result manually, because it could be an external issue. - * Post logs! Without seeing what is going on, I can't reproduce the error. - * Also check the logs before submitting, obvious errors like permission or http errors are often not related to CP. - * What is the movie + quality you are searching for? - * What are you're settings for the specific problem? - * What providers are you using? (While you're logs include these, scanning through hundred of lines of log isn't our hobby) - * Post the logs from config directory, please do not copy paste the UI. Use pastebin to store these logs! +## Contributing +Thank you for your interest in contributing to CouchPotato. There are several ways to help out, even if you've never worked on an open source project before. +If you've found a bug or want to request a feature, you can report it by [posting an issue](https://github.com/RuudBurger/CouchPotatoServer/issues/new) - be sure to read the [guidelines](#issues) first! +If you want to contribute your own work, please read the [guidelines](#pull-requests) for submitting a pull request. +Lastly, for anything related to CouchPotato, feel free to stop by the [forum](http://couchpota.to/forum/) or the [#couchpotato](http://webchat.freenode.net/?channels=couchpotato) IRC channel at irc.freenode.net. + +## Issues +Issues are intended for reporting bugs and weird behaviour or suggesting improvements to CouchPotatoServer. +Before you submit an issue, please go through the following checklist: + * Search through existing issues (*including closed issues!*) first: you might be able to get your answer there. + * Double check your issue manually, because it could be an external issue. + * Post logs with your issue: Without seeing what is going on, the developers can't reproduce the error. + * Check the logs yourself before submitting them. Obvious errors like permission or HTTP errors are often not related to CouchPotato. + * What movie and quality are you searching for? + * What are your settings for the specific problem? + * What providers are you using? (While your logs include these, scanning through hundreds of lines of logs isn't our hobby) + * Post the logs from the *config* directory, please do not copy paste the UI. Use pastebin to store these logs! * Give a short step by step of how to reproduce the error. - * What hardware / OS are you using and what are the limits? NAS can be slow and maybe have a different python installed then when you use CP on OSX or Windows for example. - * I will mark issues with the "can't reproduce" tag. Don't go asking "why closed" if it clearly says the issue in the tag ;) - * If you're running on a NAS (QNAP, Austor etc..) with pre-made packages, make sure these are setup to use our source repo (RuudBurger/CouchPotatoServer) and nothing else!! + * What hardware / OS are you using and what are its limitations? For example: NAS can be slow and maybe have a different version of python installed then when you use CP on OSX or Windows. + * Your issue might be marked with the "can't reproduce" tag. Don't ask why your issue was closed if it says so in the tag. + * If you're running on a NAS (QNAP, Austor etc..) with pre-made packages, make sure these are set up to use our source repository (RuudBurger/CouchPotatoServer) and nothing else!! + +The more relevant information you can provide, the more likely it is the issue will be resolved rather than closed. -## Pull Request - * Make sure you're pull request is made for develop branch (or relevant feature branch) +## Pull Requests +Pull requests are intended for contributing code or documentation to the project. Before you submit a pull request, consider the following: + * Make sure your pull request is made for the *develop* branch (or relevant feature branch). * Have you tested your PR? If not, why? - * Are there any limitations of your PR we should know of? - * Make sure to keep you're PR up-to-date with the branch you're trying to push into. - -**If we don't get enough info, the chance of the issue getting closed is a lot bigger ;)** + * Does your PR have any limitations we should know of? + * Is your PR up-to-date with the branch you're trying to push into? diff --git a/couchpotato/__init__.py b/couchpotato/__init__.py index ce034db..1946963 100644 --- a/couchpotato/__init__.py +++ b/couchpotato/__init__.py @@ -48,10 +48,6 @@ def addView(route, func, static = False): views[route] = func -def get_session(): - return None - - def get_db(): return Env.get('db') diff --git a/couchpotato/core/database.py b/couchpotato/core/database.py index 3aa1bb7..19fde90 100644 --- a/couchpotato/core/database.py +++ b/couchpotato/core/database.py @@ -279,8 +279,8 @@ class Database(object): quality = db.get('id', q_id) quality['order'] = q.get('order') - quality['size_min'] = q.get('size_min') - quality['size_max'] = q.get('size_max') + quality['size_min'] = tryInt(q.get('size_min')) + quality['size_max'] = tryInt(q.get('size_max')) db.update(quality) quality_link[x] = quality diff --git a/couchpotato/core/downloaders/base.py b/couchpotato/core/downloaders/base.py index 269bdc6..8042c4a 100644 --- a/couchpotato/core/downloaders/base.py +++ b/couchpotato/core/downloaders/base.py @@ -210,7 +210,7 @@ class ReleaseDownloadList(list): 'status': 'busy', 'downloader': self.provider.getName(), 'folder': '', - 'files': '', + 'files': [], } return mergeDicts(defaults, result) diff --git a/couchpotato/core/downloaders/deluge.py b/couchpotato/core/downloaders/deluge.py index 2a386ad..26592ca 100644 --- a/couchpotato/core/downloaders/deluge.py +++ b/couchpotato/core/downloaders/deluge.py @@ -147,7 +147,7 @@ class Deluge(Downloader): 'seed_ratio': torrent['ratio'], 'timeleft': str(timedelta(seconds = torrent['eta'])), 'folder': sp(download_dir if len(torrent_files) == 1 else os.path.join(download_dir, torrent['name'])), - 'files': '|'.join(torrent_files), + 'files': torrent_files, }) return release_downloads diff --git a/couchpotato/core/downloaders/qbittorrent_.py b/couchpotato/core/downloaders/qbittorrent_.py index 349af17..558222d 100644 --- a/couchpotato/core/downloaders/qbittorrent_.py +++ b/couchpotato/core/downloaders/qbittorrent_.py @@ -135,7 +135,7 @@ class qBittorrent(Downloader): 'original_status': torrent.state, 'timeleft': torrent.progress * 100 if torrent.progress else -1, # percentage 'folder': sp(torrent.save_path), - 'files': '|'.join(torrent_files) + 'files': torrent_files }) return release_downloads diff --git a/couchpotato/core/downloaders/rtorrent_.py b/couchpotato/core/downloaders/rtorrent_.py index fb8948c..98946b2 100644 --- a/couchpotato/core/downloaders/rtorrent_.py +++ b/couchpotato/core/downloaders/rtorrent_.py @@ -59,20 +59,22 @@ class rTorrent(Downloader): return self.rt url = cleanHost(self.conf('host'), protocol = True, ssl = self.conf('ssl')) + + # Automatically add '+https' to 'httprpc' protocol if SSL is enabled + if self.conf('ssl') and url.startswith('httprpc://'): + url = url.replace('httprpc://', 'httprpc+https://') + parsed = urlparse(url) # rpc_url is only used on http/https scgi pass-through if parsed.scheme in ['http', 'https']: url += self.conf('rpc_url') - if self.conf('username') and self.conf('password'): - self.rt = RTorrent( - url, - self.conf('username'), - self.conf('password') - ) - else: - self.rt = RTorrent(url) + self.rt = RTorrent( + url, + self.conf('username'), + self.conf('password') + ) self.error_msg = '' try: @@ -243,7 +245,7 @@ class rTorrent(Downloader): 'original_status': torrent.state, 'timeleft': str(timedelta(seconds = float(torrent.left_bytes) / torrent.down_rate)) if torrent.down_rate > 0 else -1, 'folder': sp(torrent.directory), - 'files': '|'.join(torrent_files) + 'files': torrent_files }) return release_downloads diff --git a/couchpotato/core/downloaders/synology.py b/couchpotato/core/downloaders/synology.py index 46d889f..f7145de 100644 --- a/couchpotato/core/downloaders/synology.py +++ b/couchpotato/core/downloaders/synology.py @@ -33,7 +33,7 @@ class Synology(Downloader): try: # Send request to Synology - srpc = SynologyRPC(host[0], host[1], self.conf('username'), self.conf('password')) + srpc = SynologyRPC(host[0], host[1], self.conf('username'), self.conf('password'), self.conf('destination')) if data['protocol'] == 'torrent_magnet': log.info('Adding torrent URL %s', data['url']) response = srpc.create_task(url = data['url']) @@ -84,7 +84,7 @@ class SynologyRPC(object): """SynologyRPC lite library""" - def __init__(self, host = 'localhost', port = 5000, username = None, password = None): + def __init__(self, host = 'localhost', port = 5000, username = None, password = None, destination = None): super(SynologyRPC, self).__init__() @@ -92,6 +92,7 @@ class SynologyRPC(object): self.auth_url = 'http://%s:%s/webapi/auth.cgi' % (host, port) self.username = username self.password = password + self.destination = destination self.session_name = 'DownloadStation' def _login(self): @@ -144,6 +145,10 @@ class SynologyRPC(object): 'version': '1', 'method': 'create', '_sid': self.sid} + + if self.destination and len(self.destination) > 0: + args['destination'] = self.destination + if url: log.info('Login success, adding torrent URI') args['uri'] = url @@ -196,6 +201,11 @@ config = [{ 'type': 'password', }, { + 'name': 'destination', + 'description': 'Specify existing destination share to where your files will be downloaded, usually Downloads', + 'advanced': True, + }, + { 'name': 'use_for', 'label': 'Use for', 'default': 'both', diff --git a/couchpotato/core/downloaders/transmission.py b/couchpotato/core/downloaders/transmission.py index f49cb9d..dd11166 100644 --- a/couchpotato/core/downloaders/transmission.py +++ b/couchpotato/core/downloaders/transmission.py @@ -141,7 +141,7 @@ class Transmission(Downloader): 'seed_ratio': torrent['uploadRatio'], 'timeleft': str(timedelta(seconds = torrent['eta'])), 'folder': sp(torrent_folder if len(torrent_files) == 1 else os.path.join(torrent_folder, torrent['name'])), - 'files': '|'.join(torrent_files) + 'files': torrent_files }) return release_downloads diff --git a/couchpotato/core/downloaders/utorrent.py b/couchpotato/core/downloaders/utorrent.py index dae8950..671dd9e 100644 --- a/couchpotato/core/downloaders/utorrent.py +++ b/couchpotato/core/downloaders/utorrent.py @@ -184,7 +184,7 @@ class uTorrent(Downloader): 'original_status': torrent[1], 'timeleft': str(timedelta(seconds = torrent[10])), 'folder': sp(torrent[26]), - 'files': '|'.join(torrent_files) + 'files': torrent_files }) return release_downloads diff --git a/couchpotato/core/helpers/variable.py b/couchpotato/core/helpers/variable.py index 5eab960..34e66e9 100644 --- a/couchpotato/core/helpers/variable.py +++ b/couchpotato/core/helpers/variable.py @@ -227,6 +227,10 @@ def toIterable(value): return [value] +def getIdentifier(media): + return media.get('identifier') or media.get('identifiers', {}).get('imdb') + + def getTitle(media_dict): try: try: @@ -241,10 +245,10 @@ def getTitle(media_dict): try: return media_dict['media']['info']['titles'][0] except: - log.error('Could not get title for %s', media_dict.get('identifier')) + log.error('Could not get title for %s', getIdentifier(media_dict)) return None - log.error('Could not get title for %s', media_dict['identifier']) + log.error('Could not get title for %s', getIdentifier(media_dict)) return None except: log.error('Could not get title for library item: %s', media_dict) @@ -293,7 +297,7 @@ def isSubFolder(sub_folder, base_folder): return base_folder and sub_folder and ss(os.path.normpath(base_folder).rstrip(os.path.sep) + os.path.sep) in ss(os.path.normpath(sub_folder).rstrip(os.path.sep) + os.path.sep) # From SABNZBD -re_password = [re.compile(r'([^/\\]+)[/\\](.+)'), re.compile(r'(.+){{([^{}]+)}}$'), re.compile(r'(.+)\s+password\s*=\s*(.+)$', re.I)] +re_password = [re.compile(r'(.+){{([^{}]+)}}$'), re.compile(r'(.+)\s+password\s*=\s*(.+)$', re.I)] def scanForPassword(name): m = None for reg in re_password: diff --git a/couchpotato/core/media/__init__.py b/couchpotato/core/media/__init__.py index f9b3b46..8e704de 100644 --- a/couchpotato/core/media/__init__.py +++ b/couchpotato/core/media/__init__.py @@ -25,7 +25,7 @@ class MediaBase(Plugin): def onComplete(): try: db = get_db() - media = db.get('id', media_id) + media = fireEvent('media.get', media_id, single = True) event_name = '%s.searcher.single' % media.get('type') fireEventAsync(event_name, media, on_complete = self.createNotifyFront(media_id)) @@ -38,8 +38,7 @@ class MediaBase(Plugin): def notifyFront(): try: - db = get_db() - media = db.get('id', media_id) + media = fireEvent('media.get', media_id, single = True) event_name = '%s.update' % media.get('type') fireEvent('notify.frontend', type = event_name, data = media) diff --git a/couchpotato/core/media/_base/media/index.py b/couchpotato/core/media/_base/media/index.py index 5065871..42e6990 100644 --- a/couchpotato/core/media/_base/media/index.py +++ b/couchpotato/core/media/_base/media/index.py @@ -6,7 +6,7 @@ from couchpotato.core.helpers.encoding import toUnicode, simplifyString class MediaIndex(MultiTreeBasedIndex): - _version = 2 + _version = 3 custom_header = """from CodernityDB.tree_index import MultiTreeBasedIndex""" @@ -26,31 +26,10 @@ class MediaIndex(MultiTreeBasedIndex): ids = [] for x in identifiers: - ids.append(md5('%s-%s' % (x, data['identifiers'][x])).hexdigest()) + ids.append(md5('%s-%s' % (x, identifiers[x])).hexdigest()) return ids, None - def run_to_dict(self, db, media_id, dict_dept = None): - if not dict_dept: dict_dept = {} - - return db.get('id', media_id) - - def run_identifiers(self, db, identifiers, with_doc = False): - for x in identifiers: - try: - media = db.get('media', '%s-%s' % (x, identifiers[x]), with_doc = with_doc) - return media - except: - pass - - def run_with_status(self, db, status = [], with_doc = True): - - status = list(status if isinstance(status, (list, tuple)) else [status]) - - for s in status: - for ms in db.get_many('media_status', s, with_doc = with_doc): - yield ms['doc'] if with_doc else ms - class MediaStatusIndex(TreeBasedIndex): _version = 1 diff --git a/couchpotato/core/media/_base/media/main.py b/couchpotato/core/media/_base/media/main.py index 85047e9..f6bcd71 100644 --- a/couchpotato/core/media/_base/media/main.py +++ b/couchpotato/core/media/_base/media/main.py @@ -74,6 +74,8 @@ class MediaPlugin(MediaBase): addEvent('app.load', self.addSingleDeleteView, priority = 100) addEvent('media.get', self.get) + addEvent('media.with_status', self.withStatus) + addEvent('media.with_identifiers', self.withIdentifiers) addEvent('media.list', self.list) addEvent('media.delete', self.delete) addEvent('media.restatus', self.restatus) @@ -99,13 +101,10 @@ class MediaPlugin(MediaBase): try: media = get_db().get('id', media_id) - - - default_title = getTitle(media) event = '%s.update_info' % media.get('type') def handler(): - fireEvent(event, identifier = media.get('identifier'), default_title = default_title, on_complete = self.createOnComplete(media_id)) + fireEvent(event, media_id = media_id, on_complete = self.createOnComplete(media_id)) if handler: return handler @@ -124,16 +123,21 @@ class MediaPlugin(MediaBase): imdb_id = getImdb(str(media_id)) + media = None if imdb_id: - m = db.get('media', 'imdb-%s' % imdb_id, with_doc = True)['doc'] + media = db.get('media', 'imdb-%s' % imdb_id, with_doc = True)['doc'] else: - m = db.get('id', media_id) + media = db.get('id', media_id) + + if media: - results = None - if m: - results = db.run('media', 'to_dict', m['_id']) + # Attach category + if media.get('category_id'): + media['category'] = db.get('id', media.get('category_id')) - return results + media['releases'] = list(fireEvent('release.for_media', media['_id'], single = True)) + + return media def getView(self, id = None, **kwargs): @@ -144,6 +148,29 @@ class MediaPlugin(MediaBase): 'media': media, } + def withStatus(self, status, with_doc = True): + + db = get_db() + + status = list(status if isinstance(status, (list, tuple)) else [status]) + + for s in status: + for ms in db.get_many('media_status', s, with_doc = with_doc): + yield ms['doc'] if with_doc else ms + + def withIdentifiers(self, identifiers, with_doc = False): + + db = get_db() + + for x in identifiers: + try: + media = db.get('media', '%s-%s' % (x, identifiers[x]), with_doc = with_doc) + return media + except: + pass + + log.error('No media found with identifiers: %s', identifiers) + def list(self, types = None, status = None, release_status = None, status_or = False, limit_offset = None, starts_with = None, search = None): db = get_db() @@ -170,13 +197,13 @@ class MediaPlugin(MediaBase): # Filter on movie status if status and len(status) > 0: filter_by['media_status'] = set() - for media_status in db.run('media', 'with_status', status, with_doc = False): + for media_status in fireEvent('media.with_status', status, with_doc = False, single = True): filter_by['media_status'].add(media_status.get('_id')) # Filter on release status if release_status and len(release_status) > 0: filter_by['release_status'] = set() - for release_status in db.run('release', 'with_status', release_status, with_doc = False): + for release_status in fireEvent('release.with_status', release_status, with_doc = False, single = True): filter_by['release_status'].add(release_status.get('media_id')) # Add search filters @@ -220,9 +247,7 @@ class MediaPlugin(MediaBase): offset -= 1 continue - media = db.run('media', 'to_dict', media_id) - - media['releases'] = list(db.run('release', 'for_media', media_id)) + media = fireEvent('media.get', media_id, single = True) # Merge releases with movie dict medias.append(media) @@ -285,13 +310,13 @@ class MediaPlugin(MediaBase): # Filter on movie status if status and len(status) > 0: filter_by['media_status'] = set() - for media_status in db.run('media', 'with_status', status, with_doc = False): + for media_status in fireEvent('media.with_status', status, with_doc = False, single = True): filter_by['media_status'].add(media_status.get('_id')) # Filter on release status if release_status and len(release_status) > 0: filter_by['release_status'] = set() - for release_status in db.run('release', 'with_status', release_status, with_doc = False): + for release_status in fireEvent('release.with_status', release_status, with_doc = False, single = True): filter_by['release_status'].add(release_status.get('media_id')) # Filter by combining ids @@ -341,7 +366,7 @@ class MediaPlugin(MediaBase): deleted = True else: - media_releases = list(db.run('release', 'for_media', media['_id'])) + media_releases = list(fireEvent('release.for_media', media['_id'], single = True)) total_releases = len(media_releases) total_deleted = 0 @@ -407,7 +432,7 @@ class MediaPlugin(MediaBase): move_to_wanted = True profile = db.get('id', m['profile_id']) - media_releases = list(db.run('release', 'for_media', m['_id'])) + media_releases = list(fireEvent('release.for_media', m['_id'], single = True)) for q_identifier in profile['qualities']: index = profile['qualities'].index(q_identifier) diff --git a/couchpotato/core/media/_base/providers/torrent/awesomehd.py b/couchpotato/core/media/_base/providers/torrent/awesomehd.py index bf10456..1372f6c 100644 --- a/couchpotato/core/media/_base/providers/torrent/awesomehd.py +++ b/couchpotato/core/media/_base/providers/torrent/awesomehd.py @@ -2,7 +2,7 @@ import re import traceback from bs4 import BeautifulSoup -from couchpotato.core.helpers.variable import tryInt +from couchpotato.core.helpers.variable import tryInt, getIdentifier from couchpotato.core.logger import CPLog from couchpotato.core.media._base.providers.torrent.base import TorrentProvider @@ -22,7 +22,7 @@ class Base(TorrentProvider): def _search(self, movie, quality, results): - data = self.getHTMLData(self.urls['search'] % (self.conf('passkey'), movie['identifier'], self.conf('only_internal'))) + data = self.getHTMLData(self.urls['search'] % (self.conf('passkey'), getIdentifier(movie), self.conf('only_internal'))) if data: try: diff --git a/couchpotato/core/media/_base/providers/torrent/hdbits.py b/couchpotato/core/media/_base/providers/torrent/hdbits.py index 73bfc16..c2e85ac 100644 --- a/couchpotato/core/media/_base/providers/torrent/hdbits.py +++ b/couchpotato/core/media/_base/providers/torrent/hdbits.py @@ -2,7 +2,7 @@ import re import json import traceback -from couchpotato.core.helpers.variable import tryInt +from couchpotato.core.helpers.variable import tryInt, getIdentifier from couchpotato.core.logger import CPLog from couchpotato.core.media._base.providers.torrent.base import TorrentProvider @@ -44,7 +44,7 @@ class Base(TorrentProvider): def _search(self, movie, quality, results): - match = re.match(r'tt(\d{7})', movie['identifier']) + match = re.match(r'tt(\d{7})', getIdentifier(movie)) data = self._post_query(imdb = {'id': match.group(1)}) @@ -56,7 +56,7 @@ class Base(TorrentProvider): 'name': result['name'], 'url': self.urls['download'] % (result['id'], self.conf('passkey')), 'detail_url': self.urls['detail'] % result['id'], - 'size': self.parseSize(result['size']), + 'size': tryInt(result['size'])/1024/1024, 'seeders': tryInt(result['seeders']), 'leechers': tryInt(result['leechers']) }) diff --git a/couchpotato/core/media/_base/providers/torrent/kickasstorrents.py b/couchpotato/core/media/_base/providers/torrent/kickasstorrents.py index 03f0a99..b347ccb 100644 --- a/couchpotato/core/media/_base/providers/torrent/kickasstorrents.py +++ b/couchpotato/core/media/_base/providers/torrent/kickasstorrents.py @@ -2,7 +2,7 @@ import re import traceback from bs4 import BeautifulSoup -from couchpotato.core.helpers.variable import tryInt +from couchpotato.core.helpers.variable import tryInt, getIdentifier from couchpotato.core.logger import CPLog from couchpotato.core.media._base.providers.torrent.base import TorrentMagnetProvider @@ -38,7 +38,7 @@ class Base(TorrentMagnetProvider): def _search(self, media, quality, results): - data = self.getHTMLData(self.urls['search'] % (self.getDomain(), 'm', media['identifier'].replace('tt', ''))) + data = self.getHTMLData(self.urls['search'] % (self.getDomain(), 'm', getIdentifier(media).replace('tt', ''))) if data: diff --git a/couchpotato/core/media/_base/providers/torrent/passthepopcorn.py b/couchpotato/core/media/_base/providers/torrent/passthepopcorn.py index 56550d2..5368ad3 100644 --- a/couchpotato/core/media/_base/providers/torrent/passthepopcorn.py +++ b/couchpotato/core/media/_base/providers/torrent/passthepopcorn.py @@ -5,7 +5,7 @@ import time import traceback from couchpotato.core.helpers.encoding import tryUrlencode -from couchpotato.core.helpers.variable import getTitle, tryInt, mergeDicts +from couchpotato.core.helpers.variable import getTitle, tryInt, mergeDicts, getIdentifier from couchpotato.core.logger import CPLog from couchpotato.core.media._base.providers.torrent.base import TorrentProvider from dateutil.parser import parse @@ -36,7 +36,7 @@ class Base(TorrentProvider): params = mergeDicts(self.quality_search_params[quality_id].copy(), { 'order_by': 'relevance', 'order_way': 'descending', - 'searchstr': media['identifier'] + 'searchstr': getIdentifier(media) }) url = '%s?json=noredirect&%s' % (self.urls['torrent'], tryUrlencode(params)) diff --git a/couchpotato/core/media/_base/providers/torrent/thepiratebay.py b/couchpotato/core/media/_base/providers/torrent/thepiratebay.py index 42006d1..4d81961 100644 --- a/couchpotato/core/media/_base/providers/torrent/thepiratebay.py +++ b/couchpotato/core/media/_base/providers/torrent/thepiratebay.py @@ -29,9 +29,9 @@ class Base(TorrentMagnetProvider): 'http://pirateproxy.ca', 'http://tpb.al', 'http://www.tpb.gr', - 'http://nl.tpb.li', + 'http://bayproxy.me', 'http://proxybay.eu', - 'https://www.getpirate.com', + 'http://www.getpirate.com', 'http://piratebay.io', ] diff --git a/couchpotato/core/media/_base/providers/torrent/yify.py b/couchpotato/core/media/_base/providers/torrent/yify.py index 9380e65..fe7a9b4 100644 --- a/couchpotato/core/media/_base/providers/torrent/yify.py +++ b/couchpotato/core/media/_base/providers/torrent/yify.py @@ -1,6 +1,6 @@ import traceback -from couchpotato.core.helpers.variable import tryInt +from couchpotato.core.helpers.variable import tryInt, getIdentifier from couchpotato.core.logger import CPLog from couchpotato.core.media._base.providers.torrent.base import TorrentProvider @@ -35,7 +35,7 @@ class Base(TorrentProvider): def _search(self, movie, quality, results): - search_url = self.urls['search'] % (self.getDomain(), movie['identifier'], quality['identifier']) + search_url = self.urls['search'] % (self.getDomain(), getIdentifier(movie), quality['identifier']) data = self.getJsonData(search_url) diff --git a/couchpotato/core/media/movie/_base/main.py b/couchpotato/core/media/movie/_base/main.py index d8060d7..a783cd1 100644 --- a/couchpotato/core/media/movie/_base/main.py +++ b/couchpotato/core/media/movie/_base/main.py @@ -6,7 +6,7 @@ from couchpotato import get_db from couchpotato.api import addApiView from couchpotato.core.event import fireEvent, fireEventAsync, addEvent from couchpotato.core.helpers.encoding import toUnicode -from couchpotato.core.helpers.variable import splitString, getTitle, getImdb +from couchpotato.core.helpers.variable import splitString, getTitle, getImdb, getIdentifier from couchpotato.core.logger import CPLog from couchpotato.core.media.movie import MovieTypeBase import six @@ -138,7 +138,7 @@ class MovieBase(MovieTypeBase): elif force_readd: # Clean snatched history - for release in db.run('release', 'for_media', m['_id']): + for release in fireEvent('release.for_media', m['_id'], single = True): if release.get('status') in ['downloaded', 'snatched', 'done']: if params.get('ignore_previous', False): release['status'] = 'ignored' @@ -164,11 +164,11 @@ class MovieBase(MovieTypeBase): fireEventAsync('movie.update_info', m['_id'], default_title = params.get('title'), on_complete = onComplete) # Remove releases - for rel in db.run('release', 'for_media', m['_id']): + for rel in fireEvent('release.for_media', m['_id'], single = True): if rel['status'] is 'available': db.delete(rel) - movie_dict = db.run('media', 'to_dict', m['_id']) + movie_dict = fireEvent('media.get', m['_id'], single = True) if do_search and search_after: onComplete = self.createOnComplete(m['_id']) @@ -215,7 +215,7 @@ class MovieBase(MovieTypeBase): m['category_id'] = cat_id if len(cat_id) > 0 else None # Remove releases - for rel in db.run('release', 'for_media', m['_id']): + for rel in fireEvent('release.for_media', m['_id'], single = True): if rel['status'] is 'available': db.delete(rel) @@ -229,7 +229,7 @@ class MovieBase(MovieTypeBase): m = db.get('id', media_id) - movie_dict = db.run('media', 'to_dict', m['_id']) + movie_dict = fireEvent('media.get', m['_id'], single = True) fireEventAsync('movie.searcher.single', movie_dict, on_complete = self.createNotifyFront(media_id)) except: @@ -266,7 +266,7 @@ class MovieBase(MovieTypeBase): else: media = db.get('media', 'imdb-%s' % identifier, with_doc = True)['doc'] - info = fireEvent('movie.info', merge = True, extended = extended, identifier = media.get('identifier')) + info = fireEvent('movie.info', merge = True, extended = extended, identifier = getIdentifier(media)) # Don't need those here try: del info['in_wanted'] @@ -275,7 +275,7 @@ class MovieBase(MovieTypeBase): except: pass if not info or len(info) == 0: - log.error('Could not update, no movie info to work with: %s', media.get('identifier')) + log.error('Could not update, no movie info to work with: %s', identifier) return False # Update basic info @@ -285,19 +285,20 @@ class MovieBase(MovieTypeBase): log.debug('Adding titles: %s', titles) # Define default title - def_title = None if default_title: - counter = 0 - for title in titles: - if title.lower() == toUnicode(default_title.lower()) or (toUnicode(default_title) == six.u('') and toUnicode(titles[0]) == title): - def_title = toUnicode(title) - break - counter += 1 + def_title = None + if default_title: + counter = 0 + for title in titles: + if title.lower() == toUnicode(default_title.lower()) or (toUnicode(default_title) == six.u('') and toUnicode(titles[0]) == title): + def_title = toUnicode(title) + break + counter += 1 - if not def_title: - def_title = toUnicode(titles[0]) + if not def_title: + def_title = toUnicode(titles[0]) - media['title'] = def_title + media['title'] = def_title # Files images = info.get('images', []) @@ -357,7 +358,7 @@ class MovieBase(MovieTypeBase): dates = media.get('info').get('release_date') if dates and (dates.get('expires', 0) < time.time() or dates.get('expires', 0) > time.time() + (604800 * 4)) or not dates: - dates = fireEvent('movie.info.release_date', identifier = media['identifier'], merge = True) + dates = fireEvent('movie.info.release_date', identifier = getIdentifier(media), merge = True) media['info'].update({'release_date': dates}) db.update(media) diff --git a/couchpotato/core/media/movie/_base/static/movie.actions.js b/couchpotato/core/media/movie/_base/static/movie.actions.js index cb4be00..5dcaed7 100644 --- a/couchpotato/core/media/movie/_base/static/movie.actions.js +++ b/couchpotato/core/media/movie/_base/static/movie.actions.js @@ -428,7 +428,7 @@ MA.Release = new Class({ Api.request('movie.searcher.try_next', { 'data': { - 'id': self.movie.get('_id') + 'media_id': self.movie.get('_id') } }); diff --git a/couchpotato/core/media/movie/providers/automation/itunes.py b/couchpotato/core/media/movie/providers/automation/itunes.py index bb6eef2..fcb20b1 100644 --- a/couchpotato/core/media/movie/providers/automation/itunes.py +++ b/couchpotato/core/media/movie/providers/automation/itunes.py @@ -26,7 +26,7 @@ class ITunes(Automation, RSS): urls = splitString(self.conf('automation_urls')) namespace = 'http://www.w3.org/2005/Atom' - namespace_im = 'https://rss.itunes.apple.com' + namespace_im = 'http://itunes.apple.com/rss' index = -1 for url in urls: diff --git a/couchpotato/core/media/movie/providers/info/_modifier.py b/couchpotato/core/media/movie/providers/info/_modifier.py index 131cc05..cea0f49 100644 --- a/couchpotato/core/media/movie/providers/info/_modifier.py +++ b/couchpotato/core/media/movie/providers/info/_modifier.py @@ -3,7 +3,7 @@ import traceback from CodernityDB.database import RecordNotFound from couchpotato import get_db -from couchpotato.core.event import addEvent +from couchpotato.core.event import addEvent, fireEvent from couchpotato.core.helpers.variable import mergeDicts, randomString from couchpotato.core.logger import CPLog from couchpotato.core.plugins.base import Plugin @@ -104,7 +104,7 @@ class MovieResultModifier(Plugin): if media.get('status') == 'active': temp['in_wanted'] = media - for release in db.run('release', 'for_media', media.get('_id')): + for release in fireEvent('release.for_media', media['_id'], single = True): if release.get('status') == 'done': if not temp['in_library']: temp['in_library'] = media diff --git a/couchpotato/core/media/movie/providers/nzb/binsearch.py b/couchpotato/core/media/movie/providers/nzb/binsearch.py index f4c16c4..d6f4852 100644 --- a/couchpotato/core/media/movie/providers/nzb/binsearch.py +++ b/couchpotato/core/media/movie/providers/nzb/binsearch.py @@ -1,4 +1,5 @@ from couchpotato.core.helpers.encoding import tryUrlencode +from couchpotato.core.helpers.variable import getIdentifier from couchpotato.core.logger import CPLog from couchpotato.core.media._base.providers.nzb.binsearch import Base from couchpotato.core.media.movie.providers.base import MovieProvider @@ -13,7 +14,7 @@ class BinSearch(MovieProvider, Base): def buildUrl(self, media, quality): query = tryUrlencode({ - 'q': media['identifier'], + 'q': getIdentifier(media), 'm': 'n', 'max': 400, 'adv_age': Env.setting('retention', 'nzb'), diff --git a/couchpotato/core/media/movie/providers/nzb/newznab.py b/couchpotato/core/media/movie/providers/nzb/newznab.py index dd4d94c..9783f8d 100644 --- a/couchpotato/core/media/movie/providers/nzb/newznab.py +++ b/couchpotato/core/media/movie/providers/nzb/newznab.py @@ -1,4 +1,5 @@ from couchpotato.core.helpers.encoding import tryUrlencode +from couchpotato.core.helpers.variable import getIdentifier from couchpotato.core.logger import CPLog from couchpotato.core.media._base.providers.nzb.newznab import Base from couchpotato.core.media.movie.providers.base import MovieProvider @@ -13,7 +14,7 @@ class Newznab(MovieProvider, Base): def buildUrl(self, media, api_key): query = tryUrlencode({ 't': 'movie', - 'imdbid': media['identifier'].replace('tt', ''), + 'imdbid': getIdentifier(media).replace('tt', ''), 'apikey': api_key, 'extended': 1 }) diff --git a/couchpotato/core/media/movie/providers/nzb/nzbindex.py b/couchpotato/core/media/movie/providers/nzb/nzbindex.py index 0b5b2b4..70e939d 100644 --- a/couchpotato/core/media/movie/providers/nzb/nzbindex.py +++ b/couchpotato/core/media/movie/providers/nzb/nzbindex.py @@ -14,7 +14,7 @@ class NzbIndex(MovieProvider, Base): def buildUrl(self, media, quality): title = fireEvent('library.query', media, include_year = False, single = True) - year = media['year'] + year = media['info']['year'] query = tryUrlencode({ 'q': '"%s %s" | "%s (%s)"' % (title, year, title, year), diff --git a/couchpotato/core/media/movie/providers/torrent/bitsoup.py b/couchpotato/core/media/movie/providers/torrent/bitsoup.py index 578de69..b3ecf2e 100644 --- a/couchpotato/core/media/movie/providers/torrent/bitsoup.py +++ b/couchpotato/core/media/movie/providers/torrent/bitsoup.py @@ -21,7 +21,7 @@ class Bitsoup(MovieProvider, Base): query = tryUrlencode({ 'search': '"%s" %s' % ( fireEvent('library.query', media, include_year = False, single = True), - media['year'] + media['info']['year'] ), 'cat': self.getCatId(quality['identifier'])[0], }) diff --git a/couchpotato/core/media/movie/providers/torrent/iptorrents.py b/couchpotato/core/media/movie/providers/torrent/iptorrents.py index a1ad4aa..4f9d651 100644 --- a/couchpotato/core/media/movie/providers/torrent/iptorrents.py +++ b/couchpotato/core/media/movie/providers/torrent/iptorrents.py @@ -17,6 +17,6 @@ class IPTorrents(MovieProvider, Base): ] def buildUrl(self, title, media, quality): - query = '%s %s' % (title.replace(':', ''), media['year']) + query = '%s %s' % (title.replace(':', ''), media['info']['year']) return self._buildUrl(query, quality['identifier']) diff --git a/couchpotato/core/media/movie/providers/torrent/torrentpotato.py b/couchpotato/core/media/movie/providers/torrent/torrentpotato.py index 9c99207..6757353 100644 --- a/couchpotato/core/media/movie/providers/torrent/torrentpotato.py +++ b/couchpotato/core/media/movie/providers/torrent/torrentpotato.py @@ -1,4 +1,5 @@ from couchpotato.core.helpers.encoding import tryUrlencode +from couchpotato.core.helpers.variable import getIdentifier from couchpotato.core.logger import CPLog from couchpotato.core.media._base.providers.torrent.torrentpotato import Base from couchpotato.core.media.movie.providers.base import MovieProvider @@ -14,6 +15,6 @@ class TorrentPotato(MovieProvider, Base): arguments = tryUrlencode({ 'user': host['name'], 'passkey': host['pass_key'], - 'imdbid': media['identifier'] + 'imdbid': getIdentifier(media), }) return '%s?%s' % (host['host'], arguments) diff --git a/couchpotato/core/media/movie/searcher.py b/couchpotato/core/media/movie/searcher.py index 515f5f9..682fa69 100644 --- a/couchpotato/core/media/movie/searcher.py +++ b/couchpotato/core/media/movie/searcher.py @@ -8,7 +8,7 @@ from couchpotato import get_db from couchpotato.api import addApiView from couchpotato.core.event import addEvent, fireEvent, fireEventAsync from couchpotato.core.helpers.encoding import simplifyString -from couchpotato.core.helpers.variable import getTitle, possibleTitles, getImdb +from couchpotato.core.helpers.variable import getTitle, possibleTitles, getImdb, getIdentifier, tryInt from couchpotato.core.logger import CPLog from couchpotato.core.media._base.searcher.base import SearcherBase from couchpotato.core.media.movie import MovieTypeBase @@ -38,7 +38,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase): addApiView('movie.searcher.try_next', self.tryNextReleaseView, docs = { 'desc': 'Marks the snatched results as ignored and try the next best release', 'params': { - 'id': {'desc': 'The id of the movie'}, + 'media_id': {'desc': 'The id of the media'}, }, }) @@ -74,9 +74,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase): self.in_progress = True fireEvent('notify.frontend', type = 'movie.searcher.started', data = True, message = 'Full search started') - db = get_db() - - medias = [x['_id'] for x in db.run('media', 'with_status', 'active', with_doc = False)] + medias = [x['_id'] for x in fireEvent('media.with_status', 'active', with_doc = False, single = True)] random.shuffle(medias) total = len(medias) @@ -90,15 +88,15 @@ class MovieSearcher(SearcherBase, MovieTypeBase): for media_id in medias: - media = db.run('media', 'to_dict', media_id) + media = fireEvent('media.get', media_id, single = True) try: self.single(media, search_protocols) except IndexError: - log.error('Forcing library update for %s, if you see this often, please report: %s', (media['identifier'], traceback.format_exc())) + log.error('Forcing library update for %s, if you see this often, please report: %s', (getIdentifier(media), traceback.format_exc())) fireEvent('movie.update_info', media_id) except: - log.error('Search failed for %s: %s', (media['identifier'], traceback.format_exc())) + log.error('Search failed for %s: %s', (getIdentifier(media), traceback.format_exc())) self.in_progress['to_go'] -= 1 @@ -142,7 +140,6 @@ class MovieSearcher(SearcherBase, MovieTypeBase): profile = db.get('id', movie['profile_id']) quality_order = fireEvent('quality.order', single = True) - media_releases = db.run('release', 'for_media', movie['_id']) ret = False @@ -161,7 +158,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase): has_better_quality = 0 # See if better quality is available - for release in media_releases: + for release in movie.get('releases', []): if quality_order.index(release['quality']) <= quality_order.index(q_identifier) and release['status'] not in ['available', 'ignored', 'failed']: has_better_quality += 1 @@ -187,7 +184,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase): ret = True # Remove releases that aren't found anymore - for release in db.run('release', 'for_media', movie['_id']): + for release in movie.get('releases', []): if release.get('status') == 'available' and release.get('identifier') not in found_releases: fireEvent('release.delete', release.get('_id'), single = True) @@ -233,12 +230,12 @@ class MovieSearcher(SearcherBase, MovieTypeBase): # File to small - if nzb['size'] and preferred_quality['size_min'] > nzb['size']: + if nzb['size'] and tryInt(preferred_quality['size_min']) > tryInt(nzb['size']): log.info2('Wrong: "%s" is too small to be %s. %sMB instead of the minimal of %sMB.', (nzb['name'], preferred_quality['label'], nzb['size'], preferred_quality['size_min'])) return False # File to large - if nzb['size'] and preferred_quality.get('size_max') < nzb['size']: + if nzb['size'] and tryInt(preferred_quality['size_max']) < tryInt(nzb['size']): log.info2('Wrong: "%s" is too large to be %s. %sMB instead of the maximum of %sMB.', (nzb['name'], preferred_quality['label'], nzb['size'], preferred_quality['size_max'])) return False @@ -257,7 +254,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase): return True # Check if nzb contains imdb link - if getImdb(nzb.get('description', '')) == media['identifier']: + if getImdb(nzb.get('description', '')) == getIdentifier(media): return True for raw_title in media['info']['titles']: @@ -317,9 +314,9 @@ class MovieSearcher(SearcherBase, MovieTypeBase): return False - def tryNextReleaseView(self, id = None, **kwargs): + def tryNextReleaseView(self, media_id = None, **kwargs): - trynext = self.tryNextRelease(id, manual = True) + trynext = self.tryNextRelease(media_id, manual = True) return { 'success': trynext @@ -329,13 +326,13 @@ class MovieSearcher(SearcherBase, MovieTypeBase): try: db = get_db() - rels = db.run('media', 'with_status', media_id, status = ['snatched', 'done']) + rels = fireEvent('media.with_status', ['snatched', 'done'], single = True) for rel in rels: rel['status'] = 'ignored' db.update(rel) - movie_dict = db.run('media', 'to_dict', media_id) + movie_dict = fireEvent('media.get', media_id, single = True) log.info('Trying next release for: %s', getTitle(movie_dict)) self.single(movie_dict, manual = manual) diff --git a/couchpotato/core/media/movie/suggestion/main.py b/couchpotato/core/media/movie/suggestion/main.py index 62b9f57..5db403f 100644 --- a/couchpotato/core/media/movie/suggestion/main.py +++ b/couchpotato/core/media/movie/suggestion/main.py @@ -1,7 +1,7 @@ from couchpotato import get_db from couchpotato.api import addApiView from couchpotato.core.event import fireEvent -from couchpotato.core.helpers.variable import splitString, removeDuplicate +from couchpotato.core.helpers.variable import splitString, removeDuplicate, getIdentifier from couchpotato.core.plugins.base import Plugin from couchpotato.environment import Env @@ -28,9 +28,8 @@ class Suggestion(Plugin): else: if not movies or len(movies) == 0: - db = get_db() - active_movies = db.run('media', 'with_status', ['active', 'done']) - movies = [x['identifier'] for x in active_movies] + active_movies = fireEvent('media.with_status', ['active', 'done'], single = True) + movies = [getIdentifier(x) for x in active_movies] if not ignored or len(ignored) == 0: ignored = splitString(Env.prop('suggest_ignore', default = '')) @@ -86,8 +85,8 @@ class Suggestion(Plugin): # Get new results and add them if len(new_suggestions) - 1 < limit: db = get_db() - active_movies = db.run('media', 'with_status', ['active', 'done']) - movies = [x['identifier'] for x in active_movies] + active_movies = fireEvent('media.with_status', ['active', 'done'], single = True) + movies = [getIdentifier(x) for x in active_movies] movies.extend(seen) ignored.extend([x.get('imdb') for x in cached_suggestion]) diff --git a/couchpotato/core/notifications/boxcar.py b/couchpotato/core/notifications/boxcar.py index 3777a42..a7acc88 100644 --- a/couchpotato/core/notifications/boxcar.py +++ b/couchpotato/core/notifications/boxcar.py @@ -7,6 +7,8 @@ from couchpotato.core.notifications.base import Notification log = CPLog(__name__) +autoload = 'Boxcar' + class Boxcar(Notification): diff --git a/couchpotato/core/notifications/boxcar2.py b/couchpotato/core/notifications/boxcar2.py index 3e19c19..04ce4f3 100644 --- a/couchpotato/core/notifications/boxcar2.py +++ b/couchpotato/core/notifications/boxcar2.py @@ -25,7 +25,7 @@ class Boxcar2(Notification): data = { 'user_credentials': self.conf('token'), - 'notification[title]': toUnicode(message), + 'notification[title]': toUnicode('%s - %s' % (self.default_title, message)), 'notification[long_message]': toUnicode(long_message), } diff --git a/couchpotato/core/notifications/email_.py b/couchpotato/core/notifications/email_.py index b42650b..938a361 100644 --- a/couchpotato/core/notifications/email_.py +++ b/couchpotato/core/notifications/email_.py @@ -12,6 +12,8 @@ from couchpotato.environment import Env log = CPLog(__name__) +autoload = 'Email' + class Email(Notification): diff --git a/couchpotato/core/notifications/growl.py b/couchpotato/core/notifications/growl.py index c7847da..160e383 100644 --- a/couchpotato/core/notifications/growl.py +++ b/couchpotato/core/notifications/growl.py @@ -9,6 +9,8 @@ from gntp import notifier log = CPLog(__name__) +autoload = 'Growl' + class Growl(Notification): diff --git a/couchpotato/core/notifications/nmj.py b/couchpotato/core/notifications/nmj.py index 6ef70d4..4b9b92b 100644 --- a/couchpotato/core/notifications/nmj.py +++ b/couchpotato/core/notifications/nmj.py @@ -15,6 +15,8 @@ except ImportError: log = CPLog(__name__) +autoload = 'NMJ' + class NMJ(Notification): diff --git a/couchpotato/core/notifications/notifymyandroid.py b/couchpotato/core/notifications/notifymyandroid.py index 8b08360..ed7a24c 100644 --- a/couchpotato/core/notifications/notifymyandroid.py +++ b/couchpotato/core/notifications/notifymyandroid.py @@ -6,6 +6,8 @@ import six log = CPLog(__name__) +autoload = 'NotifyMyAndroid' + class NotifyMyAndroid(Notification): diff --git a/couchpotato/core/notifications/notifymywp.py b/couchpotato/core/notifications/notifymywp.py index 914897f..262fd8d 100644 --- a/couchpotato/core/notifications/notifymywp.py +++ b/couchpotato/core/notifications/notifymywp.py @@ -6,6 +6,8 @@ import six log = CPLog(__name__) +autoload = 'NotifyMyWP' + class NotifyMyWP(Notification): diff --git a/couchpotato/core/notifications/prowl.py b/couchpotato/core/notifications/prowl.py index b276d7f..fdece32 100644 --- a/couchpotato/core/notifications/prowl.py +++ b/couchpotato/core/notifications/prowl.py @@ -7,6 +7,8 @@ from couchpotato.core.notifications.base import Notification log = CPLog(__name__) +autoload = 'Prowl' + class Prowl(Notification): diff --git a/couchpotato/core/notifications/pushalot.py b/couchpotato/core/notifications/pushalot.py index 0ce9027..f8fa187 100644 --- a/couchpotato/core/notifications/pushalot.py +++ b/couchpotato/core/notifications/pushalot.py @@ -7,6 +7,8 @@ from couchpotato.core.notifications.base import Notification log = CPLog(__name__) +autoload = 'Pushalot' + class Pushalot(Notification): diff --git a/couchpotato/core/notifications/pushbullet.py b/couchpotato/core/notifications/pushbullet.py index fd1a376..56cf228 100644 --- a/couchpotato/core/notifications/pushbullet.py +++ b/couchpotato/core/notifications/pushbullet.py @@ -9,6 +9,8 @@ from couchpotato.core.notifications.base import Notification log = CPLog(__name__) +autoload = 'Pushbullet' + class Pushbullet(Notification): diff --git a/couchpotato/core/plugins/base.py b/couchpotato/core/plugins/base.py index 7d52e1e..c5892f5 100644 --- a/couchpotato/core/plugins/base.py +++ b/couchpotato/core/plugins/base.py @@ -7,11 +7,10 @@ import time import traceback import urllib2 -from couchpotato import get_db from couchpotato.core.event import fireEvent, addEvent from couchpotato.core.helpers.encoding import ss, toSafeString, \ toUnicode, sp -from couchpotato.core.helpers.variable import getExt, md5, isLocalIP, scanForPassword, tryInt +from couchpotato.core.helpers.variable import getExt, md5, isLocalIP, scanForPassword, tryInt, getIdentifier from couchpotato.core.logger import CPLog from couchpotato.environment import Env import requests @@ -327,19 +326,22 @@ class Plugin(object): if name_password: release_name, password = name_password tag += '{{%s}}' % password + elif data.get('password'): + tag += '{{%s}}' % data.get('password') max_length = 127 - len(tag) # Some filesystems don't support 128+ long filenames return '%s%s' % (toSafeString(toUnicode(release_name)[:max_length]), tag) def createFileName(self, data, filedata, media): - name = sp(os.path.join(self.createNzbName(data, media))) + name = self.createNzbName(data, media) if data.get('protocol') == 'nzb' and 'DOCTYPE nzb' not in filedata and '' not in filedata: return '%s.%s' % (name, 'rar') return '%s.%s' % (name, data.get('protocol')) def cpTag(self, media): if Env.setting('enabled', 'renamer'): - return '.cp(' + media.get('identifier') + ')' if media.get('identifier') else '' + identifier = getIdentifier(media) + return '.cp(' + identifier + ')' if identifier else '' return '' diff --git a/couchpotato/core/plugins/dashboard.py b/couchpotato/core/plugins/dashboard.py index 57cd7af..fad68b0 100644 --- a/couchpotato/core/plugins/dashboard.py +++ b/couchpotato/core/plugins/dashboard.py @@ -45,7 +45,7 @@ class Dashboard(Plugin): limit = tryInt(splt[0]) # Get all active medias - active_ids = [x['_id'] for x in db.run('media', 'with_status', 'active', with_doc = False)] + active_ids = [x['_id'] for x in fireEvent('media.with_status', 'active', with_doc = False, single = True)] medias = [] now_year = date.today().year diff --git a/couchpotato/core/plugins/file.py b/couchpotato/core/plugins/file.py index 77b3ba8..b2533c0 100644 --- a/couchpotato/core/plugins/file.py +++ b/couchpotato/core/plugins/file.py @@ -4,7 +4,7 @@ import traceback from couchpotato import get_db from couchpotato.api import addApiView -from couchpotato.core.event import addEvent +from couchpotato.core.event import addEvent, fireEvent from couchpotato.core.helpers.encoding import toUnicode from couchpotato.core.helpers.variable import md5, getExt from couchpotato.core.logger import CPLog @@ -32,12 +32,11 @@ class FileManager(Plugin): 'return': {'type': 'file'} }) - addEvent('app.load', self.cleanup) + fireEvent('schedule.interval', 'file.cleanup', self.cleanup, hours = 24) def cleanup(self): # Wait a bit after starting before cleanup - time.sleep(2) log.debug('Cleaning up unused files') try: diff --git a/couchpotato/core/plugins/manage.py b/couchpotato/core/plugins/manage.py index e1e084f..623ce6f 100644 --- a/couchpotato/core/plugins/manage.py +++ b/couchpotato/core/plugins/manage.py @@ -8,7 +8,7 @@ from couchpotato import get_db from couchpotato.api import addApiView from couchpotato.core.event import fireEvent, addEvent, fireEventAsync from couchpotato.core.helpers.encoding import sp -from couchpotato.core.helpers.variable import splitString, getTitle, tryInt +from couchpotato.core.helpers.variable import splitString, getTitle, tryInt, getIdentifier from couchpotato.core.logger import CPLog from couchpotato.core.plugins.base import Plugin from couchpotato.environment import Env @@ -123,12 +123,11 @@ class Manage(Plugin): total_movies, done_movies = fireEvent('media.list', types = 'movie', status = 'done', single = True) for done_movie in done_movies: - if done_movie['identifier'] not in added_identifiers: + if getIdentifier(done_movie) not in added_identifiers: fireEvent('media.delete', media_id = done_movie['_id'], delete_from = 'all') else: - db = get_db() - releases = list(db.run('release', 'for_media', done_movie.get('_id'))) + releases = done_movie.get('releases', []) for release in releases: if release.get('files'): diff --git a/couchpotato/core/plugins/profile/main.py b/couchpotato/core/plugins/profile/main.py index 8f838f4..5d15c11 100644 --- a/couchpotato/core/plugins/profile/main.py +++ b/couchpotato/core/plugins/profile/main.py @@ -2,7 +2,7 @@ import traceback from couchpotato import get_db, tryInt from couchpotato.api import addApiView -from couchpotato.core.event import addEvent +from couchpotato.core.event import addEvent, fireEvent from couchpotato.core.helpers.encoding import toUnicode from couchpotato.core.logger import CPLog from couchpotato.core.plugins.base import Plugin @@ -41,7 +41,7 @@ class ProfilePlugin(Plugin): # Get all active movies without profile try: db = get_db() - medias = db.run('media', 'with_status', ['active']) + medias = fireEvent('media.with_status', 'active', single = True) profile_ids = [x.get('_id') for x in self.all()] default_id = profile_ids[0] diff --git a/couchpotato/core/plugins/quality/main.py b/couchpotato/core/plugins/quality/main.py index ee06d4c..e64b503 100644 --- a/couchpotato/core/plugins/quality/main.py +++ b/couchpotato/core/plugins/quality/main.py @@ -5,7 +5,7 @@ from couchpotato import get_db from couchpotato.api import addApiView from couchpotato.core.event import addEvent from couchpotato.core.helpers.encoding import toUnicode, ss -from couchpotato.core.helpers.variable import mergeDicts, getExt +from couchpotato.core.helpers.variable import mergeDicts, getExt, tryInt from couchpotato.core.logger import CPLog from couchpotato.core.plugins.base import Plugin from couchpotato.core.plugins.quality.index import QualityIndex @@ -121,7 +121,7 @@ class QualityPlugin(Plugin): quality = db.get('quality', kwargs.get('identifier'), with_doc = True) if quality: - quality['doc'][kwargs.get('value_type')] = kwargs.get('value') + quality['doc'][kwargs.get('value_type')] = tryInt(kwargs.get('value')) db.update(quality['doc']) self.cached_qualities = None @@ -148,8 +148,8 @@ class QualityPlugin(Plugin): '_t': 'quality', 'order': order, 'identifier': q.get('identifier'), - 'size_min': q.get('size')[0], - 'size_max': q.get('size')[1] + 'size_min': tryInt(q.get('size')[0]), + 'size_max': tryInt(q.get('size')[1]), }) log.info('Creating profile: %s', q.get('label')) diff --git a/couchpotato/core/plugins/release/index.py b/couchpotato/core/plugins/release/index.py index ee76253..8265fe3 100644 --- a/couchpotato/core/plugins/release/index.py +++ b/couchpotato/core/plugins/release/index.py @@ -18,18 +18,6 @@ class ReleaseIndex(TreeBasedIndex): if data.get('_t') == 'release' and data.get('media_id'): return data['media_id'], None - def run_for_media(self, db, media_id): - for release in db.get_many('release', media_id, with_doc = True): - yield release['doc'] - - def run_with_status(self, db, status = [], with_doc = True): - - status = list(status if isinstance(status, (list, tuple)) else [status]) - - for s in status: - for ms in db.get_many('release_status', s, with_doc = with_doc): - yield ms['doc'] if with_doc else ms - class ReleaseStatusIndex(TreeBasedIndex): _version = 1 @@ -62,15 +50,15 @@ class ReleaseIDIndex(HashIndex): class ReleaseDownloadIndex(HashIndex): - _version = 1 + _version = 2 def __init__(self, *args, **kwargs): kwargs['key_format'] = '32s' super(ReleaseDownloadIndex, self).__init__(*args, **kwargs) def make_key(self, key): - return md5(key).hexdigest() + return md5(key.lower()).hexdigest() def make_key_value(self, data): if data.get('_t') == 'release' and data.get('download_info') and data['download_info']['id'] and data['download_info']['downloader']: - return md5('%s-%s' % (data['download_info']['downloader'], data['download_info']['id'])).hexdigest(), None + return md5(('%s-%s' % (data['download_info']['downloader'], data['download_info']['id'])).lower()).hexdigest(), None diff --git a/couchpotato/core/plugins/release/main.py b/couchpotato/core/plugins/release/main.py index 40475a4..a80dfc0 100644 --- a/couchpotato/core/plugins/release/main.py +++ b/couchpotato/core/plugins/release/main.py @@ -53,6 +53,8 @@ class Release(Plugin): addEvent('release.delete', self.delete) addEvent('release.clean', self.clean) addEvent('release.update_status', self.updateStatus) + addEvent('release.with_status', self.withStatus) + addEvent('release.for_media', self.forMedia) # Clean releases that didn't have activity in the last week addEvent('app.load', self.cleanDone) @@ -67,13 +69,13 @@ class Release(Plugin): db = get_db() # get movies last_edit more than a week ago - medias = db.run('media', 'with_status', ['done']) + medias = fireEvent('media.with_status', 'done', single = True) for media in medias: if media.get('last_edit', 0) > (now - week): continue - for rel in db.run('release', 'for_media', media['_id']): + for rel in fireEvent('release.for_media', media['_id'], single = True): # Remove all available releases if rel['status'] in ['available']: @@ -422,3 +424,20 @@ class Release(Plugin): log.error('Failed: %s', traceback.format_exc()) return False + + def withStatus(self, status, with_doc = True): + + db = get_db() + + status = list(status if isinstance(status, (list, tuple)) else [status]) + + for s in status: + for ms in db.get_many('release_status', s, with_doc = with_doc): + yield ms['doc'] if with_doc else ms + + def forMedia(self, media_id): + + db = get_db() + + for release in db.get_many('release', media_id, with_doc = True): + yield release['doc'] diff --git a/couchpotato/core/plugins/renamer.py b/couchpotato/core/plugins/renamer.py index 0f531bc..f3b22c9 100644 --- a/couchpotato/core/plugins/renamer.py +++ b/couchpotato/core/plugins/renamer.py @@ -10,7 +10,7 @@ from couchpotato.api import addApiView from couchpotato.core.event import addEvent, fireEvent, fireEventAsync from couchpotato.core.helpers.encoding import toUnicode, ss, sp from couchpotato.core.helpers.variable import getExt, mergeDicts, getTitle, \ - getImdb, link, symlink, tryInt, splitString, fnEscape, isSubFolder + getImdb, link, symlink, tryInt, splitString, fnEscape, isSubFolder, getIdentifier from couchpotato.core.logger import CPLog from couchpotato.core.plugins.base import Plugin from couchpotato.environment import Env @@ -79,16 +79,22 @@ class Renamer(Plugin): downloader = kwargs.get('downloader') download_id = kwargs.get('download_id') - files = '|'.join([sp(filename) for filename in splitString(kwargs.get('files'), '|')]) + files = [sp(filename) for filename in splitString(kwargs.get('files'), '|')] status = kwargs.get('status', 'completed') release_download = None if not base_folder and media_folder: release_download = {'folder': media_folder} - release_download.update({'id': download_id, 'downloader': downloader, 'status': status, 'files': files} if download_id else {}) - fire_handle = fireEvent if not async else fireEventAsync + if download_id: + release_download.update({ + 'id': download_id, + 'downloader': downloader, + 'status': status, + 'files': files + }) + fire_handle = fireEvent if not async else fireEventAsync fire_handle('renamer.scan', base_folder = base_folder, release_download = release_download) return { @@ -142,7 +148,7 @@ class Renamer(Plugin): log.debug('The provided media folder %s does not exist. Trying to find it in the \'from\' folder.', media_folder) # Update to the from folder - if len(splitString(release_download.get('files'), '|')) == 1: + if len(release_download.get('files'), []) == 1: new_media_folder = from_folder else: new_media_folder = os.path.join(from_folder, os.path.basename(media_folder)) @@ -152,7 +158,7 @@ class Renamer(Plugin): return # Update the files - new_files = [os.path.join(new_media_folder, os.path.relpath(filename, media_folder)) for filename in splitString(release_download.get('files'), '|')] + new_files = [os.path.join(new_media_folder, os.path.relpath(filename, media_folder)) for filename in release_download.get('files', [])] if new_files and not os.path.isfile(new_files[0]): log.error('The provided media folder %s does not exist and its files could also not be found in the \'from\' folder.', media_folder) return @@ -160,7 +166,7 @@ class Renamer(Plugin): # Update release_download info to the from folder log.debug('Release %s found in the \'from\' folder.', media_folder) release_download['folder'] = new_media_folder - release_download['files'] = '|'.join(new_files) + release_download['files'] = new_files media_folder = new_media_folder if media_folder: @@ -182,11 +188,10 @@ class Renamer(Plugin): log.info('Scanning media folder %s...', media_folder) folder = os.path.dirname(media_folder) - if release_download.get('files', ''): - files = splitString(release_download['files'], '|') - + release_files = release_download.get('files', []) + if release_files: # If there is only one file in the torrent, the downloader did not create a subfolder - if len(files) == 1: + if len(release_files) == 1: folder = media_folder else: # Get all files from the specified folder @@ -446,7 +451,7 @@ class Renamer(Plugin): log.error('Failed marking movie finished: %s', (traceback.format_exc())) # Go over current movie releases - for release in db.run('release', 'for_media', media['_id']): + for release in fireEvent('release.for_media', media['_id'], single = True): # When a release already exists if release.get('status') == 'done': @@ -643,8 +648,8 @@ Remove it if you want it to be renamed (again, or at least let it try again) elif isinstance(release_download, dict): # Tag download_files if they are known - if release_download['files']: - tag_files = splitString(release_download['files'], '|') + if release_download.get('files', []): + tag_files = release_download.get('files', []) # Tag all files in release folder else: @@ -678,8 +683,8 @@ Remove it if you want it to be renamed (again, or at least let it try again) elif isinstance(release_download, dict): # Untag download_files if they are known - if release_download['files']: - tag_files = splitString(release_download['files'], '|') + if release_download.get('files'): + tag_files = release_download.get('files', []) # Untag all files in release folder else: @@ -719,8 +724,8 @@ Remove it if you want it to be renamed (again, or at least let it try again) ignore_files = [] # Find tag on download_files if they are known - if release_download['files']: - tag_files = splitString(release_download['files'], '|') + if release_download.get('files'): + tag_files = release_download.get('files', []) # Find tag on all files in release folder else: @@ -834,7 +839,7 @@ Remove it if you want it to be renamed (again, or at least let it try again) try: db = get_db() - rels = list(db.run('release', 'with_status', ['snatched', 'seeding', 'missing'])) + rels = list(fireEvent('release.with_status', ['snatched', 'seeding', 'missing'], single = True)) if not rels: #No releases found that need status checking @@ -906,7 +911,7 @@ Remove it if you want it to be renamed (again, or at least let it try again) found_release = True break else: - if release_download['name'] == nzbname or rel['info']['name'] in release_download['name'] or getImdb(release_download['name']) == movie_dict['identifier']: + if release_download['name'] == nzbname or rel['info']['name'] in release_download['name'] or getImdb(release_download['name']) == getIdentifier(movie_dict): log.debug('Found release by release name or imdb ID: %s', release_download['name']) found_release = True break @@ -1043,7 +1048,7 @@ Remove it if you want it to be renamed (again, or at least let it try again) if rls: media = db.get('id', rls['media_id']) release_download.update({ - 'imdb_id': media['identifier'], + 'imdb_id': getIdentifier(media), 'quality': rls['quality'], 'protocol': rls.get('info', {}).get('protocol') or rls.get('info', {}).get('type'), 'release_id': rls['_id'], diff --git a/couchpotato/core/plugins/scanner.py b/couchpotato/core/plugins/scanner.py index 0066c36..65ae257 100644 --- a/couchpotato/core/plugins/scanner.py +++ b/couchpotato/core/plugins/scanner.py @@ -8,7 +8,7 @@ from couchpotato import get_db from couchpotato.core.event import fireEvent, addEvent from couchpotato.core.helpers.encoding import toUnicode, simplifyString, sp from couchpotato.core.helpers.variable import getExt, getImdb, tryInt, \ - splitString + splitString, getIdentifier from couchpotato.core.logger import CPLog from couchpotato.core.plugins.base import Plugin from enzyme.exceptions import NoParserError, ParseError @@ -403,7 +403,7 @@ class Scanner(Plugin): if not group['media']: log.error('Unable to determine media: %s', group['identifiers']) else: - group['identifier'] = group['media'].get('identifier') or group['media']['info'].get('imdb') + group['identifier'] = getIdentifier(group['media']) or group['media']['info'].get('imdb') processed_movies[identifier] = group diff --git a/libs/rtorrent/__init__.py b/libs/rtorrent/__init__.py index d70e6e1..a3f1607 100755 --- a/libs/rtorrent/__init__.py +++ b/libs/rtorrent/__init__.py @@ -89,13 +89,16 @@ class RTorrent: def _get_conn(self): """Get ServerProxy instance""" - if self.username is not None and self.password is not None: + + if self.username and self.password: if self.scheme == 'scgi': raise NotImplementedError() + secure = self.scheme == 'https' + return self.sp( self.uri, - transport=BasicAuthTransport(self.username, self.password), + transport=BasicAuthTransport(secure, self.username, self.password), **self.sp_kwargs ) diff --git a/libs/rtorrent/lib/xmlrpc/basic_auth.py b/libs/rtorrent/lib/xmlrpc/basic_auth.py index 20c02d9..c5654a2 100644 --- a/libs/rtorrent/lib/xmlrpc/basic_auth.py +++ b/libs/rtorrent/lib/xmlrpc/basic_auth.py @@ -20,24 +20,46 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -from base64 import encodestring -import string +from base64 import b64encode +import httplib import xmlrpclib class BasicAuthTransport(xmlrpclib.Transport): - def __init__(self, username=None, password=None): + def __init__(self, secure=False, username=None, password=None): xmlrpclib.Transport.__init__(self) + self.secure = secure + self.username = username self.password = password def send_auth(self, h): - if self.username is not None and self.password is not None: - h.putheader('AUTHORIZATION', "Basic %s" % string.replace( - encodestring("%s:%s" % (self.username, self.password)), - "\012", "" - )) + if not self.username or not self.password: + return + + auth = b64encode("%s:%s" % (self.username, self.password)) + + h.putheader('Authorization', "Basic %s" % auth) + + def make_connection(self, host): + if self._connection and host == self._connection[0]: + return self._connection[1] + + chost, self._extra_headers, x509 = self.get_host_info(host) + + if self.secure: + try: + self._connection = host, httplib.HTTPSConnection(chost, None, **(x509 or {})) + except AttributeError: + raise NotImplementedError( + "your version of httplib doesn't support HTTPS" + ) + else: + self._connection = host, httplib.HTTPConnection(chost) + + return self._connection[1] + def single_request(self, host, handler, request_body, verbose=0): # issue XML-RPC request