mirror of
https://github.com/SickGear/SickGear.git
synced 2025-01-05 17:43:37 +00:00
Merge branch 'feature/AddTraktCollection' into develop
This commit is contained in:
commit
1cf0b5f9b2
29 changed files with 982 additions and 291 deletions
13
CHANGES.md
13
CHANGES.md
|
@ -64,17 +64,22 @@
|
|||
* Change increase frequency of updating show data
|
||||
* Remove Animenzb provider
|
||||
* Change increase the scope and number of non release group text that is identified and removed
|
||||
* Add a general config setting to allow adding incomplete show data
|
||||
* Add general config setting to allow adding incomplete show data
|
||||
* Change to throttle connection rate on thread initiation for adba library
|
||||
* Change default manage episodes selector to Snatched episodes if items exist else Wanted on Episode Status Manage page
|
||||
* Change snatched row colour on Episode Status Manage page to match colour used on the show details page
|
||||
* Change replace trakt with libtrakt for API v2
|
||||
* Change Trakt notification config to only handle PIN authentication with the service
|
||||
* Change improve robustness of Trakt communications
|
||||
* Change Trakt notification config to use PIN authentication with the service
|
||||
* Add multiple Trakt account support to Config/Notifications/Social
|
||||
* Add setting to Trakt notification to update collection with downloaded episode info
|
||||
* Change trakt notifier logo
|
||||
* Remove all other Trakt deprecated API V1 service features pending reconsideration
|
||||
* Change increase show search capability when using plain text and also add TVDB id, IMDb id and IMDb url search
|
||||
* Change improve existing show page and the handling when an attempt to add a show to an existing location
|
||||
* Change consolidate Trakt Trending and Recommended views into an "Add From Trakt" view which defaults to trending
|
||||
* Change Trakt view drop down "Show" to reveal Brand-new Shows, Season Premieres, Recommendations and Trending views
|
||||
* Change Add from Trakt/"Shows:" with Anticipated, New Seasons, New Shows, Popular, Recommendations, and Trending views
|
||||
* Change Add from Trakt/"Shows:" with Most Watched, Collected during the last month on Trakt
|
||||
* Change add season info to "Show: Trakt New Seasons" view on the Add from Trakt page
|
||||
* Change increase number of displayed Trakt shows to 100
|
||||
* Add genres and rating to all Trakt shows
|
||||
|
@ -102,6 +107,8 @@
|
|||
* Change to always display branch and commit hash on 'Help & Info' page
|
||||
* Add option to create season search exceptions from editShow page
|
||||
* Change sab to use requests library
|
||||
* Add "View Changes" to tools menu
|
||||
* Change disable connection attempts and remove UI references to the TVRage info source
|
||||
|
||||
|
||||
### 0.10.0 (2015-08-06 11:05:00 UTC)
|
||||
|
|
|
@ -528,6 +528,15 @@ h2.day, h2.network{
|
|||
border-color:#8DBEEE
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
viewchanges.tmpl
|
||||
========================================================================== */
|
||||
#changes{
|
||||
color:rgb(255,255,255);
|
||||
background-color:rgb(61,61,61);
|
||||
border:1px solid rgb(17,17,17)
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
config*.tmpl
|
||||
========================================================================== */
|
||||
|
@ -626,6 +635,14 @@ div.metadataDiv .disabled{
|
|||
background:url("../images/warning16.png") no-repeat right 5px center #fff
|
||||
}
|
||||
|
||||
.solid-border{
|
||||
border:1px solid #555
|
||||
}
|
||||
|
||||
.solid-border-top{
|
||||
border-top:1px solid #555
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
manage*.tmpl
|
||||
========================================================================== */
|
||||
|
@ -1181,7 +1198,7 @@ browser.css
|
|||
|
||||
#fileBrowserDialog ul li a:hover{
|
||||
color:#09a2ff;
|
||||
background:none
|
||||
background: rgb(61, 61, 61) none
|
||||
}
|
||||
|
||||
.ui-menu .ui-menu-item{
|
||||
|
@ -1215,6 +1232,10 @@ div.stepsguide .step p{
|
|||
color:#646464
|
||||
}
|
||||
|
||||
#newShowPortal #addShowForm .stepsguide .disabledstep:hover > .smalltext{
|
||||
color:#ccc;
|
||||
}
|
||||
|
||||
div.stepsguide .disabledstep p{
|
||||
border-color:#1178B3
|
||||
}
|
||||
|
|
|
@ -510,6 +510,15 @@ h2.day, h2.network{
|
|||
border-color:#C7DB40
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
viewchanges.tmpl
|
||||
========================================================================== */
|
||||
#changes{
|
||||
color:rgb(51,51,51);
|
||||
background-color:rgb(245,245,245);
|
||||
border:1px solid rgb(204,204,204)
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
config*.tmpl
|
||||
========================================================================== */
|
||||
|
@ -601,6 +610,14 @@ div.metadataDiv .disabled{
|
|||
background:url("../images/warning16.png") no-repeat right 5px center #fff
|
||||
}
|
||||
|
||||
.solid-border{
|
||||
border:1px solid #ccc
|
||||
}
|
||||
|
||||
.solid-border-top{
|
||||
border-top:1px solid #ccc
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
manage*.tmpl
|
||||
========================================================================== */
|
||||
|
@ -1143,7 +1160,7 @@ browser.css
|
|||
|
||||
#fileBrowserDialog ul li a:hover{
|
||||
color:#00f;
|
||||
background:none
|
||||
background: rgb(220, 220, 220) none
|
||||
}
|
||||
|
||||
.ui-menu .ui-menu-item{
|
||||
|
@ -1181,6 +1198,10 @@ div.stepsguide .disabledstep p{
|
|||
border-color:#8a775e
|
||||
}
|
||||
|
||||
#newShowPortal #addShowForm .stepsguide .disabledstep:hover > .smalltext{
|
||||
color:#8a775e;
|
||||
}
|
||||
|
||||
div.formpaginate .prev, div.formpaginate .next{
|
||||
color:#fff;
|
||||
background:#57442b
|
||||
|
|
|
@ -2103,6 +2103,36 @@ td.col-cache{
|
|||
width:20px
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
viewchanges.tmpl
|
||||
========================================================================== */
|
||||
#changes{
|
||||
display:block;
|
||||
padding:9.5px;
|
||||
border-radius:4px 4px 4px 4px;
|
||||
font:12px/13px "Open Sans",verdana,sans-serif
|
||||
}
|
||||
|
||||
#changes .release{
|
||||
margin:15px 5px 6px 0;
|
||||
padding-bottom:3px;
|
||||
border-bottom:1px solid gray
|
||||
}
|
||||
|
||||
#changes .ver{font:16px/17px "Open Sans",verdana,sans-serif;margin-right:0.2em;}
|
||||
#changes .old{padding-top:15px}
|
||||
|
||||
#changes div{margin:0 0 8px}
|
||||
#changes .btn-text{width:5em;margin-right:0.2em;float:left}
|
||||
#changes .change-text{display:block;margin-left:5.5em;padding-top:2px}
|
||||
|
||||
.change-add{background-color:rgb(63,127,0)}
|
||||
.change-change{background-color:rgb(91,153,13)}
|
||||
.change-fix{background-color:rgb(38,114,182)}
|
||||
.change-port{background-color:rgb(102,102,102)}
|
||||
.change-remove{}
|
||||
.change-update{background-color:rgb(98,25,147)}
|
||||
|
||||
|
||||
/* =======================================================================
|
||||
config*.tmpl
|
||||
|
@ -2529,6 +2559,32 @@ div.metadataDiv .disabled{
|
|||
margin:6px 4px 0 0
|
||||
}
|
||||
|
||||
#trakt-collection th,#trakt-collection td{
|
||||
padding:3px 5px
|
||||
}
|
||||
|
||||
#trakt-collection .col-1{
|
||||
text-align:left
|
||||
}
|
||||
|
||||
#trakt-collection th,#trakt-collection td.opt{
|
||||
text-align:center
|
||||
}
|
||||
|
||||
#trakt-collection .col-1{
|
||||
width:192px
|
||||
}
|
||||
|
||||
#config #trakt-collection input{
|
||||
float:none;
|
||||
margin:0;
|
||||
vertical-align:middle
|
||||
}
|
||||
|
||||
#config .trakt.component-desc{
|
||||
margin-left:0
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
manage*.tmpl
|
||||
========================================================================== */
|
||||
|
@ -2630,6 +2686,7 @@ span.path{
|
|||
line-height:18px
|
||||
}
|
||||
|
||||
span.btn-text,
|
||||
span.quality{
|
||||
font:12px/13px "Open Sans", verdana, sans-serif;
|
||||
background-image:-webkit-linear-gradient(top, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 50%,rgba(0, 0, 0, 0.25));
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.6 KiB |
|
@ -122,22 +122,22 @@
|
|||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
#if 1 < $len($indexers)
|
||||
<div class="field-pair">
|
||||
<label for="indexer_default">
|
||||
<span class="component-title">Use initial indexer set to</span>
|
||||
<span class="component-desc">
|
||||
<select id="indexer_default" name="indexer_default" class="form-control input-sm">
|
||||
<option value="0"#echo ('', $selected)[0 == $indexer]#>All Indexers</option>
|
||||
#for $indexer in $sickbeard.indexerApi().indexers
|
||||
#for $indexer in $indexers
|
||||
<option value="$indexer"#echo ('', $selected)[$indexer == $sickbeard.INDEXER_DEFAULT]#>$sickbeard.indexerApi().indexers[$indexer]</option>
|
||||
#end for
|
||||
#end for
|
||||
</select>
|
||||
<span>as the default selection when adding new shows</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
#end if
|
||||
<div class="field-pair">
|
||||
<label for="indexer_timeout">
|
||||
<span class="component-title">Timeout show indexer at</span>
|
||||
|
@ -636,7 +636,7 @@
|
|||
<span class="component-title">Use proxy for indexers</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="proxy_indexers" id="proxy_indexers"#echo ('', $checked)[True == $sickbeard.PROXY_INDEXERS]#>
|
||||
<p>use proxy host for connecting to indexers (thetvdb, tvrage)</p>
|
||||
<p>use proxy host for TV info source connections</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#import base64
|
||||
#import sickbeard
|
||||
#import re
|
||||
#from lib.libtrakt import TraktAPI
|
||||
|
@ -13,9 +14,9 @@
|
|||
<script type="text/javascript" src="$sbRoot/js/configNotifications.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/config.js?v=$sbPID"></script>
|
||||
|
||||
#if $varExists('header')
|
||||
#if $varExists('header')
|
||||
<h1 class="header">$header</h1>
|
||||
#else
|
||||
#else
|
||||
<h1 class="title">$title</h1>
|
||||
#end if
|
||||
|
||||
|
@ -155,7 +156,7 @@
|
|||
|
||||
</div><!-- /xbmc component-group //-->
|
||||
|
||||
<div class="component-group">
|
||||
<div class="component-group">
|
||||
<div class="component-group-desc">
|
||||
<img class="notifier-icon" src="$sbRoot/images/notifiers/kodi.png" alt="" title="Kodi" />
|
||||
<h3><a href="<%= anon_url('http://kodi.tv/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Kodi</a></h3>
|
||||
|
@ -275,7 +276,7 @@
|
|||
</div><!-- /content_use_kodi //-->
|
||||
</fieldset>
|
||||
</div><!-- /kodi component-group //-->
|
||||
|
||||
|
||||
<div class="component-group">
|
||||
<div class="component-group-desc">
|
||||
<img class="notifier-icon" src="$sbRoot/images/notifiers/plex.png" alt="" title="Plex Media Server" />
|
||||
|
@ -1481,16 +1482,77 @@
|
|||
|
||||
<div id="content_use_trakt">
|
||||
<div class="field-pair">
|
||||
<label for="trakt_accounts">
|
||||
<span class="component-title">Trakt account (status):</span>
|
||||
<span class="component-desc">
|
||||
<select name="trakt_accounts" id="trakt_accounts" class="pull-left form-control input-sm">
|
||||
<option value="new" selected="selected">Add account</option>
|
||||
#set $trakt_accounts = $sickbeard.TRAKT_ACCOUNTS
|
||||
#for $void, $account in $trakt_accounts.items()
|
||||
<option value="$account.account_id">$account.account_id - $account.name #if $account.active then '(ok)' else '(inactive)'#</option>
|
||||
#end for
|
||||
</select>
|
||||
<input type="button" class="btn" value="Delete" id="trakt-delete" disabled="disabled" />
|
||||
</span>
|
||||
</label>
|
||||
<label for="trakt_pin">
|
||||
<span class="component-title">Trakt PIN:</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="trakt_pin" id="trakt_pin" value="" class="form-control input-sm input250" />
|
||||
<input type="button" class="btn" value="Connect" id="trakt-authenticate" />
|
||||
<div class="clear-left"><p>get your PIN at: <a href="<%= anon_url(sickbeard.TRAKT_PIN_URL) %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;"><b>$sickbeard.TRAKT_PIN_URL</b></a></p></div>
|
||||
<div class="clear-left"><p>get an active PIN using: <a href="<%= anon_url(sickbeard.TRAKT_PIN_URL) %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;"><b>$sickbeard.TRAKT_PIN_URL</b></a>
|
||||
<br />tip: easily add accounts by using the link in other browsers "signed in" to Trakt
|
||||
</p></div>
|
||||
</span>
|
||||
</label>
|
||||
<div class="testNotification" id="trakt-authentication-result"></div>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<span class="trakt component-desc" style="width:100%">
|
||||
#set num_accounts = len($trakt_accounts)
|
||||
#set $num_columns = (1, num_accounts)[1 < num_accounts]
|
||||
<table id="trakt-collection" class="solid-border" cellpadding="0" cellspacing="0" border="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-1" style="font-size:12px;font-weight:normal" rowspan="2"><i>Update multiple accounts with downloaded episode info</i></th>
|
||||
<th colspan="$num_columns">#echo not len($trakt_accounts) and '<i>Connect New Pin</i>' or 1 < len($trakt_accounts) and 'Trakt accounts' or 'Account'#</th>
|
||||
</tr>
|
||||
<tr>
|
||||
#if not len($trakt_accounts)
|
||||
<th>..</th>
|
||||
#end if
|
||||
#for $void, $account in $trakt_accounts.items()
|
||||
<th class="tid-$account.account_id">$account.name#if $account.active then '' else '<br />(inactive)'#</th>
|
||||
#end for
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
#if not $root_dirs:
|
||||
#set $root_dirs = [{'root_def': False, 'loc': 'all folders. Multiple parent folders will appear here.', 'b64': ''}]
|
||||
#end if
|
||||
#for $root_info in $root_dirs:
|
||||
<tr class="solid-border-top">
|
||||
<td class="col-1"><span style="font-size:13px;font-weight:bold" data-loc="$root_info['b64']">Update collection</span></td>
|
||||
#if not len($trakt_accounts)
|
||||
<td class="opt">..</td>
|
||||
#end if
|
||||
#for $void, $account in $trakt_accounts.items()
|
||||
#set $cur_selected = ('', ' checked="checked"')[$root_info['loc'] in $sickbeard.TRAKT_UPDATE_COLLECTION.get($account.account_id, '')]
|
||||
#set $id_loc = "update_trakt_%s_%s" % ($account.account_id, $root_info['b64'])
|
||||
<td class="opt">
|
||||
<input type="checkbox" id="$id_loc" name="$id_loc"$cur_selected />
|
||||
</td>
|
||||
#end for
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="${1 + $num_columns}">for #if $root_info['root_def'] then '*' else ''#$root_info['loc']</td>
|
||||
</tr>
|
||||
#end for
|
||||
</tbody>
|
||||
</table>
|
||||
</span>
|
||||
</div>
|
||||
<!--
|
||||
<div class="field-pair">
|
||||
<label for="trakt_default_indexer">
|
||||
|
@ -1725,4 +1787,4 @@
|
|||
//-->
|
||||
</script>
|
||||
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
||||
|
|
|
@ -128,10 +128,18 @@
|
|||
#if 'Trakt' == $browse_type
|
||||
#set $mode = $kwargs and $kwargs.get('mode', None)
|
||||
#set $selected = ' class="selected"'
|
||||
<option value="traktTrending"#echo ('', selected)['trending' == $mode]#>Trakt Trending</option>
|
||||
<option value="traktNewShows"#echo ('', selected)['newshows' == $mode]#>Trakt New Shows</option>
|
||||
<option value="traktAnticipated"#echo ('', selected)['anticipated' == $mode]#>Trakt Anticipating</option>
|
||||
<option value="traktNewSeasons"#echo ('', selected)['newseasons' == $mode]#>Trakt New Seasons</option>
|
||||
<option value="traktRecommended"#echo ('', selected)['recommended' == $mode]#>Trakt Recommended</option>
|
||||
<option value="traktNewShows"#echo ('', selected)['newshows' == $mode]#>Trakt New Shows</option>
|
||||
<option value="traktPopular"#echo ('', selected)['popular' == $mode]#>Trakt Popular</option>
|
||||
<option value="traktTrending"#echo ('', selected)['trending' == $mode]#>Trakt Trending</option>
|
||||
<option value="traktWatched"#echo ('', selected)['watched' == $mode]#>Trakt Most Watched (Last Month)</option>
|
||||
<option value="traktCollected"#echo ('', selected)['collected' == $mode]#>Trakt Most Collected (Last Month)</option>
|
||||
#for $account in $sickbeard.TRAKT_ACCOUNTS
|
||||
#if $sickbeard.TRAKT_ACCOUNTS[$account].active and $sickbeard.TRAKT_ACCOUNTS[$account].name
|
||||
<option value="traktRecommended?account=$account"#echo ('', selected)['recommended-%s' % $account == $mode]#>Trakt Recommended for $sickbeard.TRAKT_ACCOUNTS[$account].name</option>
|
||||
#end if
|
||||
#end for
|
||||
#end if
|
||||
</select>
|
||||
|
||||
|
|
|
@ -31,13 +31,16 @@
|
|||
#set $indexer = $curDir['existing_info'][2]
|
||||
#end if
|
||||
|
||||
#set $indexer = $sickbeard.INDEXER_DEFAULT
|
||||
|
||||
#*
|
||||
#set $indexer = 0
|
||||
#if $curDir['existing_info'][0]
|
||||
#set $indexer = $curDir['existing_info'][2]
|
||||
#elif 0 < $sickbeard.INDEXER_DEFAULT
|
||||
#set $indexer = $sickbeard.INDEXER_DEFAULT
|
||||
#end if
|
||||
|
||||
*#
|
||||
<tr>
|
||||
<td class="col-checkbox">
|
||||
<input type="checkbox" id="$show_id" class="dirCheck" checked=checked>
|
||||
|
@ -58,7 +61,9 @@
|
|||
<td align="center">
|
||||
<select name="indexer">
|
||||
#for $curIndexer in $sickbeard.indexerApi().indexers.items()
|
||||
#if $curIndexer[0] == $sickbeard.INDEXER_DEFAULT
|
||||
<option value="$curIndexer[0]" #if $curIndexer[0] == $indexer then 'selected="selected"' else ''#>$curIndexer[1]</option>
|
||||
#end if
|
||||
#end for
|
||||
</select>
|
||||
</td>
|
||||
|
|
|
@ -49,20 +49,29 @@
|
|||
<input type="hidden" id="providedName" value="$provided_indexer_name" />
|
||||
<input type="hidden" id="providedIndexer" value="$provided_indexer" />
|
||||
#else
|
||||
#if 2 > $len($indexers)
|
||||
<style>
|
||||
#addShowForm input#nameToSearch{width:611px}
|
||||
</style>
|
||||
<input type="hidden" id="providedIndexer" value="$provided_indexer" />
|
||||
#end if
|
||||
<input type="text" id="nameToSearch" value="$default_show_name" class="form-control form-control-inline input-sm input350" />
|
||||
|
||||
<span style="float:right">
|
||||
<select name="indexerLang" id="indexerLangSelect" class="form-control form-control-inline input-sm">
|
||||
<option value="en" selected="selected">en</option>
|
||||
</select><b> *</b>
|
||||
<select name="providedIndexer" id="providedIndexer" class="form-control form-control-inline input-sm">
|
||||
<option value="0" #if $provided_indexer == 0 then "selected=\"selected\"" else ""#>All Indexers</option>
|
||||
#for $indexer in $indexers
|
||||
<option value="$indexer" #if $provided_indexer == $indexer then "selected=\"selected\"" else ""#>$indexers[$indexer]</option>
|
||||
#end for
|
||||
</select>
|
||||
|
||||
<input class="btn btn-inline" type="button" id="searchName" value="Search" />
|
||||
<select name="indexerLang" id="indexerLangSelect" class="form-control form-control-inline input-sm">
|
||||
<option value="en" selected="selected">en</option>
|
||||
</select><b> *</b>
|
||||
|
||||
#if 1 < $len($indexers)
|
||||
<select name="providedIndexer" id="providedIndexer" class="form-control form-control-inline input-sm">
|
||||
<option value="0" #if $provided_indexer == 0 then "selected=\"selected\"" else ""#>All Indexers</option>
|
||||
#for $indexer in $indexers
|
||||
<option value="$indexer" #if $provided_indexer == $indexer then "selected=\"selected\"" else ""#>$indexers[$indexer]</option>
|
||||
#end for
|
||||
</select>
|
||||
#end if
|
||||
|
||||
<input class="btn btn-inline" type="button" id="searchName" value="Search" />
|
||||
</span>
|
||||
<br />
|
||||
<p style="margin:5px 0 15px">Enter show name, TVDB ID, IMDb Url, or IMDb ID. <b>*</b>SickGear supports english, language is used for show/episode data</p>
|
||||
|
|
|
@ -168,9 +168,11 @@
|
|||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" data-delay="0" tabindex="$tab#set $tab += 1#"><img src="$sbRoot/images/menu/system18-2.png" class="navbaricon hidden-xs" /><b class="caret hidden-xs"></b><span class="visible-xs">System <b class="caret"></b></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="$sbRoot/manage/manageSearches/forceVersionCheck" tabindex="$tab#set $tab += 1#"><i class="sgicon-updatecheck"></i>Force Version Check</a></li>
|
||||
<li><a href="$sbRoot/manage/manageSearches/forceVersionCheck" tabindex="$tab#set $tab += 1#"><i class="sgicon-updatecheck"></i>Check for Updates</a></li>
|
||||
<li><a href="$sbRoot/home/viewchanges" tabindex="$tab#set $tab += 1#"><i class="sgicon-log"></i>View Changes</a></li>
|
||||
<li class="divider"></li>
|
||||
#if $sickbeard.WEB_USERNAME or $sickbeard.WEB_PASSWORD
|
||||
<li><a href="$sbRoot/logout" class="confirm logout" tabindex="$tab#set $tab += 1#"><i class="sgicon-logout"></i>Logout</a></li>
|
||||
<li><a href="$sbRoot/logout" class="confirm logout" tabindex="$tab#set $tab += 1#"><i class="sgicon-logout"></i>Logout</a></li>
|
||||
#end if
|
||||
<li><a href="$sbRoot/home/restart/?pid=$sbPID" class="confirm restart" tabindex="$tab#set $tab += 1#"><i class="sgicon-restart"></i>Restart</a></li>
|
||||
<li><a href="$sbRoot/home/shutdown/?pid=$sbPID" class="confirm shutdown" tabindex="$tab#set $tab += 1#"><i class="sgicon-shutdown"></i>Shutdown</a></li>
|
||||
|
|
20
gui/slick/interfaces/default/viewchanges.tmpl
Normal file
20
gui/slick/interfaces/default/viewchanges.tmpl
Normal file
|
@ -0,0 +1,20 @@
|
|||
#import sickbeard
|
||||
##
|
||||
#set global $header = 'Changes'
|
||||
#set global $title = 'Changes'
|
||||
#set global $topmenu = ''
|
||||
##
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
|
||||
|
||||
<h1 class="header">$header</h1>
|
||||
|
||||
<div class="align-left" style="margin:30px 0">
|
||||
<div id="changes">
|
||||
#for $i, $change in $enumerate($changelist)##if 'rel' == $change['type']#
|
||||
<div class="release#echo ('', ' old')[bool($i)]#"><span class="ver">$change['ver']</span> <span class="change-date grey-text">$change['date']</span></div>
|
||||
#else# <div><span class="btn-text change-$change['type'].lower()">$change['type']</span> <span class="change-text">$change['text']</span></div>
|
||||
#end if##end for#
|
||||
</div>
|
||||
</div>
|
||||
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
|
@ -1,4 +1,4 @@
|
|||
$(document).ready(function(){
|
||||
$(document).ready(function(){
|
||||
var loading = '<img src="' + sbRoot + '/images/loading16' + themeSpinner + '.gif" height="16" width="16" />';
|
||||
|
||||
$('#testGrowl').click(function () {
|
||||
|
@ -353,28 +353,162 @@ $(document).ready(function(){
|
|||
});
|
||||
|
||||
var elTraktAuth = $('#trakt-authenticate'), elTraktAuthResult = $('#trakt-authentication-result');
|
||||
elTraktAuth.click(function() {
|
||||
var elTrakt = $('#trakt_pin'), traktPin = $.trim(elTrakt.val());
|
||||
if(!traktPin) {
|
||||
elTrakt.addClass('warning');
|
||||
|
||||
function trakt_send_auth(){
|
||||
var elAccountSelect = $('#trakt_accounts'), strCurAccountId = elAccountSelect.find('option:selected').val(),
|
||||
elTraktPin = $('#trakt_pin'), strPin = $.trim(elTraktPin.val());
|
||||
|
||||
elTraktAuthResult.html(loading);
|
||||
|
||||
$.get(sbRoot + '/home/trakt_authenticate', {'pin': strPin, 'account': strCurAccountId})
|
||||
.done(function(data) {
|
||||
elTraktAuth.prop('disabled', !1);
|
||||
elTraktPin.val('');
|
||||
|
||||
var JSONData = $.parseJSON(data);
|
||||
|
||||
elTraktAuthResult.html('Success' == JSONData.result
|
||||
? JSONData.result + ' account: ' + JSONData.account_name
|
||||
: JSONData.result + ' ' + JSONData.error_message);
|
||||
|
||||
if ('Success' == JSONData.result) {
|
||||
var elUpdateRows = $('#trakt-collection').find('tr');
|
||||
if ('new' == strCurAccountId) {
|
||||
elAccountSelect.append($('<option>', {value: JSONData.account_id, text: JSONData.account_id + ' - ' + JSONData.account_name + ' (ok)'}));
|
||||
|
||||
if ('Connect New Pin' == elUpdateRows.eq(0).find('th').last().text()) {
|
||||
elUpdateRows.eq(0).find('th').last().html('Account');
|
||||
elUpdateRows.eq(1).find('th').last().html(JSONData.account_name);
|
||||
elUpdateRows.eq(1).find('th').last().addClass('tid-' + JSONData.account_id);
|
||||
elUpdateRows.has('td').each(function(nRow) {
|
||||
var elCells = $(this).find('td');
|
||||
if (!(nRow % 2)) {
|
||||
var IdLoc = 'update_trakt_' + JSONData.account_id + '_' + elCells.eq(0).find('span').attr('data-loc');
|
||||
elCells.last().html('<input type="checkbox" id="' + IdLoc + '" name="' + IdLoc + '">');
|
||||
} else {
|
||||
elCells.attr('colspan', 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
elUpdateRows.eq(0).find('th').last().html('Trakt accounts');
|
||||
elUpdateRows.eq(0).find('th').last().attr('colspan', 1 + parseInt(elUpdateRows.eq(0).find('th').last().attr('colspan'), 10));
|
||||
elUpdateRows.eq(1).find('th').last().after('<th>' + JSONData.account_name + '</th>');
|
||||
elUpdateRows.eq(1).find('th').last().addClass('tid-' + JSONData.account_id);
|
||||
elUpdateRows.has('td').each(function(nRow) {
|
||||
var elCells = $(this).find('td');
|
||||
if (!(nRow % 2)) {
|
||||
var IdLoc = 'update_trakt_' + JSONData.account_id + '_' + elCells.eq(0).find('span').attr('data-loc');
|
||||
elCells.last().after('<td class="opt"><input type="checkbox" id="' + IdLoc + '" name="' + IdLoc + '"></td>');
|
||||
} else {
|
||||
elCells.attr('colspan', 1 + parseInt(elCells.attr('colspan'), 10));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
elAccountSelect.find('option[value=' + strCurAccountId + ']').html(JSONData.account_id + ' - ' + JSONData.account_name + ' (ok)');
|
||||
elUpdateRows.eq(1).find('th[class*="tid-' + JSONData.account_id + '"]').text(JSONData.account_name);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
elTraktAuth.click(function(e) {
|
||||
var elTraktPin = $('#trakt_pin');
|
||||
|
||||
elTraktPin.removeClass('warning');
|
||||
if (!$.trim(elTraktPin.val())) {
|
||||
elTraktPin.addClass('warning');
|
||||
elTraktAuthResult.html('Please enter a required PIN above.');
|
||||
} else {
|
||||
elTrakt.removeClass('warning');
|
||||
$(this).prop('disabled', true);
|
||||
elTraktAuthResult.html(loading);
|
||||
$.get(sbRoot + '/home/trakt_authenticate', {'pin': traktPin})
|
||||
.done(function(data) {
|
||||
elTraktAuthResult.html(data);
|
||||
elTraktAuth.prop('disabled', false);
|
||||
var elAccountSelect = $('#trakt_accounts'), elSelected = elAccountSelect.find('option:selected');
|
||||
$(this).prop('disabled', !0);
|
||||
if ('new' != elSelected.val()) {
|
||||
$.confirm({
|
||||
'title' : 'Replace Trakt Account',
|
||||
'message' : 'Are you sure you want to replace <span class="footerhighlight">' + elSelected.text() + '</span> ?<br /><br />',
|
||||
'buttons' : {
|
||||
'Yes' : {
|
||||
'class' : 'green',
|
||||
'action': function() {
|
||||
trakt_send_auth();
|
||||
}
|
||||
},
|
||||
'No' : {
|
||||
'class' : 'red',
|
||||
'action': function() {
|
||||
e.preventDefault();
|
||||
elTraktAuth.prop('disabled', !1);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
trakt_send_auth();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
elTraktAuthResult.html(loading);
|
||||
$.get(sbRoot + '/home/trakt_get_connected_account')
|
||||
.done(function(data) {
|
||||
elTraktAuthResult.html(data);
|
||||
$('#trakt_accounts').change(function() {
|
||||
$('#trakt-delete').prop('disabled', 'new' == $('#trakt_accounts').val());
|
||||
});
|
||||
|
||||
$('#trakt-delete').click(function(e) {
|
||||
var elAccountSelect = $('#trakt_accounts'), elSelected = elAccountSelect.find('option:selected'), that = $(this);
|
||||
|
||||
that.prop('disabled', !0);
|
||||
$.confirm({
|
||||
'title' : 'Remove Trakt Account',
|
||||
'message' : 'Are you sure you want to remove <span class="footerhighlight">' + elSelected.text() + '</span> ?<br /><br />',
|
||||
'buttons' : {
|
||||
'Yes' : {
|
||||
'class' : 'green',
|
||||
'action': function() {
|
||||
$.get(sbRoot + '/home/trakt_delete', {'accountid': elSelected.val()})
|
||||
.done(function(data) {
|
||||
that.prop('disabled', !1);
|
||||
var JSONData = $.parseJSON(data);
|
||||
if ('Success' == JSONData.result) {
|
||||
var elCollection = $('#trakt-collection'), elUpdateRows = elCollection.find('tr'),
|
||||
header = elCollection.find('th[class*="tid-' + JSONData.account_id + '"]'),
|
||||
num_acc = parseInt(JSONData.num_accounts, 10);
|
||||
|
||||
elUpdateRows.eq(0).find('th').last().html(!num_acc && '<i>Connect New Pin</i>' ||
|
||||
(1 < num_acc ? 'Trakt accounts' : 'Account'));
|
||||
elUpdateRows.find('th[colspan]').attr('colspan', 1 < num_acc ? num_acc : 1);
|
||||
|
||||
!num_acc && header.html('..') || header.remove();
|
||||
|
||||
var elInputs = elUpdateRows.find('input[id*=update_trakt_' + JSONData.account_id + ']');
|
||||
!num_acc && elInputs.parent().html('..') || elInputs.parent().remove();
|
||||
|
||||
elUpdateRows.find('td[colspan]').each(function() {
|
||||
$(this).attr('colspan', (num_acc ? 1 + num_acc : 2))
|
||||
});
|
||||
|
||||
elSelected.remove();
|
||||
$('#trakt_accounts').change();
|
||||
|
||||
elTraktAuthResult.html('Deleted account: ' + JSONData.account_name);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
'No' : {
|
||||
'class' : 'red',
|
||||
'action': function() {
|
||||
e.preventDefault();
|
||||
$('#trakt_accounts').change();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#testEmail').click(function () {
|
||||
var status, host, port, tls, from, user, pwd, err, to;
|
||||
|
|
|
@ -191,8 +191,9 @@ FormToWizard.prototype = {
|
|||
$section.data('elements', []);
|
||||
|
||||
//create each 'step' DIV and add it to main Steps Container:
|
||||
var $stepwords = ['first', 'second', 'third'], $thestep = $('<div class="step disabledstep" />').data('section', i).html(($stepwords[i]
|
||||
+ ' step') + '<div class="smalltext">' + $section.find('legend:eq(0)').text() + '<p></p></div>').appendTo($stepsguide);
|
||||
var $stepwords = ['first', 'then', 'finally'],
|
||||
$thestep = $('<div class="step disabledstep" />').data('section', i).html(($stepwords[i]) +
|
||||
'<div class="smalltext">' + $section.find('legend:eq(0)').text() + '<p></p></div>').appendTo($stepsguide);
|
||||
|
||||
//assign behavior to each step div
|
||||
$thestep.click(function(){
|
||||
|
|
|
@ -1 +1 @@
|
|||
from trakt import TraktAPI
|
||||
from trakt import TraktAPI
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
class traktException(Exception):
|
||||
class TraktException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class traktAuthException(traktException):
|
||||
class TraktAuthException(TraktException):
|
||||
pass
|
||||
|
||||
|
||||
class traktServerBusy(traktException):
|
||||
class TraktServerBusy(TraktException):
|
||||
pass
|
||||
|
|
|
@ -3,33 +3,144 @@ import certifi
|
|||
import json
|
||||
import sickbeard
|
||||
import time
|
||||
import datetime
|
||||
from sickbeard import logger
|
||||
|
||||
from exceptions import traktException, traktAuthException # , traktServerBusy
|
||||
from exceptions import TraktException, TraktAuthException # , TraktServerBusy
|
||||
|
||||
|
||||
class TraktAccount:
|
||||
max_auth_fail = 9
|
||||
|
||||
def __init__(self, account_id=None, token='', refresh_token='', auth_fail=0, last_fail=None, token_valid_date=None):
|
||||
self.account_id = account_id
|
||||
self._name = ''
|
||||
self.token = token
|
||||
self.refresh_token = refresh_token
|
||||
self.auth_fail = auth_fail
|
||||
self.last_fail = last_fail
|
||||
self.token_valid_date = token_valid_date
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
if self.token and self.active:
|
||||
if not self._name:
|
||||
try:
|
||||
resp = TraktAPI().trakt_request('users/settings', send_oauth=self.account_id, sleep_retry=20)
|
||||
self.reset_auth_failure()
|
||||
if 'user' in resp:
|
||||
self._name = resp['user']['username']
|
||||
except TraktAuthException:
|
||||
self.inc_auth_failure()
|
||||
self._name = ''
|
||||
except TraktException:
|
||||
pass
|
||||
else:
|
||||
self._name = ''
|
||||
|
||||
return self._name
|
||||
|
||||
def reset_name(self):
|
||||
self._name = ''
|
||||
|
||||
@property
|
||||
def active(self):
|
||||
return self.auth_fail < self.max_auth_fail and self.token
|
||||
|
||||
@property
|
||||
def needs_refresh(self):
|
||||
return not self.token_valid_date or self.token_valid_date - datetime.datetime.now() < datetime.timedelta(days=3)
|
||||
|
||||
@property
|
||||
def token_expired(self):
|
||||
return self.token_valid_date and datetime.datetime.now() > self.token_valid_date
|
||||
|
||||
def reset_auth_failure(self):
|
||||
if self.auth_fail != 0:
|
||||
self.auth_fail = 0
|
||||
self.last_fail = None
|
||||
|
||||
def inc_auth_failure(self):
|
||||
self.auth_fail += 1
|
||||
self.last_fail = datetime.datetime.now()
|
||||
|
||||
def auth_failure(self):
|
||||
if self.auth_fail < self.max_auth_fail:
|
||||
if self.last_fail:
|
||||
time_diff = datetime.datetime.now() - self.last_fail
|
||||
if self.auth_fail % 3 == 0:
|
||||
if time_diff > datetime.timedelta(days=1):
|
||||
self.inc_auth_failure()
|
||||
sickbeard.save_config()
|
||||
elif time_diff > datetime.timedelta(minutes=15):
|
||||
self.inc_auth_failure()
|
||||
if self.auth_fail == self.max_auth_fail or time_diff > datetime.timedelta(hours=6):
|
||||
sickbeard.save_config()
|
||||
else:
|
||||
self.inc_auth_failure()
|
||||
|
||||
|
||||
class TraktAPI:
|
||||
max_retrys = 3
|
||||
|
||||
def __init__(self, ssl_verify=True, timeout=None):
|
||||
def __init__(self, timeout=None):
|
||||
|
||||
self.session = requests.Session()
|
||||
self.verify = ssl_verify and sickbeard.TRAKT_VERIFY and certifi.where()
|
||||
self.verify = sickbeard.TRAKT_VERIFY and certifi.where()
|
||||
self.timeout = timeout or sickbeard.TRAKT_TIMEOUT
|
||||
self.auth_url = sickbeard.TRAKT_BASE_URL
|
||||
self.api_url = sickbeard.TRAKT_BASE_URL
|
||||
self.headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'trakt-api-version': '2',
|
||||
'trakt-api-key': sickbeard.TRAKT_CLIENT_ID
|
||||
}
|
||||
self.headers = {'Content-Type': 'application/json',
|
||||
'trakt-api-version': '2',
|
||||
'trakt-api-key': sickbeard.TRAKT_CLIENT_ID}
|
||||
|
||||
def trakt_token(self, trakt_pin=None, refresh=False, count=0):
|
||||
|
||||
if 3 <= count:
|
||||
sickbeard.TRAKT_ACCESS_TOKEN = ''
|
||||
@staticmethod
|
||||
def build_config_string(data):
|
||||
return '!!!'.join('%s|%s|%s|%s|%s|%s' % (
|
||||
value.account_id, value.token, value.refresh_token, value.auth_fail,
|
||||
value.last_fail.strftime('%Y%m%d%H%M') if value.last_fail else '0',
|
||||
value.token_valid_date.strftime('%Y%m%d%H%M%S') if value.token_valid_date else '0') for (key, value) in data.items())
|
||||
|
||||
@staticmethod
|
||||
def read_config_string(data):
|
||||
return dict((int(a.split('|')[0]), TraktAccount(
|
||||
int(a.split('|')[0]), a.split('|')[1], a.split('|')[2], int(a.split('|')[3]),
|
||||
datetime.datetime.strptime(a.split('|')[4], '%Y%m%d%H%M') if a.split('|')[4] != '0' else None,
|
||||
datetime.datetime.strptime(a.split('|')[5], '%Y%m%d%H%M%S') if a.split('|')[5] != '0' else None)) for a in data.split('!!!') if data)
|
||||
|
||||
@staticmethod
|
||||
def add_account(token, refresh_token, token_valid_date):
|
||||
k = max(sickbeard.TRAKT_ACCOUNTS.keys() or [0]) + 1
|
||||
sickbeard.TRAKT_ACCOUNTS[k] = TraktAccount(account_id=k, token=token, refresh_token=refresh_token, token_valid_date=token_valid_date)
|
||||
sickbeard.save_config()
|
||||
return k
|
||||
|
||||
@staticmethod
|
||||
def replace_account(account, token, refresh_token, token_valid_date, refresh):
|
||||
if account in sickbeard.TRAKT_ACCOUNTS:
|
||||
sickbeard.TRAKT_ACCOUNTS[account].token = token
|
||||
sickbeard.TRAKT_ACCOUNTS[account].refresh_token = refresh_token
|
||||
sickbeard.TRAKT_ACCOUNTS[account].token_valid_date = token_valid_date
|
||||
if not refresh:
|
||||
sickbeard.TRAKT_ACCOUNTS[account].reset_name()
|
||||
sickbeard.TRAKT_ACCOUNTS[account].reset_auth_failure()
|
||||
sickbeard.save_config()
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
elif 0 < count:
|
||||
time.sleep(3)
|
||||
|
||||
@staticmethod
|
||||
def delete_account(account):
|
||||
if account in sickbeard.TRAKT_ACCOUNTS:
|
||||
sickbeard.TRAKT_ACCOUNTS.pop(account)
|
||||
sickbeard.save_config()
|
||||
return True
|
||||
return False
|
||||
|
||||
def trakt_token(self, trakt_pin=None, refresh=False, count=0, account=None):
|
||||
if self.max_retrys <= count:
|
||||
return False
|
||||
0 < count and time.sleep(3)
|
||||
|
||||
data = {
|
||||
'client_id': sickbeard.TRAKT_CLIENT_ID,
|
||||
|
@ -38,62 +149,61 @@ class TraktAPI:
|
|||
}
|
||||
|
||||
if refresh:
|
||||
data['grant_type'] = 'refresh_token'
|
||||
data['refresh_token'] = sickbeard.TRAKT_REFRESH_TOKEN
|
||||
if account and account in sickbeard.TRAKT_ACCOUNTS:
|
||||
data['grant_type'] = 'refresh_token'
|
||||
data['refresh_token'] = sickbeard.TRAKT_ACCOUNTS[account].refresh_token
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
data['grant_type'] = 'authorization_code'
|
||||
if trakt_pin:
|
||||
data['code'] = trakt_pin
|
||||
|
||||
headers = {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
|
||||
resp = self.trakt_request('oauth/token', data=data, headers=headers, url=self.auth_url, method='POST', count=count)
|
||||
|
||||
if 'access_token' in resp:
|
||||
sickbeard.TRAKT_TOKEN = resp['access_token']
|
||||
if 'refresh_token' in resp:
|
||||
sickbeard.TRAKT_REFRESH_TOKEN = resp['refresh_token']
|
||||
return True
|
||||
return False
|
||||
|
||||
def validate_account(self):
|
||||
|
||||
resp = self.trakt_request('users/settings')
|
||||
|
||||
return 'account' in resp
|
||||
|
||||
def get_connected_user(self):
|
||||
|
||||
if sickbeard.TRAKT_TOKEN:
|
||||
response = 'Connected to Trakt user account: %s'
|
||||
|
||||
if sickbeard.TRAKT_CONNECTED_ACCOUNT and sickbeard.TRAKT_TOKEN == sickbeard.TRAKT_CONNECTED_ACCOUNT[1] and sickbeard.TRAKT_CONNECTED_ACCOUNT[0]:
|
||||
return response % sickbeard.TRAKT_CONNECTED_ACCOUNT[0]
|
||||
|
||||
resp = self.trakt_request('users/settings')
|
||||
if 'user' in resp:
|
||||
sickbeard.TRAKT_CONNECTED_ACCOUNT = [resp['user']['username'], sickbeard.TRAKT_TOKEN]
|
||||
return response % sickbeard.TRAKT_CONNECTED_ACCOUNT[0]
|
||||
|
||||
return 'Not connected to Trakt'
|
||||
|
||||
def trakt_request(self, path, data=None, headers=None, url=None, method='GET', count=0):
|
||||
|
||||
if None is sickbeard.TRAKT_TOKEN:
|
||||
logger.log(u'You must get a Trakt token. Check your Trakt settings', logger.WARNING)
|
||||
return {}
|
||||
|
||||
headers = headers or self.headers
|
||||
url = url or self.api_url
|
||||
count += 1
|
||||
|
||||
headers['Authorization'] = 'Bearer ' + sickbeard.TRAKT_TOKEN
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
|
||||
try:
|
||||
resp = self.session.request(method, url + path, headers=headers, timeout=self.timeout,
|
||||
data=json.dumps(data) if data else [], verify=self.verify)
|
||||
now = datetime.datetime.now()
|
||||
resp = self.trakt_request('oauth/token', data=data, headers=headers, url=self.auth_url,
|
||||
count=count, sleep_retry=0)
|
||||
except (TraktAuthException, TraktException):
|
||||
return False
|
||||
|
||||
if 'access_token' in resp and 'refresh_token' in resp and 'expires_in' in resp:
|
||||
token_valid_date = now + datetime.timedelta(seconds=sickbeard.helpers.tryInt(resp['expires_in']))
|
||||
if refresh or (not refresh and account and account in sickbeard.TRAKT_ACCOUNTS):
|
||||
return self.replace_account(account, resp['access_token'], resp['refresh_token'], token_valid_date, refresh)
|
||||
else:
|
||||
return self.add_account(resp['access_token'], resp['refresh_token'], token_valid_date)
|
||||
return False
|
||||
|
||||
def trakt_request(self, path, data=None, headers=None, url=None, count=0, sleep_retry=60, send_oauth=None, **kwargs):
|
||||
|
||||
count += 1
|
||||
if count > self.max_retrys:
|
||||
return {}
|
||||
|
||||
# wait before retry
|
||||
count > 1 and time.sleep(sleep_retry)
|
||||
|
||||
headers = headers or self.headers
|
||||
if send_oauth and send_oauth in sickbeard.TRAKT_ACCOUNTS:
|
||||
if sickbeard.TRAKT_ACCOUNTS[send_oauth].active:
|
||||
if sickbeard.TRAKT_ACCOUNTS[send_oauth].needs_refresh:
|
||||
self.trakt_token(refresh=True, count=0, account=send_oauth)
|
||||
if sickbeard.TRAKT_ACCOUNTS[send_oauth].token_expired:
|
||||
return {}
|
||||
headers['Authorization'] = 'Bearer %s' % sickbeard.TRAKT_ACCOUNTS[send_oauth].token
|
||||
else:
|
||||
return {}
|
||||
|
||||
kwargs = dict(headers=headers, timeout=self.timeout, verify=self.verify)
|
||||
if data:
|
||||
kwargs['data'] = json.dumps(data)
|
||||
|
||||
url = url or self.api_url
|
||||
try:
|
||||
resp = self.session.request(('GET', 'POST')['data' in kwargs.keys()],
|
||||
url + path, **kwargs)
|
||||
|
||||
# check for http errors and raise if any are present
|
||||
resp.raise_for_status()
|
||||
|
@ -104,25 +214,37 @@ class TraktAPI:
|
|||
code = getattr(e.response, 'status_code', None)
|
||||
if not code:
|
||||
if 'timed out' in e:
|
||||
logger.log(u'Timeout connecting to Trakt. Try to increase timeout value in Trakt settings', logger.WARNING)
|
||||
logger.log(u'Timeout connecting to Trakt', logger.WARNING)
|
||||
# This is pretty much a fatal error if there is no status_code
|
||||
# It means there basically was no response at all
|
||||
else:
|
||||
logger.log(u'Could not connect to Trakt. Error: {0}'.format(e), logger.WARNING)
|
||||
elif 502 == code:
|
||||
# Retry the request, Cloudflare had a proxying issue
|
||||
logger.log(u'Retrying trakt api request: %s' % path, logger.WARNING)
|
||||
return self.trakt_request(path, data, headers, url, method, count=count)
|
||||
elif 401 == code:
|
||||
if self.trakt_token(refresh=True, count=count):
|
||||
sickbeard.save_config()
|
||||
return self.trakt_request(path, data, headers, url, method, count=count)
|
||||
logger.log(u'Retrying Trakt api request: %s' % path, logger.WARNING)
|
||||
return self.trakt_request(path, data, headers, url, count=count, sleep_retry=sleep_retry, send_oauth=send_oauth)
|
||||
elif 401 == code and path != 'oauth/token':
|
||||
if send_oauth:
|
||||
if sickbeard.TRAKT_ACCOUNTS[send_oauth].needs_refresh:
|
||||
if self.trakt_token(refresh=True, count=count, account=send_oauth):
|
||||
return self.trakt_request(path, data, headers, url, count=count, sleep_retry=sleep_retry, send_oauth=send_oauth)
|
||||
else:
|
||||
logger.log(u'Unauthorized. Please check your Trakt settings', logger.WARNING)
|
||||
sickbeard.TRAKT_ACCOUNTS[send_oauth].auth_failure()
|
||||
raise TraktAuthException()
|
||||
else:
|
||||
# sometimes the trakt server sends invalid token error even if it isn't
|
||||
sickbeard.TRAKT_ACCOUNTS[send_oauth].auth_failure()
|
||||
if count >= self.max_retrys:
|
||||
raise TraktAuthException()
|
||||
else:
|
||||
return self.trakt_request(path, data, headers, url, count=count, sleep_retry=sleep_retry, send_oauth=send_oauth)
|
||||
else:
|
||||
logger.log(u'Unauthorized. Please check your Trakt settings', logger.WARNING)
|
||||
raise traktAuthException()
|
||||
raise TraktAuthException()
|
||||
elif code in (500, 501, 503, 504, 520, 521, 522):
|
||||
# http://docs.trakt.apiary.io/#introduction/status-codes
|
||||
logger.log(u'Trakt may have some issues and it\'s unavailable. Try again later please', logger.WARNING)
|
||||
logger.log(u'Trakt may have some issues and it\'s unavailable. Trying again', logger.WARNING)
|
||||
self.trakt_request(path, data, headers, url, count=count, sleep_retry=sleep_retry, send_oauth=send_oauth)
|
||||
elif 404 == code:
|
||||
logger.log(u'Trakt error (404) the resource does not exist: %s' % url + path, logger.WARNING)
|
||||
else:
|
||||
|
@ -132,10 +254,12 @@ class TraktAPI:
|
|||
# check and confirm Trakt call did not fail
|
||||
if isinstance(resp, dict) and 'failure' == resp.get('status', None):
|
||||
if 'message' in resp:
|
||||
raise traktException(resp['message'])
|
||||
raise TraktException(resp['message'])
|
||||
if 'error' in resp:
|
||||
raise traktException(resp['error'])
|
||||
raise TraktException(resp['error'])
|
||||
else:
|
||||
raise traktException('Unknown Error')
|
||||
raise TraktException('Unknown Error')
|
||||
|
||||
if send_oauth and send_oauth in sickbeard.TRAKT_ACCOUNTS:
|
||||
sickbeard.TRAKT_ACCOUNTS[send_oauth].reset_auth_failure()
|
||||
return resp
|
||||
|
|
|
@ -38,11 +38,14 @@ from sickbeard import helpers, logger, db, naming, metadata, providers, scene_ex
|
|||
from sickbeard.config import CheckSection, check_setting_int, check_setting_str, ConfigMigrator, minimax
|
||||
from sickbeard.common import SD, SKIPPED
|
||||
from sickbeard.databases import mainDB, cache_db, failed_db
|
||||
from indexers.indexer_config import INDEXER_TVDB
|
||||
from indexers.indexer_api import indexerApi
|
||||
from indexers.indexer_exceptions import indexer_shownotfound, indexer_exception, indexer_error, \
|
||||
indexer_episodenotfound, indexer_attributenotfound, indexer_seasonnotfound, indexer_userabort, indexerExcepts
|
||||
from sickbeard.providers.generic import GenericProvider
|
||||
from lib.configobj import ConfigObj
|
||||
from lib.libtrakt import TraktAPI
|
||||
import trakt_helpers
|
||||
|
||||
PID = None
|
||||
|
||||
|
@ -50,7 +53,7 @@ CFG = None
|
|||
CONFIG_FILE = None
|
||||
|
||||
# This is the version of the config we EXPECT to find
|
||||
CONFIG_VERSION = 13
|
||||
CONFIG_VERSION = 14
|
||||
|
||||
# Default encryption version (0 for None)
|
||||
ENCRYPTION_VERSION = 0
|
||||
|
@ -351,8 +354,6 @@ SYNOLOGYNOTIFIER_NOTIFY_ONDOWNLOAD = False
|
|||
SYNOLOGYNOTIFIER_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
||||
|
||||
USE_TRAKT = False
|
||||
TRAKT_TOKEN = ''
|
||||
TRAKT_REFRESH_TOKEN = ''
|
||||
TRAKT_REMOVE_WATCHLIST = False
|
||||
TRAKT_REMOVE_SERIESLIST = False
|
||||
TRAKT_USE_WATCHLIST = False
|
||||
|
@ -360,6 +361,7 @@ TRAKT_METHOD_ADD = 0
|
|||
TRAKT_START_PAUSED = False
|
||||
TRAKT_SYNC = False
|
||||
TRAKT_DEFAULT_INDEXER = None
|
||||
TRAKT_UPDATE_COLLECTION = {}
|
||||
|
||||
USE_PYTIVO = False
|
||||
PYTIVO_NOTIFY_ONSNATCH = False
|
||||
|
@ -449,6 +451,7 @@ TRAKT_STAGING = False
|
|||
TRAKT_TIMEOUT = 60
|
||||
TRAKT_VERIFY = True
|
||||
TRAKT_CONNECTED_ACCOUNT = None
|
||||
TRAKT_ACCOUNTS = {}
|
||||
|
||||
if TRAKT_STAGING:
|
||||
# staging trakt values:
|
||||
|
@ -484,7 +487,7 @@ def initialize(consoleLogging=True):
|
|||
USE_XBMC, XBMC_ALWAYS_ON, XBMC_NOTIFY_ONSNATCH, XBMC_NOTIFY_ONDOWNLOAD, XBMC_NOTIFY_ONSUBTITLEDOWNLOAD, XBMC_UPDATE_FULL, XBMC_UPDATE_ONLYFIRST, \
|
||||
XBMC_UPDATE_LIBRARY, XBMC_HOST, XBMC_USERNAME, XBMC_PASSWORD, BACKLOG_FREQUENCY, \
|
||||
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, \
|
||||
USE_TRAKT, TRAKT_CONNECTED_ACCOUNT, TRAKT_VERIFY, TRAKT_REMOVE_WATCHLIST, TRAKT_TOKEN, TRAKT_TIMEOUT, TRAKT_REFRESH_TOKEN, TRAKT_USE_WATCHLIST, TRAKT_METHOD_ADD, TRAKT_START_PAUSED, traktCheckerScheduler, TRAKT_SYNC, TRAKT_DEFAULT_INDEXER, TRAKT_REMOVE_SERIESLIST, \
|
||||
USE_TRAKT, TRAKT_CONNECTED_ACCOUNT, TRAKT_ACCOUNTS, TRAKT_VERIFY, TRAKT_REMOVE_WATCHLIST, TRAKT_TIMEOUT, TRAKT_USE_WATCHLIST, TRAKT_METHOD_ADD, TRAKT_START_PAUSED, traktCheckerScheduler, TRAKT_SYNC, TRAKT_DEFAULT_INDEXER, TRAKT_REMOVE_SERIESLIST, TRAKT_UPDATE_COLLECTION, \
|
||||
USE_PLEX, PLEX_NOTIFY_ONSNATCH, PLEX_NOTIFY_ONDOWNLOAD, PLEX_NOTIFY_ONSUBTITLEDOWNLOAD, PLEX_UPDATE_LIBRARY, \
|
||||
PLEX_SERVER_HOST, PLEX_HOST, PLEX_USERNAME, PLEX_PASSWORD, DEFAULT_BACKLOG_FREQUENCY, MIN_BACKLOG_FREQUENCY, MAX_BACKLOG_FREQUENCY, BACKLOG_STARTUP, SKIP_REMOVED_FILES, \
|
||||
showUpdateScheduler, __INITIALIZED__, LAUNCH_BROWSER, TRASH_REMOVE_SHOW, TRASH_ROTATE_LOGS, HOME_SEARCH_FOCUS, SORT_ARTICLE, showList, loadingShowList, UPDATE_SHOWS_ON_START, SHOW_UPDATE_HOUR, ALLOW_INCOMPLETE_SHOWDATA, \
|
||||
|
@ -667,6 +670,8 @@ def initialize(consoleLogging=True):
|
|||
NOTIFY_ON_UPDATE = bool(check_setting_int(CFG, 'General', 'notify_on_update', 1))
|
||||
FLATTEN_FOLDERS_DEFAULT = bool(check_setting_int(CFG, 'General', 'flatten_folders_default', 0))
|
||||
INDEXER_DEFAULT = check_setting_int(CFG, 'General', 'indexer_default', 0)
|
||||
if INDEXER_DEFAULT and not indexerApi(INDEXER_DEFAULT).config['active']:
|
||||
INDEXER_DEFAULT = INDEXER_TVDB
|
||||
INDEXER_TIMEOUT = check_setting_int(CFG, 'General', 'indexer_timeout', 20)
|
||||
ANIME_DEFAULT = bool(check_setting_int(CFG, 'General', 'anime_default', 0))
|
||||
SCENE_DEFAULT = bool(check_setting_int(CFG, 'General', 'scene_default', 0))
|
||||
|
@ -872,8 +877,6 @@ def initialize(consoleLogging=True):
|
|||
check_setting_int(CFG, 'SynologyNotifier', 'synologynotifier_notify_onsubtitledownload', 0))
|
||||
|
||||
USE_TRAKT = bool(check_setting_int(CFG, 'Trakt', 'use_trakt', 0))
|
||||
TRAKT_TOKEN = check_setting_str(CFG, 'Trakt', 'trakt_token', '')
|
||||
TRAKT_REFRESH_TOKEN = check_setting_str(CFG, 'Trakt', 'trakt_refresh_token', '')
|
||||
TRAKT_REMOVE_WATCHLIST = bool(check_setting_int(CFG, 'Trakt', 'trakt_remove_watchlist', 0))
|
||||
TRAKT_REMOVE_SERIESLIST = bool(check_setting_int(CFG, 'Trakt', 'trakt_remove_serieslist', 0))
|
||||
TRAKT_USE_WATCHLIST = bool(check_setting_int(CFG, 'Trakt', 'trakt_use_watchlist', 0))
|
||||
|
@ -881,6 +884,8 @@ def initialize(consoleLogging=True):
|
|||
TRAKT_START_PAUSED = bool(check_setting_int(CFG, 'Trakt', 'trakt_start_paused', 0))
|
||||
TRAKT_SYNC = bool(check_setting_int(CFG, 'Trakt', 'trakt_sync', 0))
|
||||
TRAKT_DEFAULT_INDEXER = check_setting_int(CFG, 'Trakt', 'trakt_default_indexer', 1)
|
||||
TRAKT_UPDATE_COLLECTION = trakt_helpers.read_config_string(check_setting_str(CFG, 'Trakt', 'trakt_update_collection', ''))
|
||||
TRAKT_ACCOUNTS = TraktAPI.read_config_string(check_setting_str(CFG, 'Trakt', 'trakt_accounts', ''))
|
||||
|
||||
CheckSection(CFG, 'pyTivo')
|
||||
USE_PYTIVO = bool(check_setting_int(CFG, 'pyTivo', 'use_pytivo', 0))
|
||||
|
@ -1219,8 +1224,8 @@ def start():
|
|||
subtitlesFinderScheduler.start()
|
||||
|
||||
# start the trakt checker
|
||||
if USE_TRAKT:
|
||||
traktCheckerScheduler.start()
|
||||
#if USE_TRAKT:
|
||||
#traktCheckerScheduler.start()
|
||||
|
||||
started = True
|
||||
|
||||
|
@ -1295,13 +1300,13 @@ def halt():
|
|||
except:
|
||||
pass
|
||||
|
||||
if USE_TRAKT:
|
||||
traktCheckerScheduler.stop.set()
|
||||
logger.log(u'Waiting for the TRAKTCHECKER thread to exit')
|
||||
try:
|
||||
traktCheckerScheduler.join(10)
|
||||
except:
|
||||
pass
|
||||
# if USE_TRAKT:
|
||||
# traktCheckerScheduler.stop.set()
|
||||
# logger.log(u'Waiting for the TRAKTCHECKER thread to exit')
|
||||
# try:
|
||||
# traktCheckerScheduler.join(10)
|
||||
# except:
|
||||
# pass
|
||||
|
||||
if DOWNLOAD_PROPERS:
|
||||
properFinderScheduler.stop.set()
|
||||
|
@ -1687,8 +1692,6 @@ def save_config():
|
|||
|
||||
new_config['Trakt'] = {}
|
||||
new_config['Trakt']['use_trakt'] = int(USE_TRAKT)
|
||||
new_config['Trakt']['trakt_token'] = TRAKT_TOKEN
|
||||
new_config['Trakt']['trakt_refresh_token'] = TRAKT_REFRESH_TOKEN
|
||||
new_config['Trakt']['trakt_remove_watchlist'] = int(TRAKT_REMOVE_WATCHLIST)
|
||||
new_config['Trakt']['trakt_remove_serieslist'] = int(TRAKT_REMOVE_SERIESLIST)
|
||||
new_config['Trakt']['trakt_use_watchlist'] = int(TRAKT_USE_WATCHLIST)
|
||||
|
@ -1696,6 +1699,8 @@ def save_config():
|
|||
new_config['Trakt']['trakt_start_paused'] = int(TRAKT_START_PAUSED)
|
||||
new_config['Trakt']['trakt_sync'] = int(TRAKT_SYNC)
|
||||
new_config['Trakt']['trakt_default_indexer'] = int(TRAKT_DEFAULT_INDEXER)
|
||||
new_config['Trakt']['trakt_update_collection'] = trakt_helpers.build_config_string(TRAKT_UPDATE_COLLECTION)
|
||||
new_config['Trakt']['trakt_accounts'] = TraktAPI.build_config_string(TRAKT_ACCOUNTS)
|
||||
|
||||
new_config['pyTivo'] = {}
|
||||
new_config['pyTivo']['use_pytivo'] = int(USE_PYTIVO)
|
||||
|
|
|
@ -25,6 +25,7 @@ import sickbeard
|
|||
import sickbeard.providers
|
||||
from sickbeard import encodingKludge as ek
|
||||
from sickbeard import helpers, logger, naming, db
|
||||
from lib.libtrakt import TraktAPI
|
||||
|
||||
|
||||
naming_ep_type = ('%(seasonnumber)dx%(episodenumber)02d',
|
||||
|
@ -212,15 +213,15 @@ def change_USE_TRAKT(use_trakt):
|
|||
return
|
||||
|
||||
sickbeard.USE_TRAKT = use_trakt
|
||||
if sickbeard.USE_TRAKT:
|
||||
sickbeard.traktCheckerScheduler.start()
|
||||
else:
|
||||
sickbeard.traktCheckerScheduler.stop.set()
|
||||
logger.log(u'Waiting for the TRAKTCHECKER thread to exit')
|
||||
try:
|
||||
sickbeard.traktCheckerScheduler.join(10)
|
||||
except:
|
||||
pass
|
||||
# if sickbeard.USE_TRAKT:
|
||||
# sickbeard.traktCheckerScheduler.start()
|
||||
# else:
|
||||
# sickbeard.traktCheckerScheduler.stop.set()
|
||||
# logger.log(u'Waiting for the TRAKTCHECKER thread to exit')
|
||||
# try:
|
||||
# sickbeard.traktCheckerScheduler.join(10)
|
||||
# except:
|
||||
# pass
|
||||
|
||||
|
||||
def change_USE_SUBTITLES(use_subtitles):
|
||||
|
@ -446,7 +447,8 @@ class ConfigMigrator():
|
|||
10: 'Reset backlog frequency to default',
|
||||
11: 'Migrate anime split view to new layout',
|
||||
12: 'Add "hevc" and some non-english languages to ignore words if not found',
|
||||
13: 'Change default dereferrer url to blank'}
|
||||
13: 'Change default dereferrer url to blank',
|
||||
14: 'Convert Trakt to multi-account'}
|
||||
|
||||
def migrate_config(self):
|
||||
""" Calls each successive migration until the config is the same version as SG expects """
|
||||
|
@ -601,7 +603,7 @@ class ConfigMigrator():
|
|||
Reads in the old naming settings from your config and generates a new config template from them.
|
||||
"""
|
||||
# get the old settings from the file and store them in the new variable names
|
||||
for prov in [curProvider for curProvider in providers.sortedProviderList() if curProvider.name == 'omgwtfnzbs']:
|
||||
for prov in [curProvider for curProvider in sickbeard.providers.sortedProviderList() if curProvider.name == 'omgwtfnzbs']:
|
||||
prov.username = check_setting_str(self.config_obj, 'omgwtfnzbs', 'omgwtfnzbs_uid', '')
|
||||
prov.api_key = check_setting_str(self.config_obj, 'omgwtfnzbs', 'omgwtfnzbs_key', '')
|
||||
|
||||
|
@ -721,7 +723,7 @@ class ConfigMigrator():
|
|||
if sickbeard.RECENTSEARCH_FREQUENCY < sickbeard.MIN_RECENTSEARCH_FREQUENCY:
|
||||
sickbeard.RECENTSEARCH_FREQUENCY = sickbeard.MIN_RECENTSEARCH_FREQUENCY
|
||||
|
||||
for curProvider in providers.sortedProviderList():
|
||||
for curProvider in sickbeard.providers.sortedProviderList():
|
||||
if hasattr(curProvider, 'enable_recentsearch'):
|
||||
curProvider.enable_recentsearch = bool(check_setting_int(
|
||||
self.config_obj, curProvider.get_id().upper(), curProvider.get_id() + '_enable_dailysearch', 1))
|
||||
|
@ -776,3 +778,9 @@ class ConfigMigrator():
|
|||
# change dereferrer.org urls to blank, but leave any other url untouched
|
||||
if sickbeard.ANON_REDIRECT == 'http://dereferer.org/?':
|
||||
sickbeard.ANON_REDIRECT = ''
|
||||
|
||||
def _migrate_v14(self):
|
||||
old_token = check_setting_str(self.config_obj, 'Trakt', 'trakt_token', '')
|
||||
old_refresh_token = check_setting_str(self.config_obj, 'Trakt', 'trakt_refresh_token', '')
|
||||
if old_token and old_refresh_token:
|
||||
TraktAPI.add_account(old_token, old_refresh_token, None)
|
|
@ -17,10 +17,63 @@
|
|||
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
|
||||
import os
|
||||
import sickbeard
|
||||
import time
|
||||
|
||||
from indexer_config import initConfig, indexerConfig
|
||||
from sickbeard.helpers import proxy_setting
|
||||
|
||||
|
||||
class ShowContainer(dict):
|
||||
"""Simple dict that holds a series of Show instances
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._stack = []
|
||||
self._lastgc = time.time()
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self._stack.append(key)
|
||||
|
||||
# keep only the 100th latest results
|
||||
if time.time() - self._lastgc > 20:
|
||||
for o in self._stack[:-100]:
|
||||
del self[o]
|
||||
|
||||
self._stack = self._stack[-100:]
|
||||
|
||||
self._lastgc = time.time()
|
||||
|
||||
super(ShowContainer, self).__setitem__(key, value)
|
||||
|
||||
|
||||
class DummyIndexer:
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.config = {
|
||||
'apikey': '',
|
||||
'debug_enabled': False,
|
||||
'custom_ui': None,
|
||||
'proxy': None,
|
||||
'cache_enabled': False,
|
||||
'cache_location': '',
|
||||
'valid_languages': [],
|
||||
'langabbv_to_id': {},
|
||||
'language': 'en',
|
||||
'base_url': '',
|
||||
}
|
||||
|
||||
self.corrections = {}
|
||||
self.shows = ShowContainer()
|
||||
|
||||
def __getitem__(self, key):
|
||||
return None
|
||||
|
||||
def __repr__(self):
|
||||
return str(self.shows)
|
||||
|
||||
def search(self, series):
|
||||
return []
|
||||
|
||||
|
||||
class indexerApi(object):
|
||||
def __init__(self, indexerID=None):
|
||||
self.indexerID = int(indexerID) if indexerID else None
|
||||
|
@ -30,7 +83,10 @@ class indexerApi(object):
|
|||
|
||||
def indexer(self, *args, **kwargs):
|
||||
if self.indexerID:
|
||||
return indexerConfig[self.indexerID]['module'](*args, **kwargs)
|
||||
if indexerConfig[self.indexerID]['active']:
|
||||
return indexerConfig[self.indexerID]['module'](*args, **kwargs)
|
||||
else:
|
||||
return DummyIndexer(*args, **kwargs)
|
||||
|
||||
@property
|
||||
def config(self):
|
||||
|
|
|
@ -25,6 +25,7 @@ indexerConfig[INDEXER_TVDB] = {
|
|||
'language': 'en',
|
||||
'useZip': True,
|
||||
},
|
||||
'active': True,
|
||||
}
|
||||
|
||||
indexerConfig[INDEXER_TVRAGE] = {
|
||||
|
@ -34,6 +35,7 @@ indexerConfig[INDEXER_TVRAGE] = {
|
|||
'api_params': {'apikey': 'Uhewg1Rr0o62fvZvUIZt',
|
||||
'language': 'en',
|
||||
},
|
||||
'active': False,
|
||||
}
|
||||
|
||||
# TVDB Indexer Settings
|
||||
|
|
|
@ -26,6 +26,7 @@ import nmjv2
|
|||
import synoindex
|
||||
import synologynotifier
|
||||
import pytivo
|
||||
import trakt
|
||||
|
||||
import growl
|
||||
import prowl
|
||||
|
@ -62,7 +63,7 @@ pushalot_notifier = pushalot.PushalotNotifier()
|
|||
pushbullet_notifier = pushbullet.PushbulletNotifier()
|
||||
# social
|
||||
twitter_notifier = tweet.TwitterNotifier()
|
||||
#trakt_notifier = trakt.TraktNotifier()
|
||||
trakt_notifier = trakt.TraktNotifier()
|
||||
email_notifier = emailnotify.EmailNotifier()
|
||||
|
||||
notifiers = [
|
||||
|
@ -83,7 +84,7 @@ notifiers = [
|
|||
pushalot_notifier,
|
||||
pushbullet_notifier,
|
||||
twitter_notifier,
|
||||
# trakt_notifier,
|
||||
trakt_notifier,
|
||||
email_notifier,
|
||||
]
|
||||
|
||||
|
|
|
@ -18,10 +18,13 @@
|
|||
|
||||
import sickbeard
|
||||
from sickbeard import logger
|
||||
from lib.libtrakt import TraktAPI
|
||||
from lib.libtrakt import TraktAPI, exceptions
|
||||
|
||||
|
||||
class TraktNotifier:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
"""
|
||||
A "notifier" for trakt.tv which keeps track of what has and hasn't been added to your library.
|
||||
"""
|
||||
|
@ -34,103 +37,71 @@ class TraktNotifier:
|
|||
|
||||
def notify_subtitle_download(self, ep_name, lang):
|
||||
pass
|
||||
|
||||
|
||||
def notify_git_update(self, new_version):
|
||||
pass
|
||||
|
||||
def update_library(self, ep_obj):
|
||||
@staticmethod
|
||||
def update_collection(ep_obj):
|
||||
"""
|
||||
Sends a request to trakt indicating that the given episode is part of our library.
|
||||
|
||||
ep_obj: The TVEpisode object to add to trakt
|
||||
Sends a request to trakt indicating that the given episode is part of our collection.
|
||||
|
||||
:param ep_obj: The TVEpisode object to add to trakt
|
||||
"""
|
||||
|
||||
if sickbeard.USE_TRAKT:
|
||||
if sickbeard.USE_TRAKT and sickbeard.TRAKT_ACCOUNTS:
|
||||
|
||||
# URL parameters
|
||||
data = {
|
||||
'tvdb_id': ep_obj.show.indexerid,
|
||||
'title': ep_obj.show.name,
|
||||
'year': ep_obj.show.startyear,
|
||||
'episodes': [{
|
||||
'season': ep_obj.season,
|
||||
'episode': ep_obj.episode
|
||||
}]
|
||||
'shows': [
|
||||
{
|
||||
'title': ep_obj.show.name,
|
||||
'year': ep_obj.show.startyear,
|
||||
'ids': {},
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
if data is not None:
|
||||
TraktCall('show/episode/library/%API%', self._api(), self._username(), self._password(), data)
|
||||
if sickbeard.TRAKT_REMOVE_WATCHLIST:
|
||||
TraktCall('show/episode/unwatchlist/%API%', self._api(), self._username(), self._password(), data)
|
||||
indexer = ('tvrage', 'tvdb')[1 == ep_obj.show.indexer]
|
||||
data['shows'][0]['ids'][indexer] = ep_obj.show.indexerid
|
||||
|
||||
if sickbeard.TRAKT_REMOVE_SERIESLIST:
|
||||
data_show = None
|
||||
# Add Season and Episode + Related Episodes
|
||||
data['shows'][0]['seasons'] = [{'number': ep_obj.season, 'episodes': []}]
|
||||
|
||||
# URL parameters, should not need to recheck data (done above)
|
||||
data = {
|
||||
'shows': [
|
||||
{
|
||||
'tvdb_id': ep_obj.show.indexerid,
|
||||
'title': ep_obj.show.name,
|
||||
'year': ep_obj.show.startyear
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
TraktCall('show/unwatchlist/%API%', self._api(), self._username(), self._password(), data)
|
||||
for relEp_Obj in [ep_obj] + ep_obj.relatedEps:
|
||||
data['shows'][0]['seasons'][0]['episodes'].append({'number': relEp_Obj.episode})
|
||||
|
||||
# Remove all episodes from episode watchlist
|
||||
# Start by getting all episodes in the watchlist
|
||||
watchlist = TraktCall('user/watchlist/episodes.json/%API%/' + sickbeard.TRAKT_USERNAME, sickbeard.TRAKT_API, sickbeard.TRAKT_USERNAME, sickbeard.TRAKT_PASSWORD)
|
||||
for tid, locations in sickbeard.TRAKT_UPDATE_COLLECTION.items():
|
||||
if tid not in sickbeard.TRAKT_ACCOUNTS.keys():
|
||||
continue
|
||||
for loc in locations:
|
||||
if not ep_obj.location.startswith('%s\\' % loc.rstrip('\\')):
|
||||
continue
|
||||
|
||||
if watchlist is not None:
|
||||
# Convert watchlist to only contain current show
|
||||
for show in watchlist:
|
||||
# Check if tvdb_id exists
|
||||
if 'tvdb_id' in show:
|
||||
if unicode(data['shows'][0]['tvdb_id']) == show['tvdb_id']:
|
||||
data_show = {
|
||||
'title': show['title'],
|
||||
'tvdb_id': show['tvdb_id'],
|
||||
'episodes': []
|
||||
}
|
||||
|
||||
# Add series and episode (number) to the arry
|
||||
for episodes in show['episodes']:
|
||||
ep = {'season': episodes['season'], 'episode': episodes['number']}
|
||||
data_show['episodes'].append(ep)
|
||||
if data_show is not None:
|
||||
TraktCall('show/episode/unwatchlist/%API%', sickbeard.TRAKT_API, sickbeard.TRAKT_USERNAME, sickbeard.TRAKT_PASSWORD, data_show)
|
||||
warn, msg = False, ''
|
||||
try:
|
||||
resp = TraktAPI().trakt_request('sync/collection', data, send_oauth=tid)
|
||||
if 'added' in resp and 'episodes' in resp['added'] and 0 < sickbeard.helpers.tryInt(resp['added']['episodes']):
|
||||
msg = 'Added episode to'
|
||||
elif 'updated' in resp and 'episodes' in resp['updated'] and 0 < sickbeard.helpers.tryInt(resp['updated']['episodes']):
|
||||
msg = 'Updated episode in'
|
||||
elif 'existing' in resp and 'episodes' in resp['existing'] and 0 < sickbeard.helpers.tryInt(resp['existing']['episodes']):
|
||||
msg = 'Episode is already in'
|
||||
elif 'not_found' in resp and 'episodes' in resp['not_found'] and 0 < sickbeard.helpers.tryInt(resp['not_found']['episodes']):
|
||||
msg = 'Episode not found on Trakt, not adding to'
|
||||
else:
|
||||
warn, msg = True, 'Could not add episode to'
|
||||
except exceptions.TraktAuthException, exceptions.TraktException:
|
||||
warn, msg = True, 'Error adding episode to'
|
||||
msg = 'Trakt: %s your %s collection' % (msg, sickbeard.TRAKT_ACCOUNTS[tid].name)
|
||||
if not warn:
|
||||
logger.log(msg)
|
||||
else:
|
||||
logger.log('Failed to get watchlist from trakt. Unable to remove episode from watchlist',
|
||||
logger.ERROR)
|
||||
logger.log(msg, logger.WARNING)
|
||||
|
||||
def test_notify(self, api, username, password):
|
||||
"""
|
||||
Sends a test notification to trakt with the given authentication info and returns a boolean
|
||||
representing success.
|
||||
|
||||
api: The api string to use
|
||||
username: The username to use
|
||||
password: The password to use
|
||||
|
||||
Returns: True if the request succeeded, False otherwise
|
||||
"""
|
||||
|
||||
data = TraktCall('account/test/%API%', api, username, password)
|
||||
if data and data['status'] == 'success':
|
||||
return True
|
||||
|
||||
def _username(self):
|
||||
return sickbeard.TRAKT_USERNAME
|
||||
|
||||
def _password(self):
|
||||
return sickbeard.TRAKT_PASSWORD
|
||||
|
||||
def _api(self):
|
||||
return sickbeard.TRAKT_API
|
||||
|
||||
def _use_me(self):
|
||||
@staticmethod
|
||||
def _use_me():
|
||||
return sickbeard.USE_TRAKT
|
||||
|
||||
|
||||
|
|
|
@ -1034,7 +1034,7 @@ class PostProcessor(object):
|
|||
notifiers.pytivo_notifier.update_library(ep_obj)
|
||||
|
||||
# do the library update for Trakt
|
||||
# notifiers.trakt_notifier.update_library(ep_obj)
|
||||
notifiers.trakt_notifier.update_collection(ep_obj)
|
||||
|
||||
self._run_extra_scripts(ep_obj)
|
||||
|
||||
|
|
|
@ -556,7 +556,7 @@ class GenericProvider:
|
|||
value *= 1024 ** ['b', 'k', 'm', 'g', 't'].index(re.findall('(t|g|m|k)[i]?b', size_dim.lower())[0])
|
||||
except IndexError:
|
||||
pass
|
||||
return int(math.ceil(value))
|
||||
return long(math.ceil(value))
|
||||
|
||||
|
||||
class NZBProvider(object, GenericProvider):
|
||||
|
|
|
@ -459,13 +459,13 @@ class QueueItemAdd(ShowQueueItem):
|
|||
|
||||
self.show.flushEpisodes()
|
||||
|
||||
if sickbeard.USE_TRAKT:
|
||||
# if there are specific episodes that need to be added by trakt
|
||||
sickbeard.traktCheckerScheduler.action.manageNewShow(self.show)
|
||||
|
||||
# add show to trakt.tv library
|
||||
if sickbeard.TRAKT_SYNC:
|
||||
sickbeard.traktCheckerScheduler.action.addShowToTraktLibrary(self.show)
|
||||
# if sickbeard.USE_TRAKT:
|
||||
# # if there are specific episodes that need to be added by trakt
|
||||
# sickbeard.traktCheckerScheduler.action.manageNewShow(self.show)
|
||||
#
|
||||
# # add show to trakt.tv library
|
||||
# if sickbeard.TRAKT_SYNC:
|
||||
# sickbeard.traktCheckerScheduler.action.addShowToTraktLibrary(self.show)
|
||||
|
||||
# Load XEM data to DB for show
|
||||
sickbeard.scene_numbering.xem_refresh(self.show.indexerid, self.show.indexer, force=True)
|
||||
|
@ -578,6 +578,11 @@ class QueueItemUpdate(ShowQueueItem):
|
|||
|
||||
ShowQueueItem.run(self)
|
||||
|
||||
if not sickbeard.indexerApi(self.show.indexer).config['active']:
|
||||
logger.log(u'Indexer %s is marked inactive, aborting update for show %s and continue with refresh.' % (sickbeard.indexerApi(self.show.indexer).config['name'], self.show.name))
|
||||
sickbeard.showQueueScheduler.action.refreshShow(self.show, self.force, self.scheduled_update, after_update=True)
|
||||
return
|
||||
|
||||
logger.log(u'Beginning update of ' + self.show.name)
|
||||
|
||||
logger.log(u'Retrieving show info from ' + sickbeard.indexerApi(self.show.indexer).name + '', logger.DEBUG)
|
||||
|
|
57
sickbeard/trakt_helpers.py
Normal file
57
sickbeard/trakt_helpers.py
Normal file
|
@ -0,0 +1,57 @@
|
|||
import ast
|
||||
import base64
|
||||
import re
|
||||
import sickbeard
|
||||
from helpers import tryInt
|
||||
|
||||
|
||||
def read_config_string(data):
|
||||
|
||||
return data and dict((tryInt(x[0]), x[1]) for x in ast.literal_eval(data)) or {}
|
||||
|
||||
|
||||
def build_config(**kwargs):
|
||||
"""
|
||||
kwargs is filtered for settings that enable updates to Trakt
|
||||
|
||||
:param kwargs: kwargs to be filtered for settings that enable updates to Trakt
|
||||
:return: dict of parsed config kwargs where k is Trakt account id, v is a parent location
|
||||
"""
|
||||
|
||||
config = {}
|
||||
|
||||
root_dirs = []
|
||||
if sickbeard.ROOT_DIRS:
|
||||
root_pieces = sickbeard.ROOT_DIRS.split('|')
|
||||
root_dirs = root_pieces[1:]
|
||||
|
||||
for item in [re.findall('update_trakt_(\d+)_(.*)', k) for k, v in kwargs.items() if k.startswith('update_trakt_')]:
|
||||
for account_id, location in item:
|
||||
account_id = tryInt(account_id, None)
|
||||
if None is account_id:
|
||||
continue
|
||||
for cur_dir in root_dirs:
|
||||
account_id = tryInt(account_id, None)
|
||||
if account_id and base64.urlsafe_b64encode(cur_dir) == location:
|
||||
if isinstance(config.get(account_id), list):
|
||||
config[account_id] += [cur_dir]
|
||||
else:
|
||||
config[account_id] = [cur_dir]
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def build_config_string(config):
|
||||
"""
|
||||
:param config: dicts of Trakt account id, parent location
|
||||
:return: string csv of parsed config kwargs for config file
|
||||
"""
|
||||
return unicode(config.items())
|
||||
|
||||
|
||||
def trakt_collection_remove_account(account_id):
|
||||
if account_id in sickbeard.TRAKT_UPDATE_COLLECTION:
|
||||
sickbeard.TRAKT_UPDATE_COLLECTION.pop(account_id)
|
||||
sickbeard.save_config()
|
||||
return True
|
||||
return False
|
|
@ -1312,17 +1312,17 @@ class TVShow(object):
|
|||
|
||||
def getOverview(self, epStatus):
|
||||
|
||||
if ARCHIVED == epStatus:
|
||||
status, quality = Quality.splitCompositeStatus(epStatus)
|
||||
if ARCHIVED == status:
|
||||
return Overview.GOOD
|
||||
if WANTED == epStatus:
|
||||
if WANTED == status:
|
||||
return Overview.WANTED
|
||||
if epStatus in (SKIPPED, IGNORED):
|
||||
if status in (SKIPPED, IGNORED):
|
||||
return Overview.SKIPPED
|
||||
if epStatus in (UNAIRED, UNKNOWN):
|
||||
if status in (UNAIRED, UNKNOWN):
|
||||
return Overview.UNAIRED
|
||||
if epStatus in Quality.DOWNLOADED + Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.FAILED + Quality.SNATCHED_BEST:
|
||||
if status in Quality.DOWNLOADED + Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.FAILED + Quality.SNATCHED_BEST:
|
||||
|
||||
status, quality = Quality.splitCompositeStatus(epStatus)
|
||||
if FAILED == status:
|
||||
return Overview.WANTED
|
||||
if status in (SNATCHED, SNATCHED_PROPER, SNATCHED_BEST):
|
||||
|
|
|
@ -18,15 +18,16 @@
|
|||
|
||||
from __future__ import with_statement
|
||||
|
||||
import os
|
||||
import time
|
||||
import urllib
|
||||
import re
|
||||
import base64
|
||||
import datetime
|
||||
import dateutil.parser
|
||||
import random
|
||||
import traceback
|
||||
import itertools
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import time
|
||||
import traceback
|
||||
import urllib
|
||||
|
||||
from mimetypes import MimeTypes
|
||||
from Cheetah.Template import Template
|
||||
|
@ -57,7 +58,8 @@ from lib import subliminal
|
|||
from lib.dateutil import tz
|
||||
from lib.unrar2 import RarFile
|
||||
from lib.libtrakt import TraktAPI
|
||||
from lib.libtrakt.exceptions import traktException, traktAuthException
|
||||
from lib.libtrakt.exceptions import TraktException, TraktAuthException
|
||||
from trakt_helpers import build_config, trakt_collection_remove_account
|
||||
from sickbeard.bs4_parser import BS4Parser
|
||||
|
||||
|
||||
|
@ -883,24 +885,54 @@ class Home(MainHandler):
|
|||
return '{"message": "Unable to find NMJ Database at location: %(dbloc)s. Is the right location selected and PCH running?", "database": ""}' % {
|
||||
"dbloc": dbloc}
|
||||
|
||||
def trakt_authenticate(self, pin=None):
|
||||
def trakt_authenticate(self, pin=None, account=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
if None is pin:
|
||||
return 'Trakt PIN required for authentication'
|
||||
return json.dumps({'result': 'Fail', 'error_message': 'Trakt PIN required for authentication'})
|
||||
|
||||
if account and 'new' == account:
|
||||
account = None
|
||||
|
||||
acc = None
|
||||
if account:
|
||||
acc = sickbeard.helpers.tryInt(account, -1)
|
||||
if 0 < acc and acc not in sickbeard.TRAKT_ACCOUNTS:
|
||||
return json.dumps({'result': 'Fail', 'error_message': 'Fail: cannot update non-existing account'})
|
||||
|
||||
json_fail_auth = json.dumps({'result': 'Fail', 'error_message': 'Trakt NOT authenticated'})
|
||||
try:
|
||||
TraktAPI().trakt_token(pin)
|
||||
except traktAuthException:
|
||||
return 'Fail: Trakt NOT authenticated'
|
||||
resp = TraktAPI().trakt_token(pin, account=acc)
|
||||
except TraktAuthException:
|
||||
return json_fail_auth
|
||||
if not account and isinstance(resp, bool) and not resp:
|
||||
return json_fail_auth
|
||||
|
||||
sickbeard.USE_TRAKT = True
|
||||
sickbeard.save_config()
|
||||
return '%s %s' % ('Success: Trakt authenticated.', self.trakt_get_connected_account())
|
||||
if not sickbeard.USE_TRAKT:
|
||||
sickbeard.USE_TRAKT = True
|
||||
sickbeard.save_config()
|
||||
pick = resp if not account else acc
|
||||
return json.dumps({'result': 'Success',
|
||||
'account_id': sickbeard.TRAKT_ACCOUNTS[pick].account_id,
|
||||
'account_name': sickbeard.TRAKT_ACCOUNTS[pick].name})
|
||||
|
||||
@staticmethod
|
||||
def trakt_get_connected_account():
|
||||
return TraktAPI().get_connected_user()
|
||||
def trakt_delete(self, accountid=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
if accountid:
|
||||
aid = sickbeard.helpers.tryInt(accountid, None)
|
||||
if None is not aid:
|
||||
if aid in sickbeard.TRAKT_ACCOUNTS:
|
||||
account = {'result': 'Success',
|
||||
'account_id': sickbeard.TRAKT_ACCOUNTS[aid].account_id,
|
||||
'account_name': sickbeard.TRAKT_ACCOUNTS[aid].name}
|
||||
if TraktAPI.delete_account(aid):
|
||||
trakt_collection_remove_account(aid)
|
||||
account['num_accounts'] = len(sickbeard.TRAKT_ACCOUNTS)
|
||||
return json.dumps(account)
|
||||
|
||||
return json.dumps({'result': 'Not found: Account to delete'})
|
||||
return json.dumps({'result': 'Not found: Invalid account id'})
|
||||
|
||||
def loadShowNotifyLists(self, *args, **kwargs):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
@ -968,6 +1000,46 @@ class Home(MainHandler):
|
|||
|
||||
return notifiers.pushbullet_notifier.get_devices(accessToken)
|
||||
|
||||
def viewchanges(self):
|
||||
|
||||
t = PageTemplate(headers=self.request.headers, file='viewchanges.tmpl')
|
||||
|
||||
t.changelist = [{'type': 'rel', 'ver': '', 'date': 'Nothing to display at this time'}]
|
||||
url = 'https://raw.githubusercontent.com/wiki/SickGear/SickGear/sickgear/CHANGES.md'
|
||||
response = helpers.getURL(url)
|
||||
if not response:
|
||||
return t.respond()
|
||||
|
||||
data = response.replace('\xef\xbb\xbf', '').splitlines()
|
||||
|
||||
output, change, max_rel = [], {}, 5
|
||||
for line in data:
|
||||
if not line.strip():
|
||||
continue
|
||||
if line.startswith(' '):
|
||||
change_parts = re.findall('^[\W]+(.*)$', line)
|
||||
change['text'] += change_parts and (' %s' % change_parts[0].strip()) or ''
|
||||
else:
|
||||
if change:
|
||||
output.append(change)
|
||||
if line.startswith('* '):
|
||||
change_parts = re.findall(r'^[\*\W]+(Add|Change|Fix|Port|Remove|Update)\W(.*)', line)
|
||||
change = change_parts and {'type': change_parts[0][0], 'text': change_parts[0][1].strip()} or {}
|
||||
elif line.startswith('### '):
|
||||
rel_data = re.findall(r'(?im)^###\W*([^\s]+)\W\(([^\)]+)\)', line)
|
||||
rel_data and output.append({'type': 'rel', 'ver': rel_data[0][0], 'date': rel_data[0][1]})
|
||||
max_rel -= 1
|
||||
elif line.startswith('# '):
|
||||
max_data = re.findall(r'^#\W*([\d]+)\W*$', line)
|
||||
max_rel = max_data and helpers.tryInt(max_data[0], None) or 5
|
||||
if not max_rel:
|
||||
break
|
||||
if change:
|
||||
output.append(change)
|
||||
|
||||
t.changelist = output
|
||||
return t.respond()
|
||||
|
||||
def shutdown(self, pid=None):
|
||||
|
||||
if str(pid) != str(sickbeard.PID):
|
||||
|
@ -1436,9 +1508,9 @@ class Home(MainHandler):
|
|||
showObj) or sickbeard.showQueueScheduler.action.isBeingUpdated(showObj): # @UndefinedVariable
|
||||
return self._genericMessage("Error", "Shows can't be deleted while they're being added or updated.")
|
||||
|
||||
if sickbeard.USE_TRAKT and sickbeard.TRAKT_SYNC:
|
||||
# remove show from trakt.tv library
|
||||
sickbeard.traktCheckerScheduler.action.removeShowFromTraktLibrary(showObj)
|
||||
# if sickbeard.USE_TRAKT and sickbeard.TRAKT_SYNC:
|
||||
# # remove show from trakt.tv library
|
||||
# sickbeard.traktCheckerScheduler.action.removeShowFromTraktLibrary(showObj)
|
||||
|
||||
showObj.deleteShow(bool(full))
|
||||
|
||||
|
@ -2117,10 +2189,10 @@ class NewHomeAddShows(Home):
|
|||
|
||||
filtered = []
|
||||
try:
|
||||
resp = TraktAPI(ssl_verify=sickbeard.TRAKT_VERIFY, timeout=sickbeard.TRAKT_TIMEOUT).trakt_request(url)
|
||||
resp = TraktAPI().trakt_request(url)
|
||||
if len(resp):
|
||||
filtered = resp
|
||||
except traktException as e:
|
||||
except TraktException as e:
|
||||
logger.log(u'Could not connect to Trakt service: %s' % ex(e), logger.WARNING)
|
||||
|
||||
return filtered
|
||||
|
@ -2276,7 +2348,8 @@ class NewHomeAddShows(Home):
|
|||
t.provided_show_dir = show_dir
|
||||
t.other_shows = other_shows
|
||||
t.provided_indexer = int(indexer or sickbeard.INDEXER_DEFAULT)
|
||||
t.indexers = sickbeard.indexerApi().indexers
|
||||
t.indexers = dict([(i, sickbeard.indexerApi().indexers[i]) for i in sickbeard.indexerApi().indexers
|
||||
if sickbeard.indexerApi(i).config['active']])
|
||||
t.whitelist = []
|
||||
t.blacklist = []
|
||||
t.groups = []
|
||||
|
@ -2471,10 +2544,31 @@ class NewHomeAddShows(Home):
|
|||
|
||||
return self.browse_trakt('shows/trending?limit=%s&' % 100, 'Trending at Trakt', mode='trending')
|
||||
|
||||
def traktPopular(self, *args, **kwargs):
|
||||
|
||||
return self.browse_trakt('shows/popular?limit=%s&' % 100, 'Popular at Trakt', mode='popular')
|
||||
|
||||
def traktWatched(self, *args, **kwargs):
|
||||
|
||||
return self.browse_trakt('shows/watched/monthly?limit=%s&' % 100, 'Most watched at Trakt during the last month', mode='watched')
|
||||
|
||||
def traktCollected(self, *args, **kwargs):
|
||||
|
||||
return self.browse_trakt('shows/collected/monthly?limit=%s&' % 100, 'Most collected at Trakt during the last month', mode='collected')
|
||||
|
||||
def traktAnticipated(self, *args, **kwargs):
|
||||
|
||||
return self.browse_trakt('shows/anticipated?limit=%s&' % 100, 'Anticipated at Trakt', mode='anticipated')
|
||||
|
||||
def traktRecommended(self, *args, **kwargs):
|
||||
|
||||
account = sickbeard.helpers.tryInt(kwargs.get('account'), None)
|
||||
try:
|
||||
name = sickbeard.TRAKT_ACCOUNTS[account].name
|
||||
except KeyError:
|
||||
return self.traktDefault()
|
||||
return self.browse_trakt('recommendations/shows?limit=%s&' % 100,
|
||||
'Recommended for <b class="grey-text">you</b> by Trakt', mode='recommended')
|
||||
'Recommended for <b class="grey-text">%s</b> by Trakt' % name, mode='recommended-%s' % account, send_oauth=account)
|
||||
|
||||
def traktNewShows(self, *args, **kwargs):
|
||||
|
||||
|
@ -2488,18 +2582,25 @@ class NewHomeAddShows(Home):
|
|||
dt=datetime.datetime.now() + datetime.timedelta(days=-16), d_preset='%Y-%m-%d'), 32), 'Season premieres at Trakt',
|
||||
mode='newseasons', footnote='Note; Expect default placeholder images in this list')
|
||||
|
||||
def browse_trakt(self, url, browse_title, *args, **kwargs):
|
||||
def traktDefault(self):
|
||||
|
||||
return self.redirect('/home/addShows/traktTrending/')
|
||||
|
||||
def browse_trakt(self, url_path, browse_title, *args, **kwargs):
|
||||
|
||||
browse_type = 'Trakt'
|
||||
normalised, filtered = ([], [])
|
||||
|
||||
if 'recommended' == kwargs.get('mode', None) and not sickbeard.USE_TRAKT:
|
||||
if not sickbeard.USE_TRAKT and 'recommended' in kwargs.get('mode', ''):
|
||||
error_msg = 'To browse personal recommendations, enable Trakt.tv in Config/Notifications/Social'
|
||||
return self.browse_shows(browse_type, browse_title, filtered, error_msg=error_msg, show_header=1, **kwargs)
|
||||
|
||||
error_msg = None
|
||||
try:
|
||||
resp = TraktAPI(ssl_verify=sickbeard.TRAKT_VERIFY, timeout=sickbeard.TRAKT_TIMEOUT).trakt_request('%sextended=full,images' % url)
|
||||
account = kwargs.get('send_oauth', None)
|
||||
if account:
|
||||
account = sickbeard.helpers.tryInt(account)
|
||||
resp = TraktAPI().trakt_request('%sextended=full,images' % url_path, send_oauth=account)
|
||||
if resp:
|
||||
if 'show' in resp[0]:
|
||||
if 'first_aired' in resp[0]:
|
||||
|
@ -2511,10 +2612,10 @@ class NewHomeAddShows(Home):
|
|||
for item in resp:
|
||||
normalised.append({u'show': item})
|
||||
del resp
|
||||
except traktAuthException as e:
|
||||
except TraktAuthException as e:
|
||||
logger.log(u'Pin authorisation needed to connect to Trakt service: %s' % ex(e), logger.WARNING)
|
||||
error_msg = 'Unauthorized: Get another pin in the Notifications Trakt settings'
|
||||
except traktException as e:
|
||||
except TraktException as e:
|
||||
logger.log(u'Could not connect to Trakt service: %s' % ex(e), logger.WARNING)
|
||||
except (IndexError, KeyError):
|
||||
pass
|
||||
|
@ -3704,6 +3805,8 @@ class ConfigGeneral(Config):
|
|||
t = PageTemplate(headers=self.request.headers, file='config_general.tmpl')
|
||||
t.submenu = self.ConfigMenu
|
||||
t.show_tags = ', '.join(sickbeard.SHOW_TAGS)
|
||||
t.indexers = dict([(i, sickbeard.indexerApi().indexers[i]) for i in sickbeard.indexerApi().indexers
|
||||
if sickbeard.indexerApi(i).config['active']])
|
||||
return t.respond()
|
||||
|
||||
def saveRootDirs(self, rootDirString=None):
|
||||
|
@ -3836,6 +3939,8 @@ class ConfigGeneral(Config):
|
|||
|
||||
if indexer_default:
|
||||
sickbeard.INDEXER_DEFAULT = config.to_int(indexer_default)
|
||||
if not sickbeard.indexerApi(sickbeard.INDEXER_DEFAULT).config['active']:
|
||||
sickbeard.INDEXER_DEFAULT = INDEXER_TVDB
|
||||
|
||||
if indexer_timeout:
|
||||
sickbeard.INDEXER_TIMEOUT = config.to_int(indexer_timeout)
|
||||
|
@ -4650,9 +4755,18 @@ class ConfigProviders(Config):
|
|||
|
||||
|
||||
class ConfigNotifications(Config):
|
||||
|
||||
def index(self, *args, **kwargs):
|
||||
t = PageTemplate(headers=self.request.headers, file='config_notifications.tmpl')
|
||||
t.submenu = self.ConfigMenu
|
||||
t.root_dirs = []
|
||||
if sickbeard.ROOT_DIRS:
|
||||
root_pieces = sickbeard.ROOT_DIRS.split('|')
|
||||
root_default = helpers.tryInt(root_pieces[0], None)
|
||||
for i, location in enumerate(root_pieces[1:]):
|
||||
t.root_dirs.append({'root_def': root_default and i == root_default,
|
||||
'loc': location,
|
||||
'b64': base64.urlsafe_b64encode(location)})
|
||||
return t.respond()
|
||||
|
||||
def saveNotifications(self, use_xbmc=None, xbmc_always_on=None, xbmc_notify_onsnatch=None,
|
||||
|
@ -4684,7 +4798,7 @@ class ConfigNotifications(Config):
|
|||
use_trakt=None, trakt_pin=None,
|
||||
trakt_remove_watchlist=None, trakt_use_watchlist=None, trakt_method_add=None,
|
||||
trakt_start_paused=None, trakt_sync=None,
|
||||
trakt_default_indexer=None, trakt_remove_serieslist=None,
|
||||
trakt_default_indexer=None, trakt_remove_serieslist=None, trakt_collection=None, trakt_accounts=None,
|
||||
use_synologynotifier=None, synologynotifier_notify_onsnatch=None,
|
||||
synologynotifier_notify_ondownload=None, synologynotifier_notify_onsubtitledownload=None,
|
||||
use_pytivo=None, pytivo_notify_onsnatch=None, pytivo_notify_ondownload=None,
|
||||
|
@ -4700,7 +4814,7 @@ class ConfigNotifications(Config):
|
|||
use_email=None, email_notify_onsnatch=None, email_notify_ondownload=None,
|
||||
email_notify_onsubtitledownload=None, email_host=None, email_port=25, email_from=None,
|
||||
email_tls=None, email_user=None, email_password=None, email_list=None, email_show_list=None,
|
||||
email_show=None):
|
||||
email_show=None, **kwargs):
|
||||
|
||||
results = []
|
||||
|
||||
|
@ -4810,7 +4924,8 @@ class ConfigNotifications(Config):
|
|||
synologynotifier_notify_onsubtitledownload)
|
||||
|
||||
sickbeard.USE_TRAKT = config.checkbox_to_value(use_trakt)
|
||||
sickbeard.traktCheckerScheduler.silent = not sickbeard.USE_TRAKT
|
||||
# sickbeard.traktCheckerScheduler.silent = not sickbeard.USE_TRAKT
|
||||
sickbeard.TRAKT_UPDATE_COLLECTION = build_config(**kwargs)
|
||||
# sickbeard.TRAKT_DEFAULT_INDEXER = int(trakt_default_indexer)
|
||||
# sickbeard.TRAKT_SYNC = config.checkbox_to_value(trakt_sync)
|
||||
# sickbeard.TRAKT_USE_WATCHLIST = config.checkbox_to_value(trakt_use_watchlist)
|
||||
|
|
Loading…
Reference in a new issue