From 7692322fbad526eeb6c37912301399bc22ccd8f1 Mon Sep 17 00:00:00 2001 From: dkboy Date: Sat, 13 Jul 2013 16:45:39 +1200 Subject: [PATCH 01/31] Expand IMDB automation provider to include charts Expand IMDB automation provider to include certain top charts, this includes the 'in theaters' list, as well as the top 250 list. They both respect the minimum requirement settings. --- .../core/providers/automation/imdb/__init__.py | 28 +++++++- couchpotato/core/providers/automation/imdb/main.py | 83 ++++++++++++++++++++-- 2 files changed, 105 insertions(+), 6 deletions(-) diff --git a/couchpotato/core/providers/automation/imdb/__init__.py b/couchpotato/core/providers/automation/imdb/__init__.py index a0013c4..ee804af 100644 --- a/couchpotato/core/providers/automation/imdb/__init__.py +++ b/couchpotato/core/providers/automation/imdb/__init__.py @@ -9,7 +9,7 @@ config = [{ { 'tab': 'automation', 'list': 'watchlist_providers', - 'name': 'imdb_automation', + 'name': 'imdb_automation_watchlist', 'label': 'IMDB', 'description': 'From any public IMDB watchlists. Url should be the CSV link.', 'options': [ @@ -30,5 +30,31 @@ config = [{ }, ], }, + { + 'tab': 'automation', + 'list': 'automation_providers', + 'name': 'imdb_automation_charts', + 'label': 'IMDB', + 'description': 'Import movies from IMDB Charts', + 'options': [ + { + 'name': 'automation_enabled', + 'default': False, + 'type': 'enabler', + }, + { + 'name': 'automation_charts_theaters_use', + 'type': 'checkbox', + 'label': 'In Theaters', + 'description': 'New Movies In-Theaters chart', + }, + { + 'name': 'automation_charts_top250_use', + 'type': 'checkbox', + 'label': 'TOP 250', + 'description': 'IMDB TOP 250 chart', + }, + ], + }, ], }] diff --git a/couchpotato/core/providers/automation/imdb/main.py b/couchpotato/core/providers/automation/imdb/main.py index 75a2d75..0d49494 100644 --- a/couchpotato/core/providers/automation/imdb/main.py +++ b/couchpotato/core/providers/automation/imdb/main.py @@ -1,7 +1,9 @@ +from bs4 import BeautifulSoup from couchpotato.core.helpers.rss import RSS from couchpotato.core.helpers.variable import getImdb, splitString, tryInt from couchpotato.core.logger import CPLog from couchpotato.core.providers.automation.base import Automation +import re import traceback log = CPLog(__name__) @@ -11,22 +13,91 @@ class IMDB(Automation, RSS): interval = 1800 + chart_urls = { + 'theater': 'http://www.imdb.com/movies-in-theaters/', + 'top250': 'http://www.imdb.com/chart/top', + } + + def getIMDBids(self): movies = [] - enablers = [tryInt(x) for x in splitString(self.conf('automation_urls_use'))] - urls = splitString(self.conf('automation_urls')) + # Handle Chart URLs + if self.conf('automation_charts_theaters_use'): + log.debug('Started IMDB chart: %s', self.chart_urls['theater']) + data = self.getHTMLData(self.chart_urls['theater']) + if data: + html = BeautifulSoup(data) + + try: + result_div = html.find('div', attrs = {'id': 'main'}) + + entries = result_div.find_all('div', attrs = {'itemtype': 'http://schema.org/Movie'}) + + for entry in entries: + title = entry.find('h4', attrs = {'itemprop': 'name'}).getText() + + log.debug('Identified title: %s', title) + result = re.search('(.*) \((.*)\)', title) + + if result: + name = result.group(1) + year = result.group(2) + + imdb = self.search(name, year) + + if imdb and self.isMinimalMovie(imdb): + movies.append(imdb['imdb']) + + except: + log.error('Failed loading IMDB chart results from %s: %s', (self.chart_urls['theater'], traceback.format_exc())) + + if self.conf('automation_charts_top250_use'): + log.debug('Started IMDB chart: %s', self.chart_urls['top250']) + data = self.getHTMLData(self.chart_urls['top250']) + if data: + html = BeautifulSoup(data) + + try: + result_div = html.find('div', attrs = {'id': 'main'}) + + result_table = result_div.find_all('table')[1] + entries = result_table.find_all('tr') + + for entry in entries[1:]: + title = entry.find_all('td')[2].getText() + + log.debug('Identified title: %s', title) + result = re.search('(.*) \((.*)\)', title) + + if result: + name = result.group(1) + year = result.group(2) + + imdb = self.search(name, year) + + if imdb and self.isMinimalMovie(imdb): + movies.append(imdb['imdb']) + + except: + log.error('Failed loading IMDB chart results from %s: %s', (self.chart_urls['theater'], traceback.format_exc())) + + + # Handle Watchlists + watchlist_enablers = [tryInt(x) for x in splitString(self.conf('automation_urls_use'))] + watchlist_urls = splitString(self.conf('automation_urls')) index = -1 - for url in urls: + for watchlist_url in watchlist_urls: index += 1 - if not enablers[index]: + if not watchlist_enablers[index]: continue try: - rss_data = self.getHTMLData(url) + log.debug('Started IMDB watchlists: %s', watchlist_url) + rss_data = self.getHTMLData(watchlist_url) imdbs = getImdb(rss_data, multiple = True) if rss_data else [] for imdb in imdbs: @@ -35,4 +106,6 @@ class IMDB(Automation, RSS): except: log.error('Failed loading IMDB watchlist: %s %s', (url, traceback.format_exc())) + + # Return the combined resultset return movies From 4ebbc1a01d8bda9f5b4435afbc936ec9ff6c992d Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Sun, 14 Jul 2013 02:19:35 +0200 Subject: [PATCH 02/31] XBMC: Only scan the new movie folder --- couchpotato/core/notifications/xbmc/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/couchpotato/core/notifications/xbmc/main.py b/couchpotato/core/notifications/xbmc/main.py index ad6fa60..b1ad57a 100755 --- a/couchpotato/core/notifications/xbmc/main.py +++ b/couchpotato/core/notifications/xbmc/main.py @@ -34,7 +34,7 @@ class XBMC(Notification): ] if not self.conf('only_first') or hosts.index(host) == 0: - calls.append(('VideoLibrary.Scan', {})) + calls.append(('VideoLibrary.Scan', {'directory': data.get('destination_dir', None)})) max_successful += len(calls) response = self.request(host, calls) From 564a27461d6ab020c4bf83ea5101958d74febda2 Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Sun, 14 Jul 2013 23:30:37 +0200 Subject: [PATCH 03/31] XBMC: Only add directory if XBMC is on localhost --- couchpotato/core/notifications/xbmc/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/couchpotato/core/notifications/xbmc/main.py b/couchpotato/core/notifications/xbmc/main.py index b1ad57a..7266b25 100755 --- a/couchpotato/core/notifications/xbmc/main.py +++ b/couchpotato/core/notifications/xbmc/main.py @@ -34,7 +34,7 @@ class XBMC(Notification): ] if not self.conf('only_first') or hosts.index(host) == 0: - calls.append(('VideoLibrary.Scan', {'directory': data.get('destination_dir', None)})) + calls.append(('VideoLibrary.Scan', {'directory': data.get('destination_dir', None)} if 'localhost' in host else {})) max_successful += len(calls) response = self.request(host, calls) From 470fde08902e6c91e2ef2a05286205c2289e53a6 Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Sat, 20 Jul 2013 13:49:12 +0200 Subject: [PATCH 04/31] Unset the uTorrent read only flags Fix for #1871 Note that this is a fix for Windows only. I am unaware if this issue arises on Linux/Mac and what happens with this fix on those systems. --- couchpotato/core/downloaders/utorrent/main.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/couchpotato/core/downloaders/utorrent/main.py b/couchpotato/core/downloaders/utorrent/main.py index be6ff10..0459554 100644 --- a/couchpotato/core/downloaders/utorrent/main.py +++ b/couchpotato/core/downloaders/utorrent/main.py @@ -10,7 +10,9 @@ from multipartpost import MultipartPostHandler import cookielib import httplib import json +import os import re +import stat import time import urllib import urllib2 @@ -52,7 +54,7 @@ class uTorrent(Downloader): new_settings['seed_prio_limitul_flag'] = True log.info('Updated uTorrent settings to set a torrent to complete after it the seeding requirements are met.') - if settings.get('bt.read_only_on_complete'): #This doesnt work as this option seems to be not available through the api + if settings.get('bt.read_only_on_complete'): #This doesn't work as this option seems to be not available through the api. Mitigated with removeReadOnly function new_settings['bt.read_only_on_complete'] = False log.info('Updated uTorrent settings to not set the files to read only after completing.') @@ -130,8 +132,10 @@ class uTorrent(Downloader): status = 'busy' if 'Finished' in item[21]: status = 'completed' + self.removeReadOnly(item[26]) elif 'Seeding' in item[21]: status = 'seeding' + self.removeReadOnly(item[26]) statuses.append({ 'id': item[0], @@ -161,6 +165,13 @@ class uTorrent(Downloader): if not self.connect(): return False return self.utorrent_api.remove_torrent(item['id'], remove_data = delete_files) + + def removeReadOnly(self, folder): + #Removes all read-only flags in a folder + if folder and os.path.isdir(folder): + for root, folders, filenames in os.walk(folder): + for filename in filenames: + os.chmod(os.path.join(root, filename), stat.S_IWRITE) class uTorrentAPI(object): From fd95364d5ffd835c7b4cb4e62da8e2e9d38127ed Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Sat, 20 Jul 2013 13:53:19 +0200 Subject: [PATCH 05/31] uTorrent ratio issue fixed The tryFloat function returns 0 if it is fed with a float(!). This resulted in the seed_ratio being set to 0 on first/automatic download. When manually downloading, it did work as the ratio is stored as a string. --- couchpotato/core/downloaders/utorrent/main.py | 2 +- couchpotato/core/helpers/variable.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/couchpotato/core/downloaders/utorrent/main.py b/couchpotato/core/downloaders/utorrent/main.py index 0459554..79f9f5b 100644 --- a/couchpotato/core/downloaders/utorrent/main.py +++ b/couchpotato/core/downloaders/utorrent/main.py @@ -95,7 +95,7 @@ class uTorrent(Downloader): else: self.utorrent_api.add_torrent_file(torrent_filename, filedata) - # Change settings of added torrents + # Change settings of added torrent self.utorrent_api.set_torrent(torrent_hash, torrent_params) if self.conf('paused', default = 0): self.utorrent_api.pause_torrent(torrent_hash) diff --git a/couchpotato/core/helpers/variable.py b/couchpotato/core/helpers/variable.py index fa8a8b5..48daa28 100644 --- a/couchpotato/core/helpers/variable.py +++ b/couchpotato/core/helpers/variable.py @@ -140,7 +140,11 @@ def tryInt(s): except: return 0 def tryFloat(s): - try: return float(s) if '.' in s else tryInt(s) + try: + if isinstance(s, str): + return float(s) if '.' in s else tryInt(s) + else: + return float(s) except: return 0 def natsortKey(s): From 56a788286c83f49581b455e4d7832f9b7b1f7458 Mon Sep 17 00:00:00 2001 From: Micah James Date: Wed, 31 Jul 2013 22:41:49 -0400 Subject: [PATCH 06/31] Adding code for custom urls UI --- .../core/providers/automation/rottentomatoes/__init__.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/couchpotato/core/providers/automation/rottentomatoes/__init__.py b/couchpotato/core/providers/automation/rottentomatoes/__init__.py index 83a545b..579fe1f 100644 --- a/couchpotato/core/providers/automation/rottentomatoes/__init__.py +++ b/couchpotato/core/providers/automation/rottentomatoes/__init__.py @@ -19,6 +19,16 @@ config = [{ 'type': 'enabler', }, { + 'name': 'automation_urls_use', + 'label': 'Use', + }, + { + 'name': 'automation_urls', + 'label': 'url', + 'type': 'combined', + 'combine': ['automation_urls_use', 'automation_urls'], + }, + { 'name': 'tomatometer_percent', 'default': '80', 'label': 'Tomatometer', From 3a8f891c7d168d570b1c02ac51ebe00ebef34604 Mon Sep 17 00:00:00 2001 From: Micah James Date: Wed, 31 Jul 2013 22:45:48 -0400 Subject: [PATCH 07/31] Adding more code. --- couchpotato/core/providers/automation/rottentomatoes/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/couchpotato/core/providers/automation/rottentomatoes/__init__.py b/couchpotato/core/providers/automation/rottentomatoes/__init__.py index 579fe1f..1940614 100644 --- a/couchpotato/core/providers/automation/rottentomatoes/__init__.py +++ b/couchpotato/core/providers/automation/rottentomatoes/__init__.py @@ -8,7 +8,7 @@ config = [{ 'groups': [ { 'tab': 'automation', - 'list': 'automation_providers', + 'list': 'watchlist_providers', 'name': 'rottentomatoes_automation', 'label': 'Rottentomatoes', 'description': 'Imports movies from the rottentomatoes "in theaters"-feed.', From 797018fb8aabbc8caa39a9f91cdeb78a82c77168 Mon Sep 17 00:00:00 2001 From: Micah James Date: Wed, 31 Jul 2013 22:47:52 -0400 Subject: [PATCH 08/31] Revert "Adding more code." This reverts commit 3a8f891c7d168d570b1c02ac51ebe00ebef34604. --- couchpotato/core/providers/automation/rottentomatoes/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/couchpotato/core/providers/automation/rottentomatoes/__init__.py b/couchpotato/core/providers/automation/rottentomatoes/__init__.py index 1940614..579fe1f 100644 --- a/couchpotato/core/providers/automation/rottentomatoes/__init__.py +++ b/couchpotato/core/providers/automation/rottentomatoes/__init__.py @@ -8,7 +8,7 @@ config = [{ 'groups': [ { 'tab': 'automation', - 'list': 'watchlist_providers', + 'list': 'automation_providers', 'name': 'rottentomatoes_automation', 'label': 'Rottentomatoes', 'description': 'Imports movies from the rottentomatoes "in theaters"-feed.', From da50b19b6b08d456deef5b977eb2de40c05eafbb Mon Sep 17 00:00:00 2001 From: Micah James Date: Wed, 31 Jul 2013 23:06:12 -0400 Subject: [PATCH 09/31] Added custom url code handling --- .../providers/automation/rottentomatoes/main.py | 54 +++++++++++++--------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/couchpotato/core/providers/automation/rottentomatoes/main.py b/couchpotato/core/providers/automation/rottentomatoes/main.py index 9842d4c..47b9395 100644 --- a/couchpotato/core/providers/automation/rottentomatoes/main.py +++ b/couchpotato/core/providers/automation/rottentomatoes/main.py @@ -1,5 +1,5 @@ from couchpotato.core.helpers.rss import RSS -from couchpotato.core.helpers.variable import tryInt +from couchpotato.core.helpers.variable import tryInt, splitString from couchpotato.core.logger import CPLog from couchpotato.core.providers.automation.base import Automation from xml.etree.ElementTree import QName @@ -11,38 +11,48 @@ log = CPLog(__name__) class Rottentomatoes(Automation, RSS): interval = 1800 - urls = { - 'namespace': 'http://www.rottentomatoes.com/xmlns/rtmovie/', - 'theater': 'http://www.rottentomatoes.com/syndication/rss/in_theaters.xml', - } + + def getIMDBids(self): movies = [] - rss_movies = self.getRSSData(self.urls['theater']) - rating_tag = str(QName(self.urls['namespace'], 'tomatometer_percent')) + rotten_tomatoes_namespace = 'http://www.rottentomatoes.com/xmlns/rtmovie/' + enablers = [tryInt(x) for x in splitString(self.conf('automation_urls_use'))] + urls = splitString(self.conf('automation_urls')) + + index = -1 + + for url in urls: + + index += 1 + if not enablers[index]: + continue + + rss_movies = self.getRSSData(url) + rating_tag = str(QName(rotten_tomatoes_namespace, 'tomatometer_percent')) - for movie in rss_movies: + for movie in rss_movies: - value = self.getTextElement(movie, "title") - result = re.search('(?<=%\s).*', value) + value = self.getTextElement(movie, "title") + result = re.search('(?<=%\s).*', value) - if result: + if result: - log.info2('Something smells...') - rating = tryInt(self.getTextElement(movie, rating_tag)) - name = result.group(0) + log.info2('Something smells...') + rating = tryInt(self.getTextElement(movie, rating_tag)) + name = result.group(0) - if rating < tryInt(self.conf('tomatometer_percent')): - log.info2('%s seems to be rotten...', name) - else: + if rating < tryInt(self.conf('tomatometer_percent')): + log.info2('%s seems to be rotten...', name) + else: - log.info2('Found %s fresh enough movies, enqueuing: %s', (rating, name)) - year = datetime.datetime.now().strftime("%Y") - imdb = self.search(name, year) + log.info2('Found %s fresh enough movies, enqueuing: %s', (rating, name)) + year = datetime.datetime.now().strftime("%Y") + imdb = self.search(name, year) - if imdb and self.isMinimalMovie(imdb): - movies.append(imdb['imdb']) + if imdb and self.isMinimalMovie(imdb): + movies.append(imdb['imdb']) return movies From 4330dc39bf37d6e1332d2a2471454c6667b86aa7 Mon Sep 17 00:00:00 2001 From: Micah James Date: Wed, 31 Jul 2013 23:14:58 -0400 Subject: [PATCH 10/31] Changed description to be better suited for this. --- couchpotato/core/providers/automation/rottentomatoes/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/couchpotato/core/providers/automation/rottentomatoes/__init__.py b/couchpotato/core/providers/automation/rottentomatoes/__init__.py index 579fe1f..52b1c88 100644 --- a/couchpotato/core/providers/automation/rottentomatoes/__init__.py +++ b/couchpotato/core/providers/automation/rottentomatoes/__init__.py @@ -11,7 +11,7 @@ config = [{ 'list': 'automation_providers', 'name': 'rottentomatoes_automation', 'label': 'Rottentomatoes', - 'description': 'Imports movies from the rottentomatoes "in theaters"-feed.', + 'description': 'Imports movies from rottentomatoes rss feeds specified below.', 'options': [ { 'name': 'automation_enabled', From 4ffda9f705c32e903ea8cda2323b4272a290a653 Mon Sep 17 00:00:00 2001 From: Micah James Date: Thu, 1 Aug 2013 23:15:36 -0400 Subject: [PATCH 11/31] Made code more python-y per mano3ms recommendation. --- couchpotato/core/providers/automation/rottentomatoes/main.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/couchpotato/core/providers/automation/rottentomatoes/main.py b/couchpotato/core/providers/automation/rottentomatoes/main.py index 47b9395..40f72a5 100644 --- a/couchpotato/core/providers/automation/rottentomatoes/main.py +++ b/couchpotato/core/providers/automation/rottentomatoes/main.py @@ -19,15 +19,11 @@ class Rottentomatoes(Automation, RSS): movies = [] rotten_tomatoes_namespace = 'http://www.rottentomatoes.com/xmlns/rtmovie/' - enablers = [tryInt(x) for x in splitString(self.conf('automation_urls_use'))] - urls = splitString(self.conf('automation_urls')) - - index = -1 + urls = dict(zip(splitString(self.conf('automation_urls')), [tryInt(x) for x in splitString(self.conf('automation_urls_use'))])) for url in urls: - index += 1 - if not enablers[index]: + if not urls[url]: continue rss_movies = self.getRSSData(url) From 0492e90d6fdc76b97f505dcb851740d75187901d Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Tue, 16 Jul 2013 22:35:32 +0200 Subject: [PATCH 12/31] XBMC: properly check if host is local And added option to scan if remote --- couchpotato/core/notifications/xbmc/__init__.py | 8 ++++++++ couchpotato/core/notifications/xbmc/main.py | 12 ++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/couchpotato/core/notifications/xbmc/__init__.py b/couchpotato/core/notifications/xbmc/__init__.py index e3c467c..f0167ce 100644 --- a/couchpotato/core/notifications/xbmc/__init__.py +++ b/couchpotato/core/notifications/xbmc/__init__.py @@ -39,6 +39,14 @@ config = [{ 'description': 'Only update the first host when movie snatched, useful for synced XBMC', }, { + 'name': 'remote_dir_scan', + 'label': 'Remote Folder Scan', + 'default': 0, + 'type': 'bool', + 'advanced': True, + 'description': 'Scan new movie folder at remote XBMC servers, only works if movie location is the same.', + }, + { 'name': 'on_snatch', 'default': 0, 'type': 'bool', diff --git a/couchpotato/core/notifications/xbmc/main.py b/couchpotato/core/notifications/xbmc/main.py index 7266b25..34a9c1d 100755 --- a/couchpotato/core/notifications/xbmc/main.py +++ b/couchpotato/core/notifications/xbmc/main.py @@ -13,7 +13,7 @@ log = CPLog(__name__) class XBMC(Notification): - listen_to = ['renamer.after'] + listen_to = ['renamer.after', 'movie.snatched'] use_json_notifications = {} http_time_between_calls = 0 @@ -33,15 +33,19 @@ class XBMC(Notification): ('GUI.ShowNotification', {'title': self.default_title, 'message': message, 'image': self.getNotificationImage('small')}), ] - if not self.conf('only_first') or hosts.index(host) == 0: - calls.append(('VideoLibrary.Scan', {'directory': data.get('destination_dir', None)} if 'localhost' in host else {})) + if data and data.get('destination_dir') and (not self.conf('only_first') or hosts.index(host) == 0): + param = {} + if self.conf('remote_dir_scan') or socket.getfqdn('localhost') == socket.getfqdn(host.split(':')[0]): + param = {'directory': data['destination_dir']} + + calls.append(('VideoLibrary.Scan', param)) max_successful += len(calls) response = self.request(host, calls) else: response = self.notifyXBMCnoJSON(host, {'title':self.default_title, 'message':message}) - if not self.conf('only_first') or hosts.index(host) == 0: + if data and data.get('destination_dir') and (not self.conf('only_first') or hosts.index(host) == 0): response += self.request(host, [('VideoLibrary.Scan', {})]) max_successful += 1 From 3af6623a919769bfde5e407bfd60fff7c777ba93 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sun, 18 Aug 2013 00:22:36 +0200 Subject: [PATCH 13/31] Move registerPlugin to __new__ magic --- couchpotato/core/loader.py | 7 +------ couchpotato/core/plugins/base.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/couchpotato/core/loader.py b/couchpotato/core/loader.py index 8aef89a..9d04632 100644 --- a/couchpotato/core/loader.py +++ b/couchpotato/core/loader.py @@ -111,12 +111,7 @@ class Loader(object): def loadPlugins(self, module, name): try: - klass = module.start() - klass.registerPlugin() - - if klass and getattr(klass, 'auto_register_static'): - klass.registerStatic(module.__file__) - + module.start() return True except Exception, e: log.error('Failed loading plugin "%s": %s', (module.__file__, traceback.format_exc())) diff --git a/couchpotato/core/plugins/base.py b/couchpotato/core/plugins/base.py index f81d8a1..9e97c80 100644 --- a/couchpotato/core/plugins/base.py +++ b/couchpotato/core/plugins/base.py @@ -12,6 +12,7 @@ from urlparse import urlparse import cookielib import glob import gzip +import inspect import math import os.path import re @@ -35,11 +36,20 @@ class Plugin(object): http_failed_request = {} http_failed_disabled = {} + def __new__(typ, *args, **kwargs): + new_plugin = super(Plugin, typ).__new__(typ, *args, **kwargs) + new_plugin.registerPlugin() + + return new_plugin + def registerPlugin(self): addEvent('app.do_shutdown', self.doShutdown) addEvent('plugin.running', self.isRunning) self._running = [] + if self.auto_register_static: + self.registerStatic(inspect.getfile(self.__class__)) + def conf(self, attr, value = None, default = None, section = None): return Env.setting(attr, section = section if section else self.getName().lower(), value = value, default = default) From 62b571d5f1f232dab633201b1ed0f256fe5e1a38 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sun, 18 Aug 2013 11:44:00 +0200 Subject: [PATCH 14/31] Rename type to protocol --- couchpotato/core/downloaders/base.py | 20 ++++++++-------- couchpotato/core/downloaders/blackhole/main.py | 24 +++++++++---------- couchpotato/core/downloaders/nzbget/main.py | 2 +- couchpotato/core/downloaders/nzbvortex/main.py | 2 +- couchpotato/core/downloaders/pneumatic/main.py | 4 ++-- couchpotato/core/downloaders/sabnzbd/main.py | 2 +- couchpotato/core/downloaders/synology/main.py | 28 +++++++++++------------ couchpotato/core/downloaders/transmission/main.py | 8 +++---- couchpotato/core/downloaders/utorrent/main.py | 10 ++++---- couchpotato/core/media/_base/searcher/main.py | 22 +++++++++--------- couchpotato/core/media/movie/searcher/main.py | 18 +++++++-------- couchpotato/core/providers/base.py | 17 ++++++++------ couchpotato/core/providers/nzb/base.py | 3 ++- couchpotato/core/providers/torrent/base.py | 4 ++-- 14 files changed, 84 insertions(+), 80 deletions(-) diff --git a/couchpotato/core/downloaders/base.py b/couchpotato/core/downloaders/base.py index b820b9f..cc0d59e 100644 --- a/couchpotato/core/downloaders/base.py +++ b/couchpotato/core/downloaders/base.py @@ -11,7 +11,7 @@ log = CPLog(__name__) class Downloader(Provider): - type = [] + protocol = [] http_time_between_calls = 0 torrent_sources = [ @@ -36,16 +36,16 @@ class Downloader(Provider): def __init__(self): addEvent('download', self._download) addEvent('download.enabled', self._isEnabled) - addEvent('download.enabled_types', self.getEnabledDownloadType) + addEvent('download.enabled_protocols', self.getEnabledProtocol) addEvent('download.status', self._getAllDownloadStatus) addEvent('download.remove_failed', self._removeFailed) addEvent('download.pause', self._pause) addEvent('download.process_complete', self._processComplete) - def getEnabledDownloadType(self): - for download_type in self.type: - if self.isEnabled(manual = True, data = {'type': download_type}): - return self.type + def getEnabledProtocol(self): + for download_protocol in self.protocol: + if self.isEnabled(manual = True, data = {'protocol': download_protocol}): + return self.protocol return [] @@ -91,11 +91,11 @@ class Downloader(Provider): def processComplete(self, item, delete_files): return - def isCorrectType(self, item_type): - is_correct = item_type in self.type + def isCorrectProtocol(self, item_protocol): + is_correct = item_protocol in self.protocol if not is_correct: - log.debug("Downloader doesn't support this type") + log.debug("Downloader doesn't support this protocol") return is_correct @@ -140,7 +140,7 @@ class Downloader(Provider): d_manual = self.conf('manual', default = False) return super(Downloader, self).isEnabled() and \ ((d_manual and manual) or (d_manual is False)) and \ - (not data or self.isCorrectType(data.get('type'))) + (not data or self.isCorrectProtocol(data.get('protocol'))) def _pause(self, item, pause = True): if self.isDisabled(manual = True, data = {}): diff --git a/couchpotato/core/downloaders/blackhole/main.py b/couchpotato/core/downloaders/blackhole/main.py index 82b0727..9d2a526 100644 --- a/couchpotato/core/downloaders/blackhole/main.py +++ b/couchpotato/core/downloaders/blackhole/main.py @@ -10,20 +10,20 @@ log = CPLog(__name__) class Blackhole(Downloader): - type = ['nzb', 'torrent', 'torrent_magnet'] + protocol = ['nzb', 'torrent', 'torrent_magnet'] def download(self, data = {}, movie = {}, filedata = None): directory = self.conf('directory') if not directory or not os.path.isdir(directory): - log.error('No directory set for blackhole %s download.', data.get('type')) + log.error('No directory set for blackhole %s download.', data.get('protocol')) else: try: if not filedata or len(filedata) < 50: try: - if data.get('type') == 'torrent_magnet': + if data.get('protocol') == 'torrent_magnet': filedata = self.magnetToTorrent(data.get('url')) - data['type'] = 'torrent' + data['protocol'] = 'torrent' except: log.error('Failed download torrent via magnet url: %s', traceback.format_exc()) @@ -35,7 +35,7 @@ class Blackhole(Downloader): try: if not os.path.isfile(fullPath): - log.info('Downloading %s to %s.', (data.get('type'), fullPath)) + log.info('Downloading %s to %s.', (data.get('protocol'), fullPath)) with open(fullPath, 'wb') as f: f.write(filedata) os.chmod(fullPath, Env.getPermission('file')) @@ -54,20 +54,20 @@ class Blackhole(Downloader): return False - def getEnabledDownloadType(self): + def getEnabledProtocol(self): if self.conf('use_for') == 'both': - return super(Blackhole, self).getEnabledDownloadType() + return super(Blackhole, self).getEnabledProtocol() elif self.conf('use_for') == 'torrent': return ['torrent', 'torrent_magnet'] else: return ['nzb'] def isEnabled(self, manual, data = {}): - for_type = ['both'] - if data and 'torrent' in data.get('type'): - for_type.append('torrent') + for_protocol = ['both'] + if data and 'torrent' in data.get('protocol'): + for_protocol.append('torrent') elif data: - for_type.append(data.get('type')) + for_protocol.append(data.get('protocol')) return super(Blackhole, self).isEnabled(manual, data) and \ - ((self.conf('use_for') in for_type)) + ((self.conf('use_for') in for_protocol)) diff --git a/couchpotato/core/downloaders/nzbget/main.py b/couchpotato/core/downloaders/nzbget/main.py index ef9d4ef..ba3b261 100644 --- a/couchpotato/core/downloaders/nzbget/main.py +++ b/couchpotato/core/downloaders/nzbget/main.py @@ -15,7 +15,7 @@ log = CPLog(__name__) class NZBGet(Downloader): - type = ['nzb'] + protocol = ['nzb'] url = 'http://%(username)s:%(password)s@%(host)s/xmlrpc' diff --git a/couchpotato/core/downloaders/nzbvortex/main.py b/couchpotato/core/downloaders/nzbvortex/main.py index 805c459..b8817ac 100644 --- a/couchpotato/core/downloaders/nzbvortex/main.py +++ b/couchpotato/core/downloaders/nzbvortex/main.py @@ -19,7 +19,7 @@ log = CPLog(__name__) class NZBVortex(Downloader): - type = ['nzb'] + protocol = ['nzb'] api_level = None session_id = None diff --git a/couchpotato/core/downloaders/pneumatic/main.py b/couchpotato/core/downloaders/pneumatic/main.py index 5564dca..25923e0 100644 --- a/couchpotato/core/downloaders/pneumatic/main.py +++ b/couchpotato/core/downloaders/pneumatic/main.py @@ -9,7 +9,7 @@ log = CPLog(__name__) class Pneumatic(Downloader): - type = ['nzb'] + protocol = ['nzb'] strm_syntax = 'plugin://plugin.program.pneumatic/?mode=strm&type=add_file&nzb=%s&nzbname=%s' def download(self, data = {}, movie = {}, filedata = None): @@ -27,7 +27,7 @@ class Pneumatic(Downloader): try: if not os.path.isfile(fullPath): - log.info('Downloading %s to %s.', (data.get('type'), fullPath)) + log.info('Downloading %s to %s.', (data.get('protocol'), fullPath)) with open(fullPath, 'wb') as f: f.write(filedata) diff --git a/couchpotato/core/downloaders/sabnzbd/main.py b/couchpotato/core/downloaders/sabnzbd/main.py index 776749b..468f30b 100644 --- a/couchpotato/core/downloaders/sabnzbd/main.py +++ b/couchpotato/core/downloaders/sabnzbd/main.py @@ -13,7 +13,7 @@ log = CPLog(__name__) class Sabnzbd(Downloader): - type = ['nzb'] + protocol = ['nzb'] def download(self, data = {}, movie = {}, filedata = None): diff --git a/couchpotato/core/downloaders/synology/main.py b/couchpotato/core/downloaders/synology/main.py index 8721274..362577f 100644 --- a/couchpotato/core/downloaders/synology/main.py +++ b/couchpotato/core/downloaders/synology/main.py @@ -9,13 +9,13 @@ log = CPLog(__name__) class Synology(Downloader): - type = ['nzb', 'torrent', 'torrent_magnet'] + protocol = ['nzb', 'torrent', 'torrent_magnet'] log = CPLog(__name__) def download(self, data, movie, filedata = None): response = False - log.error('Sending "%s" (%s) to Synology.', (data['name'], data['type'])) + log.error('Sending "%s" (%s) to Synology.', (data['name'], data['protocol'])) # Load host from config and split out port. host = self.conf('host').split(':') @@ -26,38 +26,38 @@ class Synology(Downloader): try: # Send request to Synology srpc = SynologyRPC(host[0], host[1], self.conf('username'), self.conf('password')) - if data['type'] == 'torrent_magnet': + if data['protocol'] == 'torrent_magnet': log.info('Adding torrent URL %s', data['url']) response = srpc.create_task(url = data['url']) - elif data['type'] in ['nzb', 'torrent']: - log.info('Adding %s' % data['type']) + elif data['protocol'] in ['nzb', 'torrent']: + log.info('Adding %s' % data['protocol']) if not filedata: - log.error('No %s data found' % data['type']) + log.error('No %s data found' % data['protocol']) else: - filename = data['name'] + '.' + data['type'] + filename = data['name'] + '.' + data['protocol'] response = srpc.create_task(filename = filename, filedata = filedata) except Exception, err: log.error('Exception while adding torrent: %s', err) finally: return response - def getEnabledDownloadType(self): + def getEnabledProtocol(self): if self.conf('use_for') == 'both': - return super(Synology, self).getEnabledDownloadType() + return super(Synology, self).getEnabledProtocol() elif self.conf('use_for') == 'torrent': return ['torrent', 'torrent_magnet'] else: return ['nzb'] def isEnabled(self, manual, data = {}): - for_type = ['both'] - if data and 'torrent' in data.get('type'): - for_type.append('torrent') + for_protocol = ['both'] + if data and 'torrent' in data.get('protocol'): + for_protocol.append('torrent') elif data: - for_type.append(data.get('type')) + for_protocol.append(data.get('protocol')) return super(Synology, self).isEnabled(manual, data) and\ - ((self.conf('use_for') in for_type)) + ((self.conf('use_for') in for_protocol)) class SynologyRPC(object): diff --git a/couchpotato/core/downloaders/transmission/main.py b/couchpotato/core/downloaders/transmission/main.py index a619d41..89c7099 100644 --- a/couchpotato/core/downloaders/transmission/main.py +++ b/couchpotato/core/downloaders/transmission/main.py @@ -16,7 +16,7 @@ log = CPLog(__name__) class Transmission(Downloader): - type = ['torrent', 'torrent_magnet'] + protocol = ['torrent', 'torrent_magnet'] log = CPLog(__name__) trpc = None @@ -34,12 +34,12 @@ class Transmission(Downloader): def download(self, data, movie, filedata = None): - log.info('Sending "%s" (%s) to Transmission.', (data.get('name'), data.get('type'))) + log.info('Sending "%s" (%s) to Transmission.', (data.get('name'), data.get('protocol'))) if not self.connect(): return False - if not filedata and data.get('type') == 'torrent': + if not filedata and data.get('protocol') == 'torrent': log.error('Failed sending torrent, no data') return False @@ -64,7 +64,7 @@ class Transmission(Downloader): torrent_params['seedIdleMode'] = 1 # Send request to Transmission - if data.get('type') == 'torrent_magnet': + if data.get('protocol') == 'torrent_magnet': remote_torrent = self.trpc.add_torrent_uri(data.get('url'), arguments = params) torrent_params['trackerAdd'] = self.torrent_trackers else: diff --git a/couchpotato/core/downloaders/utorrent/main.py b/couchpotato/core/downloaders/utorrent/main.py index be6ff10..d5cc64f 100644 --- a/couchpotato/core/downloaders/utorrent/main.py +++ b/couchpotato/core/downloaders/utorrent/main.py @@ -20,7 +20,7 @@ log = CPLog(__name__) class uTorrent(Downloader): - type = ['torrent', 'torrent_magnet'] + protocol = ['torrent', 'torrent_magnet'] utorrent_api = None def connect(self): @@ -36,7 +36,7 @@ class uTorrent(Downloader): def download(self, data, movie, filedata = None): - log.debug('Sending "%s" (%s) to uTorrent.', (data.get('name'), data.get('type'))) + log.debug('Sending "%s" (%s) to uTorrent.', (data.get('name'), data.get('protocol'))) if not self.connect(): return False @@ -63,11 +63,11 @@ class uTorrent(Downloader): if self.conf('label'): torrent_params['label'] = self.conf('label') - if not filedata and data.get('type') == 'torrent': + if not filedata and data.get('protocol') == 'torrent': log.error('Failed sending torrent, no data') return False - if data.get('type') == 'torrent_magnet': + if data.get('protocol') == 'torrent_magnet': torrent_hash = re.findall('urn:btih:([\w]{32,40})', data.get('url'))[0].upper() torrent_params['trackers'] = '%0D%0A%0D%0A'.join(self.torrent_trackers) else: @@ -88,7 +88,7 @@ class uTorrent(Downloader): torrent_hash = b16encode(b32decode(torrent_hash)) # Send request to uTorrent - if data.get('type') == 'torrent_magnet': + if data.get('protocol') == 'torrent_magnet': self.utorrent_api.add_torrent_uri(data.get('url')) else: self.utorrent_api.add_torrent_file(torrent_filename, filedata) diff --git a/couchpotato/core/media/_base/searcher/main.py b/couchpotato/core/media/_base/searcher/main.py index 55dfe3e..7d84a58 100644 --- a/couchpotato/core/media/_base/searcher/main.py +++ b/couchpotato/core/media/_base/searcher/main.py @@ -19,7 +19,7 @@ log = CPLog(__name__) class Searcher(SearcherBase): def __init__(self): - addEvent('searcher.get_types', self.getSearchTypes) + addEvent('searcher.protocols', self.getSearchProtocols) addEvent('searcher.contains_other_quality', self.containsOtherQuality) addEvent('searcher.correct_year', self.correctYear) addEvent('searcher.correct_name', self.correctName) @@ -122,29 +122,29 @@ class Searcher(SearcherBase): return True - log.info('Tried to download, but none of the "%s" downloaders are enabled or gave an error', (data.get('type', ''))) + log.info('Tried to download, but none of the "%s" downloaders are enabled or gave an error', (data.get('protocol', ''))) return False - def getSearchTypes(self): + def getSearchProtocols(self): - download_types = fireEvent('download.enabled_types', merge = True) - provider_types = fireEvent('provider.enabled_types', merge = True) + download_protocols = fireEvent('download.enabled_protocols', merge = True) + provider_protocols = fireEvent('provider.enabled_protocols', merge = True) - if download_types and len(list(set(provider_types) & set(download_types))) == 0: - log.error('There aren\'t any providers enabled for your downloader (%s). Check your settings.', ','.join(download_types)) + if download_protocols and len(list(set(provider_protocols) & set(download_protocols))) == 0: + log.error('There aren\'t any providers enabled for your downloader (%s). Check your settings.', ','.join(download_protocols)) return [] - for useless_provider in list(set(provider_types) - set(download_types)): + for useless_provider in list(set(provider_protocols) - set(download_protocols)): log.debug('Provider for "%s" enabled, but no downloader.', useless_provider) - search_types = download_types + search_protocols = download_protocols - if len(search_types) == 0: + if len(search_protocols) == 0: log.error('There aren\'t any downloaders enabled. Please pick one in settings.') return [] - return search_types + return search_protocols def containsOtherQuality(self, nzb, movie_year = None, preferred_quality = {}): diff --git a/couchpotato/core/media/movie/searcher/main.py b/couchpotato/core/media/movie/searcher/main.py index 30f27d7..8fb4acf 100644 --- a/couchpotato/core/media/movie/searcher/main.py +++ b/couchpotato/core/media/movie/searcher/main.py @@ -85,7 +85,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase): } try: - search_types = fireEvent('searcher.get_types', single = True) + search_protocols = fireEvent('searcher.protocols', single = True) for movie in movies: movie_dict = movie.to_dict({ @@ -97,7 +97,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase): }) try: - self.single(movie_dict, search_types) + self.single(movie_dict, search_protocols) except IndexError: log.error('Forcing library update for %s, if you see this often, please report: %s', (movie_dict['library']['identifier'], traceback.format_exc())) fireEvent('library.update', movie_dict['library']['identifier'], force = True) @@ -115,12 +115,12 @@ class MovieSearcher(SearcherBase, MovieTypeBase): self.in_progress = False - def single(self, movie, search_types = None): + def single(self, movie, search_protocols = None): # Find out search type try: - if not search_types: - search_types = fireEvent('searcher.get_types', single = True) + if not search_protocols: + search_protocols = fireEvent('searcher.protocols', single = True) except SearchSetupError: return @@ -168,10 +168,10 @@ class MovieSearcher(SearcherBase, MovieTypeBase): quality = fireEvent('quality.single', identifier = quality_type['quality']['identifier'], single = True) results = [] - for search_type in search_types: - type_results = fireEvent('%s.search' % search_type, movie, quality, merge = True) - if type_results: - results += type_results + for search_protocol in search_protocols: + protocol_results = fireEvent('provider.search.%s.movie' % search_protocol, movie, quality, merge = True) + if protocol_results: + results += protocol_results sorted_results = sorted(results, key = lambda k: k['score'], reverse = True) if len(sorted_results) == 0: diff --git a/couchpotato/core/providers/base.py b/couchpotato/core/providers/base.py index d7ac7d1..08b4c6e 100644 --- a/couchpotato/core/providers/base.py +++ b/couchpotato/core/providers/base.py @@ -19,7 +19,7 @@ log = CPLog(__name__) class Provider(Plugin): - type = None # movie, nzb, torrent, subtitle, trailer + type = None # movie, show, subtitle, trailer, ... http_time_between_calls = 10 # Default timeout for url requests last_available_check = {} @@ -79,7 +79,10 @@ class Provider(Plugin): class YarrProvider(Provider): - cat_ids = [] + protocol = None # nzb, torrent, torrent_magnet + + cat_ids = {} + cat_backup_id = None sizeGb = ['gb', 'gib'] sizeMb = ['mb', 'mib'] @@ -89,14 +92,13 @@ class YarrProvider(Provider): last_login_check = 0 def __init__(self): - addEvent('provider.enabled_types', self.getEnabledProviderType) + addEvent('provider.enabled_protocols', self.getEnabledProtocol) addEvent('provider.belongs_to', self.belongsTo) - addEvent('yarr.search', self.search) - addEvent('%s.search' % self.type, self.search) + addEvent('provider.search.%s.%s' % (self.protocol, self.type), self.search) - def getEnabledProviderType(self): + def getEnabledProtocol(self): if self.isEnabled(): - return self.type + return self.protocol else: return [] @@ -273,6 +275,7 @@ class ResultList(list): defaults = { 'id': 0, + 'protocol': self.provider.protocol, 'type': self.provider.type, 'provider': self.provider.getName(), 'download': self.provider.loginDownload if self.provider.urls.get('login') else self.provider.download, diff --git a/couchpotato/core/providers/nzb/base.py b/couchpotato/core/providers/nzb/base.py index f11382b..53c73af 100644 --- a/couchpotato/core/providers/nzb/base.py +++ b/couchpotato/core/providers/nzb/base.py @@ -3,7 +3,8 @@ import time class NZBProvider(YarrProvider): - type = 'nzb' + + protocol = 'nzb' def calculateAge(self, unix): return int(time.time() - unix) / 24 / 60 / 60 diff --git a/couchpotato/core/providers/torrent/base.py b/couchpotato/core/providers/torrent/base.py index 453954c..3e7ddde 100644 --- a/couchpotato/core/providers/torrent/base.py +++ b/couchpotato/core/providers/torrent/base.py @@ -7,7 +7,7 @@ log = CPLog(__name__) class TorrentProvider(YarrProvider): - type = 'torrent' + protocol = 'torrent' def imdbMatch(self, url, imdbId): if getImdb(url) == imdbId: @@ -27,6 +27,6 @@ class TorrentProvider(YarrProvider): class TorrentMagnetProvider(TorrentProvider): - type = 'torrent_magnet' + protocol = 'torrent_magnet' download = None From 3dff598d03e9feb456ee639ede6128529c730a7e Mon Sep 17 00:00:00 2001 From: Ruud Date: Sun, 18 Aug 2013 11:45:45 +0200 Subject: [PATCH 15/31] Add multiprovider for provider grouping --- couchpotato/core/plugins/base.py | 10 ++++++++-- couchpotato/core/providers/base.py | 21 ++++++++++++++++++++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/couchpotato/core/plugins/base.py b/couchpotato/core/plugins/base.py index 9e97c80..84ecc45 100644 --- a/couchpotato/core/plugins/base.py +++ b/couchpotato/core/plugins/base.py @@ -25,6 +25,8 @@ log = CPLog(__name__) class Plugin(object): + _class_name = None + enabled_option = 'enabled' auto_register_static = True @@ -51,10 +53,14 @@ class Plugin(object): self.registerStatic(inspect.getfile(self.__class__)) def conf(self, attr, value = None, default = None, section = None): - return Env.setting(attr, section = section if section else self.getName().lower(), value = value, default = default) + class_name = self.getName().lower().split(':') + return Env.setting(attr, section = section if section else class_name[0].lower(), value = value, default = default) def getName(self): - return self.__class__.__name__ + return self._class_name or self.__class__.__name__ + + def setName(self, name): + self._class_name = name def renderTemplate(self, parent_file, templ, **params): diff --git a/couchpotato/core/providers/base.py b/couchpotato/core/providers/base.py index 08b4c6e..f2db8da 100644 --- a/couchpotato/core/providers/base.py +++ b/couchpotato/core/providers/base.py @@ -13,10 +13,29 @@ import traceback import urllib2 import xml.etree.ElementTree as XMLTree - log = CPLog(__name__) +class MultiProvider(Plugin): + + def __init__(self): + self._classes = [] + + for Type in self.getTypes(): + klass = Type() + + # Overwrite name so logger knows what we're talking about + klass.setName('%s:%s' % (self.getName(), klass.getName())) + + self._classes.append(klass) + + def getTypes(self): + return [] + + def getClasses(self): + return self._classes + + class Provider(Plugin): type = None # movie, show, subtitle, trailer, ... From 9860a1c138f42e8b776714800fc76b68c10806d7 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sun, 18 Aug 2013 13:17:40 +0200 Subject: [PATCH 16/31] Default to movie type --- couchpotato/core/providers/base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/couchpotato/core/providers/base.py b/couchpotato/core/providers/base.py index f2db8da..e6a9cb0 100644 --- a/couchpotato/core/providers/base.py +++ b/couchpotato/core/providers/base.py @@ -99,6 +99,7 @@ class Provider(Plugin): class YarrProvider(Provider): protocol = None # nzb, torrent, torrent_magnet + type = 'movie' cat_ids = {} cat_backup_id = None From 175c26bea916f2a373e54e948a9f5cd566795e6f Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Sat, 20 Jul 2013 13:55:07 +0200 Subject: [PATCH 17/31] Fix untagDir and hastagDir Changes in commit 8a252bff64b2bb2d376673a0b00e9624d44aaf4c broke the tagging functionality --- couchpotato/core/plugins/renamer/main.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/couchpotato/core/plugins/renamer/main.py b/couchpotato/core/plugins/renamer/main.py index d1daf18..1121691 100644 --- a/couchpotato/core/plugins/renamer/main.py +++ b/couchpotato/core/plugins/renamer/main.py @@ -517,22 +517,22 @@ Remove it if you want it to be renamed (again, or at least let it try again) if ignore_file: self.createFile(ignore_file, text) - def untagDir(self, folder, tag = None): + def untagDir(self, folder, tag = ''): if not os.path.isdir(folder): return # Remove any .ignore files for root, dirnames, filenames in os.walk(folder): - for filename in fnmatch.filter(filenames, '%s.ignore' % tag if tag else '*'): + for filename in fnmatch.filter(filenames, '*%s.ignore' % tag): os.remove((os.path.join(root, filename))) - def hastagDir(self, folder, tag = None): + def hastagDir(self, folder, tag = ''): if not os.path.isdir(folder): return False # Find any .ignore files for root, dirnames, filenames in os.walk(folder): - if fnmatch.filter(filenames, '%s.ignore' % tag if tag else '*'): + if fnmatch.filter(filenames, '*%s.ignore' % tag): return True return False From d0735a6d5885d821ace5743b9ccc1bf334f5bf3b Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Sat, 20 Jul 2013 16:01:37 +0200 Subject: [PATCH 18/31] Add failsafe for symlink errors E.g. on Windows you need Admin rights to symlink... --- couchpotato/core/plugins/renamer/main.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/couchpotato/core/plugins/renamer/main.py b/couchpotato/core/plugins/renamer/main.py index 1121691..d068c48 100644 --- a/couchpotato/core/plugins/renamer/main.py +++ b/couchpotato/core/plugins/renamer/main.py @@ -552,7 +552,11 @@ Remove it if you want it to be renamed (again, or at least let it try again) shutil.copy(old, dest) elif self.conf('file_action') == 'move_symlink': shutil.move(old, dest) - symlink(dest, old) + try: + symlink(dest, old) + except: + log.error('Couldn\'t symlink file "%s" to "%s". Copying the file back. Error: %s. ', (old, dest, traceback.format_exc())) + shutil.copy(dest, old) else: shutil.move(old, dest) From 695cdea4476aea8bc462b5adb0b40286f3c98138 Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Sat, 3 Aug 2013 01:13:36 +0200 Subject: [PATCH 19/31] Remove 'move' exception No need to remove files when 'move' is selected as the downloaders do this themselves now when cleaning up --- couchpotato/core/downloaders/transmission/__init__.py | 2 +- couchpotato/core/downloaders/utorrent/__init__.py | 2 +- couchpotato/core/plugins/renamer/main.py | 10 ++++------ 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/couchpotato/core/downloaders/transmission/__init__.py b/couchpotato/core/downloaders/transmission/__init__.py index d0e8279..f96e628 100644 --- a/couchpotato/core/downloaders/transmission/__init__.py +++ b/couchpotato/core/downloaders/transmission/__init__.py @@ -47,7 +47,7 @@ config = [{ { 'name': 'remove_complete', 'label': 'Remove torrent', - 'default': False, + 'default': True, 'advanced': True, 'type': 'bool', 'description': 'Remove the torrent from Transmission after it finished seeding.', diff --git a/couchpotato/core/downloaders/utorrent/__init__.py b/couchpotato/core/downloaders/utorrent/__init__.py index 6a1da36..d45e2e6 100644 --- a/couchpotato/core/downloaders/utorrent/__init__.py +++ b/couchpotato/core/downloaders/utorrent/__init__.py @@ -39,7 +39,7 @@ config = [{ { 'name': 'remove_complete', 'label': 'Remove torrent', - 'default': False, + 'default': True, 'advanced': True, 'type': 'bool', 'description': 'Remove the torrent from uTorrent after it finished seeding.', diff --git a/couchpotato/core/plugins/renamer/main.py b/couchpotato/core/plugins/renamer/main.py index d068c48..dfe1b62 100644 --- a/couchpotato/core/plugins/renamer/main.py +++ b/couchpotato/core/plugins/renamer/main.py @@ -204,7 +204,7 @@ class Renamer(Plugin): # Move nfo depending on settings if file_type is 'nfo' and not self.conf('rename_nfo'): log.debug('Skipping, renaming of %s disabled', file_type) - if self.conf('cleanup') and not (self.conf('file_action') != 'move' and self.downloadIsTorrent(download_info)): + if self.conf('cleanup') and not self.downloadIsTorrent(download_info): for current_file in group['files'][file_type]: remove_files.append(current_file) continue @@ -387,7 +387,7 @@ class Renamer(Plugin): # Remove leftover files if self.conf('cleanup') and not self.conf('move_leftover') and remove_leftovers and \ - not (self.conf('file_action') != 'move' and self.downloadIsTorrent(download_info)): + not self.downloadIsTorrent(download_info): log.debug('Removing leftover files') for current_file in group['files']['leftover']: remove_files.append(current_file) @@ -444,8 +444,7 @@ class Renamer(Plugin): self.tagDir(group, 'failed_rename') # Tag folder if it is in the 'from' folder and it will not be removed because it is a torrent - if self.movieInFromFolder(movie_folder) and \ - self.conf('file_action') != 'move' and self.downloadIsTorrent(download_info): + if self.movieInFromFolder(movie_folder) and self.downloadIsTorrent(download_info): self.tagDir(group, 'renamed_already') # Remove matching releases @@ -456,8 +455,7 @@ class Renamer(Plugin): except: log.error('Failed removing %s: %s', (release.identifier, traceback.format_exc())) - if group['dirname'] and group['parentdir'] and \ - not (self.conf('file_action') != 'move' and self.downloadIsTorrent(download_info)): + if group['dirname'] and group['parentdir'] and not self.downloadIsTorrent(download_info): try: log.info('Deleting folder: %s', group['parentdir']) self.deleteEmptyFolder(group['parentdir']) From 70bc2a6656427fa5405889e3c219e7256e55fecc Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Wed, 21 Aug 2013 20:49:01 +0200 Subject: [PATCH 20/31] use right variable for pause fixes #2049 --- couchpotato/core/downloaders/transmission/main.py | 4 ++-- couchpotato/core/downloaders/utorrent/main.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/couchpotato/core/downloaders/transmission/main.py b/couchpotato/core/downloaders/transmission/main.py index a619d41..12082f6 100644 --- a/couchpotato/core/downloaders/transmission/main.py +++ b/couchpotato/core/downloaders/transmission/main.py @@ -129,9 +129,9 @@ class Transmission(Downloader): def pause(self, item, pause = True): if pause: - return self.trpc.stop_torrent(item['hashString']) + return self.trpc.stop_torrent(item['id']) else: - return self.trpc.start_torrent(item['hashString']) + return self.trpc.start_torrent(item['id']) def removeFailed(self, item): log.info('%s failed downloading, deleting...', item['name']) diff --git a/couchpotato/core/downloaders/utorrent/main.py b/couchpotato/core/downloaders/utorrent/main.py index 79f9f5b..588d758 100644 --- a/couchpotato/core/downloaders/utorrent/main.py +++ b/couchpotato/core/downloaders/utorrent/main.py @@ -149,10 +149,10 @@ class uTorrent(Downloader): return statuses - def pause(self, download_info, pause = True): + def pause(self, item, pause = True): if not self.connect(): return False - return self.utorrent_api.pause_torrent(download_info['id'], pause) + return self.utorrent_api.pause_torrent(item['id'], pause) def removeFailed(self, item): log.info('%s failed downloading, deleting...', item['name']) From bf6bcaed723f9930b7594559d8f2e84f8278804e Mon Sep 17 00:00:00 2001 From: mano3m <-> Date: Thu, 22 Aug 2013 21:20:02 +0200 Subject: [PATCH 21/31] provide more info in case no movie is found Several users reported an issue with "more than one group found (0)", and it was unclear to them what it meant. This might help. --- couchpotato/core/plugins/scanner/main.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/couchpotato/core/plugins/scanner/main.py b/couchpotato/core/plugins/scanner/main.py index e48d274..743b1a5 100644 --- a/couchpotato/core/plugins/scanner/main.py +++ b/couchpotato/core/plugins/scanner/main.py @@ -329,14 +329,17 @@ class Scanner(Plugin): del movie_files + total_found = len(valid_files) + # Make sure only one movie was found if a download ID is provided - if download_info and not len(valid_files) == 1: + if download_info and total_found == 0: + log.info('Download ID provided (%s), but no groups found! Make sure the download contains valid media files (fully extracted).', download_info.get('imdb_id')) + elif download_info and total_found > 1: log.info('Download ID provided (%s), but more than one group found (%s). Ignoring Download ID...', (download_info.get('imdb_id'), len(valid_files))) download_info = None # Determine file types processed_movies = {} - total_found = len(valid_files) while True and not self.shuttingDown(): try: identifier, group = valid_files.popitem() From 6aec5a9a606447526cc6169ddeca723ff4e937af Mon Sep 17 00:00:00 2001 From: Ruud Date: Sat, 24 Aug 2013 12:13:45 +0200 Subject: [PATCH 22/31] Cleanup IMDB provider --- .../core/providers/automation/imdb/__init__.py | 12 +- couchpotato/core/providers/automation/imdb/main.py | 123 ++++++++++----------- 2 files changed, 63 insertions(+), 72 deletions(-) diff --git a/couchpotato/core/providers/automation/imdb/__init__.py b/couchpotato/core/providers/automation/imdb/__init__.py index ee804af..546cba9 100644 --- a/couchpotato/core/providers/automation/imdb/__init__.py +++ b/couchpotato/core/providers/automation/imdb/__init__.py @@ -38,21 +38,23 @@ config = [{ 'description': 'Import movies from IMDB Charts', 'options': [ { - 'name': 'automation_enabled', + 'name': 'automation_providers_enabled', 'default': False, 'type': 'enabler', }, { - 'name': 'automation_charts_theaters_use', - 'type': 'checkbox', + 'name': 'automation_charts_theater', + 'type': 'bool', 'label': 'In Theaters', 'description': 'New Movies In-Theaters chart', + 'default': True, }, { - 'name': 'automation_charts_top250_use', - 'type': 'checkbox', + 'name': 'automation_charts_top250', + 'type': 'bool', 'label': 'TOP 250', 'description': 'IMDB TOP 250 chart', + 'default': True, }, ], }, diff --git a/couchpotato/core/providers/automation/imdb/main.py b/couchpotato/core/providers/automation/imdb/main.py index 0d49494..c4aef7f 100644 --- a/couchpotato/core/providers/automation/imdb/main.py +++ b/couchpotato/core/providers/automation/imdb/main.py @@ -1,90 +1,41 @@ +import traceback + from bs4 import BeautifulSoup +from couchpotato import fireEvent from couchpotato.core.helpers.rss import RSS from couchpotato.core.helpers.variable import getImdb, splitString, tryInt + from couchpotato.core.logger import CPLog from couchpotato.core.providers.automation.base import Automation -import re -import traceback - -log = CPLog(__name__) - - -class IMDB(Automation, RSS): - - interval = 1800 - - chart_urls = { - 'theater': 'http://www.imdb.com/movies-in-theaters/', - 'top250': 'http://www.imdb.com/chart/top', - } - - - def getIMDBids(self): - - movies = [] - - # Handle Chart URLs - if self.conf('automation_charts_theaters_use'): - log.debug('Started IMDB chart: %s', self.chart_urls['theater']) - data = self.getHTMLData(self.chart_urls['theater']) - if data: - html = BeautifulSoup(data) - - try: - result_div = html.find('div', attrs = {'id': 'main'}) - - entries = result_div.find_all('div', attrs = {'itemtype': 'http://schema.org/Movie'}) - - for entry in entries: - title = entry.find('h4', attrs = {'itemprop': 'name'}).getText() - log.debug('Identified title: %s', title) - result = re.search('(.*) \((.*)\)', title) +from couchpotato.core.providers.base import MultiProvider - if result: - name = result.group(1) - year = result.group(2) - imdb = self.search(name, year) +log = CPLog(__name__) - if imdb and self.isMinimalMovie(imdb): - movies.append(imdb['imdb']) - except: - log.error('Failed loading IMDB chart results from %s: %s', (self.chart_urls['theater'], traceback.format_exc())) +class IMDB(MultiProvider): - if self.conf('automation_charts_top250_use'): - log.debug('Started IMDB chart: %s', self.chart_urls['top250']) - data = self.getHTMLData(self.chart_urls['top250']) - if data: - html = BeautifulSoup(data) + def getTypes(self): + return [IMDBWatchlist, IMDBAutomation] - try: - result_div = html.find('div', attrs = {'id': 'main'}) - result_table = result_div.find_all('table')[1] - entries = result_table.find_all('tr') +class IMDBBase(Automation, RSS): - for entry in entries[1:]: - title = entry.find_all('td')[2].getText() + interval = 1800 - log.debug('Identified title: %s', title) - result = re.search('(.*) \((.*)\)', title) + def getInfo(self, imdb_id): + return fireEvent('movie.info', identifier = imdb_id, merge = True) - if result: - name = result.group(1) - year = result.group(2) - imdb = self.search(name, year) +class IMDBWatchlist(IMDBBase): - if imdb and self.isMinimalMovie(imdb): - movies.append(imdb['imdb']) + enabled_option = 'automation_enabled' - except: - log.error('Failed loading IMDB chart results from %s: %s', (self.chart_urls['theater'], traceback.format_exc())) + def getIMDBids(self): + movies = [] - # Handle Watchlists watchlist_enablers = [tryInt(x) for x in splitString(self.conf('automation_urls_use'))] watchlist_urls = splitString(self.conf('automation_urls')) @@ -103,9 +54,47 @@ class IMDB(Automation, RSS): for imdb in imdbs: movies.append(imdb) + if self.shuttingDown(): + break + except: log.error('Failed loading IMDB watchlist: %s %s', (url, traceback.format_exc())) + return movies + + +class IMDBAutomation(IMDBBase): + + enabled_option = 'automation_providers_enabled' + + chart_urls = { + 'theater': 'http://www.imdb.com/movies-in-theaters/', + 'top250': 'http://www.imdb.com/chart/top', + } + + def getIMDBids(self): + + movies = [] + + for url in self.chart_urls: + if self.conf('automation_charts_%s' % url): + data = self.getHTMLData(self.chart_urls[url]) + if data: + html = BeautifulSoup(data) + + try: + result_div = html.find('div', attrs = {'id': 'main'}) + imdb_ids = getImdb(str(result_div), multiple = True) + + for imdb_id in imdb_ids: + info = self.getInfo(imdb_id) + if info and self.isMinimalMovie(info): + movies.append(imdb_id) + + if self.shuttingDown(): + break + + except: + log.error('Failed loading IMDB chart results from %s: %s', (url, traceback.format_exc())) - # Return the combined resultset return movies From 7e44af936d7186473e5c5fb781df97f102fd5243 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sat, 24 Aug 2013 12:14:02 +0200 Subject: [PATCH 23/31] Watch shutdown when adding automation movies --- couchpotato/core/plugins/automation/main.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/couchpotato/core/plugins/automation/main.py b/couchpotato/core/plugins/automation/main.py index 80e1285..92547cb 100644 --- a/couchpotato/core/plugins/automation/main.py +++ b/couchpotato/core/plugins/automation/main.py @@ -26,6 +26,10 @@ class Automation(Plugin): movie_ids = [] for imdb_id in movies: + + if self.shuttingDown(): + break + prop_name = 'automation.added.%s' % imdb_id added = Env.prop(prop_name, default = False) if not added: @@ -35,5 +39,11 @@ class Automation(Plugin): Env.prop(prop_name, True) for movie_id in movie_ids: + + if self.shuttingDown(): + break + movie_dict = fireEvent('movie.get', movie_id, single = True) fireEvent('movie.searcher.single', movie_dict) + + return True \ No newline at end of file From cef5b04eb1633b1edde308286f9d8484e2edb1d8 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sat, 24 Aug 2013 12:14:15 +0200 Subject: [PATCH 24/31] Return unique imdb list --- couchpotato/core/helpers/variable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/couchpotato/core/helpers/variable.py b/couchpotato/core/helpers/variable.py index 381889c..90caf84 100644 --- a/couchpotato/core/helpers/variable.py +++ b/couchpotato/core/helpers/variable.py @@ -128,7 +128,7 @@ def getImdb(txt, check_inside = True, multiple = False): try: ids = re.findall('(tt\d{7})', txt) if multiple: - return ids if len(ids) > 0 else [] + return list(set(ids)) if len(ids) > 0 else [] return ids[0] except IndexError: pass From ed0e5ef497d3bef71bca0288ea8db8af48522da1 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sat, 24 Aug 2013 12:24:15 +0200 Subject: [PATCH 25/31] XMBC notification, better remote folder description --- couchpotato/core/notifications/xbmc/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/couchpotato/core/notifications/xbmc/__init__.py b/couchpotato/core/notifications/xbmc/__init__.py index f0167ce..dafa0f6 100644 --- a/couchpotato/core/notifications/xbmc/__init__.py +++ b/couchpotato/core/notifications/xbmc/__init__.py @@ -44,7 +44,7 @@ config = [{ 'default': 0, 'type': 'bool', 'advanced': True, - 'description': 'Scan new movie folder at remote XBMC servers, only works if movie location is the same.', + 'description': 'Only scan new movie folder at remote XBMC servers. Works if movie location is the same.', }, { 'name': 'on_snatch', From e2bd6a91cd467e7464ab3c6503bebf072abefc58 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sat, 24 Aug 2013 13:21:39 +0200 Subject: [PATCH 26/31] MPAA rating for renamer --- couchpotato/core/plugins/renamer/__init__.py | 1 + couchpotato/core/plugins/renamer/main.py | 1 + couchpotato/core/providers/movie/_modifier/main.py | 1 + couchpotato/core/providers/movie/omdbapi/main.py | 1 + couchpotato/core/providers/movie/themoviedb/main.py | 1 + 5 files changed, 5 insertions(+) mode change 100644 => 100755 couchpotato/core/plugins/renamer/__init__.py mode change 100644 => 100755 couchpotato/core/plugins/renamer/main.py mode change 100644 => 100755 couchpotato/core/providers/movie/omdbapi/main.py diff --git a/couchpotato/core/plugins/renamer/__init__.py b/couchpotato/core/plugins/renamer/__init__.py old mode 100644 new mode 100755 index 04cd970..50cda07 --- a/couchpotato/core/plugins/renamer/__init__.py +++ b/couchpotato/core/plugins/renamer/__init__.py @@ -27,6 +27,7 @@ rename_options = { 'imdb_id': 'IMDB id (tt0123456)', 'cd': 'CD number (cd1)', 'cd_nr': 'Just the cd nr. (1)', + 'mpaa': 'MPAA Rating', }, } diff --git a/couchpotato/core/plugins/renamer/main.py b/couchpotato/core/plugins/renamer/main.py old mode 100644 new mode 100755 index 8d1a818..4f43588 --- a/couchpotato/core/plugins/renamer/main.py +++ b/couchpotato/core/plugins/renamer/main.py @@ -205,6 +205,7 @@ class Renamer(Plugin): 'imdb_id': library['identifier'], 'cd': '', 'cd_nr': '', + 'mpaa': library['info'].get('mpaa', ''), } for file_type in group['files']: diff --git a/couchpotato/core/providers/movie/_modifier/main.py b/couchpotato/core/providers/movie/_modifier/main.py index e4d7022..835cce0 100644 --- a/couchpotato/core/providers/movie/_modifier/main.py +++ b/couchpotato/core/providers/movie/_modifier/main.py @@ -28,6 +28,7 @@ class MovieResultModifier(Plugin): 'tagline': '', 'imdb': '', 'genres': [], + 'mpaa': None } def __init__(self): diff --git a/couchpotato/core/providers/movie/omdbapi/main.py b/couchpotato/core/providers/movie/omdbapi/main.py old mode 100644 new mode 100755 index 8999074..c9f4d92 --- a/couchpotato/core/providers/movie/omdbapi/main.py +++ b/couchpotato/core/providers/movie/omdbapi/main.py @@ -95,6 +95,7 @@ class OMDBAPI(MovieProvider): #'rotten': (tryFloat(movie.get('tomatoRating', 0)), tryInt(movie.get('tomatoReviews', '').replace(',', ''))), }, 'imdb': str(movie.get('imdbID', '')), + 'mpaa': str(movie.get('Rated', '')), 'runtime': self.runtimeToMinutes(movie.get('Runtime', '')), 'released': movie.get('Released'), 'year': year if isinstance(year, (int)) else None, diff --git a/couchpotato/core/providers/movie/themoviedb/main.py b/couchpotato/core/providers/movie/themoviedb/main.py index 735419c..241fc6b 100644 --- a/couchpotato/core/providers/movie/themoviedb/main.py +++ b/couchpotato/core/providers/movie/themoviedb/main.py @@ -167,6 +167,7 @@ class TheMovieDb(MovieProvider): 'backdrop_original': [backdrop_original] if backdrop_original else [], }, 'imdb': movie.get('imdb_id'), + 'mpaa': movie.get('certification', ''), 'runtime': movie.get('runtime'), 'released': movie.get('released'), 'year': year, From 08554889fd63f54e46f9cca4ef34e765c15b2bed Mon Sep 17 00:00:00 2001 From: Ruud Date: Sat, 24 Aug 2013 13:34:45 +0200 Subject: [PATCH 27/31] Add the old rottentomatoes to default enabled list --- couchpotato/core/providers/automation/rottentomatoes/__init__.py | 4 +++- couchpotato/core/providers/automation/rottentomatoes/main.py | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/couchpotato/core/providers/automation/rottentomatoes/__init__.py b/couchpotato/core/providers/automation/rottentomatoes/__init__.py index 52b1c88..4675fac 100644 --- a/couchpotato/core/providers/automation/rottentomatoes/__init__.py +++ b/couchpotato/core/providers/automation/rottentomatoes/__init__.py @@ -21,19 +21,21 @@ config = [{ { 'name': 'automation_urls_use', 'label': 'Use', + 'default': '1', }, { 'name': 'automation_urls', 'label': 'url', 'type': 'combined', 'combine': ['automation_urls_use', 'automation_urls'], + 'default': 'http://www.rottentomatoes.com/syndication/rss/in_theaters.xml', }, { 'name': 'tomatometer_percent', 'default': '80', 'label': 'Tomatometer', 'description': 'Use as extra scoring requirement', - } + }, ], }, ], diff --git a/couchpotato/core/providers/automation/rottentomatoes/main.py b/couchpotato/core/providers/automation/rottentomatoes/main.py index 40f72a5..6961170 100644 --- a/couchpotato/core/providers/automation/rottentomatoes/main.py +++ b/couchpotato/core/providers/automation/rottentomatoes/main.py @@ -12,8 +12,6 @@ class Rottentomatoes(Automation, RSS): interval = 1800 - - def getIMDBids(self): movies = [] From 8e9e7b49eabfa40f8479d081e3e78a52f84587e0 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sat, 24 Aug 2013 14:03:17 +0200 Subject: [PATCH 28/31] Simplify linking Thanks @mano3m --- couchpotato/core/plugins/renamer/__init__.py | 6 +++--- couchpotato/core/plugins/renamer/main.py | 30 +++++++++++++++------------- 2 files changed, 19 insertions(+), 17 deletions(-) mode change 100755 => 100644 couchpotato/core/plugins/renamer/main.py diff --git a/couchpotato/core/plugins/renamer/__init__.py b/couchpotato/core/plugins/renamer/__init__.py index 50cda07..56672b8 100755 --- a/couchpotato/core/plugins/renamer/__init__.py +++ b/couchpotato/core/plugins/renamer/__init__.py @@ -120,10 +120,10 @@ config = [{ { 'name': 'file_action', 'label': 'Torrent File Action', - 'default': 'move', + 'default': 'link', 'type': 'dropdown', - 'values': [('Move', 'move'), ('Copy', 'copy'), ('Hard link', 'hardlink'), ('Move & Sym link', 'move_symlink')], - 'description': 'Define which kind of file operation you want to use for torrents. Before you start using hard links or sym links, PLEASE read about their possible drawbacks.', + 'values': [('Link', 'link'), ('Copy', 'copy'), ('Move', 'move')], + 'description': 'Link or Copy after downloading completed (and allow for seeding), or Move after seeding completed. Link first tries hard link, then sym link and falls back to Copy.', 'advanced': True, }, { diff --git a/couchpotato/core/plugins/renamer/main.py b/couchpotato/core/plugins/renamer/main.py old mode 100755 new mode 100644 index 508eab2..2b73590 --- a/couchpotato/core/plugins/renamer/main.py +++ b/couchpotato/core/plugins/renamer/main.py @@ -548,21 +548,23 @@ Remove it if you want it to be renamed (again, or at least let it try again) try: if forcemove: shutil.move(old, dest) - elif self.conf('file_action') == 'hardlink': - try: - link(old, dest) - except: - log.error('Couldn\'t hardlink file "%s" to "%s". Copying instead. Error: %s. ', (old, dest, traceback.format_exc())) - shutil.copy(old, dest) elif self.conf('file_action') == 'copy': shutil.copy(old, dest) - elif self.conf('file_action') == 'move_symlink': - shutil.move(old, dest) + elif self.conf('file_action') == 'link': + # First try to hardlink try: - symlink(dest, old) + log.debug('Hardlinking file "%s" to "%s"...', (old, dest)) + link(old, dest) except: - log.error('Couldn\'t symlink file "%s" to "%s". Copying the file back. Error: %s. ', (old, dest, traceback.format_exc())) - shutil.copy(dest, old) + # Try to simlink next + log.debug('Couldn\'t hardlink file "%s" to "%s". Simlinking instead. Error: %s. ', (old, dest, traceback.format_exc())) + shutil.copy(old, dest) + try: + symlink(dest, old + '.link') + os.unlink(old) + os.rename(old + '.link', old) + except: + log.error('Couldn\'t symlink file "%s" to "%s". Copied instead. Error: %s. ', (old, dest, traceback.format_exc())) else: shutil.move(old, dest) @@ -767,10 +769,10 @@ Remove it if you want it to be renamed (again, or at least let it try again) for item in scan_items: # Ask the renamer to scan the item if item['scan']: - if item['pause'] and self.conf('file_action') == 'move_symlink': + if item['pause'] and self.conf('file_action') == 'link': fireEvent('download.pause', item = item, pause = True, single = True) fireEvent('renamer.scan', download_info = item) - if item['pause'] and self.conf('file_action') == 'move_symlink': + if item['pause'] and self.conf('file_action') == 'link': fireEvent('download.pause', item = item, pause = False, single = True) if item['process_complete']: #First make sure the files were succesfully processed @@ -829,6 +831,6 @@ Remove it if you want it to be renamed (again, or at least let it try again) def statusInfoComplete(self, item): return item['id'] and item['downloader'] and item['folder'] - + def movieInFromFolder(self, movie_folder): return movie_folder and self.conf('from') in movie_folder or not movie_folder From 770590e4f2361feaac7fb03106c9f7af17060438 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sat, 24 Aug 2013 14:08:05 +0200 Subject: [PATCH 29/31] Match default ports Thanks @cpg --- couchpotato/runner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/couchpotato/runner.py b/couchpotato/runner.py index 0c0127f..f49ba3e 100644 --- a/couchpotato/runner.py +++ b/couchpotato/runner.py @@ -212,7 +212,7 @@ def runCouchPotato(options, base_path, args, data_dir = None, log_dir = None, En # app.debug = development config = { 'use_reloader': reloader, - 'port': tryInt(Env.setting('port', default = 5000)), + 'port': tryInt(Env.setting('port', default = 5050)), 'host': host if host and len(host) > 0 else '0.0.0.0', 'ssl_cert': Env.setting('ssl_cert', default = None), 'ssl_key': Env.setting('ssl_key', default = None), From 20aa78105f56a74ba3ad83e2282cd1f27d6e3eae Mon Sep 17 00:00:00 2001 From: Ruud Date: Sat, 24 Aug 2013 14:22:15 +0200 Subject: [PATCH 30/31] Do window size check inside load event --- couchpotato/templates/index.html | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/couchpotato/templates/index.html b/couchpotato/templates/index.html index f9bc463..d45dcb9 100644 --- a/couchpotato/templates/index.html +++ b/couchpotato/templates/index.html @@ -22,17 +22,18 @@