diff --git a/TODO.txt b/TODO.txt index b18ae2199fbad4053a364723d07e6ab03f2f09ec..084a6bcc9237e271abd4249a00a59e673fd35d01 100644 --- a/TODO.txt +++ b/TODO.txt @@ -11,9 +11,6 @@ -Add 'add show and add another show' button to add show page -Change the hardcoded global ignore words to optional -2014-10-08 -VAdd login page for http auth as opposed to browser dialog box - 2014-10-13 -Fix broken backlog -Fix season searches diff --git a/gui/slick/images/donate.jpg b/gui/slick/images/donate.jpg index 67b8c319a2b1c6b1b95380f0b9d49e16e04e6b2a..7c4c7d7268b20bd66a2ce5e3910fd893b9d9e0de 100644 Binary files a/gui/slick/images/donate.jpg and b/gui/slick/images/donate.jpg differ diff --git a/gui/slick/interfaces/default/config_general.tmpl b/gui/slick/interfaces/default/config_general.tmpl index 792c9856be01cd3bffb291728fce373d38830997..aff6a9b2e5d01713a31173b8c3162c429a0bc9c6 100644 --- a/gui/slick/interfaces/default/config_general.tmpl +++ b/gui/slick/interfaces/default/config_general.tmpl @@ -93,7 +93,21 @@ </span> </label> </div> - + <div class="field-pair"> + <label for="default_page"> + <span class="component-title">Initial page</span> + <span class="component-desc"> + <select id="default_page" name="default_page" class="form-control input-sm"> + <option value="news" #if $sickbeard.DEFAULT_PAGE == 'news' then 'selected="selected"' else ''#>News</option> + <option value="home" #if $sickbeard.DEFAULT_PAGE == 'home' then 'selected="selected"' else ''#>Home</option> + <option value="comingEpisodes" #if $sickbeard.DEFAULT_PAGE == 'comingEpisodes' then 'selected="selected"' else ''#>Coming Episodes</option> + <option value="history" #if $sickbeard.DEFAULT_PAGE == 'history' then 'selected="selected"' else ''#>History</option> + <option value="IRC" #if $sickbeard.DEFAULT_PAGE == 'IRC' then 'selected="selected"' else ''#>IRC</option> + </select> + <span>when launching SickRage interface</span> + </span> + </label> + </div> <div class="field-pair"> <label for="showupdate_hour"> <span class="component-title">When to update shows</span> @@ -309,6 +323,9 @@ <span class="component-desc"> <input type="checkbox" name="filter_row" id="filter_row" #if $sickbeard.FILTER_ROW == True then 'checked="checked"' else ''#/> <p>Add a filter row to the show display on the home page</p> + <p>Supports =, >, >=, <=, <, xx to yy , xx - yy</p> + <p><b>Note:</b> =, >, >=, <=, < should be first, followed by a space, then the value.</p> + <p>Examples: '> 90', '= 100', '0 to 99'</p> </span> </label> </div> diff --git a/gui/slick/interfaces/default/manage_episodeStatuses.tmpl b/gui/slick/interfaces/default/manage_episodeStatuses.tmpl index 78a67891baeee3bebf6e17555f5248ddfbec30f8..4f7a06fc3cc4f3718d9e55ae6bc64f5350605134 100644 --- a/gui/slick/interfaces/default/manage_episodeStatuses.tmpl +++ b/gui/slick/interfaces/default/manage_episodeStatuses.tmpl @@ -61,6 +61,7 @@ $statusList.remove($int($whichStatus)) $statusList.append($common.FAILED) #end if +<select> #for $curStatus in $statusList: <option value="$curStatus">$common.statusStrings[$curStatus]</option> #end for diff --git a/gui/slick/interfaces/default/manage_subtitleMissed.tmpl b/gui/slick/interfaces/default/manage_subtitleMissed.tmpl index f5962e1db98d2a576aaa77fa37cc6eba1707eb09..e3cf6050c6e01323d14b7e02cc08369488149cec 100644 --- a/gui/slick/interfaces/default/manage_subtitleMissed.tmpl +++ b/gui/slick/interfaces/default/manage_subtitleMissed.tmpl @@ -55,7 +55,7 @@ Download missed subtitles for selected episodes <input class="btn btn-inline" ty #for $cur_indexer_id in $sorted_show_ids: <tr id="$cur_indexer_id"> <th><input type="checkbox" class="allCheck" id="allCheck-$cur_indexer_id" name="$cur_indexer_id-all" checked="checked" /></th> - <th colspan="3" style="width: 100%; text-align: left;"><a class="whitelink" href="$sbRoot/home/displayShow?show=$cur_indexer_id">$show_names[$cur_indexer_id]</a> ($ep_counts[$cur_indexer_id]) <input type="button" class="get_more_eps btn" id="$cur_indexer_id" value="Expand" /></th> + <th colspan="3" style="width: 100%; text-align: left;"><a class="whitelink" href="$sbRoot/home/displayShow?show=$cur_indexer_id">$show_names[$cur_indexer_id]</a> ($ep_counts[$cur_indexer_id]) <input type="button" class="pull-right get_more_eps btn" id="$cur_indexer_id" value="Expand" /></th> </tr> #end for </table> diff --git a/gui/slick/interfaces/default/restart_bare.tmpl b/gui/slick/interfaces/default/restart_bare.tmpl index f48b50d69551236631326b8704049f864d0ac587..ac795e04f8a8a035131f5f61158a745fbd255e42 100644 --- a/gui/slick/interfaces/default/restart_bare.tmpl +++ b/gui/slick/interfaces/default/restart_bare.tmpl @@ -18,11 +18,12 @@ sbHttpPort = "$curSBHttpPort"; sbHttpsEnabled = "$curSBHttpsEnabled"; sbHandleReverseProxy = "$curSBHandleReverseProxy"; sbHost = "$curSBHost"; +sbDefaultPage = "$sbDefaultPage"; //--> </script> <script type="text/javascript" src="$sbRoot/js/lib/jquery-1.11.2.min.js?$sbPID"></script> -<script type="text/javascript" src="$sbRoot/js/restart.js?$sbPID"></script> +<script type="text/javascript" src="$sbRoot/js/restart.js?$sbPID&$sbDefaultPage"></script> #set themeSpinner = '-dark' if 'dark' == themeSpinner else '' <h2>Performing Restart</h2> @@ -41,7 +42,7 @@ Waiting for SickRage to start again: </div> <div id="refresh_message" style="display: none;"> -Loading the home page: +Loading the default page: <img src="$sbRoot/images/loading16${themeSpinner}.gif" height="16" width="16" id="refresh_loading" /> </div> diff --git a/gui/slick/js/manageSubtitleMissed.js b/gui/slick/js/manageSubtitleMissed.js index 262ecbb282e38fbba962c0813ac97ca310d6ae03..08c78d890b2b38342d10ab8ac1394d285a6b2d1f 100644 --- a/gui/slick/js/manageSubtitleMissed.js +++ b/gui/slick/js/manageSubtitleMissed.js @@ -5,21 +5,21 @@ $(document).ready(function() { var checked = ' checked'; else var checked = ''; - + var row = ''; - row += ' <tr class="good">'; + row += ' <tr class="good show-' + indexer_id + '">'; row += ' <td align="center"><input type="checkbox" class="'+indexer_id+'-epcheck" name="'+indexer_id+'-'+season+'x'+episode+'"'+checked+'></td>'; row += ' <td style="width: 1%;">'+season+'x'+episode+'</td>'; row += ' <td>'+name+'</td>'; row += ' <td style="float: right;">'; subtitles = subtitles.split(',') - for (i in subtitles) + for (var i in subtitles) { row += ' <img src="/images/subtitles/flags/'+subtitles[i]+'.png" width="16" height="11" alt="'+subtitles[i]+'" /> '; } row += ' </td>'; row += ' </tr>' - + return row; } @@ -32,21 +32,35 @@ $(document).ready(function() { var cur_indexer_id = $(this).attr('id'); var checked = $('#allCheck-'+cur_indexer_id).prop('checked'); var last_row = $('tr#'+cur_indexer_id); - - $.getJSON(sbRoot+'/manage/showSubtitleMissed', + var clicked = $(this).attr('data-clicked'); + var action = $(this).attr('value'); + + if (!clicked) { + $.getJSON(sbRoot + '/manage/showSubtitleMissed', { indexer_id: cur_indexer_id, whichSubs: $('#selectSubLang').val() }, function (data) { - $.each(data, function(season,eps){ + $.each(data, function(season, eps) { $.each(eps, function(episode, data) { //alert(season+'x'+episode+': '+name); last_row.after(make_row(cur_indexer_id, season, episode, data.name, data.subtitles, checked)); }); }); }); - $(this).hide(); + $(this).attr('data-clicked', 1); + $(this).prop('value', 'Collapse'); + } else { + if (action === 'Collapse') { + $('table tr').filter('.show-' + cur_indexer_id).hide(); + $(this).prop('value', 'Expand'); + } + else if (action === 'Expand') { + $('table tr').filter('.show-' + cur_indexer_id).show(); + $(this).prop('value', 'Collapse'); + } + } }); // selects all visible episode checkboxes. @@ -68,5 +82,4 @@ $(document).ready(function() { this.checked = false; }); }); - }); diff --git a/gui/slick/js/restart.js b/gui/slick/js/restart.js index 1ee32d09fa3b3a36e361842f94e5586e0cf0f30e..ae7fb75b6007ecea0ddc6083f7d0afe1f14304dd 100644 --- a/gui/slick/js/restart.js +++ b/gui/slick/js/restart.js @@ -38,7 +38,7 @@ $(document).ready(function() { $('#restart_loading').hide(); $('#restart_success').show(); $('#refresh_message').show(); - setTimeout(function(){window.location = sbRoot + '/home/';}, 5000); + setTimeout(function(){window.location = sbRoot + '/' + sbDefaultPage + '/';}, 5000); } } diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py index 603f19aeee8489473798583e3512faa97018afbc..e81ec3161a90f36e560842a8edbc92ae8c765fb0 100644 --- a/sickbeard/__init__.py +++ b/sickbeard/__init__.py @@ -184,6 +184,7 @@ TRASH_ROTATE_LOGS = False SORT_ARTICLE = False DEBUG = False DISPLAY_ALL_SEASONS = True +DEFAULT_PAGE = 'home' USE_LISTVIEW = False @@ -585,7 +586,7 @@ def initialize(consoleLogging=True): POSTER_SORTBY, POSTER_SORTDIR, \ METADATA_WDTV, METADATA_TIVO, METADATA_MEDE8ER, IGNORE_WORDS, REQUIRE_WORDS, CALENDAR_UNPROTECTED, NO_RESTART, CREATE_MISSING_SHOW_DIRS, \ ADD_SHOWS_WO_DIR, USE_SUBTITLES, SUBTITLES_LANGUAGES, SUBTITLES_DIR, SUBTITLES_SERVICES_LIST, SUBTITLES_SERVICES_ENABLED, SUBTITLES_HISTORY, SUBTITLES_FINDER_FREQUENCY, SUBTITLES_MULTI, EMBEDDED_SUBTITLES_ALL, SUBTITLES_EXTRA_SCRIPTS, subtitlesFinderScheduler, \ - USE_FAILED_DOWNLOADS, DELETE_FAILED, ANON_REDIRECT, LOCALHOST_IP, TMDB_API_KEY, DEBUG, PROXY_SETTING, PROXY_INDEXERS, \ + USE_FAILED_DOWNLOADS, DELETE_FAILED, ANON_REDIRECT, LOCALHOST_IP, TMDB_API_KEY, DEBUG, DEFAULT_PAGE, PROXY_SETTING, PROXY_INDEXERS, \ AUTOPOSTPROCESSER_FREQUENCY, SHOWUPDATE_HOUR, DEFAULT_AUTOPOSTPROCESSER_FREQUENCY, MIN_AUTOPOSTPROCESSER_FREQUENCY, \ ANIME_DEFAULT, NAMING_ANIME, ANIMESUPPORT, USE_ANIDB, ANIDB_USERNAME, ANIDB_PASSWORD, ANIDB_USE_MYLIST, \ ANIME_SPLIT_HOME, SCENE_DEFAULT, DOWNLOAD_URL, BACKLOG_DAYS, GIT_ORG, GIT_REPO, GIT_USERNAME, GIT_PASSWORD, \ @@ -630,6 +631,8 @@ def initialize(consoleLogging=True): # debugging DEBUG = bool(check_setting_int(CFG, 'General', 'debug', 0)) + + DEFAULT_PAGE = check_setting_str(CFG, 'General', 'default_page', 'home') ACTUAL_LOG_DIR = check_setting_str(CFG, 'General', 'log_dir', 'Logs') LOG_DIR = os.path.normpath(os.path.join(DATA_DIR, ACTUAL_LOG_DIR)) @@ -1650,6 +1653,7 @@ def save_config(): new_config['General']['anon_redirect'] = ANON_REDIRECT new_config['General']['api_key'] = API_KEY new_config['General']['debug'] = int(DEBUG) + new_config['General']['default_page'] = DEFAULT_PAGE new_config['General']['enable_https'] = int(ENABLE_HTTPS) new_config['General']['https_cert'] = HTTPS_CERT new_config['General']['https_key'] = HTTPS_KEY diff --git a/sickbeard/helpers.py b/sickbeard/helpers.py index fa5a829f4c4787e1db2cc0e8fd34de8e69b441f9..7ae880ce8325f35731623ce8533c9e71fca810a6 100644 --- a/sickbeard/helpers.py +++ b/sickbeard/helpers.py @@ -142,6 +142,9 @@ def remove_non_release_groups(name): '^\{ www\.SceneTime\.com \} - ': 'searchre', '^\[ www\.TorrentDay\.com \] - ': 'searchre', '^\[ www\.Cpasbien\.pw \] ': 'searchre', + '^\[ www\.Cpasbien\.com \] ': 'searchre', + '^\[www\.Cpasbien\.com\] ': 'searchre', + '^\[www\.Cpasbien\.pe\] ': 'searchre', } _name = name @@ -427,7 +430,7 @@ def hardlinkFile(srcFile, destFile): try: ek.ek(link, srcFile, destFile) fixSetGroupID(destFile) - except Exception, e: + except Exception as e: logger.log(u"Failed to create hardlink of " + srcFile + " at " + destFile + ": " + ex(e) + ". Copying instead", logger.ERROR) copyFile(srcFile, destFile) @@ -459,16 +462,16 @@ def make_dirs(path): parents """ - logger.log(u"Checking if the path " + path + " already exists", logger.DEBUG) + logger.log(u"Checking if the path %s already exists" % path, logger.DEBUG) if not ek.ek(os.path.isdir, path): # Windows, create all missing folders if os.name == 'nt' or os.name == 'ce': try: - logger.log(u"Folder " + path + " didn't exist, creating it", logger.DEBUG) + logger.log(u"Folder %s didn't exist, creating it" % path, logger.DEBUG) ek.ek(os.makedirs, path) - except (OSError, IOError), e: - logger.log(u"Failed creating " + path + " : " + ex(e), logger.ERROR) + except (OSError, IOError) as e: + logger.log(u"Failed creating %s : %s" % (path, ex(e)), logger.ERROR) return False # not Windows, create all missing folders and set permissions @@ -485,14 +488,14 @@ def make_dirs(path): continue try: - logger.log(u"Folder " + sofar + " didn't exist, creating it", logger.DEBUG) + logger.log(u"Folder %s didn't exist, creating it" % sofar, logger.DEBUG) ek.ek(os.mkdir, sofar) # use normpath to remove end separator, otherwise checks permissions against itself chmodAsParent(ek.ek(os.path.normpath, sofar)) # do the library update for synoindex notifiers.synoindex_notifier.addFolder(sofar) - except (OSError, IOError), e: - logger.log(u"Failed creating " + sofar + " : " + ex(e), logger.ERROR) + except (OSError, IOError) as e: + logger.log(u"Failed creating %s : %s" % (sofar, ex(e)), logger.ERROR) return False return True @@ -533,10 +536,10 @@ def rename_ep_file(cur_path, new_path, old_path_length=0): # move the file try: - logger.log(u"Renaming file from " + cur_path + " to " + new_path) + logger.log(u"Renaming file from %s to %s" % (cur_path, new_path)) ek.ek(shutil.move, cur_path, new_path) - except (OSError, IOError), e: - logger.log(u"Failed renaming " + cur_path + " to " + new_path + ": " + ex(e), logger.ERROR) + except (OSError, IOError) as e: + logger.log(u"Failed renaming %s to %s : %s" % (cur_path, new_path, ex(e)), logger.ERROR) return False # clean up any old folders that are empty @@ -571,7 +574,7 @@ def delete_empty_folders(check_empty_dir, keep_dir=None): ek.ek(os.rmdir, check_empty_dir) # do the library update for synoindex notifiers.synoindex_notifier.deleteFolder(check_empty_dir) - except OSError, e: + except OSError as e: logger.log(u"Unable to delete " + check_empty_dir + ": " + repr(e) + " / " + str(e), logger.WARNING) break check_empty_dir = ek.ek(os.path.dirname, check_empty_dir) @@ -780,7 +783,7 @@ def create_https_certificates(ssl_cert, ssl_key): from OpenSSL import crypto # @UnresolvedImport from certgen import createKeyPair, createCertRequest, createCertificate, TYPE_RSA, \ serial # @UnresolvedImport - except Exception, e: + except Exception as e: logger.log(u"pyopenssl module missing, please install for https access", logger.WARNING) return False @@ -811,22 +814,22 @@ def backupVersionedFile(old_file, version): while not ek.ek(os.path.isfile, new_file): if not ek.ek(os.path.isfile, old_file): - logger.log(u"Not creating backup, " + old_file + " doesn't exist", logger.DEBUG) + logger.log(u"Not creating backup, %s doesn't exist" % old_file, logger.DEBUG) break try: - logger.log(u"Trying to back up " + old_file + " to " + new_file, logger.DEBUG) + logger.log(u"Trying to back up %s to %s" % (old_file, new_file), logger.DEBUG) shutil.copy(old_file, new_file) logger.log(u"Backup done", logger.DEBUG) break - except Exception, e: - logger.log(u"Error while trying to back up " + old_file + " to " + new_file + " : " + ex(e), logger.WARNING) + except Exception as e: + logger.log(u"Error while trying to back up %s to %s : %s" % (old_file, new_file, ex(e)), logger.WARNING) numTries += 1 time.sleep(1) logger.log(u"Trying again.", logger.DEBUG) if numTries >= 10: - logger.log(u"Unable to back up " + old_file + " to " + new_file + " please do it manually.", logger.ERROR) + logger.log(u"Unable to back up %s to %s please do it manually." % (old_file, new_file), logger.ERROR) return False return True @@ -839,7 +842,7 @@ def restoreVersionedFile(backup_file, version): restore_file = new_file + '.' + 'v' + str(version) if not ek.ek(os.path.isfile, new_file): - logger.log(u"Not restoring, " + new_file + " doesn't exist", logger.DEBUG) + logger.log(u"Not restoring, %s doesn't exist" % new_file, logger.DEBUG) return False try: @@ -847,7 +850,7 @@ def restoreVersionedFile(backup_file, version): u"Trying to backup " + new_file + " to " + new_file + "." + "r" + str(version) + " before restoring backup", logger.DEBUG) shutil.move(new_file, new_file + '.' + 'r' + str(version)) - except Exception, e: + except Exception as e: logger.log( u"Error while trying to backup DB file " + restore_file + " before proceeding with restore: " + ex(e), logger.WARNING) @@ -863,7 +866,7 @@ def restoreVersionedFile(backup_file, version): shutil.copy(restore_file, new_file) logger.log(u"Restore done", logger.DEBUG) break - except Exception, e: + except Exception as e: logger.log(u"Error while trying to restore " + restore_file + ": " + ex(e), logger.WARNING) numTries += 1 time.sleep(1) @@ -1258,9 +1261,9 @@ def touchFile(fname, atime=None): if e.errno == errno.ENOSYS: logger.log(u"File air date stamping not available on your OS", logger.DEBUG) elif e.errno == errno.EACCES: - logger.log(u"File air date stamping failed(Permission denied). Check permissions for file: {0}".format(fname), logger.ERROR) + logger.log(u"File air date stamping failed(Permission denied). Check permissions for file: %s" % fname, logger.ERROR) else: - logger.log(u"File air date stamping failed. The error is: {0} and the message is: {1}.".format(e.errno, e.strerror), logger.ERROR) + logger.log(u"File air date stamping failed. The error is: %s." % ex(e), logger.ERROR) pass return False @@ -1344,8 +1347,6 @@ def headURL(url, params=None, headers={}, timeout=30, session=None, json=False, logger.log(u"Requested headURL " + url + " returned status code is " + str( resp.status_code) + ': ' + codeDescription(resp.status_code), logger.DEBUG) return False - else: - logger.log(u"Requested headURL " + url + " returned status code is " + str(resp.status_code) , logger.DEBUG) if proxyGlypeProxySSLwarning is not None: if re.search('The site you are attempting to browse is on a secure connection', resp.text): @@ -1358,20 +1359,21 @@ def headURL(url, params=None, headers={}, timeout=30, session=None, json=False, return resp.status_code == 200 - except requests.exceptions.HTTPError, e: - logger.log(u"HTTP error in headURL {0}. Error: {1}".format(url,e.errno), logger.WARNING) + except requests.exceptions.HTTPError as e: + logger.log(u"HTTP error in headURL %s. Error: %s" % (url, ex(e)), logger.WARNING) pass - except requests.exceptions.ConnectionError, e: - logger.log(u"Connection error to headURL {0}. Error: {1}".format(url,e.message), logger.WARNING) + except requests.exceptions.ConnectionError as e: + logger.log(u"Connection error in headURL %s. Error: %s " % (url, ex(e)), logger.WARNING) pass - except requests.exceptions.Timeout, e: - logger.log(u"Connection timed out accessing headURL {0}. Error: {1}".format(url,e.message), logger.WARNING) + except requests.exceptions.Timeout as e: + logger.log(u"Connection timed out accessing headURL %s. Error: %s" % (url, ex(e)), logger.WARNING) pass except requests.exceptions.ContentDecodingError: logger.log(u"Content-Encoding was gzip, but content was not compressed. headURL: %s" % url, logger.DEBUG) + logger.log(traceback.format_exc(), logger.DEBUG) pass except Exception as e: - logger.log(u"Unknown exception in headURL {0}. Error: {1}".format(url,e.message), logger.WARNING) + logger.log(u"Unknown exception in headURL %s. Error: %s" % (url, ex(e)), logger.WARNING) logger.log(traceback.format_exc(), logger.WARNING) pass @@ -1398,8 +1400,6 @@ def getURL(url, post_data=None, params={}, headers={}, timeout=30, session=None, logger.log(u"Requested getURL " + url + " returned status code is " + str( resp.status_code) + ': ' + codeDescription(resp.status_code), logger.DEBUG) return - else: - logger.log(u"Requested getURL " + url + " returned status code is " + str(resp.status_code), logger.DEBUG) if proxyGlypeProxySSLwarning is not None: if re.search('The site you are attempting to browse is on a secure connection', resp.text): @@ -1410,20 +1410,21 @@ def getURL(url, post_data=None, params={}, headers={}, timeout=30, session=None, resp.status_code) + ': ' + codeDescription(resp.status_code), logger.DEBUG) return - except requests.exceptions.HTTPError, e: - logger.log(u"HTTP error in getURL {0}. Error: {1}".format(url,e.errno), logger.WARNING) + except requests.exceptions.HTTPError as e: + logger.log(u"HTTP error in getURL %s Error: %s" % (url, ex(e)), logger.WARNING) return - except requests.exceptions.ConnectionError, e: - logger.log(u"Connection error to getURL {0}. Error: {1}".format(url,e.message), logger.WARNING) + except requests.exceptions.ConnectionError as e: + logger.log(u"Connection error to getURL %s Error: %s" % (url, ex(e)), logger.WARNING) return - except requests.exceptions.Timeout, e: - logger.log(u"Connection timed out accessing getURL {0}. Error: {1}".format(url,e.message), logger.WARNING) + except requests.exceptions.Timeout as e: + logger.log(u"Connection timed out accessing getURL %s Error: %s" % (url, ex(e)), logger.WARNING) return except requests.exceptions.ContentDecodingError: logger.log(u"Content-Encoding was gzip, but content was not compressed. getURL: %s" % url, logger.DEBUG) + logger.log(traceback.format_exc(), logger.DEBUG) return except Exception as e: - logger.log(u"Unknown exception in getURL {0}. Error: {1}".format(url,e.message), logger.WARNING) + logger.log(u"Unknown exception in getURL %s Error: %s" % (url, ex(e)), logger.WARNING) logger.log(traceback.format_exc(), logger.WARNING) return @@ -1453,19 +1454,19 @@ def download_file(url, filename, session=None, headers={}): except: logger.log(u"Problem setting permissions or writing file to: %s" % filename, logger.WARNING) - except requests.exceptions.HTTPError, e: + except requests.exceptions.HTTPError as e: _remove_file_failed(filename) - logger.log(u"HTTP error " + str(e.errno) + " while loading download URL " + url, logger.WARNING) + logger.log(u"HTTP error " + ex(e) + " while loading download URL " + url, logger.WARNING) return False - except requests.exceptions.ConnectionError, e: + except requests.exceptions.ConnectionError as e: _remove_file_failed(filename) - logger.log(u"Connection error " + str(e.message) + " while loading download URL " + url, logger.WARNING) + logger.log(u"Connection error " + ex(e) + " while loading download URL " + url, logger.WARNING) return False - except requests.exceptions.Timeout, e: + except requests.exceptions.Timeout as e: _remove_file_failed(filename) - logger.log(u"Connection timed out " + str(e.message) + " while loading download URL " + url, logger.WARNING) + logger.log(u"Connection timed out " + ex(e) + " while loading download URL " + url, logger.WARNING) return False - except EnvironmentError, e: + except EnvironmentError as e: _remove_file_failed(filename) logger.log(u"Unable to save the file: " + ex(e), logger.WARNING) return False @@ -1486,7 +1487,7 @@ def get_size(start_path='.'): try: total_size += ek.ek(os.path.getsize, fp) except OSError as e: - logger.log('Unable to get size for file {filePath}. Error msg is: {errorMsg}'.format(filePath=fp, errorMsg=str(e)), logger.ERROR) + logger.log('Unable to get size for file %s Error: %s' % (fp, ex(e)), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) return total_size diff --git a/sickbeard/processTV.py b/sickbeard/processTV.py index 0e7577317af58bc71f0994d9422283b5172edb82..aebf820a04efb90bf5d2a5070bd42d5b43058fe8 100644 --- a/sickbeard/processTV.py +++ b/sickbeard/processTV.py @@ -318,7 +318,7 @@ def validateDir(path, dirName, nzbNameOriginal, failed, result): os.path.realpath, sqlShow["location"]).lower(): result.output += logHelper( u"Cannot process an episode that's already been moved to its show dir, skipping " + dirName, - logger.ERROR) + logger.WARNING) return False # Get the videofile list for the next checks diff --git a/sickbeard/providers/btn.py b/sickbeard/providers/btn.py index 4a2c6aaaeb5b3d79c4ed3788057ddfbe884bf5cd..1ca0085ab3806dfe5c1588e99471009a333cbe7d 100644 --- a/sickbeard/providers/btn.py +++ b/sickbeard/providers/btn.py @@ -33,7 +33,7 @@ from sickbeard.exceptions import ex, AuthException from sickbeard.common import MULTI_EP_RESULT, SEASON_RESULT, USER_AGENT from sickbeard import db from sickbeard.name_parser.parser import NameParser, InvalidNameException, InvalidShowException -from sickbeard.common import Quality +from sickbeard.common import Quality, cpu_presets import jsonrpclib from datetime import datetime @@ -144,6 +144,7 @@ class BTNProvider(generic.TorrentProvider): try: parsedJSON = server.getTorrents(apikey, params, int(results_per_page), int(offset)) + time.sleep(cpu_presets[sickbeard.CPU_PRESET]) except jsonrpclib.jsonrpc.ProtocolError, error: if error.message == 'Call Limit Exceeded': diff --git a/sickbeard/providers/newznab.py b/sickbeard/providers/newznab.py index 501331fd7e20c365278d256451914fcc213f06d1..865bf64ed39f26683e65c54c264f300c3e5e614d 100644 --- a/sickbeard/providers/newznab.py +++ b/sickbeard/providers/newznab.py @@ -149,7 +149,7 @@ class NewznabProvider(generic.NZBProvider): rid = helpers.mapIndexersToShow(ep_obj.show)[2] if rid: cur_params['rid'] = rid - elif 'rid' in params: + elif 'rid' in cur_params: cur_params.pop('rid') # add new query strings for exceptions diff --git a/sickbeard/scene_numbering.py b/sickbeard/scene_numbering.py index 0887278d41adc44b3dd7393cf9d3607336aca63a..686729c6ab187dedf12d526c2d2545b6d3fae2e8 100644 --- a/sickbeard/scene_numbering.py +++ b/sickbeard/scene_numbering.py @@ -463,16 +463,12 @@ def xem_refresh(indexer_id, indexer, force=False): @param indexer_id: int """ - if indexer_id is None: + if not indexer_id or indexer_id < 1: return indexer_id = int(indexer_id) indexer = int(indexer) - # XEM API URL - url = "http://thexem.de/map/all?id=%s&origin=%s&destination=scene" % ( - indexer_id, sickbeard.indexerApi(indexer).config['xem_origin']) - MAX_REFRESH_AGE_SECS = 86400 # 1 day myDB = db.DBConnection() @@ -497,41 +493,48 @@ def xem_refresh(indexer_id, indexer, force=False): try: from .scene_exceptions import xem_session + + # XEM MAP URL + url = "http://thexem.de/map/havemap?origin=%s" % sickbeard.indexerApi(indexer).config['xem_origin'] + parsedJSON = sickbeard.helpers.getURL(url, session=xem_session, json=True) + if not parsedJSON or 'result' not in parsedJSON or 'success' not in parsedJSON['result'] or 'data' not in parsedJSON or indexer_id not in parsedJSON['data']: + return + + # XEM API URL + url = "http://thexem.de/map/all?id=%s&origin=%s&destination=scene" % indexer_id, sickbeard.indexerApi(indexer).config['xem_origin'] + parsedJSON = sickbeard.helpers.getURL(url, session=xem_session, json=True) - if not parsedJSON or parsedJSON == '': + if not parsedJSON or not 'result' in parsedJSON or not 'success' in parsedJSON['result']: logger.log(u'No XEM data for show "%s on %s"' % (indexer_id, sickbeard.indexerApi(indexer).name,), logger.INFO) return - if 'success' in parsedJSON['result']: - cl = [] - for entry in parsedJSON['data']: - if 'scene' in entry: - cl.append([ - "UPDATE tv_episodes SET scene_season = ?, scene_episode = ?, scene_absolute_number = ? WHERE showid = ? AND season = ? AND episode = ?", - [entry['scene']['season'], - entry['scene']['episode'], - entry['scene']['absolute'], - indexer_id, - entry[sickbeard.indexerApi(indexer).config['xem_origin']]['season'], - entry[sickbeard.indexerApi(indexer).config['xem_origin']]['episode'] - ]]) - if 'scene_2' in entry: # for doubles - cl.append([ - "UPDATE tv_episodes SET scene_season = ?, scene_episode = ?, scene_absolute_number = ? WHERE showid = ? AND season = ? AND episode = ?", - [entry['scene_2']['season'], - entry['scene_2']['episode'], - entry['scene_2']['absolute'], - indexer_id, - entry[sickbeard.indexerApi(indexer).config['xem_origin']]['season'], - entry[sickbeard.indexerApi(indexer).config['xem_origin']]['episode'] - ]]) - - if len(cl) > 0: - myDB = db.DBConnection() - myDB.mass_action(cl) - else: - logger.log(u"Empty lookup result - no XEM data for show %s on %s" % ( - indexer_id, sickbeard.indexerApi(indexer).name,), logger.DEBUG) + cl = [] + for entry in parsedJSON['data']: + if 'scene' in entry: + cl.append([ + "UPDATE tv_episodes SET scene_season = ?, scene_episode = ?, scene_absolute_number = ? WHERE showid = ? AND season = ? AND episode = ?", + [entry['scene']['season'], + entry['scene']['episode'], + entry['scene']['absolute'], + indexer_id, + entry[sickbeard.indexerApi(indexer).config['xem_origin']]['season'], + entry[sickbeard.indexerApi(indexer).config['xem_origin']]['episode'] + ]]) + if 'scene_2' in entry: # for doubles + cl.append([ + "UPDATE tv_episodes SET scene_season = ?, scene_episode = ?, scene_absolute_number = ? WHERE showid = ? AND season = ? AND episode = ?", + [entry['scene_2']['season'], + entry['scene_2']['episode'], + entry['scene_2']['absolute'], + indexer_id, + entry[sickbeard.indexerApi(indexer).config['xem_origin']]['season'], + entry[sickbeard.indexerApi(indexer).config['xem_origin']]['episode'] + ]]) + + if len(cl) > 0: + myDB = db.DBConnection() + myDB.mass_action(cl) + except Exception, e: logger.log( u"Exception while refreshing XEM data for show " + str(indexer_id) + " on " + sickbeard.indexerApi( diff --git a/sickbeard/tv.py b/sickbeard/tv.py index deb5655fb172af006f9970c4daac38cb6baff73b..12c67056c4f0d119bb5ee755b5df447779874b0e 100644 --- a/sickbeard/tv.py +++ b/sickbeard/tv.py @@ -484,10 +484,12 @@ class TVShow(object): if self.lang: lINDEXER_API_PARMS['language'] = self.lang + logger.log(u"Using language: " + str(self.lang), logger.DEBUG) if self.dvdorder != 0: lINDEXER_API_PARMS['dvdorder'] = True + logger.log(u"lINDEXER_API_PARMS: " + str(lINDEXER_API_PARMS), logger.DEBUG) t = sickbeard.indexerApi(self.indexer).indexer(**lINDEXER_API_PARMS) cachedShow = t[self.indexerid] @@ -495,6 +497,7 @@ class TVShow(object): for curResult in sqlResults: + logger.log(u"loadEpisodesFromDB curResult: " + str(curResult), logger.DEBUG) deleteEp = False curSeason = int(curResult["season"]) @@ -509,6 +512,7 @@ class TVShow(object): deleteEp = True if not curSeason in scannedEps: + logger.log(u"Not curSeason in scannedEps", logger.DEBUG) scannedEps[curSeason] = {} logger.log(u"Loading episode S%02dE%02d from the DB" % (curSeason, curEpisode), logger.DEBUG) @@ -530,6 +534,8 @@ class TVShow(object): logger.DEBUG) continue + logger.log(u"Finished loading all episodes from the DB", logger.DEBUG) + return scannedEps def loadEpisodesFromIndexer(self, cache=True): @@ -1471,7 +1477,7 @@ class TVEpisode(object): for video, subs in foundSubs.iteritems(): for sub in subs: - subpath = subliminal.subtitle.get_subtitle_path(video.name, sub.language) + subpath = subliminal.subtitle.get_subtitle_path(video.name, sub.language if sickbeard.SUBTITLES_MULTI else None) if sickbeard.SUBTITLES_DIR and ek.ek(os.path.exists, sickbeard.SUBTITLES_DIR): subpath = ek.ek(os.path.join, sickbeard.SUBTITLES_DIR, ek.ek(os.path.basename, subpath)) helpers.chmodAsParent(subpath) diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index 1ac8621b1435b260a2bac5e1f60ae3c0ea92ac23..d0368cf9da9a3fbd9da1bb5013509d18fad84829 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -129,6 +129,7 @@ class PageTemplate(CheetahTemplate): self.sbHttpsEnabled = sickbeard.ENABLE_HTTPS self.sbHandleReverseProxy = sickbeard.HANDLE_REVERSE_PROXY self.sbThemeName = sickbeard.THEME_NAME + self.sbDefaultPage = sickbeard.DEFAULT_PAGE self.sbLogin = rh.get_current_user() if rh.request.headers['Host'][0] == '[': @@ -281,8 +282,9 @@ class WebHandler(BaseHandler): class LoginHandler(BaseHandler): def get(self, *args, **kwargs): + if self.get_current_user(): - self.redirect('/home/') + self.redirect('/' + sickbeard.DEFAULT_PAGE +'/') else: t = PageTemplate(rh=self, file="login.tmpl") self.finish(t.respond()) @@ -301,11 +303,11 @@ class LoginHandler(BaseHandler): if api_key: remember_me = int(self.get_argument('remember_me', default=0) or 0) self.set_secure_cookie('sickrage_user', api_key, expires_days=30 if remember_me > 0 else None) - logger.log('User logged into the SickRage web interface from IP: ' + self.request.remote_ip, logger.INFO) + logger.log('User logged into the SickRage web interface', logger.INFO) else: logger.log('User attempted a failed login to the SickRage web interface from IP: ' + self.request.remote_ip, logger.WARNING) - self.redirect('/home/') + self.redirect('/' + sickbeard.DEFAULT_PAGE +'/') class LogoutHandler(BaseHandler): @@ -340,7 +342,7 @@ class WebRoot(WebHandler): super(WebRoot, self).__init__(*args, **kwargs) def index(self): - return self.redirect('/home/') + return self.redirect('/' + sickbeard.DEFAULT_PAGE +'/') def robots_txt(self): """ Keep web crawlers out """ @@ -431,7 +433,7 @@ class WebRoot(WebHandler): layout = 'poster' sickbeard.HOME_LAYOUT = layout - + #Dont redirect to default page so user can see new layout return self.redirect("/home/") def setPosterSortBy(self, sort): @@ -1115,7 +1117,7 @@ class Home(WebRoot): def shutdown(self, pid=None): if str(pid) != str(sickbeard.PID): - return self.redirect("/home/") + return self.redirect('/' + sickbeard.DEFAULT_PAGE +'/') sickbeard.events.put(sickbeard.events.SystemEvent.SHUTDOWN) @@ -1126,7 +1128,7 @@ class Home(WebRoot): def restart(self, pid=None): if str(pid) != str(sickbeard.PID): - return self.redirect("/home/") + return self.redirect('/' + sickbeard.DEFAULT_PAGE +'/') t = PageTemplate(rh=self, file="restart.tmpl") t.submenu = self.HomeMenu() @@ -1142,7 +1144,7 @@ class Home(WebRoot): sickbeard.versionCheckScheduler.action.check_for_new_version(force=True) - return self.redirect('/home/') + return self.redirect('/' + sickbeard.DEFAULT_PAGE +'/') def update(self, pid=None): @@ -1164,7 +1166,7 @@ class Home(WebRoot): return self._genericMessage("Update Failed", "Update wasn't successful, not restarting. Check your log for more information.") else: - return self.redirect('/home/') + return self.redirect('/' + sickbeard.DEFAULT_PAGE +'/') def branchCheckout(self, branch): if sickbeard.BRANCH != branch: @@ -1173,7 +1175,7 @@ class Home(WebRoot): return self.update(sickbeard.PID) else: ui.notifications.message('Already on branch: ', branch) - return self.redirect('/home') + return self.redirect('/' + sickbeard.DEFAULT_PAGE +'/') def getDBcompare(self, branchDest=None): @@ -1597,7 +1599,8 @@ class Home(WebRoot): (showObj.name, ('deleted', 'trashed')[sickbeard.TRASH_REMOVE_SHOW], ('(media untouched)', '(with all related media)')[bool(full)])) - return self.redirect("/home/") + #Dont redirect to default page so user can confirm show was deleted + return self.redirect('/home/') def refreshShow(self, show=None): @@ -1697,7 +1700,7 @@ class Home(WebRoot): def setStatus(self, show=None, eps=None, status=None, direct=False): - if show is None or eps is None or status is None: + if not all([show, eps, status]): errMsg = "You must specify a show and at least one episode" if direct: ui.notifications.error('Error', errMsg) @@ -1715,7 +1718,7 @@ class Home(WebRoot): showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(show)) - if showObj is None: + if not showObj: errMsg = "Error", "Show not in show list" if direct: ui.notifications.error('Error', errMsg) @@ -1725,18 +1728,25 @@ class Home(WebRoot): segments = {} trakt_data = [] - if eps is not None: + if eps: sql_l = [] for curEp in eps.split('|'): + if not curEp: + logger.log(u"curEp was empty when trying to setStatus", logger.DEBUG) + logger.log(u"Attempting to set status on episode " + curEp + " to " + status, logger.DEBUG) epInfo = curEp.split('x') + if not all(epInfo): + logger.log(u"Something went wrong when trying to setStatus, epInfo[0]: %s, epInfo[1]: %s" % (epInfo[0], epInfo[1]), logger.DEBUG) + continue + epObj = showObj.getEpisode(int(epInfo[0]), int(epInfo[1])) - if epObj is None: + if not epObj: return self._genericMessage("Error", "Episode couldn't be retrieved") if int(status) in [WANTED, FAILED]: @@ -1752,16 +1762,14 @@ class Home(WebRoot): logger.log(u"Refusing to change status of " + curEp + " because it is UNAIRED", logger.ERROR) continue - if int( - status) in Quality.DOWNLOADED and epObj.status not in Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.DOWNLOADED + [ + if int(status) in Quality.DOWNLOADED and epObj.status not in Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.DOWNLOADED + [ IGNORED] and not ek.ek(os.path.isfile, epObj.location): logger.log( u"Refusing to change status of " + curEp + " to DOWNLOADED because it's not SNATCHED/DOWNLOADED", logger.ERROR) continue - if int( - status) == FAILED and epObj.status not in Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.DOWNLOADED: + if int(status) == FAILED and epObj.status not in Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.DOWNLOADED: logger.log( u"Refusing to change status of " + curEp + " to FAILED because it's not SNATCHED/DOWNLOADED", logger.ERROR) @@ -2706,6 +2714,7 @@ class HomeAddShows(Home): logger.log(u"Unable to create the folder " + show_dir + ", can't add the show", logger.ERROR) ui.notifications.error("Unable to add show", "Unable to create the folder " + show_dir + ", can't add the show") + #Dont redirect to default page because user wants to see the new show return self.redirect("/home/") else: helpers.chmodAsParent(show_dir) @@ -3768,7 +3777,7 @@ class ConfigGeneral(Config): proxy_setting=None, proxy_indexers=None, anon_redirect=None, git_path=None, git_remote=None, calendar_unprotected=None, debug=None, ssl_verify=None, no_restart=None, coming_eps_missed_range=None, filter_row=None, fuzzy_dating=None, trim_zero=None, date_preset=None, date_preset_na=None, time_preset=None, - indexer_timeout=None, download_url=None, rootDir=None, theme_name=None, + indexer_timeout=None, download_url=None, rootDir=None, theme_name=None, default_page=None, git_reset=None, git_username=None, git_password=None, git_autoissues=None, display_all_seasons=None): results = [] @@ -3798,7 +3807,9 @@ class ConfigGeneral(Config): sickbeard.PROXY_INDEXERS = config.checkbox_to_value(proxy_indexers) sickbeard.GIT_USERNAME = git_username sickbeard.GIT_PASSWORD = git_password - sickbeard.GIT_RESET = config.checkbox_to_value(git_reset) + #sickbeard.GIT_RESET = config.checkbox_to_value(git_reset) + #Force GIT_RESET + sickbeard.GIT_RESET = 1 sickbeard.GIT_AUTOISSUES = config.checkbox_to_value(git_autoissues) sickbeard.GIT_PATH = git_path sickbeard.GIT_REMOTE = git_remote @@ -3858,6 +3869,8 @@ class ConfigGeneral(Config): sickbeard.HANDLE_REVERSE_PROXY = config.checkbox_to_value(handle_reverse_proxy) sickbeard.THEME_NAME = theme_name + + sickbeard.DEFAULT_PAGE = default_page sickbeard.save_config() @@ -5047,6 +5060,9 @@ class ErrorLogs(WebRoot): def viewlog(self, minLevel=logger.INFO, logFilter="<NONE>",logSearch=None, maxLines=500): + if sickbeard.DEBUG == 1: + minLevel = logger.DEBUG + def Get_Data(Levelmin, data_in, lines_in, regex, Filter, Search, mlines): lastLine = False