From f6030a333a17e027607b69ddb42d10d1ae2d04cf Mon Sep 17 00:00:00 2001 From: Ruud Date: Sun, 29 Jun 2014 23:15:36 +0200 Subject: [PATCH 01/37] Don't remove pyc files when using desktop updater --- couchpotato/core/_base/updater/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/couchpotato/core/_base/updater/main.py b/couchpotato/core/_base/updater/main.py index 9f6613ab..7730d3bd 100644 --- a/couchpotato/core/_base/updater/main.py +++ b/couchpotato/core/_base/updater/main.py @@ -143,7 +143,7 @@ class Updater(Plugin): } def doShutdown(self, *args, **kwargs): - if not Env.get('dev'): + if not Env.get('dev') and not Env.get('desktop'): removePyc(Env.get('app_dir'), show_logs = False) return super(Updater, self).doShutdown(*args, **kwargs) From d20c0ee37ea306a2a27c09d1bf3d5e8daa5f1cc8 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sun, 29 Jun 2014 23:34:48 +0200 Subject: [PATCH 02/37] Remove Smackdown from defaults --- .../core/media/_base/providers/nzb/newznab.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/couchpotato/core/media/_base/providers/nzb/newznab.py b/couchpotato/core/media/_base/providers/nzb/newznab.py index 7db7e865..495463bf 100644 --- a/couchpotato/core/media/_base/providers/nzb/newznab.py +++ b/couchpotato/core/media/_base/providers/nzb/newznab.py @@ -220,7 +220,7 @@ config = [{ 'description': 'Enable NewzNab such as NZB.su, \ NZBs.org, DOGnzb.cr, \ Spotweb, NZBGeek, \ - SmackDown, NZBFinder', + NZBFinder', 'wizard': True, 'icon': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAACVBMVEVjhwD///86aRovd/sBAAAAMklEQVQI12NgAIPQUCCRmQkjssDEShiRuRIqwZqZGcDAGBrqANUhGgIkWAOABKMDxCAA24UK50b26SAAAAAASUVORK5CYII=', 'options': [ @@ -231,30 +231,30 @@ config = [{ }, { 'name': 'use', - 'default': '0,0,0,0,0,0' + 'default': '0,0,0,0,0' }, { 'name': 'host', - 'default': 'api.nzb.su,api.dognzb.cr,nzbs.org,https://index.nzbgeek.info, https://smackdownonyou.com, https://www.nzbfinder.ws', + 'default': 'api.nzb.su,api.dognzb.cr,nzbs.org,https://index.nzbgeek.info,https://www.nzbfinder.ws', 'description': 'The hostname of your newznab provider', }, { 'name': 'extra_score', 'advanced': True, 'label': 'Extra Score', - 'default': '0,0,0,0,0,0', + 'default': '0,0,0,0,0', 'description': 'Starting score for each release found via this provider.', }, { 'name': 'custom_tag', 'advanced': True, 'label': 'Custom tag', - 'default': ',,,,,', + 'default': ',,,,', 'description': 'Add custom tags, for example add rls=1 to get only scene releases from nzbs.org', }, { 'name': 'api_key', - 'default': ',,,,,', + 'default': ',,,,', 'label': 'Api Key', 'description': 'Can be found on your profile page', 'type': 'combined', From 099b72ed2759f664937e6c15584752f3375ead33 Mon Sep 17 00:00:00 2001 From: Ruud Date: Mon, 30 Jun 2014 16:28:45 +0200 Subject: [PATCH 03/37] Allow 720p in lower qualities. fix #3539 --- couchpotato/core/plugins/quality/main.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/couchpotato/core/plugins/quality/main.py b/couchpotato/core/plugins/quality/main.py index bc61afa2..2687cb14 100644 --- a/couchpotato/core/plugins/quality/main.py +++ b/couchpotato/core/plugins/quality/main.py @@ -29,9 +29,9 @@ class QualityPlugin(Plugin): {'identifier': 'dvdr', 'size': (3000, 10000), 'label': 'DVD-R', 'alternative': ['br2dvd', ('dvd', 'r')], 'allow': [], 'ext':['iso', 'img', 'vob'], 'tags': ['pal', 'ntsc', 'video_ts', 'audio_ts', ('dvd', 'r'), 'dvd9']}, {'identifier': 'dvdrip', 'size': (600, 2400), 'label': 'DVD-Rip', 'width': 720, 'alternative': [('dvd', 'rip')], 'allow': [], 'ext':['avi'], 'tags': [('dvd', 'rip'), ('dvd', 'xvid'), ('dvd', 'divx')]}, {'identifier': 'scr', 'size': (600, 1600), 'label': 'Screener', 'alternative': ['screener', 'dvdscr', 'ppvrip', 'dvdscreener', 'hdscr'], 'allow': ['dvdr', 'dvdrip', '720p', '1080p'], 'ext':[], 'tags': ['webrip', ('web', 'rip')]}, - {'identifier': 'r5', 'size': (600, 1000), 'label': 'R5', 'alternative': ['r6'], 'allow': ['dvdr'], 'ext':[]}, - {'identifier': 'tc', 'size': (600, 1000), 'label': 'TeleCine', 'alternative': ['telecine'], 'allow': [], 'ext':[]}, - {'identifier': 'ts', 'size': (600, 1000), 'label': 'TeleSync', 'alternative': ['telesync', 'hdts'], 'allow': [], 'ext':[]}, + {'identifier': 'r5', 'size': (600, 1000), 'label': 'R5', 'alternative': ['r6'], 'allow': ['dvdr', '720p'], 'ext':[]}, + {'identifier': 'tc', 'size': (600, 1000), 'label': 'TeleCine', 'alternative': ['telecine'], 'allow': ['720p'], 'ext':[]}, + {'identifier': 'ts', 'size': (600, 1000), 'label': 'TeleSync', 'alternative': ['telesync', 'hdts'], 'allow': ['720p'], 'ext':[]}, {'identifier': 'cam', 'size': (600, 1000), 'label': 'Cam', 'alternative': ['camrip', 'hdcam'], 'allow': ['720p'], 'ext':[]} ] pre_releases = ['cam', 'ts', 'tc', 'r5', 'scr'] @@ -454,6 +454,9 @@ class QualityPlugin(Plugin): 'Moviename 2014 720p HDCAM XviD DualAudio': {'size': 4000, 'quality': 'cam'}, 'Moviename (2014) - 720p CAM x264': {'size': 2250, 'quality': 'cam'}, 'Movie Name (2014).mp4': {'size': 750, 'quality': 'brrip'}, + 'Moviename.2014.720p.R6.WEB-DL.x264.AC3-xyz': {'size': 750, 'quality': 'r5'}, + 'Movie name 2014 New Source 720p HDCAM x264 AC3 xyz': {'size': 750, 'quality': 'cam'}, + 'Movie.Name.2014.720p.HD.TS.AC3.x264': {'size': 750, 'quality': 'ts'} } correct = 0 From 265f90fe6941bf0b7aa31b3a4e2e03adc4811e6c Mon Sep 17 00:00:00 2001 From: Ruud Date: Mon, 30 Jun 2014 21:36:32 +0200 Subject: [PATCH 04/37] Unrar cleanup --- libs/unrar2/unix.py | 8 +++++--- libs/unrar2/windows.py | 22 +++++++++++----------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/libs/unrar2/unix.py b/libs/unrar2/unix.py index 9ebab40d..a37b6df6 100644 --- a/libs/unrar2/unix.py +++ b/libs/unrar2/unix.py @@ -24,12 +24,14 @@ import subprocess import gc - -import os, os.path -import time, re +import os +import os.path +import time +import re from rar_exceptions import * + class UnpackerNotInstalled(Exception): pass rar_executable_cached = None diff --git a/libs/unrar2/windows.py b/libs/unrar2/windows.py index e249f8f3..bf8ffa5f 100644 --- a/libs/unrar2/windows.py +++ b/libs/unrar2/windows.py @@ -174,7 +174,7 @@ class PassiveReader: def __init__(self, usercallback = None): self.buf = [] self.ucb = usercallback - + def _callback(self, msg, UserData, P1, P2): if msg == UCM_PROCESSDATA: data = (ctypes.c_char*P2).from_address(P1).raw @@ -183,7 +183,7 @@ class PassiveReader: else: self.buf.append(data) return 1 - + def get_result(self): return ''.join(self.buf) @@ -197,10 +197,10 @@ class RarInfoIterator(object): raise IncorrectRARPassword self.arc.lockStatus = "locked" self.arc.needskip = False - + def __iter__(self): return self - + def next(self): if self.index>0: if self.arc.needskip: @@ -208,9 +208,9 @@ class RarInfoIterator(object): self.res = RARReadHeaderEx(self.arc._handle, ctypes.byref(self.headerData)) if self.res: - raise StopIteration + raise StopIteration self.arc.needskip = True - + data = {} data['index'] = self.index data['filename'] = self.headerData.FileName @@ -224,7 +224,7 @@ class RarInfoIterator(object): self.index += 1 return data - + def __del__(self): self.arc.lockStatus = "finished" @@ -254,9 +254,9 @@ class RarFileImplementation(object): if password: RARSetPassword(self._handle, password) - + self.lockStatus = "ready" - + def destruct(self): @@ -287,7 +287,7 @@ class RarFileImplementation(object): self.needskip = False res.append((info, reader.get_result())) return res - + def extract(self, checker, path, withSubpath, overwrite): res = [] @@ -300,7 +300,7 @@ class RarFileImplementation(object): fn = os.path.split(fn)[-1] target = os.path.join(path, fn) else: - raise DeprecationWarning, "Condition callbacks returning strings are deprecated and only supported in Windows" + raise DeprecationWarning, "Condition callbacks returning strings are deprecated and only supported in Windows" target = checkres if overwrite or (not os.path.exists(target)): tmpres = RARProcessFile(self._handle, RAR_EXTRACT, None, target) From 33ad4c22c7f8edc8062c13731da2bfc6a0644e6d Mon Sep 17 00:00:00 2001 From: Ruud Date: Mon, 30 Jun 2014 21:58:19 +0200 Subject: [PATCH 05/37] Try make unrar executable for user --- libs/unrar2/unix.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/libs/unrar2/unix.py b/libs/unrar2/unix.py index a37b6df6..ce2d6a7e 100644 --- a/libs/unrar2/unix.py +++ b/libs/unrar2/unix.py @@ -21,7 +21,8 @@ # SOFTWARE. # Unix version uses unrar command line executable - +import platform +import stat import subprocess import gc import os @@ -37,11 +38,19 @@ class UnpackerNotInstalled(Exception): pass rar_executable_cached = None rar_executable_version = None +osx_unrar = os.path.join(os.path.dirname(__file__), 'unrar') +if os.path.isfile(osx_unrar) and 'darwin' in platform.platform().lower(): + try: + st = os.stat(osx_unrar) + os.chmod(osx_unrar, st.st_mode | stat.S_IEXEC) + except: + pass + def call_unrar(params): "Calls rar/unrar command line executable, returns stdout pipe" global rar_executable_cached if rar_executable_cached is None: - for command in ('unrar', 'rar', os.path.join(os.path.dirname(__file__), 'unrar')): + for command in ('unrar', 'rar', osx_unrar): try: subprocess.Popen([command], stdout = subprocess.PIPE) rar_executable_cached = command From 169ddeef5df2eb898ea2496fb9240f0cbb70d6fb Mon Sep 17 00:00:00 2001 From: Ruud Date: Mon, 30 Jun 2014 22:38:18 +0200 Subject: [PATCH 06/37] Allow custom unrar path. fix #3460 --- couchpotato/core/plugins/renamer.py | 7 ++++++- libs/unrar2/__init__.py | 28 ++++++++++++++-------------- libs/unrar2/unix.py | 11 ++++++----- libs/unrar2/windows.py | 2 +- 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/couchpotato/core/plugins/renamer.py b/couchpotato/core/plugins/renamer.py index 481c9dd8..46cd6774 100644 --- a/couchpotato/core/plugins/renamer.py +++ b/couchpotato/core/plugins/renamer.py @@ -1140,7 +1140,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']) + rar_handle = RarFile(archive['file'], custom_path = self.conf('unrar_path')) extr_path = os.path.join(from_folder, os.path.relpath(os.path.dirname(archive['file']), folder)) self.makeDir(extr_path) for packedinfo in rar_handle.infolist(): @@ -1282,6 +1282,11 @@ config = [{ 'description': 'Extract rar files if found.', 'default': False, }, + { + 'advanced': True, + 'name': 'unrar_path', + 'description': 'Custom path to unrar bin', + }, { 'name': 'cleanup', 'type': 'bool', diff --git a/libs/unrar2/__init__.py b/libs/unrar2/__init__.py index fe27cfe1..41b0d71d 100644 --- a/libs/unrar2/__init__.py +++ b/libs/unrar2/__init__.py @@ -21,7 +21,7 @@ # SOFTWARE. """ -pyUnRAR2 is a ctypes based wrapper around the free UnRAR.dll. +pyUnRAR2 is a ctypes based wrapper around the free UnRAR.dll. It is an modified version of Jimmy Retzlaff's pyUnRAR - more simple, stable and foolproof. @@ -45,8 +45,8 @@ if in_windows: from windows import RarFileImplementation else: from unix import RarFileImplementation - - + + import fnmatch, time, weakref class RarInfo(object): @@ -62,7 +62,7 @@ class RarInfo(object): isdir - True if the file is a directory size - size in bytes of the uncompressed file comment - comment associated with the file - + Note - this is not currently intended to be a Python file-like object. """ @@ -74,7 +74,7 @@ class RarInfo(object): self.size = data['size'] self.datetime = data['datetime'] self.comment = data['comment'] - + def __str__(self): @@ -86,7 +86,7 @@ class RarInfo(object): class RarFile(RarFileImplementation): - def __init__(self, archiveName, password=None): + def __init__(self, archiveName, password=None, custom_path = None): """Instantiate the archive. archiveName is the name of the RAR file. @@ -99,7 +99,7 @@ class RarFile(RarFileImplementation): This is a test. """ self.archiveName = archiveName - RarFileImplementation.init(self, password) + RarFileImplementation.init(self, password, custom_path) def __del__(self): self.destruct() @@ -130,31 +130,31 @@ class RarFile(RarFileImplementation): """Read specific files from archive into memory. If "condition" is a list of numbers, then return files which have those positions in infolist. If "condition" is a string, then it is treated as a wildcard for names of files to extract. - If "condition" is a function, it is treated as a callback function, which accepts a RarInfo object + If "condition" is a function, it is treated as a callback function, which accepts a RarInfo object and returns boolean True (extract) or False (skip). If "condition" is omitted, all files are returned. - + Returns list of tuples (RarInfo info, str contents) """ checker = condition2checker(condition) return RarFileImplementation.read_files(self, checker) - + def extract(self, condition='*', path='.', withSubpath=True, overwrite=True): """Extract specific files from archive to disk. - + If "condition" is a list of numbers, then extract files which have those positions in infolist. If "condition" is a string, then it is treated as a wildcard for names of files to extract. If "condition" is a function, it is treated as a callback function, which accepts a RarInfo object and returns either boolean True (extract) or boolean False (skip). - DEPRECATED: If "condition" callback returns string (only supported for Windows) - + DEPRECATED: If "condition" callback returns string (only supported for Windows) - that string will be used as a new name to save the file under. If "condition" is omitted, all files are extracted. - + "path" is a directory to extract to "withSubpath" flag denotes whether files are extracted with their full path in the archive. "overwrite" flag denotes whether extracted files will overwrite old ones. Defaults to true. - + Returns list of RarInfos for extracted files.""" checker = condition2checker(condition) return RarFileImplementation.extract(self, checker, path, withSubpath, overwrite) diff --git a/libs/unrar2/unix.py b/libs/unrar2/unix.py index ce2d6a7e..91ed4b6c 100644 --- a/libs/unrar2/unix.py +++ b/libs/unrar2/unix.py @@ -46,11 +46,12 @@ if os.path.isfile(osx_unrar) and 'darwin' in platform.platform().lower(): except: pass -def call_unrar(params): +def call_unrar(params, custom_path = None): "Calls rar/unrar command line executable, returns stdout pipe" global rar_executable_cached if rar_executable_cached is None: - for command in ('unrar', 'rar', osx_unrar): + for command in (custom_path, 'unrar', 'rar', osx_unrar): + if not command: continue try: subprocess.Popen([command], stdout = subprocess.PIPE) rar_executable_cached = command @@ -70,10 +71,10 @@ def call_unrar(params): class RarFileImplementation(object): - def init(self, password = None): + def init(self, password = None, custom_path = None): global rar_executable_version self.password = password - + self.custom_path = custom_path stdoutdata, stderrdata = self.call('v', []).communicate() @@ -129,7 +130,7 @@ class RarFileImplementation(object): def call(self, cmd, options = [], files = []): options2 = options + ['p' + self.escaped_password()] soptions = ['-' + x for x in options2] - return call_unrar([cmd] + soptions + ['--', self.archiveName] + files) + return call_unrar([cmd] + soptions + ['--', self.archiveName] + files, self.custom_path) def infoiter(self): diff --git a/libs/unrar2/windows.py b/libs/unrar2/windows.py index bf8ffa5f..e3d920ff 100644 --- a/libs/unrar2/windows.py +++ b/libs/unrar2/windows.py @@ -237,7 +237,7 @@ def generate_password_provider(password): class RarFileImplementation(object): - def init(self, password=None): + def init(self, password=None, custom_path = None): self.password = password archiveData = RAROpenArchiveDataEx(ArcNameW=self.archiveName, OpenMode=RAR_OM_EXTRACT) self._handle = RAROpenArchiveEx(ctypes.byref(archiveData)) From 34320e617d7c908c3c9f1bad0387ca40f7596865 Mon Sep 17 00:00:00 2001 From: Ruud Date: Mon, 30 Jun 2014 22:47:41 +0200 Subject: [PATCH 07/37] Unrar extract time options. closes #2733 --- couchpotato/core/plugins/renamer.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/couchpotato/core/plugins/renamer.py b/couchpotato/core/plugins/renamer.py index 46cd6774..ef563098 100644 --- a/couchpotato/core/plugins/renamer.py +++ b/couchpotato/core/plugins/renamer.py @@ -1144,10 +1144,16 @@ Remove it if you want it to be renamed (again, or at least let it try again) extr_path = 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(sp(os.path.join(extr_path, os.path.basename(packedinfo.filename)))): + extr_file_path = sp(os.path.join(extr_path, os.path.basename(packedinfo.filename))) + if not packedinfo.isdir and not os.path.isfile(extr_file_path): log.debug('Extracting %s...', packedinfo.filename) rar_handle.extract(condition = [packedinfo.index], path = extr_path, withSubpath = False, overwrite = False) - extr_files.append(sp(os.path.join(extr_path, os.path.basename(packedinfo.filename)))) + if self.conf('unrar_modify_date'): + try: + os.utime(extr_file_path, (os.path.getatime(archive['file']), os.path.getmtime(archive['file']))) + except: + log.error('Rar modify date enabled, but failed: %s', traceback.format_exc()) + extr_files.append(extr_file_path) del rar_handle except Exception as e: log.error('Failed to extract %s: %s %s', (archive['file'], e, traceback.format_exc())) @@ -1287,6 +1293,13 @@ config = [{ 'name': 'unrar_path', 'description': 'Custom path to unrar bin', }, + { + 'advanced': True, + 'name': 'unrar_modify_date', + 'type': 'bool', + 'description': ('Set modify date of unrar-ed files to the rar-file\'s date.', 'This will allow XBMC to recognize extracted files as recently added even if the movie was released some time ago.'), + 'default': False, + }, { 'name': 'cleanup', 'type': 'bool', From f765794c99feaf548db0a77c50883b93e0fd85df Mon Sep 17 00:00:00 2001 From: Ruud Date: Tue, 1 Jul 2014 21:00:05 +0200 Subject: [PATCH 08/37] Don't add managed to no-process renamer list. fix #3538 --- couchpotato/core/plugins/renamer.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/couchpotato/core/plugins/renamer.py b/couchpotato/core/plugins/renamer.py index ef563098..024c4388 100644 --- a/couchpotato/core/plugins/renamer.py +++ b/couchpotato/core/plugins/renamer.py @@ -123,11 +123,6 @@ class Renamer(Plugin): no_process = [to_folder] cat_list = fireEvent('category.all', single = True) or [] no_process.extend([item['destination'] for item in cat_list]) - try: - if Env.setting('library', section = 'manage').strip(): - no_process.extend([sp(manage_folder) for manage_folder in splitString(Env.setting('library', section = 'manage'), '::')]) - except: - pass # Check to see if the no_process folders are inside the "from" folder. if not os.path.isdir(base_folder) or not os.path.isdir(to_folder): From d201d9fff9f99e264ac914c2f3b24f7797abb556 Mon Sep 17 00:00:00 2001 From: Ruud Date: Wed, 2 Jul 2014 19:02:55 +0200 Subject: [PATCH 09/37] Allow change of file move action --- couchpotato/core/plugins/renamer.py | 47 +++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/couchpotato/core/plugins/renamer.py b/couchpotato/core/plugins/renamer.py index 024c4388..2bc47876 100644 --- a/couchpotato/core/plugins/renamer.py +++ b/couchpotato/core/plugins/renamer.py @@ -197,14 +197,18 @@ class Renamer(Plugin): db = get_db() # Extend the download info with info stored in the downloaded release + keep_original = self.moveTypeIsLinked() + is_torrent = False if release_download: release_download = self.extendReleaseDownload(release_download) + is_torrent = self.downloadIsTorrent(release_download) + keep_original = True if is_torrent and self.conf('file_action') not in ['move'] else keep_original # Unpack any archives extr_files = None if self.conf('unrar'): 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)) + cleanup = self.conf('cleanup') and not keep_original) groups = fireEvent('scanner.scan', folder = folder if folder else base_folder, files = files, release_download = release_download, return_ignored = False, single = True) or [] @@ -321,7 +325,7 @@ class Renamer(Plugin): if file_type is 'nfo' and not self.conf('rename_nfo'): log.debug('Skipping, renaming of %s disabled', file_type) for current_file in group['files'][file_type]: - if self.conf('cleanup') and (not self.downloadIsTorrent(release_download) or self.fileIsAdded(current_file, group)): + if self.conf('cleanup') and (not keep_original or self.fileIsAdded(current_file, group)): remove_files.append(current_file) continue @@ -527,7 +531,7 @@ class Renamer(Plugin): log.debug('Removing leftover files') for current_file in group['files']['leftover']: if self.conf('cleanup') and not self.conf('move_leftover') and \ - (not self.downloadIsTorrent(release_download) or self.fileIsAdded(current_file, group)): + (not keep_original or self.fileIsAdded(current_file, group)): remove_files.append(current_file) # Remove files @@ -574,7 +578,7 @@ class Renamer(Plugin): self.makeDir(os.path.dirname(dst)) try: - self.moveFile(src, dst, forcemove = not self.downloadIsTorrent(release_download) or self.fileIsAdded(src, group)) + self.moveFile(src, dst, use_default = not is_torrent or self.fileIsAdded(src, group)) group['renamed_files'].append(dst) except: log.error('Failed renaming the file "%s" : %s', (os.path.basename(src), traceback.format_exc())) @@ -590,7 +594,7 @@ class Renamer(Plugin): self.untagRelease(group = group, tag = '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(media_folder) and self.downloadIsTorrent(release_download): + if self.movieInFromFolder(media_folder) and keep_original: self.tagRelease(group = group, tag = 'renamed_already') # Remove matching releases @@ -601,7 +605,7 @@ class Renamer(Plugin): except: log.error('Failed removing %s: %s', (release, traceback.format_exc())) - if group['dirname'] and group['parentdir'] and not self.downloadIsTorrent(release_download): + if group['dirname'] and group['parentdir'] and not keep_original: if media_folder: # Delete the movie folder group_folder = media_folder @@ -763,10 +767,15 @@ Remove it if you want it to be renamed (again, or at least let it try again) return False - def moveFile(self, old, dest, forcemove = False): + def moveFile(self, old, dest, use_default = False): dest = sp(dest) try: - if forcemove or self.conf('file_action') not in ['copy', 'link']: + + move_type = self.conf('file_action') + if use_default: + move_type = self.conf('default_file_action') + + if move_type not in ['copy', 'link']: try: shutil.move(old, dest) except: @@ -775,16 +784,16 @@ Remove it if you want it to be renamed (again, or at least let it try again) os.unlink(old) else: raise - elif self.conf('file_action') == 'copy': + elif move_type == 'copy': shutil.copy(old, dest) - elif self.conf('file_action') == 'link': + else: # First try to hardlink try: log.debug('Hardlinking file "%s" to "%s"...', (old, dest)) link(old, dest) except: # Try to simlink next - log.debug('Couldn\'t hardlink file "%s" to "%s". Simlinking instead. Error: %s.', (old, dest, traceback.format_exc())) + log.debug('Couldn\'t hardlink file "%s" to "%s". Symlinking instead. Error: %s.', (old, dest, traceback.format_exc())) shutil.copy(old, dest) try: symlink(dest, old + '.link') @@ -1084,6 +1093,9 @@ Remove it if you want it to be renamed (again, or at least let it try again) return False return src in group['before_rename'] + def moveTypeIsLinked(self): + return self.conf('default_file_action') in ['copy', 'link'] + def statusInfoComplete(self, release_download): return release_download.get('id') and release_download.get('downloader') and release_download.get('folder') @@ -1345,14 +1357,23 @@ config = [{ 'label': 'Folder-Separator', 'description': ('Replace all the spaces with a character.', 'Example: ".", "-" (without quotes). Leave empty to use spaces.'), }, + { + 'name': 'default_file_action', + 'label': 'Default File Action', + 'default': 'move', + 'type': 'dropdown', + 'values': [('Link', 'link'), ('Copy', 'copy'), ('Move', 'move')], + 'description': ('Link, Copy or Move after download completed.', + 'Link first tries hard link, then sym link and falls back to Copy.'), + 'advanced': True, + }, { 'name': 'file_action', 'label': 'Torrent File Action', 'default': 'link', 'type': 'dropdown', 'values': [('Link', 'link'), ('Copy', 'copy'), ('Move', 'move')], - 'description': ('Link, Copy or Move after download completed.', - 'Link first tries hard link, then sym link and falls back to Copy. It is perfered to use link when downloading torrents as it will save you space, while still beeing able to seed.'), + 'description': 'See above. It is prefered to use link when downloading torrents as it will save you space, while still beeing able to seed.', 'advanced': True, }, { From 330e15bbcb56d44766d5bb89ce2905e7841fd22e Mon Sep 17 00:00:00 2001 From: Ruud Date: Fri, 4 Jul 2014 20:36:48 +0200 Subject: [PATCH 10/37] Snatched not giving enough data to show notification. fix #3564 --- couchpotato/core/notifications/pushover.py | 6 +++--- couchpotato/core/plugins/release/main.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/couchpotato/core/notifications/pushover.py b/couchpotato/core/notifications/pushover.py index d9ef226c..46dc0ad8 100644 --- a/couchpotato/core/notifications/pushover.py +++ b/couchpotato/core/notifications/pushover.py @@ -1,7 +1,7 @@ from httplib import HTTPSConnection from couchpotato.core.helpers.encoding import toUnicode, tryUrlencode -from couchpotato.core.helpers.variable import getTitle +from couchpotato.core.helpers.variable import getTitle, getIdentifier from couchpotato.core.logger import CPLog from couchpotato.core.notifications.base import Notification @@ -27,9 +27,9 @@ class Pushover(Notification): 'sound': self.conf('sound'), } - if data and data.get('identifier'): + if data and getIdentifier(data): api_data.update({ - 'url': toUnicode('http://www.imdb.com/title/%s/' % data['identifier']), + 'url': toUnicode('http://www.imdb.com/title/%s/' % getIdentifier(data)), 'url_title': toUnicode('%s on IMDb' % getTitle(data)), }) diff --git a/couchpotato/core/plugins/release/main.py b/couchpotato/core/plugins/release/main.py index 196892ce..7b47aa96 100644 --- a/couchpotato/core/plugins/release/main.py +++ b/couchpotato/core/plugins/release/main.py @@ -327,7 +327,7 @@ class Release(Plugin): log_movie = '%s (%s) in %s' % (getTitle(media), media['info']['year'], rls['quality']) snatch_message = 'Snatched "%s": %s' % (data.get('name'), log_movie) log.info(snatch_message) - fireEvent('%s.snatched' % data['type'], message = snatch_message, data = rls) + fireEvent('%s.snatched' % data['type'], message = snatch_message, data = media) # Mark release as snatched if renamer_enabled: From 40a5ce087b52a7f9b940fed25a08c83aa8c94da4 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sun, 6 Jul 2014 22:19:45 +0200 Subject: [PATCH 11/37] Better label for Pushbullet settings --- couchpotato/core/notifications/pushbullet.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/couchpotato/core/notifications/pushbullet.py b/couchpotato/core/notifications/pushbullet.py index 361294e4..e9d4605c 100644 --- a/couchpotato/core/notifications/pushbullet.py +++ b/couchpotato/core/notifications/pushbullet.py @@ -84,7 +84,8 @@ config = [{ }, { 'name': 'api_key', - 'label': 'User API Key' + 'label': 'Access Token', + 'description': 'Can be found on Account Settings', }, { 'name': 'devices', From 04e22b3966977085cd706bbcb9f1806ee2d4d1fc Mon Sep 17 00:00:00 2001 From: Ruud Date: Tue, 8 Jul 2014 20:09:24 +0200 Subject: [PATCH 12/37] XBMC error > info2 --- couchpotato/core/notifications/xbmc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/couchpotato/core/notifications/xbmc.py b/couchpotato/core/notifications/xbmc.py index eb0b6996..bf5310e0 100644 --- a/couchpotato/core/notifications/xbmc.py +++ b/couchpotato/core/notifications/xbmc.py @@ -208,7 +208,7 @@ class XBMC(Notification): log.debug('Returned from request %s: %s', (host, response)) return response - except (MaxRetryError, requests.exceptions.Timeout): + except (MaxRetryError, requests.exceptions.Timeout, ConnectionError): log.info2('Couldn\'t send request to XBMC, assuming it\'s turned off') return [] except: From e0479e79bd7295c549dfc61a70694e8e1e9d30cd Mon Sep 17 00:00:00 2001 From: Ruud Date: Tue, 8 Jul 2014 20:24:18 +0200 Subject: [PATCH 13/37] AwesomeHD not returning proper size. fix #3587 --- couchpotato/core/media/_base/providers/torrent/awesomehd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/couchpotato/core/media/_base/providers/torrent/awesomehd.py b/couchpotato/core/media/_base/providers/torrent/awesomehd.py index 78c46488..bd9e1932 100644 --- a/couchpotato/core/media/_base/providers/torrent/awesomehd.py +++ b/couchpotato/core/media/_base/providers/torrent/awesomehd.py @@ -61,7 +61,7 @@ class Base(TorrentProvider): 'name': re.sub('[^A-Za-z0-9\-_ \(\).]+', '', '%s (%s) %s' % (name, year, torrent_desc)), 'url': self.urls['download'] % (torrent_id, authkey, self.conf('passkey')), 'detail_url': self.urls['detail'] % torrent_id, - 'size': self.parseSize(entry.find('size').get_text()), + 'size': tryInt(entry.find('size').get_text()) / 1048576, 'seeders': tryInt(entry.find('seeders').get_text()), 'leechers': tryInt(entry.find('leechers').get_text()), 'score': torrentscore From 32ce93d2e9bccbaa5768324984d956627694eadd Mon Sep 17 00:00:00 2001 From: Ruud Date: Fri, 11 Jul 2014 10:30:32 +0200 Subject: [PATCH 14/37] Encode video path --- couchpotato/core/plugins/scanner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/couchpotato/core/plugins/scanner.py b/couchpotato/core/plugins/scanner.py index 6a4d537d..3d39b290 100644 --- a/couchpotato/core/plugins/scanner.py +++ b/couchpotato/core/plugins/scanner.py @@ -553,7 +553,7 @@ class Scanner(Plugin): scan_result = [] for p in paths: if not group['is_dvd']: - video = Video.from_path(toUnicode(p)) + video = Video.from_path(sp(p)) video_result = [(video, video.scan())] scan_result.extend(video_result) From 0b48ad5084fdafa56abbe21dccfedabea8b12b0e Mon Sep 17 00:00:00 2001 From: Ruud Date: Fri, 11 Jul 2014 16:24:45 +0200 Subject: [PATCH 15/37] Change fanart api url --- .../core/media/movie/providers/info/fanarttv.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/couchpotato/core/media/movie/providers/info/fanarttv.py b/couchpotato/core/media/movie/providers/info/fanarttv.py index 8bfa92c8..fcd3891b 100644 --- a/couchpotato/core/media/movie/providers/info/fanarttv.py +++ b/couchpotato/core/media/movie/providers/info/fanarttv.py @@ -14,7 +14,7 @@ autoload = 'FanartTV' class FanartTV(MovieProvider): urls = { - 'api': 'http://api.fanart.tv/webservice/movie/b28b14e9be662e027cfbc7c3dd600405/%s/JSON/all/1/2' + 'api': 'http://webservice.fanart.tv/v3/movies/%s?api_key=b28b14e9be662e027cfbc7c3dd600405' } MAX_EXTRAFANART = 20 @@ -36,9 +36,8 @@ class FanartTV(MovieProvider): fanart_data = self.getJsonData(url) if fanart_data: - name, resource = fanart_data.items()[0] - log.debug('Found images for %s', name) - images = self._parseMovie(resource) + log.debug('Found images for %s', fanart_data.get('name')) + images = self._parseMovie(fanart_data) except: log.error('Failed getting extra art for %s: %s', @@ -95,7 +94,7 @@ class FanartTV(MovieProvider): for image in images: if tryInt(image.get('likes')) > highscore: highscore = tryInt(image.get('likes')) - image_url = image.get('url') + image_url = image.get('url') or image.get('href') return image_url @@ -118,7 +117,9 @@ class FanartTV(MovieProvider): if tryInt(image.get('likes')) > highscore: highscore = tryInt(image.get('likes')) best = image - image_urls.append(best.get('url')) + url = best.get('url') or best.get('href') + if url: + image_urls.append(url) pool.remove(best) return image_urls From 06f49be09007406d1608c04d84ca1cd77df52a7b Mon Sep 17 00:00:00 2001 From: Ruud Date: Fri, 11 Jul 2014 16:47:15 +0200 Subject: [PATCH 16/37] Don't error out on media.get. fix #3611 --- couchpotato/core/media/_base/media/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/couchpotato/core/media/_base/media/main.py b/couchpotato/core/media/_base/media/main.py index 1d3e1530..e79e3ea6 100644 --- a/couchpotato/core/media/_base/media/main.py +++ b/couchpotato/core/media/_base/media/main.py @@ -4,7 +4,7 @@ import time import traceback from string import ascii_lowercase -from CodernityDB.database import RecordNotFound +from CodernityDB.database import RecordNotFound, RecordDeleted from couchpotato import tryInt, get_db from couchpotato.api import addApiView from couchpotato.core.event import fireEvent, fireEventAsync, addEvent @@ -146,7 +146,7 @@ class MediaPlugin(MediaBase): return media - except RecordNotFound: + except (RecordNotFound, RecordDeleted): log.error('Media with id "%s" not found', media_id) except: raise From b13df16b53bbd35c2b59c35ce2b309de3f63b1f1 Mon Sep 17 00:00:00 2001 From: peerster Date: Sat, 12 Jul 2014 16:29:42 +0200 Subject: [PATCH 17/37] Added systemd specific instructions --- README.md | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4dbe75bb..05e3c8d1 100644 --- a/README.md +++ b/README.md @@ -29,17 +29,23 @@ OS X: * Then do `python CouchPotatoServer/CouchPotato.py` * Your browser should open up, but if it doesn't go to `http://localhost:5050/` -Linux (Ubuntu / Debian): +Linux: -* Install [GIT](http://git-scm.com/) with `apt-get install git-core` +* (Ubuntu / Debian) Install [GIT](http://git-scm.com/) with `apt-get install git-core` +* (Fedora / CentOS) Install [GIT](http://git-scm.com/) with `yum install git` * 'cd' to the folder of your choosing. * Run `git clone https://github.com/RuudBurger/CouchPotatoServer.git` * Then do `python CouchPotatoServer/CouchPotato.py` to start -* To run on boot copy the init script `sudo cp CouchPotatoServer/init/ubuntu /etc/init.d/couchpotato` -* Copy the default paths file `sudo cp CouchPotatoServer/init/ubuntu.default /etc/default/couchpotato` -* Change the paths inside the default file `sudo nano /etc/default/couchpotato` -* Make it executable `sudo chmod +x /etc/init.d/couchpotato` -* Add it to defaults `sudo update-rc.d couchpotato defaults` +* (Ubuntu / Debian) To run on boot copy the init script `sudo cp CouchPotatoServer/init/ubuntu /etc/init.d/couchpotato` +* (Ubuntu / Debian) Copy the default paths file `sudo cp CouchPotatoServer/init/ubuntu.default /etc/default/couchpotato` +* (Ubuntu / Debian) Change the paths inside the default file `sudo nano /etc/default/couchpotato` +* (Ubuntu / Debian) Make it executable `sudo chmod +x /etc/init.d/couchpotato` +* (Ubuntu / Debian) Add it to defaults `sudo update-rc.d couchpotato defaults` +Systemd specific: +* To run on boot copy the systemd config `sudo cp CouchPotatoServer/init/couchpotato.fedora.service /etc/systemd/system/couchpotato.service` +* Update the systemd config file with your user and path to CouchPotato.py +* Enable it at boot with `sudo systemctl enable couchpotato` + * Open your browser and go to `http://localhost:5050/` From bd6690b159b3a510757e262bee8dafa7e7824f87 Mon Sep 17 00:00:00 2001 From: peerster Date: Sat, 12 Jul 2014 16:34:41 +0200 Subject: [PATCH 18/37] layout fix --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 05e3c8d1..4d5a39eb 100644 --- a/README.md +++ b/README.md @@ -41,10 +41,9 @@ Linux: * (Ubuntu / Debian) Change the paths inside the default file `sudo nano /etc/default/couchpotato` * (Ubuntu / Debian) Make it executable `sudo chmod +x /etc/init.d/couchpotato` * (Ubuntu / Debian) Add it to defaults `sudo update-rc.d couchpotato defaults` -Systemd specific: -* To run on boot copy the systemd config `sudo cp CouchPotatoServer/init/couchpotato.fedora.service /etc/systemd/system/couchpotato.service` -* Update the systemd config file with your user and path to CouchPotato.py -* Enable it at boot with `sudo systemctl enable couchpotato` +* (systemd) To run on boot copy the systemd config `sudo cp CouchPotatoServer/init/couchpotato.fedora.service /etc/systemd/system/couchpotato.service` +* (systemd) Update the systemd config file with your user and path to CouchPotato.py +* (systemd) Enable it at boot with `sudo systemctl enable couchpotato` * Open your browser and go to `http://localhost:5050/` From a99d52392fce799075c230bdef4f93af62344976 Mon Sep 17 00:00:00 2001 From: peerster Date: Sat, 12 Jul 2014 16:40:24 +0200 Subject: [PATCH 19/37] Another layout fix --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 4d5a39eb..a38c0520 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,6 @@ Linux: * (systemd) To run on boot copy the systemd config `sudo cp CouchPotatoServer/init/couchpotato.fedora.service /etc/systemd/system/couchpotato.service` * (systemd) Update the systemd config file with your user and path to CouchPotato.py * (systemd) Enable it at boot with `sudo systemctl enable couchpotato` - * Open your browser and go to `http://localhost:5050/` From 687221f035587ad0339c1bb8c433c94a993f2b98 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sat, 12 Jul 2014 19:38:49 +0200 Subject: [PATCH 20/37] Update last_edit when tagging with recent --- couchpotato/core/media/_base/media/main.py | 7 +++++-- couchpotato/core/media/movie/searcher.py | 2 +- couchpotato/core/plugins/renamer.py | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/couchpotato/core/media/_base/media/main.py b/couchpotato/core/media/_base/media/main.py index e79e3ea6..ee9e6ccc 100644 --- a/couchpotato/core/media/_base/media/main.py +++ b/couchpotato/core/media/_base/media/main.py @@ -488,18 +488,21 @@ class MediaPlugin(MediaBase): db.update(m) # Tag media as recent - self.tag(media_id, 'recent') + self.tag(media_id, 'recent', update_edited = True) return m['status'] except: log.error('Failed restatus: %s', traceback.format_exc()) - def tag(self, media_id, tag): + def tag(self, media_id, tag, update_edited = False): try: db = get_db() m = db.get('id', media_id) + if update_edited: + m['last_edit'] = int(time.time()) + tags = m.get('tags') or [] if tag not in tags: tags.append(tag) diff --git a/couchpotato/core/media/movie/searcher.py b/couchpotato/core/media/movie/searcher.py index 7d92c57e..4bd8c8d7 100644 --- a/couchpotato/core/media/movie/searcher.py +++ b/couchpotato/core/media/movie/searcher.py @@ -240,7 +240,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase): break if total_result_count > 0: - fireEvent('media.tag', movie['_id'], 'recent', single = True) + fireEvent('media.tag', movie['_id'], 'recent', update_edited = True, single = True) if len(too_early_to_search) > 0: log.info2('Too early to search for %s, %s', (too_early_to_search, default_title)) diff --git a/couchpotato/core/plugins/renamer.py b/couchpotato/core/plugins/renamer.py index 2bc47876..9f6792ab 100644 --- a/couchpotato/core/plugins/renamer.py +++ b/couchpotato/core/plugins/renamer.py @@ -522,7 +522,7 @@ class Renamer(Plugin): # Mark media for dashboard if mark_as_recent: - fireEvent('media.tag', group['media'].get('_id'), 'recent', single = True) + fireEvent('media.tag', group['media'].get('_id'), 'recent', update_edited = True, single = True) # Remove leftover files if not remove_leftovers: # Don't remove anything From 12cda35494770ae4a3d8b1f4eabf4c5a416f68c7 Mon Sep 17 00:00:00 2001 From: Ruud Date: Thu, 17 Jul 2014 22:24:48 +0200 Subject: [PATCH 21/37] Try fix migration failure from 2.5.1 --- couchpotato/core/database.py | 30 +++++++++++++++++++++++++++++- couchpotato/runner.py | 7 +++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/couchpotato/core/database.py b/couchpotato/core/database.py index c7051f5c..47be7f53 100644 --- a/couchpotato/core/database.py +++ b/couchpotato/core/database.py @@ -3,10 +3,11 @@ import os import time import traceback +from CodernityDB.database import RecordNotFound from CodernityDB.index import IndexException, IndexNotFoundException, IndexConflict from couchpotato import CPLog from couchpotato.api import addApiView -from couchpotato.core.event import addEvent, fireEvent +from couchpotato.core.event import addEvent, fireEvent, fireEventAsync from couchpotato.core.helpers.encoding import toUnicode, sp from couchpotato.core.helpers.variable import getImdb, tryInt @@ -226,6 +227,33 @@ class Database(object): from couchpotato import Env db = self.getDB() + + # Try fix for migration failures on desktop + if Env.get('desktop'): + try: + list(db.all('profile', with_doc = True)) + except RecordNotFound: + + failed_location = '%s_failed' % db.path + old_db = os.path.join(Env.get('data_dir'), 'couchpotato.db.old') + + if not os.path.isdir(failed_location) and os.path.isfile(old_db): + db.close() + + # Rename database folder + os.rename(db.path, '%s_failed' % db.path) + + # Rename .old database to try another migrate + os.rename(old_db, old_db[:-4]) + + fireEventAsync('app.restart') + else: + log.error('Migration failed and couldn\'t recover database. Please report on GitHub, with this message.') + db.reindex() + + return + + # Check size and compact if needed size = db.get_db_details().get('size') prop_name = 'last_db_compact' last_check = int(Env.prop(prop_name, default = 0)) diff --git a/couchpotato/runner.py b/couchpotato/runner.py index e5f9bcab..5d3f62b0 100644 --- a/couchpotato/runner.py +++ b/couchpotato/runner.py @@ -87,6 +87,13 @@ def runCouchPotato(options, base_path, args, data_dir = None, log_dir = None, En # Do db stuff db_path = sp(os.path.join(data_dir, 'database')) + old_db_path = os.path.join(data_dir, 'couchpotato.db') + + # Remove database folder if both exists + if os.path.isdir(db_path) and os.path.isfile(old_db_path): + db = SuperThreadSafeDatabase(db_path) + db.open() + db.destroy() # Check if database exists db = SuperThreadSafeDatabase(db_path) From 324920cd8c6f080f391a99610c19d863072a190b Mon Sep 17 00:00:00 2001 From: Ruud Date: Thu, 17 Jul 2014 23:09:26 +0200 Subject: [PATCH 22/37] Don't migrate when db is closed --- couchpotato/core/database.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/couchpotato/core/database.py b/couchpotato/core/database.py index 47be7f53..10ae26c2 100644 --- a/couchpotato/core/database.py +++ b/couchpotato/core/database.py @@ -238,6 +238,7 @@ class Database(object): old_db = os.path.join(Env.get('data_dir'), 'couchpotato.db.old') if not os.path.isdir(failed_location) and os.path.isfile(old_db): + log.error('Corrupt database, trying migrate again') db.close() # Rename database folder @@ -331,6 +332,8 @@ class Database(object): log.info('Getting data took %s', time.time() - migrate_start) db = self.getDB() + if not db.opened: + return # Use properties properties = migrate_data['properties'] From cd0afd20e56571ab82f927e8dd5ba68e169a106e Mon Sep 17 00:00:00 2001 From: Ruud Date: Thu, 17 Jul 2014 23:58:51 +0200 Subject: [PATCH 23/37] Always reconnect Transmission. fix #3631 --- couchpotato/core/downloaders/transmission.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/couchpotato/core/downloaders/transmission.py b/couchpotato/core/downloaders/transmission.py index d941cca6..53ca9b8d 100644 --- a/couchpotato/core/downloaders/transmission.py +++ b/couchpotato/core/downloaders/transmission.py @@ -23,17 +23,14 @@ class Transmission(DownloaderBase): log = CPLog(__name__) trpc = None - def connect(self, reconnect = False): + def connect(self): # Load host from config and split out port. host = cleanHost(self.conf('host'), protocol = False).split(':') if not isInt(host[1]): log.error('Config properties are not filled in correctly, port is missing.') return False - if not self.trpc or reconnect: - self.trpc = TransmissionRPC(host[0], port = host[1], rpc_url = self.conf('rpc_url').strip('/ '), username = self.conf('username'), password = self.conf('password')) - - return self.trpc + self.trpc = TransmissionRPC(host[0], port = host[1], rpc_url = self.conf('rpc_url').strip('/ '), username = self.conf('username'), password = self.conf('password')) def download(self, data = None, media = None, filedata = None): if not media: media = {} @@ -88,7 +85,7 @@ class Transmission(DownloaderBase): return self.downloadReturnId(remote_torrent['torrent-added']['hashString']) def test(self): - if self.connect(True) and self.trpc.get_session(): + if self.connect() and self.trpc.get_session(): return True return False From 0771aeac3bbeebe9af58db7cd04504bb0ad6e310 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sun, 20 Jul 2014 16:20:12 +0200 Subject: [PATCH 24/37] Update nzbgeek api url --- couchpotato/core/media/_base/providers/nzb/newznab.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/couchpotato/core/media/_base/providers/nzb/newznab.py b/couchpotato/core/media/_base/providers/nzb/newznab.py index 495463bf..b1a8ec69 100644 --- a/couchpotato/core/media/_base/providers/nzb/newznab.py +++ b/couchpotato/core/media/_base/providers/nzb/newznab.py @@ -235,7 +235,7 @@ config = [{ }, { 'name': 'host', - 'default': 'api.nzb.su,api.dognzb.cr,nzbs.org,https://index.nzbgeek.info,https://www.nzbfinder.ws', + 'default': 'api.nzb.su,api.dognzb.cr,nzbs.org,https://api.nzbgeek.info,https://www.nzbfinder.ws', 'description': 'The hostname of your newznab provider', }, { From cd1dc39ef23d41cb018899c30f681b7f0dbbad22 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sun, 27 Jul 2014 21:14:31 +0200 Subject: [PATCH 25/37] Add bd50 torrentleech --- couchpotato/core/media/movie/providers/torrent/torrentleech.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/couchpotato/core/media/movie/providers/torrent/torrentleech.py b/couchpotato/core/media/movie/providers/torrent/torrentleech.py index 191ceba8..d72f4257 100644 --- a/couchpotato/core/media/movie/providers/torrent/torrentleech.py +++ b/couchpotato/core/media/movie/providers/torrent/torrentleech.py @@ -11,7 +11,7 @@ autoload = 'TorrentLeech' class TorrentLeech(MovieProvider, Base): cat_ids = [ - ([13], ['720p', '1080p']), + ([13], ['720p', '1080p', 'bd50']), ([8], ['cam']), ([9], ['ts', 'tc']), ([10], ['r5', 'scr']), From 7b6fa4f0e54cd0be7d818338b926e9329d0bf1d4 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sun, 27 Jul 2014 21:28:06 +0200 Subject: [PATCH 26/37] Transmission failed to download --- couchpotato/core/downloaders/transmission.py | 1 + 1 file changed, 1 insertion(+) diff --git a/couchpotato/core/downloaders/transmission.py b/couchpotato/core/downloaders/transmission.py index 53ca9b8d..409efaa0 100644 --- a/couchpotato/core/downloaders/transmission.py +++ b/couchpotato/core/downloaders/transmission.py @@ -31,6 +31,7 @@ class Transmission(DownloaderBase): return False self.trpc = TransmissionRPC(host[0], port = host[1], rpc_url = self.conf('rpc_url').strip('/ '), username = self.conf('username'), password = self.conf('password')) + return self.trpc def download(self, data = None, media = None, filedata = None): if not media: media = {} From 456563eab01447f31271db81b4951f767315ff25 Mon Sep 17 00:00:00 2001 From: Ruud Date: Mon, 28 Jul 2014 16:01:16 +0200 Subject: [PATCH 27/37] Update log post text --- couchpotato/core/plugins/log/static/log.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/couchpotato/core/plugins/log/static/log.js b/couchpotato/core/plugins/log/static/log.js index 11acb5ca..fcb7ed59 100644 --- a/couchpotato/core/plugins/log/static/log.js +++ b/couchpotato/core/plugins/log/static/log.js @@ -241,7 +241,7 @@ Running on: ...\n\ 'href': 'https://github.com/RuudBurger/CouchPotatoServer/blob/develop/contributing.md' }), new Element('span', { - 'text': ' before posting, then copy the text below' + 'text': ' before posting (I\'ll close if you don\'t), then copy the text below.' }) ), textarea = new Element('textarea', { From 69e3e36fae5071de3e88a3c53ef5123f90865273 Mon Sep 17 00:00:00 2001 From: Ruud Date: Mon, 28 Jul 2014 16:01:27 +0200 Subject: [PATCH 28/37] Update contribute --- contributing.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/contributing.md b/contributing.md index 821212c8..e95337d9 100644 --- a/contributing.md +++ b/contributing.md @@ -13,6 +13,8 @@ Lastly, for anything related to CouchPotato, feel free to stop by the [forum](ht ## Issues Issues are intended for reporting bugs and weird behaviour or suggesting improvements to CouchPotatoServer. Before you submit an issue, please go through the following checklist: + * FILL IN ALL THE FIELDS ASKED FOR, if you don't tell why you didn't or isn't relevant + * POST MORE THAN A SINGLE LINE, if you do, you'd better have a easy reproducable bug * Search through existing issues (*including closed issues!*) first: you might be able to get your answer there. * Double check your issue manually, because it could be an external issue. * Post logs with your issue: Without seeing what is going on, the developers can't reproduce the error. @@ -25,12 +27,14 @@ Before you submit an issue, please go through the following checklist: * What hardware / OS are you using and what are its limitations? For example: NAS can be slow and maybe have a different version of python installed than when you use CP on OS X or Windows. * Your issue might be marked with the "can't reproduce" tag. Don't ask why your issue was closed if it says so in the tag. * If you're running on a NAS (QNAP, Austor, Synology etc.) with pre-made packages, make sure these are set up to use our source repository (RuudBurger/CouchPotatoServer) and nothing else! + * Do not "bump" issues with "Any updates on this" or whatever. Yes I've seen it, you don't have to remind me of it. There will be an update when the code is done or I need information. If you feel the need to do so, you'd better have more info on the issue. The more relevant information you provide, the more likely that your issue will be resolved. +If you don't follow any of the checks above, I'll close the issue. If you are wondering why (and ask) I'll block you from posting new issues and the repo. ## Pull Requests Pull requests are intended for contributing code or documentation to the project. Before you submit a pull request, consider the following: * Make sure your pull request is made for the *develop* branch (or relevant feature branch). * Have you tested your PR? If not, why? - * Does your PR have any limitations we should know of? + * Does your PR have any limitations I should know of? * Is your PR up-to-date with the branch you're trying to push into? From bed94586041aac7c2fb0094e86f6341c96ae272d Mon Sep 17 00:00:00 2001 From: Ruud Burger Date: Mon, 28 Jul 2014 16:06:38 +0200 Subject: [PATCH 29/37] Update contributing.md --- contributing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contributing.md b/contributing.md index e95337d9..3bd42c00 100644 --- a/contributing.md +++ b/contributing.md @@ -13,8 +13,8 @@ Lastly, for anything related to CouchPotato, feel free to stop by the [forum](ht ## Issues Issues are intended for reporting bugs and weird behaviour or suggesting improvements to CouchPotatoServer. Before you submit an issue, please go through the following checklist: - * FILL IN ALL THE FIELDS ASKED FOR, if you don't tell why you didn't or isn't relevant - * POST MORE THAN A SINGLE LINE, if you do, you'd better have a easy reproducable bug + * **FILL IN ALL THE FIELDS ASKED FOR** + * **POST MORE THAN A SINGLE LINE LOG**, if you do, you'd better have a easy reproducable bug * Search through existing issues (*including closed issues!*) first: you might be able to get your answer there. * Double check your issue manually, because it could be an external issue. * Post logs with your issue: Without seeing what is going on, the developers can't reproduce the error. From e749d132cda342533c69a02ea648788cece5e752 Mon Sep 17 00:00:00 2001 From: Ruud Date: Mon, 28 Jul 2014 16:12:54 +0200 Subject: [PATCH 30/37] Better message --- couchpotato/core/plugins/log/static/log.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/couchpotato/core/plugins/log/static/log.js b/couchpotato/core/plugins/log/static/log.js index fcb7ed59..6cb683bc 100644 --- a/couchpotato/core/plugins/log/static/log.js +++ b/couchpotato/core/plugins/log/static/log.js @@ -241,7 +241,7 @@ Running on: ...\n\ 'href': 'https://github.com/RuudBurger/CouchPotatoServer/blob/develop/contributing.md' }), new Element('span', { - 'text': ' before posting (I\'ll close if you don\'t), then copy the text below.' + 'text': ' before posting (kittens die if you don\'t), then copy the text below.' }) ), textarea = new Element('textarea', { From 9fb348f3a4f9e360c5addbe958b6c9d7e5d0dea4 Mon Sep 17 00:00:00 2001 From: Ruud Date: Fri, 1 Aug 2014 13:21:15 +0200 Subject: [PATCH 31/37] Don't try to ignore None release --- couchpotato/core/plugins/release/main.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/couchpotato/core/plugins/release/main.py b/couchpotato/core/plugins/release/main.py index 7b47aa96..9def40d4 100644 --- a/couchpotato/core/plugins/release/main.py +++ b/couchpotato/core/plugins/release/main.py @@ -234,8 +234,9 @@ class Release(Plugin): db = get_db() try: - rel = db.get('id', id, with_doc = True) - self.updateStatus(id, 'available' if rel['status'] in ['ignored', 'failed'] else 'ignored') + if id: + rel = db.get('id', id, with_doc = True) + self.updateStatus(id, 'available' if rel['status'] in ['ignored', 'failed'] else 'ignored') return { 'success': True From 9f12fe263616319e1bd946e082d969f552cd9eee Mon Sep 17 00:00:00 2001 From: Ruud Date: Fri, 1 Aug 2014 13:23:00 +0200 Subject: [PATCH 32/37] Add edge meta for IE fix #3727 --- couchpotato/templates/index.html | 2 +- couchpotato/templates/login.html | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/couchpotato/templates/index.html b/couchpotato/templates/index.html index 0d8acbc1..729f1e3d 100644 --- a/couchpotato/templates/index.html +++ b/couchpotato/templates/index.html @@ -5,7 +5,7 @@ - + {% for url in fireEvent('clientscript.get_styles', as_html = True, location = 'front', single = True) %} {% end %} diff --git a/couchpotato/templates/login.html b/couchpotato/templates/login.html index 35626221..e33db2d7 100644 --- a/couchpotato/templates/login.html +++ b/couchpotato/templates/login.html @@ -4,22 +4,24 @@ + + {% for url in fireEvent('clientscript.get_styles', as_html = True, location = 'front', single = True) %} {% end %} - + {% for url in fireEvent('clientscript.get_scripts', as_html = True, location = 'front', single = True) %} {% end %} - + CouchPotato @@ -35,4 +37,4 @@ - \ No newline at end of file + From 4126007cac7ab518e22adfefb1f908b35ab1f1f3 Mon Sep 17 00:00:00 2001 From: Ruud Date: Fri, 1 Aug 2014 13:29:39 +0200 Subject: [PATCH 33/37] Don't download 0 seed torrents fix #3728 --- couchpotato/core/plugins/release/main.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/couchpotato/core/plugins/release/main.py b/couchpotato/core/plugins/release/main.py index 9def40d4..b1ecafd8 100644 --- a/couchpotato/core/plugins/release/main.py +++ b/couchpotato/core/plugins/release/main.py @@ -365,7 +365,7 @@ class Release(Plugin): let_through = False filtered_results = [] - # If a single release comes through the "wait for", let through all + # Filter out ignored and other releases we don't want for rel in results: if rel['status'] in ['ignored', 'failed']: @@ -380,6 +380,11 @@ class Release(Plugin): log.info('Ignored, size "%sMB" to low: %s', (rel['size'], rel['name'])) continue + if 'seeders' in rel and rel.get('seeders') <= 0: + log.info('Ignored, no seeders: %s', (rel['name'])) + continue + + # If a single release comes through the "wait for", let through all rel['wait_for'] = False if quality_custom.get('index') != 0 and quality_custom.get('wait_for', 0) > 0 and rel.get('age') <= quality_custom.get('wait_for', 0): rel['wait_for'] = True From accf19bb26e4ab8758727232a1547df07bf5cfb3 Mon Sep 17 00:00:00 2001 From: Ruud Date: Fri, 1 Aug 2014 13:33:59 +0200 Subject: [PATCH 34/37] Different log level --- couchpotato/core/plugins/base.py | 2 +- couchpotato/core/plugins/renamer.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/couchpotato/core/plugins/base.py b/couchpotato/core/plugins/base.py index bc66123f..2c7f9e13 100644 --- a/couchpotato/core/plugins/base.py +++ b/couchpotato/core/plugins/base.py @@ -157,7 +157,7 @@ class Plugin(object): os.rmdir(full_path) except: if show_error: - log.error('Couldn\'t remove empty directory %s: %s', (full_path, traceback.format_exc())) + log.info2('Couldn\'t remove directory %s: %s', (full_path, traceback.format_exc())) try: os.rmdir(folder) diff --git a/couchpotato/core/plugins/renamer.py b/couchpotato/core/plugins/renamer.py index 9f6792ab..ad72086e 100644 --- a/couchpotato/core/plugins/renamer.py +++ b/couchpotato/core/plugins/renamer.py @@ -807,7 +807,7 @@ Remove it if you want it to be renamed (again, or at least let it try again) if os.name == 'nt' and self.conf('ntfs_permission'): os.popen('icacls "' + dest + '"* /reset /T') except: - log.error('Failed setting permissions for file: %s, %s', (dest, traceback.format_exc(1))) + log.debug('Failed setting permissions for file: %s, %s', (dest, traceback.format_exc(1))) except: log.error('Couldn\'t move file "%s" to "%s": %s', (old, dest, traceback.format_exc())) raise From dd7de31e9f5c5f81dc7c1fede40da30672d1010a Mon Sep 17 00:00:00 2001 From: Ruud Date: Thu, 14 Aug 2014 21:28:17 +0200 Subject: [PATCH 35/37] Update TorrentShack url fix #3797 --- .../media/_base/providers/torrent/torrentshack.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/couchpotato/core/media/_base/providers/torrent/torrentshack.py b/couchpotato/core/media/_base/providers/torrent/torrentshack.py index 1af7e552..226993cc 100644 --- a/couchpotato/core/media/_base/providers/torrent/torrentshack.py +++ b/couchpotato/core/media/_base/providers/torrent/torrentshack.py @@ -13,12 +13,12 @@ log = CPLog(__name__) class Base(TorrentProvider): urls = { - 'test': 'https://torrentshack.net/', - 'login': 'https://torrentshack.net/login.php', - 'login_check': 'https://torrentshack.net/inbox.php', - 'detail': 'https://torrentshack.net/torrent/%s', - 'search': 'https://torrentshack.net/torrents.php?action=advanced&searchstr=%s&scene=%s&filter_cat[%d]=1', - 'download': 'https://torrentshack.net/%s', + 'test': 'http://torrentshack.eu/', + 'login': 'http://torrentshack.eu/login.php', + 'login_check': 'http://torrentshack.eu/inbox.php', + 'detail': 'http://torrentshack.eu/torrent/%s', + 'search': 'http://torrentshack.eu/torrents.php?action=advanced&searchstr=%s&scene=%s&filter_cat[%d]=1', + 'download': 'http://torrentshack.eu/%s', } http_time_between_calls = 1 # Seconds @@ -80,7 +80,7 @@ config = [{ 'tab': 'searcher', 'list': 'torrent_providers', 'name': 'TorrentShack', - 'description': 'TorrentShack', + 'description': 'TorrentShack', 'wizard': True, 'icon': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABmElEQVQoFQXBzY2cVRiE0afqvd84CQiAnxWWtyxsS6ThINBYg2Dc7mZBMEjE4mzs6e9WcY5+ePNuVFJJodQAoLo+SaWCy9rcV8cmjah3CI6iYu7oRU30kE5xxELRfamklY3k1NL19sSm7vPzP/ZdNZzKVDaY2sPZJBh9fv5ITrmG2+Vp4e1sPchVqTCQZJnVXi+/L4uuAJGly1+Pw8CprLbi8Om7tbT19/XRqJUk11JP9uHj9ulxhXbvJbI9qJvr5YkGXFG2IBT8tXczt+sfzDZCp3765f3t9tHEHGEDACma77+8o4oATKk+/PfW9YmHruRFjWoVSFsVsGu1YSKq6Oc37+n98unPZSRlY7vsKDqN+92X3yR9+PdXee3iJNKMStqdcZqoTJbUSi5JOkpfRlhSI0mSpEmCFKoU7FqSNOLAk54uGwCStMUCgLrVic62g7oDoFmmdI+P3S0pDe1xvDqb6XrZqbtzShWNoh9fv/XQHaDdM9OqrZi2M7M3UrB2vlkPS1IbdEBk7UiSoD6VlZ6aKWer4aH4f/AvKoHUTjuyAAAAAElFTkSuQmCC', 'options': [ From bdadd00d93247ec364b820d1e1d66a90118f0b69 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sat, 16 Aug 2014 12:44:52 +0200 Subject: [PATCH 36/37] Don't add & on url creation --- couchpotato/core/media/_base/providers/nzb/newznab.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/couchpotato/core/media/_base/providers/nzb/newznab.py b/couchpotato/core/media/_base/providers/nzb/newznab.py index b1a8ec69..81622ade 100644 --- a/couchpotato/core/media/_base/providers/nzb/newznab.py +++ b/couchpotato/core/media/_base/providers/nzb/newznab.py @@ -45,7 +45,7 @@ class Base(NZBProvider, RSS): def _searchOnHost(self, host, media, quality, results): query = self.buildUrl(media, host) - url = '%s&%s' % (self.getUrl(host['host']), query) + url = '%s%s' % (self.getUrl(host['host']), query) nzbs = self.getRSSData(url, cache_timeout = 1800, headers = {'User-Agent': Env.getIdentifier()}) for nzb in nzbs: @@ -83,7 +83,7 @@ class Base(NZBProvider, RSS): try: # Get details for extended description to retrieve passwords query = self.buildDetailsUrl(nzb_id, host['api_key']) - url = '%s&%s' % (self.getUrl(host['host']), query) + url = '%s%s' % (self.getUrl(host['host']), query) nzb_details = self.getRSSData(url, cache_timeout = 1800, headers = {'User-Agent': Env.getIdentifier()})[0] description = self.getTextElement(nzb_details, 'description') From e3414fe91f8c155adecf13f4754448508995f3e0 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sat, 16 Aug 2014 13:29:34 +0200 Subject: [PATCH 37/37] Remove movie if no releases are left after delete --- couchpotato/core/media/_base/media/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/couchpotato/core/media/_base/media/main.py b/couchpotato/core/media/_base/media/main.py index ee9e6ccc..036badca 100644 --- a/couchpotato/core/media/_base/media/main.py +++ b/couchpotato/core/media/_base/media/main.py @@ -415,7 +415,7 @@ class MediaPlugin(MediaBase): db.delete(release) total_deleted += 1 - if (total_releases == total_deleted and media['status'] != 'active') or (total_releases == 0 and not new_media_status) or (not new_media_status and delete_from == 'late'): + if (total_releases == total_deleted) or (total_releases == 0 and not new_media_status) or (not new_media_status and delete_from == 'late'): db.delete(media) deleted = True elif new_media_status: