diff --git a/data/images/notifiers/betaseries.png b/data/images/notifiers/betaseries.png new file mode 100644 index 0000000000000000000000000000000000000000..2a803b6453cf5bfbda8b7f539333b2cb394fa7fa Binary files /dev/null and b/data/images/notifiers/betaseries.png differ diff --git a/data/interfaces/default/config_notifications.tmpl b/data/interfaces/default/config_notifications.tmpl index a4e4d25c9bc1eb39f5f7985666de26898b94f323..ca0de96f0176ca0f913f0d5407e19917e26b6e4e 100755 --- a/data/interfaces/default/config_notifications.tmpl +++ b/data/interfaces/default/config_notifications.tmpl @@ -1261,6 +1261,50 @@ </fieldset> </div><!-- /trakt component-group //--> + <div class="component-group clearfix"> + <div class="component-group-desc"> + <img class="notifier-icon" src="$sbRoot/images/notifiers/betaseries.png" alt="" title="BetaSeries" /> + <h3><a href="http://betaseries.com/" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">{ betaseries.com }</a></h3> + <p>French equivalent of trakt.tv.</p> + </div> + <fieldset class="component-group-list"> + <div class="field-pair"> + <input type="checkbox" class="enabler" name="use_betaseries" id="use_betaseries" #if $sickbeard.USE_BETASERIES then "checked=\"checked\"" else ""# /> + <label class="clearfix" for="use_betaseries"> + <span class="component-title">Enable</span> + <span class="component-desc">Should Sick Beard send BetaSeries notifications?</span> + </label> + </div> + + <div id="content_use_betaseries"> + <div class="field-pair"> + <label class="nocheck clearfix"> + <span class="component-title">BetaSeries Username</span> + <input type="text" name="betaseries_username" id="betaseries_username" value="$sickbeard.BETASERIES_USERNAME" size="35" /> + </label> + <label class="nocheck clearfix"> + <span class="component-title"> </span> + <span class="component-desc">Username of your BetaSeries account.</span> + </label> + </div> + <div class="field-pair"> + <label class="nocheck clearfix"> + <span class="component-title">BetaSeries Password</span> + <input type="password" name="betaseries_password" id="betaseries_password" value="$sickbeard.BETASERIES_PASSWORD" size="35" /> + </label> + <label class="nocheck clearfix"> + <span class="component-title"> </span> + <span class="component-desc">Password of your BetaSeries account.</span> + </label> + </div> + <div class="testNotification" id="testBetaSeries-result">Click below to test.</div> + <input type="button" class="btn" value="Test BetaSeries" id="testBetaSeries" /> + <input type="submit" class="btn config_submitter" value="Save Changes" /> + </div><!-- /content_use_betaseries //--> + + </fieldset> + </div><!-- /betaseries component-group //--> + <div class="component-group clearfix"> <div class="component-group-desc"> <img class="notifier-icon" src="$sbRoot/images/notifiers/mail.png" alt="" title="Mail" /> diff --git a/data/js/configNotifications.js b/data/js/configNotifications.js index 198a7ef4c98cecf1ae6abb146f31e1b80f7cd9cc..1e1f93c8f9141407bcfa79dfce9fa9558a7a4e24 100644 --- a/data/js/configNotifications.js +++ b/data/js/configNotifications.js @@ -298,6 +298,15 @@ $(document).ready(function () { // we have to call this function on dom ready to create the devices select get_pushbullet_devices(); + + $('#testBetaSeries').click(function () { + $('#testBetaSeries-result').html(loading); + var betaseries_username = $("#betaseries_username").val(); + var betaseries_password = $("#betaseries_password").val(); + + $.get(sbRoot + "/home/testBetaSeries", {'username': betaseries_username, 'password': betaseries_password}, + function (data) { $('#testBetaSeries-result').html(data); }); + }); $('#email_show').change(function () { var key = parseInt($('#email_show').val(), 10); diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py index ca77bffdbfc6fb1dc8bcde24d0db9b3091bf6d6e..1fc8340e59c614e02f4cf716b36201c8e62efb88 100644 --- a/sickbeard/__init__.py +++ b/sickbeard/__init__.py @@ -378,6 +378,13 @@ TRAKT_USE_WATCHLIST = False TRAKT_METHOD_ADD = 0 TRAKT_START_PAUSED = False +USE_BETASERIES = False +BETASERIES_USERNAME = None +BETASERIES_PASSWORD = None +# This key is registered for Sick Beard. +# For your application, please get one at http://www.betaseries.com/api/ +BETASERIES_API = '94318dc0bb30' + USE_PYTIVO = False PYTIVO_NOTIFY_ONSNATCH = False PYTIVO_NOTIFY_ONDOWNLOAD = False @@ -475,6 +482,7 @@ def initialize(consoleLogging=True): USE_XBMC, XBMC_NOTIFY_ONSNATCH, XBMC_NOTIFY_ONDOWNLOAD, XBMC_NOTIFY_ONSUBTITLEDOWNLOAD, XBMC_UPDATE_FULL, XBMC_UPDATE_ONLYFIRST, \ XBMC_UPDATE_LIBRARY, XBMC_HOST, XBMC_USERNAME, XBMC_PASSWORD, \ USE_TRAKT, TRAKT_USERNAME, TRAKT_PASSWORD, TRAKT_API,TRAKT_REMOVE_WATCHLIST,TRAKT_USE_WATCHLIST,TRAKT_METHOD_ADD,TRAKT_START_PAUSED,traktWatchListCheckerSchedular, \ + USE_BETASERIES, BETASERIES_USERNAME, BETASERIES_PASSWORD, BETASERIES_API, \ USE_PLEX, PLEX_NOTIFY_ONSNATCH, PLEX_NOTIFY_ONDOWNLOAD, PLEX_NOTIFY_ONSUBTITLEDOWNLOAD, PLEX_UPDATE_LIBRARY, \ PLEX_SERVER_HOST, PLEX_HOST, PLEX_USERNAME, PLEX_PASSWORD, \ showUpdateScheduler, __INITIALIZED__, LAUNCH_BROWSER, UPDATE_SHOWS_ON_START, SORT_ARTICLE, FRENCH_COLUMN, showList, loadingShowList, \ @@ -978,7 +986,13 @@ def initialize(consoleLogging=True): TRAKT_USE_WATCHLIST = bool(check_setting_int(CFG, 'Trakt', 'trakt_use_watchlist', 0)) TRAKT_METHOD_ADD = check_setting_str(CFG, 'Trakt', 'trakt_method_add', "0") TRAKT_START_PAUSED = bool(check_setting_int(CFG, 'Trakt', 'trakt_start_paused', 0)) - + + CheckSection(CFG, 'BetaSeries') + USE_BETASERIES = bool(check_setting_int(CFG, 'BetaSeries', 'use_betaseries', 0)) + BETASERIES_USERNAME = check_setting_str(CFG, 'BetaSeries', 'betaseries_username', '') + BETASERIES_PASSWORD = check_setting_str(CFG, 'BetaSeries', 'betaseries_password', '') + BETASERIES_API = check_setting_str(CFG, 'BetaSeries', 'betaseries_api', '') + CheckSection(CFG, 'pyTivo') USE_PYTIVO = bool(check_setting_int(CFG, 'pyTivo', 'use_pytivo', 0)) PYTIVO_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'pyTivo', 'pytivo_notify_onsnatch', 0)) @@ -1734,7 +1748,13 @@ def save_config(): new_config['Trakt']['trakt_use_watchlist'] = int(TRAKT_USE_WATCHLIST) new_config['Trakt']['trakt_method_add'] = TRAKT_METHOD_ADD new_config['Trakt']['trakt_start_paused'] = int(TRAKT_START_PAUSED) - + + new_config['BetaSeries'] = {} + new_config['BetaSeries']['use_betaseries'] = int(USE_BETASERIES) + new_config['BetaSeries']['betaseries_username'] = BETASERIES_USERNAME + new_config['BetaSeries']['betaseries_password'] = BETASERIES_PASSWORD + new_config['BetaSeries']['betaseries_api'] = BETASERIES_API + new_config['pyTivo'] = {} new_config['pyTivo']['use_pytivo'] = int(USE_PYTIVO) new_config['pyTivo']['pytivo_notify_onsnatch'] = int(PYTIVO_NOTIFY_ONSNATCH) diff --git a/sickbeard/notifiers/__init__.py b/sickbeard/notifiers/__init__.py index 8f9759f870fe6aaedf2080fbdb0409a99dd03da5..747918baad2bca563e3e1e0dc629dd88ed418772 100755 --- a/sickbeard/notifiers/__init__.py +++ b/sickbeard/notifiers/__init__.py @@ -39,6 +39,7 @@ import pushbullet import tweet import trakt +import betaseries from sickbeard.common import * @@ -63,6 +64,7 @@ pushbullet_notifier = pushbullet.PushbulletNotifier() # online twitter_notifier = tweet.TwitterNotifier() trakt_notifier = trakt.TraktNotifier() +betaseries_notifier = betaseries.BetaSeriesNotifier() mail_notifier = mail.MailNotifier() notifiers = [ @@ -84,6 +86,7 @@ notifiers = [ pushbullet_notifier, twitter_notifier, trakt_notifier, + betaseries_notifier, mail_notifier, ] diff --git a/sickbeard/notifiers/betaseries.py b/sickbeard/notifiers/betaseries.py new file mode 100644 index 0000000000000000000000000000000000000000..ec5e36562a1f2d8dee7df7e4993ff970882478f7 --- /dev/null +++ b/sickbeard/notifiers/betaseries.py @@ -0,0 +1,162 @@ +# Author: Gregoire Astruc <gregoire.astruc@gmail.com> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sick Beard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. +import urllib2, urllib + +from hashlib import md5 + +try: + import json +except ImportError: + from lib import simplejson as json + +import sickbeard + +from sickbeard import logger + +class BetaSeriesNotifier: + """ + A "notifier" for betaseries.com which keeps track of what has and hasn't been added to your library. + """ + + def notify_snatch(self, ep_name): + pass + + def notify_download(self, ep_name): + pass + + def update_library(self, ep_obj): + """ + Sends a request to betaseries indicating that the given episode is part of our library. + + ep_obj: The TVEpisode object to add to betaseries + """ + if not sickbeard.USE_BETASERIES: + return + + # TODO: Figure out if the show is present, and eventually add it. + + self._notifyBetaSeries('episodes/downloaded', {'thetvdb_id': ep_obj.tvdbid}) + + def test_notify(self, 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 + """ + return self._notifyBetaSeries("timeline/home", login=username, password=password) + + def _login(self): + return sickbeard.BETASERIES_USERNAME + + def _password(self): + return sickbeard.BETASERIES_PASSWORD + + def _api(self): + return sickbeard.BETASERIES_API + + def _use_me(self): + return sickbeard.USE_BETASERIES + + def _notifyBetaSeries(self, method, data = {}, login = None, password = None): + """ + A generic method for communicating with betaseries. Uses the method and data provided along + with the auth info to send the command. + + method: The URL to use at trakt, relative, no leading slash. + api: The API string to provide to trakt + login: The username to use when logging in + password: The unencrypted password to use when logging in + + Returns: A boolean representing success + """ + logger.log("betaseries_notifier: Call method {0}".format(method), logger.DEBUG) + + # if the login isn't given then use the config login + if not login: + login = self._login() + + # if the password isn't given then use the config password + if not password: + password = self._password() + + password = md5(password).hexdigest() + token = None + + try: + stream = urllib2.urlopen(self._requestBetaSeries('members/auth', {"login": login, "password": password})) + auth_resp = stream.read() + auth_resp = json.loads(auth_resp) + + token = auth_resp["token"] + except urllib2.URLError, e: + error = ''.join(e.readlines()) + logger.log("betaseries_notifier: Failed authenticating: {0} ({1})".format(e, error), logger.ERROR) + return False + except IOError, e: + logger.log("betaseries_notifier: Failed authenticating: {0}".format(e), logger.ERROR) + return False + + + # request the URL from betaseries and parse the result as json + try: + stream = urllib2.urlopen(self._requestBetaSeries(method, data, token)) + resp = stream.read() + + resp = json.loads(resp) + + if resp["errors"]: + raise Exception(resp["errors"]) + except urllib2.URLError, e: + error = ''.join(e.readlines()) + logger.log("betaseries_notifier: Failed method call on {0}: {1} ({2})".format(method, e, error), logger.ERROR) + + except IOError: + logger.log("betaseries_notifier: Failed calling method {0}".format(method), logger.ERROR) + return False + + if not resp["errors"]: + logger.log("betaseries_notifier: Succeeded calling {0}. Result: {1}".format(method, resp), logger.DEBUG) + return True + + #TODO: Destroy token. + + logger.log("betaseries_notifier: Failed calling method", logger.ERROR) + return False + + def _requestBetaSeries(self, method, data, token = None, version = "2.2"): + logger.log("betaseries_notifier: building request for {0} with payload {1}".format(method, data), logger.DEBUG) + request = urllib2.Request( + url="https://api.betaseries.com/{0}".format(method), + headers={ + "User-Agent": "Sickbeard/1.0", + "X-BetaSeries-Version": "2.2", + "X-BetaSeries-Key": self._api()}) + if data: + request.add_data(urllib.urlencode(data)) + + if token: + request.add_header("X-BetaSeries-Token", token) + + return request + +notifier = BetaSeriesNotifier diff --git a/sickbeard/postProcessor.py b/sickbeard/postProcessor.py index a1dccfa93951aee19da1c459724ed00c174fc9ee..7dd5d79e76bb70e6ca168a679fc209ba78646e01 100755 --- a/sickbeard/postProcessor.py +++ b/sickbeard/postProcessor.py @@ -1050,6 +1050,9 @@ class PostProcessor(object): # do the library update for Trakt notifiers.trakt_notifier.update_library(ep_obj) + # do the library update for BetaSeries + notifiers.betaseries_notifier.update_library(ep_obj) + self._run_extra_scripts(ep_obj) return True diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index 33a47629fc69fe840f22b2abbe13a528c5ac4707..8c5fffb4a9a0535ae07df3085a1c3ccd44bb7cd6 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -1535,6 +1535,7 @@ class ConfigNotifications: use_nmj=None, nmj_host=None, nmj_database=None, nmj_mount=None, use_synoindex=None, use_nmjv2=None, nmjv2_host=None, nmjv2_dbloc=None, nmjv2_database=None, use_trakt=None, trakt_username=None, trakt_password=None, trakt_api=None,trakt_remove_watchlist=None,trakt_use_watchlist=None,trakt_start_paused=None,trakt_method_add=None, + use_betaseries=None, betaseries_username=None, betaseries_password=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, pytivo_notify_onsubtitledownload=None, pytivo_update_library=None, pytivo_host=None, pytivo_share_name=None, pytivo_tivo_name=None, @@ -1761,6 +1762,11 @@ class ConfigNotifications: else: trakt_start_paused = 0 + if use_betaseries == "on": + use_betaseries = 1 + else: + use_betaseries = 0 + if use_pytivo == "on": use_pytivo = 1 else: @@ -1971,6 +1977,10 @@ class ConfigNotifications: sickbeard.TRAKT_METHOD_ADD = trakt_method_add sickbeard.TRAKT_START_PAUSED = trakt_start_paused + sickbeard.USE_BETASERIES = use_betaseries + sickbeard.BETASERIES_USERNAME = betaseries_username + sickbeard.BETASERIES_PASSWORD = betaseries_password + sickbeard.USE_PYTIVO = use_pytivo sickbeard.PYTIVO_NOTIFY_ONSNATCH = pytivo_notify_onsnatch == "off" sickbeard.PYTIVO_NOTIFY_ONDOWNLOAD = pytivo_notify_ondownload == "off" @@ -2856,6 +2866,16 @@ class Home: else: return "Test notice failed to Trakt" + @cherrypy.expose + def testBetaSeries(self, username=None, password=None): + cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store" + + result = notifiers.betaseries_notifier.test_notify(username, password) + if result: + return "Test notice sent successfully to BetaSeries" + else: + return "Test notice failed to BetaSeries" + @cherrypy.expose def testMail(self, mail_from=None, mail_to=None, mail_server=None, mail_ssl=None, mail_user=None, mail_password=None): cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"