diff --git a/gui/slick/interfaces/default/inc_top.tmpl b/gui/slick/interfaces/default/inc_top.tmpl
index 02d2c25f..582deea3 100644
--- a/gui/slick/interfaces/default/inc_top.tmpl
+++ b/gui/slick/interfaces/default/inc_top.tmpl
@@ -134,6 +134,9 @@
#if $sickbeard.USE_KODI and $sickbeard.KODI_HOST != ''
Update Kodi
#end if
+#if $sickbeard.USE_EMBY and $sickbeard.EMBY_HOST != '' and $sickbeard.EMBY_APIKEY != ''
+
Update Emby
+#end if
#if $sickbeard.USE_FAILED_DOWNLOADS
Failed Downloads
#end if
diff --git a/gui/slick/js/configNotifications.js b/gui/slick/js/configNotifications.js
index d616e3c3..38870882 100644
--- a/gui/slick/js/configNotifications.js
+++ b/gui/slick/js/configNotifications.js
@@ -123,6 +123,33 @@
});
});
+ $('#testEMBY').click(function () {
+ var emby_host = $('#emby_host').val();
+ var emby_apikey = $('#emby_apikey').val();
+ if (!emby_host || !emby_apikey) {
+ $('#testEMBY-result').html('Please fill out the necessary fields above.');
+ if (!emby_host) {
+ $('#emby_host').addClass('warning');
+ } else {
+ $('#emby_host').removeClass('warning');
+ }
+ if (!emby_apikey) {
+ $('#emby_apikey').addClass('warning');
+ } else {
+ $('#emby_apikey').removeClass('warning');
+ }
+ return;
+ }
+ $('#emby_host, #emby_apikey').removeClass('warning');
+ $(this).prop('disabled', true);
+ $('#testEMBY-result').html(loading);
+ $.get(sbRoot + '/home/testEMBY', {'host': emby_host, 'emby_apikey': emby_apikey})
+ .done(function (data) {
+ $('#testEMBY-result').html(data);
+ $('#testEMBY').prop('disabled', false);
+ });
+ });
+
$('#testBoxcar2').click(function () {
var boxcar2_accesstoken = $.trim($('#boxcar2_accesstoken').val());
var boxcar2_sound = $('#boxcar2_sound').val() || 'default';
diff --git a/gui/slick/js/inc_top.js b/gui/slick/js/inc_top.js
index 2af9b287..200eae99 100644
--- a/gui/slick/js/inc_top.js
+++ b/gui/slick/js/inc_top.js
@@ -31,6 +31,7 @@ function initActions() {
$('#SubMenu a[href$="/home/updateXBMC/"]').addClass('btn').html('
Update XBMC');
$('#SubMenu a:contains("Update show in Kodi")').addClass('btn').html('
Update show in Kodi');
$('#SubMenu a[href$="/home/updateKODI/"]').addClass('btn').html('
Update Kodi');
+ $('#SubMenu a[href$="/home/updateEMBY/"]').addClass('btn').html('
Update Emby');
}
$(document).ready(function(){
diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py
index cb67f6ef..183a7c21 100755
--- a/sickbeard/__init__.py
+++ b/sickbeard/__init__.py
@@ -287,6 +287,10 @@ PLEX_HOST = None
PLEX_USERNAME = None
PLEX_PASSWORD = None
+USE_EMBY = False
+EMBY_HOST = None
+EMBY_APIKEY = None
+
USE_GROWL = False
GROWL_NOTIFY_ONSNATCH = False
GROWL_NOTIFY_ONDOWNLOAD = False
@@ -492,8 +496,8 @@ def initialize(consoleLogging=True):
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_ACCOUNTS, TRAKT_MRU, 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, \
+ USE_PLEX, PLEX_NOTIFY_ONSNATCH, PLEX_NOTIFY_ONDOWNLOAD, PLEX_NOTIFY_ONSUBTITLEDOWNLOAD, PLEX_UPDATE_LIBRARY, PLEX_SERVER_HOST, PLEX_HOST, PLEX_USERNAME, PLEX_PASSWORD, \
+ USE_EMBY, EMBY_HOST, EMBY_APIKEY, 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, \
NEWZNAB_DATA, INDEXER_DEFAULT, INDEXER_TIMEOUT, USENET_RETENTION, TORRENT_DIR, \
QUALITY_DEFAULT, FLATTEN_FOLDERS_DEFAULT, SUBTITLES_DEFAULT, STATUS_DEFAULT, WANTED_BEGIN_DEFAULT, WANTED_LATEST_DEFAULT, RECENTSEARCH_STARTUP, \
@@ -538,6 +542,7 @@ def initialize(consoleLogging=True):
CheckSection(CFG, 'XBMC')
CheckSection(CFG, 'Kodi')
CheckSection(CFG, 'PLEX')
+ CheckSection(CFG, 'Emby')
CheckSection(CFG, 'Growl')
CheckSection(CFG, 'Prowl')
CheckSection(CFG, 'Twitter')
@@ -815,6 +820,10 @@ def initialize(consoleLogging=True):
PLEX_USERNAME = check_setting_str(CFG, 'Plex', 'plex_username', '')
PLEX_PASSWORD = check_setting_str(CFG, 'Plex', 'plex_password', '')
+ USE_EMBY = bool(check_setting_int(CFG, 'Emby', 'use_emby', 0))
+ EMBY_HOST = check_setting_str(CFG, 'Emby', 'emby_host', '')
+ EMBY_APIKEY = check_setting_str(CFG, 'Emby', 'emby_apikey', '')
+
USE_GROWL = bool(check_setting_int(CFG, 'Growl', 'use_growl', 0))
GROWL_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'Growl', 'growl_notify_onsnatch', 0))
GROWL_NOTIFY_ONDOWNLOAD = bool(check_setting_int(CFG, 'Growl', 'growl_notify_ondownload', 0))
@@ -1628,6 +1637,11 @@ def save_config():
new_config['Plex']['plex_username'] = PLEX_USERNAME
new_config['Plex']['plex_password'] = helpers.encrypt(PLEX_PASSWORD, ENCRYPTION_VERSION)
+ new_config['Emby'] = {}
+ new_config['Emby']['use_emby'] = int(USE_EMBY)
+ new_config['Emby']['emby_host'] = EMBY_HOST
+ new_config['Emby']['emby_apikey'] = EMBY_APIKEY
+
new_config['Growl'] = {}
new_config['Growl']['use_growl'] = int(USE_GROWL)
new_config['Growl']['growl_notify_onsnatch'] = int(GROWL_NOTIFY_ONSNATCH)
diff --git a/sickbeard/notifiers/__init__.py b/sickbeard/notifiers/__init__.py
index ee7dbf8c..653a4a7d 100644
--- a/sickbeard/notifiers/__init__.py
+++ b/sickbeard/notifiers/__init__.py
@@ -21,6 +21,7 @@ import sickbeard
import xbmc
import kodi
import plex
+import emby
import nmj
import nmjv2
import synoindex
@@ -47,6 +48,7 @@ from sickbeard.common import *
xbmc_notifier = xbmc.XBMCNotifier()
kodi_notifier = kodi.KODINotifier()
plex_notifier = plex.PLEXNotifier()
+emby_notifier = emby.EmbyNotifier()
nmj_notifier = nmj.NMJNotifier()
nmjv2_notifier = nmjv2.NMJv2Notifier()
synoindex_notifier = synoindex.synoIndexNotifier()
diff --git a/sickbeard/notifiers/emby.py b/sickbeard/notifiers/emby.py
new file mode 100644
index 00000000..c9ace10e
--- /dev/null
+++ b/sickbeard/notifiers/emby.py
@@ -0,0 +1,99 @@
+# Author: Nic Wolfe
+# URL: http://code.google.com/p/sickbeard/
+#
+# This file is part of SickGear.
+#
+# SickGear 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.
+#
+# SickGear 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 SickGear. If not, see .
+
+import urllib
+import urllib2
+
+import sickbeard
+from sickbeard import logger
+from sickbeard.exceptions import ex
+
+try:
+ import json
+except ImportError:
+ import simplejson as json
+
+class EmbyNotifier:
+
+ def _notify_emby(self, message, host=None, emby_apikey=None):
+ """Handles notifying Emby Server host via HTTP API
+
+ Returns: True if the request succeeded, False otherwise
+
+ """
+
+ # fill in omitted parameters
+ if not host:
+ host = sickbeard.EMBY_HOST
+ if not emby_apikey:
+ emby_apikey = sickbeard.EMBY_APIKEY
+
+ url = 'http://%s/emby/Notifications/Admin' % (host)
+ values = {'Name': 'SickGear', 'Description': message, 'ImageUrl': 'https://raw.githubusercontent.com/SickGear/SickGear/master/gui/slick/images/ico/apple-touch-icon-precomposed.png'}
+ data = json.dumps(values)
+
+ try:
+ req = urllib2.Request(url, data)
+ req.add_header('X-MediaBrowser-Token', emby_apikey)
+ req.add_header('Content-Type', 'application/json')
+
+ response = urllib2.urlopen(req)
+ response.close()
+
+ except (urllib2.URLError, IOError) as e:
+ logger.log(u'EMBY: Warning: Couldn\'t contact Emby Server at ' + url + ' ' + ex(e), logger.WARNING)
+ return False
+
+ logger.log(u'EMBY: Notification successful.', logger.MESSAGE)
+ return True
+
+ def test_notify(self, host, emby_apikey):
+ return self._notify_emby('This is a test notification from SickGear', host, emby_apikey)
+
+ def update_library(self):
+ """Handles updating the Emby Server host via HTTP API
+
+ Returns: True if the request succeeded, False otherwise
+
+ """
+
+ if sickbeard.USE_EMBY:
+
+ if not sickbeard.EMBY_HOST:
+ logger.log(u'EMBY: No host specified, check your settings', logger.DEBUG)
+ return False
+
+ url = 'http://%s/emby/Library/Series/Updated' % (sickbeard.EMBY_HOST)
+ values = {}
+ data = urllib.urlencode(values)
+
+ try:
+ req = urllib2.Request(url, data)
+ req.add_header('X-MediaBrowser-Token', sickbeard.EMBY_APIKEY)
+
+ response = urllib2.urlopen(req)
+ response.close()
+
+ except (urllib2.URLError, IOError) as e:
+ logger.log(u'EMBY: Warning: Couldn\'t contact Emby Server at ' + url + ' ' + ex(e), logger.WARNING)
+ return False
+
+ logger.log(u'EMBY: Updating library on host: %s' % sickbeard.EMBY_HOST, logger.MESSAGE)
+ return True
+
+notifier = EmbyNotifier
diff --git a/sickbeard/postProcessor.py b/sickbeard/postProcessor.py
index 06e21278..404b1a04 100644
--- a/sickbeard/postProcessor.py
+++ b/sickbeard/postProcessor.py
@@ -1044,6 +1044,9 @@ class PostProcessor(object):
# do the library update for Plex
notifiers.plex_notifier.update_library(ep_obj)
+ # do the library update for Emby
+ notifiers.emby_notifier.update_library()
+
# do the library update for NMJ
# nmj_notifier kicks off its library update when the notify_download is issued (inside notifiers)
diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py
index 6d7b6280..45ab8f93 100644
--- a/sickbeard/webserve.py
+++ b/sickbeard/webserve.py
@@ -535,6 +535,7 @@ class Home(MainHandler):
{'title': 'Update XBMC', 'path': 'home/updateXBMC/', 'requires': self.haveXBMC},
{'title': 'Update Kodi', 'path': 'home/updateKODI/', 'requires': self.haveKODI},
{'title': 'Update Plex', 'path': 'home/updatePLEX/', 'requires': self.havePLEX},
+ {'title': 'Update Emby', 'path': 'home/updateEMBY/', 'requires': self.haveEMBY},
{'title': 'Restart', 'path': 'home/restart/?pid=' + str(sickbeard.PID), 'confirm': True},
{'title': 'Shutdown', 'path': 'home/shutdown/?pid=' + str(sickbeard.PID), 'confirm': True},
]
@@ -551,6 +552,10 @@ class Home(MainHandler):
def havePLEX():
return sickbeard.USE_PLEX and sickbeard.PLEX_UPDATE_LIBRARY
+ @staticmethod
+ def haveEMBY():
+ return sickbeard.USE_EMBY
+
@staticmethod
def _getEpisode(show, season=None, episode=None, absolute=None):
if show is None:
@@ -844,6 +849,19 @@ class Home(MainHandler):
return finalResult
+ def testEMBY(self, host=None, emby_apikey=None):
+ self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
+
+ if None is not emby_apikey and starify(emby_apikey, True):
+ emby_apikey = sickbeard.EMBY_APIKEY
+
+ host = config.clean_host(host)
+ result = notifiers.emby_notifier.test_notify(urllib.unquote_plus(host), emby_apikey)
+ if result:
+ return 'Test Emby notice sent successfully to ' + urllib.unquote_plus(host)
+ else:
+ return 'Test Emby notice failed to ' + urllib.unquote_plus(host)
+
def testLibnotify(self, *args, **kwargs):
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
@@ -1660,6 +1678,15 @@ class Home(MainHandler):
ui.notifications.error('Unable to contact', 'Plex Media Server host(s): ' + result.replace(',', ', '))
self.redirect('/home/')
+ def updateEMBY(self):
+
+ if notifiers.emby_notifier.update_library():
+ ui.notifications.message(
+ 'Library update command sent to Emby host: ' + sickbeard.EMBY_HOST)
+ else:
+ ui.notifications.error('Unable to contact Emby host: ' + sickbeard.EMBY_HOST)
+ self.redirect('/home/')
+
def setStatus(self, show=None, eps=None, status=None, direct=False):
if show is None or eps is None or status is None:
@@ -4880,6 +4907,7 @@ class ConfigNotifications(Config):
use_plex=None, plex_notify_onsnatch=None, plex_notify_ondownload=None,
plex_notify_onsubtitledownload=None, plex_update_library=None,
plex_server_host=None, plex_host=None, plex_username=None, plex_password=None,
+ use_emby=None, emby_host=None, emby_apikey=None,
use_growl=None, growl_notify_onsnatch=None, growl_notify_ondownload=None,
growl_notify_onsubtitledownload=None, growl_host=None, growl_password=None,
use_prowl=None, prowl_notify_onsnatch=None, prowl_notify_ondownload=None,
@@ -4955,6 +4983,12 @@ class ConfigNotifications(Config):
if set('*') != set(plex_password):
sickbeard.PLEX_PASSWORD = plex_password
+ sickbeard.USE_EMBY = config.checkbox_to_value(use_emby)
+ sickbeard.EMBY_HOST = config.clean_host(emby_host)
+ key = emby_apikey.strip()
+ if not starify(key, True):
+ sickbeard.EMBY_APIKEY = key
+
sickbeard.USE_GROWL = config.checkbox_to_value(use_growl)
sickbeard.GROWL_NOTIFY_ONSNATCH = config.checkbox_to_value(growl_notify_onsnatch)
sickbeard.GROWL_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(growl_notify_ondownload)