mirror of
https://github.com/SickGear/SickGear.git
synced 2024-12-01 00:43:37 +00:00
Added trending shows feature, this allows you to choose from a wide selection of popular/trending shows with rating stats and easily add the show into SR by simply clicking the plus sign.
This commit is contained in:
parent
9136df2ae5
commit
130daf7d0a
7 changed files with 232 additions and 32 deletions
92
gui/slick/css/trakt.css
Normal file
92
gui/slick/css/trakt.css
Normal file
|
@ -0,0 +1,92 @@
|
|||
.traktShowDiv {
|
||||
|
||||
clear: both;
|
||||
border-left: 1px solid #CCCCCC;
|
||||
border-right: 1px solid #CCCCCC;
|
||||
border-bottom: 1px solid #CCCCCC;
|
||||
margin: auto;
|
||||
padding: 0px;
|
||||
text-align: left;
|
||||
width: 750px;
|
||||
}
|
||||
|
||||
.traktShowDiv a, .traktShowDiv a:link, .traktShowDiv a:visited, .traktShowDiv a:hover {
|
||||
text-decoration: none;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.traktShowTitle a {
|
||||
color: #000000;
|
||||
float: left;
|
||||
padding-top: 3px;
|
||||
line-height: 1.2em;
|
||||
font-size: 1.1em;
|
||||
text-shadow: -1px -1px 0 #FFF);
|
||||
}
|
||||
|
||||
.traktShowTitleIcons {
|
||||
float: right;
|
||||
padding: 3px 5px;
|
||||
}
|
||||
|
||||
.traktShowDiv .title {
|
||||
font-weight: 900;
|
||||
color: #333;
|
||||
}
|
||||
.imgWrapper {
|
||||
background: url("../images/loading.gif") no-repeat scroll center center #FFFFFF;
|
||||
border: 3px solid #FFFFFF;
|
||||
box-shadow: 1px 1px 2px 0 #555555;
|
||||
float: left;
|
||||
height: 50px;
|
||||
overflow: hidden;
|
||||
text-indent: -3000px;
|
||||
width: 50px;
|
||||
}
|
||||
.imgWrapper .traktPosterThumb {
|
||||
float: left;
|
||||
min-height: 100%;
|
||||
min-width: 100%;
|
||||
width: 50px;
|
||||
height: auto;
|
||||
position: relative;
|
||||
border: none;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.traktPosterThumb {
|
||||
-ms-interpolation-mode: bicubic; /* make scaling look nicer for ie */
|
||||
vertical-align: top;
|
||||
height: auto;
|
||||
width: 160px;
|
||||
border-top: 1px solid #ccc;
|
||||
border-right: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.traktShowDiv th {
|
||||
color: #000;
|
||||
letter-spacing: 1px;
|
||||
text-align: left;
|
||||
background-color: #333333;
|
||||
}
|
||||
|
||||
.traktShowDiv th.nobg {
|
||||
background: #efefef;
|
||||
border-top: 1px solid #666;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.traktShowDiv td {
|
||||
border-top: 1px solid #d2ebe8;
|
||||
background: #fff;
|
||||
padding: 5px 10px 5px 10px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.traktShowDiv td.trakts_show {
|
||||
width: 100%;
|
||||
height: 90%;
|
||||
border-top: 1px solid #ccc;
|
||||
vertical-align: top;
|
||||
background: #F5FAFA;
|
||||
color: #000;
|
||||
}
|
|
@ -26,15 +26,23 @@
|
|||
<p>For shows that you haven't downloaded yet, this option finds a show on theTVDB.com and TVRage.com, creates a directory for its episodes, and adds it to SickRage.</p>
|
||||
</div>
|
||||
</a>
|
||||
<br/><br/>
|
||||
|
||||
<a href="$sbRoot/home/addShows/trendingShows/" id="btnNewShow" class="btn btn-large">
|
||||
<div class="button"><img src="$sbRoot/images/add-new32.png" height="32" width="32" alt="Add Trending Shows"/></div>
|
||||
<div class="buttontext">
|
||||
<h2>Add Trending Show</h2>
|
||||
<p>For shows that you haven't downloaded yet, this option lets you choose from a list of current trending shows with ratings to add, creates a directory for its episodes, and adds it to SickRage.</p>
|
||||
</div>
|
||||
</a>
|
||||
<br/><br/>
|
||||
|
||||
#if $sickbeard.TRAKT_USE_RECOMMENDED:
|
||||
<a href="$sbRoot/home/addShows/recommendedShows/" id="btnNewShow" class="btn btn-large">
|
||||
<div class="button"><img src="$sbRoot/images/add-new32.png" height="32" width="32" alt="Add Recommended Shows"/></div>
|
||||
<div class="buttontext">
|
||||
<h2>Add Recommended Shows</h2>
|
||||
<p>For shows that you haven't downloaded yet, this option recommends shows to add based on your Trakt.tv watch list, creates a directory for its episodes, and adds it to SickRage. *** Trakt.tv must be enabled ***</p>
|
||||
<h2>Add Recommended Show</h2>
|
||||
<p>For shows that you haven't downloaded yet, this option recommends shows to add based on your Trakt.tv show library, creates a directory for its episodes, and adds it to SickRage. *** Trakt.tv must be enabled ***</p>
|
||||
</div>
|
||||
</a>
|
||||
<br/><br/>
|
||||
|
|
58
gui/slick/interfaces/default/home_trendingShows.tmpl
Normal file
58
gui/slick/interfaces/default/home_trendingShows.tmpl
Normal file
|
@ -0,0 +1,58 @@
|
|||
#import sickbeard
|
||||
#import datetime
|
||||
#import re
|
||||
#from sickbeard.common import *
|
||||
#from sickbeard import sbdatetime
|
||||
|
||||
#set global $title="Trending Shows"
|
||||
#set global $header="Trending Shows"
|
||||
|
||||
#set global $sbPath=".."
|
||||
|
||||
#set global $topmenu="comingEpisodes"
|
||||
#import os.path
|
||||
#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?$sbPID"></script>
|
||||
<link rel="stylesheet" type="text/css" href="$sbRoot/css/trakt.css?$sbPID" />
|
||||
|
||||
#if $varExists('header')
|
||||
<h1 class="header">$header</h1>
|
||||
#else
|
||||
<h1 class="title">$title</h1>
|
||||
#end if
|
||||
|
||||
<div class="traktShowDiv">
|
||||
<table width="100%" cellspacing="1" border="0" cellpadding="0">
|
||||
#for $i, $cur_show in $enumerate($trending_shows):
|
||||
<div id="listing_${cur_show["tvdb_id"]}">
|
||||
|
||||
#if not $i%4
|
||||
<tr>
|
||||
#end if
|
||||
|
||||
<th style="background-color: #efefef;" valign="top">
|
||||
<td class="trakt_show">
|
||||
<a href="${cur_show["url"]}"><img alt="" class="traktPosterThumb" src="${cur_show["images"]["poster"]}" /></a>
|
||||
<div class="clearfix">
|
||||
<h2>$cur_show["ratings"]["percentage"]% <img src="$sbRoot/images/like.png"></h2>
|
||||
<i>$cur_show["ratings"]["votes"] votes</i>
|
||||
|
||||
<div class="traktShowTitleIcons">
|
||||
<a href="$sbRoot/home/addTraktShow?indexer_id=${cur_show["tvdb_id"]}&showName=${cur_show["title"]}"><img alt="[add show]" height="16" width="16" src="$sbRoot/images/plus.png"></a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</th>
|
||||
</div>
|
||||
#end for
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
<!--
|
||||
window.setInterval( "location.reload(true)", 600000); // Refresh every 10 minutes
|
||||
//-->
|
||||
</script>
|
||||
|
||||
#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_bottom.tmpl")
|
|
@ -7,7 +7,7 @@ try:
|
|||
except ImportError:
|
||||
from lib import simplejson as json
|
||||
|
||||
def TraktCall(method, api, username, password, data = {}):
|
||||
def TraktCall(method, api, username=None, password=None, data={}):
|
||||
"""
|
||||
A generic method for communicating with trakt. Uses the method and data provided along
|
||||
with the auth info to send the command.
|
||||
|
@ -26,19 +26,16 @@ def TraktCall(method, api, username, password, data = {}):
|
|||
return None
|
||||
|
||||
# if the username isn't given then it failed
|
||||
if not username:
|
||||
return None
|
||||
|
||||
password = sha1(password).hexdigest()
|
||||
if username and password:
|
||||
password = sha1(password).hexdigest()
|
||||
data["username"] = username
|
||||
data["password"] = password
|
||||
|
||||
# replace the API string with what we found
|
||||
method = method.replace("%API%", api)
|
||||
|
||||
data["username"] = username
|
||||
data["password"] = password
|
||||
|
||||
# take the URL params and make a json object out of them
|
||||
encoded_data = json.dumps(data);
|
||||
encoded_data = json.dumps(data)
|
||||
|
||||
# request the URL from trakt and parse the result as json
|
||||
try:
|
||||
|
|
|
@ -432,7 +432,7 @@ IGNORE_WORDS = "german,french,core2hd,dutch,swedish,reenc,MrLss"
|
|||
CALENDAR_UNPROTECTED = False
|
||||
|
||||
TMDB_API_KEY = 'edc5f123313769de83a71e157758030b'
|
||||
|
||||
TRAKT_API_KEY = 'abd806c54516240c76e4ebc9c5ccf394'
|
||||
|
||||
__INITIALIZED__ = False
|
||||
def initialize(consoleLogging=True):
|
||||
|
|
|
@ -147,9 +147,9 @@ class TraktChecker():
|
|||
"""
|
||||
Adds a new show with the default settings
|
||||
"""
|
||||
showObj = helpers.findCertainShow(sickbeard.showList, int(indexerid))
|
||||
if showObj != None:
|
||||
if helpers.findCertainShow(sickbeard.showList, int(indexerid)):
|
||||
return
|
||||
|
||||
logger.log(u"Adding show " + str(indexerid))
|
||||
root_dirs = sickbeard.ROOT_DIRS.split('|')
|
||||
location = root_dirs[int(root_dirs[0]) + 1]
|
||||
|
@ -161,6 +161,7 @@ class TraktChecker():
|
|||
return
|
||||
else:
|
||||
helpers.chmodAsParent(showPath)
|
||||
|
||||
sickbeard.showQueueScheduler.action.addShow(1, int(indexerid), showPath, status,
|
||||
int(sickbeard.QUALITY_DEFAULT),
|
||||
int(sickbeard.FLATTEN_FOLDERS_DEFAULT))
|
||||
|
|
|
@ -48,7 +48,7 @@ from sickbeard import subtitles
|
|||
from sickbeard import network_timezones
|
||||
|
||||
from sickbeard.providers import newznab, rsstorrent
|
||||
from sickbeard.common import Quality, Overview, statusStrings, qualityPresetStrings, cpu_presets
|
||||
from sickbeard.common import Quality, Overview, statusStrings, qualityPresetStrings, cpu_presets, SKIPPED
|
||||
from sickbeard.common import SNATCHED, UNAIRED, IGNORED, ARCHIVED, WANTED, FAILED
|
||||
from sickbeard.common import SD, HD720p, HD1080p
|
||||
from sickbeard.exceptions import ex
|
||||
|
@ -82,6 +82,7 @@ from lib import adba
|
|||
from Cheetah.Template import Template
|
||||
from tornado.web import RequestHandler, HTTPError
|
||||
|
||||
|
||||
def authenticated(handler_class):
|
||||
def wrap_execute(handler_execute):
|
||||
def basicauth(handler, transforms, *args, **kwargs):
|
||||
|
@ -125,6 +126,7 @@ def authenticated(handler_class):
|
|||
handler_class._execute = wrap_execute(handler_class._execute)
|
||||
return handler_class
|
||||
|
||||
|
||||
class HTTPRedirect(Exception):
|
||||
"""Exception raised when the request should be redirected."""
|
||||
|
||||
|
@ -138,9 +140,11 @@ class HTTPRedirect(Exception):
|
|||
"""Use this exception as a request.handler (raise self)."""
|
||||
raise self
|
||||
|
||||
|
||||
def redirect(url, permanent=False, status=None):
|
||||
raise HTTPRedirect(url, permanent, status)
|
||||
|
||||
|
||||
@authenticated
|
||||
class MainHandler(RequestHandler):
|
||||
def http_error_401_handler(self):
|
||||
|
@ -216,7 +220,7 @@ class MainHandler(RequestHandler):
|
|||
def get(self, *args, **kwargs):
|
||||
try:
|
||||
self.finish(self._dispatch())
|
||||
except HTTPRedirect,inst:
|
||||
except HTTPRedirect, inst:
|
||||
self.redirect(inst.url, inst.permanent, inst.status)
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
|
@ -462,6 +466,7 @@ class MainHandler(RequestHandler):
|
|||
|
||||
browser = WebFileBrowser
|
||||
|
||||
|
||||
class PageTemplate(Template):
|
||||
def __init__(self, headers, *args, **KWs):
|
||||
KWs['file'] = os.path.join(sickbeard.PROG_DIR, "gui/" + sickbeard.GUI_NAME + "/interfaces/default/",
|
||||
|
@ -499,7 +504,7 @@ class PageTemplate(Template):
|
|||
{'title': 'Manage', 'key': 'manage'},
|
||||
{'title': 'Config', 'key': 'config'},
|
||||
{'title': logPageTitle, 'key': 'errorlogs'},
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
class IndexerWebUI(MainHandler):
|
||||
|
@ -518,6 +523,7 @@ class IndexerWebUI(MainHandler):
|
|||
def _munge(string):
|
||||
return unicode(string).encode('utf-8', 'xmlcharrefreplace')
|
||||
|
||||
|
||||
def _getEpisode(show, season=None, episode=None, absolute=None):
|
||||
if show is None:
|
||||
return "Invalid show parameters"
|
||||
|
@ -2643,7 +2649,7 @@ class NewHomeAddShows(MainHandler):
|
|||
'display_dir': '<b>' + ek.ek(os.path.dirname, cur_path) + os.sep + '</b>' + ek.ek(
|
||||
os.path.basename,
|
||||
cur_path),
|
||||
}
|
||||
}
|
||||
|
||||
# see if the folder is in XBMC already
|
||||
dirResults = myDB.select("SELECT * FROM tv_shows WHERE location = ?", [cur_path])
|
||||
|
@ -2745,10 +2751,10 @@ class NewHomeAddShows(MainHandler):
|
|||
logger.log(u"Could not connect to trakt service, aborting recommended list update", logger.ERROR)
|
||||
return
|
||||
|
||||
map(final_results.append, ([int(show['tvdb_id']), show['url'], show['title'], show['overview'],
|
||||
datetime.date.fromtimestamp(show['first_aired']).strftime('%Y%m%d')] for show in
|
||||
recommendedlist if
|
||||
not helpers.findCertainShow(sickbeard.showList, indexerid=int(show['tvdb_id']))))
|
||||
map(final_results.append,
|
||||
([int(show['tvdb_id']), show['url'], show['title'], show['overview'],
|
||||
datetime.date.fromtimestamp(int(show['first_aired']) / 1000.0).strftime('%Y%m%d')] for show in
|
||||
recommendedlist if not helpers.findCertainShow(sickbeard.showList, indexerid=int(show['tvdb_id']))))
|
||||
|
||||
return json.dumps({'results': final_results})
|
||||
|
||||
|
@ -2764,10 +2770,22 @@ class NewHomeAddShows(MainHandler):
|
|||
show_name = whichSeries.split('|')[2]
|
||||
|
||||
return self.addNewShow('|'.join([indexer_name, str(indexer), show_url, indexer_id, show_name, ""]),
|
||||
indexerLang, rootDir,
|
||||
defaultStatus,
|
||||
anyQualities, bestQualities, flatten_folders, subtitles, fullShowPath, other_shows,
|
||||
skipShow, providedIndexer, anime, scene)
|
||||
indexerLang, rootDir,
|
||||
defaultStatus,
|
||||
anyQualities, bestQualities, flatten_folders, subtitles, fullShowPath, other_shows,
|
||||
skipShow, providedIndexer, anime, scene)
|
||||
|
||||
def trendingShows(self, *args, **kwargs):
|
||||
"""
|
||||
Display the new show page which collects a tvdb id, folder, and extra options and
|
||||
posts them to addNewShow
|
||||
"""
|
||||
t = PageTemplate(headers=self.request.headers, file="home_trendingShows.tmpl")
|
||||
t.submenu = HomeMenu()
|
||||
|
||||
t.trending_shows = TraktCall("shows/trending.json/%API%/", sickbeard.TRAKT_API_KEY)
|
||||
|
||||
return _munge(t)
|
||||
|
||||
def existingShows(self, *args, **kwargs):
|
||||
"""
|
||||
|
@ -2778,6 +2796,33 @@ class NewHomeAddShows(MainHandler):
|
|||
|
||||
return _munge(t)
|
||||
|
||||
def addTraktShow(self, indexer_id, showName):
|
||||
if helpers.findCertainShow(sickbeard.showList, int(indexer_id)):
|
||||
return
|
||||
|
||||
root_dirs = sickbeard.ROOT_DIRS.split('|')
|
||||
location = root_dirs[int(root_dirs[0]) + 1]
|
||||
|
||||
show_dir = ek.ek(os.path.join, location, helpers.sanitizeFileName(showName))
|
||||
dir_exists = helpers.makeDir(show_dir)
|
||||
if not dir_exists:
|
||||
logger.log(u"Unable to create the folder " + show_dir + ", can't add the show", logger.ERROR)
|
||||
return
|
||||
else:
|
||||
helpers.chmodAsParent(show_dir)
|
||||
|
||||
sickbeard.showQueueScheduler.action.addShow(1, int(indexer_id), show_dir,
|
||||
default_status=sickbeard.STATUS_DEFAULT,
|
||||
quality=sickbeard.QUALITY_DEFAULT,
|
||||
flatten_folders=sickbeard.FLATTEN_FOLDERS_DEFAULT,
|
||||
subtitles=sickbeard.SUBTITLES_DEFAULT,
|
||||
anime=sickbeard.ANIME_DEFAULT,
|
||||
scene=sickbeard.SCENE_DEFAULT)
|
||||
|
||||
ui.notifications.message('Show added', 'Adding the specified show into ' + show_dir)
|
||||
|
||||
# done adding show
|
||||
redirect('/home/')
|
||||
|
||||
def addNewShow(self, whichSeries=None, indexerLang="en", rootDir=None, defaultStatus=None,
|
||||
anyQualities=None, bestQualities=None, flatten_folders=None, subtitles=None,
|
||||
|
@ -3394,7 +3439,7 @@ class Home(MainHandler):
|
|||
return _munge(t)
|
||||
else:
|
||||
return self._genericMessage("Update Failed",
|
||||
"Update wasn't successful, not restarting. Check your log for more information.")
|
||||
"Update wasn't successful, not restarting. Check your log for more information.")
|
||||
|
||||
|
||||
def displayShow(self, show=None):
|
||||
|
@ -3999,7 +4044,6 @@ class Home(MainHandler):
|
|||
myDB = db.DBConnection()
|
||||
myDB.mass_action(sql_l)
|
||||
|
||||
|
||||
if int(status) == WANTED:
|
||||
msg = "Backlog was automatically started for the following seasons of <b>" + showObj.name + "</b>:<br />"
|
||||
for season in segment:
|
||||
|
@ -4294,9 +4338,9 @@ class Home(MainHandler):
|
|||
|
||||
return json.dumps({'result': 'failure'})
|
||||
|
||||
|
||||
class UI(MainHandler):
|
||||
def add_message(self):
|
||||
|
||||
ui.notifications.message('Test 1', 'This is test number 1')
|
||||
ui.notifications.error('Test 2', 'This is test number 2')
|
||||
|
||||
|
@ -4307,8 +4351,8 @@ class UI(MainHandler):
|
|||
cur_notification_num = 1
|
||||
for cur_notification in ui.notifications.get_notifications(self.request.remote_ip):
|
||||
messages['notification-' + str(cur_notification_num)] = {'title': cur_notification.title,
|
||||
'message': cur_notification.message,
|
||||
'type': cur_notification.type}
|
||||
'message': cur_notification.message,
|
||||
'type': cur_notification.type}
|
||||
cur_notification_num += 1
|
||||
|
||||
return json.dumps(messages)
|
Loading…
Reference in a new issue