mirror of
https://github.com/moparisthebest/SickRage
synced 2024-12-12 19:12:26 -05: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
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>
|
<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>
|
</div>
|
||||||
</a>
|
</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/>
|
<br/><br/>
|
||||||
|
|
||||||
#if $sickbeard.TRAKT_USE_RECOMMENDED:
|
#if $sickbeard.TRAKT_USE_RECOMMENDED:
|
||||||
<a href="$sbRoot/home/addShows/recommendedShows/" id="btnNewShow" class="btn btn-large">
|
<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="button"><img src="$sbRoot/images/add-new32.png" height="32" width="32" alt="Add Recommended Shows"/></div>
|
||||||
<div class="buttontext">
|
<div class="buttontext">
|
||||||
<h2>Add Recommended Shows</h2>
|
<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 watch list, creates a directory for its episodes, and adds it to SickRage. *** Trakt.tv must be enabled ***</p>
|
<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>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<br/><br/>
|
<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:
|
except ImportError:
|
||||||
from lib import simplejson as json
|
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
|
A generic method for communicating with trakt. Uses the method and data provided along
|
||||||
with the auth info to send the command.
|
with the auth info to send the command.
|
||||||
@ -26,19 +26,16 @@ def TraktCall(method, api, username, password, data = {}):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
# if the username isn't given then it failed
|
# if the username isn't given then it failed
|
||||||
if not username:
|
if username and password:
|
||||||
return None
|
|
||||||
|
|
||||||
password = sha1(password).hexdigest()
|
password = sha1(password).hexdigest()
|
||||||
|
data["username"] = username
|
||||||
|
data["password"] = password
|
||||||
|
|
||||||
# replace the API string with what we found
|
# replace the API string with what we found
|
||||||
method = method.replace("%API%", api)
|
method = method.replace("%API%", api)
|
||||||
|
|
||||||
data["username"] = username
|
|
||||||
data["password"] = password
|
|
||||||
|
|
||||||
# take the URL params and make a json object out of them
|
# 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
|
# request the URL from trakt and parse the result as json
|
||||||
try:
|
try:
|
||||||
|
@ -432,7 +432,7 @@ IGNORE_WORDS = "german,french,core2hd,dutch,swedish,reenc,MrLss"
|
|||||||
CALENDAR_UNPROTECTED = False
|
CALENDAR_UNPROTECTED = False
|
||||||
|
|
||||||
TMDB_API_KEY = 'edc5f123313769de83a71e157758030b'
|
TMDB_API_KEY = 'edc5f123313769de83a71e157758030b'
|
||||||
|
TRAKT_API_KEY = 'abd806c54516240c76e4ebc9c5ccf394'
|
||||||
|
|
||||||
__INITIALIZED__ = False
|
__INITIALIZED__ = False
|
||||||
def initialize(consoleLogging=True):
|
def initialize(consoleLogging=True):
|
||||||
|
@ -147,9 +147,9 @@ class TraktChecker():
|
|||||||
"""
|
"""
|
||||||
Adds a new show with the default settings
|
Adds a new show with the default settings
|
||||||
"""
|
"""
|
||||||
showObj = helpers.findCertainShow(sickbeard.showList, int(indexerid))
|
if helpers.findCertainShow(sickbeard.showList, int(indexerid)):
|
||||||
if showObj != None:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.log(u"Adding show " + str(indexerid))
|
logger.log(u"Adding show " + str(indexerid))
|
||||||
root_dirs = sickbeard.ROOT_DIRS.split('|')
|
root_dirs = sickbeard.ROOT_DIRS.split('|')
|
||||||
location = root_dirs[int(root_dirs[0]) + 1]
|
location = root_dirs[int(root_dirs[0]) + 1]
|
||||||
@ -161,6 +161,7 @@ class TraktChecker():
|
|||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
helpers.chmodAsParent(showPath)
|
helpers.chmodAsParent(showPath)
|
||||||
|
|
||||||
sickbeard.showQueueScheduler.action.addShow(1, int(indexerid), showPath, status,
|
sickbeard.showQueueScheduler.action.addShow(1, int(indexerid), showPath, status,
|
||||||
int(sickbeard.QUALITY_DEFAULT),
|
int(sickbeard.QUALITY_DEFAULT),
|
||||||
int(sickbeard.FLATTEN_FOLDERS_DEFAULT))
|
int(sickbeard.FLATTEN_FOLDERS_DEFAULT))
|
||||||
|
@ -48,7 +48,7 @@ from sickbeard import subtitles
|
|||||||
from sickbeard import network_timezones
|
from sickbeard import network_timezones
|
||||||
|
|
||||||
from sickbeard.providers import newznab, rsstorrent
|
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 SNATCHED, UNAIRED, IGNORED, ARCHIVED, WANTED, FAILED
|
||||||
from sickbeard.common import SD, HD720p, HD1080p
|
from sickbeard.common import SD, HD720p, HD1080p
|
||||||
from sickbeard.exceptions import ex
|
from sickbeard.exceptions import ex
|
||||||
@ -82,6 +82,7 @@ from lib import adba
|
|||||||
from Cheetah.Template import Template
|
from Cheetah.Template import Template
|
||||||
from tornado.web import RequestHandler, HTTPError
|
from tornado.web import RequestHandler, HTTPError
|
||||||
|
|
||||||
|
|
||||||
def authenticated(handler_class):
|
def authenticated(handler_class):
|
||||||
def wrap_execute(handler_execute):
|
def wrap_execute(handler_execute):
|
||||||
def basicauth(handler, transforms, *args, **kwargs):
|
def basicauth(handler, transforms, *args, **kwargs):
|
||||||
@ -125,6 +126,7 @@ def authenticated(handler_class):
|
|||||||
handler_class._execute = wrap_execute(handler_class._execute)
|
handler_class._execute = wrap_execute(handler_class._execute)
|
||||||
return handler_class
|
return handler_class
|
||||||
|
|
||||||
|
|
||||||
class HTTPRedirect(Exception):
|
class HTTPRedirect(Exception):
|
||||||
"""Exception raised when the request should be redirected."""
|
"""Exception raised when the request should be redirected."""
|
||||||
|
|
||||||
@ -138,9 +140,11 @@ class HTTPRedirect(Exception):
|
|||||||
"""Use this exception as a request.handler (raise self)."""
|
"""Use this exception as a request.handler (raise self)."""
|
||||||
raise self
|
raise self
|
||||||
|
|
||||||
|
|
||||||
def redirect(url, permanent=False, status=None):
|
def redirect(url, permanent=False, status=None):
|
||||||
raise HTTPRedirect(url, permanent, status)
|
raise HTTPRedirect(url, permanent, status)
|
||||||
|
|
||||||
|
|
||||||
@authenticated
|
@authenticated
|
||||||
class MainHandler(RequestHandler):
|
class MainHandler(RequestHandler):
|
||||||
def http_error_401_handler(self):
|
def http_error_401_handler(self):
|
||||||
@ -216,7 +220,7 @@ class MainHandler(RequestHandler):
|
|||||||
def get(self, *args, **kwargs):
|
def get(self, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
self.finish(self._dispatch())
|
self.finish(self._dispatch())
|
||||||
except HTTPRedirect,inst:
|
except HTTPRedirect, inst:
|
||||||
self.redirect(inst.url, inst.permanent, inst.status)
|
self.redirect(inst.url, inst.permanent, inst.status)
|
||||||
|
|
||||||
def post(self, *args, **kwargs):
|
def post(self, *args, **kwargs):
|
||||||
@ -462,6 +466,7 @@ class MainHandler(RequestHandler):
|
|||||||
|
|
||||||
browser = WebFileBrowser
|
browser = WebFileBrowser
|
||||||
|
|
||||||
|
|
||||||
class PageTemplate(Template):
|
class PageTemplate(Template):
|
||||||
def __init__(self, headers, *args, **KWs):
|
def __init__(self, headers, *args, **KWs):
|
||||||
KWs['file'] = os.path.join(sickbeard.PROG_DIR, "gui/" + sickbeard.GUI_NAME + "/interfaces/default/",
|
KWs['file'] = os.path.join(sickbeard.PROG_DIR, "gui/" + sickbeard.GUI_NAME + "/interfaces/default/",
|
||||||
@ -518,6 +523,7 @@ class IndexerWebUI(MainHandler):
|
|||||||
def _munge(string):
|
def _munge(string):
|
||||||
return unicode(string).encode('utf-8', 'xmlcharrefreplace')
|
return unicode(string).encode('utf-8', 'xmlcharrefreplace')
|
||||||
|
|
||||||
|
|
||||||
def _getEpisode(show, season=None, episode=None, absolute=None):
|
def _getEpisode(show, season=None, episode=None, absolute=None):
|
||||||
if show is None:
|
if show is None:
|
||||||
return "Invalid show parameters"
|
return "Invalid show parameters"
|
||||||
@ -2745,10 +2751,10 @@ class NewHomeAddShows(MainHandler):
|
|||||||
logger.log(u"Could not connect to trakt service, aborting recommended list update", logger.ERROR)
|
logger.log(u"Could not connect to trakt service, aborting recommended list update", logger.ERROR)
|
||||||
return
|
return
|
||||||
|
|
||||||
map(final_results.append, ([int(show['tvdb_id']), show['url'], show['title'], show['overview'],
|
map(final_results.append,
|
||||||
datetime.date.fromtimestamp(show['first_aired']).strftime('%Y%m%d')] for show in
|
([int(show['tvdb_id']), show['url'], show['title'], show['overview'],
|
||||||
recommendedlist if
|
datetime.date.fromtimestamp(int(show['first_aired']) / 1000.0).strftime('%Y%m%d')] for show in
|
||||||
not helpers.findCertainShow(sickbeard.showList, indexerid=int(show['tvdb_id']))))
|
recommendedlist if not helpers.findCertainShow(sickbeard.showList, indexerid=int(show['tvdb_id']))))
|
||||||
|
|
||||||
return json.dumps({'results': final_results})
|
return json.dumps({'results': final_results})
|
||||||
|
|
||||||
@ -2769,6 +2775,18 @@ class NewHomeAddShows(MainHandler):
|
|||||||
anyQualities, bestQualities, flatten_folders, subtitles, fullShowPath, other_shows,
|
anyQualities, bestQualities, flatten_folders, subtitles, fullShowPath, other_shows,
|
||||||
skipShow, providedIndexer, anime, scene)
|
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):
|
def existingShows(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Prints out the page to add existing shows from a root dir
|
Prints out the page to add existing shows from a root dir
|
||||||
@ -2778,6 +2796,33 @@ class NewHomeAddShows(MainHandler):
|
|||||||
|
|
||||||
return _munge(t)
|
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,
|
def addNewShow(self, whichSeries=None, indexerLang="en", rootDir=None, defaultStatus=None,
|
||||||
anyQualities=None, bestQualities=None, flatten_folders=None, subtitles=None,
|
anyQualities=None, bestQualities=None, flatten_folders=None, subtitles=None,
|
||||||
@ -3999,7 +4044,6 @@ class Home(MainHandler):
|
|||||||
myDB = db.DBConnection()
|
myDB = db.DBConnection()
|
||||||
myDB.mass_action(sql_l)
|
myDB.mass_action(sql_l)
|
||||||
|
|
||||||
|
|
||||||
if int(status) == WANTED:
|
if int(status) == WANTED:
|
||||||
msg = "Backlog was automatically started for the following seasons of <b>" + showObj.name + "</b>:<br />"
|
msg = "Backlog was automatically started for the following seasons of <b>" + showObj.name + "</b>:<br />"
|
||||||
for season in segment:
|
for season in segment:
|
||||||
@ -4294,9 +4338,9 @@ class Home(MainHandler):
|
|||||||
|
|
||||||
return json.dumps({'result': 'failure'})
|
return json.dumps({'result': 'failure'})
|
||||||
|
|
||||||
|
|
||||||
class UI(MainHandler):
|
class UI(MainHandler):
|
||||||
def add_message(self):
|
def add_message(self):
|
||||||
|
|
||||||
ui.notifications.message('Test 1', 'This is test number 1')
|
ui.notifications.message('Test 1', 'This is test number 1')
|
||||||
ui.notifications.error('Test 2', 'This is test number 2')
|
ui.notifications.error('Test 2', 'This is test number 2')
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user