diff --git a/CHANGES.md b/CHANGES.md index e4758094de0f4750783dccfcbbd26284e86a17e9..b847ed4970fe481ebfea621bc04ee3b2cf48141d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,4 +1,45 @@ -### 4.0.14 (2014-03-29) +### 4.0.16 (2015-04-12) + +[full changelog](https://github.com/SiCKRAGETV/SickRage/compare/v4.0.15...v4.0.16) + +* New Feature: Added option to add a filter row on main show page (enabled in general settings - interface) +* New Feature: added scheduling status page +* Fixed EZTV rss blank result +* Fixed RARBG tokend +* Fixed Home page filter: change to allow to use parsed data for active(yes/no) +* Fixed OldTPB: Check if the returned results ar proper|repack +* Don't display paused show in backlogOverview +* Trakt Sync by Episode not by Show +* Added gzip setting in config.ini +* Added TRAKTROLLING to filter in viewlog +* Redone Scheduler +* Replace fuzzy images on Add Show ( Add Trending) + +### 4.0.15 (2015-04-05) + +[full changelog](https://github.com/SiCKRAGETV/SickRage/compare/v4.0.14...v4.0.15) + +* Fixed KAT season search +* Fixed episode description decode error in calendar +* Fixed torrent empty in multi-episode +* Fixed XEM invalid entries +* Fixed unicode error in verify_freespace +* Fixed rtorrent encoding issue in windows +* Fixed "add shows" with white spaces +* Fixed frequency definition in start queue +* Fixed EZTV import error +* Added qbittorrent sync file extention to ignore list +* Added T411 API - instead of web scrapping +* Added RARBG API - instead of web scrapping +* Added option to choose removed episodes status +* Added removeWordsList from all providers - clean release names if provider add more text to it +* Added more network logos +* Added check in manual postprocess to remove hard/sym links in windows +* Added code to debug scheduler errors +* Updated TRAKT api urls +* Relocated coming_eps_missed_range to general settings + +### 4.0.14 (2015-03-29) [full changelog](https://github.com/SiCKRAGETV/SickRage/compare/v4.0.13...v4.0.14) @@ -30,7 +71,7 @@ * Hide Proxy indexers settings when proxy host is empty * Change removed episodes status from IGNORED to ARCHIVED -### 4.0.13 (2014-03-22) +### 4.0.13 (2015-03-22) [full changelog](https://github.com/SiCKRAGETV/SickRage/compare/v4.0.12...v4.0.13) @@ -85,7 +126,7 @@ * New Feature: Added the ability to choose displaying columns in home page -### 4.0.12 (2014-03-15) +### 4.0.12 (2015-03-15) [full changelog](https://github.com/SiCKRAGETV/SickRage/compare/v4.0.11...v4.0.12) @@ -118,7 +159,7 @@ * Added limit title length when Submitting log * Send LOCALE when submitting issue -### 4.0.11 (2014-03-08) +### 4.0.11 (2015-03-08) [full changelog](https://github.com/SiCKRAGETV/SickRage/compare/v4.0.10...v4.0.11) * Use Scene Exceptions in Post Processing @@ -138,7 +179,7 @@ * If you missed this feature: you can change the number of logs in settings and size per file) * WARNING: Windows users: please set number of logs to 0 (zero) to avoid errors. Known issue. -### 4.0.10 (2014-03-03) +### 4.0.10 (2015-03-03) [full changelog](https://github.com/SiCKRAGETV/SickRage/compare/v4.0.9...v4.0.10) * Add "Use failed downloads" to search settings diff --git a/SickBeard.py b/SickBeard.py index 6db397cc1d672490e93caed4e4efa96df2694cc3..ea45d45fd26b8eec11d36a34c647e2e61aed6f21 100755 --- a/SickBeard.py +++ b/SickBeard.py @@ -367,7 +367,7 @@ class SickRage(object): # Start an update if we're supposed to if self.forceUpdate or sickbeard.UPDATE_SHOWS_ON_START: - sickbeard.showUpdateScheduler.action.run(force=True) # @UndefinedVariable + sickbeard.showUpdateScheduler.forceRun() # Launch browser if sickbeard.LAUNCH_BROWSER and not (self.noLaunch or self.runAsDaemon): diff --git a/gui/slick/css/dark.css b/gui/slick/css/dark.css index cc7651e5d12b5cd186456ce6e8b72626e4b1033c..95467041f27778b1f1ba2ff1e62d3ea9a5217582 100644 --- a/gui/slick/css/dark.css +++ b/gui/slick/css/dark.css @@ -2836,6 +2836,28 @@ thead.tablesorter-stickyHeader { border-bottom: 1px solid #111; } +/* hidden filter row */ +.tablesorter-filter-row.hideme td { + /*** *********************************************** ***/ + /*** change this padding to modify the thickness ***/ + /*** of the closed filter row (height = padding x 2) ***/ + padding: 2px; + /*** *********************************************** ***/ + margin: 0; + line-height: 0; + cursor: pointer; +} +.tablesorter-filter-row.hideme * { + height: 1px; + min-height: 0; + border: 0; + padding: 0; + margin: 0; + /* don't use visibility: hidden because it disables tabbing */ + opacity: 0; + filter: alpha(opacity=0); +} + /* optional disabled input styling */ .tablesorter input.tablesorter-filter-row .disabled { display: none; diff --git a/gui/slick/css/light.css b/gui/slick/css/light.css index 834bf260da4603c6f2bb14ace0cfcc61259d7996..8df3fe1d64aacf8d1fc65bcdca2ee36d32c58ce7 100644 --- a/gui/slick/css/light.css +++ b/gui/slick/css/light.css @@ -2774,6 +2774,29 @@ thead.tablesorter-stickyHeader { display: none; } +/* hidden filter row */ +.tablesorter-filter-row.hideme td { + /*** *********************************************** ***/ + /*** change this padding to modify the thickness ***/ + /*** of the closed filter row (height = padding x 2) ***/ + padding: 2px; + /*** *********************************************** ***/ + margin: 0; + line-height: 0; + cursor: pointer; +} +.tablesorter-filter-row.hideme * { + height: 1px; + min-height: 0; + border: 0; + padding: 0; + margin: 0; + /* don't use visibility: hidden because it disables tabbing */ + opacity: 0; + filter: alpha(opacity=0); +} + + .tablesorter-header-inner { padding: 0 2px; text-align: center; diff --git a/gui/slick/images/addshows/add-trending32-black.png b/gui/slick/images/addshows/add-trending32-black.png index 136db480d6666ae7997dc92ae8b911bbf7e27cfc..8f14e668f3feb39afdb5aa205e78665cd56cd1de 100644 Binary files a/gui/slick/images/addshows/add-trending32-black.png and b/gui/slick/images/addshows/add-trending32-black.png differ diff --git a/gui/slick/images/addshows/add-trending32-white.png b/gui/slick/images/addshows/add-trending32-white.png index 9dae6063a12a7816c861b24ffc4018b7ff5e6998..3ea23661fce1950c7613c3e0e93a9335874d7d26 100644 Binary files a/gui/slick/images/addshows/add-trending32-white.png and b/gui/slick/images/addshows/add-trending32-white.png differ diff --git a/gui/slick/interfaces/default/config_general.tmpl b/gui/slick/interfaces/default/config_general.tmpl index d3cc3cfd750c2164182de7ef66bc36e34a6b5cf8..e4651ce30aa262b1cb2560b3fc16242296476a06 100644 --- a/gui/slick/interfaces/default/config_general.tmpl +++ b/gui/slick/interfaces/default/config_general.tmpl @@ -295,6 +295,15 @@ </span> </label> </div> + <div class="field-pair"> + <label for="filter_row"> + <span class="component-title">Filter Row</span> + <span class="component-desc"> + <input type="checkbox" name="filter_row" id="display_filesize" #if $sickbeard.FILTER_ROW == True then 'checked="checked"' else ''#/> + <p>Add a filter row to the show display on the home page</p> + </span> + </label> + </div> <div class="field-pair"> <label for="display_filesize"> <span class="component-title">Display Filesizes</span> diff --git a/gui/slick/interfaces/default/config_notifications.tmpl b/gui/slick/interfaces/default/config_notifications.tmpl index c08b94d61068578713972d4e9f3907eb158a7b66..3fb21751c7305d6e4c29eb30956b2c06d3784230 100644 --- a/gui/slick/interfaces/default/config_notifications.tmpl +++ b/gui/slick/interfaces/default/config_notifications.tmpl @@ -1440,10 +1440,21 @@ <span class="component-title">Sync libraries:</span> <span class="component-desc"> <input type="checkbox" class="enabler" name="trakt_sync" id="trakt_sync" #if $sickbeard.TRAKT_SYNC then "checked=\"checked\"" else ""# /> - <p>sync your SickRage show library with your trakt show library.</p> + <p>Sync your SickRage show Library with your Trakt Show Collection.</p> </span> </label> </div> + <div id="content_trakt_sync"> + <div class="field-pair"> + <label for="trakt_sync_remove"> + <span class="component-title">Remove Episodes From Collection:</span> + <span class="component-desc"> + <input type="checkbox" name="trakt_sync_remove" id="trakt_sync_remove" #if $sickbeard.TRAKT_SYNC_REMOVE then "checked=\"checked\"" else ""# /> + <p>Remove an Episode from your Trakt Collection if it is not in your SickRage Library.</p> + </span> + </label> + </div> + </div> <div class="field-pair"> <label for="trakt_sync_watchlist"> <span class="component-title">Sync watchlist:</span> @@ -1504,7 +1515,7 @@ </label> <label> <span class="component-title"> </span> - <span class="component-desc">Name(slug) of List on Trakt for blacklisting show on tranding page</span> + <span class="component-desc">Name(slug) of List on Trakt for blacklisting show on 'Add Trending Show' page</span> </label> </div> <div class="field-pair"> @@ -1534,7 +1545,7 @@ <input type="text" name="trakt_rolling_frequency" id="trakt_rolling_frequency" value="$sickbeard.TRAKT_ROLLING_FREQUENCY" class="form-control input-sm input250" /> </label> <p> - <span class="component-desc">Minutes between check.</span> + <span class="component-desc">Hours between check. (Cannot be lower than 4 hours)</span> </p> </div> <div class="field-pair"> diff --git a/gui/slick/interfaces/default/config_postProcessing.tmpl b/gui/slick/interfaces/default/config_postProcessing.tmpl index 459a649dd2df17ea090ea2244009d2d33610d5d9..a027dbf326e79791ab102a1bfe6ba1128acbc133 100644 --- a/gui/slick/interfaces/default/config_postProcessing.tmpl +++ b/gui/slick/interfaces/default/config_postProcessing.tmpl @@ -1,6 +1,5 @@ #import os.path #import sickbeard -#import sys #from sickbeard.common import * #from sickbeard import config #from sickbeard import metadata @@ -72,12 +71,7 @@ <span class="component-desc"> <select name="process_method" id="process_method" class="form-control input-sm"> #set $process_method_text = {'copy': "Copy", 'move': "Move", 'hardlink': "Hard Link", 'symlink' : "Symbolic Link"} - #if sys.platform == 'win32' - #set $process_action = ('copy', 'move') - #else - #set $process_action = ('copy', 'move', 'hardlink', 'symlink') - #end if - #for $curAction in $process_action: + #for $curAction in ('copy', 'move', 'hardlink', 'symlink'): #if $sickbeard.PROCESS_METHOD == $curAction: #set $process_method = "selected=\"selected\"" #else diff --git a/gui/slick/interfaces/default/home.tmpl b/gui/slick/interfaces/default/home.tmpl index 621b60be57b0ea56a29af66cfa03d4e71e56d4df..3d27ae4c744d5531d02c65516405d672e56317ff 100644 --- a/gui/slick/interfaces/default/home.tmpl +++ b/gui/slick/interfaces/default/home.tmpl @@ -102,10 +102,18 @@ 1: { columnSelector: false }, 2: { sorter: 'loadingNames' }, 4: { sorter: 'quality' }, - 5: { sorter: 'eps' } + 5: { sorter: 'eps' }, + #if $sickbeard.FILTER_ROW: + 6: { filter : 'parsed' } + #end if }, widgetOptions : { - filter_columnFilters: false, + #if $sickbeard.FILTER_ROW: + filter_columnFilters: true, + filter_hideFilters : true, + #else + filter_columnFilters: false, + #end if filter_reset: '.resetshows', columnSelector_mediaquery: false, }, @@ -129,10 +137,18 @@ 1: { columnSelector: false }, 2: { sorter: 'loadingNames' }, 4: { sorter: 'quality' }, - 5: { sorter: 'eps' } + 5: { sorter: 'eps' }, + #if $sickbeard.FILTER_ROW: + 6: { filter : 'parsed' } + #end if }, widgetOptions : { - filter_columnFilters: false, + #if $sickbeard.FILTER_ROW: + filter_columnFilters: true, + filter_hideFilters : true, + #else + filter_columnFilters: false, + #end if filter_reset: '.resetanime', columnSelector_mediaquery: false, }, @@ -687,9 +703,9 @@ $myShowList.sort(lambda x, y: cmp(x.name, y.name)) <td align="center"> #if sickbeard.TRAKT_USE_ROLLING_DOWNLOAD and sickbeard.USE_TRAKT - <img src="$sbRoot/images/#if int($curShow.paused) == 0 then "yes16.png\" alt=\"Y\"" else "no16.png\" alt=\"N\""# width="16" height="16" /> + <img src="$sbRoot/images/#if int($curShow.paused) == 0 then "yes16.png\" alt=\"Yes\"" else "no16.png\" alt=\"No\""# width="16" height="16" /> #else - <img src="$sbRoot/images/#if int($curShow.paused) == 0 and $curShow.status == "Continuing" then "yes16.png\" alt=\"Y\"" else "no16.png\" alt=\"N\""# width="16" height="16" /> + <img src="$sbRoot/images/#if int($curShow.paused) == 0 and $curShow.status == "Continuing" then "yes16.png\" alt=\"Yes\"" else "no16.png\" alt=\"No\""# width="16" height="16" /> #end if </td> diff --git a/gui/slick/interfaces/default/home_postprocess.tmpl b/gui/slick/interfaces/default/home_postprocess.tmpl index dff00bd54b1dbcd757cb6093d37ed26a93c9af3d..404e8c7773beb8d2ab66c8fbb9e3c613d26ff0b1 100644 --- a/gui/slick/interfaces/default/home_postprocess.tmpl +++ b/gui/slick/interfaces/default/home_postprocess.tmpl @@ -33,12 +33,7 @@ <td> <select name="process_method" id="process_method" class="form-control form-control-inline input-sm" > #set $process_method_text = {'copy': "Copy", 'move': "Move", 'hardlink': "Hard Link", 'symlink' : "Symbolic Link"} - #if sys.platform == 'win32' - #set $process_action = ('copy', 'move') - #else - #set $process_action = ('copy', 'move', 'hardlink', 'symlink') - #end if - #for $curAction in $process_action: + #for $curAction in ('copy', 'move', 'hardlink', 'symlink'): #if $sickbeard.PROCESS_METHOD == $curAction: #set $process_method = "selected=\"selected\"" #else diff --git a/gui/slick/interfaces/default/inc_top.tmpl b/gui/slick/interfaces/default/inc_top.tmpl index 2ea64165b7881de449d7eba3ddb92546a4a1a3aa..2bc4e16b18687a751426fdf66885b47f573b24da 100644 --- a/gui/slick/interfaces/default/inc_top.tmpl +++ b/gui/slick/interfaces/default/inc_top.tmpl @@ -184,7 +184,7 @@ #end if #if $sickbeard.USE_TORRENTS and $sickbeard.TORRENT_METHOD != 'blackhole' \ and ($sickbeard.ENABLE_HTTPS and $sickbeard.TORRENT_HOST[:5] == 'https' \ - or not $sickbeard.ENABLE_HTTPS and $sickbeard.TORRENT_HOST[:5] == 'http:'): + or not $sickbeard.ENABLE_HTTPS and $sickbeard.TORRENT_HOST[:5] == 'http:'): <li><a href="$sbRoot/manage/manageTorrents/"><i class="menu-icon-bittorrent"></i> Manage Torrents</a></li> #end if #if $sickbeard.USE_FAILED_DOWNLOADS: @@ -226,6 +226,7 @@ <li><a href="$sbRoot/home/restart/?pid=$sbPID" class="confirm restart"><i class="menu-icon-restart"></i> Restart</a></li> <li><a href="$sbRoot/home/shutdown/?pid=$sbPID" class="confirm shutdown"><i class="menu-icon-shutdown"></i> Shutdown</a></li> <li><a href="$sbRoot/logout" class="confirm logout"><i class="menu-icon-shutdown"></i> Logout</a></li> + <li><a href="$sbRoot/home/status/"><i class="menu-icon-help"></i> Server Status</a></li> </ul> </li> <li id="donate"><a href="http://sr-upgrade.appspot.com" rel="noreferrer" onclick="window.open('${sickbeard.ANON_REDIRECT}' + this.href); return false;"><img src="$sbRoot/images/donate.jpg" alt="[donate]" class="navbaricon hidden-xs" /></a></li> diff --git a/gui/slick/interfaces/default/status.tmpl b/gui/slick/interfaces/default/status.tmpl new file mode 100644 index 0000000000000000000000000000000000000000..675c5886a47e55957a3d682d0d8678129544c4fe --- /dev/null +++ b/gui/slick/interfaces/default/status.tmpl @@ -0,0 +1,200 @@ +#import sickbeard +#from sickbeard import helpers +#from sickbeard.show_queue import ShowQueueActions + +#set global $title="Status" +#set global $header="Status" +#set global $sbPath=".." + +#set global $topmenu="config"# +#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl") + +#if $varExists('header') + <h1 class="header">$header</h1> +#else + <h1 class="title">$title</h1> +#end if + +#set schedulerList = {'Daily Search': 'dailySearchScheduler', + 'Backlog': 'backlogSearchScheduler', + 'Show Update': 'showUpdateScheduler', + 'Version Check': 'versionCheckScheduler', + 'Show Queue': 'showQueueScheduler', + 'Search Queue': 'searchQueueScheduler', + 'Proper Finder': 'properFinderScheduler', + 'Post Process': 'autoPostProcesserScheduler', + 'Subtitles Finder': 'subtitlesFinderScheduler', + 'Trakt Checker': 'traktCheckerScheduler', + 'Trakt Rolling': 'traktRollingScheduler', +} + +<script type="text/javascript"> + \$(document).ready(function() { + \$("#schedulerStatusTable").tablesorter({ + widgets: ['saveSort', 'zebra'] + }); + }); + \$(document).ready(function() { + \$("#queueStatusTable").tablesorter({ + widgets: ['saveSort', 'zebra'], + sortList: [[3,0], [4,0], [2,1]] + }); + }); +</script> + +<div id="config-content"> + <h2 class="header">Scheduler</h2> + <table id="schedulerStatusTable" class="tablesorter" width="100%"> + <thead> + <tr> + <th>Scheduler</th> + <th>Alive</th> + <th>Enable</th> + <th>Active</th> + <th>Start Time</th> + <th>Cycle Time</th> + <th>Next Run</th> + <th>Last Run</th> + <th>Silent</th> + </tr> + </thead> + <tbody> + #for $schedulerName, $scheduler in $schedulerList.iteritems() + #set service = getattr($sickbeard, $scheduler) + <tr> + <td>#echo $schedulerName #</td> + #if $service.isAlive() + <td style="background-color:green">#echo $service.isAlive() #</td> + #else + <td style="background-color:red">#echo $service.isAlive() #</td> + #end if + #if $scheduler == 'backlogSearchScheduler' + #set searchQueue = getattr($sickbeard, 'searchQueueScheduler') + #set $BLSpaused = $searchQueue.action.is_backlog_paused() + #del searchQueue + #if $BLSpaused + <td>Paused</td> + #else + <td>#echo $service.enable #</td> + #end if + #else + <td>#echo $service.enable #</td> + #end if + #if $scheduler == 'backlogSearchScheduler' + #set searchQueue = getattr($sickbeard, 'searchQueueScheduler') + #set $BLSinProgress = $searchQueue.action.is_backlog_in_progress() + #del searchQueue + #if $BLSinProgress + <td>True</td> + #else + #try + #set amActive = $service.action.amActive + <td>#echo $amActive #</td> + #except Exception + <td>N/A</td> + #end try + #end if + #else + #try + #set amActive = $service.action.amActive + <td>#echo $amActive #</td> + #except Exception + <td>N/A</td> + #end try + #end if + <td>#echo $service.start_time #</td> + #set $cycleTime = ($service.cycleTime.microseconds + ($service.cycleTime.seconds + $service.cycleTime.days * 24 * 3600) * 10**6) / 10**6 + <td>#echo $helpers.pretty_time_delta($cycleTime) #</td> + #if $service.enable + #set $timeLeft = ($service.timeLeft().microseconds + ($service.timeLeft().seconds + $service.timeLeft().days * 24 * 3600) * 10**6) / 10**6 + <td>#echo $helpers.pretty_time_delta($timeLeft) #</td> + #else + <td></td> + #end if + <td>#echo $service.lastRun.strftime("%Y-%m-%d %H:%M:%S") #</td> + <td>#echo $service.silent #</td> + </tr> + #del service + #end for + </tbody> + </table> + <h2 class="header">Show Queue</h2> + <table id="queueStatusTable" class="tablesorter" width="100%"> + <thead> + <tr> + <th>Show id</th> + <th>Show name</th> + <th>In Progress</th> + <th>Priority</th> + <th>Added</th> + <th>Queue type</th> + </tr> + </thead> + <tbody> + #if $sickbeard.showQueueScheduler.action.currentItem is not None + <tr> + #try + #set showindexerid = $sickbeard.showQueueScheduler.action.currentItem.show.indexerid + <td>$showindexerid</td> + #except Exception + <td></td> + #end try + #try + #set showname = $sickbeard.showQueueScheduler.action.currentItem.show.name + <td>$showname</td> + #except Exception + #if $sickbeard.showQueueScheduler.action.currentItem.action_id == $ShowQueueActions.ADD + <td>$sickbeard.showQueueScheduler.action.currentItem.showDir</td> + #else + <td></td> + #end if + #end try + <td>$sickbeard.showQueueScheduler.action.currentItem.inProgress</td> + #if $sickbeard.showQueueScheduler.action.currentItem.priority == 10 + <td>LOW</td> + #elif $sickbeard.showQueueScheduler.action.currentItem.priority == 20 + <td>NORMAL</td> + #elif $sickbeard.showQueueScheduler.action.currentItem.priority == 30 + <td>HIGH</td> + #else + <td>$sickbeard.showQueueScheduler.action.currentItem.priority</td> + #end if + <td>$sickbeard.showQueueScheduler.action.currentItem.added.strftime("%Y-%m-%d %H:%M:%S")</td> + <td>$ShowQueueActions.names[$sickbeard.showQueueScheduler.action.currentItem.action_id]</td> + </tr> + #end if + #for item in $sickbeard.showQueueScheduler.action.queue + <tr> + #try + #set showindexerid = $item.show.indexerid + <td>$showindexerid</td> + #except Exception + <td></td> + #end try + #try + #set showname = $item.show.name + <td>$showname</td> + #except Exception + #if $item.action_id == $ShowQueueActions.ADD + <td>$item.showDir</td> + #else + <td></td> + #end if + #end try + <td>$item.inProgress</td> + #if $item.priority == 10 + <td>LOW</td> + #elif $item.priority == 20 + <td>NORMAL</td> + #elif $item.priority == 30 + <td>HIGH</td> + #else + <td>$item.priority</td> + #end if + <td>$item.added.strftime("%Y-%m-%d %H:%M:%S")</td> + <td>$ShowQueueActions.names[$item.action_id]</td> + </tr> + #end for + </tbody> + </table> +</div> \ No newline at end of file diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py index 5a4f295fcdf25f73fd8e574bd0125bcae8d4f3f4..2a9b9e0900c2db4036ad9e7cc7d570173352e8a3 100755 --- a/sickbeard/__init__.py +++ b/sickbeard/__init__.py @@ -149,6 +149,7 @@ WEB_PASSWORD = None WEB_HOST = None WEB_IPV6 = None WEB_COOKIE_SECRET = None +WEB_USE_GZIP = True DOWNLOAD_URL = None @@ -344,7 +345,7 @@ FREEMOBILE_NOTIFY_ONSNATCH = False FREEMOBILE_NOTIFY_ONDOWNLOAD = False FREEMOBILE_NOTIFY_ONSUBTITLEDOWNLOAD = False FREEMOBILE_ID = '' -FREEMOBILE_APIKEY= '' +FREEMOBILE_APIKEY = '' USE_PROWL = False PROWL_NOTIFY_ONSNATCH = False @@ -418,18 +419,19 @@ TRAKT_PASSWORD = None TRAKT_REMOVE_WATCHLIST = False TRAKT_REMOVE_SERIESLIST = False TRAKT_SYNC_WATCHLIST = False -TRAKT_METHOD_ADD = 0 +TRAKT_METHOD_ADD = None TRAKT_START_PAUSED = False TRAKT_USE_RECOMMENDED = False TRAKT_SYNC = False +TRAKT_SYNC_REMOVE = False TRAKT_DEFAULT_INDEXER = None TRAKT_DISABLE_SSL_VERIFY = False -TRAKT_TIMEOUT = 60 -TRAKT_BLACKLIST_NAME = '' -TRAKT_USE_ROLLING_DOWNLOAD = 0 -TRAKT_ROLLING_NUM_EP = 0 -TRAKT_ROLLING_ADD_PAUSED = 1 -TRAKT_ROLLING_FREQUENCY = 15 +TRAKT_TIMEOUT = None +TRAKT_BLACKLIST_NAME = None +TRAKT_USE_ROLLING_DOWNLOAD = None +TRAKT_ROLLING_NUM_EP = None +TRAKT_ROLLING_ADD_PAUSED = None +TRAKT_ROLLING_FREQUENCY = None USE_PYTIVO = False PYTIVO_NOTIFY_ONSNATCH = False @@ -490,6 +492,7 @@ TIMEZONE_DISPLAY = None THEME_NAME = None POSTER_SORTBY = None POSTER_SORTDIR = None +FILTER_ROW = False USE_SUBTITLES = False SUBTITLES_LANGUAGES = [] @@ -528,14 +531,14 @@ def get_backlog_cycle_time(): def initialize(consoleLogging=True): with INIT_LOCK: - global BRANCH, GIT_RESET, GIT_REMOTE, GIT_REMOTE_URL, CUR_COMMIT_HASH, CUR_COMMIT_BRANCH, ACTUAL_LOG_DIR, LOG_DIR, LOG_NR, LOG_SIZE, WEB_PORT, WEB_LOG, ENCRYPTION_VERSION, ENCRYPTION_SECRET, WEB_ROOT, WEB_USERNAME, WEB_PASSWORD, WEB_HOST, WEB_IPV6, WEB_COOKIE_SECRET, API_KEY, API_ROOT, ENABLE_HTTPS, HTTPS_CERT, HTTPS_KEY, \ + global BRANCH, GIT_RESET, GIT_REMOTE, GIT_REMOTE_URL, CUR_COMMIT_HASH, CUR_COMMIT_BRANCH, ACTUAL_LOG_DIR, LOG_DIR, LOG_NR, LOG_SIZE, WEB_PORT, WEB_LOG, ENCRYPTION_VERSION, ENCRYPTION_SECRET, WEB_ROOT, WEB_USERNAME, WEB_PASSWORD, WEB_HOST, WEB_IPV6, WEB_COOKIE_SECRET, WEB_USE_GZIP, API_KEY, API_ROOT, ENABLE_HTTPS, HTTPS_CERT, HTTPS_KEY, \ HANDLE_REVERSE_PROXY, USE_NZBS, USE_TORRENTS, NZB_METHOD, NZB_DIR, DOWNLOAD_PROPERS, RANDOMIZE_PROVIDERS, CHECK_PROPERS_INTERVAL, ALLOW_HIGH_PRIORITY, SAB_FORCED, TORRENT_METHOD, \ SAB_USERNAME, SAB_PASSWORD, SAB_APIKEY, SAB_CATEGORY, SAB_CATEGORY_ANIME, SAB_HOST, \ NZBGET_USERNAME, NZBGET_PASSWORD, NZBGET_CATEGORY, NZBGET_CATEGORY_ANIME, NZBGET_PRIORITY, NZBGET_HOST, NZBGET_USE_HTTPS, backlogSearchScheduler, \ TORRENT_USERNAME, TORRENT_PASSWORD, TORRENT_HOST, TORRENT_PATH, TORRENT_SEED_TIME, TORRENT_PAUSED, TORRENT_HIGH_BANDWIDTH, TORRENT_LABEL, TORRENT_LABEL_ANIME, TORRENT_VERIFY_CERT, TORRENT_RPCURL, TORRENT_AUTH_TYPE, \ USE_KODI, KODI_ALWAYS_ON, KODI_NOTIFY_ONSNATCH, KODI_NOTIFY_ONDOWNLOAD, KODI_NOTIFY_ONSUBTITLEDOWNLOAD, KODI_UPDATE_FULL, KODI_UPDATE_ONLYFIRST, \ KODI_UPDATE_LIBRARY, KODI_HOST, KODI_USERNAME, KODI_PASSWORD, BACKLOG_FREQUENCY, \ - USE_TRAKT, TRAKT_USERNAME, TRAKT_PASSWORD, TRAKT_REMOVE_WATCHLIST, TRAKT_SYNC_WATCHLIST, TRAKT_METHOD_ADD, TRAKT_START_PAUSED, traktCheckerScheduler, traktRollingScheduler, TRAKT_USE_RECOMMENDED, TRAKT_SYNC, TRAKT_DEFAULT_INDEXER, TRAKT_REMOVE_SERIESLIST, TRAKT_DISABLE_SSL_VERIFY, TRAKT_TIMEOUT, TRAKT_BLACKLIST_NAME, TRAKT_USE_ROLLING_DOWNLOAD, TRAKT_ROLLING_NUM_EP, TRAKT_ROLLING_ADD_PAUSED, TRAKT_ROLLING_FREQUENCY, \ + USE_TRAKT, TRAKT_USERNAME, TRAKT_PASSWORD, TRAKT_REMOVE_WATCHLIST, TRAKT_SYNC_WATCHLIST, TRAKT_METHOD_ADD, TRAKT_START_PAUSED, traktCheckerScheduler, traktRollingScheduler, TRAKT_USE_RECOMMENDED, TRAKT_SYNC, TRAKT_SYNC_REMOVE, TRAKT_DEFAULT_INDEXER, TRAKT_REMOVE_SERIESLIST, TRAKT_DISABLE_SSL_VERIFY, TRAKT_TIMEOUT, TRAKT_BLACKLIST_NAME, TRAKT_USE_ROLLING_DOWNLOAD, TRAKT_ROLLING_NUM_EP, TRAKT_ROLLING_ADD_PAUSED, TRAKT_ROLLING_FREQUENCY, \ USE_PLEX, PLEX_NOTIFY_ONSNATCH, PLEX_NOTIFY_ONDOWNLOAD, PLEX_NOTIFY_ONSUBTITLEDOWNLOAD, PLEX_UPDATE_LIBRARY, \ PLEX_SERVER_HOST, PLEX_SERVER_TOKEN, PLEX_HOST, PLEX_USERNAME, PLEX_PASSWORD, DEFAULT_BACKLOG_FREQUENCY, MIN_BACKLOG_FREQUENCY, BACKLOG_STARTUP, SKIP_REMOVED_FILES, \ showUpdateScheduler, __INITIALIZED__, INDEXER_DEFAULT_LANGUAGE, EP_DEFAULT_DELETED_STATUS, LAUNCH_BROWSER, UPDATE_SHOWS_ON_START, UPDATE_SHOWS_ON_SNATCH, TRASH_REMOVE_SHOW, TRASH_ROTATE_LOGS, SORT_ARTICLE, showList, loadingShowList, \ @@ -562,7 +565,7 @@ def initialize(consoleLogging=True): USE_EMAIL, EMAIL_HOST, EMAIL_PORT, EMAIL_TLS, EMAIL_USER, EMAIL_PASSWORD, EMAIL_FROM, EMAIL_NOTIFY_ONSNATCH, EMAIL_NOTIFY_ONDOWNLOAD, EMAIL_NOTIFY_ONSUBTITLEDOWNLOAD, EMAIL_LIST, \ USE_LISTVIEW, METADATA_KODI, METADATA_KODI_12PLUS, METADATA_MEDIABROWSER, METADATA_PS3, metadata_provider_dict, \ NEWZBIN, NEWZBIN_USERNAME, NEWZBIN_PASSWORD, GIT_PATH, MOVE_ASSOCIATED_FILES, SYNC_FILES, POSTPONE_IF_SYNC_FILES, dailySearchScheduler, NFO_RENAME, \ - GUI_NAME, HOME_LAYOUT, HISTORY_LAYOUT, DISPLAY_SHOW_SPECIALS, COMING_EPS_LAYOUT, COMING_EPS_SORT, COMING_EPS_DISPLAY_PAUSED, COMING_EPS_MISSED_RANGE, DISPLAY_FILESIZE, FUZZY_DATING, TRIM_ZERO, DATE_PRESET, TIME_PRESET, TIME_PRESET_W_SECONDS, THEME_NAME, \ + GUI_NAME, HOME_LAYOUT, HISTORY_LAYOUT, DISPLAY_SHOW_SPECIALS, COMING_EPS_LAYOUT, COMING_EPS_SORT, COMING_EPS_DISPLAY_PAUSED, COMING_EPS_MISSED_RANGE, DISPLAY_FILESIZE, FUZZY_DATING, TRIM_ZERO, DATE_PRESET, TIME_PRESET, TIME_PRESET_W_SECONDS, THEME_NAME, FILTER_ROW, \ 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, subtitlesFinderScheduler, \ @@ -707,7 +710,7 @@ def initialize(consoleLogging=True): try: WEB_PORT = check_setting_int(CFG, 'General', 'web_port', 8081) - except: + except Exception: WEB_PORT = 8081 if WEB_PORT < 21 or WEB_PORT > 65535: @@ -723,6 +726,8 @@ def initialize(consoleLogging=True): if not WEB_COOKIE_SECRET: WEB_COOKIE_SECRET = helpers.generateCookieSecret() + WEB_USE_GZIP = bool(check_setting_int(CFG, 'General', 'web_use_gzip', 1)) + INDEXER_DEFAULT_LANGUAGE = check_setting_str(CFG, 'General', 'indexerDefaultLang', 'en') EP_DEFAULT_DELETED_STATUS = check_setting_int(CFG, 'General', 'ep_default_deleted_status', 6) @@ -1007,6 +1012,7 @@ def initialize(consoleLogging=True): TRAKT_START_PAUSED = bool(check_setting_int(CFG, 'Trakt', 'trakt_start_paused', 0)) TRAKT_USE_RECOMMENDED = bool(check_setting_int(CFG, 'Trakt', 'trakt_use_recommended', 0)) TRAKT_SYNC = bool(check_setting_int(CFG, 'Trakt', 'trakt_sync', 0)) + TRAKT_SYNC_REMOVE = bool(check_setting_int(CFG, 'Trakt', 'trakt_sync_remove', 0)) TRAKT_DEFAULT_INDEXER = check_setting_int(CFG, 'Trakt', 'trakt_default_indexer', 1) TRAKT_DISABLE_SSL_VERIFY = bool(check_setting_int(CFG, 'Trakt', 'trakt_disable_ssl_verify', 0)) TRAKT_TIMEOUT = check_setting_int(CFG, 'Trakt', 'trakt_timeout', 30) @@ -1014,7 +1020,9 @@ def initialize(consoleLogging=True): TRAKT_USE_ROLLING_DOWNLOAD = bool(check_setting_int(CFG, 'Trakt', 'trakt_use_rolling_download', 0)) TRAKT_ROLLING_NUM_EP = check_setting_int(CFG, 'Trakt', 'trakt_rolling_num_ep', 0) TRAKT_ROLLING_ADD_PAUSED = check_setting_int(CFG, 'Trakt', 'trakt_rolling_add_paused', 1) - TRAKT_ROLLING_FREQUENCY = check_setting_int(CFG, 'Trakt', 'trakt_rolling_frequency', 15) + TRAKT_ROLLING_FREQUENCY = check_setting_int(CFG, 'Trakt', 'trakt_rolling_frequency', 8) + if TRAKT_ROLLING_FREQUENCY < 4: + TRAKT_ROLLING_FREQUENCY = 4 CheckSection(CFG, 'pyTivo') USE_PYTIVO = bool(check_setting_int(CFG, 'pyTivo', 'use_pytivo', 0)) @@ -1084,7 +1092,7 @@ def initialize(consoleLogging=True): REQUIRE_WORDS = check_setting_str(CFG, 'General', 'require_words', REQUIRE_WORDS) CALENDAR_UNPROTECTED = bool(check_setting_int(CFG, 'General', 'calendar_unprotected', 0)) - + NO_RESTART = bool(check_setting_int(CFG, 'General', 'no_restart', 0)) EXTRA_SCRIPTS = [x.strip() for x in check_setting_str(CFG, 'General', 'extra_scripts', '').split('|') if @@ -1124,6 +1132,7 @@ def initialize(consoleLogging=True): TIMEZONE_DISPLAY = check_setting_str(CFG, 'GUI', 'timezone_display', 'network') POSTER_SORTBY = check_setting_str(CFG, 'GUI', 'poster_sortby', 'name') POSTER_SORTDIR = check_setting_int(CFG, 'GUI', 'poster_sortdir', 1) + FILTER_ROW = bool(check_setting_int(CFG, 'GUI', 'filter_row', 0)) # initialize NZB and TORRENT providers providerList = providers.makeProviderList() @@ -1337,7 +1346,7 @@ def initialize(consoleLogging=True): silent=not USE_TRAKT) traktRollingScheduler = scheduler.Scheduler(traktChecker.TraktRolling(), - cycleTime=datetime.timedelta(minutes=TRAKT_ROLLING_FREQUENCY), + cycleTime=datetime.timedelta(hours=TRAKT_ROLLING_FREQUENCY), threadName="TRAKTROLLING", silent=not TRAKT_USE_ROLLING_DOWNLOAD) @@ -1366,42 +1375,73 @@ def start(): events.start() # start the daily search scheduler + dailySearchScheduler.enable = True dailySearchScheduler.start() # start the backlog scheduler + backlogSearchScheduler.enable = True backlogSearchScheduler.start() # start the show updater + showUpdateScheduler.enable = True showUpdateScheduler.start() # start the version checker + versionCheckScheduler.enable = True versionCheckScheduler.start() # start the queue checker + showQueueScheduler.enable = True showQueueScheduler.start() # start the search queue checker + searchQueueScheduler.enable = True searchQueueScheduler.start() # start the queue checker if DOWNLOAD_PROPERS: - properFinderScheduler.start() + properFinderScheduler.silent = False + properFinderScheduler.enable = True + else: + properFinderScheduler.enable = False + properFinderScheduler.silent = True + properFinderScheduler.start() # start the proper finder if PROCESS_AUTOMATICALLY: - autoPostProcesserScheduler.start() + autoPostProcesserScheduler.silent = False + autoPostProcesserScheduler.enable = True + else: + autoPostProcesserScheduler.enable = False + autoPostProcesserScheduler.silent = True + autoPostProcesserScheduler.start() # start the subtitles finder if USE_SUBTITLES: - subtitlesFinderScheduler.start() + subtitlesFinderScheduler.silent = False + subtitlesFinderScheduler.enable = True + else: + subtitlesFinderScheduler.enable = False + subtitlesFinderScheduler.silent = True + subtitlesFinderScheduler.start() # start the trakt checker if USE_TRAKT: - traktCheckerScheduler.start() + traktCheckerScheduler.silent = False + traktCheckerScheduler.enable = True + else: + traktCheckerScheduler.enable = False + traktCheckerScheduler.silent = True + traktCheckerScheduler.start() # start the trakt checker if TRAKT_USE_ROLLING_DOWNLOAD and USE_TRAKT: - traktRollingScheduler.start() + traktRollingScheduler.silent = False + traktRollingScheduler.enable = True + else: + traktRollingScheduler.enable = False + traktRollingScheduler.silent = True + traktRollingScheduler.start() started = True @@ -1423,97 +1463,92 @@ def halt(): logger.log(u"Waiting for the EVENTS thread to exit") try: events.join(10) - except: + except Exception: pass dailySearchScheduler.stop.set() logger.log(u"Waiting for the DAILYSEARCH thread to exit") try: dailySearchScheduler.join(10) - except: + except Exception: pass backlogSearchScheduler.stop.set() logger.log(u"Waiting for the BACKLOG thread to exit") try: backlogSearchScheduler.join(10) - except: + except Exception: pass showUpdateScheduler.stop.set() logger.log(u"Waiting for the SHOWUPDATER thread to exit") try: showUpdateScheduler.join(10) - except: + except Exception: pass versionCheckScheduler.stop.set() logger.log(u"Waiting for the VERSIONCHECKER thread to exit") try: versionCheckScheduler.join(10) - except: + except Exception: pass showQueueScheduler.stop.set() logger.log(u"Waiting for the SHOWQUEUE thread to exit") try: showQueueScheduler.join(10) - except: + except Exception: pass searchQueueScheduler.stop.set() logger.log(u"Waiting for the SEARCHQUEUE thread to exit") try: searchQueueScheduler.join(10) - except: + except Exception: pass - if PROCESS_AUTOMATICALLY: - autoPostProcesserScheduler.stop.set() - logger.log(u"Waiting for the POSTPROCESSER thread to exit") - try: - autoPostProcesserScheduler.join(10) - except: - pass + autoPostProcesserScheduler.stop.set() + logger.log(u"Waiting for the POSTPROCESSER thread to exit") + try: + autoPostProcesserScheduler.join(10) + except Exception: + pass - if USE_TRAKT: - traktCheckerScheduler.stop.set() - logger.log(u"Waiting for the TRAKTCHECKER thread to exit") - try: - traktCheckerScheduler.join(10) - except: - pass + traktCheckerScheduler.stop.set() + logger.log(u"Waiting for the TRAKTCHECKER thread to exit") + try: + traktCheckerScheduler.join(10) + except Exception: + pass - if TRAKT_USE_ROLLING_DOWNLOAD and USE_TRAKT: - traktRollingScheduler.stop.set() - logger.log(u"Waiting for the TRAKTROLLING thread to exit") - try: - traktRollingScheduler.join(10) - except: - pass + traktRollingScheduler.stop.set() + logger.log(u"Waiting for the TRAKTROLLING thread to exit") + try: + traktRollingScheduler.join(10) + except Exception: + pass - if DOWNLOAD_PROPERS: - properFinderScheduler.stop.set() - logger.log(u"Waiting for the PROPERFINDER thread to exit") - try: - properFinderScheduler.join(10) - except: - pass + properFinderScheduler.stop.set() + logger.log(u"Waiting for the PROPERFINDER thread to exit") + try: + properFinderScheduler.join(10) + except Exception: + pass - if USE_SUBTITLES: - subtitlesFinderScheduler.stop.set() - logger.log(u"Waiting for the SUBTITLESFINDER thread to exit") - try: - subtitlesFinderScheduler.join(10) - except: - pass + subtitlesFinderScheduler.stop.set() + logger.log(u"Waiting for the SUBTITLESFINDER thread to exit") + try: + subtitlesFinderScheduler.join(10) + except Exception: + pass if ADBA_CONNECTION: ADBA_CONNECTION.logout() logger.log(u"Waiting for the ANIDB CONNECTION thread to exit") try: ADBA_CONNECTION.join(10) - except: + except Exception: pass __INITIALIZED__ = False @@ -1579,6 +1614,7 @@ def save_config(): new_config['General']['web_username'] = WEB_USERNAME new_config['General']['web_password'] = helpers.encrypt(WEB_PASSWORD, ENCRYPTION_VERSION) new_config['General']['web_cookie_secret'] = WEB_COOKIE_SECRET + new_config['General']['web_use_gzip'] = WEB_USE_GZIP new_config['General']['download_url'] = DOWNLOAD_URL new_config['General']['localhost_ip'] = LOCALHOST_IP new_config['General']['cpu_preset'] = CPU_PRESET @@ -1932,6 +1968,7 @@ def save_config(): new_config['Trakt']['trakt_start_paused'] = int(TRAKT_START_PAUSED) new_config['Trakt']['trakt_use_recommended'] = int(TRAKT_USE_RECOMMENDED) new_config['Trakt']['trakt_sync'] = int(TRAKT_SYNC) + new_config['Trakt']['trakt_sync_remove'] = int(TRAKT_SYNC_REMOVE) new_config['Trakt']['trakt_default_indexer'] = int(TRAKT_DEFAULT_INDEXER) new_config['Trakt']['trakt_disable_ssl_verify'] = int(TRAKT_DISABLE_SSL_VERIFY) new_config['Trakt']['trakt_timeout'] = int(TRAKT_TIMEOUT) @@ -2011,7 +2048,8 @@ def save_config(): new_config['GUI']['timezone_display'] = TIMEZONE_DISPLAY new_config['GUI']['poster_sortby'] = POSTER_SORTBY new_config['GUI']['poster_sortdir'] = POSTER_SORTDIR - + new_config['GUI']['filter_row'] = int(FILTER_ROW) + new_config['Subtitles'] = {} new_config['Subtitles']['use_subtitles'] = int(USE_SUBTITLES) new_config['Subtitles']['subtitles_languages'] = ','.join(SUBTITLES_LANGUAGES) @@ -2048,10 +2086,10 @@ def launchBrowser(protocol='http', startPort=None, web_root='/'): try: webbrowser.open(browserURL, 2, 1) - except: + except Exception: try: webbrowser.open(browserURL, 1, 1) - except: + except Exception: logger.log(u"Unable to launch a browser", logger.ERROR) diff --git a/sickbeard/config.py b/sickbeard/config.py index 92644a3e47cfc64f0f6ecb4dc884328b56b5c7a2..94413e32fcfe7ced4e738f164806859fd4961723 100644 --- a/sickbeard/config.py +++ b/sickbeard/config.py @@ -186,7 +186,7 @@ def change_UPDATE_FREQUENCY(freq): sickbeard.versionCheckScheduler.cycleTime = datetime.timedelta(hours=sickbeard.UPDATE_FREQUENCY) def change_SHOWUPDATE_HOUR(freq): - sickbeard.SHOWUPDATE_HOUR = to_int(freq, default=sickbeard.SHOWUPDATE_HOUR) + sickbeard.SHOWUPDATE_HOUR = to_int(freq, default=sickbeard.DEFAULT_SHOWUPDATE_HOUR) if sickbeard.SHOWUPDATE_HOUR > 23: sickbeard.SHOWUPDATE_HOUR = 0 @@ -195,6 +195,13 @@ def change_SHOWUPDATE_HOUR(freq): sickbeard.showUpdateScheduler.start_time = datetime.time(hour=sickbeard.SHOWUPDATE_HOUR) +def change_SUBTITLES_FINDER_FREQUENCY(subtitles_finder_frequency): + + if subtitles_finder_frequency == '' or subtitles_finder_frequency is None: + subtitles_finder_frequency = 1 + + sickbeard.SUBTITLES_FINDER_FREQUENCY = to_int(subtitles_finder_frequency, 1) + def change_VERSION_NOTIFY(version_notify): oldSetting = sickbeard.VERSION_NOTIFY @@ -204,52 +211,103 @@ def change_VERSION_NOTIFY(version_notify): sickbeard.NEWEST_VERSION_STRING = None if oldSetting == False and version_notify == True: - sickbeard.versionCheckScheduler.action.run() # @UndefinedVariable + sickbeard.versionCheckScheduler.forceRun() def change_DOWNLOAD_PROPERS(download_propers): + download_propers = checkbox_to_value(download_propers) + if sickbeard.DOWNLOAD_PROPERS == download_propers: return sickbeard.DOWNLOAD_PROPERS = download_propers if sickbeard.DOWNLOAD_PROPERS: - sickbeard.properFinderScheduler.start() + if not sickbeard.properFinderScheduler.enable: + logger.log(u"Starting PROPERFINDER thread", logger.INFO) + sickbeard.properFinderScheduler.silent = False + sickbeard.properFinderScheduler.enable = True + else: + logger.log(u"Unable to start PROPERFINDER thread. Already running", logger.INFO) else: - sickbeard.properFinderScheduler.stop.set() - logger.log(u"Waiting for the PROPERFINDER thread to exit") - try: - sickbeard.properFinderScheduler.join(10) - except: - pass + sickbeard.properFinderScheduler.enable = False + sickbeard.traktCheckerScheduler.silent = True + logger.log(u"Waiting for the PROPERFINDER thread to exit", logger.INFO) def change_USE_TRAKT(use_trakt): + use_trakt = checkbox_to_value(use_trakt) + if sickbeard.USE_TRAKT == use_trakt: return sickbeard.USE_TRAKT = use_trakt if sickbeard.USE_TRAKT: - sickbeard.traktCheckerScheduler.start() + if not sickbeard.traktCheckerScheduler.enable: + logger.log(u"Starting TRAKTCHECKER thread", logger.INFO) + sickbeard.traktCheckerScheduler.silent = False + sickbeard.traktCheckerScheduler.enable = True + else: + logger.log(u"Unable to start TRAKTCHECKER thread. Already running", logger.INFO) else: - sickbeard.traktCheckerScheduler.stop.set() - logger.log(u"Waiting for the TRAKTCHECKER thread to exit") - try: - sickbeard.traktCheckerScheduler.join(10) - except: - pass + sickbeard.traktCheckerScheduler.enable = False + sickbeard.traktCheckerScheduler.silent = True + logger.log(u"Stopping TRAKTCHECKER thread", logger.INFO) + +def change_TRAKT_USE_ROLLING_DOWNLOAD(trakt_use_rolling_download): + trakt_use_rolling_download = checkbox_to_value(trakt_use_rolling_download) + + if sickbeard.TRAKT_USE_ROLLING_DOWNLOAD == trakt_use_rolling_download: + return + + sickbeard.TRAKT_USE_ROLLING_DOWNLOAD = trakt_use_rolling_download + + if sickbeard.USE_TRAKT and sickbeard.TRAKT_USE_ROLLING_DOWNLOAD: + if not sickbeard.traktRollingScheduler.enable: + logger.log(u"Starting TRAKTROLLING thread", logger.INFO) + sickbeard.traktRollingScheduler.silent = False + sickbeard.traktRollingScheduler.enable = True + else: + logger.log(u"Unable to start TRAKTROLLING thread. Already running", logger.INFO) + else: + sickbeard.traktRollingScheduler.enable = False + sickbeard.traktRollingScheduler.silent = True + logger.log(u"Stopping TRAKTROLLING thread", logger.INFO) def change_USE_SUBTITLES(use_subtitles): + use_subtitles = checkbox_to_value(use_subtitles) + if sickbeard.USE_SUBTITLES == use_subtitles: return sickbeard.USE_SUBTITLES = use_subtitles if sickbeard.USE_SUBTITLES: - sickbeard.subtitlesFinderScheduler.start() + if not sickbeard.subtitlesFinderScheduler.enable: + logger.log(u"Starting SUBTITLESFINDER thread", logger.INFO) + sickbeard.subtitlesFinderScheduler.silent = False + sickbeard.subtitlesFinderScheduler.enable = True + else: + logger.log(u"Unable to start SUBTITLESFINDER thread. Already running", logger.INFO) else: - sickbeard.subtitlesFinderScheduler.stop.set() - logger.log(u"Waiting for the SUBTITLESFINDER thread to exit") - try: - sickbeard.subtitlesFinderScheduler.join(10) - except: - pass + sickbeard.subtitlesFinderScheduler.enable = False + sickbeard.subtitlesFinderScheduler.silent = True + logger.log(u"Stopping SUBTITLESFINDER thread", logger.INFO) + +def change_PROCESS_AUTOMATICALLY(process_automatically): + process_automatically = checkbox_to_value(process_automatically) + + if sickbeard.PROCESS_AUTOMATICALLY == process_automatically: + return + + sickbeard.PROCESS_AUTOMATICALLY = process_automatically + if sickbeard.PROCESS_AUTOMATICALLY: + if not sickbeard.autoPostProcesserScheduler.enable: + logger.log(u"Starting POSTPROCESSER thread", logger.INFO) + sickbeard.autoPostProcesserScheduler.silent = False + sickbeard.autoPostProcesserScheduler.enable = True + else: + logger.log(u"Unable to start POSTPROCESSER thread. Already running", logger.INFO) + else: + logger.log(u"Stopping POSTPROCESSER thread", logger.INFO) + sickbeard.autoPostProcesserScheduler.enable = False + sickbeard.autoPostProcesserScheduler.silent = True def CheckSection(CFG, sec): """ Check if INI section exists, if not create it """ diff --git a/sickbeard/event_queue.py b/sickbeard/event_queue.py index aeb0c000455df09d687883cdd4c12c5cdafac2c4..8e6d2afc7984e2a6536c5e65b30c91c07aa99674 100644 --- a/sickbeard/event_queue.py +++ b/sickbeard/event_queue.py @@ -1,5 +1,9 @@ import threading +import traceback from Queue import Queue, Empty +from sickbeard import logger +from sickbeard.exceptions import ex + class Event: def __init__(self, type): @@ -9,6 +13,7 @@ class Event: def type(self): return self._type + class Events(threading.Thread): def __init__(self, callback): super(Events, self).__init__() @@ -22,23 +27,27 @@ class Events(threading.Thread): self.queue.put(type) def run(self): - while (not self.stop.is_set()): - try: - # get event type - type = self.queue.get(True, 1) - - # perform callback if we got a event type - self.callback(type) - - # event completed - self.queue.task_done() - except Empty: - type = None - - # exiting thread - self.stop.clear() + try: + while (not self.stop.is_set()): + try: + # get event type + type = self.queue.get(True, 1) + + # perform callback if we got a event type + self.callback(type) + + # event completed + self.queue.task_done() + except Empty: + type = None + + # exiting thread + self.stop.clear() + except Exception, e: + logger.log(u"Exception generated in thread " + self.name + ": " + ex(e), logger.ERROR) + logger.log(repr(traceback.format_exc()), logger.DEBUG) # System Events class SystemEvent(Event): RESTART = "RESTART" - SHUTDOWN = "SHUTDOWN" \ No newline at end of file + SHUTDOWN = "SHUTDOWN" diff --git a/sickbeard/helpers.py b/sickbeard/helpers.py index e6a8b20376e786dbe5528bb641c228d91a8c0d83..e3451266735199e79a47db046c9c5364637c2908 100644 --- a/sickbeard/helpers.py +++ b/sickbeard/helpers.py @@ -1521,4 +1521,20 @@ def verify_freespace(src, dest, oldfile=None): return True else: logger.log("Not enough free space: Needed: " + str(neededspace) + " bytes (" + pretty_filesize(neededspace) + "), found: " + str(diskfree) + " bytes (" + pretty_filesize(diskfree) + ")", logger.WARNING) - return False \ No newline at end of file + return False + +# https://gist.github.com/thatalextaylor/7408395 +def pretty_time_delta(seconds): + sign_string = '-' if seconds < 0 else '' + seconds = abs(int(seconds)) + days, seconds = divmod(seconds, 86400) + hours, seconds = divmod(seconds, 3600) + minutes, seconds = divmod(seconds, 60) + if days > 0: + return '%s%dd%02dh%02dm%02ds' % (sign_string, days, hours, minutes, seconds) + elif hours > 0: + return '%s%02dh%02dm%02ds' % (sign_string, hours, minutes, seconds) + elif minutes > 0: + return '%s%02dm%02ds' % (sign_string, minutes, seconds) + else: + return '%s%02ds' % (sign_string, seconds) \ No newline at end of file diff --git a/sickbeard/notifiers/trakt.py b/sickbeard/notifiers/trakt.py index 23908d0755a08ceb228a83e82ece271a59d22239..c35f27b86ad3622df1cf022ff176081edf68bc4c 100644 --- a/sickbeard/notifiers/trakt.py +++ b/sickbeard/notifiers/trakt.py @@ -58,46 +58,32 @@ class TraktNotifier: 'title': ep_obj.show.name, 'year': ep_obj.show.startyear, 'ids': {}, - 'seasons': [ - { - 'number': ep_obj.season, - 'episodes': [ - { - 'number': ep_obj.episode - } - ] - } - ] } ] } - + if trakt_id == 'tvdb_id': data['shows'][0]['ids']['tvdb'] = ep_obj.show.indexerid else: data['shows'][0]['ids']['tvrage'] = ep_obj.show.indexerid + if sickbeard.TRAKT_SYNC_WATCHLIST: + if sickbeard.TRAKT_REMOVE_SERIESLIST: + trakt_api.traktRequest("sync/watchlist/remove", data, method='POST') + + # Add Season and Episode + Related Episodes + data['shows'][0]['seasons']=[{'number': ep_obj.season,'episodes': [] }] + + for relEp_Obj in [ep_obj] + ep_obj.relatedEps: + data['shows'][0]['seasons'][0]['episodes'].append({'number': relEp_Obj.episode}) + + if sickbeard.TRAKT_SYNC_WATCHLIST: + if sickbeard.TRAKT_REMOVE_WATCHLIST: + trakt_api.traktRequest("sync/watchlist/remove", data, method='POST') + # update library trakt_api.traktRequest("sync/collection", data, method='POST') - - if sickbeard.TRAKT_REMOVE_SERIESLIST: - data = { - 'shows': [ - { - 'title': ep_obj.show.name, - 'year': ep_obj.show.startyear, - 'ids': {} - } - ] - } - - if trakt_id == 'tvdb_id': - data['shows'][0]['ids']['tvdb'] = ep_obj.show.indexerid - else: - data['shows'][0]['ids']['tvrage'] = ep_obj.show.indexerid - - trakt_api.traktRequest("sync/watchlist/remove", data, method='POST') - + except (traktException, traktAuthException, traktServerBusy) as e: logger.log(u"Could not connect to Trakt service: %s" % ex(e), logger.WARNING) diff --git a/sickbeard/postProcessor.py b/sickbeard/postProcessor.py index 30234766f65406a85d851e00eb3aa75ed5d0f15b..173c47a2a7670793288b17c61055e5c083f3f16c 100644 --- a/sickbeard/postProcessor.py +++ b/sickbeard/postProcessor.py @@ -931,7 +931,7 @@ class PostProcessor(object): # update the ep info before we rename so the quality & release name go into the name properly sql_l = [] - trakt_data = [] + for cur_ep in [ep_obj] + ep_obj.relatedEps: with cur_ep.lock: @@ -963,15 +963,6 @@ class PostProcessor(object): sql_l.append(cur_ep.get_sql()) - trakt_data.append((cur_ep.season, cur_ep.episode)) - - data = notifiers.trakt_notifier.trakt_episode_data_generate(trakt_data) - - if sickbeard.USE_TRAKT and sickbeard.TRAKT_SYNC_WATCHLIST and sickbeard.TRAKT_REMOVE_WATCHLIST: - logger.log(u"Remove episodes, showid: indexerid " + str(show.indexerid) + ", Title " + str(show.name) + " to Traktv Watchlist", logger.DEBUG) - if data: - notifiers.trakt_notifier.update_watchlist(show, data_episode=data, update="remove") - # Just want to keep this consistent for failed handling right now releaseName = show_name_helpers.determineReleaseName(self.folder_path, self.nzb_name) if releaseName is not None: diff --git a/sickbeard/properFinder.py b/sickbeard/properFinder.py index aff484a1ddeecedb5b1d8ed0a72fe7d843568271..4a0a2d45bf33d960d201e8bf087824787bd3fed4 100644 --- a/sickbeard/properFinder.py +++ b/sickbeard/properFinder.py @@ -41,10 +41,6 @@ class ProperFinder(): self.amActive = False def run(self, force=False): - - if not sickbeard.DOWNLOAD_PROPERS: - return - logger.log(u"Beginning the search for new propers") self.amActive = True diff --git a/sickbeard/providers/eztv.py b/sickbeard/providers/eztv.py index a51dc404d5914ac1df7e7c78736b63bacd7eac45..909725c5daca12bfabd39607f701cbf221df3180 100644 --- a/sickbeard/providers/eztv.py +++ b/sickbeard/providers/eztv.py @@ -93,7 +93,11 @@ class EZTVProvider(generic.TorrentProvider): searchURL = self.urls['show'] % (search_string['imdb_id']) logger.log(u"" + self.name + " search page URL: " + searchURL, logger.DEBUG) - parsedJSON = self.getURL(searchURL, json=True) + try: + parsedJSON = self.getURL(searchURL, json=True) + except ValueError as e: + parsedJSON = None + if not parsedJSON: logger.log(u"" + self.name + " could not retrieve page URL:" + searchURL, logger.DEBUG) return results diff --git a/sickbeard/providers/nyaatorrents.py b/sickbeard/providers/nyaatorrents.py index e4e590f0355cfca2cc111baa113cdb594d9049d6..d2e6d38ddf4ba48443d996a8d4b7939bf633cc20 100644 --- a/sickbeard/providers/nyaatorrents.py +++ b/sickbeard/providers/nyaatorrents.py @@ -73,7 +73,7 @@ class NyaaProvider(generic.TorrentProvider): params = { "term": search_string.encode('utf-8'), - "cats": '1_37', # Limit to English-translated Anime (for now) + "cats": '1_0', # All anime "sort": '2', # Sort Descending By Seeders } diff --git a/sickbeard/providers/oldpiratebay.py b/sickbeard/providers/oldpiratebay.py index 88155e797ba0450f50bf09d8b2c6f0a372ea3a58..49381b2c80b26d3ef349fd3cd4be72a6a6a9b1da 100644 --- a/sickbeard/providers/oldpiratebay.py +++ b/sickbeard/providers/oldpiratebay.py @@ -327,7 +327,8 @@ class OldPirateBayProvider(generic.TorrentProvider): for item in self._doSearch(searchString[0]): title, url = self._get_title_and_url(item) - results.append(classes.Proper(title, url, datetime.datetime.today(), self.show)) + if re.search('(PROPER|REPACK)', title, re.I): + results.append(classes.Proper(title, url, datetime.datetime.today(), self.show)) return results diff --git a/sickbeard/providers/rarbg.py b/sickbeard/providers/rarbg.py index c6afb5d2410df9ed67da54e907212af38b4f2d69..ad8e63d6a7e7302c4b8b8d859b82f48ded7c395f 100644 --- a/sickbeard/providers/rarbg.py +++ b/sickbeard/providers/rarbg.py @@ -44,6 +44,10 @@ from lib.requests.exceptions import RequestException from sickbeard.indexers.indexer_config import INDEXER_TVDB,INDEXER_TVRAGE +class GetOutOfLoop(Exception): + pass + + class RarbgProvider(generic.TorrentProvider): def __init__(self): @@ -60,10 +64,10 @@ class RarbgProvider(generic.TorrentProvider): self.urls = {'url': 'https://rarbg.com', 'token': 'https://torrentapi.org/pubapi.php?get_token=get_token&format=json', - 'listing': 'https://torrentapi.org/pubapi.php?mode=list&token={token}', - 'search': 'https://torrentapi.org/pubapi.php?mode=search&search_string={search_string}&token={token}', - 'search_tvdb': 'https://torrentapi.org/pubapi.php?mode=search&search_tvdb={tvdb}&search_string={search_string}&token={token}', - 'search_tvrage': 'https://torrentapi.org/pubapi.php?mode=search&search_tvrage={tvrage}&search_string={search_string}&token={token}', + 'listing': 'https://torrentapi.org/pubapi.php?mode=list', + 'search': 'https://torrentapi.org/pubapi.php?mode=search&search_string={search_string}', + 'search_tvdb': 'https://torrentapi.org/pubapi.php?mode=search&search_tvdb={tvdb}&search_string={search_string}', + 'search_tvrage': 'https://torrentapi.org/pubapi.php?mode=search&search_tvrage={tvrage}&search_string={search_string}', 'api_spec': 'https://rarbg.com/pubapi/apidocs.txt', } @@ -76,6 +80,7 @@ class RarbgProvider(generic.TorrentProvider): 'limit': '&limit={limit}', 'format': '&format={format}', 'ranked': '&ranked={ranked}', + 'token': '&token={token}', } self.defaultOptions = self.urlOptions['categories'].format(categories='18;41') + \ @@ -116,7 +121,7 @@ class RarbgProvider(generic.TorrentProvider): try: if resp_json['token']: self.token = resp_json['token'] - self.tokenExpireDate = datetime.datetime.now() + datetime.timedelta(minutes=15) + self.tokenExpireDate = datetime.datetime.now() + datetime.timedelta(minutes=14) return True except Exception as e: logger.log(u'{name} provider: No token found'.format(name=self.name), logger.ERROR) @@ -196,21 +201,21 @@ class RarbgProvider(generic.TorrentProvider): for mode in search_params.keys(): #Mode = RSS, Season, Episode for search_string in search_params[mode]: if mode == 'RSS': - searchURL = self.urls['listing'].format(token=self.token) + self.defaultOptions + searchURL = self.urls['listing'] + self.defaultOptions elif mode == 'Season': if ep_indexer == INDEXER_TVDB: - searchURL = self.urls['search_tvdb'].format(token=self.token, search_string=urllib.quote(search_string), tvdb=ep_indexerid) + self.defaultOptions + searchURL = self.urls['search_tvdb'].format(search_string=urllib.quote(search_string), tvdb=ep_indexerid) + self.defaultOptions elif ep_indexer == INDEXER_TVRAGE: - searchURL = self.urls['search_tvrage'].format(token=self.token, search_string=urllib.quote(search_string), tvrage=ep_indexerid) + self.defaultOptions + searchURL = self.urls['search_tvrage'].format(search_string=urllib.quote(search_string), tvrage=ep_indexerid) + self.defaultOptions else: - searchURL = self.urls['search'].format(token=self.token, search_string=urllib.quote(search_string)) + self.defaultOptions + searchURL = self.urls['search'].format(search_string=urllib.quote(search_string)) + self.defaultOptions elif mode == 'Episode': if ep_indexer == INDEXER_TVDB: - searchURL = self.urls['search_tvdb'].format(token=self.token, search_string=urllib.quote(search_string), tvdb=ep_indexerid) + self.defaultOptions + searchURL = self.urls['search_tvdb'].format(search_string=urllib.quote(search_string), tvdb=ep_indexerid) + self.defaultOptions elif ep_indexer == INDEXER_TVRAGE: - searchURL = self.urls['search_tvrage'].format(token=self.token, search_string=urllib.quote(search_string), tvrage=ep_indexerid) + self.defaultOptions + searchURL = self.urls['search_tvrage'].format(search_string=urllib.quote(search_string), tvrage=ep_indexerid) + self.defaultOptions else: - searchURL = self.urls['search'].format(token=self.token, search_string=urllib.quote(search_string)) + self.defaultOptions + searchURL = self.urls['search'].format(search_string=urllib.quote(search_string)) + self.defaultOptions else: logger.log(u'{name} invalid search mode:{mode}'.format(name=self.name, mode=mode), logger.ERROR) @@ -222,36 +227,57 @@ class RarbgProvider(generic.TorrentProvider): logger.log(u'{name} search page URL: {url}'.format(name=self.name, url=searchURL), logger.DEBUG) - time_out = 0 - while (datetime.datetime.now() < self.next_request) and time_out <= 15: - time_out = time_out + 1 - time.sleep(1) - - data = self.getURL(searchURL) - - self.next_request = datetime.datetime.now() + datetime.timedelta(seconds=10) - - if not data: - logger.log(u'{name} no data returned.'.format(name=self.name), logger.DEBUG) - continue - if re.search('ERROR', data): - logger.log(u'{name} returned an error.'.format(name=self.name), logger.DEBUG) - continue - if re.search('No results found', data): - logger.log(u'{name} no results found.'.format(name=self.name), logger.DEBUG) - continue - if re.search('Invalid token set!', data): - logger.log(u'{name} Invalid token set!'.format(name=self.name), logger.ERROR) - return results - if re.search('Too many requests per minute. Please try again later!', data): - logger.log(u'{name} Too many requests per minute.'.format(name=self.name), logger.ERROR) - time.sleep(10) - continue - if re.search('Cant find search_tvdb in database. Are you sure this imdb exists?', data): - logger.log(u'{name} no results found. Search tvdb id do not exist on server.'.format(name=self.name), logger.DEBUG) - continue - if re.search('Cant find search_tvrage in database. Are you sure this imdb exists?', data): - logger.log(u'{name} no results found. Search tvrage id do not exist on server.'.format(name=self.name), logger.DEBUG) + try: + retry = 3 + while retry > 0: + time_out = 0 + while (datetime.datetime.now() < self.next_request) and time_out <= 15: + time_out = time_out + 1 + time.sleep(1) + + data = self.getURL(searchURL + self.urlOptions['token'].format(token=self.token)) + + self.next_request = datetime.datetime.now() + datetime.timedelta(seconds=10) + + if not data: + logger.log(u'{name} no data returned.'.format(name=self.name), logger.DEBUG) + raise GetOutOfLoop + if re.search('ERROR', data): + logger.log(u'{name} returned an error.'.format(name=self.name), logger.DEBUG) + raise GetOutOfLoop + if re.search('No results found', data): + logger.log(u'{name} no results found.'.format(name=self.name), logger.DEBUG) + raise GetOutOfLoop + if re.search('Invalid token set!', data): + logger.log(u'{name} Invalid token set!'.format(name=self.name), logger.ERROR) + return results + if re.search('Too many requests per minute. Please try again later!', data): + logger.log(u'{name} Too many requests per minute.'.format(name=self.name), logger.DEBUG) + retry = retry - 1 + time.sleep(10) + continue + if re.search('Cant find search_tvdb in database. Are you sure this imdb exists?', data): + logger.log(u'{name} no results found. Search tvdb id do not exist on server.'.format(name=self.name), logger.DEBUG) + raise GetOutOfLoop + if re.search('Cant find search_tvrage in database. Are you sure this imdb exists?', data): + logger.log(u'{name} no results found. Search tvrage id do not exist on server.'.format(name=self.name), logger.DEBUG) + raise GetOutOfLoop + if re.search('Invalid token. Use get_token for a new one!', data): + logger.log(u'{name} Invalid token, retrieving new token'.format(name=self.name), logger.DEBUG) + retry = retry - 1 + self.token = None + self.tokenExpireDate = None + if not self._doLogin(): + logger.log(u'{name} Failed retrieving new token'.format(name=self.name), logger.DEBUG) + return results + logger.log(u'{name} Using new token'.format(name=self.name), logger.DEBUG) + continue + #No error found break + break + else: + logger.log(u'{name} Retried 3 times without getting results.'.format(name=self.name), logger.DEBUG) + continue + except GetOutOfLoop: continue try: @@ -269,12 +295,10 @@ class RarbgProvider(generic.TorrentProvider): torrent_download = item['d'] if torrent_title and torrent_download: items[mode].append((torrent_title, torrent_download)) - logger.log(u'{name} found valid result: {title}'.format(name=self.name, title=torrent_title), logger.DEBUG) else: logger.log(u'{name} skipping invalid result'.format(name=self.name), logger.DEBUG) except Exception: logger.log(u'{name} skipping invalid result: {traceback_info}'.format(name=self.name, traceback_info=traceback.format_exc()), logger.DEBUG) - except Exception: logger.log(u'{name} failed parsing data: {traceback_info}'.format(name=self.name, traceback_info=traceback.format_exc()), logger.ERROR) results += items[mode] diff --git a/sickbeard/scheduler.py b/sickbeard/scheduler.py index 92e57e6aa05cbabf913f110df0c1da8b20953d91..65686caa2420f7c4e43483c2fc8a6f633d992222 100644 --- a/sickbeard/scheduler.py +++ b/sickbeard/scheduler.py @@ -30,7 +30,13 @@ class Scheduler(threading.Thread): start_time=None, threadName="ScheduledThread", silent=True): super(Scheduler, self).__init__() - self.lastRun = datetime.datetime.now() + run_delay - cycleTime + self.run_delay = run_delay + if start_time is None: + self.lastRun = datetime.datetime.now() + self.run_delay - cycleTime + else: + # Set last run to the last full hour + temp_now = datetime.datetime.now() + cycleTime + self.lastRun = datetime.datetime(temp_now.year, temp_now.month, temp_now.day, temp_now.hour, 0, 0, 0) + self.run_delay - cycleTime self.action = action self.cycleTime = cycleTime self.start_time = start_time @@ -39,13 +45,25 @@ class Scheduler(threading.Thread): self.silent = silent self.stop = threading.Event() self.force = False + self.enable = False def timeLeft(self): - return self.cycleTime - (datetime.datetime.now() - self.lastRun) + if self.isAlive(): + if self.start_time is None: + return self.cycleTime - (datetime.datetime.now() - self.lastRun) + else: + time_now = datetime.datetime.now() + start_time_today = datetime.datetime.combine(time_now.date(), self.start_time) + start_time_tomorrow = datetime.datetime.combine(time_now.date(), self.start_time) + datetime.timedelta(days=1) + if time_now.hour >= self.start_time.hour: + return start_time_tomorrow - time_now + elif time_now.hour < self.start_time.hour: + return start_time_today - time_now + else: + return datetime.timedelta(seconds=0) def forceRun(self): if not self.action.amActive: - self.lastRun = datetime.datetime.fromordinal(1) self.force = True return True return False @@ -53,36 +71,35 @@ class Scheduler(threading.Thread): def run(self): try: while not self.stop.is_set(): - - current_time = datetime.datetime.now() - should_run = False - - # check if interval has passed - if current_time - self.lastRun >= self.cycleTime: - # check if wanting to start around certain time taking interval into account - if self.start_time: - hour_diff = current_time.time().hour - self.start_time.hour - if not hour_diff < 0 and hour_diff < self.cycleTime.seconds / 3600: - should_run = True - else: - # set lastRun to only check start_time after another cycleTime - self.lastRun = current_time - else: + if self.enable: + current_time = datetime.datetime.now() + should_run = False + #Is self.force enable + if self.force: should_run = True + # check if interval has passed + elif current_time - self.lastRun >= self.cycleTime: + # check if wanting to start around certain time taking interval into account + if self.start_time: + hour_diff = current_time.time().hour - self.start_time.hour + if not hour_diff < 0 and hour_diff < self.cycleTime.seconds / 3600: + should_run = True + else: + # set lastRun to only check start_time after another cycleTime + self.lastRun = current_time + else: + should_run = True - if should_run: - self.lastRun = current_time - - if not self.silent: - logger.log(u"Starting new thread: " + self.name, logger.DEBUG) - - self.action.run(self.force) + if should_run: + self.lastRun = current_time + if not self.silent: + logger.log(u"Starting new thread: " + self.name, logger.DEBUG) + self.action.run(self.force) - if self.force: - self.force = False + if self.force: + self.force = False time.sleep(1) - # exiting thread self.stop.clear() except Exception, e: diff --git a/sickbeard/searchBacklog.py b/sickbeard/searchBacklog.py index 6273a46f59fce2a0be27562499d5c1c6fdc8ef94..582709640e3e9b5508f25ed61d44074c6733920a 100644 --- a/sickbeard/searchBacklog.py +++ b/sickbeard/searchBacklog.py @@ -105,7 +105,7 @@ class BacklogSearcher: backlog_queue_item = search_queue.BacklogQueueItem(curShow, segment) sickbeard.searchQueueScheduler.action.add_item(backlog_queue_item) # @UndefinedVariable else: - logger.log(u"Nothing needs to be downloaded for " + str(curShow.name) + ", skipping",logger.DEBUG) + logger.log(u"Nothing needs to be downloaded for {show_name}, skipping".format(show_name=curShow.name),logger.DEBUG) # don't consider this an actual backlog search if we only did recent eps # or if we only did certain shows @@ -137,7 +137,7 @@ class BacklogSearcher: def _get_segments(self, show, fromDate): anyQualities, bestQualities = common.Quality.splitQuality(show.quality) # @UnusedVariable - logger.log(u"Seeing if we need anything from " + show.name) + logger.log(u"Seeing if we need anything from {show_name}".format(show_name=show.name)) myDB = db.DBConnection() if show.air_by_date: diff --git a/sickbeard/show_queue.py b/sickbeard/show_queue.py index 1c094925c91f637a137a9ed40af9711918f1b9f0..5fced4e47087b2b8e7fd70fd7888c05f2834637e 100644 --- a/sickbeard/show_queue.py +++ b/sickbeard/show_queue.py @@ -212,6 +212,9 @@ class QueueItemAdd(ShowQueueItem): if sickbeard.TRAKT_USE_ROLLING_DOWNLOAD and sickbeard.USE_TRAKT: self.paused = sickbeard.TRAKT_ROLLING_ADD_PAUSED + # Process add show in priority + self.priority = generic_queue.QueuePriorities.HIGH + self.show = None # this will initialize self.show to None diff --git a/sickbeard/subtitles.py b/sickbeard/subtitles.py index dce615856dfc1523a1dd53faadbd16c7963fbf03..a3ba16d383960642d3433ed706624509b4514f27 100644 --- a/sickbeard/subtitles.py +++ b/sickbeard/subtitles.py @@ -87,6 +87,9 @@ class SubtitlesFinder(): and download subtitles. Only if the defined rule is true """ def run(self, force=False): + if not sickbeard.USE_SUBTITLES: + return + if len(sickbeard.subtitles.getEnabledServiceList()) < 1: logger.log(u'Not enough services selected. At least 1 service is required to search subtitles in the background', logger.ERROR) return diff --git a/sickbeard/traktChecker.py b/sickbeard/traktChecker.py index 921e37c7b081265f51a65fbb4246e84942707f82..4bc4ce825e7564903a13d347ff616c0ae6b80c5c 100644 --- a/sickbeard/traktChecker.py +++ b/sickbeard/traktChecker.py @@ -37,7 +37,7 @@ from trakt.exceptions import traktException def setEpisodeToWanted(show, s, e): """ - Sets an episode to wanted, only is it is currently skipped + Sets an episode to wanted, only if it is currently skipped """ epObj = show.getEpisode(int(s), int(e)) if epObj: @@ -62,16 +62,14 @@ def setEpisodeToWanted(show, s, e): class TraktChecker(): def __init__(self): - self.todoWanted = [] self.trakt_api = TraktAPI(sickbeard.TRAKT_API_KEY, sickbeard.TRAKT_USERNAME, sickbeard.TRAKT_PASSWORD, sickbeard.TRAKT_DISABLE_SSL_VERIFY, sickbeard.TRAKT_TIMEOUT) self.todoBacklog = [] - self.ShowWatchlist = [] - self.EpisodeWatchlist = [] + self.todoWanted = [] + self.ShowWatchlist = {} + self.EpisodeWatchlist = {} + self.Collectionlist = {} def run(self, force=False): - if not sickbeard.USE_TRAKT: - logger.log(u"Trakt integration disabled, quit", logger.DEBUG) - return # add shows from trakt.tv watchlist if sickbeard.TRAKT_SYNC_WATCHLIST: @@ -87,8 +85,7 @@ class TraktChecker(): try: # sync trakt.tv library with sickrage library - if sickbeard.TRAKT_SYNC: - self.syncLibrary() + self.syncLibrary() except Exception: logger.log(traceback.format_exc(), logger.DEBUG) @@ -112,12 +109,6 @@ class TraktChecker(): return traktShow - def syncLibrary(self): - logger.log(u"Syncing Trakt.tv show library", logger.DEBUG) - - for myShow in sickbeard.showList: - self.addShowToTraktLibrary(myShow) - def removeShowFromTraktLibrary(self, show_obj): if self.findShow(show_obj.indexer, show_obj.indexerid): trakt_id = sickbeard.indexerApi(show_obj.indexer).config['trakt_id'] @@ -178,115 +169,161 @@ class TraktChecker(): except traktException as e: logger.log(u"Could not connect to Trakt service: %s" % ex(e), logger.WARNING) return + + def syncLibrary(self): + if sickbeard.TRAKT_SYNC and sickbeard.USE_TRAKT: + logger.log(u"Sync SickRage with Trakt Collection", logger.DEBUG) - def syncWatchlist(self): + if self._getShowCollection(): + self.addEpisodeToTraktCollection() + if sickbeard.TRAKT_SYNC_REMOVE: + self.removeEpisodeFromTraktCollection() - logger.log(u"Syncing Trakt.tv show watchlist", logger.DEBUG) + def removeEpisodeFromTraktCollection(self): + if sickbeard.TRAKT_SYNC_REMOVE and sickbeard.TRAKT_SYNC and sickbeard.USE_TRAKT: + logger.log(u"COLLECTION::REMOVE::START - Look for Episodes to Remove From Trakt Collection", logger.DEBUG) - logger.log(u"Getting ShowWatchlist", logger.DEBUG) - if self._getShowWatchlist(): - self.addShowToTraktWatchList() - self.updateShows() + myDB = db.DBConnection() + sql_selection='select tv_shows.indexer, tv_shows.startyear, showid, show_name, season, episode, tv_episodes.status, tv_episodes.location from tv_episodes,tv_shows where tv_shows.indexer_id = tv_episodes.showid' + episodes = myDB.select(sql_selection) + if episodes is not None: + trakt_data = [] + for cur_episode in episodes: + trakt_id = sickbeard.indexerApi(cur_episode["indexer"]).config['trakt_id'] + if self._checkInList(trakt_id,str(cur_episode["showid"]),str(cur_episode["season"]),str(cur_episode["episode"]), List='Collection'): + if cur_episode["location"] == '': + logger.log(u"Removing Episode: Indexer %s %s - %s - S%sE%s from Collection" % (trakt_id,str(cur_episode["showid"]), cur_episode["show_name"],str(cur_episode["season"]),str(cur_episode["episode"])), logger.DEBUG) + trakt_data.append((cur_episode["showid"],cur_episode["indexer"],cur_episode["show_name"],cur_episode["startyear"],cur_episode["season"], cur_episode["episode"])) + + if len(trakt_data): + data = self.trakt_bulk_data_generate(trakt_data) + self.trakt_api.traktRequest("sync/collection/remove", data, method='POST') + self._getShowCollection() + + logger.log(u"COLLECTION::REMOVE::FINISH - Look for Episodes to Remove From Trakt Collection", logger.DEBUG) + + def addEpisodeToTraktCollection(self): + if sickbeard.TRAKT_SYNC and sickbeard.USE_TRAKT: + logger.log(u"COLLECTION::ADD::START - Look for Episodes to Add to Trakt Collection", logger.DEBUG) - logger.log(u"Getting EpisodeWatchlist", logger.DEBUG) - if self._getEpisodeWatchlist(): - self.removeEpisodeFromTraktWatchList() - self.addEpisodeToTraktWatchList() - self.updateEpisodes() + myDB = db.DBConnection() + sql_selection='select tv_shows.indexer, tv_shows.startyear, showid, show_name, season, episode from tv_episodes,tv_shows where tv_shows.indexer_id = tv_episodes.showid and tv_episodes.status in ('+','.join([str(x) for x in Quality.DOWNLOADED + [ARCHIVED]])+')' + episodes = myDB.select(sql_selection) + if episodes is not None: + trakt_data = [] + for cur_episode in episodes: + trakt_id = sickbeard.indexerApi(cur_episode["indexer"]).config['trakt_id'] + if not self._checkInList(trakt_id,str(cur_episode["showid"]),str(cur_episode["season"]),str(cur_episode["episode"]), List='Collection'): + logger.log(u"Adding Episode: Indexer %s %s - %s - S%sE%s to Collection" % (trakt_id,str(cur_episode["showid"]), cur_episode["show_name"],str(cur_episode["season"]),str(cur_episode["episode"])), logger.DEBUG) + trakt_data.append((cur_episode["showid"],cur_episode["indexer"],cur_episode["show_name"],cur_episode["startyear"],cur_episode["season"], cur_episode["episode"])) - def removeEpisodeFromTraktWatchList(self): + if len(trakt_data): + data = self.trakt_bulk_data_generate(trakt_data) + self.trakt_api.traktRequest("sync/collection", data, method='POST') + self._getShowCollection() - logger.log(u"Start looking if some episode has to be removed from watchlist", logger.DEBUG) + logger.log(u"COLLECTION::ADD::FINISH - Look for Episodes to Add to Trakt Collection", logger.DEBUG) + + def syncWatchlist(self): + if sickbeard.TRAKT_SYNC_WATCHLIST and sickbeard.USE_TRAKT: + logger.log(u"Sync SickRage with Trakt Watchlist", logger.DEBUG) - if not len(self.EpisodeWatchlist): - logger.log(u"No episode found in your watchlist, aborting watchlist update", logger.DEBUG) - return True + if self._getShowWatchlist(): + self.addShowToTraktWatchList() + self.updateShows() - trakt_data = [] - for episode in self.EpisodeWatchlist: - tvdb_id = int(episode['show']['ids']['tvdb']) - tvrage_id = int(episode['show']['ids']['tvrage'] or 0) - newShow = helpers.findCertainShow(sickbeard.showList, [tvdb_id, tvrage_id]) - if newShow is not None: - ep_obj = newShow.getEpisode(int(episode['episode']['season']), int(episode['episode']['number'])) - if ep_obj is not None: - if ep_obj.status != WANTED and ep_obj.status != UNKNOWN and ep_obj.status not in Quality.SNATCHED and ep_obj.status not in Quality.SNATCHED_PROPER: - logger.log(u"Removing episode: Indexer " + str(newShow.indexer) + ", indexer_id " + str(newShow.indexerid) + ", Title " + str(newShow.name) + ", Season " + str(episode['episode']['season']) + ", Episode " + str(episode['episode']['number']) + ", Status " + str(ep_obj.status) + " from Watchlist", logger.DEBUG) - trakt_data.append((ep_obj.season, ep_obj.episode)) - else: - logger.log(u"Episode: Indexer " + str(newShow.indexer) + ", indexer_id " + str(newShow.indexerid) + ", Title " + str(newShow.name) + ", Season " + str(episode['episode']["season"]) + ", Episode" + str(episode['episode']["number"]) + " not in Sickberad ShowList", logger.DEBUG) - continue - else: - logger.log(u"Show: tvdb_id " + str(episode['show']['ids']['tvdb']) + ", Title " + str(episode['show']['title']) + " not in Sickberad ShowList", logger.DEBUG) - continue + if self._getEpisodeWatchlist(): + self.removeEpisodeFromTraktWatchList() + self.addEpisodeToTraktWatchList() + self.updateEpisodes() - if len(trakt_data): - data = notifiers.trakt_notifier.trakt_episode_data_generate(trakt_data) - notifiers.trakt_notifier.update_watchlist(newShow, data_episode=data, update="remove") - self._getEpisodeWatchlist() + def removeEpisodeFromTraktWatchList(self): + if sickbeard.TRAKT_SYNC_WATCHLIST and sickbeard.USE_TRAKT: + logger.log(u"WATCHLIST::REMOVE::START - Look for Episodes to Remove from Trakt Watchlist", logger.DEBUG) - logger.log(u"Stop looking if some episode has to be removed from watchlist", logger.DEBUG) + myDB = db.DBConnection() + sql_selection='select tv_shows.indexer, tv_shows.startyear, showid, show_name, season, episode, tv_episodes.status from tv_episodes,tv_shows where tv_shows.indexer_id = tv_episodes.showid' + episodes = myDB.select(sql_selection) + if episodes is not None: + trakt_data = [] + for cur_episode in episodes: + trakt_id = sickbeard.indexerApi(cur_episode["indexer"]).config['trakt_id'] + if self._checkInList(trakt_id,str(cur_episode["showid"]),str(cur_episode["season"]),str(cur_episode["episode"])): + if cur_episode["status"] not in Quality.SNATCHED + Quality.SNATCHED_PROPER + [UNKNOWN] + [WANTED]: + logger.log(u"Removing Episode: Indexer %s %s - %s - S%sE%s from Watchlist" % (trakt_id,str(cur_episode["showid"]), cur_episode["show_name"],str(cur_episode["season"]),str(cur_episode["episode"])), logger.DEBUG) + trakt_data.append((cur_episode["showid"],cur_episode["indexer"],cur_episode["show_name"],cur_episode["startyear"],cur_episode["season"], cur_episode["episode"])) - def addEpisodeToTraktWatchList(self): + if len(trakt_data): + data = self.trakt_bulk_data_generate(trakt_data) + self.trakt_api.traktRequest("sync/watchlist/remove", data, method='POST') + self._getEpisodeWatchlist() + logger.log(u"WATCHLIST::REMOVE::FINISH - Look for Episodes to Remove from Trakt Watchlist", logger.DEBUG) + + def addEpisodeToTraktWatchList(self): if sickbeard.TRAKT_SYNC_WATCHLIST and sickbeard.USE_TRAKT: - logger.log(u"Start looking if some WANTED episode need to be added to watchlist", logger.DEBUG) + logger.log(u"WATCHLIST::ADD::START - Look for Episodes to Add to Trakt Watchlist", logger.DEBUG) myDB = db.DBConnection() - sql_selection='select tv_shows.indexer, showid, show_name, season, episode from tv_episodes,tv_shows where tv_shows.indexer_id = tv_episodes.showid and tv_episodes.status in ('+','.join([str(x) for x in Quality.SNATCHED + Quality.SNATCHED_PROPER + [WANTED]])+')' + sql_selection='select tv_shows.indexer, tv_shows.startyear, showid, show_name, season, episode from tv_episodes,tv_shows where tv_shows.indexer_id = tv_episodes.showid and tv_episodes.status in ('+','.join([str(x) for x in Quality.SNATCHED + Quality.SNATCHED_PROPER + [WANTED]])+')' episodes = myDB.select(sql_selection) if episodes is not None: trakt_data = [] for cur_episode in episodes: - newShow = helpers.findCertainShow(sickbeard.showList, int(cur_episode["showid"])) - if not self.check_watchlist(newShow, cur_episode["season"], cur_episode["episode"]): - logger.log(u"Episode: Indexer " + str(cur_episode["indexer"]) + ", indexer_id " + str(cur_episode["showid"])+ ", Title " + str(cur_episode["show_name"]) + " " + str(cur_episode["season"]) + "x" + str(cur_episode["episode"]) + " should be added to watchlist", logger.DEBUG) - trakt_data.append((cur_episode["season"], cur_episode["episode"])) + trakt_id = sickbeard.indexerApi(cur_episode["indexer"]).config['trakt_id'] + if not self._checkInList(trakt_id,str(cur_episode["showid"]),str(cur_episode["season"]),str(cur_episode["episode"])): + logger.log(u"Adding Episode: Indexer %s %s - %s - S%sE%s to Watchlist" % (trakt_id,str(cur_episode["showid"]), cur_episode["show_name"],str(cur_episode["season"]),str(cur_episode["episode"])), logger.DEBUG) + trakt_data.append((cur_episode["showid"],cur_episode["indexer"],cur_episode["show_name"],cur_episode["startyear"],cur_episode["season"], cur_episode["episode"])) if len(trakt_data): - data = notifiers.trakt_notifier.trakt_episode_data_generate(trakt_data) - notifiers.trakt_notifier.update_watchlist(newShow, data_episode=data) + data = self.trakt_bulk_data_generate(trakt_data) + self.trakt_api.traktRequest("sync/watchlist", data, method='POST') self._getEpisodeWatchlist() - logger.log(u"Stop looking if some WANTED episode need to be added to watchlist", logger.DEBUG) + logger.log(u"WATCHLIST::ADD::FINISH - Look for Episodes to Add to Trakt Watchlist", logger.DEBUG) def addShowToTraktWatchList(self): - if sickbeard.TRAKT_SYNC_WATCHLIST and sickbeard.USE_TRAKT: - logger.log(u"Start looking if some show need to be added to watchlist", logger.DEBUG) - + logger.log(u"SHOW_WATCHLIST::ADD::START - Look for Shows to Add to Trakt Watchlist", logger.DEBUG) + if sickbeard.showList is not None: trakt_data = [] for show in sickbeard.showList: - if not self.check_watchlist(show): - logger.log(u"Show: Indexer " + str(show.indexer) + ", indexer_id " + str(show.indexerid) + ", Title " + str(show.name) + " should be added to watchlist", logger.DEBUG) - trakt_data.append((show.indexer, show.indexerid, show.name, show.startyear)) + trakt_id = sickbeard.indexerApi(show.indexer).config['trakt_id'] + if not self._checkInList(trakt_id,str(show.indexerid),'0','0',List='Show'): + logger.log(u"Adding Show: Indexer %s %s - %s to Watchlist" % ( trakt_id,str(show.indexerid), show.name), logger.DEBUG) + show_el = {'title': show.name, 'year': show.startyear, 'ids': {}} + if trakt_id == 'tvdb_id': + show_el['ids']['tvdb'] = show.indexerid + else: + show_el['ids']['tvrage'] = show.indexerid + trakt_data.append(show_el) if len(trakt_data): - data = notifiers.trakt_notifier.trakt_show_data_generate(trakt_data) - notifiers.trakt_notifier.update_watchlist(data_show=data) + data = {'shows': trakt_data} + self.trakt_api.traktRequest("sync/watchlist", data, method='POST') self._getShowWatchlist() - logger.log(u"Stop looking if some show need to be added to watchlist", logger.DEBUG) + logger.log(u"SHOW_WATCHLIST::ADD::FINISH - Look for Shows to Add to Trakt Watchlist", logger.DEBUG) def updateShows(self): - logger.log(u"Starting trakt show watchlist check", logger.DEBUG) + logger.log(u"SHOW_WATCHLIST::CHECK::START - Trakt Show Watchlist", logger.DEBUG) if not len(self.ShowWatchlist): logger.log(u"No shows found in your watchlist, aborting watchlist update", logger.DEBUG) return - for show in self.ShowWatchlist: - indexer = int(sickbeard.TRAKT_DEFAULT_INDEXER) - if indexer == 2: - indexer_id = int(show["show"]["ids"]["tvrage"]) - else: - indexer_id = int(show["show"]["ids"]["tvdb"]) + indexer = int(sickbeard.TRAKT_DEFAULT_INDEXER) + trakt_id = sickbeard.indexerApi(indexer).config['trakt_id'] + for show_el in self.ShowWatchlist[trakt_id]: + indexer_id = int(str(show_el)) + show = self.ShowWatchlist[trakt_id][show_el] + #logger.log(u"Checking Show: %s %s %s" % (trakt_id, indexer_id, show['title']),logger.DEBUG) if int(sickbeard.TRAKT_METHOD_ADD) != 2: - self.addDefaultShow(indexer, indexer_id, show["show"]["title"], SKIPPED) + self.addDefaultShow(indexer, indexer_id, show['title'], SKIPPED) else: - self.addDefaultShow(indexer, indexer_id, show["show"]["title"], WANTED) + self.addDefaultShow(indexer, indexer_id, show['title'], WANTED) if int(sickbeard.TRAKT_METHOD_ADD) == 1: newShow = helpers.findCertainShow(sickbeard.showList, indexer_id) @@ -294,38 +331,49 @@ class TraktChecker(): setEpisodeToWanted(newShow, 1, 1) else: self.todoWanted.append((indexer_id, 1, 1)) - + logger.log(u"SHOW_WATCHLIST::CHECK::FINISH - Trakt Show Watchlist", logger.DEBUG) + def updateEpisodes(self): """ Sets episodes to wanted that are in trakt watchlist """ - logger.log(u"Starting trakt episode watchlist check", logger.DEBUG) + logger.log(u"SHOW_WATCHLIST::CHECK::START - Trakt Episode Watchlist", logger.DEBUG) if not len(self.EpisodeWatchlist): logger.log(u"No episode found in your watchlist, aborting episode update", logger.DEBUG) return managed_show = [] - for show in self.EpisodeWatchlist: - indexer = int(sickbeard.TRAKT_DEFAULT_INDEXER) - if indexer == 2: - indexer_id = int(show["show"]["ids"]["tvrage"]) - else: - indexer_id = int(show["show"]["ids"]["tvdb"]) + + indexer = int(sickbeard.TRAKT_DEFAULT_INDEXER) + trakt_id = sickbeard.indexerApi(indexer).config['trakt_id'] + + for show_el in self.EpisodeWatchlist[trakt_id]: + indexer_id = int(show_el) + show = self.EpisodeWatchlist[trakt_id][show_el] + newShow = helpers.findCertainShow(sickbeard.showList, indexer_id) + try: if newShow is None: if indexer_id not in managed_show: - self.addDefaultShow(indexer, indexer_id, show["show"]["title"], SKIPPED) + self.addDefaultShow(indexer, indexer_id, show['title'], SKIPPED) managed_show.append(indexer_id) - self.todoWanted.append((indexer_id, show['episode']['season'], show['episode']['number'])) + for season_el in show['seasons']: + season = int(season_el) + for episode_el in show['seasons'][season_el]['episodes']: + self.todoWanted.append((indexer_id, season, int(episode_el))) else: if newShow.indexer == indexer: - setEpisodeToWanted(newShow, show['episode']['season'], show['episode']['number']) + for season_el in show['seasons']: + season = int(season_el) + for episode_el in show['seasons'][season_el]['episodes']: + setEpisodeToWanted(newShow, season, int(episode_el)) except TypeError: - logger.log(u"Could not parse the output from trakt for " + show["show"]["title"], logger.DEBUG) - + logger.log(u"Could not parse the output from trakt for " + show["title"], logger.DEBUG) + logger.log(u"SHOW_WATCHLIST::CHECK::FINISH - Trakt Episode Watchlist", logger.DEBUG) + def addDefaultShow(self, indexer, indexer_id, name, status): """ Adds a new show with the default settings @@ -363,55 +411,215 @@ class TraktChecker(): self.todoWanted.remove(episode) setEpisodeToWanted(show, episode[1], episode[2]) + def _checkInList(self,trakt_id, showid, season, episode, List = None): + """ + Check in the Watchlist or CollectionList for Show + Is the Show, Season and Episode in the trakt_id list (tvdb / tvrage) + """ + #logger.log(u"Checking Show: %s %s %s " % (trakt_id, showid, List),logger.DEBUG) + + if "Collection" == List: + try: + if self.Collectionlist[trakt_id][showid]['seasons'][season]['episodes'][episode] == episode: + return True + except: + return False + elif "Show" == List: + try: + if self.ShowWatchlist[trakt_id][showid]['id'] == showid: + return True + except: + return False + else: + try: + if self.EpisodeWatchlist[trakt_id][showid]['seasons'][season]['episodes'][episode] == episode: + return True + except: + return False + def _getShowWatchlist(self): - + """ + Get Watchlist and parse once into addressable structure + """ + try: - self.ShowWatchlist = self.trakt_api.traktRequest("sync/watchlist/shows") + self.ShowWatchlist = { 'tvdb_id' : {}, 'tvrage_id': {} } + TraktShowWatchlist = self.trakt_api.traktRequest("sync/watchlist/shows") + tvdb_id = 'tvdb' + tvrage_id = 'tvrage' + + for watchlist_el in TraktShowWatchlist: + tvdb = False + tvrage = False + if not watchlist_el['show']['ids']["tvdb"] is None: + tvdb = True + if not watchlist_el['show']['ids']["tvrage"] is None: + tvrage = True + + title = watchlist_el['show']['title'] + year = str(watchlist_el['show']['year']) + + if tvdb: + showid = str(watchlist_el['show']['ids'][tvdb_id]) + self.ShowWatchlist[tvdb_id + '_id'][showid] = { 'id': showid , 'title' : title , 'year': year } + if tvrage: + showid = str(watchlist_el['show']['ids'][tvrage_id]) + self.ShowWatchlist[tvrage_id + '_id'][showid] = { 'id': showid , 'title' : title , 'year': year } + except traktException as e: logger.log(u"Could not connect to trakt service, cannot download Show Watchlist: %s" % ex(e), logger.ERROR) return False return True - + def _getEpisodeWatchlist(self): - + """ + Get Watchlist and parse once into addressable structure + """ + try: - self.EpisodeWatchlist = self.trakt_api.traktRequest("sync/watchlist/episodes") + self.EpisodeWatchlist = { 'tvdb_id' : {}, 'tvrage_id': {} } + TraktEpisodeWatchlist = self.trakt_api.traktRequest("sync/watchlist/episodes") + tvdb_id = 'tvdb' + tvrage_id = 'tvrage' + + for watchlist_el in TraktEpisodeWatchlist: + tvdb = False + tvrage = False + if not watchlist_el['show']['ids']["tvdb"] is None: + tvdb = True + if not watchlist_el['show']['ids']["tvrage"] is None: + tvrage = True + + title = watchlist_el['show']['title'] + year = str(watchlist_el['show']['year']) + season = str(watchlist_el['episode']['season']) + episode = str(watchlist_el['episode']['number']) + + if tvdb: + showid = str(watchlist_el['show']['ids'][tvdb_id]) + if not showid in self.EpisodeWatchlist[tvdb_id + '_id'].keys(): + self.EpisodeWatchlist[tvdb_id + '_id'][showid] = { 'id': showid , 'title' : title , 'year': year , 'seasons' : {} } + + if not season in self.EpisodeWatchlist[tvdb_id + '_id'][showid]['seasons'].keys(): + self.EpisodeWatchlist[tvdb_id + '_id'][showid]['seasons'][season] = { 's': season , 'episodes' : {} } + + if not episode in self.EpisodeWatchlist[tvdb_id + '_id'][showid]['seasons'][season]['episodes'].keys(): + self.EpisodeWatchlist[tvdb_id + '_id'][showid]['seasons'][season]['episodes'][episode] = episode + + if tvrage: + showid = str(watchlist_el['show']['ids'][tvrage_id]) + if not showid in self.EpisodeWatchlist[tvrage_id + '_id'].keys(): + self.EpisodeWatchlist[tvrage_id + '_id'][showid] = { 'id': showid , 'title' : title , 'year': year , 'seasons' : {} } + + if not season in self.EpisodeWatchlist[tvrage_id + '_id'][showid]['seasons'].keys(): + self.EpisodeWatchlist[tvrage_id + '_id'][showid]['seasons'][season] = { 's': season , 'episodes' : {} } + + if not episode in self.EpisodeWatchlist[tvrage_id + '_id'][showid]['seasons'][season]['episodes'].keys(): + self.EpisodeWatchlist[tvrage_id + '_id'][showid]['seasons'][season]['episodes'][episode] = episode + except traktException as e: logger.log(u"Could not connect to trakt service, cannot download Episode Watchlist: %s" % ex(e), logger.WARNING) return False return True - def check_watchlist (self, show_obj, season=None, episode=None): - - found = False - if episode is not None: - watchlist = self.EpisodeWatchlist - else: - watchlist = self.ShowWatchlist - - trakt_id = sickbeard.indexerApi(show_obj.indexer).config['trakt_id'] + def _getShowCollection(self): + """ + Get Collection and parse once into addressable structure + """ - for watchlist_el in watchlist: - - if trakt_id == 'tvdb_id': - indexer_id = int(watchlist_el['show']['ids']["tvdb"]) - else: + try: + + self.Collectionlist = { 'tvdb_id' : {}, 'tvrage_id': {} } + logger.log(u"Getting Show Collection", logger.DEBUG) + TraktCollectionList = self.trakt_api.traktRequest("sync/collection/shows") + tvdb_id = 'tvdb' + tvrage_id = 'tvrage' + + for watchlist_el in TraktCollectionList: + tvdb = False + tvrage = False + if not watchlist_el['show']['ids']["tvdb"] is None: + tvdb = True if not watchlist_el['show']['ids']["tvrage"] is None: - indexer_id = int(watchlist_el['show']['ids']["tvrage"]) - else: - indexer_id = 0 - - if indexer_id == show_obj.indexerid and season is None and episode is None: - found=True - break - elif indexer_id == show_obj.indexerid and season == watchlist_el['episode']["season"] and episode == watchlist_el['episode']["number"]: - found=True - break + tvrage = True + + title = watchlist_el['show']['title'] + year = str(watchlist_el['show']['year']) + + if 'seasons' in watchlist_el: + for season_el in watchlist_el['seasons']: + for episode_el in season_el['episodes']: + season = str(season_el['number']) + episode = str(episode_el['number']) + + if tvdb: + showid = str(watchlist_el['show']['ids'][tvdb_id]) + if not showid in self.Collectionlist[tvdb_id + '_id'].keys(): + self.Collectionlist[tvdb_id + '_id'][showid] = { 'id': showid , 'title' : title , 'year': year , 'seasons' : {} } + + if not season in self.Collectionlist[tvdb_id + '_id'][showid]['seasons'].keys(): + self.Collectionlist[tvdb_id + '_id'][showid]['seasons'][season] = { 's': season , 'episodes' : {} } + + if not episode in self.Collectionlist[tvdb_id + '_id'][showid]['seasons'][season]['episodes'].keys(): + self.Collectionlist[tvdb_id + '_id'][showid]['seasons'][season]['episodes'][episode] = episode + + if tvrage: + showid = str(watchlist_el['show']['ids'][tvrage_id]) + if not showid in self.Collectionlist[tvrage_id + '_id'].keys(): + self.Collectionlist[tvrage_id + '_id'][showid] = { 'id': showid , 'title' : title , 'year': year , 'seasons' : {} } + + if not season in self.Collectionlist[tvrage_id + '_id'][showid]['seasons'].keys(): + self.Collectionlist[tvrage_id + '_id'][showid]['seasons'][season] = { 's': season , 'episodes' : {} } + + if not episode in self.Collectionlist[tvrage_id + '_id'][showid]['seasons'][season]['episodes'].keys(): + self.Collectionlist[tvrage_id + '_id'][showid]['seasons'][season]['episodes'][episode] = episode + + except traktException as e: + logger.log(u"Could not connect to trakt service, cannot download Show Collection: %s" % ex(e), logger.ERROR) + return False - return found + return True + def trakt_bulk_data_generate(self, data): + """ + Build the JSON structure to send back to Trakt + """ + + uniqueShows = {} + uniqueSeasons = {} + for showid,indexerid,show_name,startyear,season,episode in data: + if showid not in uniqueShows: + uniqueShows[showid] = {'title': show_name, 'year': startyear, 'ids': {},'seasons': []} + trakt_id = sickbeard.indexerApi(indexerid).config['trakt_id'] + if trakt_id == 'tvdb_id': + uniqueShows[showid]['ids']["tvdb"] = showid + else: + uniqueShows[showid]['ids']["tvrage"] = showid + uniqueSeasons[showid] = [] + + # Get the unique seasons per Show + for showid,indexerid,show_name,startyear,season,episode in data: + if season not in uniqueSeasons[showid]: + uniqueSeasons[showid].append(season) + + #build the query + showList = [] + seasonsList = {} + for searchedShow in uniqueShows: + seasonsList[searchedShow] = [] + for searchedSeason in uniqueSeasons[searchedShow]: + episodesList = [] + for showid,indexerid,show_name,startyear,season,episode in data: + if season == searchedSeason and showid == searchedShow: + episodesList.append({'number': episode}) + show = uniqueShows[searchedShow] + show['seasons'].append({'number': searchedSeason, 'episodes': episodesList}) + showList.append(show) + post_data = {'shows': showList} + return post_data + class TraktRolling(): def __init__(self): @@ -419,9 +627,6 @@ class TraktRolling(): self.EpisodeWatched = [] def run(self, force=False): - if not (sickbeard.TRAKT_USE_ROLLING_DOWNLOAD and sickbeard.USE_TRAKT): - return - logger.log(u"Start getting list from Traktv", logger.DEBUG) logger.log(u"Getting EpisodeWatched", logger.DEBUG) @@ -442,13 +647,13 @@ class TraktRolling(): def refreshEpisodeWatched(self): - if not (sickbeard.TRAKT_USE_ROLLING_DOWNLOAD and sickbeard.USE_TRAKT): - return False + if not (sickbeard.TRAKT_USE_ROLLING_DOWNLOAD and sickbeard.USE_TRAKT): + return False - if not self._getEpisodeWatched(): - return False + if not self._getEpisodeWatched(): + return False - return True + return True def updateWantedList(self, indexer_id = None): @@ -474,7 +679,7 @@ class TraktRolling(): else: sql_selection=sql_selection + " and T1.paused = 0" - sql_selection=sql_selection + " ORDER BY T1.show_name,season,episode" + sql_selection=sql_selection + " ORDER BY T1.show_name,season,episode" results = myDB.select(sql_selection,[SKIPPED]) @@ -584,4 +789,3 @@ class TraktRolling(): continue return num_ep - diff --git a/sickbeard/tv.py b/sickbeard/tv.py index 90882e45cb11090ae7bd7c4618bb159d2accee79..512893fadf542c5852713d3cee4850e229b7e837 100644 --- a/sickbeard/tv.py +++ b/sickbeard/tv.py @@ -1265,7 +1265,7 @@ class TVShow(object): def getOverview(self, epStatus): - if epStatus == WANTED and not self.paused: + if epStatus == WANTED: return Overview.WANTED elif epStatus in (UNAIRED, UNKNOWN): return Overview.UNAIRED diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index 39391c241b201b5e12c2e5bb13315206eaca1f73..d373be2228159b237f5f089f881168b13c157556 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -991,9 +991,9 @@ class Home(WebRoot): myDB = db.DBConnection() if myDB.action("UPDATE tv_shows SET notify_list = ? WHERE show_id = ?", [emails, show]): - return 'OK' - else: - return 'ERROR: %s' % myDB.last_err + return 'OK' + else: + return 'ERROR: %s' % myDB.last_err def testEmail(self, host=None, port=None, smtp_from=None, use_tls=None, user=None, pwd=None, to=None): @@ -1045,6 +1045,10 @@ class Home(WebRoot): else: return "Error sending Pushbullet notification" + def status(self): + t = PageTemplate(rh=self, file='status.tmpl') + return t.respond() + def shutdown(self, pid=None): if str(pid) != str(sickbeard.PID): return self.redirect("/home/") @@ -2970,7 +2974,7 @@ class Manage(Home, WebRoot): epCounts[Overview.SNATCHED] = 0 sqlResults = myDB.select( - "SELECT * FROM tv_episodes WHERE showid = ? ORDER BY season DESC, episode DESC", + "SELECT * FROM tv_episodes WHERE tv_episodes.showid in (SELECT tv_shows.indexer_id FROM tv_shows WHERE tv_shows.indexer_id = ? AND paused = 0) ORDER BY tv_episodes.season DESC, tv_episodes.episode DESC", [curShow.indexerid]) for curResult in sqlResults: @@ -3639,7 +3643,7 @@ class ConfigGeneral(Config): handle_reverse_proxy=None, sort_article=None, auto_update=None, notify_on_update=None, proxy_setting=None, proxy_indexers=None, anon_redirect=None, git_path=None, git_remote=None, calendar_unprotected=None, debug=None, no_restart=None, coming_eps_missed_range=None, - display_filesize=None, fuzzy_dating=None, trim_zero=None, date_preset=None, date_preset_na=None, time_preset=None, + display_filesize=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, git_reset=None, git_username=None, git_password=None, git_autoissues=None): @@ -3691,6 +3695,7 @@ class ConfigGeneral(Config): sickbeard.WEB_PASSWORD = web_password sickbeard.DISPLAY_FILESIZE = config.checkbox_to_value(display_filesize) + sickbeard.FILTER_ROW = config.checkbox_to_value(filter_row) sickbeard.FUZZY_DATING = config.checkbox_to_value(fuzzy_dating) sickbeard.TRIM_ZERO = config.checkbox_to_value(trim_zero) @@ -3834,7 +3839,6 @@ class ConfigSearch(Config): results += ["Unable to create directory " + os.path.normpath(torrent_dir) + ", dir not changed."] config.change_DAILYSEARCH_FREQUENCY(dailysearch_frequency) - config.change_BACKLOG_FREQUENCY(backlog_frequency) @@ -3853,20 +3857,6 @@ class ConfigSearch(Config): sickbeard.DOWNLOAD_PROPERS = config.checkbox_to_value(download_propers) config.change_DOWNLOAD_PROPERS(sickbeard.DOWNLOAD_PROPERS) - if sickbeard.DOWNLOAD_PROPERS and not sickbeard.properFinderScheduler.isAlive(): - sickbeard.properFinderScheduler.silent = False - try: - sickbeard.properFinderScheduler.start() - except: - pass - elif not sickbeard.DOWNLOAD_PROPERS: - sickbeard.properFinderScheduler.stop.set() - sickbeard.properFinderScheduler.silent = True - try: - sickbeard.properFinderScheduler.join(5) - except: - pass - sickbeard.CHECK_PROPERS_INTERVAL = check_propers_interval sickbeard.ALLOW_HIGH_PRIORITY = config.checkbox_to_value(allow_high_priority) @@ -3948,22 +3938,8 @@ class ConfigPostProcessing(Config): if not config.change_TV_DOWNLOAD_DIR(tv_download_dir): results += ["Unable to create directory " + os.path.normpath(tv_download_dir) + ", dir not changed."] - sickbeard.PROCESS_AUTOMATICALLY = config.checkbox_to_value(process_automatically) config.change_AUTOPOSTPROCESSER_FREQUENCY(autopostprocesser_frequency) - - if sickbeard.PROCESS_AUTOMATICALLY and not sickbeard.autoPostProcesserScheduler.isAlive(): - sickbeard.autoPostProcesserScheduler.silent = False - try: - sickbeard.autoPostProcesserScheduler.start() - except: - pass - elif not sickbeard.PROCESS_AUTOMATICALLY: - sickbeard.autoPostProcesserScheduler.stop.set() - sickbeard.autoPostProcesserScheduler.silent = True - try: - sickbeard.autoPostProcesserScheduler.join(5) - except: - pass + config.change_PROCESS_AUTOMATICALLY(process_automatically) if unpack: if self.isRarSupported() != 'not supported': @@ -4618,7 +4594,7 @@ class ConfigNotifications(Config): use_nmjv2=None, nmjv2_host=None, nmjv2_dbloc=None, nmjv2_database=None, use_trakt=None, trakt_username=None, trakt_password=None, trakt_remove_watchlist=None, trakt_sync_watchlist=None, trakt_method_add=None, - trakt_start_paused=None, trakt_use_recommended=None, trakt_sync=None, + trakt_start_paused=None, trakt_use_recommended=None, trakt_sync=None, trakt_sync_remove=None, trakt_default_indexer=None, trakt_remove_serieslist=None, trakt_disable_ssl_verify=None, trakt_timeout=None, trakt_blacklist_name=None, trakt_use_rolling_download=None, trakt_rolling_num_ep=None, trakt_rolling_add_paused=None, trakt_rolling_frequency=None, use_synologynotifier=None, synologynotifier_notify_onsnatch=None, @@ -4731,7 +4707,7 @@ class ConfigNotifications(Config): sickbeard.SYNOLOGYNOTIFIER_NOTIFY_ONSUBTITLEDOWNLOAD = config.checkbox_to_value( synologynotifier_notify_onsubtitledownload) - sickbeard.USE_TRAKT = config.checkbox_to_value(use_trakt) + config.change_USE_TRAKT(use_trakt) sickbeard.TRAKT_USERNAME = trakt_username sickbeard.TRAKT_PASSWORD = trakt_password sickbeard.TRAKT_REMOVE_WATCHLIST = config.checkbox_to_value(trakt_remove_watchlist) @@ -4741,20 +4717,16 @@ class ConfigNotifications(Config): sickbeard.TRAKT_START_PAUSED = config.checkbox_to_value(trakt_start_paused) sickbeard.TRAKT_USE_RECOMMENDED = config.checkbox_to_value(trakt_use_recommended) sickbeard.TRAKT_SYNC = config.checkbox_to_value(trakt_sync) + sickbeard.TRAKT_SYNC_REMOVE = config.checkbox_to_value(trakt_sync_remove) sickbeard.TRAKT_DEFAULT_INDEXER = int(trakt_default_indexer) sickbeard.TRAKT_DISABLE_SSL_VERIFY = config.checkbox_to_value(trakt_disable_ssl_verify) sickbeard.TRAKT_TIMEOUT = int(trakt_timeout) sickbeard.TRAKT_BLACKLIST_NAME = trakt_blacklist_name - sickbeard.TRAKT_USE_ROLLING_DOWNLOAD = config.checkbox_to_value(trakt_use_rolling_download) + config.change_TRAKT_USE_ROLLING_DOWNLOAD(trakt_use_rolling_download) sickbeard.TRAKT_ROLLING_NUM_EP = int(trakt_rolling_num_ep) sickbeard.TRAKT_ROLLING_ADD_PAUSED = config.checkbox_to_value(trakt_rolling_add_paused) sickbeard.TRAKT_ROLLING_FREQUENCY = int(trakt_rolling_frequency) - if sickbeard.USE_TRAKT: - sickbeard.traktCheckerScheduler.silent = False - else: - sickbeard.traktCheckerScheduler.silent = True - sickbeard.USE_EMAIL = config.checkbox_to_value(use_email) sickbeard.EMAIL_NOTIFY_ONSNATCH = config.checkbox_to_value(email_notify_onsnatch) sickbeard.EMAIL_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(email_notify_ondownload) @@ -4826,30 +4798,14 @@ class ConfigSubtitles(Config): results = [] - if subtitles_finder_frequency == '' or subtitles_finder_frequency is None: - subtitles_finder_frequency = 1 - - if use_subtitles == "on" and not sickbeard.subtitlesFinderScheduler.isAlive(): - sickbeard.subtitlesFinderScheduler.silent = False - try: - sickbeard.subtitlesFinderScheduler.start() - except: - pass - elif not use_subtitles == "on": - sickbeard.subtitlesFinderScheduler.stop.set() - sickbeard.subtitlesFinderScheduler.silent = True - try: - sickbeard.subtitlesFinderScheduler.join(5) - except: - pass + config.change_SUBTITLES_FINDER_FREQUENCY(subtitles_finder_frequency) + config.change_USE_SUBTITLES(use_subtitles) - sickbeard.USE_SUBTITLES = config.checkbox_to_value(use_subtitles) sickbeard.SUBTITLES_LANGUAGES = [lang.alpha2 for lang in subtitles.isValidLanguage( subtitles_languages.replace(' ', '').split(','))] if subtitles_languages != '' else '' sickbeard.SUBTITLES_DIR = subtitles_dir sickbeard.SUBTITLES_HISTORY = config.checkbox_to_value(subtitles_history) sickbeard.EMBEDDED_SUBTITLES_ALL = config.checkbox_to_value(embedded_subtitles_all) - sickbeard.SUBTITLES_FINDER_FREQUENCY = config.to_int(subtitles_finder_frequency, default=1) sickbeard.SUBTITLES_MULTI = config.checkbox_to_value(subtitles_multi) # Subtitles services @@ -5008,7 +4964,8 @@ class ErrorLogs(WebRoot): 'ERROR': u'Error', 'TORNADO': u'Tornado', 'Thread': u'Thread', - 'MAIN': u'Main' + 'MAIN': u'Main', + 'TRAKTROLLING': u'Trakt Rolling' } if logFilter not in logNameFilters: diff --git a/sickbeard/webserveInit.py b/sickbeard/webserveInit.py index 5f26c0eb639110dc2b0a9c668e92887db3ddad73..8f2bd31e762dd73b2890b4094eb5223fc8cb6e8c 100644 --- a/sickbeard/webserveInit.py +++ b/sickbeard/webserveInit.py @@ -69,7 +69,7 @@ class SRWebServer(threading.Thread): self.app = Application([], debug=True, autoreload=False, - gzip=True, + gzip=sickbeard.WEB_USE_GZIP, xheaders=sickbeard.HANDLE_REVERSE_PROXY, cookie_secret=sickbeard.WEB_COOKIE_SECRET, login_url='%s/login/' % self.options['web_root'], @@ -153,4 +153,4 @@ class SRWebServer(threading.Thread): def shutDown(self): self.alive = False - self.io_loop.stop() \ No newline at end of file + self.io_loop.stop()