diff --git a/gui/slick/css/trakt.css b/gui/slick/css/trakt.css new file mode 100644 index 00000000..f2de517f --- /dev/null +++ b/gui/slick/css/trakt.css @@ -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; +} diff --git a/gui/slick/interfaces/default/home_addShows.tmpl b/gui/slick/interfaces/default/home_addShows.tmpl index 4d28ec81..953ccf96 100644 --- a/gui/slick/interfaces/default/home_addShows.tmpl +++ b/gui/slick/interfaces/default/home_addShows.tmpl @@ -26,15 +26,23 @@

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.

- +

+ + +
Add Trending Shows
+
+

Add Trending Show

+

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.

+
+


#if $sickbeard.TRAKT_USE_RECOMMENDED:
Add Recommended Shows
-

Add Recommended Shows

-

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 ***

+

Add Recommended Show

+

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 ***



diff --git a/gui/slick/interfaces/default/home_trendingShows.tmpl b/gui/slick/interfaces/default/home_trendingShows.tmpl new file mode 100644 index 00000000..7bbdeb19 --- /dev/null +++ b/gui/slick/interfaces/default/home_trendingShows.tmpl @@ -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") + + + + +#if $varExists('header') +

$header

+#else +

$title

+#end if + +
+ +#for $i, $cur_show in $enumerate($trending_shows): +
+ +#if not $i%4 +
+#end if + + + + +#end for +
+ + +
+

$cur_show["ratings"]["percentage"]%

+ $cur_show["ratings"]["votes"] votes + +
+ [add show] +
+
+
+
+ + + +#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_bottom.tmpl") \ No newline at end of file diff --git a/lib/trakt/__init__.py b/lib/trakt/__init__.py index f4cc7f48..c9565a3b 100644 --- a/lib/trakt/__init__.py +++ b/lib/trakt/__init__.py @@ -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: diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py index 8da92f76..0b774322 100644 --- a/sickbeard/__init__.py +++ b/sickbeard/__init__.py @@ -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): diff --git a/sickbeard/traktChecker.py b/sickbeard/traktChecker.py index 51d2b7ef..eec11e15 100644 --- a/sickbeard/traktChecker.py +++ b/sickbeard/traktChecker.py @@ -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)) diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index c55dbdc5..8f8d0ee2 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -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': '' + ek.ek(os.path.dirname, cur_path) + os.sep + '' + 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 " + showObj.name + ":
" 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) \ No newline at end of file