From ae666bd9b69798b7f08b0bbbfe3cdd05dbf788e8 Mon Sep 17 00:00:00 2001 From: mano3m Date: Sat, 5 Oct 2013 16:20:02 +0200 Subject: [PATCH 1/8] Add API call to scan a folder for multiple movies --- couchpotato/core/plugins/renamer/main.py | 26 +++++++++++++++--------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/couchpotato/core/plugins/renamer/main.py b/couchpotato/core/plugins/renamer/main.py index 4aa0f20b..0df3ad02 100755 --- a/couchpotato/core/plugins/renamer/main.py +++ b/couchpotato/core/plugins/renamer/main.py @@ -30,7 +30,8 @@ class Renamer(Plugin): 'desc': 'For the renamer to check for new files to rename in a folder', 'params': { 'async': {'desc': 'Optional: Set to 1 if you dont want to fire the renamer.scan asynchronous.'}, - 'movie_folder': {'desc': 'Optional: The folder of the movie to scan. Keep empty for default renamer folder.'}, + 'base_folder': {'desc': 'Optional: The folder to find releases in. Leave empty for default folder.'}, + 'movie_folder': {'desc': 'Optional: The folder of a specific release to scan. Don\'t use in combination with base_folder.'}, 'downloader' : {'desc': 'Optional: The downloader the release has been downloaded with. \'download_id\' is required with this option.'}, 'download_id': {'desc': 'Optional: The nzb/torrent ID of the release in movie_folder. \'downloader\' is required with this option.'}, 'status': {'desc': 'Optional: The status of the release: \'completed\' (default) or \'seeding\''}, @@ -63,24 +64,26 @@ class Renamer(Plugin): def scanView(self, **kwargs): async = tryInt(kwargs.get('async', 0)) + base_folder = kwargs.get('base_folder') movie_folder = kwargs.get('movie_folder') downloader = kwargs.get('downloader') download_id = kwargs.get('download_id') status = kwargs.get('status', 'completed') - release_download = {'folder': movie_folder} if movie_folder else None - if release_download: + release_download = None + if not base_folder and movie_folder: + release_download = {'folder': movie_folder} release_download.update({'id': download_id, 'downloader': downloader, 'status': status} if download_id else {}) fire_handle = fireEvent if not async else fireEventAsync - fire_handle('renamer.scan', release_download) + fire_handle('renamer.scan', base_folder = base_folder, release_download = release_download) return { 'success': True } - def scan(self, release_download = None): + def scan(self, base_folder = None, release_download = None): if self.isDisabled(): return @@ -89,6 +92,9 @@ class Renamer(Plugin): log.info('Renamer is already running, if you see this often, check the logs above for errors.') return + if not base_folder: + base_folder = self.conf('from') + # Get movie folder to process movie_folder = release_download and release_download.get('folder') @@ -103,12 +109,12 @@ class Renamer(Plugin): pass # Check to see if the no_process folders are inside the "from" folder. - if not os.path.isdir(self.conf('from')) or not os.path.isdir(self.conf('to')): + if not os.path.isdir(base_folder) or not os.path.isdir(self.conf('to')): log.error('Both the "To" and "From" have to exist.') return else: for item in no_process: - if self.conf('from') in item: + if base_folder in item: log.error('To protect your data, the movie libraries can\'t be inside of or the same as the "from" folder.') return @@ -161,7 +167,7 @@ class Renamer(Plugin): folder, movie_folder, files, extr_files = self.extractFiles(folder = folder, movie_folder = movie_folder, files = files, cleanup = self.conf('cleanup') and not self.downloadIsTorrent(release_download)) - groups = fireEvent('scanner.scan', folder = folder if folder else self.conf('from'), + groups = fireEvent('scanner.scan', folder = folder if folder else base_folder, files = files, release_download = release_download, return_ignored = False, single = True) or [] folder_name = self.conf('folder_name') @@ -470,7 +476,7 @@ class Renamer(Plugin): os.remove(src) parent_dir = os.path.normpath(os.path.dirname(src)) - if delete_folders.count(parent_dir) == 0 and os.path.isdir(parent_dir) and not parent_dir in [destination, movie_folder] and not self.conf('from') in parent_dir: + if delete_folders.count(parent_dir) == 0 and os.path.isdir(parent_dir) and not parent_dir in [destination, movie_folder] and not base_folder in parent_dir: delete_folders.append(parent_dir) except: @@ -519,7 +525,7 @@ class Renamer(Plugin): group_folder = movie_folder else: # Delete the first empty subfolder in the tree relative to the 'from' folder - group_folder = os.path.join(self.conf('from'), os.path.relpath(group['parentdir'], self.conf('from')).split(os.path.sep)[0]) + group_folder = os.path.join(base_folder, os.path.relpath(group['parentdir'], base_folder).split(os.path.sep)[0]) try: log.info('Deleting folder: %s', group_folder) From d984f11cbffa2527ab8f4fda603243331b7e45f5 Mon Sep 17 00:00:00 2001 From: mano3m Date: Sun, 6 Oct 2013 23:23:29 +0200 Subject: [PATCH 2/8] First attempt at creating a working directory selector --- couchpotato/static/scripts/page/wanted.js | 64 +++++++++++++++++++++-- couchpotato/static/style/settings.css | 4 +- 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/couchpotato/static/scripts/page/wanted.js b/couchpotato/static/scripts/page/wanted.js index 98a676c8..415f4b02 100644 --- a/couchpotato/static/scripts/page/wanted.js +++ b/couchpotato/static/scripts/page/wanted.js @@ -4,6 +4,7 @@ Page.Wanted = new Class({ name: 'wanted', title: 'Gimmy gimmy gimmy!', + folder_browser: null, indexAction: function(){ var self = this; @@ -18,13 +19,22 @@ Page.Wanted = new Class({ } }); + + self.scan_folder = new Element('a', { + 'title': 'Scan a folder and rename all movies in it', + 'text': 'Scan Folder', + 'events':{ + 'click': self.scanFolder.bind(self, event) + } + }); + // Wanted movies self.wanted = new MovieList({ 'identifier': 'wanted', 'status': 'active', 'actions': [MA.IMDB, MA.Trailer, MA.Release, MA.Edit, MA.Refresh, MA.Readd, MA.Delete], 'add_new': true, - 'menu': [self.manual_search], + 'menu': [self.manual_search, self.scan_folder], 'on_empty_element': App.createUserscriptButtons().addClass('empty_wanted') }); $(self.wanted).inject(self.el); @@ -69,6 +79,54 @@ Page.Wanted = new Class({ }); }, 1000); - } + }, -}); \ No newline at end of file + scanFolder: function(e) { + if (!e) + e = window.event; + + //IE9 & Other Browsers + if (e.stopPropagation) { + e.stopPropagation(); + } + //IE8 and Lower + else { + e.cancelBubble = true; + } + + var self = this; + var options = {}; + options.name = "Scan_folder"; + + if(!self.folder_browser){ + self.folder_browser = new Option['Directory']("Scan", "folder", "", options); + + self.folder_browser.save = function() { + var folder = self.folder_browser.getValue(); + Api.request('renamer.scan', { + 'data': { + 'base_folder': folder, + }, + }); + }; + + self.folder_browser.inject(self.el, 'top'); + self.folder_browser.fireEvent('injected'); + + // Hide the settings box + self.folder_browser.directory_inlay.hide(); + self.folder_browser.el.removeChild(self.folder_browser.el.firstChild); + + self.folder_browser.showBrowser(); + + // Make adjustments to the browser + self.folder_browser.browser.getElementsByClassName("clear button")[0].hide() + self.folder_browser.save_button.text = "Select"; + self.folder_browser.browser.style.zIndex=1000; + } + else{ + self.folder_browser.showBrowser(); + } + } + +}); diff --git a/couchpotato/static/style/settings.css b/couchpotato/static/style/settings.css index 744531a9..861ca713 100644 --- a/couchpotato/static/style/settings.css +++ b/couchpotato/static/style/settings.css @@ -113,6 +113,8 @@ width: 20px; } + .Scan_folder { padding: 0 !important; } + .page .ctrlHolder { line-height: 25px; padding: 10px 10px 10px 30px; @@ -159,7 +161,7 @@ } .page .xsmall { width: 20px !important; text-align: center; } - + .page .enabler { display: block; } From 3b3288c53d9f1c00d701a6cbf847b3043bf02f78 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sat, 16 Nov 2013 14:29:34 +0100 Subject: [PATCH 3/8] Manual scan folder cleanup --- couchpotato/core/plugins/renamer/main.py | 35 +++++++++++++---------- couchpotato/static/scripts/page/wanted.js | 27 ++++++----------- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/couchpotato/core/plugins/renamer/main.py b/couchpotato/core/plugins/renamer/main.py index bbe79a39..ba2cf245 100755 --- a/couchpotato/core/plugins/renamer/main.py +++ b/couchpotato/core/plugins/renamer/main.py @@ -30,7 +30,8 @@ class Renamer(Plugin): 'desc': 'For the renamer to check for new files to rename in a folder', 'params': { 'async': {'desc': 'Optional: Set to 1 if you dont want to fire the renamer.scan asynchronous.'}, - 'movie_folder': {'desc': 'Optional: The folder of the movie to scan. Keep empty for default renamer folder.'}, + 'base_folder': {'desc': 'Optional: The folder to find releases in. Leave empty for default folder.'}, + 'movie_folder': {'desc': 'Optional: The folder of a specific release to scan. Don\'t use in combination with base_folder.'}, 'downloader' : {'desc': 'Optional: The downloader the release has been downloaded with. \'download_id\' is required with this option.'}, 'download_id': {'desc': 'Optional: The nzb/torrent ID of the release in movie_folder. \'downloader\' is required with this option.'}, 'status': {'desc': 'Optional: The status of the release: \'completed\' (default) or \'seeding\''}, @@ -64,23 +65,25 @@ class Renamer(Plugin): async = tryInt(kwargs.get('async', 0)) movie_folder = sp(kwargs.get('movie_folder')) + base_folder = sp(kwargs.get('base_folder')) downloader = kwargs.get('downloader') download_id = kwargs.get('download_id') status = kwargs.get('status', 'completed') - release_download = {'folder': movie_folder} if movie_folder else None - if release_download: + release_download = None + if not base_folder and movie_folder: + release_download = {'folder': movie_folder} release_download.update({'id': download_id, 'downloader': downloader, 'status': status} if download_id else {}) fire_handle = fireEvent if not async else fireEventAsync - fire_handle('renamer.scan', release_download) + fire_handle('renamer.scan', base_folder = base_folder, release_download = release_download) return { 'success': True } - def scan(self, release_download = None): + def scan(self, base_folder = None, release_download = None): if self.isDisabled(): return @@ -89,11 +92,11 @@ class Renamer(Plugin): log.info('Renamer is already running, if you see this often, check the logs above for errors.') return - from_folder = sp(self.conf('from')) + from_folder = sp(self.conf('from') if not base_folder else base_folder) to_folder = sp(self.conf('to')) # Get movie folder to process - movie_folder = release_download and release_download.get('folder') + movie_folder = sp(release_download and release_download.get('folder')) # Get all folders that should not be processed no_process = [to_folder] @@ -493,7 +496,7 @@ class Renamer(Plugin): if os.path.isfile(src): os.remove(src) - parent_dir = os.path.dirname(src) + parent_dir = sp(os.path.dirname(src)) if delete_folders.count(parent_dir) == 0 and os.path.isdir(parent_dir) and not parent_dir in [destination, movie_folder] and not from_folder in parent_dir: delete_folders.append(parent_dir) @@ -993,7 +996,7 @@ Remove it if you want it to be renamed (again, or at least let it try again) return release_download['id'] and release_download['downloader'] and release_download['folder'] def movieInFromFolder(self, movie_folder): - return movie_folder and sp(self.conf('from')) in movie_folder or not movie_folder + return (movie_folder and sp(self.conf('from')) in movie_folder) or not movie_folder def extractFiles(self, folder = None, movie_folder = None, files = None, cleanup = False): if not files: files = [] @@ -1003,9 +1006,11 @@ Remove it if you want it to be renamed (again, or at least let it try again) restfile_regex = '(^%s\.(?:part(?!0*1\.rar$)\d+\.rar$|[rstuvw]\d+$))' extr_files = [] + from_folder = sp(self.conf('from')) + # Check input variables if not folder: - folder = sp(self.conf('from')) + folder = from_folder check_file_date = True if movie_folder: @@ -1059,7 +1064,7 @@ Remove it if you want it to be renamed (again, or at least let it try again) log.info('Archive %s found. Extracting...', os.path.basename(archive['file'])) try: rar_handle = RarFile(archive['file']) - extr_path = os.path.join(sp(self.conf('from')), os.path.relpath(os.path.dirname(archive['file']), folder)) + extr_path = sp(os.path.join(from_folder, os.path.relpath(os.path.dirname(archive['file']), folder))) self.makeDir(extr_path) for packedinfo in rar_handle.infolist(): if not packedinfo.isdir and not os.path.isfile(os.path.join(extr_path, os.path.basename(packedinfo.filename))): @@ -1082,9 +1087,9 @@ Remove it if you want it to be renamed (again, or at least let it try again) files.remove(filename) # Move the rest of the files and folders if any files are extracted to the from folder (only if folder was provided) - if extr_files and folder != sp(self.conf('from')): + if extr_files and sp(folder) != from_folder: for leftoverfile in list(files): - move_to = os.path.join(sp(self.conf('from')), os.path.relpath(leftoverfile, folder)) + move_to = sp(os.path.join(from_folder, os.path.relpath(leftoverfile, folder))) try: self.makeDir(os.path.dirname(move_to)) @@ -1107,8 +1112,8 @@ Remove it if you want it to be renamed (again, or at least let it try again) log.debug('Removing old movie folder %s...', movie_folder) self.deleteEmptyFolder(movie_folder) - movie_folder = os.path.join(sp(self.conf('from')), os.path.relpath(movie_folder, folder)) - folder = sp(self.conf('from')) + movie_folder = sp(os.path.join(from_folder, os.path.relpath(movie_folder, folder))) + folder = from_folder if extr_files: files.extend(extr_files) diff --git a/couchpotato/static/scripts/page/wanted.js b/couchpotato/static/scripts/page/wanted.js index 415f4b02..6a3d4cbc 100644 --- a/couchpotato/static/scripts/page/wanted.js +++ b/couchpotato/static/scripts/page/wanted.js @@ -22,12 +22,12 @@ Page.Wanted = new Class({ self.scan_folder = new Element('a', { 'title': 'Scan a folder and rename all movies in it', - 'text': 'Scan Folder', + 'text': 'Manual folder scan', 'events':{ - 'click': self.scanFolder.bind(self, event) + 'click': self.scanFolder.bind(self) } }); - + // Wanted movies self.wanted = new MovieList({ 'identifier': 'wanted', @@ -82,21 +82,12 @@ Page.Wanted = new Class({ }, scanFolder: function(e) { - if (!e) - e = window.event; - - //IE9 & Other Browsers - if (e.stopPropagation) { - e.stopPropagation(); - } - //IE8 and Lower - else { - e.cancelBubble = true; - } + (e).stop(); var self = this; - var options = {}; - options.name = "Scan_folder"; + var options = { + 'name': 'Scan_folder' + } if(!self.folder_browser){ self.folder_browser = new Option['Directory']("Scan", "folder", "", options); @@ -108,7 +99,7 @@ Page.Wanted = new Class({ 'base_folder': folder, }, }); - }; + }; self.folder_browser.inject(self.el, 'top'); self.folder_browser.fireEvent('injected'); @@ -120,7 +111,7 @@ Page.Wanted = new Class({ self.folder_browser.showBrowser(); // Make adjustments to the browser - self.folder_browser.browser.getElementsByClassName("clear button")[0].hide() + self.folder_browser.browser.getElements('.clear.button').hide(); self.folder_browser.save_button.text = "Select"; self.folder_browser.browser.style.zIndex=1000; } From e9a457e2635c520e6b4377d6198c9cfea0bbce49 Mon Sep 17 00:00:00 2001 From: mano3m Date: Sun, 15 Dec 2013 21:03:40 +0100 Subject: [PATCH 4/8] mark release das downloaded if renamer is disabled. if the renamer is not enabled and the quality of the downloaded release is not the finish quality, the release did not get a status update. --- couchpotato/core/plugins/release/main.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/couchpotato/core/plugins/release/main.py b/couchpotato/core/plugins/release/main.py index 11779803..2bc75e8a 100644 --- a/couchpotato/core/plugins/release/main.py +++ b/couchpotato/core/plugins/release/main.py @@ -260,7 +260,7 @@ class Release(Plugin): downloader_enabled = fireEvent('download.enabled', manual, data, single = True) if downloader_enabled: - snatched_status, done_status, active_status = fireEvent('status.get', ['snatched', 'done', 'active'], single = True) + snatched_status, done_status, downloaded_status, active_status = fireEvent('status.get', ['snatched', 'done', 'downloaded', 'active'], single = True) # Download release to temp filedata = None @@ -297,6 +297,9 @@ class Release(Plugin): # If renamer isn't used, mark media done if not renamer_enabled: + # Mark release downloaded + self.updateStatus(rls.id, status = downloaded_status) + try: if media['status_id'] == active_status.get('id'): for profile_type in media['profile']['types']: From 99ce8dacbf07f930952fe9d24b5e66f3c9b690ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joel=20K=C3=A5berg?= Date: Mon, 16 Dec 2013 17:07:34 +0100 Subject: [PATCH 5/8] added api calls for manual scan (kudos to @mano3m) --- couchpotato/core/plugins/renamer/main.py | 26 +++++++++++++++--------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/couchpotato/core/plugins/renamer/main.py b/couchpotato/core/plugins/renamer/main.py index b4f668ff..2c6df8ae 100755 --- a/couchpotato/core/plugins/renamer/main.py +++ b/couchpotato/core/plugins/renamer/main.py @@ -32,6 +32,7 @@ class Renamer(Plugin): 'async': {'desc': 'Optional: Set to 1 if you dont want to fire the renamer.scan asynchronous.'}, 'media_folder': {'desc': 'Optional: The folder of the media to scan. Keep empty for default renamer folder.'}, 'files': {'desc': 'Optional: Provide the release files if more releases are in the same media_folder, delimited with a \'|\'. Note that no dedicated release folder is expected for releases with one file.'}, + 'base_folder': {'desc': 'Optional: The folder to find releases in. Leave empty for default folder.'}, 'downloader' : {'desc': 'Optional: The downloader the release has been downloaded with. \'download_id\' is required with this option.'}, 'download_id': {'desc': 'Optional: The nzb/torrent ID of the release in media_folder. \'downloader\' is required with this option.'}, 'status': {'desc': 'Optional: The status of the release: \'completed\' (default) or \'seeding\''}, @@ -64,6 +65,7 @@ class Renamer(Plugin): def scanView(self, **kwargs): async = tryInt(kwargs.get('async', 0)) + base_folder = kwargs.get('base_folder') media_folder = sp(kwargs.get('media_folder')) # Backwards compatibility, to be removed after a few versions :) @@ -75,19 +77,20 @@ class Renamer(Plugin): files = '|'.join([sp(filename) for filename in splitString(kwargs.get('files'), '|')]) status = kwargs.get('status', 'completed') - release_download = {'folder': media_folder} if media_folder else None - if release_download: + 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 - fire_handle('renamer.scan', release_download) + fire_handle('renamer.scan', base_folder = base_folder, release_download = release_download) return { 'success': True } - def scan(self, release_download = None): + def scan(self, base_folder = None, release_download = None): if not release_download: release_download = {} if self.isDisabled(): @@ -97,6 +100,9 @@ class Renamer(Plugin): log.info('Renamer is already running, if you see this often, check the logs above for errors.') return + if not base_folder: + base_folder = self.conf('from') + from_folder = sp(self.conf('from')) to_folder = sp(self.conf('to')) @@ -114,12 +120,12 @@ class Renamer(Plugin): pass # Check to see if the no_process folders are inside the "from" folder. - if not os.path.isdir(from_folder) or not os.path.isdir(to_folder): - log.error('Both the "To" and "From" have to exist.') + if not os.path.isdir(base_folder) or not os.path.isdir(to_folder): + log.error('Both the "To" and "From" folder have to exist.') return else: for item in no_process: - if '%s%s' % (from_folder, os.path.sep) in item: + if '%s%s' % (base_folder, os.path.sep) in item: log.error('To protect your data, the media libraries can\'t be inside of or the same as the "from" folder.') return @@ -193,7 +199,7 @@ class Renamer(Plugin): folder, media_folder, files, extr_files = self.extractFiles(folder = folder, media_folder = media_folder, files = files, cleanup = self.conf('cleanup') and not self.downloadIsTorrent(release_download)) - groups = fireEvent('scanner.scan', folder = folder if folder else from_folder, + groups = fireEvent('scanner.scan', folder = folder if folder else base_folder, files = files, release_download = release_download, return_ignored = False, single = True) or [] folder_name = self.conf('folder_name') @@ -502,7 +508,7 @@ class Renamer(Plugin): os.remove(src) parent_dir = os.path.dirname(src) - if delete_folders.count(parent_dir) == 0 and os.path.isdir(parent_dir) and not '%s%s' % (parent_dir, os.path.sep) in [destination, media_folder] and not '%s%s' % (from_folder, os.path.sep) in parent_dir: + if delete_folders.count(parent_dir) == 0 and os.path.isdir(parent_dir) and not '%s%s' % (parent_dir, os.path.sep) in [destination, media_folder] and not '%s%s' % (base_folder, os.path.sep) in parent_dir: delete_folders.append(parent_dir) except: @@ -551,7 +557,7 @@ class Renamer(Plugin): group_folder = media_folder else: # Delete the first empty subfolder in the tree relative to the 'from' folder - group_folder = sp(os.path.join(from_folder, os.path.relpath(group['parentdir'], from_folder).split(os.path.sep)[0])) + group_folder = sp(os.path.join(base_folder, os.path.relpath(group['parentdir'], base_folder).split(os.path.sep)[0])) try: log.info('Deleting folder: %s', group_folder) From 55777531d5726c88744d8152a2b82ec9bdd4887a Mon Sep 17 00:00:00 2001 From: mano3m Date: Mon, 16 Dec 2013 22:43:05 +0100 Subject: [PATCH 6/8] Clean-up and dont mark status twice --- couchpotato/core/plugins/release/main.py | 183 ++++++++++++----------- 1 file changed, 95 insertions(+), 88 deletions(-) diff --git a/couchpotato/core/plugins/release/main.py b/couchpotato/core/plugins/release/main.py index 2bc75e8a..061a856c 100644 --- a/couchpotato/core/plugins/release/main.py +++ b/couchpotato/core/plugins/release/main.py @@ -211,120 +211,127 @@ class Release(Plugin): db = get_session() rel = db.query(Relea).filter_by(id = id).first() - if rel: - item = {} - for info in rel.info: - item[info.identifier] = info.value - - fireEvent('notify.frontend', type = 'release.manual_download', data = True, message = 'Snatching "%s"' % item['name']) - - # Get matching provider - provider = fireEvent('provider.belongs_to', item['url'], provider = item.get('provider'), single = True) - - if not item.get('protocol'): - item['protocol'] = item['type'] - item['type'] = 'movie' - - if item.get('protocol') != 'torrent_magnet': - item['download'] = provider.loginDownload if provider.urls.get('login') else provider.download - - success = self.download(data = item, media = rel.movie.to_dict({ - 'profile': {'types': {'quality': {}}}, - 'releases': {'status': {}, 'quality': {}}, - 'library': {'titles': {}, 'files':{}}, - 'files': {} - }), manual = True) - - if success: - db.expunge_all() - rel = db.query(Relea).filter_by(id = id).first() # Get release again @RuudBurger why do we need to get it again?? - - fireEvent('notify.frontend', type = 'release.manual_download', data = True, message = 'Successfully snatched "%s"' % item['name']) - return { - 'success': success - } - else: + if not rel: log.error('Couldn\'t find release with id: %s', id) + return { + 'success': False + } + item = {} + for info in rel.info: + item[info.identifier] = info.value + + fireEvent('notify.frontend', type = 'release.manual_download', data = True, message = 'Snatching "%s"' % item['name']) + + # Get matching provider + provider = fireEvent('provider.belongs_to', item['url'], provider = item.get('provider'), single = True) + + # Backwards compatibility code + if not item.get('protocol'): + item['protocol'] = item['type'] + item['type'] = 'movie' + + if item.get('protocol') != 'torrent_magnet': + item['download'] = provider.loginDownload if provider.urls.get('login') else provider.download + + success = self.download(data = item, media = rel.movie.to_dict({ + 'profile': {'types': {'quality': {}}}, + 'releases': {'status': {}, 'quality': {}}, + 'library': {'titles': {}, 'files':{}}, + 'files': {} + }), manual = True) + + if success: + db.expunge_all() + rel = db.query(Relea).filter_by(id = id).first() # Get release again @RuudBurger why do we need to get it again?? + + fireEvent('notify.frontend', type = 'release.manual_download', data = True, message = 'Successfully snatched "%s"' % item['name']) return { - 'success': False + 'success': success } def download(self, data, media, manual = False): + # Backwards compatibility code if not data.get('protocol'): data['protocol'] = data['type'] data['type'] = 'movie' # Test to see if any downloaders are enabled for this type downloader_enabled = fireEvent('download.enabled', manual, data, single = True) + if not downloader_enabled: + log.info('Tried to download, but none of the "%s" downloaders are enabled or gave an error', (data.get('protocol'))) + return False - if downloader_enabled: - snatched_status, done_status, downloaded_status, active_status = fireEvent('status.get', ['snatched', 'done', 'downloaded', 'active'], single = True) + # Download NZB or torrent file + filedata = None + if data.get('download') and (ismethod(data.get('download')) or isfunction(data.get('download'))): + filedata = data.get('download')(url = data.get('url'), nzb_id = data.get('id')) + if filedata == 'try_next': + return filedata - # Download release to temp - filedata = None - if data.get('download') and (ismethod(data.get('download')) or isfunction(data.get('download'))): - filedata = data.get('download')(url = data.get('url'), nzb_id = data.get('id')) - if filedata == 'try_next': - return filedata + # Send NZB or torrent file to downloader + download_result = fireEvent('download', data = data, media = media, manual = manual, filedata = filedata, single = True) + if not download_result: + log.info('Tried to download, but the "%s" downloader gave an error', (data.get('protocol'))) + return False + log.debug('Downloader result: %s', download_result) - download_result = fireEvent('download', data = data, media = media, manual = manual, filedata = filedata, single = True) - log.debug('Downloader result: %s', download_result) + snatched_status, done_status, downloaded_status, active_status = fireEvent('status.get', ['snatched', 'done', 'downloaded', 'active'], single = True) - if download_result: - try: - # Mark release as snatched - db = get_session() - rls = db.query(Relea).filter_by(identifier = md5(data['url'])).first() - if rls: - renamer_enabled = Env.setting('enabled', 'renamer') + try: + db = get_session() + rls = db.query(Relea).filter_by(identifier = md5(data['url'])).first() + if not rls: + log.error('No release found to store download information in') + return False - # Save download-id info if returned - if isinstance(download_result, dict): - for key in download_result: - rls_info = ReleaseInfo( - identifier = 'download_%s' % key, - value = toUnicode(download_result.get(key)) - ) - rls.info.append(rls_info) - db.commit() + renamer_enabled = Env.setting('enabled', 'renamer') - log_movie = '%s (%s) in %s' % (getTitle(media['library']), media['library']['year'], rls.quality.label) - snatch_message = 'Snatched "%s": %s' % (data.get('name'), log_movie) - log.info(snatch_message) - fireEvent('%s.snatched' % data['type'], message = snatch_message, data = rls.to_dict()) + # Save download-id info if returned + if isinstance(download_result, dict): + for key in download_result: + rls_info = ReleaseInfo( + identifier = 'download_%s' % key, + value = toUnicode(download_result.get(key)) + ) + rls.info.append(rls_info) + db.commit() - # If renamer isn't used, mark media done - if not renamer_enabled: - # Mark release downloaded - self.updateStatus(rls.id, status = downloaded_status) + log_movie = '%s (%s) in %s' % (getTitle(media['library']), media['library']['year'], rls.quality.label) + snatch_message = 'Snatched "%s": %s' % (data.get('name'), log_movie) + log.info(snatch_message) + fireEvent('%s.snatched' % data['type'], message = snatch_message, data = rls.to_dict()) - try: - if media['status_id'] == active_status.get('id'): - for profile_type in media['profile']['types']: - if profile_type['quality_id'] == rls.quality.id and profile_type['finish']: - log.info('Renamer disabled, marking media as finished: %s', log_movie) + # Mark release as snatched + if renamer_enabled: + self.updateStatus(rls.id, status = snatched_status) - # Mark release done - self.updateStatus(rls.id, status = done_status) + # If renamer isn't used, mark media done if finished or release downloaded + else: + if media['status_id'] == active_status.get('id'): + finished = next((True for profile_type in media['profile']['types'] if \ + profile_type['quality_id'] == rls.quality.id and profile_type['finish']), False) + if finished: + log.info('Renamer disabled, marking media as finished: %s', log_movie) - # Mark media done - mdia = db.query(Media).filter_by(id = media['id']).first() - mdia.status_id = done_status.get('id') - mdia.last_edit = int(time.time()) - db.commit() - except: - log.error('Failed marking media finished, renamer disabled: %s', traceback.format_exc()) - else: - self.updateStatus(rls.id, status = snatched_status) + # Mark release done + self.updateStatus(rls.id, status = done_status) - except: - log.error('Failed marking media finished: %s', traceback.format_exc()) + # Mark media done + mdia = db.query(Media).filter_by(id = media['id']).first() + mdia.status_id = done_status.get('id') + mdia.last_edit = int(time.time()) + db.commit() - return True + return True - log.info('Tried to download, but none of the "%s" downloaders are enabled or gave an error', (data.get('protocol'))) + # Assume release downloaded + self.updateStatus(rls.id, status = downloaded_status) + + except: + log.error('Failed storing download status: %s', traceback.format_exc()) + return False return False From 69569758d90f96d941d23ea363789e6d5993d5e5 Mon Sep 17 00:00:00 2001 From: mano3m Date: Mon, 16 Dec 2013 22:51:04 +0100 Subject: [PATCH 7/8] Make sure we return true on success --- couchpotato/core/plugins/release/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/couchpotato/core/plugins/release/main.py b/couchpotato/core/plugins/release/main.py index 061a856c..aec691e5 100644 --- a/couchpotato/core/plugins/release/main.py +++ b/couchpotato/core/plugins/release/main.py @@ -333,7 +333,7 @@ class Release(Plugin): log.error('Failed storing download status: %s', traceback.format_exc()) return False - return False + return True def tryDownloadResult(self, results, media, quality_type, manual = False): ignored_status, failed_status = fireEvent('status.get', ['ignored', 'failed'], single = True) From 7b3645ea7ce9d6ff106a266405cdda31987d1395 Mon Sep 17 00:00:00 2001 From: mano3m Date: Tue, 17 Dec 2013 21:41:26 +0100 Subject: [PATCH 8/8] Don't consider stalled as failed when seeding Fixes the issue where Transmission is seeding but still considering the torrent stalled (new functionality of Transmission). CPS marks it as failed and a perfectly good torrent gets deleted. Several people on the forum have this issue, --- couchpotato/core/downloaders/transmission/main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/couchpotato/core/downloaders/transmission/main.py b/couchpotato/core/downloaders/transmission/main.py index 46b5f3b4..b8b1f9df 100644 --- a/couchpotato/core/downloaders/transmission/main.py +++ b/couchpotato/core/downloaders/transmission/main.py @@ -103,15 +103,15 @@ class Transmission(Downloader): for torrent in queue['torrents']: if torrent['hashString'] in ids: - log.debug('name=%s / id=%s / downloadDir=%s / hashString=%s / percentDone=%s / status=%s / eta=%s / uploadRatio=%s / isFinished=%s', - (torrent['name'], torrent['id'], torrent['downloadDir'], torrent['hashString'], torrent['percentDone'], torrent['status'], torrent['eta'], torrent['uploadRatio'], torrent['isFinished'])) + log.debug('name=%s / id=%s / downloadDir=%s / hashString=%s / percentDone=%s / status=%s / isStalled=%s / eta=%s / uploadRatio=%s / isFinished=%s', + (torrent['name'], torrent['id'], torrent['downloadDir'], torrent['hashString'], torrent['percentDone'], torrent['status'], torrent.get('isStalled', 'N/A'), torrent['eta'], torrent['uploadRatio'], torrent['isFinished'])) torrent_files = [] for file_item in torrent['files']: torrent_files.append(sp(os.path.join(torrent['downloadDir'], file_item['name']))) status = 'busy' - if torrent.get('isStalled') and self.conf('stalled_as_failed'): + if torrent.get('isStalled') and not torrent['percentDone'] == 1 and self.conf('stalled_as_failed'): status = 'failed' elif torrent['status'] == 0 and torrent['percentDone'] == 1: status = 'completed'