Change overhaul Notifications, add Notifier Factory and DRY refactoring.
Notifiers are now loaded into memory on demand. Add bubble links to Notifications config tabs. Add Discordapp notifier to Notifications config/Social. Add Gitter notifier to Notifications config/Social. Change order of notifiers in Notifications config tabs. Remove Pushalot notifier. Remove XBMC notifier. Refactor update_library, notify, test notify and test results functions. Change most IDs and vars consistent for HTML, CSS, JS, and Python - related to notifications, camelCase for JS, underscore separated lower_case for python, hyphen separated-lowercase for CSS. A couple of exceptions have been left untouched in this clean up. Change commented out some unused vars in preparation for later removal.
10
CHANGES.md
|
@ -110,13 +110,21 @@
|
|||
* Add warn icon indicator of abandoned IDs to "Manage" menu bar and "Manage/Show Processes" menu item
|
||||
* Add shows that have no replacement ID can be ignored at "Manage/Show Processes", the menu bar warn icon hides if all are ignored
|
||||
* Change FreeBSD initscript to use command_interpreter
|
||||
* Add Slack notifier
|
||||
* Add Slack notifier to Notifications config/Social
|
||||
* Change allow Cheetah template engine version 2 and newer
|
||||
* Change improve handling of relative download links from providers
|
||||
* Change enable TorrentBytes provider
|
||||
* Change after SG is updated, don't attempt to send a Plex client notifications if there is no client host set
|
||||
* Add file name to possible names in history lookup post processing
|
||||
* Add garbage name handling to name parser
|
||||
* Change overhaul Notifications, add Notifier Factory and DRY refactoring
|
||||
* Notifiers are now loaded into memory on demand
|
||||
* Add bubble links to Notifications config tabs
|
||||
* Add Discordapp notifier to Notifications config/Social
|
||||
* Add Gitter notifier to Notifications config/Social
|
||||
* Change order of notifiers in Notifications config tabs
|
||||
* Remove Pushalot notifier
|
||||
* Remove XBMC notifier
|
||||
|
||||
|
||||
[develop changelog]
|
||||
|
|
|
@ -644,7 +644,7 @@ config*.tmpl
|
|||
color:#ddd
|
||||
}
|
||||
|
||||
.testNotification{
|
||||
.test-notification{
|
||||
border:1px dotted #ccc
|
||||
}
|
||||
|
||||
|
@ -935,6 +935,7 @@ fieldset[disabled] .navbar-default .btn-link:focus{
|
|||
color:#ddd
|
||||
}
|
||||
|
||||
.component-group.typelist .bgcol,
|
||||
.dropdown-menu{
|
||||
background-color:#333;
|
||||
border:1px solid rgba(0, 0, 0, 0.15);
|
||||
|
|
|
@ -633,7 +633,7 @@ config*.tmpl
|
|||
color:#666
|
||||
}
|
||||
|
||||
.testNotification{
|
||||
.test-notification{
|
||||
border:1px dotted #ccc
|
||||
}
|
||||
|
||||
|
@ -907,6 +907,7 @@ fieldset[disabled] .navbar-default .btn-link:focus{
|
|||
background-color:#333
|
||||
}
|
||||
|
||||
.component-group.typelist .bgcol,
|
||||
.dropdown-menu{
|
||||
background-color:#f5f1e4;
|
||||
border:1px solid rgba(0, 0, 0, 0.15);
|
||||
|
|
|
@ -2839,7 +2839,7 @@ select .selected:before{
|
|||
line-height:normal
|
||||
}
|
||||
|
||||
.testNotification{
|
||||
.test-notification{
|
||||
padding:5px;
|
||||
margin-bottom:10px;
|
||||
line-height:20px;
|
||||
|
|
BIN
gui/slick/images/notifiers/discordapp.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.1 KiB |
BIN
gui/slick/images/notifiers/gitter.png
Normal file
After Width: | Height: | Size: 732 B |
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 632 B After Width: | Height: | Size: 570 B |
|
@ -410,7 +410,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="testNotification" id="test-nzb-result">Click below to test</div>
|
||||
<div class="test-notification" id="test-nzb-result">Click below to test</div>
|
||||
<input type="button" value="Test SABnzbd" id="test_sabnzbd" class="btn test-button sabnzbd">
|
||||
<input type="button" value="Test NZBget" id="test_nzbget" class="btn test-button nzbget">
|
||||
<input type="submit" class="btn config_submitter" value="Save Changes"><br />
|
||||
|
@ -573,7 +573,7 @@
|
|||
</label>
|
||||
</div>
|
||||
|
||||
<div class="testNotification" id="test-torrent-result">Click below to test</div>
|
||||
<div class="test-notification" id="test-torrent-result">Click below to test</div>
|
||||
<input type="button" value="Test Connection" id="test_torrent" class="btn test-button">
|
||||
<input type="submit" class="btn config_submitter" value="Save Changes"><br />
|
||||
</div>
|
||||
|
|
|
@ -160,17 +160,17 @@
|
|||
<li><a href="$sbRoot/manage/showProcesses/" tabindex="$tab#set $tab += 1#"><span class="snf item"><i class="sgicon-warning"></i><i class="sgicon-showqueue"></i></span>Show Processes</a></li>
|
||||
<li><a href="$sbRoot/manage/episodeStatuses/" tabindex="$tab#set $tab += 1#"><i class="sgicon-episodestatus"></i>Episode Status</a></li>
|
||||
#if hasattr($sickbeard, 'USE_EMBY') and $sg_var('USE_EMBY') and $sg_str('EMBY_HOST') != '' and $sg_str('EMBY_APIKEY') != ''
|
||||
<li><a href="$sbRoot/home/updateEMBY/" tabindex="$tab#set $tab += 1#"><i class="sgicon-emby"></i>Update Emby</a></li>
|
||||
<li><a href="$sbRoot/home/update_emby/" tabindex="$tab#set $tab += 1#"><i class="sgicon-emby"></i>Update Emby</a></li>
|
||||
#end if
|
||||
#if hasattr($sickbeard, 'USE_KODI') and $sg_var('USE_KODI') and $sg_str('KODI_HOST') != ''
|
||||
<li><a href="$sbRoot/home/updateKODI/" tabindex="$tab#set $tab += 1#"><i class="sgicon-kodi"></i>Update Kodi</a></li>
|
||||
#end if
|
||||
#if hasattr($sickbeard, 'USE_XBMC') and $sg_var('USE_XBMC') and $sg_str('XBMC_HOST') != ''
|
||||
<li><a href="$sbRoot/home/updateXBMC/" tabindex="$tab#set $tab += 1#"><i class="sgicon-xbmc"></i>Update XBMC</a></li>
|
||||
<li><a href="$sbRoot/home/update_kodi/" tabindex="$tab#set $tab += 1#"><i class="sgicon-kodi"></i>Update Kodi</a></li>
|
||||
#end if
|
||||
#if hasattr($sickbeard, 'USE_PLEX') and $sg_var('USE_PLEX') and $sg_str('PLEX_SERVER_HOST') != ''
|
||||
<li><a href="$sbRoot/home/updatePLEX/" tabindex="$tab#set $tab += 1#"><i class="sgicon-plex"></i>Update PLEX</a></li>
|
||||
<li><a href="$sbRoot/home/update_plex/" tabindex="$tab#set $tab += 1#"><i class="sgicon-plex"></i>Update PLEX</a></li>
|
||||
#end if
|
||||
## #if hasattr($sickbeard, 'USE_XBMC') and $sg_var('USE_XBMC') and $sg_str('XBMC_HOST') != ''
|
||||
## <li><a href="$sbRoot/home/update_xbmc/" tabindex="$tab#set $tab += 1#"><i class="sgicon-xbmc"></i>Update XBMC</a></li>
|
||||
## #end if
|
||||
#if hasattr($sickbeard, 'USE_FAILED_DOWNLOADS') and $sg_var('USE_FAILED_DOWNLOADS')
|
||||
<li><a href="$sbRoot/manage/failedDownloads/" tabindex="$tab#set $tab += 1#"><i class="sgicon-failed"></i>Failed Downloads</a></li>
|
||||
#end if
|
||||
|
|
|
@ -11,7 +11,7 @@ function initActions() {
|
|||
menu$.find('a[href$="/errorlogs/clearerrors/"]').addClass('btn').html('<i class="sgicon-delete"></i>Clear Errors');
|
||||
menu$.find('a:contains("Re-scan")').addClass('btn').html('<i class="sgicon-refresh"></i>Re-scan');
|
||||
menu$.find('a:contains("Backlog Overview")').addClass('btn').html('<i class="sgicon-backlog"></i>Backlog Overview');
|
||||
menu$.find('a[href$="/home/updatePLEX/"]').addClass('btn').html('<i class="sgicon-plex"></i>Update PLEX');
|
||||
menu$.find('a[href$="/home/update_plex/"]').addClass('btn').html('<i class="sgicon-plex"></i>Update PLEX');
|
||||
menu$.find('a:contains("Force")').addClass('btn').html('<i class="sgicon-fullupdate"></i>Force Full Update');
|
||||
menu$.find('a:contains("Rename")').addClass('btn').html('<i class="sgicon-rename"></i>Media Renamer');
|
||||
menu$.find('a[href$="/config/subtitles/"]').addClass('btn').html('<i class="sgicon-subtitles"></i>Subtitles');
|
||||
|
@ -29,12 +29,12 @@ function initActions() {
|
|||
menu$.find('a:contains("Show Processes")').addClass('btn').html('<i class="sgicon-showqueue"></i>Show Processes');
|
||||
menu$.find('a[href$="/manage/failedDownloads/"]').addClass('btn').html('<i class="sgicon-failed"></i>Failed Downloads');
|
||||
menu$.find('a:contains("Notification")').addClass('btn').html('<i class="sgicon-notification"></i>Notifications');
|
||||
menu$.find('a[href$="/home/updateEMBY/"]').addClass('btn').html('<i class="sgicon-emby"></i>Update Emby');
|
||||
menu$.find('a[href$="/home/updateKODI/"]').addClass('btn').html('<i class="sgicon-kodi"></i>Update Kodi');
|
||||
menu$.find('a[href$="/home/updateXBMC/"]').addClass('btn').html('<i class="sgicon-xbmc"></i>Update XBMC');
|
||||
menu$.find('a[href$="/home/update_emby/"]').addClass('btn').html('<i class="sgicon-emby"></i>Update Emby');
|
||||
menu$.find('a[href$="/home/update_kodi/"]').addClass('btn').html('<i class="sgicon-kodi"></i>Update Kodi');
|
||||
// menu$.find('a[href$="/home/update_xbmc/"]').addClass('btn').html('<i class="sgicon-xbmc"></i>Update XBMC');
|
||||
menu$.find('a:contains("Update show in Emby")').addClass('btn').html('<i class="sgicon-emby"></i>Update show in Emby');
|
||||
menu$.find('a:contains("Update show in Kodi")').addClass('btn').html('<i class="sgicon-kodi"></i>Update show in Kodi');
|
||||
menu$.find('a:contains("Update show in XBMC")').addClass('btn').html('<i class="sgicon-xbmc"></i>Update show in XBMC');
|
||||
// menu$.find('a:contains("Update show in XBMC")').addClass('btn').html('<i class="sgicon-xbmc"></i>Update show in XBMC');
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
|
|
|
@ -249,12 +249,6 @@ TV_DOWNLOAD_DIR = None
|
|||
UNPACK = False
|
||||
SKIP_REMOVED_FILES = False
|
||||
|
||||
SAB_USERNAME = None
|
||||
SAB_PASSWORD = None
|
||||
SAB_APIKEY = None
|
||||
SAB_CATEGORY = None
|
||||
SAB_HOST = ''
|
||||
|
||||
NZBGET_USERNAME = None
|
||||
NZBGET_PASSWORD = None
|
||||
NZBGET_CATEGORY = None
|
||||
|
@ -262,6 +256,12 @@ NZBGET_HOST = None
|
|||
NZBGET_USE_HTTPS = False
|
||||
NZBGET_PRIORITY = 100
|
||||
|
||||
SAB_USERNAME = None
|
||||
SAB_PASSWORD = None
|
||||
SAB_APIKEY = None
|
||||
SAB_CATEGORY = None
|
||||
SAB_HOST = ''
|
||||
|
||||
TORRENT_USERNAME = None
|
||||
TORRENT_PASSWORD = None
|
||||
TORRENT_HOST = ''
|
||||
|
@ -289,6 +289,16 @@ KODI_HOST = ''
|
|||
KODI_USERNAME = None
|
||||
KODI_PASSWORD = None
|
||||
|
||||
USE_PLEX = False
|
||||
PLEX_NOTIFY_ONSNATCH = False
|
||||
PLEX_NOTIFY_ONDOWNLOAD = False
|
||||
PLEX_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
||||
PLEX_UPDATE_LIBRARY = False
|
||||
PLEX_SERVER_HOST = None
|
||||
PLEX_HOST = None
|
||||
PLEX_USERNAME = None
|
||||
PLEX_PASSWORD = None
|
||||
|
||||
USE_XBMC = False
|
||||
XBMC_ALWAYS_ON = True
|
||||
XBMC_NOTIFY_ONSNATCH = False
|
||||
|
@ -301,15 +311,57 @@ XBMC_HOST = ''
|
|||
XBMC_USERNAME = None
|
||||
XBMC_PASSWORD = None
|
||||
|
||||
USE_PLEX = False
|
||||
PLEX_NOTIFY_ONSNATCH = False
|
||||
PLEX_NOTIFY_ONDOWNLOAD = False
|
||||
PLEX_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
||||
PLEX_UPDATE_LIBRARY = False
|
||||
PLEX_SERVER_HOST = None
|
||||
PLEX_HOST = None
|
||||
PLEX_USERNAME = None
|
||||
PLEX_PASSWORD = None
|
||||
USE_NMJ = False
|
||||
NMJ_HOST = None
|
||||
NMJ_DATABASE = None
|
||||
NMJ_MOUNT = None
|
||||
|
||||
USE_NMJv2 = False
|
||||
NMJv2_HOST = None
|
||||
NMJv2_DATABASE = None
|
||||
NMJv2_DBLOC = None
|
||||
|
||||
USE_SYNOINDEX = False
|
||||
SYNOINDEX_UPDATE_LIBRARY = True
|
||||
|
||||
USE_SYNOLOGYNOTIFIER = False
|
||||
SYNOLOGYNOTIFIER_NOTIFY_ONSNATCH = False
|
||||
SYNOLOGYNOTIFIER_NOTIFY_ONDOWNLOAD = False
|
||||
SYNOLOGYNOTIFIER_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
||||
|
||||
USE_PYTIVO = False
|
||||
PYTIVO_HOST = ''
|
||||
PYTIVO_SHARE_NAME = ''
|
||||
PYTIVO_TIVO_NAME = ''
|
||||
PYTIVO_UPDATE_LIBRARY = True
|
||||
# PYTIVO_NOTIFY_ONSNATCH = False
|
||||
# PYTIVO_NOTIFY_ONDOWNLOAD = False
|
||||
# PYTIVO_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
||||
# PYTIVO_UPDATE_LIBRARY = False
|
||||
|
||||
USE_BOXCAR2 = False
|
||||
BOXCAR2_NOTIFY_ONSNATCH = False
|
||||
BOXCAR2_NOTIFY_ONDOWNLOAD = False
|
||||
BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
||||
BOXCAR2_ACCESSTOKEN = None
|
||||
BOXCAR2_SOUND = None
|
||||
|
||||
USE_PUSHBULLET = False
|
||||
PUSHBULLET_NOTIFY_ONSNATCH = False
|
||||
PUSHBULLET_NOTIFY_ONDOWNLOAD = False
|
||||
PUSHBULLET_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
||||
PUSHBULLET_ACCESS_TOKEN = None
|
||||
PUSHBULLET_DEVICE_IDEN = None
|
||||
|
||||
USE_PUSHOVER = False
|
||||
PUSHOVER_NOTIFY_ONSNATCH = False
|
||||
PUSHOVER_NOTIFY_ONDOWNLOAD = False
|
||||
PUSHOVER_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
||||
PUSHOVER_USERKEY = None
|
||||
PUSHOVER_APIKEY = None
|
||||
PUSHOVER_PRIORITY = 0
|
||||
PUSHOVER_DEVICE = None
|
||||
PUSHOVER_SOUND = None
|
||||
|
||||
USE_GROWL = False
|
||||
GROWL_NOTIFY_ONSNATCH = False
|
||||
|
@ -325,59 +377,23 @@ PROWL_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
|||
PROWL_API = None
|
||||
PROWL_PRIORITY = 0
|
||||
|
||||
USE_TWITTER = False
|
||||
TWITTER_NOTIFY_ONSNATCH = False
|
||||
TWITTER_NOTIFY_ONDOWNLOAD = False
|
||||
TWITTER_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
||||
TWITTER_USERNAME = None
|
||||
TWITTER_PASSWORD = None
|
||||
TWITTER_PREFIX = None
|
||||
|
||||
USE_BOXCAR2 = False
|
||||
BOXCAR2_NOTIFY_ONSNATCH = False
|
||||
BOXCAR2_NOTIFY_ONDOWNLOAD = False
|
||||
BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
||||
BOXCAR2_ACCESSTOKEN = None
|
||||
BOXCAR2_SOUND = None
|
||||
|
||||
USE_PUSHOVER = False
|
||||
PUSHOVER_NOTIFY_ONSNATCH = False
|
||||
PUSHOVER_NOTIFY_ONDOWNLOAD = False
|
||||
PUSHOVER_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
||||
PUSHOVER_USERKEY = None
|
||||
PUSHOVER_APIKEY = None
|
||||
PUSHOVER_PRIORITY = 0
|
||||
PUSHOVER_DEVICE = None
|
||||
PUSHOVER_SOUND = None
|
||||
USE_NMA = False
|
||||
NMA_NOTIFY_ONSNATCH = False
|
||||
NMA_NOTIFY_ONDOWNLOAD = False
|
||||
NMA_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
||||
NMA_API = None
|
||||
NMA_PRIORITY = 0
|
||||
|
||||
USE_LIBNOTIFY = False
|
||||
LIBNOTIFY_NOTIFY_ONSNATCH = False
|
||||
LIBNOTIFY_NOTIFY_ONDOWNLOAD = False
|
||||
LIBNOTIFY_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
||||
|
||||
USE_NMJ = False
|
||||
NMJ_HOST = None
|
||||
NMJ_DATABASE = None
|
||||
NMJ_MOUNT = None
|
||||
|
||||
USE_ANIDB = False
|
||||
ANIDB_USERNAME = None
|
||||
ANIDB_PASSWORD = None
|
||||
ANIDB_USE_MYLIST = False
|
||||
ADBA_CONNECTION = None
|
||||
ANIME_TREAT_AS_HDTV = False
|
||||
|
||||
USE_SYNOINDEX = False
|
||||
|
||||
USE_NMJv2 = False
|
||||
NMJv2_HOST = None
|
||||
NMJv2_DATABASE = None
|
||||
NMJv2_DBLOC = None
|
||||
|
||||
USE_SYNOLOGYNOTIFIER = False
|
||||
SYNOLOGYNOTIFIER_NOTIFY_ONSNATCH = False
|
||||
SYNOLOGYNOTIFIER_NOTIFY_ONDOWNLOAD = False
|
||||
SYNOLOGYNOTIFIER_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
||||
USE_PUSHALOT = False
|
||||
PUSHALOT_NOTIFY_ONSNATCH = False
|
||||
PUSHALOT_NOTIFY_ONDOWNLOAD = False
|
||||
PUSHALOT_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
||||
PUSHALOT_AUTHORIZATIONTOKEN = None
|
||||
|
||||
USE_TRAKT = False
|
||||
TRAKT_REMOVE_WATCHLIST = False
|
||||
|
@ -389,45 +405,41 @@ TRAKT_SYNC = False
|
|||
TRAKT_DEFAULT_INDEXER = None
|
||||
TRAKT_UPDATE_COLLECTION = {}
|
||||
|
||||
USE_PYTIVO = False
|
||||
PYTIVO_NOTIFY_ONSNATCH = False
|
||||
PYTIVO_NOTIFY_ONDOWNLOAD = False
|
||||
PYTIVO_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
||||
PYTIVO_UPDATE_LIBRARY = False
|
||||
PYTIVO_HOST = ''
|
||||
PYTIVO_SHARE_NAME = ''
|
||||
PYTIVO_TIVO_NAME = ''
|
||||
|
||||
USE_NMA = False
|
||||
NMA_NOTIFY_ONSNATCH = False
|
||||
NMA_NOTIFY_ONDOWNLOAD = False
|
||||
NMA_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
||||
NMA_API = None
|
||||
NMA_PRIORITY = 0
|
||||
|
||||
USE_PUSHALOT = False
|
||||
PUSHALOT_NOTIFY_ONSNATCH = False
|
||||
PUSHALOT_NOTIFY_ONDOWNLOAD = False
|
||||
PUSHALOT_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
||||
PUSHALOT_AUTHORIZATIONTOKEN = None
|
||||
|
||||
USE_PUSHBULLET = False
|
||||
PUSHBULLET_NOTIFY_ONSNATCH = False
|
||||
PUSHBULLET_NOTIFY_ONDOWNLOAD = False
|
||||
PUSHBULLET_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
||||
PUSHBULLET_ACCESS_TOKEN = None
|
||||
PUSHBULLET_DEVICE_IDEN = None
|
||||
|
||||
USE_SLACK = False
|
||||
SLACK_NOTIFY_ONSNATCH = False
|
||||
SLACK_NOTIFY_ONDOWNLOAD = False
|
||||
SLACK_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
||||
SLACK_CHANNEL = None
|
||||
SLACK_AS_USER = False
|
||||
SLACK_AS_AUTHED = False
|
||||
SLACK_BOT_NAME = None
|
||||
SLACK_ICON_URL = None
|
||||
SLACK_ACCESS_TOKEN = None
|
||||
|
||||
USE_DISCORDAPP = False
|
||||
DISCORDAPP_NOTIFY_ONSNATCH = False
|
||||
DISCORDAPP_NOTIFY_ONDOWNLOAD = False
|
||||
DISCORDAPP_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
||||
DISCORDAPP_AS_AUTHED = False
|
||||
DISCORDAPP_USERNAME = None
|
||||
DISCORDAPP_ICON_URL = None
|
||||
DISCORDAPP_AS_TTS = None
|
||||
DISCORDAPP_ACCESS_TOKEN = None
|
||||
|
||||
USE_GITTER = False
|
||||
GITTER_NOTIFY_ONSNATCH = False
|
||||
GITTER_NOTIFY_ONDOWNLOAD = False
|
||||
GITTER_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
||||
GITTER_ROOM = None
|
||||
GITTER_ACCESS_TOKEN = None
|
||||
|
||||
USE_TWITTER = False
|
||||
TWITTER_NOTIFY_ONSNATCH = False
|
||||
TWITTER_NOTIFY_ONDOWNLOAD = False
|
||||
TWITTER_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
||||
TWITTER_USERNAME = None
|
||||
TWITTER_PASSWORD = None
|
||||
TWITTER_PREFIX = None
|
||||
|
||||
USE_EMAIL = False
|
||||
EMAIL_OLD_SUBJECTS = None
|
||||
EMAIL_NOTIFY_ONSNATCH = False
|
||||
|
@ -441,6 +453,13 @@ EMAIL_PASSWORD = None
|
|||
EMAIL_FROM = None
|
||||
EMAIL_LIST = None
|
||||
|
||||
USE_ANIDB = False
|
||||
ANIDB_USERNAME = None
|
||||
ANIDB_PASSWORD = None
|
||||
ANIDB_USE_MYLIST = False
|
||||
ADBA_CONNECTION = None
|
||||
ANIME_TREAT_AS_HDTV = False
|
||||
|
||||
GUI_NAME = None
|
||||
DEFAULT_HOME = None
|
||||
FANART_LIMIT = None
|
||||
|
@ -617,8 +636,9 @@ def initialize(console_logging=True):
|
|||
USE_SYNOINDEX, \
|
||||
USE_SYNOLOGYNOTIFIER, SYNOLOGYNOTIFIER_NOTIFY_ONSNATCH, \
|
||||
SYNOLOGYNOTIFIER_NOTIFY_ONDOWNLOAD, SYNOLOGYNOTIFIER_NOTIFY_ONSUBTITLEDOWNLOAD, \
|
||||
USE_PYTIVO, PYTIVO_HOST, PYTIVO_SHARE_NAME, PYTIVO_TIVO_NAME, \
|
||||
PYTIVO_NOTIFY_ONSNATCH, PYTIVO_NOTIFY_ONDOWNLOAD, PYTIVO_NOTIFY_ONSUBTITLEDOWNLOAD, PYTIVO_UPDATE_LIBRARY
|
||||
USE_PYTIVO, PYTIVO_HOST, PYTIVO_SHARE_NAME, PYTIVO_TIVO_NAME
|
||||
# , \
|
||||
# PYTIVO_NOTIFY_ONSNATCH, PYTIVO_NOTIFY_ONDOWNLOAD, PYTIVO_NOTIFY_ONSUBTITLEDOWNLOAD, PYTIVO_UPDATE_LIBRARY
|
||||
# Notification Settings/Devices
|
||||
global USE_GROWL, GROWL_NOTIFY_ONSNATCH, GROWL_NOTIFY_ONDOWNLOAD, GROWL_NOTIFY_ONSUBTITLEDOWNLOAD, \
|
||||
GROWL_HOST, GROWL_PASSWORD, \
|
||||
|
@ -642,7 +662,12 @@ def initialize(console_logging=True):
|
|||
TRAKT_USE_WATCHLIST, TRAKT_REMOVE_WATCHLIST, TRAKT_TIMEOUT, TRAKT_METHOD_ADD, TRAKT_START_PAUSED, \
|
||||
TRAKT_SYNC, TRAKT_DEFAULT_INDEXER, TRAKT_REMOVE_SERIESLIST, TRAKT_UPDATE_COLLECTION, \
|
||||
USE_SLACK, SLACK_NOTIFY_ONSNATCH, SLACK_NOTIFY_ONDOWNLOAD, SLACK_NOTIFY_ONSUBTITLEDOWNLOAD, \
|
||||
SLACK_CHANNEL, SLACK_AS_USER, SLACK_BOT_NAME, SLACK_ICON_URL, SLACK_ACCESS_TOKEN, \
|
||||
SLACK_CHANNEL, SLACK_AS_AUTHED, SLACK_BOT_NAME, SLACK_ICON_URL, SLACK_ACCESS_TOKEN, \
|
||||
USE_DISCORDAPP, DISCORDAPP_NOTIFY_ONSNATCH, DISCORDAPP_NOTIFY_ONDOWNLOAD, \
|
||||
DISCORDAPP_NOTIFY_ONSUBTITLEDOWNLOAD, \
|
||||
DISCORDAPP_AS_AUTHED, DISCORDAPP_USERNAME, DISCORDAPP_ICON_URL, DISCORDAPP_AS_TTS, DISCORDAPP_ACCESS_TOKEN,\
|
||||
USE_GITTER, GITTER_NOTIFY_ONSNATCH, GITTER_NOTIFY_ONDOWNLOAD, GITTER_NOTIFY_ONSUBTITLEDOWNLOAD,\
|
||||
GITTER_ROOM, GITTER_ACCESS_TOKEN, \
|
||||
USE_EMAIL, EMAIL_NOTIFY_ONSNATCH, EMAIL_NOTIFY_ONDOWNLOAD, EMAIL_NOTIFY_ONSUBTITLEDOWNLOAD, EMAIL_FROM, \
|
||||
EMAIL_HOST, EMAIL_PORT, EMAIL_TLS, EMAIL_USER, EMAIL_PASSWORD, EMAIL_LIST, EMAIL_OLD_SUBJECTS
|
||||
# Anime Settings
|
||||
|
@ -652,7 +677,8 @@ def initialize(console_logging=True):
|
|||
return False
|
||||
|
||||
for stanza in ('General', 'Blackhole', 'SABnzbd', 'NZBget', 'Emby', 'Kodi', 'XBMC', 'PLEX',
|
||||
'Growl', 'Prowl', 'Twitter', 'Slack', 'Boxcar2', 'NMJ', 'NMJv2', 'Synology', 'SynologyNotifier',
|
||||
'Growl', 'Prowl', 'Twitter', 'Slack', 'Discordapp', 'Boxcar2', 'NMJ', 'NMJv2',
|
||||
'Synology', 'SynologyNotifier',
|
||||
'pyTivo', 'NMA', 'Pushalot', 'Pushbullet', 'Subtitles'):
|
||||
CheckSection(CFG, stanza)
|
||||
|
||||
|
@ -1015,13 +1041,13 @@ def initialize(console_logging=True):
|
|||
|
||||
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))
|
||||
PYTIVO_NOTIFY_ONDOWNLOAD = bool(check_setting_int(CFG, 'pyTivo', 'pytivo_notify_ondownload', 0))
|
||||
PYTIVO_NOTIFY_ONSUBTITLEDOWNLOAD = bool(check_setting_int(CFG, 'pyTivo', 'pytivo_notify_onsubtitledownload', 0))
|
||||
PYTIVO_UPDATE_LIBRARY = bool(check_setting_int(CFG, 'pyTivo', 'pyTivo_update_library', 0))
|
||||
PYTIVO_HOST = check_setting_str(CFG, 'pyTivo', 'pytivo_host', '')
|
||||
PYTIVO_SHARE_NAME = check_setting_str(CFG, 'pyTivo', 'pytivo_share_name', '')
|
||||
PYTIVO_TIVO_NAME = check_setting_str(CFG, 'pyTivo', 'pytivo_tivo_name', '')
|
||||
# PYTIVO_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'pyTivo', 'pytivo_notify_onsnatch', 0))
|
||||
# PYTIVO_NOTIFY_ONDOWNLOAD = bool(check_setting_int(CFG, 'pyTivo', 'pytivo_notify_ondownload', 0))
|
||||
# PYTIVO_NOTIFY_ONSUBTITLEDOWNLOAD = bool(check_setting_int(CFG, 'pyTivo', 'pytivo_notify_onsubtitledownload', 0))
|
||||
# PYTIVO_UPDATE_LIBRARY = bool(check_setting_int(CFG, 'pyTivo', 'pyTivo_update_library', 0))
|
||||
|
||||
USE_NMA = bool(check_setting_int(CFG, 'NMA', 'use_nma', 0))
|
||||
NMA_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'NMA', 'nma_notify_onsnatch', 0))
|
||||
|
@ -1050,11 +1076,29 @@ def initialize(console_logging=True):
|
|||
SLACK_NOTIFY_ONDOWNLOAD = bool(check_setting_int(CFG, 'Slack', 'slack_notify_ondownload', 0))
|
||||
SLACK_NOTIFY_ONSUBTITLEDOWNLOAD = bool(check_setting_int(CFG, 'Slack', 'slack_notify_onsubtitledownload', 0))
|
||||
SLACK_CHANNEL = check_setting_str(CFG, 'Slack', 'slack_channel', '')
|
||||
SLACK_AS_USER = bool(check_setting_int(CFG, 'Slack', 'slack_as_user', 0))
|
||||
SLACK_AS_AUTHED = bool(check_setting_int(CFG, 'Slack', 'slack_as_authed', 0))
|
||||
SLACK_BOT_NAME = check_setting_str(CFG, 'Slack', 'slack_bot_name', '')
|
||||
SLACK_ICON_URL = check_setting_str(CFG, 'Slack', 'slack_icon_url', '')
|
||||
SLACK_ACCESS_TOKEN = check_setting_str(CFG, 'Slack', 'slack_access_token', '')
|
||||
|
||||
USE_DISCORDAPP = bool(check_setting_int(CFG, 'Discordapp', 'use_discordapp', 0))
|
||||
DISCORDAPP_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'Discordapp', 'discordapp_notify_onsnatch', 0))
|
||||
DISCORDAPP_NOTIFY_ONDOWNLOAD = bool(check_setting_int(CFG, 'Discordapp', 'discordapp_notify_ondownload', 0))
|
||||
DISCORDAPP_NOTIFY_ONSUBTITLEDOWNLOAD = bool(
|
||||
check_setting_int(CFG, 'Discordapp', 'discordapp_notify_onsubtitledownload', 0))
|
||||
DISCORDAPP_AS_AUTHED = bool(check_setting_int(CFG, 'Discordapp', 'discordapp_as_authed', 0))
|
||||
DISCORDAPP_USERNAME = check_setting_str(CFG, 'Discordapp', 'discordapp_username', '')
|
||||
DISCORDAPP_ICON_URL = check_setting_str(CFG, 'Discordapp', 'discordapp_icon_url', '')
|
||||
DISCORDAPP_AS_TTS = bool(check_setting_str(CFG, 'Discordapp', 'discordapp_as_tts', 0))
|
||||
DISCORDAPP_ACCESS_TOKEN = check_setting_str(CFG, 'Discordapp', 'discordapp_access_token', '')
|
||||
|
||||
USE_GITTER = bool(check_setting_int(CFG, 'Gitter', 'use_gitter', 0))
|
||||
GITTER_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'Gitter', 'gitter_notify_onsnatch', 0))
|
||||
GITTER_NOTIFY_ONDOWNLOAD = bool(check_setting_int(CFG, 'Gitter', 'gitter_notify_ondownload', 0))
|
||||
GITTER_NOTIFY_ONSUBTITLEDOWNLOAD = bool(check_setting_int(CFG, 'Gitter', 'gitter_notify_onsubtitledownload', 0))
|
||||
GITTER_ROOM = check_setting_str(CFG, 'Gitter', 'gitter_room', '')
|
||||
GITTER_ACCESS_TOKEN = check_setting_str(CFG, 'Gitter', 'gitter_access_token', '')
|
||||
|
||||
USE_EMAIL = bool(check_setting_int(CFG, 'Email', 'use_email', 0))
|
||||
EMAIL_OLD_SUBJECTS = bool(check_setting_int(CFG, 'Email', 'email_old_subjects',
|
||||
None is not EMAIL_HOST and any(EMAIL_HOST)))
|
||||
|
@ -1691,90 +1735,40 @@ def save_config():
|
|||
new_config['Kodi'] = {}
|
||||
new_config['Kodi']['use_kodi'] = int(USE_KODI)
|
||||
new_config['Kodi']['kodi_always_on'] = int(KODI_ALWAYS_ON)
|
||||
new_config['Kodi']['kodi_notify_onsnatch'] = int(KODI_NOTIFY_ONSNATCH)
|
||||
new_config['Kodi']['kodi_notify_ondownload'] = int(KODI_NOTIFY_ONDOWNLOAD)
|
||||
new_config['Kodi']['kodi_notify_onsubtitledownload'] = int(KODI_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['Kodi']['kodi_update_library'] = int(KODI_UPDATE_LIBRARY)
|
||||
new_config['Kodi']['kodi_update_full'] = int(KODI_UPDATE_FULL)
|
||||
new_config['Kodi']['kodi_update_onlyfirst'] = int(KODI_UPDATE_ONLYFIRST)
|
||||
new_config['Kodi']['kodi_host'] = KODI_HOST
|
||||
new_config['Kodi']['kodi_username'] = KODI_USERNAME
|
||||
new_config['Kodi']['kodi_password'] = helpers.encrypt(KODI_PASSWORD, ENCRYPTION_VERSION)
|
||||
new_config['Kodi']['kodi_notify_onsnatch'] = int(KODI_NOTIFY_ONSNATCH)
|
||||
new_config['Kodi']['kodi_notify_ondownload'] = int(KODI_NOTIFY_ONDOWNLOAD)
|
||||
new_config['Kodi']['kodi_notify_onsubtitledownload'] = int(KODI_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
|
||||
new_config['Plex'] = {}
|
||||
new_config['Plex']['use_plex'] = int(USE_PLEX)
|
||||
new_config['Plex']['plex_username'] = PLEX_USERNAME
|
||||
new_config['Plex']['plex_password'] = helpers.encrypt(PLEX_PASSWORD, ENCRYPTION_VERSION)
|
||||
new_config['Plex']['plex_update_library'] = int(PLEX_UPDATE_LIBRARY)
|
||||
new_config['Plex']['plex_server_host'] = PLEX_SERVER_HOST
|
||||
new_config['Plex']['plex_notify_onsnatch'] = int(PLEX_NOTIFY_ONSNATCH)
|
||||
new_config['Plex']['plex_notify_ondownload'] = int(PLEX_NOTIFY_ONDOWNLOAD)
|
||||
new_config['Plex']['plex_notify_onsubtitledownload'] = int(PLEX_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['Plex']['plex_host'] = PLEX_HOST
|
||||
|
||||
new_config['XBMC'] = {}
|
||||
new_config['XBMC']['use_xbmc'] = int(USE_XBMC)
|
||||
new_config['XBMC']['xbmc_always_on'] = int(XBMC_ALWAYS_ON)
|
||||
new_config['XBMC']['xbmc_notify_onsnatch'] = int(XBMC_NOTIFY_ONSNATCH)
|
||||
new_config['XBMC']['xbmc_notify_ondownload'] = int(XBMC_NOTIFY_ONDOWNLOAD)
|
||||
new_config['XBMC']['xbmc_notify_onsubtitledownload'] = int(XBMC_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['XBMC']['xbmc_update_library'] = int(XBMC_UPDATE_LIBRARY)
|
||||
new_config['XBMC']['xbmc_update_full'] = int(XBMC_UPDATE_FULL)
|
||||
new_config['XBMC']['xbmc_update_onlyfirst'] = int(XBMC_UPDATE_ONLYFIRST)
|
||||
new_config['XBMC']['xbmc_notify_onsnatch'] = int(XBMC_NOTIFY_ONSNATCH)
|
||||
new_config['XBMC']['xbmc_notify_ondownload'] = int(XBMC_NOTIFY_ONDOWNLOAD)
|
||||
new_config['XBMC']['xbmc_notify_onsubtitledownload'] = int(XBMC_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['XBMC']['xbmc_host'] = XBMC_HOST
|
||||
new_config['XBMC']['xbmc_username'] = XBMC_USERNAME
|
||||
new_config['XBMC']['xbmc_password'] = helpers.encrypt(XBMC_PASSWORD, ENCRYPTION_VERSION)
|
||||
|
||||
new_config['Plex'] = {}
|
||||
new_config['Plex']['use_plex'] = int(USE_PLEX)
|
||||
new_config['Plex']['plex_notify_onsnatch'] = int(PLEX_NOTIFY_ONSNATCH)
|
||||
new_config['Plex']['plex_notify_ondownload'] = int(PLEX_NOTIFY_ONDOWNLOAD)
|
||||
new_config['Plex']['plex_notify_onsubtitledownload'] = int(PLEX_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['Plex']['plex_update_library'] = int(PLEX_UPDATE_LIBRARY)
|
||||
new_config['Plex']['plex_server_host'] = PLEX_SERVER_HOST
|
||||
new_config['Plex']['plex_host'] = PLEX_HOST
|
||||
new_config['Plex']['plex_username'] = PLEX_USERNAME
|
||||
new_config['Plex']['plex_password'] = helpers.encrypt(PLEX_PASSWORD, ENCRYPTION_VERSION)
|
||||
|
||||
new_config['Growl'] = {}
|
||||
new_config['Growl']['use_growl'] = int(USE_GROWL)
|
||||
new_config['Growl']['growl_notify_onsnatch'] = int(GROWL_NOTIFY_ONSNATCH)
|
||||
new_config['Growl']['growl_notify_ondownload'] = int(GROWL_NOTIFY_ONDOWNLOAD)
|
||||
new_config['Growl']['growl_notify_onsubtitledownload'] = int(GROWL_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['Growl']['growl_host'] = GROWL_HOST
|
||||
new_config['Growl']['growl_password'] = helpers.encrypt(GROWL_PASSWORD, ENCRYPTION_VERSION)
|
||||
|
||||
new_config['Prowl'] = {}
|
||||
new_config['Prowl']['use_prowl'] = int(USE_PROWL)
|
||||
new_config['Prowl']['prowl_notify_onsnatch'] = int(PROWL_NOTIFY_ONSNATCH)
|
||||
new_config['Prowl']['prowl_notify_ondownload'] = int(PROWL_NOTIFY_ONDOWNLOAD)
|
||||
new_config['Prowl']['prowl_notify_onsubtitledownload'] = int(PROWL_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['Prowl']['prowl_api'] = PROWL_API
|
||||
new_config['Prowl']['prowl_priority'] = PROWL_PRIORITY
|
||||
|
||||
new_config['Twitter'] = {}
|
||||
new_config['Twitter']['use_twitter'] = int(USE_TWITTER)
|
||||
new_config['Twitter']['twitter_notify_onsnatch'] = int(TWITTER_NOTIFY_ONSNATCH)
|
||||
new_config['Twitter']['twitter_notify_ondownload'] = int(TWITTER_NOTIFY_ONDOWNLOAD)
|
||||
new_config['Twitter']['twitter_notify_onsubtitledownload'] = int(TWITTER_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['Twitter']['twitter_username'] = TWITTER_USERNAME
|
||||
new_config['Twitter']['twitter_password'] = helpers.encrypt(TWITTER_PASSWORD, ENCRYPTION_VERSION)
|
||||
new_config['Twitter']['twitter_prefix'] = TWITTER_PREFIX
|
||||
|
||||
new_config['Boxcar2'] = {}
|
||||
new_config['Boxcar2']['use_boxcar2'] = int(USE_BOXCAR2)
|
||||
new_config['Boxcar2']['boxcar2_notify_onsnatch'] = int(BOXCAR2_NOTIFY_ONSNATCH)
|
||||
new_config['Boxcar2']['boxcar2_notify_ondownload'] = int(BOXCAR2_NOTIFY_ONDOWNLOAD)
|
||||
new_config['Boxcar2']['boxcar2_notify_onsubtitledownload'] = int(BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['Boxcar2']['boxcar2_accesstoken'] = BOXCAR2_ACCESSTOKEN
|
||||
new_config['Boxcar2']['boxcar2_sound'] = BOXCAR2_SOUND
|
||||
|
||||
new_config['Pushover'] = {}
|
||||
new_config['Pushover']['use_pushover'] = int(USE_PUSHOVER)
|
||||
new_config['Pushover']['pushover_notify_onsnatch'] = int(PUSHOVER_NOTIFY_ONSNATCH)
|
||||
new_config['Pushover']['pushover_notify_ondownload'] = int(PUSHOVER_NOTIFY_ONDOWNLOAD)
|
||||
new_config['Pushover']['pushover_notify_onsubtitledownload'] = int(PUSHOVER_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['Pushover']['pushover_userkey'] = PUSHOVER_USERKEY
|
||||
new_config['Pushover']['pushover_apikey'] = PUSHOVER_APIKEY
|
||||
new_config['Pushover']['pushover_priority'] = int(PUSHOVER_PRIORITY)
|
||||
new_config['Pushover']['pushover_device'] = PUSHOVER_DEVICE
|
||||
new_config['Pushover']['pushover_sound'] = PUSHOVER_SOUND
|
||||
|
||||
new_config['Libnotify'] = {}
|
||||
new_config['Libnotify']['use_libnotify'] = int(USE_LIBNOTIFY)
|
||||
new_config['Libnotify']['libnotify_notify_onsnatch'] = int(LIBNOTIFY_NOTIFY_ONSNATCH)
|
||||
new_config['Libnotify']['libnotify_notify_ondownload'] = int(LIBNOTIFY_NOTIFY_ONDOWNLOAD)
|
||||
new_config['Libnotify']['libnotify_notify_onsubtitledownload'] = int(LIBNOTIFY_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
|
||||
new_config['NMJ'] = {}
|
||||
new_config['NMJ']['use_nmj'] = int(USE_NMJ)
|
||||
new_config['NMJ']['nmj_host'] = NMJ_HOST
|
||||
|
@ -1797,6 +1791,80 @@ def save_config():
|
|||
new_config['SynologyNotifier']['synologynotifier_notify_onsubtitledownload'] = int(
|
||||
SYNOLOGYNOTIFIER_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
|
||||
new_config['pyTivo'] = {}
|
||||
new_config['pyTivo']['use_pytivo'] = int(USE_PYTIVO)
|
||||
new_config['pyTivo']['pytivo_host'] = PYTIVO_HOST
|
||||
new_config['pyTivo']['pytivo_share_name'] = PYTIVO_SHARE_NAME
|
||||
new_config['pyTivo']['pytivo_tivo_name'] = PYTIVO_TIVO_NAME
|
||||
# new_config['pyTivo']['pytivo_notify_onsnatch'] = int(PYTIVO_NOTIFY_ONSNATCH)
|
||||
# new_config['pyTivo']['pytivo_notify_ondownload'] = int(PYTIVO_NOTIFY_ONDOWNLOAD)
|
||||
# new_config['pyTivo']['pytivo_notify_onsubtitledownload'] = int(PYTIVO_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
# new_config['pyTivo']['pyTivo_update_library'] = int(PYTIVO_UPDATE_LIBRARY)
|
||||
|
||||
new_config['Boxcar2'] = {}
|
||||
new_config['Boxcar2']['use_boxcar2'] = int(USE_BOXCAR2)
|
||||
new_config['Boxcar2']['boxcar2_notify_onsnatch'] = int(BOXCAR2_NOTIFY_ONSNATCH)
|
||||
new_config['Boxcar2']['boxcar2_notify_ondownload'] = int(BOXCAR2_NOTIFY_ONDOWNLOAD)
|
||||
new_config['Boxcar2']['boxcar2_notify_onsubtitledownload'] = int(BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['Boxcar2']['boxcar2_accesstoken'] = BOXCAR2_ACCESSTOKEN
|
||||
new_config['Boxcar2']['boxcar2_sound'] = BOXCAR2_SOUND
|
||||
|
||||
new_config['Pushbullet'] = {}
|
||||
new_config['Pushbullet']['use_pushbullet'] = int(USE_PUSHBULLET)
|
||||
new_config['Pushbullet']['pushbullet_notify_onsnatch'] = int(PUSHBULLET_NOTIFY_ONSNATCH)
|
||||
new_config['Pushbullet']['pushbullet_notify_ondownload'] = int(PUSHBULLET_NOTIFY_ONDOWNLOAD)
|
||||
new_config['Pushbullet']['pushbullet_notify_onsubtitledownload'] = int(PUSHBULLET_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['Pushbullet']['pushbullet_access_token'] = PUSHBULLET_ACCESS_TOKEN
|
||||
new_config['Pushbullet']['pushbullet_device_iden'] = PUSHBULLET_DEVICE_IDEN
|
||||
|
||||
new_config['Pushover'] = {}
|
||||
new_config['Pushover']['use_pushover'] = int(USE_PUSHOVER)
|
||||
new_config['Pushover']['pushover_notify_onsnatch'] = int(PUSHOVER_NOTIFY_ONSNATCH)
|
||||
new_config['Pushover']['pushover_notify_ondownload'] = int(PUSHOVER_NOTIFY_ONDOWNLOAD)
|
||||
new_config['Pushover']['pushover_notify_onsubtitledownload'] = int(PUSHOVER_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['Pushover']['pushover_userkey'] = PUSHOVER_USERKEY
|
||||
new_config['Pushover']['pushover_apikey'] = PUSHOVER_APIKEY
|
||||
new_config['Pushover']['pushover_priority'] = int(PUSHOVER_PRIORITY)
|
||||
new_config['Pushover']['pushover_device'] = PUSHOVER_DEVICE
|
||||
new_config['Pushover']['pushover_sound'] = PUSHOVER_SOUND
|
||||
|
||||
new_config['Growl'] = {}
|
||||
new_config['Growl']['use_growl'] = int(USE_GROWL)
|
||||
new_config['Growl']['growl_notify_onsnatch'] = int(GROWL_NOTIFY_ONSNATCH)
|
||||
new_config['Growl']['growl_notify_ondownload'] = int(GROWL_NOTIFY_ONDOWNLOAD)
|
||||
new_config['Growl']['growl_notify_onsubtitledownload'] = int(GROWL_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['Growl']['growl_host'] = GROWL_HOST
|
||||
new_config['Growl']['growl_password'] = helpers.encrypt(GROWL_PASSWORD, ENCRYPTION_VERSION)
|
||||
|
||||
new_config['Prowl'] = {}
|
||||
new_config['Prowl']['use_prowl'] = int(USE_PROWL)
|
||||
new_config['Prowl']['prowl_notify_onsnatch'] = int(PROWL_NOTIFY_ONSNATCH)
|
||||
new_config['Prowl']['prowl_notify_ondownload'] = int(PROWL_NOTIFY_ONDOWNLOAD)
|
||||
new_config['Prowl']['prowl_notify_onsubtitledownload'] = int(PROWL_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['Prowl']['prowl_api'] = PROWL_API
|
||||
new_config['Prowl']['prowl_priority'] = PROWL_PRIORITY
|
||||
|
||||
new_config['NMA'] = {}
|
||||
new_config['NMA']['use_nma'] = int(USE_NMA)
|
||||
new_config['NMA']['nma_notify_onsnatch'] = int(NMA_NOTIFY_ONSNATCH)
|
||||
new_config['NMA']['nma_notify_ondownload'] = int(NMA_NOTIFY_ONDOWNLOAD)
|
||||
new_config['NMA']['nma_notify_onsubtitledownload'] = int(NMA_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['NMA']['nma_api'] = NMA_API
|
||||
new_config['NMA']['nma_priority'] = NMA_PRIORITY
|
||||
|
||||
new_config['Libnotify'] = {}
|
||||
new_config['Libnotify']['use_libnotify'] = int(USE_LIBNOTIFY)
|
||||
new_config['Libnotify']['libnotify_notify_onsnatch'] = int(LIBNOTIFY_NOTIFY_ONSNATCH)
|
||||
new_config['Libnotify']['libnotify_notify_ondownload'] = int(LIBNOTIFY_NOTIFY_ONDOWNLOAD)
|
||||
new_config['Libnotify']['libnotify_notify_onsubtitledownload'] = int(LIBNOTIFY_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
|
||||
new_config['Pushalot'] = {}
|
||||
new_config['Pushalot']['use_pushalot'] = int(USE_PUSHALOT)
|
||||
new_config['Pushalot']['pushalot_notify_onsnatch'] = int(PUSHALOT_NOTIFY_ONSNATCH)
|
||||
new_config['Pushalot']['pushalot_notify_ondownload'] = int(PUSHALOT_NOTIFY_ONDOWNLOAD)
|
||||
new_config['Pushalot']['pushalot_notify_onsubtitledownload'] = int(PUSHALOT_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['Pushalot']['pushalot_authorizationtoken'] = PUSHALOT_AUTHORIZATIONTOKEN
|
||||
|
||||
new_config['Trakt'] = {}
|
||||
new_config['Trakt']['use_trakt'] = int(USE_TRAKT)
|
||||
new_config['Trakt']['trakt_remove_watchlist'] = int(TRAKT_REMOVE_WATCHLIST)
|
||||
|
@ -1810,50 +1878,45 @@ def save_config():
|
|||
new_config['Trakt']['trakt_accounts'] = TraktAPI.build_config_string(TRAKT_ACCOUNTS)
|
||||
new_config['Trakt']['trakt_mru'] = TRAKT_MRU
|
||||
|
||||
new_config['pyTivo'] = {}
|
||||
new_config['pyTivo']['use_pytivo'] = int(USE_PYTIVO)
|
||||
new_config['pyTivo']['pytivo_notify_onsnatch'] = int(PYTIVO_NOTIFY_ONSNATCH)
|
||||
new_config['pyTivo']['pytivo_notify_ondownload'] = int(PYTIVO_NOTIFY_ONDOWNLOAD)
|
||||
new_config['pyTivo']['pytivo_notify_onsubtitledownload'] = int(PYTIVO_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['pyTivo']['pyTivo_update_library'] = int(PYTIVO_UPDATE_LIBRARY)
|
||||
new_config['pyTivo']['pytivo_host'] = PYTIVO_HOST
|
||||
new_config['pyTivo']['pytivo_share_name'] = PYTIVO_SHARE_NAME
|
||||
new_config['pyTivo']['pytivo_tivo_name'] = PYTIVO_TIVO_NAME
|
||||
|
||||
new_config['NMA'] = {}
|
||||
new_config['NMA']['use_nma'] = int(USE_NMA)
|
||||
new_config['NMA']['nma_notify_onsnatch'] = int(NMA_NOTIFY_ONSNATCH)
|
||||
new_config['NMA']['nma_notify_ondownload'] = int(NMA_NOTIFY_ONDOWNLOAD)
|
||||
new_config['NMA']['nma_notify_onsubtitledownload'] = int(NMA_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['NMA']['nma_api'] = NMA_API
|
||||
new_config['NMA']['nma_priority'] = NMA_PRIORITY
|
||||
|
||||
new_config['Pushalot'] = {}
|
||||
new_config['Pushalot']['use_pushalot'] = int(USE_PUSHALOT)
|
||||
new_config['Pushalot']['pushalot_notify_onsnatch'] = int(PUSHALOT_NOTIFY_ONSNATCH)
|
||||
new_config['Pushalot']['pushalot_notify_ondownload'] = int(PUSHALOT_NOTIFY_ONDOWNLOAD)
|
||||
new_config['Pushalot']['pushalot_notify_onsubtitledownload'] = int(PUSHALOT_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['Pushalot']['pushalot_authorizationtoken'] = PUSHALOT_AUTHORIZATIONTOKEN
|
||||
|
||||
new_config['Pushbullet'] = {}
|
||||
new_config['Pushbullet']['use_pushbullet'] = int(USE_PUSHBULLET)
|
||||
new_config['Pushbullet']['pushbullet_notify_onsnatch'] = int(PUSHBULLET_NOTIFY_ONSNATCH)
|
||||
new_config['Pushbullet']['pushbullet_notify_ondownload'] = int(PUSHBULLET_NOTIFY_ONDOWNLOAD)
|
||||
new_config['Pushbullet']['pushbullet_notify_onsubtitledownload'] = int(PUSHBULLET_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['Pushbullet']['pushbullet_access_token'] = PUSHBULLET_ACCESS_TOKEN
|
||||
new_config['Pushbullet']['pushbullet_device_iden'] = PUSHBULLET_DEVICE_IDEN
|
||||
|
||||
new_config['Slack'] = {}
|
||||
new_config['Slack']['use_slack'] = int(USE_SLACK)
|
||||
new_config['Slack']['slack_notify_onsnatch'] = int(SLACK_NOTIFY_ONSNATCH)
|
||||
new_config['Slack']['slack_notify_ondownload'] = int(SLACK_NOTIFY_ONDOWNLOAD)
|
||||
new_config['Slack']['slack_notify_onsubtitledownload'] = int(SLACK_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['Slack']['slack_channel'] = SLACK_CHANNEL
|
||||
new_config['Slack']['slack_as_user'] = int(SLACK_AS_USER)
|
||||
new_config['Slack']['slack_as_authed'] = int(SLACK_AS_AUTHED)
|
||||
new_config['Slack']['slack_bot_name'] = SLACK_BOT_NAME
|
||||
new_config['Slack']['slack_icon_url'] = SLACK_ICON_URL
|
||||
new_config['Slack']['slack_access_token'] = SLACK_ACCESS_TOKEN
|
||||
|
||||
new_config['Discordapp'] = {}
|
||||
new_config['Discordapp']['use_discordapp'] = int(USE_DISCORDAPP)
|
||||
new_config['Discordapp']['discordapp_notify_onsnatch'] = int(DISCORDAPP_NOTIFY_ONSNATCH)
|
||||
new_config['Discordapp']['discordapp_notify_ondownload'] = int(DISCORDAPP_NOTIFY_ONDOWNLOAD)
|
||||
new_config['Discordapp']['discordapp_notify_onsubtitledownload'] = int(DISCORDAPP_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['Discordapp']['discordapp_as_authed'] = int(DISCORDAPP_AS_AUTHED)
|
||||
new_config['Discordapp']['discordapp_username'] = DISCORDAPP_USERNAME
|
||||
new_config['Discordapp']['discordapp_icon_url'] = DISCORDAPP_ICON_URL
|
||||
new_config['Discordapp']['discordapp_as_tts'] = int(DISCORDAPP_AS_TTS)
|
||||
new_config['Discordapp']['discordapp_access_token'] = DISCORDAPP_ACCESS_TOKEN
|
||||
|
||||
new_config['Gitter'] = {}
|
||||
new_config['Gitter']['use_gitter'] = int(USE_GITTER)
|
||||
new_config['Gitter']['gitter_notify_onsnatch'] = int(GITTER_NOTIFY_ONSNATCH)
|
||||
new_config['Gitter']['gitter_notify_ondownload'] = int(GITTER_NOTIFY_ONDOWNLOAD)
|
||||
new_config['Gitter']['gitter_notify_onsubtitledownload'] = int(GITTER_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['Gitter']['gitter_room'] = GITTER_ROOM
|
||||
new_config['Gitter']['gitter_access_token'] = GITTER_ACCESS_TOKEN
|
||||
|
||||
new_config['Twitter'] = {}
|
||||
new_config['Twitter']['use_twitter'] = int(USE_TWITTER)
|
||||
new_config['Twitter']['twitter_notify_onsnatch'] = int(TWITTER_NOTIFY_ONSNATCH)
|
||||
new_config['Twitter']['twitter_notify_ondownload'] = int(TWITTER_NOTIFY_ONDOWNLOAD)
|
||||
new_config['Twitter']['twitter_notify_onsubtitledownload'] = int(TWITTER_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['Twitter']['twitter_username'] = TWITTER_USERNAME
|
||||
new_config['Twitter']['twitter_password'] = helpers.encrypt(TWITTER_PASSWORD, ENCRYPTION_VERSION)
|
||||
new_config['Twitter']['twitter_prefix'] = TWITTER_PREFIX
|
||||
|
||||
new_config['Email'] = {}
|
||||
new_config['Email']['use_email'] = int(USE_EMAIL)
|
||||
new_config['Email']['email_old_subjects'] = int(EMAIL_OLD_SUBJECTS)
|
||||
|
|
|
@ -224,7 +224,7 @@ def makeDir(path):
|
|||
try:
|
||||
ek.ek(os.makedirs, path)
|
||||
# do the library update for synoindex
|
||||
notifiers.synoindex_notifier.addFolder(path)
|
||||
notifiers.NotifierFactory().get('SYNOINDEX').addFolder(path)
|
||||
except OSError:
|
||||
return False
|
||||
return True
|
||||
|
@ -394,7 +394,7 @@ def make_dirs(path):
|
|||
# use normpath to remove end separator, otherwise checks permissions against itself
|
||||
chmodAsParent(ek.ek(os.path.normpath, sofar))
|
||||
# do the library update for synoindex
|
||||
notifiers.synoindex_notifier.addFolder(sofar)
|
||||
notifiers.NotifierFactory().get('SYNOINDEX').addFolder(sofar)
|
||||
except (OSError, IOError) as e:
|
||||
logger.log(u'Failed creating %s : %s' % (sofar, ex(e)), logger.ERROR)
|
||||
return False
|
||||
|
@ -477,7 +477,7 @@ def delete_empty_folders(check_empty_dir, keep_dir=None):
|
|||
# need shutil.rmtree when ignore_items is really implemented
|
||||
ek.ek(os.rmdir, check_empty_dir)
|
||||
# do the library update for synoindex
|
||||
notifiers.synoindex_notifier.deleteFolder(check_empty_dir)
|
||||
notifiers.NotifierFactory().get('SYNOINDEX').deleteFolder(check_empty_dir)
|
||||
except OSError as e:
|
||||
logger.log(u"Unable to delete " + check_empty_dir + ": " + repr(e) + " / " + str(e), logger.WARNING)
|
||||
break
|
||||
|
|
|
@ -18,93 +18,141 @@
|
|||
|
||||
import emby
|
||||
import kodi
|
||||
import xbmc
|
||||
import plex
|
||||
import xbmc
|
||||
import nmj
|
||||
import nmjv2
|
||||
import synoindex
|
||||
import synologynotifier
|
||||
import pytivo
|
||||
import trakt
|
||||
|
||||
import boxcar2
|
||||
# import pushalot
|
||||
import pushbullet
|
||||
import pushover
|
||||
import growl
|
||||
import prowl
|
||||
from . import libnotify
|
||||
import pushover
|
||||
import boxcar2
|
||||
import nma
|
||||
import pushalot
|
||||
import pushbullet
|
||||
from . import libnotify
|
||||
|
||||
import slack
|
||||
import tweet
|
||||
from lib import libtrakt
|
||||
import trakt
|
||||
import slack
|
||||
import discordapp
|
||||
import gitter
|
||||
import tweet
|
||||
import emailnotify
|
||||
|
||||
# home theater / nas
|
||||
emby_notifier = emby.EmbyNotifier()
|
||||
kodi_notifier = kodi.KodiNotifier()
|
||||
xbmc_notifier = xbmc.XBMCNotifier()
|
||||
plex_notifier = plex.PLEXNotifier()
|
||||
nmj_notifier = nmj.NMJNotifier()
|
||||
nmjv2_notifier = nmjv2.NMJv2Notifier()
|
||||
synoindex_notifier = synoindex.synoIndexNotifier()
|
||||
synology_notifier = synologynotifier.synologyNotifier()
|
||||
pytivo_notifier = pytivo.pyTivoNotifier()
|
||||
# devices
|
||||
growl_notifier = growl.GrowlNotifier()
|
||||
prowl_notifier = prowl.ProwlNotifier()
|
||||
libnotify_notifier = libnotify.LibnotifyNotifier()
|
||||
pushover_notifier = pushover.PushoverNotifier()
|
||||
boxcar2_notifier = boxcar2.Boxcar2Notifier()
|
||||
nma_notifier = nma.NMA_Notifier()
|
||||
pushalot_notifier = pushalot.PushalotNotifier()
|
||||
pushbullet_notifier = pushbullet.PushbulletNotifier()
|
||||
# social
|
||||
twitter_notifier = tweet.TwitterNotifier()
|
||||
trakt_notifier = trakt.TraktNotifier()
|
||||
slack_notifier = slack.SlackNotifier()
|
||||
email_notifier = emailnotify.EmailNotifier()
|
||||
import sickbeard
|
||||
|
||||
notifiers = [
|
||||
libnotify_notifier, # Libnotify notifier goes first because it doesn't involve blocking on network activity.
|
||||
kodi_notifier,
|
||||
xbmc_notifier,
|
||||
plex_notifier,
|
||||
nmj_notifier,
|
||||
nmjv2_notifier,
|
||||
synoindex_notifier,
|
||||
synology_notifier,
|
||||
pytivo_notifier,
|
||||
growl_notifier,
|
||||
prowl_notifier,
|
||||
pushover_notifier,
|
||||
boxcar2_notifier,
|
||||
nma_notifier,
|
||||
pushalot_notifier,
|
||||
pushbullet_notifier,
|
||||
twitter_notifier,
|
||||
trakt_notifier,
|
||||
slack_notifier,
|
||||
email_notifier,
|
||||
]
|
||||
|
||||
class NotifierFactory(object):
|
||||
|
||||
def __init__(self):
|
||||
self.notifiers = dict(
|
||||
# home theater / nas
|
||||
EMBY=emby.EmbyNotifier,
|
||||
KODI=kodi.KodiNotifier,
|
||||
PLEX=plex.PLEXNotifier,
|
||||
# ### XBMC=xbmc.XBMCNotifier,
|
||||
NMJ=nmj.NMJNotifier,
|
||||
NMJV2=nmjv2.NMJv2Notifier,
|
||||
SYNOINDEX=synoindex.SynoIndexNotifier,
|
||||
SYNOLOGY=synologynotifier.SynologyNotifier,
|
||||
PYTIVO=pytivo.PyTivoNotifier,
|
||||
|
||||
# devices,
|
||||
BOXCAR2=boxcar2.Boxcar2Notifier,
|
||||
# PUSHALOT=pushalot.PushalotNotifier,
|
||||
PUSHBULLET=pushbullet.PushbulletNotifier,
|
||||
PUSHOVER=pushover.PushoverNotifier,
|
||||
GROWL=growl.GrowlNotifier,
|
||||
PROWL=prowl.ProwlNotifier,
|
||||
NMA=nma.NMANotifier,
|
||||
LIBNOTIFY=libnotify.LibnotifyNotifier,
|
||||
|
||||
# social
|
||||
TRAKT=trakt.TraktNotifier,
|
||||
SLACK=slack.SlackNotifier,
|
||||
DISCORDAPP=discordapp.DiscordappNotifier,
|
||||
GITTER=gitter.GitterNotifier,
|
||||
TWITTER=tweet.TwitterNotifier,
|
||||
EMAIL=emailnotify.EmailNotifier,
|
||||
)
|
||||
|
||||
@property
|
||||
def enabled(self):
|
||||
"""
|
||||
Generator to yield iterable IDs for enabled notifiers
|
||||
:return: ID String
|
||||
:rtype: String
|
||||
"""
|
||||
for n in filter(lambda v: v.is_enabled(), self.notifiers.values()):
|
||||
yield n.id()
|
||||
|
||||
@property
|
||||
def enabled_onsnatch(self):
|
||||
for n in filter(lambda v: v.is_enabled() and v.is_enabled_onsnatch(), self.notifiers.values()):
|
||||
yield n.id()
|
||||
|
||||
@property
|
||||
def enabled_ondownload(self):
|
||||
for n in filter(lambda v: v.is_enabled() and v.is_enabled_ondownload(), self.notifiers.values()):
|
||||
yield n.id()
|
||||
|
||||
@property
|
||||
def enabled_onsubtitledownload(self):
|
||||
for n in filter(lambda v: v.is_enabled() and v.is_enabled_onsubtitledownload(), self.notifiers.values()):
|
||||
yield n.id()
|
||||
|
||||
@property
|
||||
def enabled_library(self):
|
||||
for n in filter(lambda v: v.is_enabled() and v.is_enabled_library(), self.notifiers.values()):
|
||||
yield n.id()
|
||||
|
||||
def get(self, nid):
|
||||
"""
|
||||
Get a notifier instance
|
||||
:param nid: Notified ID
|
||||
:type nid: String
|
||||
:return: Notifier instance
|
||||
:rtype: Notifier
|
||||
"""
|
||||
return self.notifiers[nid]()
|
||||
|
||||
def get_enabled(self, kind=None):
|
||||
"""
|
||||
Generator to yield iterable notifier instance(s) that are either enabled or enabled for requested actions
|
||||
:param kind: Action
|
||||
:type kind: String
|
||||
:return: Notifier instance
|
||||
:rtype: Notifier
|
||||
"""
|
||||
for n in getattr(self, 'enabled' + ('' if None is kind else ('_' + kind))):
|
||||
yield self.get(n)
|
||||
|
||||
|
||||
def notify_snatch(ep_name):
|
||||
for n in NotifierFactory().get_enabled('onsnatch'):
|
||||
n.notify_snatch(ep_name)
|
||||
|
||||
|
||||
def notify_download(ep_name):
|
||||
for n in notifiers:
|
||||
for n in NotifierFactory().get_enabled('ondownload'):
|
||||
n.notify_download(ep_name)
|
||||
|
||||
|
||||
def notify_subtitle_download(ep_name, lang):
|
||||
for n in notifiers:
|
||||
for n in NotifierFactory().get_enabled('ondownloadsubtitles'):
|
||||
n.notify_subtitle_download(ep_name, lang)
|
||||
|
||||
|
||||
def notify_snatch(ep_name):
|
||||
for n in notifiers:
|
||||
n.notify_snatch(ep_name)
|
||||
|
||||
|
||||
def notify_git_update(new_version=''):
|
||||
for n in notifiers:
|
||||
n.notify_git_update(new_version)
|
||||
if sickbeard.NOTIFY_ON_UPDATE:
|
||||
for n in NotifierFactory().get_enabled():
|
||||
n.notify_git_update(new_version)
|
||||
|
||||
|
||||
def notify_update_library(ep_obj):
|
||||
for n in NotifierFactory().get_enabled('library'):
|
||||
n.update_library(show=ep_obj.show, show_name=ep_obj.show.name, ep_obj=ep_obj)
|
||||
|
|
|
@ -18,115 +18,79 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import time
|
||||
import urllib
|
||||
import urllib2
|
||||
import time
|
||||
|
||||
import sickbeard
|
||||
|
||||
from sickbeard import logger
|
||||
from sickbeard.common import notifyStrings, NOTIFY_SNATCH, NOTIFY_DOWNLOAD, NOTIFY_SUBTITLE_DOWNLOAD, NOTIFY_GIT_UPDATE, NOTIFY_GIT_UPDATE_TEXT
|
||||
from sickbeard.exceptions import ex
|
||||
|
||||
API_URL = 'https://new.boxcar.io/api/notifications'
|
||||
from sickbeard.notifiers.generic import Notifier
|
||||
|
||||
|
||||
class Boxcar2Notifier:
|
||||
def _sendBoxcar2(self, title, msg, accesstoken, sound):
|
||||
class Boxcar2Notifier(Notifier):
|
||||
|
||||
def __init__(self):
|
||||
super(Boxcar2Notifier, self).__init__()
|
||||
|
||||
self.sg_logo_file = 'apple-touch-icon-60x60.png'
|
||||
|
||||
def _notify(self, title, body, access_token=None, sound=None, **kwargs):
|
||||
"""
|
||||
Sends a boxcar2 notification to the address provided
|
||||
|
||||
msg: The message to send
|
||||
|
||||
title: The title of the message
|
||||
accesstoken: to send to this device
|
||||
body: The message to send
|
||||
access_token: To send to this device
|
||||
sound: Sound profile to use
|
||||
|
||||
returns: True if the message succeeded, False otherwise
|
||||
"""
|
||||
access_token = self._choose(access_token, sickbeard.BOXCAR2_ACCESSTOKEN)
|
||||
sound = self._choose(sound, sickbeard.BOXCAR2_SOUND)
|
||||
|
||||
# build up the URL and parameters
|
||||
# more info goes here - https://boxcar.uservoice.com/knowledgebase/articles/306788-how-to-send-your-boxcar-account-a-notification
|
||||
msg = msg.strip().encode('utf-8')
|
||||
# more info goes here -
|
||||
# https://boxcar.uservoice.com/knowledgebase/articles/306788-how-to-send-your-boxcar-account-a-notification
|
||||
body = body.strip().encode('utf-8')
|
||||
|
||||
data = urllib.urlencode({
|
||||
'user_credentials': accesstoken,
|
||||
'notification[title]': title + ' - ' + msg,
|
||||
'notification[long_message]': msg,
|
||||
'user_credentials': access_token,
|
||||
'notification[title]': '%s - %s' % (title, body),
|
||||
'notification[long_message]': body,
|
||||
'notification[sound]': sound,
|
||||
'notification[source_name]': 'SickGear',
|
||||
'notification[icon_url]': 'https://cdn.rawgit.com/SickGear/SickGear/master/gui/slick/images/ico/apple-touch-icon-60x60.png'
|
||||
'notification[icon_url]': self._sg_logo_url
|
||||
})
|
||||
|
||||
# send the request to boxcar2
|
||||
result = None
|
||||
try:
|
||||
req = urllib2.Request(API_URL)
|
||||
req = urllib2.Request('https://new.boxcar.io/api/notifications')
|
||||
handle = urllib2.urlopen(req, data)
|
||||
handle.close()
|
||||
|
||||
except urllib2.URLError as e:
|
||||
# if we get an error back that doesn't have an error code then who knows what's really happening
|
||||
if not hasattr(e, 'code'):
|
||||
logger.log(u'BOXCAR2: Notification failed.' + ex(e), logger.ERROR)
|
||||
self._log_error(u'Notification failed: %s' % ex(e))
|
||||
else:
|
||||
logger.log(u'BOXCAR2: Notification failed. Error code: ' + str(e.code), logger.ERROR)
|
||||
result = 'Notification failed. Error code: %s' % e.code
|
||||
self._log_error(result)
|
||||
|
||||
if e.code == 404:
|
||||
logger.log(u'BOXCAR2: Access token is wrong/not associated to a device.', logger.ERROR)
|
||||
elif e.code == 401:
|
||||
logger.log(u'BOXCAR2: Access token not recognized.', logger.ERROR)
|
||||
elif e.code == 400:
|
||||
logger.log(u'BOXCAR2: Wrong data sent to boxcar.', logger.ERROR)
|
||||
elif e.code == 503:
|
||||
logger.log(u'BOXCAR2: Boxcar server to busy to handle the request at this time.', logger.WARNING)
|
||||
return False
|
||||
if 503 == e.code:
|
||||
result = 'Server too busy to handle the request at this time'
|
||||
self._log_warning(result)
|
||||
else:
|
||||
if 404 == e.code:
|
||||
result = 'Access token is wrong/not associated to a device'
|
||||
self._log_error(result)
|
||||
elif 401 == e.code:
|
||||
result = 'Access token not recognized'
|
||||
self._log_error(result)
|
||||
elif 400 == e.code:
|
||||
result = 'Wrong data sent to Boxcar'
|
||||
self._log_error(result)
|
||||
|
||||
logger.log(u'BOXCAR2: Notification successful.', logger.MESSAGE)
|
||||
return True
|
||||
return self._choose((True, 'Failed to send notification: %s' % result)[bool(result)], not bool(result))
|
||||
|
||||
def _notifyBoxcar2(self, title, message, accesstoken=None, sound=None, force=False):
|
||||
"""
|
||||
Sends a boxcar2 notification based on the provided info or SG config
|
||||
|
||||
title: The title of the notification to send
|
||||
message: The message string to send
|
||||
accesstoken: to send to this device
|
||||
force: If True then the notification will be sent even if Boxcar is disabled in the config
|
||||
"""
|
||||
|
||||
# suppress notifications if the notifier is disabled but the notify options are checked
|
||||
if not sickbeard.USE_BOXCAR2 and not force:
|
||||
logger.log(u'BOXCAR2: Notifications are not enabled, skipping this notification', logger.DEBUG)
|
||||
return False
|
||||
|
||||
# fill in omitted parameters
|
||||
if not accesstoken:
|
||||
accesstoken = sickbeard.BOXCAR2_ACCESSTOKEN
|
||||
if not sound:
|
||||
sound = sickbeard.BOXCAR2_SOUND
|
||||
|
||||
logger.log(u'BOXCAR2: Sending notification for ' + message, logger.DEBUG)
|
||||
|
||||
self._sendBoxcar2(title, message, accesstoken, sound)
|
||||
return True
|
||||
|
||||
def test_notify(self, accesstoken, sound, force=True):
|
||||
return self._sendBoxcar2('Test', 'This is a test notification from SickGear', accesstoken, sound)
|
||||
|
||||
def notify_snatch(self, ep_name):
|
||||
if sickbeard.BOXCAR2_NOTIFY_ONSNATCH:
|
||||
self._notifyBoxcar2(notifyStrings[NOTIFY_SNATCH], ep_name)
|
||||
|
||||
def notify_download(self, ep_name):
|
||||
if sickbeard.BOXCAR2_NOTIFY_ONDOWNLOAD:
|
||||
self._notifyBoxcar2(notifyStrings[NOTIFY_DOWNLOAD], ep_name)
|
||||
|
||||
def notify_subtitle_download(self, ep_name, lang):
|
||||
if sickbeard.BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD:
|
||||
self._notifyBoxcar2(notifyStrings[NOTIFY_SUBTITLE_DOWNLOAD], ep_name + ': ' + lang)
|
||||
|
||||
def notify_git_update(self, new_version = '??'):
|
||||
if sickbeard.USE_BOXCAR2:
|
||||
update_text=notifyStrings[NOTIFY_GIT_UPDATE_TEXT]
|
||||
title=notifyStrings[NOTIFY_GIT_UPDATE]
|
||||
self._notifyBoxcar2(title, update_text + new_version)
|
||||
|
||||
notifier = Boxcar2Notifier
|
||||
|
|
47
sickbeard/notifiers/discordapp.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
# coding=utf-8
|
||||
#
|
||||
# This file is part of SickGear.
|
||||
#
|
||||
# Thanks to: mallen86, generica
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sickbeard
|
||||
from sickbeard.notifiers.generic import Notifier
|
||||
|
||||
|
||||
class DiscordappNotifier(Notifier):
|
||||
|
||||
def __init__(self):
|
||||
super(DiscordappNotifier, self).__init__()
|
||||
|
||||
def _notify(self, title, body, as_authed=None, username='', icon_url='', as_tts='', access_token='', **kwargs):
|
||||
params = [] if not bool(self._choose(not as_authed, sickbeard.DISCORDAPP_AS_AUTHED)) else \
|
||||
[('username', self._choose(username, sickbeard.DISCORDAPP_USERNAME) or 'SickGear'),
|
||||
('avatar_url', self._choose(icon_url, sickbeard.DISCORDAPP_ICON_URL) or self._sg_logo_url)]
|
||||
as_tts = self._choose(as_tts, bool(sickbeard.DISCORDAPP_AS_TTS))
|
||||
|
||||
resp = sickbeard.helpers.getURL(
|
||||
url=self._choose(access_token, sickbeard.DISCORDAPP_ACCESS_TOKEN),
|
||||
post_json=dict([('content', self._body_only(title, body)), ('tts', as_tts)] + params))
|
||||
|
||||
result = '' == resp or self._choose('bad webhook?', None)
|
||||
if True is not result:
|
||||
self._log_error('%s failed to send message: %s' % (self.name, result))
|
||||
|
||||
return self._choose(('Success, notification sent. (Note: %s clients display icon once in a sequence)'
|
||||
% self.name, 'Failed to send notification, %s' % result)[True is not result], result)
|
||||
|
||||
|
||||
notifier = DiscordappNotifier
|
|
@ -19,118 +19,57 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import re
|
||||
|
||||
import sickbeard
|
||||
import smtplib
|
||||
|
||||
from sickbeard import db
|
||||
from sickbeard import logger
|
||||
from sickbeard.common import notifyStrings, NOTIFY_SNATCH, NOTIFY_DOWNLOAD, NOTIFY_SUBTITLE_DOWNLOAD
|
||||
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from email.utils import formatdate
|
||||
import re
|
||||
import smtplib
|
||||
|
||||
import sickbeard
|
||||
from sickbeard import db
|
||||
from sickbeard.notifiers.generic import Notifier, notify_strings
|
||||
|
||||
|
||||
class EmailNotifier:
|
||||
class EmailNotifier(Notifier):
|
||||
|
||||
def __init__(self):
|
||||
super(EmailNotifier, self).__init__()
|
||||
|
||||
self.last_err = None
|
||||
|
||||
def test_notify(self, host, port, smtp_from, use_tls, user, pwd, to):
|
||||
def _sendmail(self, host, port, smtp_from, use_tls, user, pwd, to, msg, smtp_debug=False):
|
||||
|
||||
msg = MIMEText('Success. This is a SickGear test message. Typically sent on, %s' %
|
||||
notifyStrings[NOTIFY_DOWNLOAD])
|
||||
msg['Subject'] = 'SickGear: Test message'
|
||||
msg['From'] = smtp_from
|
||||
msg['To'] = to
|
||||
msg['Date'] = formatdate(localtime=True)
|
||||
return self._sendmail(host, port, smtp_from, use_tls, user, pwd, [to], msg, True)
|
||||
|
||||
def _send_email(self, title, ep_name, lang='', extra='', force=False):
|
||||
|
||||
if not sickbeard.USE_EMAIL and not force:
|
||||
return
|
||||
|
||||
show = ep_name.split(' - ')[0]
|
||||
to = self._get_recipients(show)
|
||||
if not any(to):
|
||||
logger.log(u'No email recipients to notify, skipping', logger.WARNING)
|
||||
return
|
||||
|
||||
logger.log(u'Email recipients to notify: %s' % to, logger.DEBUG)
|
||||
use_tls = 1 == sickbeard.helpers.tryInt(use_tls)
|
||||
login = any(user) and any(pwd)
|
||||
self._log_debug(u'Sendmail HOST: %s; PORT: %s; LOGIN: %s, TLS: %s, USER: %s, FROM: %s, TO: %s' % (
|
||||
host, port, login, use_tls, user, smtp_from, to))
|
||||
|
||||
try:
|
||||
msg = MIMEMultipart('alternative')
|
||||
msg.attach(MIMEText(
|
||||
'<body style="font-family:Helvetica, Arial, sans-serif;">' +
|
||||
'<h3>SickGear Notification - %s</h3>\n' % title +
|
||||
'<p>Show: <b>' + show.encode('ascii', 'xmlcharrefreplace') +
|
||||
'</b></p>\n<p>Episode: <b>' +
|
||||
unicode(re.search('.+ - (.+?-.+) -.+', ep_name).group(1)).encode('ascii', 'xmlcharrefreplace') +
|
||||
extra +
|
||||
'</b></p>\n\n' +
|
||||
'<footer style="margin-top:2.5em;padding:.7em 0;color:#777;border-top:#BBB solid 1px;">' +
|
||||
'Powered by SickGear.</footer></body>',
|
||||
'html'))
|
||||
except:
|
||||
try:
|
||||
msg = MIMEText(ep_name)
|
||||
except:
|
||||
msg = MIMEText('Episode %s' % title)
|
||||
srv = smtplib.SMTP(host, int(port))
|
||||
if smtp_debug:
|
||||
srv.set_debuglevel(1)
|
||||
|
||||
msg['Subject'] = '%s%s: %s' % (lang, title, ep_name)
|
||||
msg['From'] = sickbeard.EMAIL_FROM
|
||||
msg['To'] = ','.join(to)
|
||||
msg['Date'] = formatdate(localtime=True)
|
||||
if self._sendmail(sickbeard.EMAIL_HOST, sickbeard.EMAIL_PORT, sickbeard.EMAIL_FROM, sickbeard.EMAIL_TLS,
|
||||
sickbeard.EMAIL_USER, sickbeard.EMAIL_PASSWORD, to, msg):
|
||||
logger.log(u'%s notification sent to [%s] for "%s"' % (title, to, ep_name), logger.DEBUG)
|
||||
else:
|
||||
logger.log(u'%s notification ERROR: %s' % (title, self.last_err), logger.ERROR)
|
||||
if use_tls or login:
|
||||
srv.ehlo()
|
||||
self._log_debug(u'Sent initial EHLO command')
|
||||
|
||||
def notify_snatch(self, ep_name, title=notifyStrings[NOTIFY_SNATCH]):
|
||||
"""
|
||||
Send a notification that an episode was snatched
|
||||
if use_tls:
|
||||
srv.starttls()
|
||||
srv.ehlo()
|
||||
self._log_debug(u'Sent STARTTLS and EHLO command')
|
||||
|
||||
:param ep_name: The name of the episode that was snatched
|
||||
:param title: The title of the notification (optional)
|
||||
"""
|
||||
if login:
|
||||
srv.login(user, pwd)
|
||||
self._log_debug(u'Sent LOGIN command')
|
||||
|
||||
if sickbeard.EMAIL_NOTIFY_ONSNATCH:
|
||||
title = sickbeard.EMAIL_OLD_SUBJECTS and 'Snatched' or title
|
||||
self._send_email(title, ep_name)
|
||||
srv.sendmail(smtp_from, to, msg.as_string())
|
||||
srv.quit()
|
||||
|
||||
def notify_download(self, ep_name, title=notifyStrings[NOTIFY_DOWNLOAD]):
|
||||
"""
|
||||
Send a notification that an episode was downloaded
|
||||
except Exception as e:
|
||||
self.last_err = '%s' % e
|
||||
return False
|
||||
|
||||
:param ep_name: The name of the episode that was downloaded
|
||||
:param title: The title of the notification (optional)
|
||||
"""
|
||||
|
||||
if sickbeard.EMAIL_NOTIFY_ONDOWNLOAD:
|
||||
title = sickbeard.EMAIL_OLD_SUBJECTS and 'Downloaded' or title
|
||||
self._send_email(title, ep_name)
|
||||
|
||||
def notify_subtitle_download(self, ep_name, lang, title=notifyStrings[NOTIFY_SUBTITLE_DOWNLOAD]):
|
||||
"""
|
||||
Send a notification that a subtitle was downloaded
|
||||
|
||||
:param ep_name: The name of the episode that was downloaded
|
||||
:param lang: Subtitle language
|
||||
:param title: The title of the notification (optional)
|
||||
"""
|
||||
|
||||
if sickbeard.EMAIL_NOTIFY_ONSUBTITLEDOWNLOAD:
|
||||
title = sickbeard.EMAIL_OLD_SUBJECTS and 'Subtitle Downloaded' or title
|
||||
self._send_email(title, ep_name, '%s ' % lang, '</b></p>\n<p>Language: <b>%s' % lang)
|
||||
|
||||
def notify_git_update(self, new_version='??'):
|
||||
|
||||
pass
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _get_recipients(show_name=None):
|
||||
|
@ -154,39 +93,91 @@ class EmailNotifier:
|
|||
|
||||
return list(set(email_list))
|
||||
|
||||
def _sendmail(self, host, port, smtp_from, use_tls, user, pwd, to, msg, smtp_debug=False):
|
||||
def _notify(self, title, body, lang='', extra='', **kwargs):
|
||||
|
||||
use_tls = 1 == sickbeard.helpers.tryInt(use_tls)
|
||||
login = any(user) and any(pwd)
|
||||
logger.log(u'Sendmail HOST: %s; PORT: %s; LOGIN: %s, TLS: %s, USER: %s, FROM: %s, TO: %s' % (
|
||||
host, port, login, use_tls, user, smtp_from, to), logger.DEBUG)
|
||||
show = body.split(' - ')[0]
|
||||
to = self._get_recipients(show)
|
||||
if not any(to):
|
||||
self._log_warning(u'No email recipients to notify, skipping')
|
||||
return
|
||||
|
||||
self._log_debug(u'Email recipients to notify: %s' % to)
|
||||
|
||||
try:
|
||||
srv = smtplib.SMTP(host, int(port))
|
||||
if smtp_debug:
|
||||
srv.set_debuglevel(1)
|
||||
msg = MIMEMultipart('alternative')
|
||||
msg.attach(MIMEText(
|
||||
'<body style="font-family:Helvetica, Arial, sans-serif;">' +
|
||||
'<h3>SickGear Notification - %s</h3>\n' % title +
|
||||
'<p>Show: <b>' + show.encode('ascii', 'xmlcharrefreplace') +
|
||||
'</b></p>\n<p>Episode: <b>' +
|
||||
unicode(re.search('.+ - (.+?-.+) -.+', body).group(1)).encode('ascii', 'xmlcharrefreplace') +
|
||||
extra +
|
||||
'</b></p>\n\n' +
|
||||
'<footer style="margin-top:2.5em;padding:.7em 0;color:#777;border-top:#BBB solid 1px;">' +
|
||||
'Powered by SickGear.</footer></body>',
|
||||
'html'))
|
||||
except (StandardError, Exception):
|
||||
try:
|
||||
msg = MIMEText(body)
|
||||
except (StandardError, Exception):
|
||||
msg = MIMEText('Episode %s' % title)
|
||||
|
||||
if use_tls or login:
|
||||
srv.ehlo()
|
||||
logger.log(u'Sent initial EHLO command', logger.DEBUG)
|
||||
msg['Subject'] = '%s%s: %s' % (lang, title, body)
|
||||
msg['From'] = sickbeard.EMAIL_FROM
|
||||
msg['To'] = ','.join(to)
|
||||
msg['Date'] = formatdate(localtime=True)
|
||||
if self._sendmail(sickbeard.EMAIL_HOST, sickbeard.EMAIL_PORT, sickbeard.EMAIL_FROM, sickbeard.EMAIL_TLS,
|
||||
sickbeard.EMAIL_USER, sickbeard.EMAIL_PASSWORD, to, msg):
|
||||
self._log_debug(u'%s notification sent to [%s] for "%s"' % (title, to, body))
|
||||
else:
|
||||
self._log_error(u'%s notification ERROR: %s' % (title, self.last_err))
|
||||
|
||||
if use_tls:
|
||||
srv.starttls()
|
||||
srv.ehlo()
|
||||
logger.log(u'Sent STARTTLS and EHLO command', logger.DEBUG)
|
||||
def test_notify(self, host, port, smtp_from, use_tls, user, pwd, to):
|
||||
self._testing = True
|
||||
|
||||
if login:
|
||||
srv.login(user, pwd)
|
||||
logger.log(u'Sent LOGIN command', logger.DEBUG)
|
||||
msg = MIMEText('Success. This is a SickGear test message. Typically sent on, %s' % notify_strings['download'])
|
||||
msg['Subject'] = 'SickGear: Test message'
|
||||
msg['From'] = smtp_from
|
||||
msg['To'] = to
|
||||
msg['Date'] = formatdate(localtime=True)
|
||||
|
||||
srv.sendmail(smtp_from, to, msg.as_string())
|
||||
srv.quit()
|
||||
r = self._sendmail(host, port, smtp_from, use_tls, user, pwd, [to], msg, True)
|
||||
return self._choose(('Success, notification sent.',
|
||||
'Failed to send notification: %s' % self.last_err)[not r], r)
|
||||
|
||||
except Exception as e:
|
||||
self.last_err = '%s' % e
|
||||
return False
|
||||
def notify_snatch(self, ep_name, title=None):
|
||||
"""
|
||||
Send a notification that an episode was snatched
|
||||
|
||||
return True
|
||||
:param ep_name: The name of the episode that was snatched
|
||||
:param title: The title of the notification (optional)
|
||||
"""
|
||||
|
||||
title = sickbeard.EMAIL_OLD_SUBJECTS and 'Snatched' or title or notify_strings['snatch']
|
||||
self._notify(title, ep_name)
|
||||
|
||||
def notify_download(self, ep_name, title=None):
|
||||
"""
|
||||
Send a notification that an episode was downloaded
|
||||
|
||||
:param ep_name: The name of the episode that was downloaded
|
||||
:param title: The title of the notification (optional)
|
||||
"""
|
||||
|
||||
title = sickbeard.EMAIL_OLD_SUBJECTS and 'Downloaded' or title or notify_strings['download']
|
||||
self._notify(title, ep_name)
|
||||
|
||||
def notify_subtitle_download(self, ep_name, lang, title=None):
|
||||
"""
|
||||
Send a notification that a subtitle was downloaded
|
||||
|
||||
:param ep_name: The name of the episode that was downloaded
|
||||
:param lang: Subtitle language
|
||||
:param title: The title of the notification (optional)
|
||||
"""
|
||||
|
||||
title = sickbeard.EMAIL_OLD_SUBJECTS and 'Subtitle Downloaded' or title or notify_strings['subtitle_download']
|
||||
self._notify(title, ep_name, '%s ' % lang, '</b></p>\n<p>Language: <b>%s' % lang)
|
||||
|
||||
|
||||
notifier = EmailNotifier
|
||||
|
|
|
@ -15,68 +15,31 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sickbeard
|
||||
from sickbeard import logger
|
||||
from socket import socket, AF_INET, SOCK_DGRAM, SOL_SOCKET, SO_REUSEADDR, SO_BROADCAST, SHUT_RDWR
|
||||
|
||||
import sickbeard
|
||||
from sickbeard.notifiers.generic import Notifier
|
||||
|
||||
from lib import simplejson as json
|
||||
|
||||
|
||||
class EmbyNotifier:
|
||||
class EmbyNotifier(Notifier):
|
||||
|
||||
def __init__(self):
|
||||
self.sg_logo_url = 'https://raw.githubusercontent.com/SickGear/SickGear/master/gui/slick/images/ico/' + \
|
||||
'apple-touch-icon-precomposed.png'
|
||||
super(EmbyNotifier, self).__init__()
|
||||
|
||||
self.response = None
|
||||
self.test_mode = False
|
||||
|
||||
def _notify_emby(self, msg, hosts=None, apikeys=None):
|
||||
""" Internal wrapper for the test_notify function
|
||||
def update_library(self, show=None, **kwargs):
|
||||
""" Update library function
|
||||
|
||||
Args:
|
||||
msg: Message body of the notice to send
|
||||
:param show: TVShow object
|
||||
|
||||
Returns:
|
||||
2-Tuple True if msg successfully sent otherwise False, Failure message string or None
|
||||
Returns: None if no processing done, True if processing succeeded with no issues else False if any issues found
|
||||
"""
|
||||
|
||||
if not sickbeard.USE_EMBY and not self.test_mode:
|
||||
self._log(u'Notification not enabled, skipping this notification', logger.DEBUG)
|
||||
return False, None
|
||||
|
||||
hosts, keys, message = self._check_config(hosts, apikeys)
|
||||
if not hosts:
|
||||
return False, message
|
||||
|
||||
total_success = True
|
||||
messages = []
|
||||
|
||||
args = dict(post_json={'Name': 'SickGear', 'Description': msg, 'ImageUrl': self.sg_logo_url})
|
||||
for i, cur_host in enumerate(hosts):
|
||||
|
||||
self.response = None
|
||||
response = sickbeard.helpers.getURL(
|
||||
'http://%s/emby/Notifications/Admin' % cur_host,
|
||||
headers={'Content-type': 'application/json', 'X-MediaBrowser-Token': keys[i]},
|
||||
timeout=10, hooks=dict(response=self._cb_response), **args)
|
||||
if not response or self.response:
|
||||
if self.response and 401 == self.response.get('status_code'):
|
||||
total_success = False
|
||||
messages += ['Fail: Cannot authenticate API key with %s' % cur_host]
|
||||
self._log(u'Failed to authenticate with %s' % cur_host)
|
||||
continue
|
||||
elif not response and not self.response or not self.response.get('ok'):
|
||||
total_success = False
|
||||
messages += ['Fail: No supported Emby server found at %s' % cur_host]
|
||||
self._log(u'Warning, could not connect with server at ' + cur_host)
|
||||
continue
|
||||
messages += ['OK: %s' % cur_host]
|
||||
|
||||
return total_success, '<br />\n'.join(messages)
|
||||
|
||||
def _update_library(self, show=None):
|
||||
|
||||
hosts, keys, message = self._check_config()
|
||||
if not hosts:
|
||||
self._log(u'Issue with hosts or api keys, check your settings')
|
||||
self._log_warning(u'Issue with hosts or api keys, check your settings')
|
||||
return False
|
||||
|
||||
from sickbeard.indexers.indexer_config import INDEXER_TVDB
|
||||
|
@ -94,30 +57,28 @@ class EmbyNotifier:
|
|||
timeout=20, hooks=dict(response=self._cb_response), **args)
|
||||
# Emby will initiate a LibraryMonitor path refresh one minute after this success
|
||||
if self.response and 204 == self.response.get('status_code') and self.response.get('ok'):
|
||||
self._log(u'Success: update %s sent to host %s in a library updated call' % (mode_to_log, cur_host),
|
||||
logger.MESSAGE)
|
||||
self._log(u'Success: update %s sent to host %s in a library updated call' % (mode_to_log, cur_host))
|
||||
continue
|
||||
elif self.response and 401 == self.response.get('status_code'):
|
||||
self._log(u'Failed to authenticate with %s' % cur_host)
|
||||
self._log_warning(u'Failed to authenticate with %s' % cur_host)
|
||||
elif self.response and 404 == self.response.get('status_code'):
|
||||
self._log(u'Warning, Library update responded 404 not found at %s' % cur_host, logger.DEBUG)
|
||||
self._log_debug(u'Warning, Library update responded 404 not found at %s' % cur_host)
|
||||
elif not response and not self.response or not self.response.get('ok'):
|
||||
self._log(u'Warning, could not connect with server at %s' % cur_host)
|
||||
self._log_warning(u'Warning, could not connect with server at %s' % cur_host)
|
||||
else:
|
||||
self._log(u'Warning, unknown response %sfrom %s, can most likely be ignored'
|
||||
% (self.response and '%s ' % self.response.get('status_code') or '', cur_host), logger.DEBUG)
|
||||
self._log_debug(u'Warning, unknown response %sfrom %s, can most likely be ignored'
|
||||
% (self.response and '%s ' % self.response.get('status_code') or '', cur_host))
|
||||
total_success = False
|
||||
|
||||
return total_success
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
def _cb_response(self, r, *args, **kwargs):
|
||||
|
||||
self.response = dict(status_code=r.status_code, ok=r.ok)
|
||||
return r
|
||||
|
||||
@staticmethod
|
||||
def _discover_server():
|
||||
def _discover_server(self):
|
||||
|
||||
cs = socket(AF_INET, SOCK_DGRAM)
|
||||
mb_listen_port = 7359
|
||||
|
||||
|
@ -132,14 +93,14 @@ class EmbyNotifier:
|
|||
'Not all data sent through the socket'
|
||||
message, host = cs.recvfrom(1024)
|
||||
if message:
|
||||
logger.log('%s found at %s: udp query response (%s)' % (server, host[0], message))
|
||||
self._log('%s found at %s: udp query response (%s)' % (server, host[0], message))
|
||||
result = ('{"Address":' not in message and message.split('|')[1] or
|
||||
json.loads(message).get('Address', ''))
|
||||
if result:
|
||||
break
|
||||
except AssertionError:
|
||||
sock_issue = True
|
||||
except Exception:
|
||||
except (StandardError, Exception):
|
||||
pass
|
||||
if not sock_issue:
|
||||
cs.shutdown(SHUT_RDWR)
|
||||
|
@ -149,7 +110,7 @@ class EmbyNotifier:
|
|||
|
||||
from sickbeard.helpers import starify
|
||||
|
||||
hosts, keys = hosts or sickbeard.EMBY_HOST, apikeys or sickbeard.EMBY_APIKEY
|
||||
hosts, keys = self._choose(hosts, sickbeard.EMBY_HOST), self._choose(apikeys, sickbeard.EMBY_APIKEY)
|
||||
hosts = [x.strip() for x in hosts.split(',') if x.strip()]
|
||||
keys = [x.strip() for x in keys.split(',') if x.strip()]
|
||||
|
||||
|
@ -165,15 +126,50 @@ class EmbyNotifier:
|
|||
|
||||
if len(hosts) != len(apikeys):
|
||||
message = ('Not enough Api keys for hosts', 'More Api keys than hosts')[len(apikeys) > len(hosts)]
|
||||
self._log(u'%s, check your settings' % message)
|
||||
self._log_warning(u'%s, check your settings' % message)
|
||||
return False, False, message
|
||||
|
||||
return hosts, apikeys, 'OK'
|
||||
|
||||
@staticmethod
|
||||
def _log(msg, log_level=logger.WARNING):
|
||||
def _notify(self, title, body, hosts, apikeys, **kwargs):
|
||||
""" Internal wrapper for the test_notify function
|
||||
|
||||
logger.log(u'Emby: %s' % msg, log_level)
|
||||
Args:
|
||||
title: The title of the message
|
||||
body: Message body of the notice to send
|
||||
|
||||
Returns:
|
||||
2-Tuple True if body successfully sent otherwise False, Failure message string or None
|
||||
"""
|
||||
hosts, keys, message = self._check_config(hosts, apikeys)
|
||||
if not hosts:
|
||||
return False, message
|
||||
|
||||
success = True
|
||||
message = []
|
||||
|
||||
args = dict(post_json={'Name': 'SickGear', 'Description': body, 'ImageUrl': self._sg_logo_url})
|
||||
for i, cur_host in enumerate(hosts):
|
||||
|
||||
self.response = None
|
||||
response = sickbeard.helpers.getURL(
|
||||
'http://%s/emby/Notifications/Admin' % cur_host,
|
||||
headers={'Content-type': 'application/json', 'X-MediaBrowser-Token': keys[i]},
|
||||
timeout=10, hooks=dict(response=self._cb_response), **args)
|
||||
if not response or self.response:
|
||||
if self.response and 401 == self.response.get('status_code'):
|
||||
success = False
|
||||
message += ['Fail: Cannot authenticate API key with %s' % cur_host]
|
||||
self._log_warning(u'Failed to authenticate with %s' % cur_host)
|
||||
continue
|
||||
elif not response and not self.response or not self.response.get('ok'):
|
||||
success = False
|
||||
message += ['Fail: No supported Emby server found at %s' % cur_host]
|
||||
self._log_warning(u'Warning, could not connect with server at ' + cur_host)
|
||||
continue
|
||||
message += ['OK: %s' % cur_host]
|
||||
|
||||
return self._choose(('Success, all hosts tested', '<br />\n'.join(message))[not success], success)
|
||||
|
||||
##############################################################################
|
||||
# Public functions
|
||||
|
@ -182,23 +178,5 @@ class EmbyNotifier:
|
|||
def discover_server(self):
|
||||
return self._discover_server()
|
||||
|
||||
def test_notify(self, host, apikey):
|
||||
|
||||
self.test_mode = True
|
||||
result = self._notify_emby('Testing SickGear Emby notifier', host, apikey)
|
||||
self.test_mode = False
|
||||
return result
|
||||
|
||||
def update_library(self, show=None, force=False):
|
||||
""" Wrapper for the update library functions
|
||||
|
||||
:param show: TVShow object
|
||||
:param force: True force update process
|
||||
|
||||
Returns: None if no processing done, True if processing succeeded with no issues else False if any issues found
|
||||
"""
|
||||
if sickbeard.USE_EMBY and (sickbeard.EMBY_UPDATE_LIBRARY or force):
|
||||
return self._update_library(show)
|
||||
|
||||
|
||||
notifier = EmbyNotifier
|
||||
|
|
141
sickbeard/notifiers/generic.py
Normal file
|
@ -0,0 +1,141 @@
|
|||
# coding=utf-8
|
||||
#
|
||||
# Author: Nic Wolfe <nic@wolfeden.ca>
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sickbeard
|
||||
from sickbeard import logger
|
||||
|
||||
notify_strings = dict(
|
||||
snatch='Started download',
|
||||
download='Download finished',
|
||||
subtitle_download='Subtitle download finished',
|
||||
git_updated='SickGear updated',
|
||||
git_updated_text='SickGear updated to commit#: ',
|
||||
test_title='SickGear notification test',
|
||||
test_body=u'Success testing %s settings from SickGear ʕ•ᴥ•ʔ',
|
||||
)
|
||||
|
||||
|
||||
class BaseNotifier(object):
|
||||
|
||||
def __init__(self):
|
||||
self.sg_logo_file = 'apple-touch-icon-precomposed.png'
|
||||
self._testing = False
|
||||
|
||||
@property
|
||||
def _sg_logo_url(self):
|
||||
return 'https://raw.githubusercontent.com/SickGear/SickGear/master/gui/slick/images/ico/' + self.sg_logo_file
|
||||
|
||||
def _log(self, msg, level=logger.MESSAGE):
|
||||
logger.log(u'%s: %s' % (self.name, msg), level)
|
||||
|
||||
def _log_debug(self, msg):
|
||||
self._log(msg, logger.DEBUG)
|
||||
|
||||
def _log_error(self, msg):
|
||||
self._log(msg, logger.ERROR)
|
||||
|
||||
def _log_warning(self, msg):
|
||||
self._log(msg, logger.WARNING)
|
||||
|
||||
@classmethod
|
||||
def id(cls):
|
||||
return cls.__name__.replace('Notifier', '').upper()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.__class__.__name__.replace('Notifier', '')
|
||||
|
||||
@classmethod
|
||||
def is_enabled_onsnatch(cls):
|
||||
return cls.is_enabled('NOTIFY_ONSNATCH')
|
||||
|
||||
@classmethod
|
||||
def is_enabled_ondownload(cls):
|
||||
return cls.is_enabled('NOTIFY_ONDOWNLOAD')
|
||||
|
||||
@classmethod
|
||||
def is_enabled_onsubtitledownload(cls):
|
||||
return cls.is_enabled('NOTIFY_ONSUBTITLEDOWNLOAD')
|
||||
|
||||
@classmethod
|
||||
def is_enabled_library(cls):
|
||||
return cls.is_enabled('UPDATE_LIBRARY')
|
||||
|
||||
@classmethod
|
||||
def is_enabled(cls, action=None):
|
||||
return getattr(sickbeard, action and '%s_%s' % (cls.id(), action) or 'USE_%s' % cls.id(), False)
|
||||
|
||||
def notify_snatch(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def notify_download(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def notify_subtitle_download(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def notify_git_update(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def update_library(self, **kwargs):
|
||||
"""
|
||||
note: nmj_notifier fires its library update when the notify_download is issued (inside notifiers)
|
||||
"""
|
||||
pass
|
||||
|
||||
def _notify(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def _choose(self, current=True, saved=True):
|
||||
if self._testing:
|
||||
return current
|
||||
return saved
|
||||
|
||||
@staticmethod
|
||||
def _body_only(title, body):
|
||||
# don't use title with updates or testing, as only one str is used
|
||||
return body if 'SickGear' in title else '%s: %s' % (title, body.replace('#: ', '# '))
|
||||
|
||||
|
||||
class Notifier(BaseNotifier):
|
||||
|
||||
def test_notify(self, *args, **kwargs):
|
||||
self._testing = True
|
||||
r = self._pre_notify('test_title', notify_strings['test_body'] % (self.name + ' notifier'), *args, **kwargs)
|
||||
return (r, (('Success, notification sent.', 'Failed to send notification.')[not r]))[r in (True, False)]
|
||||
|
||||
def notify_snatch(self, ep_name, **kwargs):
|
||||
self._pre_notify('snatch', ep_name, **kwargs)
|
||||
|
||||
def notify_download(self, ep_name, **kwargs):
|
||||
self._pre_notify('download', ep_name, **kwargs)
|
||||
|
||||
def notify_subtitle_download(self, ep_name, lang, **kwargs):
|
||||
self._pre_notify('subtitle_download', '%s : %s' % (ep_name, lang), **kwargs)
|
||||
|
||||
def notify_git_update(self, new_version='??', **kwargs):
|
||||
self._pre_notify('git_updated', notify_strings['git_updated_text'] + new_version, **kwargs)
|
||||
|
||||
def _pre_notify(self, notify_string, message, *args, **kwargs):
|
||||
self._log_debug(u'Sending notification "%s"' % (self._body_only(notify_strings[notify_string], message)))
|
||||
try:
|
||||
return self._notify(notify_strings[notify_string], message, *args, **kwargs)
|
||||
except (StandardError, Exception):
|
||||
return False
|
80
sickbeard/notifiers/gitter.py
Normal file
|
@ -0,0 +1,80 @@
|
|||
# coding=utf-8
|
||||
#
|
||||
# This file is part of SickGear.
|
||||
#
|
||||
# Thanks to: mallen86, generica
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sickbeard
|
||||
from sickbeard.notifiers.generic import Notifier
|
||||
|
||||
|
||||
class GitterNotifier(Notifier):
|
||||
|
||||
def __init__(self):
|
||||
super(GitterNotifier, self).__init__()
|
||||
|
||||
def _notify(self, title, body, room_name='', access_token='', **kwargs):
|
||||
|
||||
api_url = 'https://api.gitter.im/v1/'
|
||||
params = [('headers', dict(
|
||||
Authorization='Bearer %s' % self._choose(access_token, sickbeard.GITTER_ACCESS_TOKEN))), ('json', True)]
|
||||
is_locked = False
|
||||
|
||||
# get user of token
|
||||
# noinspection PyTypeChecker
|
||||
resp = sickbeard.helpers.getURL(**dict([('url', '%suser' % api_url)] + params))
|
||||
user_id = resp and 1 == len(resp) and resp[0].get('id') or None
|
||||
if None is user_id:
|
||||
result = self._failed('bad oath access token?')
|
||||
else:
|
||||
# get a room
|
||||
# noinspection PyTypeChecker
|
||||
resp = sickbeard.helpers.getURL(**dict(
|
||||
[('url', '%srooms' % api_url),
|
||||
('post_json', dict(uri=self._choose(room_name, sickbeard.GITTER_ROOM)))] + params))
|
||||
room_id = resp and resp.get('id') or None
|
||||
if None is room_id:
|
||||
result = self._failed('room locked or not found')
|
||||
else:
|
||||
is_locked = 'private' == resp.get('security', '').lower()
|
||||
|
||||
# join room
|
||||
# noinspection PyTypeChecker
|
||||
if not sickbeard.helpers.getURL(**dict(
|
||||
[('url', '%suser/%s/rooms' % (api_url, user_id)),
|
||||
('post_json', dict(id=room_id))] + params)):
|
||||
result = self._failed('failed to join room')
|
||||
else:
|
||||
# send text
|
||||
# noinspection PyTypeChecker
|
||||
resp = sickbeard.helpers.getURL(**dict(
|
||||
[('url', '%srooms/%s/chatMessages' % (api_url, room_id)),
|
||||
('post_json', dict(text=self._body_only(title, body)))] + params))
|
||||
if None is (resp and resp.get('id') or None):
|
||||
result = self._failed('failed to send text', append=False)
|
||||
else:
|
||||
result = True
|
||||
|
||||
return self._choose(('Error sending notification, %s' % result,
|
||||
'Successful test notice sent%s. (Note: %s clients display icon once in a sequence).' %
|
||||
(('', ' to locked room')[is_locked], self.name))[True is result], result)
|
||||
|
||||
def _failed(self, result, append=True):
|
||||
self._log_error('%s failed to send message%s' % (self.name, append and ', %s' % result or ''))
|
||||
return self._choose(result, None)
|
||||
|
||||
|
||||
notifier = GitterNotifier
|
|
@ -18,45 +18,28 @@
|
|||
|
||||
from __future__ import print_function
|
||||
import socket
|
||||
import urllib
|
||||
|
||||
import sickbeard
|
||||
|
||||
from sickbeard import logger, common
|
||||
from sickbeard.exceptions import ex
|
||||
from sickbeard.notifiers.generic import Notifier, notify_strings
|
||||
|
||||
from lib.growl import gntp
|
||||
|
||||
|
||||
class GrowlNotifier:
|
||||
def test_notify(self, host, password):
|
||||
self._sendRegistration(host, password, 'Test')
|
||||
return self._sendGrowl('Test Growl', 'Testing Growl settings from SickGear', 'Test', host, password,
|
||||
force=True)
|
||||
class GrowlNotifier(Notifier):
|
||||
|
||||
def notify_snatch(self, ep_name):
|
||||
if sickbeard.GROWL_NOTIFY_ONSNATCH:
|
||||
self._sendGrowl(common.notifyStrings[common.NOTIFY_SNATCH], ep_name)
|
||||
def __init__(self):
|
||||
super(GrowlNotifier, self).__init__()
|
||||
|
||||
def notify_download(self, ep_name):
|
||||
if sickbeard.GROWL_NOTIFY_ONDOWNLOAD:
|
||||
self._sendGrowl(common.notifyStrings[common.NOTIFY_DOWNLOAD], ep_name)
|
||||
self.sg_logo_file = 'apple-touch-icon-72x72.png'
|
||||
|
||||
def notify_subtitle_download(self, ep_name, lang):
|
||||
if sickbeard.GROWL_NOTIFY_ONSUBTITLEDOWNLOAD:
|
||||
self._sendGrowl(common.notifyStrings[common.NOTIFY_SUBTITLE_DOWNLOAD], ep_name + ': ' + lang)
|
||||
|
||||
def notify_git_update(self, new_version = '??'):
|
||||
if sickbeard.USE_GROWL:
|
||||
update_text=common.notifyStrings[common.NOTIFY_GIT_UPDATE_TEXT]
|
||||
title=common.notifyStrings[common.NOTIFY_GIT_UPDATE]
|
||||
self._sendGrowl(title, update_text + new_version)
|
||||
def _send_growl_msg(self, options, message=None):
|
||||
|
||||
def _send_growl(self, options, message=None):
|
||||
|
||||
#Send Notification
|
||||
# Send Notification
|
||||
notice = gntp.GNTPNotice()
|
||||
|
||||
#Required
|
||||
# Required
|
||||
notice.add_header('Application-Name', options['app'])
|
||||
notice.add_header('Notification-Name', options['name'])
|
||||
notice.add_header('Notification-Title', options['title'])
|
||||
|
@ -64,7 +47,7 @@ class GrowlNotifier:
|
|||
if options['password']:
|
||||
notice.set_password(options['password'])
|
||||
|
||||
#Optional
|
||||
# Optional
|
||||
if options['sticky']:
|
||||
notice.add_header('Notification-Sticky', options['sticky'])
|
||||
if options['priority']:
|
||||
|
@ -77,11 +60,15 @@ class GrowlNotifier:
|
|||
notice.add_header('Notification-Text', message)
|
||||
|
||||
response = self._send(options['host'], options['port'], notice.encode(), options['debug'])
|
||||
if isinstance(response, gntp.GNTPOK): return True
|
||||
if isinstance(response, gntp.GNTPOK):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _send(self, host, port, data, debug=False):
|
||||
if debug: print('<Sending>\n', data, '\n</Sending>')
|
||||
@staticmethod
|
||||
def _send(host, port, data, debug=False):
|
||||
|
||||
if debug:
|
||||
print('<Sending>\n', data, '\n</Sending>')
|
||||
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.connect((host, port))
|
||||
|
@ -89,107 +76,71 @@ class GrowlNotifier:
|
|||
response = gntp.parse_gntp(s.recv(1024))
|
||||
s.close()
|
||||
|
||||
if debug: print('<Recieved>\n', response, '\n</Recieved>')
|
||||
if debug:
|
||||
print('<Recieved>\n', response, '\n</Recieved>')
|
||||
|
||||
return response
|
||||
|
||||
def _sendGrowl(self, title='SickGear Notification', message=None, name=None, host=None, password=None,
|
||||
force=False):
|
||||
if not sickbeard.USE_GROWL and not force:
|
||||
return False
|
||||
def _send_registration(self, host=None, password=None):
|
||||
|
||||
if name == None:
|
||||
name = title
|
||||
host_parts = self._choose(host, sickbeard.GROWL_HOST).split(':')
|
||||
port = 23053 if (2 != len(host_parts) or '' == host_parts[1]) else int(host_parts[1])
|
||||
password = self._choose(password, sickbeard.GROWL_PASSWORD)
|
||||
|
||||
if host == None:
|
||||
hostParts = sickbeard.GROWL_HOST.split(':')
|
||||
else:
|
||||
hostParts = host.split(':')
|
||||
opts = dict(app='SickGear', host=host_parts[0], port=port, password=password, debug=False)
|
||||
|
||||
if len(hostParts) != 2 or hostParts[1] == '':
|
||||
port = 23053
|
||||
else:
|
||||
port = int(hostParts[1])
|
||||
|
||||
growlHosts = [(hostParts[0], port)]
|
||||
|
||||
opts = {}
|
||||
|
||||
opts['name'] = name
|
||||
|
||||
opts['title'] = title
|
||||
opts['app'] = 'SickGear'
|
||||
|
||||
opts['sticky'] = None
|
||||
opts['priority'] = None
|
||||
opts['debug'] = False
|
||||
|
||||
if password == None:
|
||||
opts['password'] = sickbeard.GROWL_PASSWORD
|
||||
else:
|
||||
opts['password'] = password
|
||||
|
||||
opts['icon'] = True
|
||||
|
||||
for pc in growlHosts:
|
||||
opts['host'] = pc[0]
|
||||
opts['port'] = pc[1]
|
||||
logger.log(u'GROWL: Sending message "' + message + '" to ' + opts['host'] + ':' + str(opts['port']), logger.DEBUG)
|
||||
try:
|
||||
if self._send_growl(opts, message):
|
||||
return True
|
||||
else:
|
||||
if self._sendRegistration(host, password, 'Sickbeard'):
|
||||
return self._send_growl(opts, message)
|
||||
else:
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.log(u'GROWL: Unable to send growl to ' + opts['host'] + ':' + str(opts['port']) + ' - ' + ex(e), logger.WARNING)
|
||||
return False
|
||||
|
||||
def _sendRegistration(self, host=None, password=None, name='SickGear Notification'):
|
||||
opts = {}
|
||||
|
||||
if host == None:
|
||||
hostParts = sickbeard.GROWL_HOST.split(':')
|
||||
else:
|
||||
hostParts = host.split(':')
|
||||
|
||||
if len(hostParts) != 2 or hostParts[1] == '':
|
||||
port = 23053
|
||||
else:
|
||||
port = int(hostParts[1])
|
||||
|
||||
opts['host'] = hostParts[0]
|
||||
opts['port'] = port
|
||||
|
||||
if password == None:
|
||||
opts['password'] = sickbeard.GROWL_PASSWORD
|
||||
else:
|
||||
opts['password'] = password
|
||||
|
||||
opts['app'] = 'SickGear'
|
||||
opts['debug'] = False
|
||||
|
||||
#Send Registration
|
||||
# Send Registration
|
||||
register = gntp.GNTPRegister()
|
||||
register.add_header('Application-Name', opts['app'])
|
||||
register.add_header('Application-Icon',
|
||||
'https://raw.githubusercontent.com/SickGear/SickGear/master/gui/slick/images/ico/apple-touch-icon-72x72.png')
|
||||
register.add_header('Application-Icon', self._sg_logo_url)
|
||||
|
||||
register.add_notification('Test', True)
|
||||
register.add_notification(common.notifyStrings[common.NOTIFY_SNATCH], True)
|
||||
register.add_notification(common.notifyStrings[common.NOTIFY_DOWNLOAD], True)
|
||||
register.add_notification(common.notifyStrings[common.NOTIFY_GIT_UPDATE], True)
|
||||
|
||||
register.add_notification(notify_strings['snatch'], True)
|
||||
register.add_notification(notify_strings['download'], True)
|
||||
register.add_notification(notify_strings['git_updated'], True)
|
||||
|
||||
if opts['password']:
|
||||
register.set_password(opts['password'])
|
||||
|
||||
try:
|
||||
return self._send(opts['host'], opts['port'], register.encode(), opts['debug'])
|
||||
except Exception as e:
|
||||
logger.log(u'GROWL: Unable to send growl to ' + opts['host'] + ':' + str(opts['port']) + ' - ' + ex(e), logger.WARNING)
|
||||
self._log_warning(u'Unable to send growl to %s:%s - %s' % (opts['host'], opts['port'], ex(e)))
|
||||
return False
|
||||
|
||||
def _notify(self, title, body, name=None, host=None, password=None, **kwargs):
|
||||
|
||||
name = name or title or 'SickGear Notification'
|
||||
|
||||
host_parts = self._choose(host, sickbeard.GROWL_HOST).split(':')
|
||||
port = (int(host_parts[1]), 23053)[len(host_parts) != 2 or '' == host_parts[1]]
|
||||
growl_hosts = [(host_parts[0], port)]
|
||||
password = self._choose(password, sickbeard.GROWL_PASSWORD)
|
||||
|
||||
opts = dict(title=title, name=name, app='SickGear', sticky=None, priority=None,
|
||||
password=password, icon=True, debug=False)
|
||||
|
||||
for pc in growl_hosts:
|
||||
opts['host'] = pc[0]
|
||||
opts['port'] = pc[1]
|
||||
try:
|
||||
if self._send_growl_msg(opts, body):
|
||||
return True
|
||||
|
||||
if self._send_registration(host, password):
|
||||
return self._send_growl_msg(opts, body)
|
||||
|
||||
except Exception as e:
|
||||
self._log_warning(u'Unable to send growl to %s:%s - %s' % (opts['host'], opts['port'], ex(e)))
|
||||
|
||||
return False
|
||||
|
||||
def test_notify(self, host, password):
|
||||
self._testing = True
|
||||
self._send_registration(host, password)
|
||||
return ('Success, registered and tested', 'Failed registration and testing')[
|
||||
True is not super(GrowlNotifier, self).test_notify(name='Test', host=host, password=password)] + \
|
||||
(urllib.unquote_plus(host) + ' with password: ' + password, '')[password in (None, '')]
|
||||
|
||||
|
||||
notifier = GrowlNotifier
|
||||
|
|
|
@ -17,56 +17,29 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import time
|
||||
import urllib
|
||||
|
||||
import sickbeard
|
||||
import sickbeard.helpers
|
||||
from sickbeard.exceptions import ex
|
||||
from sickbeard import logger, common
|
||||
|
||||
try:
|
||||
# noinspection PyPep8Naming
|
||||
import xml.etree.cElementTree as etree
|
||||
except ImportError:
|
||||
# noinspection PyPep8Naming
|
||||
import xml.etree.ElementTree as etree
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
from lib import simplejson as json
|
||||
import time
|
||||
import urllib
|
||||
import xml.etree.cElementTree as XmlEtree
|
||||
|
||||
import sickbeard
|
||||
import sickbeard.helpers
|
||||
from sickbeard import logger
|
||||
from sickbeard.exceptions import ex
|
||||
from sickbeard.notifiers.generic import Notifier
|
||||
|
||||
|
||||
class KodiNotifier:
|
||||
class KodiNotifier(Notifier):
|
||||
|
||||
def __init__(self):
|
||||
self.sg_logo_url = 'https://raw.githubusercontent.com/SickGear/SickGear/master/gui/slick/images/ico/' + \
|
||||
'apple-touch-icon-precomposed.png'
|
||||
super(KodiNotifier, self).__init__()
|
||||
|
||||
self.username, self.password = (None, None)
|
||||
self.response = None
|
||||
self.prefix = ''
|
||||
self.test_mode = False
|
||||
|
||||
@staticmethod
|
||||
def _log(msg, log_level=logger.WARNING):
|
||||
|
||||
logger.log(u'Kodi: %s' % msg, log_level)
|
||||
|
||||
def _maybe_log(self, msg, log_level=logger.WARNING):
|
||||
|
||||
if msg and (sickbeard.KODI_ALWAYS_ON or self.test_mode):
|
||||
self._log(msg + (not sickbeard.KODI_ALWAYS_ON and self.test_mode and
|
||||
' (Test mode ignores "Always On")' or ''), log_level)
|
||||
|
||||
def _maybe_log_failed_detection(self, host, msg='connect to'):
|
||||
|
||||
self._maybe_log(u'Failed to %s %s, check device(s) and config.' % (msg, host), logger.ERROR)
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
def cb_response(self, r, *args, **kwargs):
|
||||
self.response = dict(status_code=r.status_code)
|
||||
return r
|
||||
|
||||
def _get_kodi_version(self, host):
|
||||
""" Return Kodi JSON-RPC API version (odd # = dev, even # = stable)
|
||||
|
@ -88,7 +61,7 @@ class KodiNotifier:
|
|||
"""
|
||||
|
||||
timeout = 10
|
||||
response = self._send_to_kodi_json(host, dict(method='JSONRPC.Version'), timeout)
|
||||
response = self._send_json(host, dict(method='JSONRPC.Version'), timeout)
|
||||
if self.response and 401 == self.response.get('status_code'):
|
||||
return False
|
||||
|
||||
|
@ -98,77 +71,19 @@ class KodiNotifier:
|
|||
|
||||
# fallback to legacy HTTPAPI method
|
||||
test_command = {'command': 'Help'}
|
||||
if self._send_to_kodi(host, test_command, timeout):
|
||||
if self._send(host, test_command, timeout):
|
||||
# return fake version number to use the legacy method
|
||||
return 1
|
||||
|
||||
if self.response and 404 == self.response.get('status_code'):
|
||||
self.prefix = 'xbmc'
|
||||
if self._send_to_kodi(host, test_command, timeout):
|
||||
if self._send(host, test_command, timeout):
|
||||
# return fake version number to use the legacy method
|
||||
return 1
|
||||
|
||||
return False
|
||||
|
||||
def _notify_kodi(self, msg, title='SickGear', kodi_hosts=None):
|
||||
""" Internal wrapper for the notify_snatch and notify_download functions
|
||||
|
||||
Call either the JSON-RPC over HTTP or the legacy HTTP API methods depending on the Kodi API version.
|
||||
|
||||
Args:
|
||||
msg: Message body of the notice to send
|
||||
title: Title of the notice to send
|
||||
|
||||
Return:
|
||||
A list of results in the format of host:ip:result, where result will either be 'OK' or False.
|
||||
"""
|
||||
|
||||
# fill in omitted parameters
|
||||
if not kodi_hosts:
|
||||
kodi_hosts = sickbeard.KODI_HOST
|
||||
|
||||
if not sickbeard.USE_KODI and not self.test_mode:
|
||||
self._log(u'Notification not enabled, skipping this notification', logger.DEBUG)
|
||||
return False, None
|
||||
|
||||
total_success = True
|
||||
message = []
|
||||
for host in [x.strip() for x in kodi_hosts.split(',')]:
|
||||
cur_host = urllib.unquote_plus(host)
|
||||
|
||||
self._log(u'Sending notification to "%s" - %s' % (cur_host, message), logger.DEBUG)
|
||||
|
||||
api_version = self._get_kodi_version(cur_host)
|
||||
if self.response and 401 == self.response.get('status_code'):
|
||||
total_success = False
|
||||
message += ['Fail: Cannot authenticate with %s' % cur_host]
|
||||
self._log(u'Failed to authenticate with %s' % cur_host, logger.DEBUG)
|
||||
elif not api_version:
|
||||
total_success = False
|
||||
message += ['Fail: No supported Kodi found at %s' % cur_host]
|
||||
self._maybe_log_failed_detection(cur_host, 'connect and detect version for')
|
||||
else:
|
||||
if 4 >= api_version:
|
||||
self._log(u'Detected %sversion <= 11, using HTTP API'
|
||||
% self.prefix and ' ' + self.prefix.capitalize(), logger.DEBUG)
|
||||
__method_send = self._send_to_kodi
|
||||
command = dict(command='ExecBuiltIn',
|
||||
parameter='Notification(%s,%s)' % (title, msg))
|
||||
else:
|
||||
self._log(u'Detected version >= 12, using JSON API', logger.DEBUG)
|
||||
__method_send = self._send_to_kodi_json
|
||||
command = dict(method='GUI.ShowNotification',
|
||||
params={'title': '%s' % title,
|
||||
'message': '%s' % msg,
|
||||
'image': '%s' % self.sg_logo_url})
|
||||
|
||||
response_notify = __method_send(cur_host, command, 10)
|
||||
if response_notify:
|
||||
message += ['%s: %s' % ((response_notify, 'OK')['OK' in response_notify], cur_host)]
|
||||
|
||||
return total_success, '<br />\n'.join(message)
|
||||
|
||||
def _update_library(self, show_name=None):
|
||||
def update_library(self, show_name=None, **kwargs):
|
||||
""" Wrapper for the update library functions
|
||||
|
||||
Call either the JSON-RPC over HTTP or the legacy HTTP API methods depending on the Kodi API version.
|
||||
|
@ -184,7 +99,7 @@ class KodiNotifier:
|
|||
Returns: True if processing succeeded with no issues else False if any issues found
|
||||
"""
|
||||
if not sickbeard.KODI_HOST:
|
||||
self._log(u'No Kodi hosts specified, check your settings')
|
||||
self._log_warning(u'No Kodi hosts specified, check your settings')
|
||||
return False
|
||||
|
||||
# either update each host, or only attempt to update until one successful result
|
||||
|
@ -196,15 +111,15 @@ class KodiNotifier:
|
|||
|
||||
for cur_host in [x.strip() for x in sickbeard.KODI_HOST.split(',')]:
|
||||
|
||||
response = self._send_to_kodi_json(cur_host, dict(method='Profiles.GetCurrentProfile'))
|
||||
response = self._send_json(cur_host, dict(method='Profiles.GetCurrentProfile'))
|
||||
if self.response and 401 == self.response.get('status_code'):
|
||||
self._log(u'Failed to authenticate with %s' % cur_host, logger.DEBUG)
|
||||
self._log_debug(u'Failed to authenticate with %s' % cur_host)
|
||||
continue
|
||||
if not response:
|
||||
self._maybe_log_failed_detection(cur_host)
|
||||
continue
|
||||
|
||||
if self._send_update_library(cur_host, show_name):
|
||||
if self._send_library_update(cur_host, show_name):
|
||||
only_first.update(dict(profile=response.get('label') or 'Master', host=cur_host))
|
||||
self._log('Success: profile;' +
|
||||
u'"%(profile)s" at%(first)s host;%(host)s updated%(show)s%(first_note)s' % only_first)
|
||||
|
@ -218,7 +133,7 @@ class KodiNotifier:
|
|||
# needed for the 'update kodi' submenu command as it only cares of the final result vs the individual ones
|
||||
return 0 == result
|
||||
|
||||
def _send_update_library(self, host, show_name=None):
|
||||
def _send_library_update(self, host, show_name=None):
|
||||
""" Internal wrapper for the update library function
|
||||
|
||||
Call either the JSON-RPC over HTTP or the legacy HTTP API methods depending on the Kodi API version.
|
||||
|
@ -229,9 +144,6 @@ class KodiNotifier:
|
|||
Return:
|
||||
True if the update was successful else False
|
||||
"""
|
||||
|
||||
self._log(u'Sending request to update library for host: "%s"' % host, logger.DEBUG)
|
||||
|
||||
api_version = self._get_kodi_version(host)
|
||||
if api_version:
|
||||
# try to update just the show, if it fails, do full update if enabled
|
||||
|
@ -241,18 +153,17 @@ class KodiNotifier:
|
|||
|
||||
failed_msg = 'Single show update failed,'
|
||||
if sickbeard.KODI_UPDATE_FULL:
|
||||
self._log(u'%s falling back to full update' % failed_msg, logger.DEBUG)
|
||||
self._log_debug(u'%s falling back to full update' % failed_msg)
|
||||
return __method_update(host)
|
||||
|
||||
self._log(u'%s consider enabling "Perform full library update" in config/notifications' % failed_msg,
|
||||
logger.DEBUG)
|
||||
self._log_debug(u'%s consider enabling "Perform full library update" in config/notifications' % failed_msg)
|
||||
return False
|
||||
|
||||
##############################################################################
|
||||
# Legacy HTTP API (pre Kodi 12) methods
|
||||
##############################################################################
|
||||
|
||||
def _send_to_kodi(self, host, command, timeout=30):
|
||||
def _send(self, host, command, timeout=30):
|
||||
""" Handle communication to Kodi servers via HTTP API
|
||||
|
||||
Args:
|
||||
|
@ -263,11 +174,11 @@ class KodiNotifier:
|
|||
"""
|
||||
|
||||
if not host:
|
||||
self._log(u'No host specified, aborting update', logger.WARNING)
|
||||
self._log_warning(u'No host specified, aborting update')
|
||||
return False
|
||||
|
||||
args = {}
|
||||
if not sickbeard.KODI_ALWAYS_ON and not self.test_mode:
|
||||
if not sickbeard.KODI_ALWAYS_ON and not self._testing:
|
||||
args['mute_connect_err'] = True
|
||||
|
||||
if self.password or sickbeard.KODI_PASSWORD:
|
||||
|
@ -292,51 +203,54 @@ class KodiNotifier:
|
|||
"""
|
||||
|
||||
if not host:
|
||||
self._log(u'No host specified, aborting update', logger.WARNING)
|
||||
self._log_warning(u'No host specified, aborting update')
|
||||
return False
|
||||
|
||||
self._log(u'Updating library via HTTP method for host: %s' % host, logger.DEBUG)
|
||||
self._log_debug(u'Updating library via HTTP method for host: %s' % host)
|
||||
|
||||
# if we're doing per-show
|
||||
if show_name:
|
||||
self._log(u'Updating library via HTTP method for show %s' % show_name, logger.DEBUG)
|
||||
self._log_debug(u'Updating library via HTTP method for show %s' % show_name)
|
||||
|
||||
path_sql = 'SELECT path.strPath FROM path, tvshow, tvshowlinkpath WHERE ' \
|
||||
'tvshow.c00 = "%s"' % show_name \
|
||||
+ ' AND tvshowlinkpath.idShow = tvshow.idShow AND tvshowlinkpath.idPath = path.idPath'
|
||||
# noinspection SqlResolve
|
||||
path_sql = 'SELECT path.strPath' \
|
||||
' FROM path, tvshow, tvshowlinkpath' \
|
||||
' WHERE tvshow.c00 = "%s"' % show_name \
|
||||
+ ' AND tvshowlinkpath.idShow = tvshow.idShow' \
|
||||
' AND tvshowlinkpath.idPath = path.idPath'
|
||||
|
||||
# set xml response format, if this fails then don't bother with the rest
|
||||
if not self._send_to_kodi(
|
||||
host, {'command': 'SetResponseFormat(webheader;false;webfooter;false;header;<xml>;footer;</xml>;' +
|
||||
if not self._send(
|
||||
host, {'command': 'SetResponseFormat(webheader;false;webfooter;false;header;<xml>;footer;</xml>;'
|
||||
'opentag;<tag>;closetag;</tag>;closefinaltag;false)'}):
|
||||
return False
|
||||
|
||||
# sql used to grab path(s)
|
||||
response = self._send_to_kodi(host, {'command': 'QueryVideoDatabase(%s)' % path_sql})
|
||||
response = self._send(host, {'command': 'QueryVideoDatabase(%s)' % path_sql})
|
||||
if not response:
|
||||
self._log(u'Invalid response for %s on %s' % (show_name, host), logger.DEBUG)
|
||||
self._log_debug(u'Invalid response for %s on %s' % (show_name, host))
|
||||
return False
|
||||
|
||||
try:
|
||||
et = etree.fromstring(urllib.quote(response, ':\\/<>'))
|
||||
et = XmlEtree.fromstring(urllib.quote(response, ':\\/<>'))
|
||||
except SyntaxError as e:
|
||||
self._log(u'Unable to parse XML in response: %s' % ex(e), logger.ERROR)
|
||||
self._log_error(u'Unable to parse XML in response: %s' % ex(e))
|
||||
return False
|
||||
|
||||
paths = et.findall('.//field')
|
||||
if not paths:
|
||||
self._log(u'No valid path found for %s on %s' % (show_name, host), logger.DEBUG)
|
||||
self._log_debug(u'No valid path found for %s on %s' % (show_name, host))
|
||||
return False
|
||||
|
||||
for path in paths:
|
||||
# we do not need it double-encoded, gawd this is dumb
|
||||
un_enc_path = urllib.unquote(path.text).decode(sickbeard.SYS_ENCODING)
|
||||
self._log(u'Updating %s on %s at %s' % (show_name, host, un_enc_path), logger.DEBUG)
|
||||
self._log_debug(u'Updating %s on %s at %s' % (show_name, host, un_enc_path))
|
||||
|
||||
if not self._send_to_kodi(
|
||||
host, {'command': 'ExecBuiltIn', 'parameter': 'Kodi.updatelibrary(video, %s)' % un_enc_path}):
|
||||
self._log(u'Update of show directory failed for %s on %s at %s'
|
||||
% (show_name, host, un_enc_path), logger.ERROR)
|
||||
if not self._send(
|
||||
host, dict(command='ExecBuiltIn', parameter='Kodi.updatelibrary(video, %s)' % un_enc_path)):
|
||||
self._log_error(u'Update of show directory failed for %s on %s at %s'
|
||||
% (show_name, host, un_enc_path))
|
||||
return False
|
||||
|
||||
# sleep for a few seconds just to be sure kodi has a chance to finish each directory
|
||||
|
@ -344,10 +258,10 @@ class KodiNotifier:
|
|||
time.sleep(5)
|
||||
# do a full update if requested
|
||||
else:
|
||||
self._log(u'Full library update on host: %s' % host, logger.DEBUG)
|
||||
self._log_debug(u'Full library update on host: %s' % host)
|
||||
|
||||
if not self._send_to_kodi(host, {'command': 'ExecBuiltIn', 'parameter': 'Kodi.updatelibrary(video)'}):
|
||||
self._log(u'Failed full library update on: %s' % host, logger.ERROR)
|
||||
if not self._send(host, dict(command='ExecBuiltIn', parameter='Kodi.updatelibrary(video)')):
|
||||
self._log_error(u'Failed full library update on: %s' % host)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
@ -356,7 +270,7 @@ class KodiNotifier:
|
|||
# JSON-RPC API (Kodi 12+) methods
|
||||
##############################################################################
|
||||
|
||||
def _send_to_kodi_json(self, host, command, timeout=30):
|
||||
def _send_json(self, host, command, timeout=30):
|
||||
""" Handle communication to Kodi installations via JSONRPC
|
||||
|
||||
Args:
|
||||
|
@ -368,7 +282,7 @@ class KodiNotifier:
|
|||
|
||||
result = {}
|
||||
if not host:
|
||||
self._log(u'No host specified, aborting update', logger.WARNING)
|
||||
self._log_warning(u'No host specified, aborting update')
|
||||
return result
|
||||
|
||||
if isinstance(command, dict):
|
||||
|
@ -378,7 +292,7 @@ class KodiNotifier:
|
|||
else:
|
||||
args = dict(data=command)
|
||||
|
||||
if not sickbeard.KODI_ALWAYS_ON and not self.test_mode:
|
||||
if not sickbeard.KODI_ALWAYS_ON and not self._testing:
|
||||
args['mute_connect_err'] = True
|
||||
|
||||
if self.password or sickbeard.KODI_PASSWORD:
|
||||
|
@ -391,8 +305,8 @@ class KodiNotifier:
|
|||
if not response.get('error'):
|
||||
return 'OK' == response.get('result') and {'OK': True} or response.get('result')
|
||||
|
||||
self._log(u'API error; %s from %s in response to command: %s'
|
||||
% (json.dumps(response['error']), host, json.dumps(command)), logger.ERROR)
|
||||
self._log_error(u'API error; %s from %s in response to command: %s'
|
||||
% (json.dumps(response['error']), host, json.dumps(command)))
|
||||
return result
|
||||
|
||||
def _update_json(self, host=None, show_name=None):
|
||||
|
@ -408,12 +322,12 @@ class KodiNotifier:
|
|||
"""
|
||||
|
||||
if not host:
|
||||
self._log(u'No host specified, aborting update', logger.WARNING)
|
||||
self._log_warning(u'No host specified, aborting update')
|
||||
return False
|
||||
|
||||
# if we're doing per-show
|
||||
if show_name:
|
||||
self._log(u'JSON library update. Host: %s Show: %s' % (host, show_name), logger.DEBUG)
|
||||
self._log_debug(u'JSON library update. Host: %s Show: %s' % (host, show_name))
|
||||
|
||||
# try fetching tvshowid using show_name with a fallback to getting show list
|
||||
show_name = urllib.unquote_plus(show_name)
|
||||
|
@ -424,17 +338,18 @@ class KodiNotifier:
|
|||
|
||||
shows = None
|
||||
for command in commands:
|
||||
response = self._send_to_kodi_json(host, command)
|
||||
response = self._send_json(host, command)
|
||||
shows = response.get('tvshows')
|
||||
if shows:
|
||||
break
|
||||
|
||||
if not shows:
|
||||
self._log(u'No items in GetTVShows response', logger.DEBUG)
|
||||
self._log_debug(u'No items in GetTVShows response')
|
||||
return False
|
||||
|
||||
tvshowid = -1
|
||||
path = ''
|
||||
# noinspection PyTypeChecker
|
||||
for show in shows:
|
||||
if show_name == show.get('title') or show_name == show.get('label'):
|
||||
tvshowid = show.get('tvshowid', -1)
|
||||
|
@ -444,82 +359,104 @@ class KodiNotifier:
|
|||
|
||||
# we didn't find the show (exact match), thus revert to just doing a full update if enabled
|
||||
if -1 == tvshowid:
|
||||
self._log(u'Doesn\'t have "%s" in it\'s known shows, full library update required' % show_name,
|
||||
logger.DEBUG)
|
||||
self._log_debug(u'Doesn\'t have "%s" in it\'s known shows, full library update required' % show_name)
|
||||
return False
|
||||
|
||||
# lookup tv-show path if we don't already know it
|
||||
if not len(path):
|
||||
command = dict(method='VideoLibrary.GetTVShowDetails',
|
||||
params={'tvshowid': tvshowid, 'properties': ['file']})
|
||||
response = self._send_to_kodi_json(host, command)
|
||||
response = self._send_json(host, command)
|
||||
path = 'tvshowdetails' in response and response['tvshowdetails'].get('file', '') or ''
|
||||
|
||||
if not len(path):
|
||||
self._log(u'No valid path found for %s with ID: %s on %s' % (show_name, tvshowid, host), logger.WARNING)
|
||||
self._log_warning(u'No valid path found for %s with ID: %s on %s' % (show_name, tvshowid, host))
|
||||
return False
|
||||
|
||||
self._log(u'Updating %s on %s at %s' % (show_name, host, path), logger.DEBUG)
|
||||
self._log_debug(u'Updating %s on %s at %s' % (show_name, host, path))
|
||||
command = dict(method='VideoLibrary.Scan', params={'directory': '%s' % json.dumps(path)[1:-1]})
|
||||
response_scan = self._send_to_kodi_json(host, command)
|
||||
response_scan = self._send_json(host, command)
|
||||
if not response_scan.get('OK'):
|
||||
self._log(u'Update of show directory failed for %s on %s at %s response: %s' %
|
||||
(show_name, host, path, response_scan), logger.ERROR)
|
||||
self._log_error(u'Update of show directory failed for %s on %s at %s response: %s' %
|
||||
(show_name, host, path, response_scan))
|
||||
return False
|
||||
|
||||
# do a full update if requested
|
||||
else:
|
||||
self._log(u'Full library update on host: %s' % host, logger.DEBUG)
|
||||
response_scan = self._send_to_kodi_json(host, dict(method='VideoLibrary.Scan'))
|
||||
self._log_debug(u'Full library update on host: %s' % host)
|
||||
response_scan = self._send_json(host, dict(method='VideoLibrary.Scan'))
|
||||
if not response_scan.get('OK'):
|
||||
self._log(u'Failed full library update on: %s response: %s' % (host, response_scan), logger.ERROR)
|
||||
self._log_error(u'Failed full library update on: %s response: %s' % (host, response_scan))
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
##############################################################################
|
||||
# Public functions which will call the JSON or Legacy HTTP API methods
|
||||
##############################################################################
|
||||
# noinspection PyUnusedLocal
|
||||
def cb_response(self, r, *args, **kwargs):
|
||||
self.response = dict(status_code=r.status_code)
|
||||
return r
|
||||
|
||||
def notify_snatch(self, ep_name):
|
||||
def _maybe_log(self, msg, log_level=logger.WARNING):
|
||||
|
||||
if sickbeard.KODI_NOTIFY_ONSNATCH:
|
||||
self._notify_kodi(ep_name, common.notifyStrings[common.NOTIFY_SNATCH])
|
||||
if msg and (sickbeard.KODI_ALWAYS_ON or self._testing):
|
||||
self._log(msg + (not sickbeard.KODI_ALWAYS_ON and self._testing and
|
||||
' (Test mode ignores "Always On")' or ''), log_level)
|
||||
|
||||
def notify_download(self, ep_name):
|
||||
def _maybe_log_failed_detection(self, host, msg='connect to'):
|
||||
|
||||
if sickbeard.KODI_NOTIFY_ONDOWNLOAD:
|
||||
self._notify_kodi(ep_name, common.notifyStrings[common.NOTIFY_DOWNLOAD])
|
||||
self._maybe_log(u'Failed to %s %s, check device(s) and config' % (msg, host), logger.ERROR)
|
||||
|
||||
def notify_subtitle_download(self, ep_name, lang):
|
||||
def _notify(self, title, body, hosts, username, password, **kwargs):
|
||||
""" Internal wrapper for the notify_snatch and notify_download functions
|
||||
|
||||
if sickbeard.KODI_NOTIFY_ONSUBTITLEDOWNLOAD:
|
||||
self._notify_kodi('%s: %s' % (ep_name, lang), common.notifyStrings[common.NOTIFY_SUBTITLE_DOWNLOAD])
|
||||
Call either the JSON-RPC over HTTP or the legacy HTTP API methods depending on the Kodi API version.
|
||||
|
||||
def notify_git_update(self, new_version='??'):
|
||||
Args:
|
||||
title: Title of the notice to send
|
||||
body: Message body of the notice to send
|
||||
|
||||
if sickbeard.USE_KODI:
|
||||
update_text = common.notifyStrings[common.NOTIFY_GIT_UPDATE_TEXT]
|
||||
title = common.notifyStrings[common.NOTIFY_GIT_UPDATE]
|
||||
self._notify_kodi('%s %s' % (update_text, new_version), title)
|
||||
|
||||
def test_notify(self, host, username, password):
|
||||
|
||||
self.test_mode, self.username, self.password = True, username, password
|
||||
result = self._notify_kodi('Testing SickGear Kodi notifier', 'Test Notification', kodi_hosts=host)
|
||||
self.test_mode = False
|
||||
return result
|
||||
|
||||
def update_library(self, showName=None, force=False):
|
||||
""" Wrapper for the update library functions
|
||||
|
||||
:param showName: Name of a TV show
|
||||
:param force: True force update process
|
||||
|
||||
Returns: None if no processing done, True if processing succeeded with no issues else False if any issues found
|
||||
Return:
|
||||
A list of results in the format of host:ip:result, where result will either be 'OK' or False.
|
||||
"""
|
||||
if sickbeard.USE_KODI and (sickbeard.KODI_UPDATE_LIBRARY or force):
|
||||
return self._update_library(showName)
|
||||
self.username, self.password = username, password
|
||||
|
||||
title = title or 'SickGear'
|
||||
|
||||
hosts = self._choose(hosts, sickbeard.KODI_HOST)
|
||||
|
||||
success = True
|
||||
message = []
|
||||
for host in [x.strip() for x in hosts.split(',')]:
|
||||
cur_host = urllib.unquote_plus(host)
|
||||
|
||||
api_version = self._get_kodi_version(cur_host)
|
||||
if self.response and 401 == self.response.get('status_code'):
|
||||
success = False
|
||||
message += ['Fail: Cannot authenticate with %s' % cur_host]
|
||||
self._log_debug(u'Failed to authenticate with %s' % cur_host)
|
||||
elif not api_version:
|
||||
success = False
|
||||
message += ['Fail: No supported Kodi found at %s' % cur_host]
|
||||
self._maybe_log_failed_detection(cur_host, 'connect and detect version for')
|
||||
else:
|
||||
if 4 >= api_version:
|
||||
self._log_debug(u'Detected %sversion <= 11, using HTTP API'
|
||||
% self.prefix and ' ' + self.prefix.capitalize())
|
||||
__method_send = self._send
|
||||
command = dict(command='ExecBuiltIn',
|
||||
parameter='Notification(%s,%s)' % (title, body))
|
||||
else:
|
||||
self._log_debug(u'Detected version >= 12, using JSON API')
|
||||
__method_send = self._send_json
|
||||
command = dict(method='GUI.ShowNotification', params=dict(
|
||||
[('title', title), ('message', body), ('image', self._sg_logo_url)]
|
||||
+ ([], [('displaytime', 8000)])[self._testing]))
|
||||
|
||||
response_notify = __method_send(cur_host, command, 10)
|
||||
if response_notify:
|
||||
message += ['%s: %s' % ((response_notify, 'OK')['OK' in response_notify], cur_host)]
|
||||
|
||||
return self._choose(('Success, all hosts tested', '<br />\n'.join(message))[not success], success)
|
||||
|
||||
|
||||
notifier = KodiNotifier
|
||||
|
|
|
@ -20,23 +20,24 @@ import os
|
|||
import cgi
|
||||
import sickbeard
|
||||
|
||||
from sickbeard import logger, common
|
||||
from sickbeard.notifiers.generic import Notifier
|
||||
|
||||
|
||||
def diagnose():
|
||||
'''
|
||||
"""
|
||||
Check the environment for reasons libnotify isn't working. Return a
|
||||
user-readable message indicating possible issues.
|
||||
'''
|
||||
"""
|
||||
try:
|
||||
import pynotify #@UnusedImport
|
||||
# noinspection PyPackageRequirements
|
||||
import pynotify
|
||||
except ImportError:
|
||||
return (u"<p>Error: pynotify isn't installed. On Ubuntu/Debian, install the "
|
||||
u"<a href=\"apt:python-notify\">python-notify</a> package.")
|
||||
return ('Error: pynotify isn\'t installed. On Ubuntu/Debian, install the '
|
||||
'<a href=\'apt:python-notify\'>python-notify</a> package.')
|
||||
if 'DISPLAY' not in os.environ and 'DBUS_SESSION_BUS_ADDRESS' not in os.environ:
|
||||
return (u"<p>Error: Environment variables DISPLAY and DBUS_SESSION_BUS_ADDRESS "
|
||||
u"aren't set. libnotify will only work when you run SickGear "
|
||||
u"from a desktop login.")
|
||||
return ('Error: Environment variables DISPLAY and DBUS_SESSION_BUS_ADDRESS '
|
||||
'aren\'t set. libnotify will only work when you run SickGear '
|
||||
'from a desktop login.')
|
||||
try:
|
||||
import dbus
|
||||
except ImportError:
|
||||
|
@ -45,82 +46,71 @@ def diagnose():
|
|||
try:
|
||||
bus = dbus.SessionBus()
|
||||
except dbus.DBusException as e:
|
||||
return (u"<p>Error: unable to connect to D-Bus session bus: <code>%s</code>."
|
||||
u"<p>Are you running SickGear in a desktop session?") % (cgi.escape(e),)
|
||||
return (u'Error: unable to connect to D-Bus session bus: <code>%s</code>. '
|
||||
u'Are you running SickGear in a desktop session?') % (cgi.escape(e),)
|
||||
try:
|
||||
bus.get_object('org.freedesktop.Notifications',
|
||||
'/org/freedesktop/Notifications')
|
||||
except dbus.DBusException as e:
|
||||
return (u"<p>Error: there doesn't seem to be a notification daemon available: <code>%s</code> "
|
||||
u"<p>Try installing notification-daemon or notify-osd.") % (cgi.escape(e),)
|
||||
return u"<p>Error: Unable to send notification."
|
||||
return (u'Error: there doesn\'t seem to be a notification daemon available: <code>%s</code> '
|
||||
u'Try installing notification-daemon or notify-osd.') % (cgi.escape(e),)
|
||||
return 'Error: Unable to send notification.'
|
||||
|
||||
|
||||
class LibnotifyNotifier:
|
||||
class LibnotifyNotifier(Notifier):
|
||||
|
||||
def __init__(self):
|
||||
super(LibnotifyNotifier, self).__init__()
|
||||
|
||||
self.pynotify = None
|
||||
self.gobject = None
|
||||
|
||||
def init_pynotify(self):
|
||||
if self.pynotify is not None:
|
||||
return True
|
||||
|
||||
try:
|
||||
# noinspection PyPackageRequirements
|
||||
import pynotify
|
||||
except ImportError:
|
||||
logger.log(u"Unable to import pynotify. libnotify notifications won't work.", logger.ERROR)
|
||||
self._log_error(u'Unable to import pynotify. libnotify notifications won\'t work')
|
||||
return False
|
||||
|
||||
try:
|
||||
# noinspection PyPackageRequirements
|
||||
from gi.repository import GObject
|
||||
except ImportError:
|
||||
logger.log(u"Unable to import GObject from gi.repository. We can't catch a GError in display.", logger.ERROR)
|
||||
self._log_error(u'Unable to import GObject from gi.repository. Cannot catch a GError in display')
|
||||
return False
|
||||
|
||||
if not pynotify.init('SickGear'):
|
||||
logger.log(u"Initialization of pynotify failed. libnotify notifications won't work.", logger.ERROR)
|
||||
self._log_error(u'Initialization of pynotify failed. libnotify notifications won\'t work')
|
||||
return False
|
||||
|
||||
self.pynotify = pynotify
|
||||
self.gobject = GObject
|
||||
return True
|
||||
|
||||
def notify_snatch(self, ep_name):
|
||||
if sickbeard.LIBNOTIFY_NOTIFY_ONSNATCH:
|
||||
self._notify(common.notifyStrings[common.NOTIFY_SNATCH], ep_name)
|
||||
def _notify(self, title, body, **kwargs):
|
||||
|
||||
def notify_download(self, ep_name):
|
||||
if sickbeard.LIBNOTIFY_NOTIFY_ONDOWNLOAD:
|
||||
self._notify(common.notifyStrings[common.NOTIFY_DOWNLOAD], ep_name)
|
||||
result = False
|
||||
if self.init_pynotify():
|
||||
|
||||
def notify_subtitle_download(self, ep_name, lang):
|
||||
if sickbeard.LIBNOTIFY_NOTIFY_ONSUBTITLEDOWNLOAD:
|
||||
self._notify(common.notifyStrings[common.NOTIFY_SUBTITLE_DOWNLOAD], ep_name + ": " + lang)
|
||||
# Can't make this a global constant because PROG_DIR isn't available
|
||||
# when the module is imported.
|
||||
icon_path = os.path.join(sickbeard.PROG_DIR, 'data/images/sickbeard_touch_icon.png')
|
||||
icon_uri = 'file://' + os.path.abspath(icon_path)
|
||||
|
||||
def notify_git_update(self, new_version = "??"):
|
||||
if sickbeard.USE_LIBNOTIFY:
|
||||
update_text=common.notifyStrings[common.NOTIFY_GIT_UPDATE_TEXT]
|
||||
title=common.notifyStrings[common.NOTIFY_GIT_UPDATE]
|
||||
self._notify(title, update_text + new_version)
|
||||
# If the session bus can't be acquired here a bunch of warning messages
|
||||
# will be printed but the call to show() will still return True.
|
||||
# pynotify doesn't seem too keen on error handling.
|
||||
n = self.pynotify.Notification(title, body, icon_uri)
|
||||
try:
|
||||
result = n.show()
|
||||
except self.gobject.GError:
|
||||
pass
|
||||
|
||||
def test_notify(self):
|
||||
return self._notify('Test notification', "This is a test notification from SickGear", force=True)
|
||||
|
||||
def _notify(self, title, message, force=False):
|
||||
if not sickbeard.USE_LIBNOTIFY and not force:
|
||||
return False
|
||||
if not self.init_pynotify():
|
||||
return False
|
||||
|
||||
# Can't make this a global constant because PROG_DIR isn't available
|
||||
# when the module is imported.
|
||||
icon_path = os.path.join(sickbeard.PROG_DIR, "data/images/sickbeard_touch_icon.png")
|
||||
icon_uri = 'file://' + os.path.abspath(icon_path)
|
||||
|
||||
# If the session bus can't be acquired here a bunch of warning messages
|
||||
# will be printed but the call to show() will still return True.
|
||||
# pynotify doesn't seem too keen on error handling.
|
||||
n = self.pynotify.Notification(title, message, icon_uri)
|
||||
try:
|
||||
return n.show()
|
||||
except self.gobject.GError:
|
||||
return False
|
||||
return self._choose((True if result else diagnose()), result)
|
||||
|
||||
|
||||
notifier = LibnotifyNotifier
|
||||
|
|
|
@ -1,47 +1,15 @@
|
|||
import sickbeard
|
||||
from sickbeard.notifiers.generic import Notifier
|
||||
|
||||
from sickbeard import logger, common
|
||||
from lib.pynma import pynma
|
||||
|
||||
|
||||
class NMA_Notifier:
|
||||
def test_notify(self, nma_api, nma_priority):
|
||||
return self._sendNMA(nma_api, nma_priority, event='Test', message='Testing NMA settings from SickGear',
|
||||
force=True)
|
||||
class NMANotifier(Notifier):
|
||||
|
||||
def notify_snatch(self, ep_name):
|
||||
if sickbeard.NMA_NOTIFY_ONSNATCH:
|
||||
self._sendNMA(nma_api=None, nma_priority=None, event=common.notifyStrings[common.NOTIFY_SNATCH],
|
||||
message=ep_name)
|
||||
def _notify(self, title, body, nma_api=None, nma_priority=None, **kwargs):
|
||||
|
||||
def notify_download(self, ep_name):
|
||||
if sickbeard.NMA_NOTIFY_ONDOWNLOAD:
|
||||
self._sendNMA(nma_api=None, nma_priority=None, event=common.notifyStrings[common.NOTIFY_DOWNLOAD],
|
||||
message=ep_name)
|
||||
|
||||
def notify_subtitle_download(self, ep_name, lang):
|
||||
if sickbeard.NMA_NOTIFY_ONSUBTITLEDOWNLOAD:
|
||||
self._sendNMA(nma_api=None, nma_priority=None, event=common.notifyStrings[common.NOTIFY_SUBTITLE_DOWNLOAD],
|
||||
message=ep_name + ': ' + lang)
|
||||
|
||||
def notify_git_update(self, new_version = '??'):
|
||||
if sickbeard.USE_NMA:
|
||||
update_text=common.notifyStrings[common.NOTIFY_GIT_UPDATE_TEXT]
|
||||
title=common.notifyStrings[common.NOTIFY_GIT_UPDATE]
|
||||
self._sendNMA(nma_api=None, nma_priority=None, event=title, message=update_text + new_version)
|
||||
|
||||
def _sendNMA(self, nma_api=None, nma_priority=None, event=None, message=None, force=False):
|
||||
|
||||
title = 'SickGear'
|
||||
|
||||
if not sickbeard.USE_NMA and not force:
|
||||
return False
|
||||
|
||||
if nma_api == None:
|
||||
nma_api = sickbeard.NMA_API
|
||||
|
||||
if nma_priority == None:
|
||||
nma_priority = sickbeard.NMA_PRIORITY
|
||||
nma_api = self._choose(nma_api, sickbeard.NMA_API)
|
||||
nma_priority = self._choose(nma_priority, sickbeard.NMA_PRIORITY)
|
||||
|
||||
batch = False
|
||||
|
||||
|
@ -49,17 +17,22 @@ class NMA_Notifier:
|
|||
keys = nma_api.split(',')
|
||||
p.addkey(keys)
|
||||
|
||||
if len(keys) > 1: batch = True
|
||||
if 1 < len(keys):
|
||||
batch = True
|
||||
|
||||
logger.log('NMA: Sending notice with details: event="%s", message="%s", priority=%s, batch=%s' % (event, message, nma_priority, batch), logger.DEBUG)
|
||||
response = p.push(title, event, message, priority=nma_priority, batch_mode=batch)
|
||||
self._log_debug('Sending notice with priority=%s, batch=%s' % (nma_priority, batch))
|
||||
response = p.push('SickGear', title, body, priority=nma_priority, batch_mode=batch)
|
||||
|
||||
if not response[nma_api][u'code'] == u'200':
|
||||
logger.log(u'Could not send notification to NotifyMyAndroid', logger.ERROR)
|
||||
return False
|
||||
else:
|
||||
logger.log(u'NMA: Notification sent to NotifyMyAndroid', logger.MESSAGE)
|
||||
return True
|
||||
result = False
|
||||
try:
|
||||
if u'200' != response[nma_api][u'code']:
|
||||
self._log_error('Notification failed')
|
||||
else:
|
||||
result = True
|
||||
except (StandardError, Exception):
|
||||
pass
|
||||
|
||||
return result
|
||||
|
||||
|
||||
notifier = NMA_Notifier
|
||||
notifier = NMANotifier
|
||||
|
|
|
@ -16,188 +16,159 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import urllib, urllib2
|
||||
import sickbeard
|
||||
import telnetlib
|
||||
import re
|
||||
import telnetlib
|
||||
import urllib
|
||||
import urllib2
|
||||
import xml.etree.cElementTree as XmlEtree
|
||||
|
||||
from sickbeard import logger
|
||||
import sickbeard
|
||||
from sickbeard.exceptions import ex
|
||||
|
||||
try:
|
||||
import xml.etree.cElementTree as etree
|
||||
except ImportError:
|
||||
import xml.etree.ElementTree as etree
|
||||
from sickbeard.notifiers.generic import BaseNotifier
|
||||
|
||||
|
||||
class NMJNotifier:
|
||||
class NMJNotifier(BaseNotifier):
|
||||
|
||||
def notify_settings(self, host):
|
||||
"""
|
||||
Retrieves the settings from a NMJ/Popcorn hour
|
||||
|
||||
|
||||
host: The hostname/IP of the Popcorn Hour server
|
||||
|
||||
|
||||
Returns: True if the settings were retrieved successfully, False otherwise
|
||||
"""
|
||||
|
||||
# establish a terminal session to the PC
|
||||
terminal = False
|
||||
result, terminal = False, None
|
||||
try:
|
||||
terminal = telnetlib.Telnet(host)
|
||||
except Exception:
|
||||
logger.log(u'Warning: unable to get a telnet session to %s' % (host), logger.WARNING)
|
||||
return False
|
||||
except (StandardError, Exception):
|
||||
self._log_warning(u'Unable to get a telnet session to %s' % host)
|
||||
|
||||
# tell the terminal to output the necessary info to the screen so we can search it later
|
||||
logger.log(u'Connected to %s via telnet' % (host), logger.DEBUG)
|
||||
terminal.read_until('sh-3.00# ')
|
||||
terminal.write('cat /tmp/source\n')
|
||||
terminal.write('cat /tmp/netshare\n')
|
||||
terminal.write('exit\n')
|
||||
tnoutput = terminal.read_all()
|
||||
if result:
|
||||
# tell the terminal to output the necessary info to the screen so we can search it later
|
||||
self._log_debug(u'Connected to %s via telnet' % host)
|
||||
terminal.read_until('sh-3.00# ')
|
||||
terminal.write('cat /tmp/source\n')
|
||||
terminal.write('cat /tmp/netshare\n')
|
||||
terminal.write('exit\n')
|
||||
tnoutput = terminal.read_all()
|
||||
|
||||
database = ''
|
||||
device = ''
|
||||
match = re.search(r'(.+\.db)\r\n?(.+)(?=sh-3.00# cat /tmp/netshare)', tnoutput)
|
||||
|
||||
# if we found the database in the terminal output then save that database to the config
|
||||
if match:
|
||||
database = match.group(1)
|
||||
device = match.group(2)
|
||||
logger.log(u'Found NMJ database %s on device %s' % (database, device), logger.DEBUG)
|
||||
sickbeard.NMJ_DATABASE = database
|
||||
else:
|
||||
logger.log(u'Could not get current NMJ database on %s, NMJ is probably not running!' % (host), logger.WARNING)
|
||||
return False
|
||||
|
||||
# if the device is a remote host then try to parse the mounting URL and save it to the config
|
||||
if device.startswith('NETWORK_SHARE/'):
|
||||
match = re.search('.*(?=\r\n?%s)' % (re.escape(device[14:])), tnoutput)
|
||||
|
||||
if match:
|
||||
mount = match.group().replace('127.0.0.1', host)
|
||||
logger.log(u'Found mounting url on the Popcorn Hour in configuration: %s' % (mount), logger.DEBUG)
|
||||
sickbeard.NMJ_MOUNT = mount
|
||||
match = re.search(r'(.+\.db)\r\n?(.+)(?=sh-3.00# cat /tmp/netshare)', tnoutput)
|
||||
# if we found the database in the terminal output then save that database to the config
|
||||
if not match:
|
||||
self._log_warning(u'Could not get current NMJ database on %s, NMJ is probably not running!' % host)
|
||||
else:
|
||||
logger.log(u'Detected a network share on the Popcorn Hour, but could not get the mounting url',
|
||||
logger.WARNING)
|
||||
return False
|
||||
database = match.group(1)
|
||||
device = match.group(2)
|
||||
self._log_debug(u'Found NMJ database %s on device %s' % (database, device))
|
||||
sickbeard.NMJ_DATABASE = database
|
||||
# if the device is a remote host then try to parse the mounting URL and save it to the config
|
||||
if device.startswith('NETWORK_SHARE/'):
|
||||
match = re.search('.*(?=\r\n?%s)' % (re.escape(device[14:])), tnoutput)
|
||||
|
||||
return True
|
||||
if not match:
|
||||
self._log_warning('Detected a network share on the Popcorn Hour, '
|
||||
'but could not get the mounting url')
|
||||
else:
|
||||
mount = match.group().replace('127.0.0.1', host)
|
||||
self._log_debug(u'Found mounting url on the Popcorn Hour in configuration: %s' % mount)
|
||||
sickbeard.NMJ_MOUNT = mount
|
||||
result = True
|
||||
|
||||
def notify_snatch(self, ep_name):
|
||||
return False
|
||||
#Not implemented: Start the scanner when snatched does not make any sense
|
||||
if result:
|
||||
return '{"message": "Got settings from %(host)s", "database": "%(database)s", "mount": "%(mount)s"}' % {
|
||||
"host": host, "database": sickbeard.NMJ_DATABASE, "mount": sickbeard.NMJ_MOUNT}
|
||||
return '{"message": "Failed! Make sure your Popcorn is on and NMJ is running. ' \
|
||||
'(see Error Log -> Debug for detailed info)", "database": "", "mount": ""}'
|
||||
|
||||
def notify_download(self, ep_name):
|
||||
if sickbeard.USE_NMJ:
|
||||
self._notifyNMJ()
|
||||
|
||||
def notify_subtitle_download(self, ep_name, lang):
|
||||
if sickbeard.USE_NMJ:
|
||||
self._notifyNMJ()
|
||||
|
||||
def notify_git_update(self, new_version):
|
||||
return False
|
||||
# Not implemented, no reason to start scanner.
|
||||
|
||||
def test_notify(self, host, database, mount):
|
||||
return self._sendNMJ(host, database, mount)
|
||||
|
||||
def _sendNMJ(self, host, database, mount=None):
|
||||
def _send(self, host=None, database=None, mount=None):
|
||||
"""
|
||||
Sends a NMJ update command to the specified machine
|
||||
|
||||
|
||||
host: The hostname/IP to send the request to (no port)
|
||||
database: The database to send the requst to
|
||||
mount: The mount URL to use (optional)
|
||||
|
||||
|
||||
Returns: True if the request succeeded, False otherwise
|
||||
"""
|
||||
host = self._choose(host, sickbeard.NMJ_HOST)
|
||||
database = self._choose(database, sickbeard.NMJ_DATABASE)
|
||||
mount = self._choose(mount, sickbeard.NMJ_MOUNT)
|
||||
|
||||
self._log_debug(u'Sending scan command for NMJ ')
|
||||
|
||||
# if a mount URL is provided then attempt to open a handle to that URL
|
||||
if mount:
|
||||
try:
|
||||
req = urllib2.Request(mount)
|
||||
logger.log(u'Try to mount network drive via url: %s' % (mount), logger.DEBUG)
|
||||
handle = urllib2.urlopen(req)
|
||||
self._log_debug(u'Try to mount network drive via url: %s' % mount)
|
||||
urllib2.urlopen(req)
|
||||
except IOError as e:
|
||||
if hasattr(e, 'reason'):
|
||||
logger.log(u'NMJ: Could not contact Popcorn Hour on host %s: %s' % (host, e.reason), logger.WARNING)
|
||||
self._log_warning(u'Could not contact Popcorn Hour on host %s: %s' % (host, e.reason))
|
||||
elif hasattr(e, 'code'):
|
||||
logger.log(u'NMJ: Problem with Popcorn Hour on host %s: %s' % (host, e.code), logger.WARNING)
|
||||
self._log_warning(u'Problem with Popcorn Hour on host %s: %s' % (host, e.code))
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.log(u'NMJ: Unknown exception: ' + ex(e), logger.ERROR)
|
||||
self._log_error(u'Unknown exception: ' + ex(e))
|
||||
return False
|
||||
|
||||
# build up the request URL and parameters
|
||||
UPDATE_URL = 'http://%(host)s:8008/metadata_database?%(params)s'
|
||||
params = {
|
||||
'arg0': 'scanner_start',
|
||||
'arg1': database,
|
||||
'arg2': 'background',
|
||||
'arg3': ''
|
||||
}
|
||||
params = dict(arg0='scanner_start', arg1=database, arg2='background', arg3='')
|
||||
params = urllib.urlencode(params)
|
||||
updateUrl = UPDATE_URL % {'host': host, 'params': params}
|
||||
update_url = 'http://%(host)s:8008/metadata_database?%(params)s' % {'host': host, 'params': params}
|
||||
|
||||
# send the request to the server
|
||||
try:
|
||||
req = urllib2.Request(updateUrl)
|
||||
logger.log(u'Sending NMJ scan update command via url: %s' % (updateUrl), logger.DEBUG)
|
||||
req = urllib2.Request(update_url)
|
||||
self._log_debug(u'Sending scan update command via url: %s' % update_url)
|
||||
handle = urllib2.urlopen(req)
|
||||
response = handle.read()
|
||||
except IOError as e:
|
||||
if hasattr(e, 'reason'):
|
||||
logger.log(u'NMJ: Could not contact Popcorn Hour on host %s: %s' % (host, e.reason), logger.WARNING)
|
||||
self._log_warning(u'Could not contact Popcorn Hour on host %s: %s' % (host, e.reason))
|
||||
elif hasattr(e, 'code'):
|
||||
logger.log(u'NMJ: Problem with Popcorn Hour on host %s: %s' % (host, e.code), logger.WARNING)
|
||||
self._log_warning(u'Problem with Popcorn Hour on host %s: %s' % (host, e.code))
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.log(u'NMJ: Unknown exception: ' + ex(e), logger.ERROR)
|
||||
self._log_error(u'Unknown exception: ' + ex(e))
|
||||
return False
|
||||
|
||||
# try to parse the resulting XML
|
||||
try:
|
||||
et = etree.fromstring(response)
|
||||
et = XmlEtree.fromstring(response)
|
||||
result = et.findtext('returnValue')
|
||||
except SyntaxError as e:
|
||||
logger.log(u'Unable to parse XML returned from the Popcorn Hour: %s' % (e), logger.ERROR)
|
||||
self._log_error(u'Unable to parse XML returned from the Popcorn Hour: %s' % e)
|
||||
return False
|
||||
|
||||
# if the result was a number then consider that an error
|
||||
if int(result) > 0:
|
||||
logger.log(u'Popcorn Hour returned an errorcode: %s' % (result), logger.ERROR)
|
||||
return False
|
||||
else:
|
||||
logger.log(u'NMJ started background scan', logger.MESSAGE)
|
||||
return True
|
||||
|
||||
def _notifyNMJ(self, host=None, database=None, mount=None, force=False):
|
||||
"""
|
||||
Sends a NMJ update command based on the SB config settings
|
||||
|
||||
host: The host to send the command to (optional, defaults to the host in the config)
|
||||
database: The database to use (optional, defaults to the database in the config)
|
||||
mount: The mount URL (optional, defaults to the mount URL in the config)
|
||||
force: If True then the notification will be sent even if NMJ is disabled in the config
|
||||
"""
|
||||
if not sickbeard.USE_NMJ and not force:
|
||||
logger.log('Notification for NMJ scan update not enabled, skipping this notification', logger.DEBUG)
|
||||
if 0 < int(result):
|
||||
self._log_error(u'Popcorn Hour returned an errorcode: %s' % result)
|
||||
return False
|
||||
|
||||
# fill in omitted parameters
|
||||
if not host:
|
||||
host = sickbeard.NMJ_HOST
|
||||
if not database:
|
||||
database = sickbeard.NMJ_DATABASE
|
||||
if not mount:
|
||||
mount = sickbeard.NMJ_MOUNT
|
||||
self._log(u'NMJ started background scan')
|
||||
return True
|
||||
|
||||
logger.log(u'Sending scan command for NMJ ', logger.DEBUG)
|
||||
def _notify(self, host=None, database=None, mount=None, **kwargs):
|
||||
|
||||
return self._sendNMJ(host, database, mount)
|
||||
result = self._send(host, database, mount)
|
||||
|
||||
return self._choose((('Success, started %s', 'Failed to start %s')[not result] % 'the scan update'), result)
|
||||
|
||||
def test_notify(self, host, database, mount):
|
||||
self._testing = True
|
||||
return self._notify(host, database, mount)
|
||||
|
||||
# notify_snatch() Not implemented: Start the scanner when snatched does not make sense
|
||||
# notify_git_update() Not implemented, no reason to start scanner
|
||||
|
||||
def notify_download(self):
|
||||
self._notify()
|
||||
|
||||
def notify_subtitle_download(self):
|
||||
self._notify()
|
||||
|
||||
|
||||
notifier = NMJNotifier
|
||||
|
|
|
@ -17,122 +17,129 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import urllib, urllib2, xml.dom.minidom
|
||||
from xml.dom.minidom import parseString
|
||||
import sickbeard
|
||||
import telnetlib
|
||||
import re
|
||||
import telnetlib
|
||||
import time
|
||||
import urllib
|
||||
import urllib2
|
||||
import xml.dom.minidom
|
||||
from xml.dom.minidom import parseString
|
||||
import xml.etree.cElementTree as XmlEtree
|
||||
|
||||
from sickbeard import logger
|
||||
|
||||
try:
|
||||
import xml.etree.cElementTree as etree
|
||||
except ImportError:
|
||||
import xml.etree.ElementTree as etree
|
||||
import sickbeard
|
||||
from sickbeard.notifiers.generic import BaseNotifier
|
||||
|
||||
|
||||
class NMJv2Notifier:
|
||||
def notify_snatch(self, ep_name):
|
||||
return False
|
||||
#Not implemented: Start the scanner when snatched does not make any sense
|
||||
class NMJv2Notifier(BaseNotifier):
|
||||
|
||||
def notify_download(self, ep_name):
|
||||
self._notifyNMJ()
|
||||
|
||||
def notify_subtitle_download(self, ep_name, lang):
|
||||
self._notifyNMJ()
|
||||
|
||||
def notify_git_update(self, new_version):
|
||||
return False
|
||||
# Not implemented, no reason to start scanner.
|
||||
|
||||
def test_notify(self, host):
|
||||
return self._sendNMJ(host)
|
||||
|
||||
def notify_settings(self, host, dbloc, instance):
|
||||
def notify_settings(self, host, db_loc, instance):
|
||||
"""
|
||||
Retrieves the NMJv2 database location from Popcorn hour
|
||||
|
||||
|
||||
host: The hostname/IP of the Popcorn Hour server
|
||||
dbloc: 'local' for PCH internal harddrive. 'network' for PCH network shares
|
||||
instance: Allows for selection of different DB in case of multiple databases
|
||||
|
||||
|
||||
Returns: True if the settings were retrieved successfully, False otherwise
|
||||
"""
|
||||
result = False
|
||||
try:
|
||||
url_loc = 'http://' + host + ':8008/file_operation?arg0=list_user_storage_file&arg1=&arg2=' + instance + '&arg3=20&arg4=true&arg5=true&arg6=true&arg7=all&arg8=name_asc&arg9=false&arg10=false'
|
||||
req = urllib2.Request(url_loc)
|
||||
handle1 = urllib2.urlopen(req)
|
||||
response1 = handle1.read()
|
||||
xml = parseString(response1)
|
||||
base_url = 'http://%s:8008/' % host
|
||||
|
||||
req = urllib2.Request('%s%s%s' % (base_url, 'file_operation?', urllib.urlencode(
|
||||
dict(arg0='list_user_storage_file', arg1='', arg2=instance, arg3=20, arg4='true', arg5='true',
|
||||
arg6='true', arg7='all', arg8='name_asc', arg9='false', arg10='false'))))
|
||||
handle = urllib2.urlopen(req)
|
||||
response = handle.read()
|
||||
xml_data = parseString(response)
|
||||
|
||||
time.sleep(300.0 / 1000.0)
|
||||
for node in xml.getElementsByTagName('path'):
|
||||
xmlTag = node.toxml();
|
||||
xmlData = xmlTag.replace('<path>', '').replace('</path>', '').replace('[=]', '')
|
||||
url_db = 'http://' + host + ':8008/metadata_database?arg0=check_database&arg1=' + xmlData
|
||||
reqdb = urllib2.Request(url_db)
|
||||
for node in xml_data.getElementsByTagName('path'):
|
||||
xml_tag = node.toxml()
|
||||
|
||||
reqdb = urllib2.Request('%s%s%s' % (base_url, 'metadata_database?', urllib.urlencode(
|
||||
dict(arg0='check_database',
|
||||
arg1=xml_tag.replace('<path>', '').replace('</path>', '').replace('[=]', '')))))
|
||||
handledb = urllib2.urlopen(reqdb)
|
||||
responsedb = handledb.read()
|
||||
xmldb = parseString(responsedb)
|
||||
returnvalue = xmldb.getElementsByTagName('returnValue')[0].toxml().replace('<returnValue>', '').replace(
|
||||
'</returnValue>', '')
|
||||
if returnvalue == '0':
|
||||
DB_path = xmldb.getElementsByTagName('database_path')[0].toxml().replace('<database_path>',
|
||||
'').replace(
|
||||
'</database_path>', '').replace('[=]', '')
|
||||
if dbloc == 'local' and DB_path.find('localhost') > -1:
|
||||
xml_db = parseString(responsedb)
|
||||
|
||||
if '0' == xml_db.getElementsByTagName('returnValue')[0].toxml().replace(
|
||||
'<returnValue>', '').replace('</returnValue>', ''):
|
||||
db_path = xml_db.getElementsByTagName('database_path')[0].toxml().replace(
|
||||
'<database_path>', '').replace('</database_path>', '').replace('[=]', '')
|
||||
if 'local' == db_loc and db_path.find('localhost') > -1:
|
||||
sickbeard.NMJv2_HOST = host
|
||||
sickbeard.NMJv2_DATABASE = DB_path
|
||||
return True
|
||||
if dbloc == 'network' and DB_path.find('://') > -1:
|
||||
sickbeard.NMJv2_DATABASE = db_path
|
||||
result = True
|
||||
if 'network' == db_loc and db_path.find('://') > -1:
|
||||
sickbeard.NMJv2_HOST = host
|
||||
sickbeard.NMJv2_DATABASE = DB_path
|
||||
return True
|
||||
sickbeard.NMJv2_DATABASE = db_path
|
||||
result = True
|
||||
|
||||
except IOError as e:
|
||||
logger.log(u"Warning: Couldn't contact popcorn hour on host %s: %s" % (host, e), logger.WARNING)
|
||||
return False
|
||||
return False
|
||||
self._log_warning(u'Couldn\'t contact popcorn hour on host %s: %s' % (host, e))
|
||||
|
||||
def _sendNMJ(self, host):
|
||||
if result:
|
||||
return '{"message": "Success, NMJ Database found at: %(host)s", "database": "%(database)s"}' % {
|
||||
"host": host, "database": sickbeard.NMJv2_DATABASE}
|
||||
|
||||
return '{"message": "Failed to find NMJ Database at location: %(dbloc)s. ' \
|
||||
'Is the right location selected and PCH running? ", "database": ""}' % {"dbloc": db_loc}
|
||||
|
||||
def _send(self, host=None):
|
||||
"""
|
||||
Sends a NMJ update command to the specified machine
|
||||
|
||||
|
||||
host: The hostname/IP to send the request to (no port)
|
||||
database: The database to send the requst to
|
||||
mount: The mount URL to use (optional)
|
||||
|
||||
|
||||
Returns: True if the request succeeded, False otherwise
|
||||
"""
|
||||
|
||||
#if a host is provided then attempt to open a handle to that URL
|
||||
host = self._choose(host, sickbeard.NMJv2_HOST)
|
||||
|
||||
self._log_debug(u'Sending scan command for NMJ ')
|
||||
|
||||
# if a host is provided then attempt to open a handle to that URL
|
||||
try:
|
||||
url_scandir = 'http://' + host + ':8008/metadata_database?arg0=update_scandir&arg1=' + sickbeard.NMJv2_DATABASE + '&arg2=&arg3=update_all'
|
||||
logger.log(u'NMJ scan update command sent to host: %s' % (host), logger.DEBUG)
|
||||
url_updatedb = 'http://' + host + ':8008/metadata_database?arg0=scanner_start&arg1=' + sickbeard.NMJv2_DATABASE + '&arg2=background&arg3='
|
||||
logger.log(u'Try to mount network drive via url: %s' % (host), logger.DEBUG)
|
||||
base_url = 'http://%s:8008/' % host
|
||||
|
||||
url_scandir = '%s%s%s' % (base_url, 'metadata_database?', urllib.urlencode(
|
||||
dict(arg0='update_scandir', arg1=sickbeard.NMJv2_DATABASE, arg2='', arg3='update_all')))
|
||||
self._log_debug(u'Scan update command sent to host: %s' % host)
|
||||
|
||||
url_updatedb = '%s%s%s' % (base_url, 'metadata_database?', urllib.urlencode(
|
||||
dict(arg0='scanner_start', arg1=sickbeard.NMJv2_DATABASE, arg2='background', arg3='')))
|
||||
self._log_debug(u'Try to mount network drive via url: %s' % host)
|
||||
|
||||
prereq = urllib2.Request(url_scandir)
|
||||
req = urllib2.Request(url_updatedb)
|
||||
|
||||
handle1 = urllib2.urlopen(prereq)
|
||||
response1 = handle1.read()
|
||||
|
||||
time.sleep(300.0 / 1000.0)
|
||||
|
||||
handle2 = urllib2.urlopen(req)
|
||||
response2 = handle2.read()
|
||||
except IOError as e:
|
||||
logger.log(u"Warning: Couldn't contact popcorn hour on host %s: %s" % (host, e), logger.WARNING)
|
||||
self._log_warning(u'Couldn\'t contact popcorn hour on host %s: %s' % (host, e))
|
||||
return False
|
||||
|
||||
try:
|
||||
et = etree.fromstring(response1)
|
||||
et = XmlEtree.fromstring(response1)
|
||||
result1 = et.findtext('returnValue')
|
||||
except SyntaxError as e:
|
||||
logger.log(u'Unable to parse XML returned from the Popcorn Hour: update_scandir, %s' % (e), logger.ERROR)
|
||||
self._log_error(u'Unable to parse XML returned from the Popcorn Hour: update_scandir, %s' % e)
|
||||
return False
|
||||
|
||||
try:
|
||||
et = etree.fromstring(response2)
|
||||
et = XmlEtree.fromstring(response2)
|
||||
result2 = et.findtext('returnValue')
|
||||
except SyntaxError as e:
|
||||
logger.log(u'Unable to parse XML returned from the Popcorn Hour: scanner_start, %s' % (e), logger.ERROR)
|
||||
self._log_error(u'Unable to parse XML returned from the Popcorn Hour: scanner_start, %s' % e)
|
||||
return False
|
||||
|
||||
# if the result was a number then consider that an error
|
||||
|
@ -144,39 +151,38 @@ class NMJv2Notifier:
|
|||
'Database read error',
|
||||
'Open fifo pipe failed',
|
||||
'Read only file system']
|
||||
if int(result1) > 0:
|
||||
if 0 < int(result1):
|
||||
index = error_codes.index(result1)
|
||||
logger.log(u'Popcorn Hour returned an error: %s' % (error_messages[index]), logger.ERROR)
|
||||
return False
|
||||
else:
|
||||
if int(result2) > 0:
|
||||
index = error_codes.index(result2)
|
||||
logger.log(u'Popcorn Hour returned an error: %s' % (error_messages[index]), logger.ERROR)
|
||||
return False
|
||||
else:
|
||||
logger.log(u'NMJv2 started background scan', logger.MESSAGE)
|
||||
return True
|
||||
|
||||
def _notifyNMJ(self, host=None, force=False):
|
||||
"""
|
||||
Sends a NMJ update command based on the SB config settings
|
||||
|
||||
host: The host to send the command to (optional, defaults to the host in the config)
|
||||
database: The database to use (optional, defaults to the database in the config)
|
||||
mount: The mount URL (optional, defaults to the mount URL in the config)
|
||||
force: If True then the notification will be sent even if NMJ is disabled in the config
|
||||
"""
|
||||
if not sickbeard.USE_NMJv2 and not force:
|
||||
logger.log('Notification for NMJ scan update not enabled, skipping this notification', logger.DEBUG)
|
||||
self._log_error(u'Popcorn Hour returned an error: %s' % (error_messages[index]))
|
||||
return False
|
||||
|
||||
# fill in omitted parameters
|
||||
if not host:
|
||||
host = sickbeard.NMJv2_HOST
|
||||
elif 0 < int(result2):
|
||||
index = error_codes.index(result2)
|
||||
self._log_error(u'Popcorn Hour returned an error: %s' % (error_messages[index]))
|
||||
return False
|
||||
|
||||
logger.log(u'Sending scan command for NMJ ', logger.DEBUG)
|
||||
self._log(u'NMJv2 started background scan')
|
||||
return True
|
||||
|
||||
return self._sendNMJ(host)
|
||||
def _notify(self, host=None, **kwargs):
|
||||
|
||||
result = self._send(host)
|
||||
|
||||
return self._choose((('Success, started %s', 'Failed to start %s')[not result] % 'the scan update at "%s"'
|
||||
% host), result)
|
||||
|
||||
def test_notify(self, host):
|
||||
self._testing = True
|
||||
return self._notify(host)
|
||||
|
||||
# notify_snatch() Not implemented: Start the scanner when snatched does not make sense
|
||||
# notify_git_update() Not implemented, no reason to start scanner
|
||||
|
||||
def notify_download(self):
|
||||
self._notify()
|
||||
|
||||
def notify_subtitle_download(self):
|
||||
self._notify()
|
||||
|
||||
|
||||
notifier = NMJv2Notifier
|
||||
|
|
|
@ -20,28 +20,18 @@ import urllib
|
|||
import urllib2
|
||||
import base64
|
||||
import re
|
||||
import xml.etree.cElementTree as XmlEtree
|
||||
|
||||
import sickbeard
|
||||
|
||||
from sickbeard import common, logger
|
||||
from sickbeard.exceptions import ex
|
||||
from sickbeard.encodingKludge import fixStupidEncodings
|
||||
|
||||
try:
|
||||
import xml.etree.cElementTree as etree
|
||||
except ImportError:
|
||||
import elementtree.ElementTree as etree
|
||||
from sickbeard.exceptions import ex
|
||||
from sickbeard.notifiers.generic import Notifier
|
||||
|
||||
|
||||
class PLEXNotifier:
|
||||
class PLEXNotifier(Notifier):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.name = 'PLEX'
|
||||
|
||||
def log(self, msg, level=logger.MESSAGE):
|
||||
|
||||
logger.log(u'%s: %s' % (self.name, msg), level)
|
||||
super(PLEXNotifier, self).__init__()
|
||||
|
||||
def _send_to_plex(self, command, host, username=None, password=None):
|
||||
"""Handles communication to Plex hosts via HTTP API
|
||||
|
@ -53,18 +43,11 @@ class PLEXNotifier:
|
|||
password: Plex API password
|
||||
|
||||
Returns:
|
||||
Returns 'OK' for successful commands or False if there was an error
|
||||
Returns True for successful commands or False if there was an error
|
||||
|
||||
"""
|
||||
|
||||
# fill in omitted parameters
|
||||
if not username:
|
||||
username = sickbeard.PLEX_USERNAME
|
||||
if not password:
|
||||
password = sickbeard.PLEX_PASSWORD
|
||||
|
||||
if not host:
|
||||
self.log(u'No host specified, check your settings', logger.ERROR)
|
||||
self._log_error(u'No host specified, check your settings')
|
||||
return False
|
||||
|
||||
for key in command:
|
||||
|
@ -72,102 +55,30 @@ class PLEXNotifier:
|
|||
command[key] = command[key].encode('utf-8')
|
||||
|
||||
enc_command = urllib.urlencode(command)
|
||||
self.log(u'Encoded API command: ' + enc_command, logger.DEBUG)
|
||||
self._log_debug(u'Encoded API command: ' + enc_command)
|
||||
|
||||
url = 'http://%s/xbmcCmds/xbmcHttp/?%s' % (host, enc_command)
|
||||
try:
|
||||
req = urllib2.Request(url)
|
||||
# if we have a password, use authentication
|
||||
if password:
|
||||
base64string = base64.encodestring('%s:%s' % (username, password))[:-1]
|
||||
authheader = 'Basic %s' % base64string
|
||||
req.add_header('Authorization', authheader)
|
||||
self.log(u'Contacting (with auth header) via url: ' + url, logger.DEBUG)
|
||||
self._log_debug(u'Contacting (with auth header) via url: ' + url)
|
||||
else:
|
||||
self.log(u'Contacting via url: ' + url, logger.DEBUG)
|
||||
self._log_debug(u'Contacting via url: ' + url)
|
||||
|
||||
response = urllib2.urlopen(req)
|
||||
|
||||
result = response.read().decode(sickbeard.SYS_ENCODING)
|
||||
response.close()
|
||||
|
||||
self.log(u'HTTP response: ' + result.replace('\n', ''), logger.DEBUG)
|
||||
# could return result response = re.compile('<html><li>(.+\w)</html>').findall(result)
|
||||
return 'OK'
|
||||
self._log_debug(u'HTTP response: ' + result.replace('\n', ''))
|
||||
return True
|
||||
|
||||
except (urllib2.URLError, IOError) as e:
|
||||
self.log(u'Couldn\'t contact Plex at ' + fixStupidEncodings(url) + ' ' + ex(e), logger.WARNING)
|
||||
self._log_warning(u'Couldn\'t contact Plex at ' + fixStupidEncodings(url) + ' ' + ex(e))
|
||||
return False
|
||||
|
||||
def _notify_pmc(self, message, title='SickGear', host=None, username=None, password=None, force=False):
|
||||
"""Internal wrapper for the notify_snatch and notify_download functions
|
||||
|
||||
Args:
|
||||
message: Message body of the notice to send
|
||||
title: Title of the notice to send
|
||||
host: Plex Media Client(s) host:port
|
||||
username: Plex username
|
||||
password: Plex password
|
||||
force: Used for the Test method to override config safety checks
|
||||
|
||||
Returns:
|
||||
Returns a list results in the format of host:ip:result
|
||||
The result will either be 'OK' or False, this is used to be parsed by the calling function.
|
||||
|
||||
"""
|
||||
|
||||
# suppress notifications if the notifier is disabled but the notify options are checked
|
||||
if not sickbeard.USE_PLEX and not force:
|
||||
return False
|
||||
|
||||
# fill in omitted parameters
|
||||
if not host:
|
||||
host = sickbeard.PLEX_HOST
|
||||
if not username:
|
||||
username = sickbeard.PLEX_USERNAME
|
||||
if not password:
|
||||
password = sickbeard.PLEX_PASSWORD
|
||||
|
||||
result = ''
|
||||
for curHost in [x.strip() for x in host.split(',')]:
|
||||
self.log(u'Sending notification to \'%s\' - %s' % (curHost, message))
|
||||
|
||||
command = {'command': 'ExecBuiltIn',
|
||||
'parameter': 'Notification(%s,%s)' % (title.encode('utf-8'), message.encode('utf-8'))}
|
||||
notify_result = self._send_to_plex(command, curHost, username, password)
|
||||
if notify_result:
|
||||
result += '%s:%s' % (curHost, str(notify_result))
|
||||
|
||||
return result
|
||||
|
||||
##############################################################################
|
||||
# Public functions
|
||||
##############################################################################
|
||||
|
||||
def notify_snatch(self, ep_name):
|
||||
if sickbeard.PLEX_NOTIFY_ONSNATCH:
|
||||
self._notify_pmc(ep_name, common.notifyStrings[common.NOTIFY_SNATCH])
|
||||
|
||||
def notify_download(self, ep_name):
|
||||
if sickbeard.PLEX_NOTIFY_ONDOWNLOAD:
|
||||
self._notify_pmc(ep_name, common.notifyStrings[common.NOTIFY_DOWNLOAD])
|
||||
|
||||
def notify_subtitle_download(self, ep_name, lang):
|
||||
if sickbeard.PLEX_NOTIFY_ONSUBTITLEDOWNLOAD:
|
||||
self._notify_pmc(ep_name + ': ' + lang, common.notifyStrings[common.NOTIFY_SUBTITLE_DOWNLOAD])
|
||||
|
||||
def notify_git_update(self, new_version='??'):
|
||||
if sickbeard.USE_PLEX and sickbeard.PLEX_HOST:
|
||||
update_text = common.notifyStrings[common.NOTIFY_GIT_UPDATE_TEXT]
|
||||
title = common.notifyStrings[common.NOTIFY_GIT_UPDATE]
|
||||
self._notify_pmc(update_text + new_version, title)
|
||||
|
||||
def test_notify(self, host, username, password, server=False):
|
||||
if server:
|
||||
return self.update_library(host=host, username=username, password=password, force=False, test=True)
|
||||
return self._notify_pmc(
|
||||
'This is a test notification from SickGear', 'Test', host, username, password, force=True)
|
||||
|
||||
@staticmethod
|
||||
def _get_host_list(host='', enable_secure=False):
|
||||
"""
|
||||
|
@ -184,7 +95,54 @@ class PLEXNotifier:
|
|||
|
||||
return host_list
|
||||
|
||||
def update_library(self, ep_obj=None, host=None, username=None, password=None, force=True, test=False):
|
||||
def _notify(self, title, body, host=None, username=None, password=None, **kwargs):
|
||||
"""Internal wrapper for the notify_snatch and notify_download functions
|
||||
|
||||
Args:
|
||||
title: Title of the notice to send
|
||||
body: Message body of the notice to send
|
||||
host: Plex Media Client(s) host:port
|
||||
username: Plex username
|
||||
password: Plex password
|
||||
|
||||
Returns:
|
||||
Returns a test result string for ui output while testing, otherwise True if all tests are a success
|
||||
"""
|
||||
host = self._choose(host, sickbeard.PLEX_HOST)
|
||||
username = self._choose(username, sickbeard.PLEX_USERNAME)
|
||||
password = self._choose(password, sickbeard.PLEX_PASSWORD)
|
||||
|
||||
command = {'command': 'ExecBuiltIn',
|
||||
'parameter': 'Notification(%s,%s)' % (title.encode('utf-8'), body.encode('utf-8'))}
|
||||
|
||||
results = []
|
||||
for cur_host in [x.strip() for x in host.split(',')]:
|
||||
cur_host = urllib.unquote_plus(cur_host)
|
||||
self._log(u'Sending notification to \'%s\'' % cur_host)
|
||||
result = self._send_to_plex(command, cur_host, username, password)
|
||||
results += [self._choose(('%s Plex client ... %s' % (('Successful test notice sent to',
|
||||
'Failed test for')[not result], cur_host)), result)]
|
||||
|
||||
return self._choose('<br>\n'.join(results), all(results))
|
||||
|
||||
##############################################################################
|
||||
# Public functions
|
||||
##############################################################################
|
||||
|
||||
def notify_git_update(self, new_version='??', **kwargs):
|
||||
# ensure PMS is setup, this is not for when clients are
|
||||
if sickbeard.PLEX_HOST:
|
||||
super(PLEXNotifier, self).notify_git_update(new_version, **kwargs)
|
||||
|
||||
def test_update_library(self, host=None, username=None, password=None):
|
||||
self._testing = True
|
||||
result = self.update_library(host=urllib.unquote_plus(host), username=username, password=password)
|
||||
if '<br>' == result:
|
||||
result += 'Fail: No valid host set to connect with'
|
||||
return (('Test result for', 'Successful test of')['Fail' not in result]
|
||||
+ ' Plex server(s) ... %s<br>\n' % result)
|
||||
|
||||
def update_library(self, ep_obj=None, host=None, username=None, password=None, **kwargs):
|
||||
"""Handles updating the Plex Media Server host via HTTP API
|
||||
|
||||
Plex Media Server currently only supports updating the whole video library and not a specific path.
|
||||
|
@ -193,126 +151,120 @@ class PLEXNotifier:
|
|||
Returns None for no issue, else a string of host with connection issues
|
||||
|
||||
"""
|
||||
host = self._choose(host, sickbeard.PLEX_SERVER_HOST)
|
||||
if not host:
|
||||
msg = u'No Plex Media Server host specified, check your settings'
|
||||
self._log_debug(msg)
|
||||
return '%sFail: %s' % (('', '<br>')[self._testing], msg)
|
||||
|
||||
if sickbeard.USE_PLEX and sickbeard.PLEX_UPDATE_LIBRARY or test:
|
||||
username = self._choose(username, sickbeard.PLEX_USERNAME)
|
||||
password = self._choose(password, sickbeard.PLEX_PASSWORD)
|
||||
|
||||
if not test:
|
||||
if not sickbeard.PLEX_SERVER_HOST:
|
||||
msg = u'No Plex Media Server host specified, check your settings'
|
||||
self.log(msg, logger.DEBUG)
|
||||
return '%sFail: %s' % (('', '<br />')[test], msg)
|
||||
# if username and password were provided, fetch the auth token from plex.tv
|
||||
token_arg = None
|
||||
if username and password:
|
||||
|
||||
if not host:
|
||||
host = sickbeard.PLEX_SERVER_HOST
|
||||
if not username:
|
||||
username = sickbeard.PLEX_USERNAME
|
||||
if not password:
|
||||
password = sickbeard.PLEX_PASSWORD
|
||||
self._log_debug(u'Fetching plex.tv credentials for user: ' + username)
|
||||
req = urllib2.Request('https://plex.tv/users/sign_in.xml', data='')
|
||||
authheader = 'Basic %s' % base64.encodestring('%s:%s' % (username, password))[:-1]
|
||||
req.add_header('Authorization', authheader)
|
||||
req.add_header('X-Plex-Device-Name', 'SickGear')
|
||||
req.add_header('X-Plex-Product', 'SickGear Notifier')
|
||||
req.add_header('X-Plex-Client-Identifier', '5f48c063eaf379a565ff56c9bb2b401e')
|
||||
req.add_header('X-Plex-Version', '1.0')
|
||||
token_arg = False
|
||||
|
||||
# if username and password were provided, fetch the auth token from plex.tv
|
||||
token_arg = None
|
||||
if username and password:
|
||||
try:
|
||||
response = urllib2.urlopen(req)
|
||||
auth_tree = XmlEtree.parse(response)
|
||||
token = auth_tree.findall('.//authentication-token')[0].text
|
||||
token_arg = '?X-Plex-Token=' + token
|
||||
|
||||
self.log(u'fetching plex.tv credentials for user: ' + username, logger.DEBUG)
|
||||
req = urllib2.Request('https://plex.tv/users/sign_in.xml', data='')
|
||||
authheader = 'Basic %s' % base64.encodestring('%s:%s' % (username, password))[:-1]
|
||||
req.add_header('Authorization', authheader)
|
||||
req.add_header('X-Plex-Device-Name', 'SickGear')
|
||||
req.add_header('X-Plex-Product', 'SickGear Notifier')
|
||||
req.add_header('X-Plex-Client-Identifier', '5f48c063eaf379a565ff56c9bb2b401e')
|
||||
req.add_header('X-Plex-Version', '1.0')
|
||||
token_arg = False
|
||||
except urllib2.URLError as e:
|
||||
self._log(u'Error fetching credentials from plex.tv for user %s: %s' % (username, ex(e)))
|
||||
|
||||
try:
|
||||
response = urllib2.urlopen(req)
|
||||
auth_tree = etree.parse(response)
|
||||
token = auth_tree.findall('.//authentication-token')[0].text
|
||||
token_arg = '?X-Plex-Token=' + token
|
||||
except (ValueError, IndexError) as e:
|
||||
self._log(u'Error parsing plex.tv response: ' + ex(e))
|
||||
|
||||
except urllib2.URLError as e:
|
||||
self.log(u'Error fetching credentials from plex.tv for user %s: %s' % (username, ex(e)))
|
||||
file_location = '' if None is ep_obj else ep_obj.location
|
||||
host_validate = self._get_host_list(host, all([token_arg]))
|
||||
hosts_all = {}
|
||||
hosts_match = {}
|
||||
hosts_failed = []
|
||||
for cur_host in host_validate:
|
||||
response = sickbeard.helpers.getURL(
|
||||
'%s/library/sections%s' % (cur_host, token_arg or ''), timeout=10,
|
||||
mute_connect_err=True, mute_read_timeout=True, mute_connect_timeout=True)
|
||||
if response:
|
||||
response = sickbeard.helpers.parse_xml(response)
|
||||
if not response:
|
||||
hosts_failed.append(cur_host)
|
||||
continue
|
||||
|
||||
except (ValueError, IndexError) as e:
|
||||
self.log(u'Error parsing plex.tv response: ' + ex(e))
|
||||
sections = response.findall('.//Directory')
|
||||
if not sections:
|
||||
self._log(u'Plex Media Server not running on: ' + cur_host)
|
||||
hosts_failed.append(cur_host)
|
||||
continue
|
||||
|
||||
file_location = '' if None is ep_obj else ep_obj.location
|
||||
host_validate = self._get_host_list(host, all([token_arg]))
|
||||
hosts_all = {}
|
||||
hosts_match = {}
|
||||
hosts_failed = []
|
||||
for cur_host in host_validate:
|
||||
response = sickbeard.helpers.getURL(
|
||||
'%s/library/sections%s' % (cur_host, token_arg or ''), timeout=10,
|
||||
mute_connect_err=True, mute_read_timeout=True, mute_connect_timeout=True)
|
||||
if response:
|
||||
response = sickbeard.helpers.parse_xml(response)
|
||||
if not response:
|
||||
hosts_failed.append(cur_host)
|
||||
for section in filter(lambda x: 'show' == x.attrib['type'], sections):
|
||||
if str(section.attrib['key']) in hosts_all:
|
||||
continue
|
||||
keyed_host = [(str(section.attrib['key']), cur_host)]
|
||||
hosts_all.update(keyed_host)
|
||||
if not file_location:
|
||||
continue
|
||||
|
||||
sections = response.findall('.//Directory')
|
||||
if not sections:
|
||||
self.log(u'Plex Media Server not running on: ' + cur_host)
|
||||
hosts_failed.append(cur_host)
|
||||
continue
|
||||
for section_location in section.findall('.//Location'):
|
||||
section_path = re.sub(r'[/\\]+', '/', section_location.attrib['path'].lower())
|
||||
section_path = re.sub(r'^(.{,2})[/\\]', '', section_path)
|
||||
location_path = re.sub(r'[/\\]+', '/', file_location.lower())
|
||||
location_path = re.sub(r'^(.{,2})[/\\]', '', location_path)
|
||||
|
||||
for section in filter(lambda x: 'show' == x.attrib['type'], sections):
|
||||
if str(section.attrib['key']) in hosts_all:
|
||||
continue
|
||||
keyed_host = [(str(section.attrib['key']), cur_host)]
|
||||
hosts_all.update(keyed_host)
|
||||
if not file_location:
|
||||
continue
|
||||
if section_path in location_path:
|
||||
hosts_match.update(keyed_host)
|
||||
break
|
||||
|
||||
for section_location in section.findall('.//Location'):
|
||||
section_path = re.sub(r'[/\\]+', '/', section_location.attrib['path'].lower())
|
||||
section_path = re.sub(r'^(.{,2})[/\\]', '', section_path)
|
||||
location_path = re.sub(r'[/\\]+', '/', file_location.lower())
|
||||
location_path = re.sub(r'^(.{,2})[/\\]', '', location_path)
|
||||
|
||||
if section_path in location_path:
|
||||
hosts_match.update(keyed_host)
|
||||
break
|
||||
|
||||
if not test:
|
||||
hosts_try = (hosts_all.copy(), hosts_match.copy())[any(hosts_match)]
|
||||
host_list = []
|
||||
for section_key, cur_host in hosts_try.items():
|
||||
refresh_result = None
|
||||
if force:
|
||||
refresh_result = sickbeard.helpers.getURL(
|
||||
'%s/library/sections/%s/refresh%s' % (cur_host, section_key, token_arg or ''))
|
||||
if (force and '' == refresh_result) or not force:
|
||||
host_list.append(cur_host)
|
||||
else:
|
||||
hosts_failed.append(cur_host)
|
||||
self.log(u'Error updating library section for Plex Media Server: %s' % cur_host, logger.ERROR)
|
||||
|
||||
if len(hosts_failed) == len(host_validate):
|
||||
self.log(u'No successful Plex host updated')
|
||||
return 'Fail no successful Plex host updated: %s' % ', '.join(host for host in hosts_failed)
|
||||
if not self._testing:
|
||||
hosts_try = (hosts_all.copy(), hosts_match.copy())[any(hosts_match)]
|
||||
host_list = []
|
||||
for section_key, cur_host in hosts_try.items():
|
||||
refresh_result = None
|
||||
if not self._testing:
|
||||
refresh_result = sickbeard.helpers.getURL(
|
||||
'%s/library/sections/%s/refresh%s' % (cur_host, section_key, token_arg or ''))
|
||||
if (not self._testing and '' == refresh_result) or self._testing:
|
||||
host_list.append(cur_host)
|
||||
else:
|
||||
hosts = ', '.join(set(host_list))
|
||||
if len(hosts_match):
|
||||
self.log(u'Hosts updating where TV section paths match the downloaded show: %s' % hosts)
|
||||
else:
|
||||
self.log(u'Updating all hosts with TV sections: %s' % hosts)
|
||||
return ''
|
||||
hosts_failed.append(cur_host)
|
||||
self._log_error(u'Error updating library section for Plex Media Server: %s' % cur_host)
|
||||
|
||||
if len(hosts_failed) == len(host_validate):
|
||||
self._log(u'No successful Plex host updated')
|
||||
return 'Fail no successful Plex host updated: %s' % ', '.join(host for host in hosts_failed)
|
||||
else:
|
||||
hosts = ', '.join(set(host_list))
|
||||
if len(hosts_match):
|
||||
self._log(u'Hosts updating where TV section paths match the downloaded show: %s' % hosts)
|
||||
else:
|
||||
self._log(u'Updating all hosts with TV sections: %s' % hosts)
|
||||
return ''
|
||||
|
||||
hosts = [
|
||||
host.replace('http://', '') for host in filter(lambda x: x.startswith('http:'), hosts_all.values())]
|
||||
secured = [
|
||||
host.replace('https://', '') for host in filter(lambda x: x.startswith('https:'), hosts_all.values())]
|
||||
failed = ', '.join([
|
||||
host.replace('http://', '') for host in filter(lambda x: x.startswith('http:'), hosts_failed)])
|
||||
failed_secured = ', '.join(filter(
|
||||
lambda x: x not in hosts,
|
||||
[host.replace('https://', '') for host in filter(lambda x: x.startswith('https:'), hosts_failed)]))
|
||||
return '<br>' + '<br>'.join(result for result in [
|
||||
('', 'Fail: username/password when fetching credentials from plex.tv')[False is token_arg],
|
||||
('', 'OK (secure connect): %s' % ', '.join(secured))[any(secured)],
|
||||
('', 'OK%s: %s' % ((' (legacy connect)', '')[None is token_arg], ', '.join(hosts)))[any(hosts)],
|
||||
('', 'Fail (secure connect): %s' % failed_secured)[any(failed_secured)],
|
||||
('', 'Fail%s: %s' % ((' (legacy connect)', '')[None is token_arg], failed))[bool(failed)]] if result)
|
||||
|
||||
hosts = [
|
||||
host.replace('http://', '') for host in filter(lambda x: x.startswith('http:'), hosts_all.values())]
|
||||
secured = [
|
||||
host.replace('https://', '') for host in filter(lambda x: x.startswith('https:'), hosts_all.values())]
|
||||
failed = [
|
||||
host.replace('http://', '') for host in filter(lambda x: x.startswith('http:'), hosts_failed)]
|
||||
failed_secured = ', '.join(filter(
|
||||
lambda x: x not in hosts,
|
||||
[host.replace('https://', '') for host in filter(lambda x: x.startswith('https:'), hosts_failed)]))
|
||||
return '<br />' + '<br />'.join(result for result in [
|
||||
('', 'Fail: username/password when fetching credentials from plex.tv')[False is token_arg],
|
||||
('', 'OK (secure connect): %s' % ', '.join(secured))[any(secured)],
|
||||
('', 'OK%s: %s' % ((' (legacy connect)', '')[None is token_arg], ', '.join(hosts)))[any(hosts)],
|
||||
('', 'Fail (secure connect): %s' % failed_secured)[any(failed_secured)],
|
||||
('', 'Fail%s: %s' % ((' (legacy connect)', '')[None is token_arg], failed))[any(failed)]] if result)
|
||||
|
||||
notifier = PLEXNotifier
|
||||
|
|
|
@ -16,94 +16,50 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from lib.six import moves
|
||||
|
||||
import socket
|
||||
from ssl import SSLError
|
||||
from urllib import urlencode
|
||||
|
||||
try:
|
||||
# this only exists in 2.6
|
||||
from ssl import SSLError
|
||||
except ImportError:
|
||||
# make a fake one since I don't know what it is supposed to be in 2.5
|
||||
class SSLError(Exception):
|
||||
pass
|
||||
|
||||
import sickbeard
|
||||
from sickbeard.notifiers.generic import Notifier
|
||||
|
||||
from sickbeard import logger, common
|
||||
from lib.six import moves
|
||||
|
||||
|
||||
class ProwlNotifier:
|
||||
def test_notify(self, prowl_api, prowl_priority):
|
||||
return self._sendProwl(prowl_api, prowl_priority, event='Test',
|
||||
message='Testing Prowl settings from SickGear', force=True)
|
||||
class ProwlNotifier(Notifier):
|
||||
|
||||
def notify_snatch(self, ep_name):
|
||||
if sickbeard.PROWL_NOTIFY_ONSNATCH:
|
||||
self._sendProwl(prowl_api=None, prowl_priority=None, event=common.notifyStrings[common.NOTIFY_SNATCH],
|
||||
message=ep_name)
|
||||
def _notify(self, title, body, prowl_api=None, prowl_priority=None, **kwargs):
|
||||
|
||||
def notify_download(self, ep_name):
|
||||
if sickbeard.PROWL_NOTIFY_ONDOWNLOAD:
|
||||
self._sendProwl(prowl_api=None, prowl_priority=None, event=common.notifyStrings[common.NOTIFY_DOWNLOAD],
|
||||
message=ep_name)
|
||||
prowl_api = self._choose(prowl_api, sickbeard.PROWL_API)
|
||||
prowl_priority = self._choose(prowl_priority, sickbeard.PROWL_PRIORITY)
|
||||
|
||||
def notify_subtitle_download(self, ep_name, lang):
|
||||
if sickbeard.PROWL_NOTIFY_ONSUBTITLEDOWNLOAD:
|
||||
self._sendProwl(prowl_api=None, prowl_priority=None,
|
||||
event=common.notifyStrings[common.NOTIFY_SUBTITLE_DOWNLOAD], message=ep_name + ': ' + lang)
|
||||
|
||||
def notify_git_update(self, new_version = '??'):
|
||||
if sickbeard.USE_PROWL:
|
||||
update_text=common.notifyStrings[common.NOTIFY_GIT_UPDATE_TEXT]
|
||||
title=common.notifyStrings[common.NOTIFY_GIT_UPDATE]
|
||||
self._sendProwl(prowl_api=None, prowl_priority=None,
|
||||
event=title, message=update_text + new_version)
|
||||
|
||||
def _sendProwl(self, prowl_api=None, prowl_priority=None, event=None, message=None, force=False):
|
||||
|
||||
if not sickbeard.USE_PROWL and not force:
|
||||
return False
|
||||
|
||||
if prowl_api == None:
|
||||
prowl_api = sickbeard.PROWL_API
|
||||
|
||||
if prowl_priority == None:
|
||||
prowl_priority = sickbeard.PROWL_PRIORITY
|
||||
|
||||
title = 'SickGear'
|
||||
|
||||
logger.log('PROWL: Sending notice with details: event="%s", message="%s", priority=%s, api=%s' % (event, message, prowl_priority, prowl_api), logger.DEBUG)
|
||||
self._log_debug('Sending notice with details: title="%s", message="%s", priority=%s, api=%s' % (
|
||||
title, body, prowl_priority, prowl_api))
|
||||
|
||||
http_handler = moves.http_client.HTTPSConnection('api.prowlapp.com')
|
||||
|
||||
data = {'apikey': prowl_api,
|
||||
'application': title,
|
||||
'event': event,
|
||||
'description': message.encode('utf-8'),
|
||||
'priority': prowl_priority}
|
||||
data = dict(apikey=prowl_api, application='SickGear', event=title,
|
||||
description=body.encode('utf-8'), priority=prowl_priority)
|
||||
|
||||
try:
|
||||
http_handler.request('POST',
|
||||
'/publicapi/add',
|
||||
headers={'Content-type': 'application/x-www-form-urlencoded'},
|
||||
body=urlencode(data))
|
||||
http_handler.request('POST', '/publicapi/add',
|
||||
headers={'Content-type': 'application/x-www-form-urlencoded'}, body=urlencode(data))
|
||||
except (SSLError, moves.http_client.HTTPException, socket.error):
|
||||
logger.log(u'Prowl notification failed.', logger.ERROR)
|
||||
return False
|
||||
response = http_handler.getresponse()
|
||||
request_status = response.status
|
||||
|
||||
if request_status == 200:
|
||||
logger.log(u'Prowl notifications sent.', logger.MESSAGE)
|
||||
return True
|
||||
elif request_status == 401:
|
||||
logger.log(u'Prowl authentication failed: %s' % response.reason, logger.ERROR)
|
||||
return False
|
||||
result = 'Connection failed'
|
||||
self._log_error(result)
|
||||
else:
|
||||
logger.log(u'Prowl notification failed.', logger.ERROR)
|
||||
return False
|
||||
response = http_handler.getresponse()
|
||||
result = None
|
||||
|
||||
if 200 != response.status:
|
||||
if 401 == response.status:
|
||||
result = u'Authentication, %s (bad API key?)' % response.reason
|
||||
else:
|
||||
result = 'Http response code "%s"' % response.status
|
||||
|
||||
self._log_error(result)
|
||||
|
||||
return self._choose((True, 'Failed to send notification: %s' % result)[bool(result)], not bool(result))
|
||||
|
||||
|
||||
notifier = ProwlNotifier
|
||||
|
|
|
@ -17,83 +17,47 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from lib.six import moves
|
||||
|
||||
import socket
|
||||
from urllib import urlencode
|
||||
from ssl import SSLError
|
||||
from urllib import urlencode
|
||||
|
||||
import sickbeard
|
||||
from sickbeard import logger, common
|
||||
from sickbeard.notifiers.generic import Notifier
|
||||
|
||||
from lib.six import moves
|
||||
|
||||
|
||||
class PushalotNotifier:
|
||||
def test_notify(self, pushalot_authorizationtoken):
|
||||
return self._sendPushalot(pushalot_authorizationtoken, event='Test',
|
||||
message='Testing Pushalot settings from SickGear', force=True)
|
||||
class PushalotNotifier(Notifier):
|
||||
|
||||
def notify_snatch(self, ep_name):
|
||||
if sickbeard.PUSHALOT_NOTIFY_ONSNATCH:
|
||||
self._sendPushalot(pushalot_authorizationtoken=None, event=common.notifyStrings[common.NOTIFY_SNATCH],
|
||||
message=ep_name)
|
||||
def _notify(self, title, body, pushalot_auth_token=None, **kwargs):
|
||||
|
||||
def notify_download(self, ep_name):
|
||||
if sickbeard.PUSHALOT_NOTIFY_ONDOWNLOAD:
|
||||
self._sendPushalot(pushalot_authorizationtoken=None, event=common.notifyStrings[common.NOTIFY_DOWNLOAD],
|
||||
message=ep_name)
|
||||
pushalot_auth_token = self._choose(pushalot_auth_token, sickbeard.PUSHALOT_AUTHORIZATIONTOKEN)
|
||||
|
||||
def notify_subtitle_download(self, ep_name, lang):
|
||||
if sickbeard.PUSHALOT_NOTIFY_ONSUBTITLEDOWNLOAD:
|
||||
self._sendPushalot(pushalot_authorizationtoken=None,
|
||||
event=common.notifyStrings[common.NOTIFY_SUBTITLE_DOWNLOAD],
|
||||
message=ep_name + ': ' + lang)
|
||||
|
||||
def notify_git_update(self, new_version = '??'):
|
||||
if sickbeard.USE_PUSHALOT:
|
||||
update_text=common.notifyStrings[common.NOTIFY_GIT_UPDATE_TEXT]
|
||||
title=common.notifyStrings[common.NOTIFY_GIT_UPDATE]
|
||||
self._sendPushalot(pushalot_authorizationtoken=None,
|
||||
event=title,
|
||||
message=update_text + new_version)
|
||||
|
||||
def _sendPushalot(self, pushalot_authorizationtoken=None, event=None, message=None, force=False):
|
||||
|
||||
if not sickbeard.USE_PUSHALOT and not force:
|
||||
return False
|
||||
|
||||
if pushalot_authorizationtoken == None:
|
||||
pushalot_authorizationtoken = sickbeard.PUSHALOT_AUTHORIZATIONTOKEN
|
||||
|
||||
logger.log(u'Pushalot event: ' + event, logger.DEBUG)
|
||||
logger.log(u'Pushalot message: ' + message, logger.DEBUG)
|
||||
logger.log(u'Pushalot api: ' + pushalot_authorizationtoken, logger.DEBUG)
|
||||
self._log_debug(u'Title: %s, Message: %s, API: %s' % (title, body, pushalot_auth_token))
|
||||
|
||||
http_handler = moves.http_client.HTTPSConnection('pushalot.com')
|
||||
|
||||
data = {'AuthorizationToken': pushalot_authorizationtoken,
|
||||
'Title': event.encode('utf-8'),
|
||||
'Body': message.encode('utf-8')}
|
||||
|
||||
try:
|
||||
http_handler.request('POST',
|
||||
'/api/sendmessage',
|
||||
headers={'Content-type': 'application/x-www-form-urlencoded'},
|
||||
body=urlencode(data))
|
||||
http_handler.request('POST', '/api/sendmessage',
|
||||
body=urlencode(dict(Title=title.encode('utf-8'), Body=body.encode('utf-8'),
|
||||
AuthorizationToken=pushalot_auth_token)),
|
||||
headers={'Content-type': 'application/x-www-form-urlencoded'})
|
||||
except (SSLError, moves.http_client.HTTPException, socket.error):
|
||||
logger.log(u'Pushalot notification failed.', logger.ERROR)
|
||||
return False
|
||||
response = http_handler.getresponse()
|
||||
request_status = response.status
|
||||
|
||||
if request_status == 200:
|
||||
logger.log(u'Pushalot notifications sent.', logger.DEBUG)
|
||||
return True
|
||||
elif request_status == 410:
|
||||
logger.log(u'Pushalot authentication failed: %s' % response.reason, logger.ERROR)
|
||||
return False
|
||||
result = 'Connection failed'
|
||||
self._log_error(result)
|
||||
else:
|
||||
logger.log(u'Pushalot notification failed.', logger.ERROR)
|
||||
return False
|
||||
response = http_handler.getresponse()
|
||||
result = None
|
||||
|
||||
if 200 != response.status:
|
||||
if 410 == response.status:
|
||||
result = u'Authentication, %s (bad API key?)' % response.reason
|
||||
else:
|
||||
result = 'Http response code "%s"' % response.status
|
||||
|
||||
self._log_error(result)
|
||||
|
||||
return self._choose((True, 'Failed to send notification: %s' % result)[bool(result)], not bool(result))
|
||||
|
||||
|
||||
notifier = PushalotNotifier
|
||||
|
|
|
@ -18,102 +18,63 @@
|
|||
|
||||
import base64
|
||||
import simplejson as json
|
||||
import sickbeard
|
||||
|
||||
from sickbeard import logger
|
||||
from sickbeard.common import notifyStrings, NOTIFY_SNATCH, NOTIFY_DOWNLOAD, NOTIFY_SUBTITLE_DOWNLOAD, NOTIFY_GIT_UPDATE, NOTIFY_GIT_UPDATE_TEXT
|
||||
import sickbeard
|
||||
from sickbeard.notifiers.generic import Notifier
|
||||
|
||||
import requests
|
||||
|
||||
PUSHAPI_ENDPOINT = 'https://api.pushbullet.com/v2/pushes'
|
||||
DEVICEAPI_ENDPOINT = 'https://api.pushbullet.com/v2/devices'
|
||||
|
||||
|
||||
class PushbulletNotifier:
|
||||
class PushbulletNotifier(Notifier):
|
||||
|
||||
def get_devices(self, accessToken=None):
|
||||
@staticmethod
|
||||
def get_devices(access_token=None):
|
||||
# fill in omitted parameters
|
||||
if not accessToken:
|
||||
accessToken = sickbeard.PUSHBULLET_ACCESS_TOKEN
|
||||
if not access_token:
|
||||
access_token = sickbeard.PUSHBULLET_ACCESS_TOKEN
|
||||
|
||||
# get devices from pushbullet
|
||||
try:
|
||||
base64string = base64.encodestring('%s:%s' % (accessToken, ''))[:-1]
|
||||
headers = {'Authorization': 'Basic %s' % base64string}
|
||||
base64string = base64.encodestring('%s:%s' % (access_token, ''))[:-1]
|
||||
headers = dict(Authorization='Basic %s' % base64string)
|
||||
return requests.get(DEVICEAPI_ENDPOINT, headers=headers).text
|
||||
except Exception as e:
|
||||
return json.dumps({'error': {'message': 'Error failed to connect: %s' % e}})
|
||||
except (StandardError, Exception):
|
||||
return json.dumps(dict(error=dict(message='Error failed to connect')))
|
||||
|
||||
def _sendPushbullet(self, title, body, accessToken, device_iden):
|
||||
|
||||
# build up the URL and parameters
|
||||
payload = {
|
||||
'type': 'note',
|
||||
'title': title,
|
||||
'body': body.strip().encode('utf-8'),
|
||||
'device_iden': device_iden
|
||||
}
|
||||
|
||||
# send the request to pushbullet
|
||||
try:
|
||||
base64string = base64.encodestring('%s:%s' % (accessToken, ''))[:-1]
|
||||
headers = {'Authorization': 'Basic %s' % base64string, 'Content-Type': 'application/json'}
|
||||
result = requests.post(PUSHAPI_ENDPOINT, headers=headers, data=json.dumps(payload))
|
||||
result.raise_for_status()
|
||||
except Exception as e:
|
||||
try:
|
||||
e = result.json()['error']['message']
|
||||
except:
|
||||
pass
|
||||
logger.log(u'PUSHBULLET: %s' % e, logger.WARNING)
|
||||
return 'Error sending Pushbullet notification: %s' % e
|
||||
|
||||
logger.log(u'PUSHBULLET: Pushbullet notification succeeded', logger.MESSAGE)
|
||||
return 'Pushbullet notification succeeded'
|
||||
|
||||
def _notifyPushbullet(self, title, body, accessToken=None, device_iden=None, force=False):
|
||||
def _notify(self, title, body, access_token=None, device_iden=None, **kwargs):
|
||||
"""
|
||||
Sends a pushbullet notification based on the provided info or SG config
|
||||
|
||||
title: The title of the notification to send
|
||||
body: The body string to send
|
||||
accessToken: The access token to grant access
|
||||
access_token: The access token to grant access
|
||||
device_iden: The iden of a specific target, if none provided send to all devices
|
||||
force: If True then the notification will be sent even if Pushbullet is disabled in the config
|
||||
"""
|
||||
access_token = self._choose(access_token, sickbeard.PUSHBULLET_ACCESS_TOKEN)
|
||||
device_iden = self._choose(device_iden, sickbeard.PUSHBULLET_DEVICE_IDEN)
|
||||
|
||||
# suppress notifications if the notifier is disabled but the notify options are checked
|
||||
if not sickbeard.USE_PUSHBULLET and not force:
|
||||
return False
|
||||
# send the request to Pushbullet
|
||||
result = None
|
||||
try:
|
||||
base64string = base64.encodestring('%s:%s' % (access_token, ''))[:-1]
|
||||
headers = {'Authorization': 'Basic %s' % base64string, 'Content-Type': 'application/json'}
|
||||
resp = requests.post(PUSHAPI_ENDPOINT, headers=headers,
|
||||
data=json.dumps(dict(
|
||||
type='note', title=title, body=body.strip().encode('utf-8'),
|
||||
device_iden=device_iden)))
|
||||
resp.raise_for_status()
|
||||
except (StandardError, Exception):
|
||||
try:
|
||||
# noinspection PyUnboundLocalVariable
|
||||
result = resp.json()['error']['message']
|
||||
except (StandardError, Exception):
|
||||
result = 'no response'
|
||||
self._log_warning(u'%s' % result)
|
||||
|
||||
# fill in omitted parameters
|
||||
if not accessToken:
|
||||
accessToken = sickbeard.PUSHBULLET_ACCESS_TOKEN
|
||||
if not device_iden:
|
||||
device_iden = sickbeard.PUSHBULLET_DEVICE_IDEN
|
||||
return self._choose((True, 'Failed to send notification: %s' % result)[bool(result)], not bool(result))
|
||||
|
||||
logger.log(u'PUSHBULLET: Sending notice with details: "%s - %s", device_iden: %s' % (title, body, device_iden), logger.DEBUG)
|
||||
|
||||
return self._sendPushbullet(title, body, accessToken, device_iden)
|
||||
|
||||
def notify_snatch(self, ep_name):
|
||||
if sickbeard.PUSHBULLET_NOTIFY_ONSNATCH:
|
||||
self._notifyPushbullet(notifyStrings[NOTIFY_SNATCH], ep_name)
|
||||
|
||||
def notify_download(self, ep_name):
|
||||
if sickbeard.PUSHBULLET_NOTIFY_ONDOWNLOAD:
|
||||
self._notifyPushbullet(notifyStrings[NOTIFY_DOWNLOAD], ep_name)
|
||||
|
||||
def notify_subtitle_download(self, ep_name, lang):
|
||||
if sickbeard.PUSHBULLET_NOTIFY_ONSUBTITLEDOWNLOAD:
|
||||
self._notifyPushbullet(notifyStrings[NOTIFY_SUBTITLE_DOWNLOAD], ep_name + ': ' + lang)
|
||||
|
||||
def notify_git_update(self, new_version = '??'):
|
||||
if sickbeard.USE_PUSHBULLET:
|
||||
update_text=notifyStrings[NOTIFY_GIT_UPDATE_TEXT]
|
||||
title=notifyStrings[NOTIFY_GIT_UPDATE]
|
||||
self._notifyPushbullet(title, update_text + new_version)
|
||||
|
||||
def test_notify(self, accessToken, device_iden):
|
||||
return self._notifyPushbullet('Test', 'This is a test notification from SickGear', accessToken, device_iden, force=True)
|
||||
|
||||
notifier = PushbulletNotifier
|
||||
|
|
|
@ -18,171 +18,114 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import base64
|
||||
import socket
|
||||
import time
|
||||
import urllib
|
||||
import urllib2
|
||||
import time
|
||||
import socket
|
||||
import base64
|
||||
|
||||
import sickbeard
|
||||
from sickbeard import logger
|
||||
from sickbeard.common import notifyStrings, NOTIFY_SNATCH, NOTIFY_DOWNLOAD, NOTIFY_SUBTITLE_DOWNLOAD, NOTIFY_GIT_UPDATE, NOTIFY_GIT_UPDATE_TEXT
|
||||
from sickbeard.exceptions import ex
|
||||
from sickbeard.notifiers.generic import Notifier
|
||||
|
||||
API_URL = 'https://api.pushover.net/1/messages.json'
|
||||
DEVICE_URL = 'https://api.pushover.net/1/users/validate.json'
|
||||
|
||||
|
||||
class PushoverNotifier:
|
||||
class PushoverNotifier(Notifier):
|
||||
|
||||
def get_devices(self, userKey=None, apiKey=None):
|
||||
# fill in omitted parameters
|
||||
if not userKey:
|
||||
userKey = sickbeard.PUSHOVER_USERKEY
|
||||
if not apiKey:
|
||||
apiKey = sickbeard.PUSHOVER_APIKEY
|
||||
def get_devices(self, user_key=None, api_key=None):
|
||||
|
||||
data = urllib.urlencode({
|
||||
'token': apiKey,
|
||||
'user': userKey
|
||||
})
|
||||
user_key = self._choose(user_key, sickbeard.PUSHOVER_USERKEY)
|
||||
api_key = self._choose(api_key, sickbeard.PUSHOVER_APIKEY)
|
||||
|
||||
data = urllib.urlencode(dict(token=api_key, user=user_key))
|
||||
|
||||
# get devices from pushover
|
||||
result = False
|
||||
try:
|
||||
req = urllib2.Request(DEVICE_URL)
|
||||
handle = urllib2.urlopen(req, data)
|
||||
if handle:
|
||||
result = handle.read()
|
||||
handle.close()
|
||||
return result
|
||||
except urllib2.URLError:
|
||||
return None
|
||||
except socket.timeout:
|
||||
return None
|
||||
except (urllib2.URLError, socket.timeout):
|
||||
pass
|
||||
|
||||
def _sendPushover(self, title, msg, userKey, apiKey, priority, device, sound):
|
||||
return ('{}', result)[bool(result)]
|
||||
|
||||
def _notify(self, title, body, user_key=None, api_key=None, priority=None, device=None, sound=None, **kwargs):
|
||||
"""
|
||||
Sends a pushover notification to the address provided
|
||||
|
||||
msg: The message to send (unicode)
|
||||
|
||||
title: The title of the message
|
||||
userKey: The pushover user id to send the message to (or to subscribe with)
|
||||
|
||||
msg: The message to send (unicode)
|
||||
user_key: The pushover user id to send the message to (or to subscribe with)
|
||||
|
||||
returns: True if the message succeeded, False otherwise
|
||||
"""
|
||||
|
||||
# fill in omitted parameters
|
||||
if not userKey:
|
||||
userKey = sickbeard.PUSHOVER_USERKEY
|
||||
if not apiKey:
|
||||
apiKey = sickbeard.PUSHOVER_APIKEY
|
||||
user_key = self._choose(user_key, sickbeard.PUSHOVER_USERKEY)
|
||||
api_key = self._choose(api_key, sickbeard.PUSHOVER_APIKEY)
|
||||
priority = self._choose(priority, sickbeard.PUSHOVER_PRIORITY)
|
||||
device = self._choose(device, sickbeard.PUSHOVER_DEVICE)
|
||||
sound = self._choose(sound, sickbeard.PUSHOVER_SOUND)
|
||||
|
||||
# build up the URL and parameters
|
||||
msg = msg.strip()
|
||||
|
||||
data = urllib.urlencode({
|
||||
'token': apiKey,
|
||||
'title': title,
|
||||
'user': userKey,
|
||||
'message': msg.encode('utf-8'),
|
||||
'priority': priority,
|
||||
'device': device,
|
||||
'sound': sound,
|
||||
'timestamp': int(time.time())
|
||||
})
|
||||
params = dict(title=title, message=body.strip().encode('utf-8'), user=user_key, timestamp=int(time.time()))
|
||||
if api_key:
|
||||
params.update(token=api_key)
|
||||
if priority:
|
||||
params.update(priority=priority)
|
||||
if not device:
|
||||
params.update(device=device)
|
||||
if not sound:
|
||||
params.update(sound=sound)
|
||||
|
||||
# send the request to pushover
|
||||
result = None
|
||||
try:
|
||||
req = urllib2.Request(API_URL)
|
||||
handle = urllib2.urlopen(req, data)
|
||||
handle = urllib2.urlopen(req, urllib.urlencode(params))
|
||||
handle.close()
|
||||
|
||||
except urllib2.URLError as e:
|
||||
# HTTP status 404 if the provided email address isn't a Pushover user.
|
||||
if e.code == 404:
|
||||
logger.log(u'PUSHOVER: Username is wrong/not a Pushover email. Pushover will send an email to it', logger.WARNING)
|
||||
return False
|
||||
if 404 == e.code:
|
||||
result = 'Username is wrong/not a Pushover email. Pushover will send an email to it'
|
||||
self._log_warning(result)
|
||||
|
||||
# For HTTP status code 401's, it is because you are passing in either an invalid token, or the user has not added your service.
|
||||
elif e.code == 401:
|
||||
# For HTTP status code 401's, it is because you are passing in either an invalid token,
|
||||
# or the user has not added your service.
|
||||
elif 401 == e.code:
|
||||
|
||||
# HTTP status 401 if the user doesn't have the service added
|
||||
subscribeNote = self._sendPushover(title, msg, userKey)
|
||||
if subscribeNote:
|
||||
logger.log(u'PUSHOVER: Subscription sent', logger.DEBUG)
|
||||
return True
|
||||
subscribe_note = self._send_pushover(title, body, user_key)
|
||||
if subscribe_note:
|
||||
self._log_debug('Subscription sent')
|
||||
# return True
|
||||
else:
|
||||
logger.log(u'PUSHOVER: Subscription could not be sent', logger.ERROR)
|
||||
return False
|
||||
result = 'Subscription could not be sent'
|
||||
self._log_error(result)
|
||||
else:
|
||||
# If you receive an HTTP status code of 400, it is because you failed to send the proper parameters
|
||||
if 400 == e.code:
|
||||
result = 'Wrong data sent to Pushover'
|
||||
|
||||
# If you receive an HTTP status code of 400, it is because you failed to send the proper parameters
|
||||
elif e.code == 400:
|
||||
logger.log(u'PUSHOVER: Wrong data sent to Pushover', logger.ERROR)
|
||||
return False
|
||||
# If you receive a HTTP status code of 429,
|
||||
# it is because the message limit has been reached (free limit is 7,500)
|
||||
elif 429 == e.code:
|
||||
result = 'API message limit reached - try a different API key'
|
||||
|
||||
# If you receive a HTTP status code of 429, it is because the message limit has been reached (free limit is 7,500)
|
||||
elif e.code == 429:
|
||||
logger.log(u'PUSHOVER: API message limit reached - try a different API key', logger.ERROR)
|
||||
return False
|
||||
# If you receive a HTTP status code of 500, service is unavailable
|
||||
elif 500 == e.code:
|
||||
result = 'Unable to connect to API, service unavailable'
|
||||
|
||||
# If you receive a HTTP status code of 500, service is unavailable
|
||||
elif e.code == 500:
|
||||
logger.log(u'PUSHOVER: Unable to connect to API, service unavailable', logger.ERROR)
|
||||
return False
|
||||
else:
|
||||
result = 'Http response code "%s"' % response.status
|
||||
|
||||
logger.log(u'PUSHOVER: Notification successful.', logger.MESSAGE)
|
||||
return True
|
||||
self._log_error(result)
|
||||
|
||||
def _notifyPushover(self, title, message, userKey=None, apiKey=None, priority=None, device=None, sound=None, force=False):
|
||||
"""
|
||||
Sends a pushover notification based on the provided info or SG config
|
||||
return self._choose((True, 'Failed to send notification: %s' % result)[bool(result)], not bool(result))
|
||||
|
||||
title: The title of the notification to send
|
||||
message: The message string to send
|
||||
userKey: The userKey to send the notification to
|
||||
force: Enforce sending, for instance for testing
|
||||
"""
|
||||
|
||||
# suppress notifications if the notifier is disabled but the notify options are checked
|
||||
if not sickbeard.USE_PUSHOVER and not force:
|
||||
logger.log(u'PUSHOVER: Notifications not enabled, skipping this notification', logger.DEBUG)
|
||||
return False
|
||||
|
||||
# fill in omitted parameters
|
||||
if not userKey:
|
||||
userKey = sickbeard.PUSHOVER_USERKEY
|
||||
if not apiKey:
|
||||
apiKey = sickbeard.PUSHOVER_APIKEY
|
||||
if not priority:
|
||||
priority = sickbeard.PUSHOVER_PRIORITY
|
||||
if not device:
|
||||
device = sickbeard.PUSHOVER_DEVICE
|
||||
if not sound:
|
||||
sound = sickbeard.PUSHOVER_SOUND
|
||||
|
||||
logger.log(u'PUSHOVER: Sending notice with details: %s - %s, priority: %s, device: %s, sound: %s' % (title, message, priority, device, sound), logger.DEBUG)
|
||||
|
||||
return self._sendPushover(title, message, userKey, apiKey, priority, device, sound)
|
||||
|
||||
def test_notify(self, userKey, apiKey, priority, device, sound):
|
||||
return self._notifyPushover('Test', 'This is a test notification from SickGear', userKey, apiKey, priority, device, sound, force=True)
|
||||
|
||||
def notify_snatch(self, ep_name):
|
||||
if sickbeard.PUSHOVER_NOTIFY_ONSNATCH:
|
||||
self._notifyPushover(notifyStrings[NOTIFY_SNATCH], ep_name)
|
||||
|
||||
def notify_download(self, ep_name):
|
||||
if sickbeard.PUSHOVER_NOTIFY_ONDOWNLOAD:
|
||||
self._notifyPushover(notifyStrings[NOTIFY_DOWNLOAD], ep_name)
|
||||
|
||||
def notify_subtitle_download(self, ep_name, lang):
|
||||
if sickbeard.PUSHOVER_NOTIFY_ONSUBTITLEDOWNLOAD:
|
||||
self._notifyPushover(notifyStrings[NOTIFY_SUBTITLE_DOWNLOAD], ep_name + ': ' + lang)
|
||||
|
||||
def notify_git_update(self, new_version = '??'):
|
||||
if sickbeard.USE_PUSHOVER:
|
||||
update_text=notifyStrings[NOTIFY_GIT_UPDATE_TEXT]
|
||||
title=notifyStrings[NOTIFY_GIT_UPDATE]
|
||||
self._notifyPushover(title, update_text + new_version)
|
||||
|
||||
notifier = PushoverNotifier
|
||||
|
|
|
@ -17,93 +17,78 @@
|
|||
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import sickbeard
|
||||
|
||||
from urllib import urlencode
|
||||
from urllib2 import Request, urlopen, HTTPError
|
||||
|
||||
from sickbeard import logger
|
||||
from sickbeard.exceptions import ex
|
||||
import sickbeard
|
||||
# noinspection PyPep8Naming
|
||||
from sickbeard import encodingKludge as ek
|
||||
from sickbeard.exceptions import ex
|
||||
from sickbeard.notifiers.generic import BaseNotifier
|
||||
|
||||
|
||||
class pyTivoNotifier:
|
||||
def notify_snatch(self, ep_name):
|
||||
pass
|
||||
class PyTivoNotifier(BaseNotifier):
|
||||
|
||||
def notify_download(self, ep_name):
|
||||
pass
|
||||
|
||||
def notify_subtitle_download(self, ep_name, lang):
|
||||
pass
|
||||
|
||||
def notify_git_update(self, new_version):
|
||||
pass
|
||||
|
||||
def update_library(self, ep_obj):
|
||||
|
||||
# Values from config
|
||||
|
||||
if not sickbeard.USE_PYTIVO:
|
||||
return False
|
||||
def update_library(self, ep_obj=None, **kwargs):
|
||||
|
||||
host = sickbeard.PYTIVO_HOST
|
||||
shareName = sickbeard.PYTIVO_SHARE_NAME
|
||||
share_name = sickbeard.PYTIVO_SHARE_NAME
|
||||
tsn = sickbeard.PYTIVO_TIVO_NAME
|
||||
|
||||
# There are two more values required, the container and file.
|
||||
#
|
||||
#
|
||||
# container: The share name, show name and season
|
||||
#
|
||||
# file: The file name
|
||||
#
|
||||
#
|
||||
# Some slicing and dicing of variables is required to get at these values.
|
||||
#
|
||||
# There might be better ways to arrive at the values, but this is the best I have been able to
|
||||
# There might be better ways to arrive at the values, but this is the best I have been able to
|
||||
# come up with.
|
||||
#
|
||||
|
||||
|
||||
# Calculated values
|
||||
|
||||
showPath = ep_obj.show.location
|
||||
showName = ep_obj.show.name
|
||||
rootShowAndSeason = ek.ek(os.path.dirname, ep_obj.location)
|
||||
absPath = ep_obj.location
|
||||
show_path = ep_obj.show.location
|
||||
show_name = ep_obj.show.name
|
||||
root_show_and_season = ek.ek(os.path.dirname, ep_obj.location)
|
||||
abs_path = ep_obj.location
|
||||
|
||||
# Some show names have colons in them which are illegal in a path location, so strip them out.
|
||||
# (Are there other characters?)
|
||||
showName = showName.replace(':', '')
|
||||
show_name = show_name.replace(':', '')
|
||||
|
||||
root = showPath.replace(showName, '')
|
||||
showAndSeason = rootShowAndSeason.replace(root, '')
|
||||
root = show_path.replace(show_name, '')
|
||||
show_and_season = root_show_and_season.replace(root, '')
|
||||
|
||||
container = shareName + '/' + showAndSeason
|
||||
file = '/' + absPath.replace(root, '')
|
||||
container = share_name + '/' + show_and_season
|
||||
file_path = '/' + abs_path.replace(root, '')
|
||||
|
||||
# Finally create the url and make request
|
||||
requestUrl = 'http://' + host + '/TiVoConnect?' + urlencode(
|
||||
{'Command': 'Push', 'Container': container, 'File': file, 'tsn': tsn})
|
||||
request_url = 'http://%s/TiVoConnect?%s' % (host, urlencode(
|
||||
dict(Command='Push', Container=container, File=file_path, tsn=tsn)))
|
||||
|
||||
logger.log(u'pyTivo notification: Requesting ' + requestUrl, logger.DEBUG)
|
||||
self._log_debug(u'Requesting ' + request_url)
|
||||
|
||||
request = Request(requestUrl)
|
||||
request = Request(request_url)
|
||||
|
||||
try:
|
||||
response = urlopen(request) #@UnusedVariable
|
||||
urlopen(request)
|
||||
|
||||
except HTTPError as e:
|
||||
if hasattr(e, 'reason'):
|
||||
logger.log(u'pyTivo notification: Error, failed to reach a server - ' + e.reason, logger.ERROR)
|
||||
self._log_error(u'Error, failed to reach a server - ' + e.reason)
|
||||
return False
|
||||
elif hasattr(e, 'code'):
|
||||
logger.log(u"pyTivo notification: Error, the server couldn't fulfill the request - " + e.code, logger.ERROR)
|
||||
self._log_error(u'Error, the server couldn\'t fulfill the request - ' + e.code)
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.log(u'PYTIVO: Unknown exception: ' + ex(e), logger.ERROR)
|
||||
self._log_error(u'Unknown exception: ' + ex(e))
|
||||
return False
|
||||
else:
|
||||
logger.log(u'pyTivo notification: Successfully requested transfer of file')
|
||||
return True
|
||||
|
||||
self._log(u'Successfully requested transfer of file')
|
||||
return True
|
||||
|
||||
|
||||
notifier = pyTivoNotifier
|
||||
notifier = PyTivoNotifier
|
||||
|
|
|
@ -18,49 +18,34 @@
|
|||
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sickbeard
|
||||
from sickbeard import common, logger
|
||||
from sickbeard.notifiers.generic import Notifier
|
||||
|
||||
|
||||
class SlackNotifier:
|
||||
class SlackNotifier(Notifier):
|
||||
|
||||
def __init__(self):
|
||||
self.sg_logo_url = 'https://raw.githubusercontent.com/SickGear/SickGear/master' + \
|
||||
'/gui/slick/images/ico/apple-touch-icon-precomposed.png'
|
||||
super(SlackNotifier, self).__init__()
|
||||
|
||||
def _notify(self, msg, channel='', as_user=False, bot_name='', icon_url='', access_token='', force=False):
|
||||
custom = (force and not as_user) or (not (force or sickbeard.SLACK_AS_USER))
|
||||
resp = (sickbeard.USE_SLACK or force) and sickbeard.helpers.getURL(
|
||||
def _notify(self, title, body, channel='', as_authed=None, bot_name='', icon_url='', access_token='', **kwargs):
|
||||
|
||||
custom = not self._choose(as_authed, sickbeard.SLACK_AS_AUTHED)
|
||||
resp = sickbeard.helpers.getURL(
|
||||
url='https://slack.com/api/chat.postMessage',
|
||||
post_data=dict(
|
||||
[('text', msg), ('token', (access_token, sickbeard.SLACK_ACCESS_TOKEN)[not access_token]),
|
||||
('channel', (channel, sickbeard.SLACK_CHANNEL)[not channel]), ('as_user', not custom)] +
|
||||
([], [('username', (bot_name, sickbeard.SLACK_BOT_NAME or 'SickGear')[not bot_name]),
|
||||
('icon_url', (icon_url, sickbeard.SLACK_ICON_URL or self.sg_logo_url)[not icon_url])])[custom]),
|
||||
[('text', self._body_only(title, body)),
|
||||
('channel', self._choose(channel, sickbeard.SLACK_CHANNEL)), ('as_authed', not custom),
|
||||
('token', self._choose(access_token, sickbeard.SLACK_ACCESS_TOKEN))]
|
||||
+ ([], [('username', self._choose(bot_name, sickbeard.SLACK_BOT_NAME) or 'SickGear'),
|
||||
('icon_url', self._choose(icon_url, sickbeard.SLACK_ICON_URL) or self._sg_logo_url)])[custom]),
|
||||
json=True)
|
||||
|
||||
result = resp and resp.get('ok', resp.get('error')) or not (sickbeard.USE_SLACK or force)
|
||||
result = resp and resp.get('ok') or 'response: "%s"' % (resp.get('error') or self._choose(
|
||||
'bad oath access token?', None))
|
||||
if True is not result:
|
||||
logger.log(u'Slack failed sending message, response: "%s"' % result, logger.ERROR)
|
||||
return result
|
||||
self._log_error('Failed to send message, %s' % result)
|
||||
|
||||
def _notify_str(self, pre_text, post_text):
|
||||
return self._notify('%s: %s' % (common.notifyStrings[pre_text].strip('#: '), post_text))
|
||||
|
||||
def test_notify(self, channel, as_user, bot_name, icon_url, access_token):
|
||||
return self._notify('This is a test notification from SickGear',
|
||||
channel, as_user, bot_name, icon_url, access_token, force=True)
|
||||
|
||||
def notify_snatch(self, ep_name):
|
||||
return sickbeard.SLACK_NOTIFY_ONSNATCH and self._notify_str(common.NOTIFY_SNATCH, ep_name)
|
||||
|
||||
def notify_download(self, ep_name):
|
||||
return sickbeard.SLACK_NOTIFY_ONDOWNLOAD and self._notify_str(common.NOTIFY_DOWNLOAD, ep_name)
|
||||
|
||||
def notify_subtitle_download(self, ep_name, lang):
|
||||
return sickbeard.SLACK_NOTIFY_ONSUBTITLEDOWNLOAD and \
|
||||
self._notify_str(common.NOTIFY_SUBTITLE_DOWNLOAD, '%s: %s' % (ep_name, lang))
|
||||
|
||||
def notify_git_update(self, new_version='??'):
|
||||
return self._notify_str(common.NOTIFY_GIT_UPDATE_TEXT, new_version)
|
||||
return self._choose(('Successful test notice sent. (Note: %s clients display icon once in a sequence)'
|
||||
% self.name, 'Error sending notification, %s' % result)[True is not result], result)
|
||||
|
||||
|
||||
notifier = SlackNotifier
|
||||
|
|
|
@ -16,75 +16,66 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import sickbeard
|
||||
|
||||
from sickbeard import logger
|
||||
# noinspection PyPep8Naming
|
||||
from sickbeard import encodingKludge as ek
|
||||
from sickbeard.exceptions import ex
|
||||
from sickbeard.notifiers.generic import BaseNotifier
|
||||
|
||||
|
||||
class synoIndexNotifier:
|
||||
def notify_snatch(self, ep_name):
|
||||
pass
|
||||
|
||||
def notify_download(self, ep_name):
|
||||
pass
|
||||
|
||||
def notify_subtitle_download(self, ep_name, lang):
|
||||
pass
|
||||
|
||||
def notify_git_update(self, new_version):
|
||||
pass
|
||||
# noinspection PyPep8Naming
|
||||
class SynoIndexNotifier(BaseNotifier):
|
||||
|
||||
def moveFolder(self, old_path, new_path):
|
||||
self.moveObject(old_path, new_path)
|
||||
self._move_object(old_path, new_path)
|
||||
|
||||
def moveFile(self, old_file, new_file):
|
||||
self.moveObject(old_file, new_file)
|
||||
self._move_object(old_file, new_file)
|
||||
|
||||
def moveObject(self, old_path, new_path):
|
||||
if sickbeard.USE_SYNOINDEX:
|
||||
def _move_object(self, old_path, new_path):
|
||||
if self.is_enabled():
|
||||
synoindex_cmd = ['/usr/syno/bin/synoindex', '-N', ek.ek(os.path.abspath, new_path),
|
||||
ek.ek(os.path.abspath, old_path)]
|
||||
logger.log(u'Executing command ' + str(synoindex_cmd), logger.DEBUG)
|
||||
logger.log(u'Absolute path to command: ' + ek.ek(os.path.abspath, synoindex_cmd[0]), logger.DEBUG)
|
||||
self._log_debug(u'Executing command ' + str(synoindex_cmd))
|
||||
self._log_debug(u'Absolute path to command: ' + ek.ek(os.path.abspath, synoindex_cmd[0]))
|
||||
try:
|
||||
p = subprocess.Popen(synoindex_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||
cwd=sickbeard.PROG_DIR)
|
||||
out, err = p.communicate() #@UnusedVariable
|
||||
logger.log(u'Script result: ' + str(out), logger.DEBUG)
|
||||
out, err = p.communicate()
|
||||
self._log_debug(u'Script result: ' + str(out))
|
||||
except OSError as e:
|
||||
logger.log(u'Unable to run synoindex: ' + ex(e), logger.ERROR)
|
||||
self._log_error(u'Unable to run synoindex: ' + ex(e))
|
||||
|
||||
def deleteFolder(self, cur_path):
|
||||
self.makeObject('-D', cur_path)
|
||||
self._make_object('-D', cur_path)
|
||||
|
||||
def addFolder(self, cur_path):
|
||||
self.makeObject('-A', cur_path)
|
||||
self._make_object('-A', cur_path)
|
||||
|
||||
def deleteFile(self, cur_file):
|
||||
self.makeObject('-d', cur_file)
|
||||
self._make_object('-d', cur_file)
|
||||
|
||||
def addFile(self, cur_file):
|
||||
self.makeObject('-a', cur_file)
|
||||
self._make_object('-a', cur_file)
|
||||
|
||||
def makeObject(self, cmd_arg, cur_path):
|
||||
if sickbeard.USE_SYNOINDEX:
|
||||
def _make_object(self, cmd_arg, cur_path):
|
||||
if self.is_enabled():
|
||||
synoindex_cmd = ['/usr/syno/bin/synoindex', cmd_arg, ek.ek(os.path.abspath, cur_path)]
|
||||
logger.log(u'Executing command ' + str(synoindex_cmd), logger.DEBUG)
|
||||
logger.log(u'Absolute path to command: ' + ek.ek(os.path.abspath, synoindex_cmd[0]), logger.DEBUG)
|
||||
self._log_debug(u'Executing command ' + str(synoindex_cmd))
|
||||
self._log_debug(u'Absolute path to command: ' + ek.ek(os.path.abspath, synoindex_cmd[0]))
|
||||
try:
|
||||
p = subprocess.Popen(synoindex_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||
cwd=sickbeard.PROG_DIR)
|
||||
out, err = p.communicate() #@UnusedVariable
|
||||
logger.log(u'Script result: ' + str(out), logger.DEBUG)
|
||||
out, err = p.communicate()
|
||||
self._log_debug(u'Script result: ' + str(out))
|
||||
except OSError as e:
|
||||
logger.log(u'Unable to run synoindex: ' + ex(e), logger.ERROR)
|
||||
self._log_error(u'Unable to run synoindex: ' + ex(e))
|
||||
|
||||
def update_library(self, ep_obj=None, **kwargs):
|
||||
self.addFile(ep_obj.location)
|
||||
|
||||
|
||||
notifier = synoIndexNotifier
|
||||
notifier = SynoIndexNotifier
|
||||
|
|
|
@ -15,49 +15,30 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import sickbeard
|
||||
|
||||
from sickbeard import logger
|
||||
# noinspection PyPep8Naming
|
||||
from sickbeard import encodingKludge as ek
|
||||
from sickbeard.exceptions import ex
|
||||
from sickbeard import common
|
||||
from sickbeard.notifiers.generic import Notifier
|
||||
|
||||
|
||||
class synologyNotifier:
|
||||
def notify_snatch(self, ep_name):
|
||||
if sickbeard.SYNOLOGYNOTIFIER_NOTIFY_ONSNATCH:
|
||||
self._send_synologyNotifier(ep_name, common.notifyStrings[common.NOTIFY_SNATCH])
|
||||
class SynologyNotifier(Notifier):
|
||||
|
||||
def notify_download(self, ep_name):
|
||||
if sickbeard.SYNOLOGYNOTIFIER_NOTIFY_ONDOWNLOAD:
|
||||
self._send_synologyNotifier(ep_name, common.notifyStrings[common.NOTIFY_DOWNLOAD])
|
||||
def _notify(self, title, body, **kwargs):
|
||||
|
||||
def notify_subtitle_download(self, ep_name, lang):
|
||||
if sickbeard.SYNOLOGYNOTIFIER_NOTIFY_ONSUBTITLEDOWNLOAD:
|
||||
self._send_synologyNotifier(ep_name + ': ' + lang, common.notifyStrings[common.NOTIFY_SUBTITLE_DOWNLOAD])
|
||||
|
||||
def notify_git_update(self, new_version = '??'):
|
||||
if sickbeard.USE_SYNOLOGYNOTIFIER:
|
||||
update_text=common.notifyStrings[common.NOTIFY_GIT_UPDATE_TEXT]
|
||||
title=common.notifyStrings[common.NOTIFY_GIT_UPDATE]
|
||||
self._send_synologyNotifier(update_text + new_version, title)
|
||||
|
||||
def _send_synologyNotifier(self, message, title):
|
||||
synodsmnotify_cmd = ['/usr/syno/bin/synodsmnotify', '@administrators', title, message]
|
||||
logger.log(u'Executing command ' + str(synodsmnotify_cmd))
|
||||
logger.log(u'Absolute path to command: ' + ek.ek(os.path.abspath, synodsmnotify_cmd[0]), logger.DEBUG)
|
||||
synodsmnotify_cmd = ['/usr/syno/bin/synodsmnotify', '@administrators', title, body]
|
||||
self._log(u'Executing command ' + str(synodsmnotify_cmd))
|
||||
self._log_debug(u'Absolute path to command: ' + ek.ek(os.path.abspath, synodsmnotify_cmd[0]))
|
||||
try:
|
||||
p = subprocess.Popen(synodsmnotify_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||
cwd=sickbeard.PROG_DIR)
|
||||
out, err = p.communicate() #@UnusedVariable
|
||||
logger.log(u'Script result: ' + str(out), logger.DEBUG)
|
||||
out, err = p.communicate()
|
||||
self._log_debug(u'Script result: ' + str(out))
|
||||
except OSError as e:
|
||||
logger.log(u'Unable to run synodsmnotify: ' + ex(e))
|
||||
self._log(u'Unable to run synodsmnotify: ' + ex(e))
|
||||
|
||||
|
||||
notifier = synologyNotifier
|
||||
notifier = SynologyNotifier
|
||||
|
|
|
@ -16,52 +16,36 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sickbeard
|
||||
from sickbeard import logger
|
||||
from lib.libtrakt import TraktAPI, exceptions
|
||||
import os
|
||||
|
||||
import sickbeard
|
||||
from sickbeard.notifiers.generic import BaseNotifier
|
||||
|
||||
class TraktNotifier:
|
||||
def __init__(self):
|
||||
pass
|
||||
from lib.libtrakt import TraktAPI, exceptions
|
||||
|
||||
|
||||
class TraktNotifier(BaseNotifier):
|
||||
"""
|
||||
A "notifier" for trakt.tv which keeps track of what has and hasn't been added to your library.
|
||||
"""
|
||||
|
||||
def notify_snatch(self, ep_name):
|
||||
pass
|
||||
def update_library(self, ep_obj=None, **kwargs):
|
||||
|
||||
def notify_download(self, ep_name):
|
||||
pass
|
||||
self._update_collection(ep_obj)
|
||||
|
||||
def notify_subtitle_download(self, ep_name, lang):
|
||||
pass
|
||||
|
||||
def notify_git_update(self, new_version):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def update_collection(ep_obj):
|
||||
def _update_collection(self, ep_obj):
|
||||
"""
|
||||
Sends a request to trakt indicating that the given episode is part of our collection.
|
||||
|
||||
:param ep_obj: The TVEpisode object to add to trakt
|
||||
"""
|
||||
|
||||
if sickbeard.USE_TRAKT and sickbeard.TRAKT_ACCOUNTS:
|
||||
if sickbeard.TRAKT_ACCOUNTS:
|
||||
|
||||
# URL parameters
|
||||
data = {
|
||||
'shows': [
|
||||
{
|
||||
'title': ep_obj.show.name,
|
||||
'year': ep_obj.show.startyear,
|
||||
'ids': {},
|
||||
}
|
||||
]
|
||||
}
|
||||
data = dict(shows=[
|
||||
dict(title=ep_obj.show.name, year=ep_obj.show.startyear, ids={})
|
||||
])
|
||||
|
||||
from sickbeard.indexers.indexer_config import INDEXER_TVDB, INDEXER_TVRAGE, INDEXER_IMDB, INDEXER_TMDB, \
|
||||
INDEXER_TRAKT
|
||||
|
@ -75,12 +59,12 @@ class TraktNotifier:
|
|||
indexer, indexerid = supported_indexer[ep_obj.show.indexer], ep_obj.show.indexerid
|
||||
else:
|
||||
for i in indexer_priorities:
|
||||
if ep_obj.show.ids.get(i, {'id': 0}).get('id', 0) > 0:
|
||||
if 0 < ep_obj.show.ids.get(i, {'id': 0}).get('id', 0):
|
||||
indexer, indexerid = supported_indexer[i], ep_obj.show.ids[i]['id']
|
||||
break
|
||||
|
||||
if indexer is None or indexerid is None:
|
||||
logger.log('Missing trakt supported id, could not add to collection.', logger.WARNING)
|
||||
if None is indexer or None is indexerid:
|
||||
self._log_warning('Missing trakt supported id, could not add to collection')
|
||||
return
|
||||
|
||||
data['shows'][0]['ids'][indexer] = indexerid
|
||||
|
@ -101,13 +85,17 @@ class TraktNotifier:
|
|||
warn, msg = False, ''
|
||||
try:
|
||||
resp = TraktAPI().trakt_request('sync/collection', data, send_oauth=tid)
|
||||
if 'added' in resp and 'episodes' in resp['added'] and 0 < sickbeard.helpers.tryInt(resp['added']['episodes']):
|
||||
if 'added' in resp and 'episodes' in resp['added'] \
|
||||
and 0 < sickbeard.helpers.tryInt(resp['added']['episodes']):
|
||||
msg = 'Added episode to'
|
||||
elif 'updated' in resp and 'episodes' in resp['updated'] and 0 < sickbeard.helpers.tryInt(resp['updated']['episodes']):
|
||||
elif 'updated' in resp and 'episodes' in resp['updated'] \
|
||||
and 0 < sickbeard.helpers.tryInt(resp['updated']['episodes']):
|
||||
msg = 'Updated episode in'
|
||||
elif 'existing' in resp and 'episodes' in resp['existing'] and 0 < sickbeard.helpers.tryInt(resp['existing']['episodes']):
|
||||
elif 'existing' in resp and 'episodes' in resp['existing'] \
|
||||
and 0 < sickbeard.helpers.tryInt(resp['existing']['episodes']):
|
||||
msg = 'Episode is already in'
|
||||
elif 'not_found' in resp and 'episodes' in resp['not_found'] and 0 < sickbeard.helpers.tryInt(resp['not_found']['episodes']):
|
||||
elif 'not_found' in resp and 'episodes' in resp['not_found'] \
|
||||
and 0 < sickbeard.helpers.tryInt(resp['not_found']['episodes']):
|
||||
msg = 'Episode not found on Trakt, not adding to'
|
||||
else:
|
||||
warn, msg = True, 'Could not add episode to'
|
||||
|
@ -115,14 +103,9 @@ class TraktNotifier:
|
|||
warn, msg = True, 'Error adding episode to'
|
||||
msg = 'Trakt: %s your %s collection' % (msg, sickbeard.TRAKT_ACCOUNTS[tid].name)
|
||||
if not warn:
|
||||
logger.log(msg)
|
||||
self._log(msg)
|
||||
else:
|
||||
logger.log(msg, logger.WARNING)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _use_me():
|
||||
return sickbeard.USE_TRAKT
|
||||
self._log_warning(msg)
|
||||
|
||||
|
||||
notifier = TraktNotifier
|
||||
|
|
|
@ -16,22 +16,18 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from urlparse import parse_qsl
|
||||
|
||||
import sickbeard
|
||||
|
||||
from sickbeard import logger, common
|
||||
from sickbeard.exceptions import ex
|
||||
|
||||
# parse_qsl moved to urlparse module in v2.6
|
||||
try:
|
||||
from urlparse import parse_qsl #@UnusedImport
|
||||
except:
|
||||
from cgi import parse_qsl #@Reimport
|
||||
from sickbeard.notifiers.generic import Notifier
|
||||
|
||||
import lib.oauth2 as oauth
|
||||
import lib.pythontwitter as twitter
|
||||
|
||||
|
||||
class TwitterNotifier:
|
||||
class TwitterNotifier(Notifier):
|
||||
|
||||
consumer_key = 'vHHtcB6WzpWDG6KYlBMr8g'
|
||||
consumer_secret = 'zMqq5CB3f8cWKiRO2KzWPTlBanYmV0VYxSXZ0Pxds0E'
|
||||
|
||||
|
@ -40,39 +36,19 @@ class TwitterNotifier:
|
|||
AUTHORIZATION_URL = 'https://api.twitter.com/oauth/authorize'
|
||||
SIGNIN_URL = 'https://api.twitter.com/oauth/authenticate'
|
||||
|
||||
def notify_snatch(self, ep_name):
|
||||
if sickbeard.TWITTER_NOTIFY_ONSNATCH:
|
||||
self._notifyTwitter(common.notifyStrings[common.NOTIFY_SNATCH] + ': ' + ep_name)
|
||||
def get_authorization(self):
|
||||
|
||||
def notify_download(self, ep_name):
|
||||
if sickbeard.TWITTER_NOTIFY_ONDOWNLOAD:
|
||||
self._notifyTwitter(common.notifyStrings[common.NOTIFY_DOWNLOAD] + ': ' + ep_name)
|
||||
|
||||
def notify_subtitle_download(self, ep_name, lang):
|
||||
if sickbeard.TWITTER_NOTIFY_ONSUBTITLEDOWNLOAD:
|
||||
self._notifyTwitter(common.notifyStrings[common.NOTIFY_SUBTITLE_DOWNLOAD] + ' ' + ep_name + ': ' + lang)
|
||||
|
||||
def notify_git_update(self, new_version = '??'):
|
||||
if sickbeard.USE_TWITTER:
|
||||
update_text=common.notifyStrings[common.NOTIFY_GIT_UPDATE_TEXT]
|
||||
title=common.notifyStrings[common.NOTIFY_GIT_UPDATE]
|
||||
self._notifyTwitter(title + ' - ' + update_text + new_version)
|
||||
|
||||
def test_notify(self):
|
||||
return self._notifyTwitter('This is a test notification from SickGear', force=True)
|
||||
|
||||
def _get_authorization(self):
|
||||
|
||||
signature_method_hmac_sha1 = oauth.SignatureMethod_HMAC_SHA1() #@UnusedVariable
|
||||
# noinspection PyUnusedLocal
|
||||
signature_method_hmac_sha1 = oauth.SignatureMethod_HMAC_SHA1()
|
||||
oauth_consumer = oauth.Consumer(key=self.consumer_key, secret=self.consumer_secret)
|
||||
oauth_client = oauth.Client(oauth_consumer)
|
||||
|
||||
logger.log('Requesting temp token from Twitter', logger.DEBUG)
|
||||
self._log_debug('Requesting temp token from Twitter')
|
||||
|
||||
resp, content = oauth_client.request(self.REQUEST_TOKEN_URL, 'GET')
|
||||
|
||||
if resp['status'] != '200':
|
||||
logger.log('Invalid response from Twitter requesting temp token: %s' % resp['status'], logger.ERROR)
|
||||
if '200' != resp['status']:
|
||||
self._log_error('Invalid response from Twitter requesting temp token: %s' % resp['status'])
|
||||
else:
|
||||
request_token = dict(parse_qsl(content))
|
||||
|
||||
|
@ -81,67 +57,62 @@ class TwitterNotifier:
|
|||
|
||||
return self.AUTHORIZATION_URL + '?oauth_token=' + request_token['oauth_token']
|
||||
|
||||
def _get_credentials(self, key):
|
||||
request_token = {}
|
||||
|
||||
request_token['oauth_token'] = sickbeard.TWITTER_USERNAME
|
||||
request_token['oauth_token_secret'] = sickbeard.TWITTER_PASSWORD
|
||||
request_token['oauth_callback_confirmed'] = 'true'
|
||||
def get_credentials(self, key):
|
||||
request_token = dict(oauth_token=sickbeard.TWITTER_USERNAME, oauth_token_secret=sickbeard.TWITTER_PASSWORD,
|
||||
oauth_callback_confirmed='true')
|
||||
|
||||
token = oauth.Token(request_token['oauth_token'], request_token['oauth_token_secret'])
|
||||
token.set_verifier(key)
|
||||
|
||||
logger.log('Generating and signing request for an access token using key ' + key, logger.DEBUG)
|
||||
self._log_debug('Generating and signing request for an access token using key ' + key)
|
||||
|
||||
signature_method_hmac_sha1 = oauth.SignatureMethod_HMAC_SHA1() #@UnusedVariable
|
||||
# noinspection PyUnusedLocal
|
||||
signature_method_hmac_sha1 = oauth.SignatureMethod_HMAC_SHA1()
|
||||
oauth_consumer = oauth.Consumer(key=self.consumer_key, secret=self.consumer_secret)
|
||||
logger.log('oauth_consumer: ' + str(oauth_consumer), logger.DEBUG)
|
||||
self._log_debug('oauth_consumer: ' + str(oauth_consumer))
|
||||
oauth_client = oauth.Client(oauth_consumer, token)
|
||||
logger.log('oauth_client: ' + str(oauth_client), logger.DEBUG)
|
||||
self._log_debug('oauth_client: ' + str(oauth_client))
|
||||
resp, content = oauth_client.request(self.ACCESS_TOKEN_URL, method='POST', body='oauth_verifier=%s' % key)
|
||||
logger.log('resp, content: ' + str(resp) + ',' + str(content), logger.DEBUG)
|
||||
self._log_debug('resp, content: ' + str(resp) + ',' + str(content))
|
||||
|
||||
access_token = dict(parse_qsl(content))
|
||||
logger.log('access_token: ' + str(access_token), logger.DEBUG)
|
||||
self._log_debug('access_token: ' + str(access_token))
|
||||
|
||||
logger.log('resp[status] = ' + str(resp['status']), logger.DEBUG)
|
||||
if resp['status'] != '200':
|
||||
logger.log('The request for a token with did not succeed: ' + str(resp['status']), logger.ERROR)
|
||||
return False
|
||||
self._log_debug('resp[status] = ' + str(resp['status']))
|
||||
if '200' != resp['status']:
|
||||
self._log_error('The request for a token with did not succeed: ' + str(resp['status']))
|
||||
result = False
|
||||
else:
|
||||
logger.log('Your Twitter Access Token key: %s' % access_token['oauth_token'], logger.DEBUG)
|
||||
logger.log('Access Token secret: %s' % access_token['oauth_token_secret'], logger.DEBUG)
|
||||
self._log_debug('Your Twitter Access Token key: %s' % access_token['oauth_token'])
|
||||
self._log_debug('Access Token secret: %s' % access_token['oauth_token_secret'])
|
||||
sickbeard.TWITTER_USERNAME = access_token['oauth_token']
|
||||
sickbeard.TWITTER_PASSWORD = access_token['oauth_token_secret']
|
||||
return True
|
||||
result = True
|
||||
|
||||
message = ('Key verification successful', 'Unable to verify key')[not result]
|
||||
logger.log(u'%s result: %s' % (self.name, message))
|
||||
return self._choose(message, result)
|
||||
|
||||
def _send_tweet(self, message=None):
|
||||
def _notify(self, title, body, **kwargs):
|
||||
|
||||
# don't use title with updates or testing, as only one str is used
|
||||
body = '::'.join(([], [sickbeard.TWITTER_PREFIX])[bool(sickbeard.TWITTER_PREFIX)]
|
||||
+ [body.replace('#: ', ': ') if 'SickGear' in title else body])
|
||||
|
||||
username = self.consumer_key
|
||||
password = self.consumer_secret
|
||||
access_token_key = sickbeard.TWITTER_USERNAME
|
||||
access_token_secret = sickbeard.TWITTER_PASSWORD
|
||||
|
||||
logger.log(u'Sending tweet: ' + message, logger.DEBUG)
|
||||
|
||||
api = twitter.Api(username, password, access_token_key, access_token_secret)
|
||||
|
||||
try:
|
||||
api.PostUpdate(message.encode('utf8'))
|
||||
api.PostUpdate(body.encode('utf8'))
|
||||
except Exception as e:
|
||||
logger.log(u'Error Sending Tweet: ' + ex(e), logger.ERROR)
|
||||
self._log_error(u'Error sending Tweet: ' + ex(e))
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _notifyTwitter(self, message='', force=False):
|
||||
prefix = sickbeard.TWITTER_PREFIX
|
||||
|
||||
if not sickbeard.USE_TWITTER and not force:
|
||||
return False
|
||||
|
||||
return self._send_tweet(prefix + ': ' + message)
|
||||
|
||||
|
||||
notifier = TwitterNotifier
|
||||
|
|
|
@ -16,32 +16,30 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import urllib
|
||||
import urllib2
|
||||
import socket
|
||||
import base64
|
||||
import time
|
||||
|
||||
import sickbeard
|
||||
|
||||
from sickbeard import logger
|
||||
from sickbeard import common
|
||||
from sickbeard.exceptions import ex
|
||||
from sickbeard.encodingKludge import fixStupidEncodings
|
||||
|
||||
try:
|
||||
import xml.etree.cElementTree as etree
|
||||
except ImportError:
|
||||
import xml.etree.ElementTree as etree
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
from lib import simplejson as json
|
||||
import socket
|
||||
import time
|
||||
import urllib
|
||||
import urllib2
|
||||
import xml.etree.cElementTree as XmlEtree
|
||||
|
||||
import sickbeard
|
||||
from sickbeard.exceptions import ex
|
||||
from sickbeard.encodingKludge import fixStupidEncodings
|
||||
from sickbeard.notifiers.generic import Notifier
|
||||
|
||||
|
||||
class XBMCNotifier:
|
||||
sb_logo_url = 'https://raw.githubusercontent.com/SickGear/SickGear/master/gui/slick/images/ico/apple-touch-icon-72x72.png'
|
||||
class XBMCNotifier(Notifier):
|
||||
|
||||
def __init__(self):
|
||||
super(XBMCNotifier, self).__init__()
|
||||
|
||||
self.sg_logo_file = 'apple-touch-icon-72x72.png'
|
||||
|
||||
def _get_xbmc_version(self, host, username, password):
|
||||
"""Returns XBMC JSON-RPC API version (odd # = dev, even # = stable)
|
||||
|
@ -70,12 +68,12 @@ class XBMCNotifier:
|
|||
|
||||
"""
|
||||
|
||||
# since we need to maintain python 2.5 compatability we can not pass a timeout delay to urllib2 directly (python 2.6+)
|
||||
# override socket timeout to reduce delay for this call alone
|
||||
# since we need to maintain python 2.5 compatability we can not pass a timeout delay
|
||||
# to urllib2 directly (python 2.6+) override socket timeout to reduce delay for this call alone
|
||||
socket.setdefaulttimeout(10)
|
||||
|
||||
checkCommand = '{"jsonrpc":"2.0","method":"JSONRPC.Version","id":1}'
|
||||
result = self._send_to_xbmc_json(checkCommand, host, username, password)
|
||||
check_command = '{"jsonrpc":"2.0","method":"JSONRPC.Version","id":1}'
|
||||
result = self._send_to_xbmc_json(check_command, host, username, password)
|
||||
|
||||
# revert back to default socket timeout
|
||||
socket.setdefaulttimeout(sickbeard.SOCKET_TIMEOUT)
|
||||
|
@ -84,113 +82,49 @@ class XBMCNotifier:
|
|||
return result['result']['version']
|
||||
else:
|
||||
# fallback to legacy HTTPAPI method
|
||||
testCommand = {'command': 'Help'}
|
||||
request = self._send_to_xbmc(testCommand, host, username, password)
|
||||
test_command = {'command': 'Help'}
|
||||
request = self._send_to_xbmc(test_command, host, username, password)
|
||||
if request:
|
||||
# return a fake version number, so it uses the legacy method
|
||||
return 1
|
||||
else:
|
||||
return False
|
||||
|
||||
def _notify_xbmc(self, message, title='SickGear', host=None, username=None, password=None, force=False):
|
||||
"""Internal wrapper for the notify_snatch and notify_download functions
|
||||
|
||||
Detects JSON-RPC version then branches the logic for either the JSON-RPC or legacy HTTP API methods.
|
||||
|
||||
Args:
|
||||
message: Message body of the notice to send
|
||||
title: Title of the notice to send
|
||||
host: XBMC webserver host:port
|
||||
username: XBMC webserver username
|
||||
password: XBMC webserver password
|
||||
force: Used for the Test method to override config saftey checks
|
||||
|
||||
Returns:
|
||||
Returns a list results in the format of host:ip:result
|
||||
The result will either be 'OK' or False, this is used to be parsed by the calling function.
|
||||
|
||||
"""
|
||||
|
||||
# fill in omitted parameters
|
||||
if not host:
|
||||
host = sickbeard.XBMC_HOST
|
||||
if not username:
|
||||
username = sickbeard.XBMC_USERNAME
|
||||
if not password:
|
||||
password = sickbeard.XBMC_PASSWORD
|
||||
|
||||
# suppress notifications if the notifier is disabled but the notify options are checked
|
||||
if not sickbeard.USE_XBMC and not force:
|
||||
logger.log('Notification for XBMC not enabled, skipping this notification', logger.DEBUG)
|
||||
return False
|
||||
|
||||
result = ''
|
||||
for curHost in [x.strip() for x in host.split(',')]:
|
||||
logger.log(u'Sending XBMC notification to "' + curHost + '" - ' + message, logger.MESSAGE)
|
||||
|
||||
xbmcapi = self._get_xbmc_version(curHost, username, password)
|
||||
if xbmcapi:
|
||||
if (xbmcapi <= 4):
|
||||
logger.log(u'Detected XBMC version <= 11, using XBMC HTTP API', logger.DEBUG)
|
||||
command = {'command': 'ExecBuiltIn',
|
||||
'parameter': 'Notification(' + title.encode('utf-8') + ',' + message.encode(
|
||||
'utf-8') + ')'}
|
||||
notifyResult = self._send_to_xbmc(command, curHost, username, password)
|
||||
if notifyResult:
|
||||
result += curHost + ':' + str(notifyResult)
|
||||
else:
|
||||
logger.log(u'Detected XBMC version >= 12, using XBMC JSON API', logger.DEBUG)
|
||||
command = '{"jsonrpc":"2.0","method":"GUI.ShowNotification","params":{"title":"%s","message":"%s", "image": "%s"},"id":1}' % (
|
||||
title.encode('utf-8'), message.encode('utf-8'), self.sb_logo_url)
|
||||
notifyResult = self._send_to_xbmc_json(command, curHost, username, password)
|
||||
if notifyResult.get('result'):
|
||||
result += curHost + ':' + notifyResult['result'].decode(sickbeard.SYS_ENCODING)
|
||||
else:
|
||||
if sickbeard.XBMC_ALWAYS_ON or force:
|
||||
logger.log(
|
||||
u'Failed to detect XBMC version for "' + curHost + '", check configuration and try again.',
|
||||
logger.ERROR)
|
||||
result += curHost + ':False'
|
||||
|
||||
return result
|
||||
|
||||
def _send_update_library(self, host, showName=None):
|
||||
def _send_update_library(self, host, show_name=None):
|
||||
"""Internal wrapper for the update library function to branch the logic for JSON-RPC or legacy HTTP API
|
||||
|
||||
Checks the XBMC API version to branch the logic to call either the legacy HTTP API or the newer JSON-RPC over HTTP methods.
|
||||
Checks the XBMC API version to branch the logic
|
||||
to call either the legacy HTTP API or the newer JSON-RPC over HTTP methods.
|
||||
|
||||
Args:
|
||||
host: XBMC webserver host:port
|
||||
showName: Name of a TV show to specifically target the library update for
|
||||
show_name: Name of a TV show to specifically target the library update for
|
||||
|
||||
Returns:
|
||||
Returns True or False, if the update was successful
|
||||
|
||||
"""
|
||||
|
||||
logger.log(u'Sending request to update library for XBMC host: "' + host + '"', logger.MESSAGE)
|
||||
self._log(u'Sending request to update library for host: "%s"' % host)
|
||||
|
||||
xbmcapi = self._get_xbmc_version(host, sickbeard.XBMC_USERNAME, sickbeard.XBMC_PASSWORD)
|
||||
if xbmcapi:
|
||||
if (xbmcapi <= 4):
|
||||
if 4 >= xbmcapi:
|
||||
# try to update for just the show, if it fails, do full update if enabled
|
||||
if not self._update_library(host, showName) and sickbeard.XBMC_UPDATE_FULL:
|
||||
logger.log(u'Single show update failed, falling back to full update', logger.WARNING)
|
||||
return self._update_library(host)
|
||||
if not self._update_library_http(host, show_name) and sickbeard.XBMC_UPDATE_FULL:
|
||||
self._log_warning(u'Single show update failed, falling back to full update')
|
||||
return self._update_library_http(host)
|
||||
else:
|
||||
return True
|
||||
else:
|
||||
# try to update for just the show, if it fails, do full update if enabled
|
||||
if not self._update_library_json(host, showName) and sickbeard.XBMC_UPDATE_FULL:
|
||||
logger.log(u'Single show update failed, falling back to full update', logger.WARNING)
|
||||
if not self._update_library_json(host, show_name) and sickbeard.XBMC_UPDATE_FULL:
|
||||
self._log_warning(u'Single show update failed, falling back to full update')
|
||||
return self._update_library_json(host)
|
||||
else:
|
||||
return True
|
||||
else:
|
||||
logger.log(u'Failed to detect XBMC version for "' + host + '", check configuration and try again.',
|
||||
logger.DEBUG)
|
||||
return False
|
||||
|
||||
self._log_debug(u'Failed to detect version for "%s", check configuration and try again' % host)
|
||||
return False
|
||||
|
||||
# #############################################################################
|
||||
|
@ -210,23 +144,19 @@ class XBMCNotifier:
|
|||
Returns response.result for successful commands or False if there was an error
|
||||
|
||||
"""
|
||||
|
||||
# fill in omitted parameters
|
||||
if not username:
|
||||
username = sickbeard.XBMC_USERNAME
|
||||
if not password:
|
||||
password = sickbeard.XBMC_PASSWORD
|
||||
|
||||
if not host:
|
||||
logger.log(u'No XBMC host passed, aborting update', logger.DEBUG)
|
||||
self._log_debug(u'No host passed, aborting update')
|
||||
return False
|
||||
|
||||
username = self._choose(username, sickbeard.XBMC_USERNAME)
|
||||
password = self._choose(password, sickbeard.XBMC_PASSWORD)
|
||||
|
||||
for key in command:
|
||||
if type(command[key]) == unicode:
|
||||
command[key] = command[key].encode('utf-8')
|
||||
|
||||
enc_command = urllib.urlencode(command)
|
||||
logger.log(u'XBMC encoded API command: ' + enc_command, logger.DEBUG)
|
||||
self._log_debug(u'Encoded API command: ' + enc_command)
|
||||
|
||||
url = 'http://%s/xbmcCmds/xbmcHttp/?%s' % (host, enc_command)
|
||||
try:
|
||||
|
@ -236,23 +166,22 @@ class XBMCNotifier:
|
|||
base64string = base64.encodestring('%s:%s' % (username, password))[:-1]
|
||||
authheader = 'Basic %s' % base64string
|
||||
req.add_header('Authorization', authheader)
|
||||
logger.log(u'Contacting XBMC (with auth header) via url: ' + fixStupidEncodings(url), logger.DEBUG)
|
||||
self._log_debug(u'Contacting (with auth header) via url: ' + fixStupidEncodings(url))
|
||||
else:
|
||||
logger.log(u'Contacting XBMC via url: ' + fixStupidEncodings(url), logger.DEBUG)
|
||||
self._log_debug(u'Contacting via url: ' + fixStupidEncodings(url))
|
||||
|
||||
response = urllib2.urlopen(req)
|
||||
result = response.read().decode(sickbeard.SYS_ENCODING)
|
||||
response.close()
|
||||
|
||||
logger.log(u'XBMC HTTP response: ' + result.replace('\n', ''), logger.DEBUG)
|
||||
self._log_debug(u'HTTP response: ' + result.replace('\n', ''))
|
||||
return result
|
||||
|
||||
except (urllib2.URLError, IOError) as e:
|
||||
logger.log(u"Warning: Couldn't contact XBMC HTTP at " + fixStupidEncodings(url) + ' ' + ex(e),
|
||||
logger.WARNING)
|
||||
self._log_warning(u'Couldn\'t contact HTTP at %s %s' % (fixStupidEncodings(url), ex(e)))
|
||||
return False
|
||||
|
||||
def _update_library(self, host=None, showName=None):
|
||||
def _update_library_http(self, host=None, show_name=None):
|
||||
"""Handles updating XBMC host via HTTP API
|
||||
|
||||
Attempts to update the XBMC video library for a specific tv show if passed,
|
||||
|
@ -260,7 +189,7 @@ class XBMCNotifier:
|
|||
|
||||
Args:
|
||||
host: XBMC webserver host:port
|
||||
showName: Name of a TV show to specifically target the library update for
|
||||
show_name: Name of a TV show to specifically target the library update for
|
||||
|
||||
Returns:
|
||||
Returns True or False
|
||||
|
@ -268,73 +197,76 @@ class XBMCNotifier:
|
|||
"""
|
||||
|
||||
if not host:
|
||||
logger.log(u'No XBMC host passed, aborting update', logger.DEBUG)
|
||||
self._log_debug(u'No host passed, aborting update')
|
||||
return False
|
||||
|
||||
logger.log(u'Updating XMBC library via HTTP method for host: ' + host, logger.DEBUG)
|
||||
self._log_debug(u'Updating XMBC library via HTTP method for host: ' + host)
|
||||
|
||||
# if we're doing per-show
|
||||
if showName:
|
||||
logger.log(u'Updating library in XBMC via HTTP method for show ' + showName, logger.DEBUG)
|
||||
if show_name:
|
||||
self._log_debug(u'Updating library via HTTP method for show ' + show_name)
|
||||
|
||||
pathSql = 'select path.strPath from path, tvshow, tvshowlinkpath where ' \
|
||||
'tvshow.c00 = "%s" and tvshowlinkpath.idShow = tvshow.idShow ' \
|
||||
'and tvshowlinkpath.idPath = path.idPath' % (showName)
|
||||
# noinspection SqlResolve
|
||||
path_sql = 'select path.strPath' \
|
||||
' from path, tvshow, tvshowlinkpath' \
|
||||
' where tvshow.c00 = "%s"' \
|
||||
' and tvshowlinkpath.idShow = tvshow.idShow' \
|
||||
' and tvshowlinkpath.idPath = path.idPath' % show_name
|
||||
|
||||
# use this to get xml back for the path lookups
|
||||
xmlCommand = {
|
||||
'command': 'SetResponseFormat(webheader;false;webfooter;false;header;<xml>;footer;</xml>;opentag;<tag>;closetag;</tag>;closefinaltag;false)'}
|
||||
xml_command = dict(command='SetResponseFormat(webheader;false;webfooter;false;header;<xml>;footer;</xml>;'
|
||||
'opentag;<tag>;closetag;</tag>;closefinaltag;false)')
|
||||
# sql used to grab path(s)
|
||||
sqlCommand = {'command': 'QueryVideoDatabase(%s)' % (pathSql)}
|
||||
sql_command = dict(command='QueryVideoDatabase(%s)' % path_sql)
|
||||
# set output back to default
|
||||
resetCommand = {'command': 'SetResponseFormat()'}
|
||||
reset_command = dict(command='SetResponseFormat()')
|
||||
|
||||
# set xml response format, if this fails then don't bother with the rest
|
||||
request = self._send_to_xbmc(xmlCommand, host)
|
||||
request = self._send_to_xbmc(xml_command, host)
|
||||
if not request:
|
||||
return False
|
||||
|
||||
sqlXML = self._send_to_xbmc(sqlCommand, host)
|
||||
request = self._send_to_xbmc(resetCommand, host)
|
||||
sql_xml = self._send_to_xbmc(sql_command, host)
|
||||
self._send_to_xbmc(reset_command, host)
|
||||
|
||||
if not sqlXML:
|
||||
logger.log(u'Invalid response for ' + showName + ' on ' + host, logger.DEBUG)
|
||||
if not sql_xml:
|
||||
self._log_debug(u'Invalid response for ' + show_name + ' on ' + host)
|
||||
return False
|
||||
|
||||
encSqlXML = urllib.quote(sqlXML, ':\\/<>')
|
||||
enc_sql_xml = urllib.quote(sql_xml, ':\\/<>')
|
||||
try:
|
||||
et = etree.fromstring(encSqlXML)
|
||||
et = XmlEtree.fromstring(enc_sql_xml)
|
||||
except SyntaxError as e:
|
||||
logger.log(u'Unable to parse XML returned from XBMC: ' + ex(e), logger.ERROR)
|
||||
self._log_error(u'Unable to parse XML response: ' + ex(e))
|
||||
return False
|
||||
|
||||
paths = et.findall('.//field')
|
||||
|
||||
if not paths:
|
||||
logger.log(u'No valid paths found for ' + showName + ' on ' + host, logger.DEBUG)
|
||||
self._log_debug(u'No valid paths found for ' + show_name + ' on ' + host)
|
||||
return False
|
||||
|
||||
for path in paths:
|
||||
# we do not need it double-encoded, gawd this is dumb
|
||||
unEncPath = urllib.unquote(path.text).decode(sickbeard.SYS_ENCODING)
|
||||
logger.log(u'XBMC Updating ' + showName + ' on ' + host + ' at ' + unEncPath, logger.DEBUG)
|
||||
updateCommand = {'command': 'ExecBuiltIn', 'parameter': 'XBMC.updatelibrary(video, %s)' % (unEncPath)}
|
||||
request = self._send_to_xbmc(updateCommand, host)
|
||||
un_enc_path = urllib.unquote(path.text).decode(sickbeard.SYS_ENCODING)
|
||||
self._log_debug(u'Updating ' + show_name + ' on ' + host + ' at ' + un_enc_path)
|
||||
update_command = dict(command='ExecBuiltIn', parameter='XBMC.updatelibrary(video, %s)' % un_enc_path)
|
||||
request = self._send_to_xbmc(update_command, host)
|
||||
if not request:
|
||||
logger.log(u'Update of show directory failed on ' + showName + ' on ' + host + ' at ' + unEncPath,
|
||||
logger.ERROR)
|
||||
self._log_error(u'Update of show directory failed on ' + show_name
|
||||
+ ' on ' + host + ' at ' + un_enc_path)
|
||||
return False
|
||||
# sleep for a few seconds just to be sure xbmc has a chance to finish each directory
|
||||
if len(paths) > 1:
|
||||
time.sleep(5)
|
||||
# do a full update if requested
|
||||
else:
|
||||
logger.log(u'Doing Full Library XBMC update on host: ' + host, logger.MESSAGE)
|
||||
updateCommand = {'command': 'ExecBuiltIn', 'parameter': 'XBMC.updatelibrary(video)'}
|
||||
request = self._send_to_xbmc(updateCommand, host)
|
||||
self._log(u'Doing full library update on host: ' + host)
|
||||
update_command = {'command': 'ExecBuiltIn', 'parameter': 'XBMC.updatelibrary(video)'}
|
||||
request = self._send_to_xbmc(update_command, host)
|
||||
|
||||
if not request:
|
||||
logger.log(u'XBMC Full Library update failed on: ' + host, logger.ERROR)
|
||||
self._log_error(u'Full Library update failed on: ' + host)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
@ -356,21 +288,17 @@ class XBMCNotifier:
|
|||
Returns response.result for successful commands or False if there was an error
|
||||
|
||||
"""
|
||||
|
||||
# fill in omitted parameters
|
||||
if not username:
|
||||
username = sickbeard.XBMC_USERNAME
|
||||
if not password:
|
||||
password = sickbeard.XBMC_PASSWORD
|
||||
|
||||
if not host:
|
||||
logger.log(u'No XBMC host passed, aborting update', logger.DEBUG)
|
||||
self._log_debug(u'No host passed, aborting update')
|
||||
return False
|
||||
|
||||
command = command.encode('utf-8')
|
||||
logger.log(u'XBMC JSON command: ' + command, logger.DEBUG)
|
||||
username = self._choose(username, sickbeard.XBMC_USERNAME)
|
||||
password = self._choose(password, sickbeard.XBMC_PASSWORD)
|
||||
|
||||
url = 'http://%s/jsonrpc' % (host)
|
||||
command = command.encode('utf-8')
|
||||
self._log_debug(u'JSON command: ' + command)
|
||||
|
||||
url = 'http://%s/jsonrpc' % host
|
||||
try:
|
||||
req = urllib2.Request(url, command)
|
||||
req.add_header('Content-type', 'application/json')
|
||||
|
@ -379,33 +307,31 @@ class XBMCNotifier:
|
|||
base64string = base64.encodestring('%s:%s' % (username, password))[:-1]
|
||||
authheader = 'Basic %s' % base64string
|
||||
req.add_header('Authorization', authheader)
|
||||
logger.log(u'Contacting XBMC (with auth header) via url: ' + fixStupidEncodings(url), logger.DEBUG)
|
||||
self._log_debug(u'Contacting (with auth header) via url: ' + fixStupidEncodings(url))
|
||||
else:
|
||||
logger.log(u'Contacting XBMC via url: ' + fixStupidEncodings(url), logger.DEBUG)
|
||||
self._log_debug(u'Contacting via url: ' + fixStupidEncodings(url))
|
||||
|
||||
try:
|
||||
response = urllib2.urlopen(req)
|
||||
except urllib2.URLError as e:
|
||||
logger.log(u'Error while trying to retrieve XBMC API version for ' + host + ': ' + ex(e),
|
||||
logger.WARNING)
|
||||
self._log_warning(u'Error while trying to retrieve API version for "%s": %s' % (host, ex(e)))
|
||||
return False
|
||||
|
||||
# parse the json result
|
||||
try:
|
||||
result = json.load(response)
|
||||
response.close()
|
||||
logger.log(u'XBMC JSON response: ' + str(result), logger.DEBUG)
|
||||
self._log_debug(u'JSON response: ' + str(result))
|
||||
return result # need to return response for parsing
|
||||
except ValueError as e:
|
||||
logger.log(u'Unable to decode JSON: ' + response, logger.WARNING)
|
||||
except ValueError:
|
||||
self._log_warning(u'Unable to decode JSON: ' + response)
|
||||
return False
|
||||
|
||||
except IOError as e:
|
||||
logger.log(u"Warning: Couldn't contact XBMC JSON API at " + fixStupidEncodings(url) + ' ' + ex(e),
|
||||
logger.WARNING)
|
||||
self._log_warning(u'Couldn\'t contact JSON API at ' + fixStupidEncodings(url) + ' ' + ex(e))
|
||||
return False
|
||||
|
||||
def _update_library_json(self, host=None, showName=None):
|
||||
def _update_library_json(self, host=None, show_name=None):
|
||||
"""Handles updating XBMC host via HTTP JSON-RPC
|
||||
|
||||
Attempts to update the XBMC video library for a specific tv show if passed,
|
||||
|
@ -413,7 +339,7 @@ class XBMCNotifier:
|
|||
|
||||
Args:
|
||||
host: XBMC webserver host:port
|
||||
showName: Name of a TV show to specifically target the library update for
|
||||
show_name: Name of a TV show to specifically target the library update for
|
||||
|
||||
Returns:
|
||||
Returns True or False
|
||||
|
@ -421,28 +347,28 @@ class XBMCNotifier:
|
|||
"""
|
||||
|
||||
if not host:
|
||||
logger.log(u'No XBMC host passed, aborting update', logger.DEBUG)
|
||||
self._log_debug(u'No host passed, aborting update')
|
||||
return False
|
||||
|
||||
logger.log(u'Updating XMBC library via JSON method for host: ' + host, logger.MESSAGE)
|
||||
self._log(u'Updating XMBC library via JSON method for host: ' + host)
|
||||
|
||||
# if we're doing per-show
|
||||
if showName:
|
||||
if show_name:
|
||||
tvshowid = -1
|
||||
logger.log(u'Updating library in XBMC via JSON method for show ' + showName, logger.DEBUG)
|
||||
self._log_debug(u'Updating library via JSON method for show ' + show_name)
|
||||
|
||||
# get tvshowid by showName
|
||||
showsCommand = '{"jsonrpc":"2.0","method":"VideoLibrary.GetTVShows","id":1}'
|
||||
showsResponse = self._send_to_xbmc_json(showsCommand, host)
|
||||
shows_command = '{"jsonrpc":"2.0","method":"VideoLibrary.GetTVShows","id":1}'
|
||||
shows_response = self._send_to_xbmc_json(shows_command, host)
|
||||
|
||||
if showsResponse and 'result' in showsResponse and 'tvshows' in showsResponse['result']:
|
||||
shows = showsResponse['result']['tvshows']
|
||||
if shows_response and 'result' in shows_response and 'tvshows' in shows_response['result']:
|
||||
shows = shows_response['result']['tvshows']
|
||||
else:
|
||||
logger.log(u'XBMC: No tvshows in XBMC TV show list', logger.DEBUG)
|
||||
self._log_debug(u'No tvshows in TV show list')
|
||||
return False
|
||||
|
||||
for show in shows:
|
||||
if (show['label'] == showName):
|
||||
if show['label'] == show_name:
|
||||
tvshowid = show['tvshowid']
|
||||
break # exit out of loop otherwise the label and showname will not match up
|
||||
|
||||
|
@ -450,121 +376,145 @@ class XBMCNotifier:
|
|||
del shows
|
||||
|
||||
# we didn't find the show (exact match), thus revert to just doing a full update if enabled
|
||||
if (tvshowid == -1):
|
||||
logger.log(u'Exact show name not matched in XBMC TV show list', logger.DEBUG)
|
||||
if -1 == tvshowid:
|
||||
self._log_debug(u'Exact show name not matched in TV show list')
|
||||
return False
|
||||
|
||||
# lookup tv-show path
|
||||
pathCommand = '{"jsonrpc":"2.0","method":"VideoLibrary.GetTVShowDetails","params":{"tvshowid":%d, "properties": ["file"]},"id":1}' % (
|
||||
tvshowid)
|
||||
pathResponse = self._send_to_xbmc_json(pathCommand, host)
|
||||
path_command = '{"jsonrpc":"2.0","method":"VideoLibrary.GetTVShowDetails",' \
|
||||
'"params":{"tvshowid":%d, "properties": ["file"]},"id":1}' % tvshowid
|
||||
path_response = self._send_to_xbmc_json(path_command, host)
|
||||
|
||||
path = pathResponse['result']['tvshowdetails']['file']
|
||||
logger.log(u'Received Show: ' + showName + ' with ID: ' + str(tvshowid) + ' Path: ' + path,
|
||||
logger.DEBUG)
|
||||
path = path_response['result']['tvshowdetails']['file']
|
||||
self._log_debug(u'Received Show: ' + show_name + ' with ID: ' + str(tvshowid) + ' Path: ' + path)
|
||||
|
||||
if (len(path) < 1):
|
||||
logger.log(u'No valid path found for ' + showName + ' with ID: ' + str(tvshowid) + ' on ' + host,
|
||||
logger.WARNING)
|
||||
if 1 > len(path):
|
||||
self._log_warning(u'No valid path found for ' + show_name + ' with ID: '
|
||||
+ str(tvshowid) + ' on ' + host)
|
||||
return False
|
||||
|
||||
logger.log(u'XBMC Updating ' + showName + ' on ' + host + ' at ' + path, logger.DEBUG)
|
||||
updateCommand = '{"jsonrpc":"2.0","method":"VideoLibrary.Scan","params":{"directory":%s},"id":1}' % (
|
||||
self._log_debug(u'Updating ' + show_name + ' on ' + host + ' at ' + path)
|
||||
update_command = '{"jsonrpc":"2.0","method":"VideoLibrary.Scan","params":{"directory":%s},"id":1}' % (
|
||||
json.dumps(path))
|
||||
request = self._send_to_xbmc_json(updateCommand, host)
|
||||
request = self._send_to_xbmc_json(update_command, host)
|
||||
if not request:
|
||||
logger.log(u'Update of show directory failed on ' + showName + ' on ' + host + ' at ' + path,
|
||||
logger.ERROR)
|
||||
self._log_error(u'Update of show directory failed on ' + show_name + ' on ' + host + ' at ' + path)
|
||||
return False
|
||||
|
||||
# catch if there was an error in the returned request
|
||||
# noinspection PyTypeChecker
|
||||
for r in request:
|
||||
if 'error' in r:
|
||||
logger.log(
|
||||
u'Error while attempting to update show directory for ' + showName + ' on ' + host + ' at ' + path,
|
||||
logger.ERROR)
|
||||
self._log_error(
|
||||
u'Error while attempting to update show directory for ' + show_name
|
||||
+ ' on ' + host + ' at ' + path)
|
||||
return False
|
||||
|
||||
# do a full update if requested
|
||||
else:
|
||||
logger.log(u'Doing Full Library XBMC update on host: ' + host, logger.MESSAGE)
|
||||
updateCommand = '{"jsonrpc":"2.0","method":"VideoLibrary.Scan","id":1}'
|
||||
request = self._send_to_xbmc_json(updateCommand, host, sickbeard.XBMC_USERNAME, sickbeard.XBMC_PASSWORD)
|
||||
self._log(u'Doing Full Library update on host: ' + host)
|
||||
update_command = '{"jsonrpc":"2.0","method":"VideoLibrary.Scan","id":1}'
|
||||
request = self._send_to_xbmc_json(update_command, host, sickbeard.XBMC_USERNAME, sickbeard.XBMC_PASSWORD)
|
||||
|
||||
if not request:
|
||||
logger.log(u'XBMC Full Library update failed on: ' + host, logger.ERROR)
|
||||
self._log_error(u'Full Library update failed on: ' + host)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
##############################################################################
|
||||
# Public functions which will call the JSON or Legacy HTTP API methods
|
||||
##############################################################################
|
||||
def _notify(self, title, body, hosts=None, username=None, password=None, **kwargs):
|
||||
"""Internal wrapper for the notify_snatch and notify_download functions
|
||||
|
||||
def notify_snatch(self, ep_name):
|
||||
if sickbeard.XBMC_NOTIFY_ONSNATCH:
|
||||
self._notify_xbmc(ep_name, common.notifyStrings[common.NOTIFY_SNATCH])
|
||||
Detects JSON-RPC version then branches the logic for either the JSON-RPC or legacy HTTP API methods.
|
||||
|
||||
def notify_download(self, ep_name):
|
||||
if sickbeard.XBMC_NOTIFY_ONDOWNLOAD:
|
||||
self._notify_xbmc(ep_name, common.notifyStrings[common.NOTIFY_DOWNLOAD])
|
||||
Args:
|
||||
title: Title of the notice to send
|
||||
body: Message body of the notice to send
|
||||
hosts: XBMC webserver host:port
|
||||
username: XBMC webserver username
|
||||
password: XBMC webserver password
|
||||
|
||||
def notify_subtitle_download(self, ep_name, lang):
|
||||
if sickbeard.XBMC_NOTIFY_ONSUBTITLEDOWNLOAD:
|
||||
self._notify_xbmc(ep_name + ': ' + lang, common.notifyStrings[common.NOTIFY_SUBTITLE_DOWNLOAD])
|
||||
|
||||
def notify_git_update(self, new_version = '??'):
|
||||
if sickbeard.USE_XBMC:
|
||||
update_text=common.notifyStrings[common.NOTIFY_GIT_UPDATE_TEXT]
|
||||
title=common.notifyStrings[common.NOTIFY_GIT_UPDATE]
|
||||
self._notify_xbmc(update_text + new_version, title)
|
||||
Returns:
|
||||
Returns a list results in the format of host:ip:result
|
||||
The result will either be 'OK' or False, this is used to be parsed by the calling function.
|
||||
|
||||
def test_notify(self, host, username, password):
|
||||
return self._notify_xbmc('Testing XBMC notifications from SickGear', 'Test Notification', host, username,
|
||||
password, force=True)
|
||||
"""
|
||||
hosts = self._choose(hosts, sickbeard.XBMC_HOST)
|
||||
username = self._choose(username, sickbeard.XBMC_USERNAME)
|
||||
password = self._choose(password, sickbeard.XBMC_PASSWORD)
|
||||
|
||||
def update_library(self, showName=None):
|
||||
success = False
|
||||
result = []
|
||||
for cur_host in [x.strip() for x in hosts.split(',')]:
|
||||
cur_host = urllib.unquote_plus(cur_host)
|
||||
|
||||
self._log(u'Sending notification to "%s"' % cur_host)
|
||||
|
||||
xbmcapi = self._get_xbmc_version(cur_host, username, password)
|
||||
if xbmcapi:
|
||||
if 4 >= xbmcapi:
|
||||
self._log_debug(u'Detected version <= 11, using HTTP API')
|
||||
command = dict(command='ExecBuiltIn',
|
||||
parameter='Notification(' + title.encode('utf-8') + ',' + body.encode('utf-8') + ')')
|
||||
notify_result = self._send_to_xbmc(command, cur_host, username, password)
|
||||
if notify_result:
|
||||
result += [cur_host + ':' + str(notify_result)]
|
||||
success |= 'OK' in notify_result or success
|
||||
else:
|
||||
self._log_debug(u'Detected version >= 12, using JSON API')
|
||||
command = '{"jsonrpc":"2.0","method":"GUI.ShowNotification",' \
|
||||
'"params":{"title":"%s","message":"%s", "image": "%s"},"id":1}' % \
|
||||
(title.encode('utf-8'), body.encode('utf-8'), self._sg_logo_url)
|
||||
notify_result = self._send_to_xbmc_json(command, cur_host, username, password)
|
||||
if notify_result.get('result'):
|
||||
result += [cur_host + ':' + notify_result['result'].decode(sickbeard.SYS_ENCODING)]
|
||||
success |= 'OK' in notify_result or success
|
||||
else:
|
||||
if sickbeard.XBMC_ALWAYS_ON or self._testing:
|
||||
self._log_error(u'Failed to detect version for "%s", check configuration and try again' % cur_host)
|
||||
result += [cur_host + ':No response']
|
||||
success = False
|
||||
|
||||
return self._choose(('Success, all hosts tested', '<br />\n'.join(result))[not bool(success)], bool(success))
|
||||
|
||||
def update_library(self, show_name=None, **kwargs):
|
||||
"""Public wrapper for the update library functions to branch the logic for JSON-RPC or legacy HTTP API
|
||||
|
||||
Checks the XBMC API version to branch the logic to call either the legacy HTTP API or the newer JSON-RPC over HTTP methods.
|
||||
Do the ability of accepting a list of hosts deliminated by comma, only one host is updated, the first to respond with success.
|
||||
Checks the XBMC API version to branch the logic to call either the legacy HTTP API
|
||||
or the newer JSON-RPC over HTTP methods.
|
||||
Do the ability of accepting a list of hosts delimited by comma, only one host is updated,
|
||||
the first to respond with success.
|
||||
This is a workaround for SQL backend users as updating multiple clients causes duplicate entries.
|
||||
Future plan is to revist how we store the host/ip/username/pw/options so that it may be more flexible.
|
||||
|
||||
Args:
|
||||
showName: Name of a TV show to specifically target the library update for
|
||||
show_name: Name of a TV show to specifically target the library update for
|
||||
|
||||
Returns:
|
||||
Returns True or False
|
||||
|
||||
"""
|
||||
if not sickbeard.XBMC_HOST:
|
||||
self._log_debug(u'No hosts specified, check your settings')
|
||||
return False
|
||||
|
||||
if sickbeard.USE_XBMC and sickbeard.XBMC_UPDATE_LIBRARY:
|
||||
if not sickbeard.XBMC_HOST:
|
||||
logger.log(u'No XBMC hosts specified, check your settings', logger.DEBUG)
|
||||
return False
|
||||
|
||||
# either update each host, or only attempt to update until one successful result
|
||||
result = 0
|
||||
for host in [x.strip() for x in sickbeard.XBMC_HOST.split(',')]:
|
||||
if self._send_update_library(host, showName):
|
||||
if sickbeard.XBMC_UPDATE_ONLYFIRST:
|
||||
logger.log(u'Successfully updated "' + host + '", stopped sending update library commands.',
|
||||
logger.DEBUG)
|
||||
return True
|
||||
else:
|
||||
if sickbeard.XBMC_ALWAYS_ON:
|
||||
logger.log(
|
||||
u'Failed to detect XBMC version for "' + host + '", check configuration and try again.',
|
||||
logger.ERROR)
|
||||
result = result + 1
|
||||
|
||||
# needed for the 'update xbmc' submenu command
|
||||
# as it only cares of the final result vs the individual ones
|
||||
if result == 0:
|
||||
return True
|
||||
# either update each host, or only attempt to update until one successful result
|
||||
result = 0
|
||||
for host in [x.strip() for x in sickbeard.XBMC_HOST.split(',')]:
|
||||
if self._send_update_library(host, show_name):
|
||||
if sickbeard.XBMC_UPDATE_ONLYFIRST:
|
||||
self._log_debug(u'Successfully updated "%s", stopped sending update library commands' % host)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
if sickbeard.XBMC_ALWAYS_ON:
|
||||
self._log_error(u'Failed to detect version for "%s", check configuration and try again' % host)
|
||||
result = result + 1
|
||||
|
||||
# needed for the 'update xbmc' submenu command
|
||||
# as it only cares of the final result vs the individual ones
|
||||
if not 0 != result:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
notifier = XBMCNotifier
|
||||
|
|
|
@ -247,7 +247,7 @@ class PostProcessor(object):
|
|||
self._log(u'Deleted file ' + cur_file, logger.DEBUG)
|
||||
|
||||
# do the library update for synoindex
|
||||
notifiers.synoindex_notifier.deleteFile(cur_file)
|
||||
notifiers.NotifierFactory().get('SYNOINDEX').deleteFile(cur_file)
|
||||
|
||||
def _combined_file_operation(self, file_path, new_path, new_base_name, associated_files=False, action=None,
|
||||
subtitles=False, action_tmpl=None):
|
||||
|
@ -900,7 +900,7 @@ class PostProcessor(object):
|
|||
try:
|
||||
ek.ek(os.mkdir, ep_obj.show.location)
|
||||
# do the library update for synoindex
|
||||
notifiers.synoindex_notifier.addFolder(ep_obj.show.location)
|
||||
notifiers.NotifierFactory().get('SYNOINDEX').addFolder(ep_obj.show.location)
|
||||
except (OSError, IOError):
|
||||
raise exceptions.PostProcessingFailed(u'Unable to create show directory: ' + ep_obj.show.location)
|
||||
|
||||
|
@ -1054,29 +1054,8 @@ class PostProcessor(object):
|
|||
# send notifications
|
||||
notifiers.notify_download(ep_obj._format_pattern('%SN - %Sx%0E - %EN - %QN'))
|
||||
|
||||
# do the library update for Emby
|
||||
notifiers.emby_notifier.update_library(ep_obj.show)
|
||||
|
||||
# do the library update for Kodi
|
||||
notifiers.kodi_notifier.update_library(ep_obj.show.name)
|
||||
|
||||
# do the library update for XBMC
|
||||
notifiers.xbmc_notifier.update_library(ep_obj.show.name)
|
||||
|
||||
# do the library update for Plex
|
||||
notifiers.plex_notifier.update_library(ep_obj)
|
||||
|
||||
# do the library update for NMJ
|
||||
# nmj_notifier kicks off its library update when the notify_download is issued (inside notifiers)
|
||||
|
||||
# do the library update for Synology Indexer
|
||||
notifiers.synoindex_notifier.addFile(ep_obj.location)
|
||||
|
||||
# do the library update for pyTivo
|
||||
notifiers.pytivo_notifier.update_library(ep_obj)
|
||||
|
||||
# do the library update for Trakt
|
||||
notifiers.trakt_notifier.update_collection(ep_obj)
|
||||
# trigger library updates
|
||||
notifiers.notify_update_library(ep_obj=ep_obj)
|
||||
|
||||
self._run_extra_scripts(ep_obj)
|
||||
|
||||
|
|
|
@ -410,8 +410,7 @@ class GitUpdateManager(UpdateManager):
|
|||
self._find_installed_version()
|
||||
|
||||
# Notify update successful
|
||||
if sickbeard.NOTIFY_ON_UPDATE:
|
||||
notifiers.notify_git_update(sickbeard.CUR_COMMIT_HASH if sickbeard.CUR_COMMIT_HASH else "")
|
||||
notifiers.notify_git_update(sickbeard.CUR_COMMIT_HASH if sickbeard.CUR_COMMIT_HASH else "")
|
||||
return True
|
||||
|
||||
return False
|
||||
|
|
|
@ -625,10 +625,10 @@ class Home(MainHandler):
|
|||
def HomeMenu(self):
|
||||
return [
|
||||
{'title': 'Process Media', 'path': 'home/postprocess/'},
|
||||
{'title': 'Update Emby', 'path': 'home/updateEMBY/', 'requires': self.haveEMBY},
|
||||
{'title': 'Update Kodi', 'path': 'home/updateKODI/', 'requires': self.haveKODI},
|
||||
{'title': 'Update XBMC', 'path': 'home/updateXBMC/', 'requires': self.haveXBMC},
|
||||
{'title': 'Update Plex', 'path': 'home/updatePLEX/', 'requires': self.havePLEX}
|
||||
{'title': 'Update Emby', 'path': 'home/update_emby/', 'requires': self.haveEMBY},
|
||||
{'title': 'Update Kodi', 'path': 'home/update_kodi/', 'requires': self.haveKODI},
|
||||
{'title': 'Update XBMC', 'path': 'home/update_xbmc/', 'requires': self.haveXBMC},
|
||||
{'title': 'Update Plex', 'path': 'home/update_plex/', 'requires': self.havePLEX}
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
|
@ -783,236 +783,191 @@ class Home(MainHandler):
|
|||
|
||||
client = clients.get_client_instance(torrent_method)
|
||||
|
||||
connection, accesMsg = client(host, username, password).test_authentication()
|
||||
connection, acces_msg = client(host, username, password).test_authentication()
|
||||
|
||||
return accesMsg
|
||||
return acces_msg
|
||||
|
||||
def testGrowl(self, host=None, password=None):
|
||||
@staticmethod
|
||||
def discover_emby():
|
||||
return notifiers.NotifierFactory().get('EMBY').discover_server()
|
||||
|
||||
def test_emby(self, host=None, apikey=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
hosts = config.clean_hosts(host, default_port=8096)
|
||||
if not hosts:
|
||||
return 'Fail: No valid host(s)'
|
||||
|
||||
result = notifiers.NotifierFactory().get('EMBY').test_notify(hosts, apikey)
|
||||
|
||||
ui.notifications.message('Tested Emby:', urllib.unquote_plus(hosts.replace(',', ', ')))
|
||||
return result
|
||||
|
||||
def test_kodi(self, host=None, username=None, password=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
hosts = config.clean_hosts(host, default_port=8080)
|
||||
if not hosts:
|
||||
return 'Fail: No valid host(s)'
|
||||
|
||||
if None is not password and set('*') == set(password):
|
||||
password = sickbeard.KODI_PASSWORD
|
||||
|
||||
result = notifiers.NotifierFactory().get('KODI').test_notify(hosts, username, password)
|
||||
|
||||
ui.notifications.message('Tested Kodi:', urllib.unquote_plus(hosts.replace(',', ', ')))
|
||||
return result
|
||||
|
||||
def test_plex(self, host=None, username=None, password=None, server=False):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
hosts = config.clean_hosts(host, default_port=32400)
|
||||
if not hosts:
|
||||
return 'Fail: No valid host(s)'
|
||||
|
||||
if None is not password and set('*') == set(password):
|
||||
password = sickbeard.PLEX_PASSWORD
|
||||
|
||||
server = 'true' == server
|
||||
n = notifiers.NotifierFactory().get('PLEX')
|
||||
method = n.test_update_library if server else n.test_notify
|
||||
result = method(hosts, username, password)
|
||||
|
||||
ui.notifications.message('Tested Plex %s(s): ' % ('client', 'Media Server host')[server],
|
||||
urllib.unquote_plus(hosts.replace(',', ', ')))
|
||||
return result
|
||||
|
||||
# def test_xbmc(self, host=None, username=None, password=None):
|
||||
# self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
#
|
||||
# hosts = config.clean_hosts(host, default_port=80)
|
||||
# if not hosts:
|
||||
# return 'Fail: No valid host(s)'
|
||||
#
|
||||
# if None is not password and set('*') == set(password):
|
||||
# password = sickbeard.XBMC_PASSWORD
|
||||
#
|
||||
# result = notifiers.NotifierFactory().get('XBMC').test_notify(hosts, username, password)
|
||||
#
|
||||
# ui.notifications.message('Tested XBMC: ', urllib.unquote_plus(hosts.replace(',', ', ')))
|
||||
# return result
|
||||
|
||||
def test_nmj(self, host=None, database=None, mount=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
host = config.clean_host(host)
|
||||
if not hosts:
|
||||
return 'Fail: No valid host(s)'
|
||||
|
||||
return notifiers.NotifierFactory().get('NMJ').test_notify(urllib.unquote_plus(host), database, mount)
|
||||
|
||||
def settings_nmj(self, host=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
host = config.clean_host(host)
|
||||
if not hosts:
|
||||
return 'Fail: No valid host(s)'
|
||||
|
||||
return notifiers.NotifierFactory().get('NMJ').notify_settings(urllib.unquote_plus(host))
|
||||
|
||||
def test_nmj2(self, host=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
host = config.clean_host(host)
|
||||
if not hosts:
|
||||
return 'Fail: No valid host(s)'
|
||||
|
||||
return notifiers.NotifierFactory().get('NMJV2').test_notify(urllib.unquote_plus(host))
|
||||
|
||||
def settings_nmj2(self, host=None, dbloc=None, instance=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
host = config.clean_host(host)
|
||||
return notifiers.NotifierFactory().get('NMJV2').notify_settings(urllib.unquote_plus(host), dbloc, instance)
|
||||
|
||||
def test_boxcar2(self, access_token=None, sound=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
if None is not access_token and starify(access_token, True):
|
||||
access_token = sickbeard.BOXCAR2_ACCESSTOKEN
|
||||
|
||||
return notifiers.NotifierFactory().get('BOXCAR2').test_notify(access_token, sound)
|
||||
|
||||
def test_pushbullet(self, access_token=None, device_iden=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
if None is not access_token and starify(access_token, True):
|
||||
access_token = sickbeard.PUSHBULLET_ACCESS_TOKEN
|
||||
|
||||
return notifiers.NotifierFactory().get('PUSHBULLET').test_notify(access_token, device_iden)
|
||||
|
||||
def get_pushbullet_devices(self, access_token=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
if None is not access_token and starify(access_token, True):
|
||||
access_token = sickbeard.PUSHBULLET_ACCESS_TOKEN
|
||||
|
||||
return notifiers.NotifierFactory().get('PUSHBULLET').get_devices(access_token)
|
||||
|
||||
def test_pushover(self, user_key=None, api_key=None, priority=None, device=None, sound=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
if None is not user_key and starify(user_key, True):
|
||||
user_key = sickbeard.PUSHOVER_USERKEY
|
||||
|
||||
if None is not api_key and starify(api_key, True):
|
||||
api_key = sickbeard.PUSHOVER_APIKEY
|
||||
|
||||
return notifiers.NotifierFactory().get('PUSHOVER').test_notify(user_key, api_key, priority, device, sound)
|
||||
|
||||
def get_pushover_devices(self, user_key=None, api_key=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
if None is not user_key and starify(user_key, True):
|
||||
user_key = sickbeard.PUSHOVER_USERKEY
|
||||
|
||||
if None is not api_key and starify(api_key, True):
|
||||
api_key = sickbeard.PUSHOVER_APIKEY
|
||||
|
||||
return notifiers.NotifierFactory().get('PUSHOVER').get_devices(user_key, api_key)
|
||||
|
||||
def test_growl(self, host=None, password=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
host = config.clean_host(host, default_port=23053)
|
||||
if None is not password and set('*') == set(password):
|
||||
password = sickbeard.GROWL_PASSWORD
|
||||
|
||||
result = notifiers.growl_notifier.test_notify(host, password)
|
||||
if password is None or password == '':
|
||||
pw_append = ''
|
||||
else:
|
||||
pw_append = ' with password: ' + password
|
||||
return notifiers.NotifierFactory().get('GROWL').test_notify(host, password)
|
||||
|
||||
if result:
|
||||
return 'Registered and Tested growl successfully ' + urllib.unquote_plus(host) + pw_append
|
||||
else:
|
||||
return 'Registration and Testing of growl failed ' + urllib.unquote_plus(host) + pw_append
|
||||
|
||||
def testProwl(self, prowl_api=None, prowl_priority=0):
|
||||
def test_prowl(self, prowl_api=None, prowl_priority=0):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
if None is not prowl_api and starify(prowl_api, True):
|
||||
prowl_api = sickbeard.PROWL_API
|
||||
|
||||
result = notifiers.prowl_notifier.test_notify(prowl_api, prowl_priority)
|
||||
if result:
|
||||
return 'Test prowl notice sent successfully'
|
||||
else:
|
||||
return 'Test prowl notice failed'
|
||||
return notifiers.NotifierFactory().get('PROWL').test_notify(prowl_api, prowl_priority)
|
||||
|
||||
def testBoxcar2(self, accesstoken=None, sound=None):
|
||||
def test_nma(self, nma_api=None, nma_priority=0):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
if None is not accesstoken and starify(accesstoken, True):
|
||||
accesstoken = sickbeard.BOXCAR2_ACCESSTOKEN
|
||||
if None is not nma_api and starify(nma_api, True):
|
||||
nma_api = sickbeard.NMA_API
|
||||
|
||||
result = notifiers.boxcar2_notifier.test_notify(accesstoken, sound)
|
||||
if result:
|
||||
return 'Boxcar2 notification succeeded. Check your Boxcar2 clients to make sure it worked'
|
||||
else:
|
||||
return 'Error sending Boxcar2 notification'
|
||||
return notifiers.NotifierFactory().get('NMA').test_notify(nma_api, nma_priority)
|
||||
|
||||
def testPushover(self, userKey=None, apiKey=None, priority=None, device=None, sound=None):
|
||||
def test_libnotify(self, *args, **kwargs):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
if None is not userKey and starify(userKey, True):
|
||||
userKey = sickbeard.PUSHOVER_USERKEY
|
||||
return notifiers.NotifierFactory().get('LIBNOTIFY').test_notify()
|
||||
|
||||
if None is not apiKey and starify(apiKey, True):
|
||||
apiKey = sickbeard.PUSHOVER_APIKEY
|
||||
|
||||
result = notifiers.pushover_notifier.test_notify(userKey, apiKey, priority, device, sound)
|
||||
if result:
|
||||
return 'Pushover notification succeeded. Check your Pushover clients to make sure it worked'
|
||||
else:
|
||||
return 'Error sending Pushover notification'
|
||||
|
||||
def getPushoverDevices(self, userKey=None, apiKey=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
if None is not userKey and starify(userKey, True):
|
||||
userKey = sickbeard.PUSHOVER_USERKEY
|
||||
|
||||
if None is not apiKey and starify(apiKey, True):
|
||||
apiKey = sickbeard.PUSHOVER_APIKEY
|
||||
|
||||
result = notifiers.pushover_notifier.get_devices(userKey, apiKey)
|
||||
if result:
|
||||
return result
|
||||
else:
|
||||
return "{}"
|
||||
|
||||
def twitterStep1(self, *args, **kwargs):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
return notifiers.twitter_notifier._get_authorization()
|
||||
|
||||
def twitterStep2(self, key):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
result = notifiers.twitter_notifier._get_credentials(key)
|
||||
logger.log(u'result: ' + str(result))
|
||||
if result:
|
||||
return 'Key verification successful'
|
||||
else:
|
||||
return 'Unable to verify key'
|
||||
|
||||
def testTwitter(self, *args, **kwargs):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
result = notifiers.twitter_notifier.test_notify()
|
||||
if result:
|
||||
return 'Tweet successful, check your twitter to make sure it worked'
|
||||
else:
|
||||
return 'Error sending tweet'
|
||||
|
||||
@staticmethod
|
||||
def discover_emby():
|
||||
return notifiers.emby_notifier.discover_server()
|
||||
|
||||
def testEMBY(self, host=None, apikey=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
hosts = config.clean_hosts(host)
|
||||
if not hosts:
|
||||
return 'Fail: At least one invalid host'
|
||||
|
||||
total_success, cur_message = notifiers.emby_notifier.test_notify(hosts, apikey)
|
||||
return (cur_message, u'Success. All Emby hosts tested.')[total_success]
|
||||
|
||||
def testKODI(self, host=None, username=None, password=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
hosts = config.clean_hosts(host)
|
||||
if not hosts:
|
||||
return 'Fail: At least one invalid host'
|
||||
|
||||
if None is not password and set('*') == set(password):
|
||||
password = sickbeard.KODI_PASSWORD
|
||||
|
||||
total_success, cur_message = notifiers.kodi_notifier.test_notify(hosts, username, password)
|
||||
return (cur_message, u'Success. All Kodi hosts tested.')[total_success]
|
||||
|
||||
def testXBMC(self, host=None, username=None, password=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
host = config.clean_hosts(host)
|
||||
if None is not password and set('*') == set(password):
|
||||
password = sickbeard.XBMC_PASSWORD
|
||||
|
||||
finalResult = ''
|
||||
for curHost in [x.strip() for x in host.split(',')]:
|
||||
curResult = notifiers.xbmc_notifier.test_notify(urllib.unquote_plus(curHost), username, password)
|
||||
if len(curResult.split(':')) > 2 and 'OK' in curResult.split(':')[2]:
|
||||
finalResult += 'Test XBMC notice sent successfully to ' + urllib.unquote_plus(curHost)
|
||||
else:
|
||||
finalResult += 'Test XBMC notice failed to ' + urllib.unquote_plus(curHost)
|
||||
finalResult += "<br />\n"
|
||||
|
||||
return finalResult
|
||||
|
||||
def testPMC(self, host=None, username=None, password=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
if None is not password and set('*') == set(password):
|
||||
password = sickbeard.PLEX_PASSWORD
|
||||
|
||||
finalResult = ''
|
||||
for curHost in [x.strip() for x in host.split(',')]:
|
||||
curResult = notifiers.plex_notifier.test_notify(urllib.unquote_plus(curHost), username, password)
|
||||
if len(curResult.split(':')) > 2 and 'OK' in curResult.split(':')[2]:
|
||||
finalResult += 'Successful test notice sent to Plex client ... ' + urllib.unquote_plus(curHost)
|
||||
else:
|
||||
finalResult += 'Test failed for Plex client ... ' + urllib.unquote_plus(curHost)
|
||||
finalResult += '<br />' + '\n'
|
||||
|
||||
ui.notifications.message('Tested Plex client(s): ', urllib.unquote_plus(host.replace(',', ', ')))
|
||||
|
||||
return finalResult
|
||||
|
||||
def testPMS(self, host=None, username=None, password=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
if None is not password and set('*') == set(password):
|
||||
password = sickbeard.PLEX_PASSWORD
|
||||
|
||||
cur_result = notifiers.plex_notifier.test_notify(urllib.unquote_plus(host), username, password, server=True)
|
||||
if '<br />' == cur_result:
|
||||
cur_result += 'Fail: No valid host set to connect with'
|
||||
final_result = (('Test result for', 'Successful test of')['Fail' not in cur_result]
|
||||
+ ' Plex server(s) ... %s<br />\n' % cur_result)
|
||||
|
||||
ui.notifications.message('Tested Plex Media Server host(s): ', urllib.unquote_plus(host.replace(',', ', ')))
|
||||
|
||||
return final_result
|
||||
|
||||
def testLibnotify(self, *args, **kwargs):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
if notifiers.libnotify_notifier.test_notify():
|
||||
return 'Tried sending desktop notification via libnotify'
|
||||
else:
|
||||
return notifiers.libnotify.diagnose()
|
||||
|
||||
def testNMJ(self, host=None, database=None, mount=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
host = config.clean_host(host)
|
||||
result = notifiers.nmj_notifier.test_notify(urllib.unquote_plus(host), database, mount)
|
||||
if result:
|
||||
return 'Successfully started the scan update'
|
||||
else:
|
||||
return 'Test failed to start the scan update'
|
||||
|
||||
def settingsNMJ(self, host=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
host = config.clean_host(host)
|
||||
result = notifiers.nmj_notifier.notify_settings(urllib.unquote_plus(host))
|
||||
if result:
|
||||
return '{"message": "Got settings from %(host)s", "database": "%(database)s", "mount": "%(mount)s"}' % {
|
||||
"host": host, "database": sickbeard.NMJ_DATABASE, "mount": sickbeard.NMJ_MOUNT}
|
||||
else:
|
||||
return '{"message": "Failed! Make sure your Popcorn is on and NMJ is running. (see Log & Errors -> Debug for detailed info)", "database": "", "mount": ""}'
|
||||
|
||||
def testNMJv2(self, host=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
host = config.clean_host(host)
|
||||
result = notifiers.nmjv2_notifier.test_notify(urllib.unquote_plus(host))
|
||||
if result:
|
||||
return 'Test notice sent successfully to ' + urllib.unquote_plus(host)
|
||||
else:
|
||||
return 'Test notice failed to ' + urllib.unquote_plus(host)
|
||||
|
||||
def settingsNMJv2(self, host=None, dbloc=None, instance=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
host = config.clean_host(host)
|
||||
result = notifiers.nmjv2_notifier.notify_settings(urllib.unquote_plus(host), dbloc, instance)
|
||||
if result:
|
||||
return '{"message": "NMJ Database found at: %(host)s", "database": "%(database)s"}' % {"host": host,
|
||||
"database": sickbeard.NMJv2_DATABASE}
|
||||
else:
|
||||
return '{"message": "Unable to find NMJ Database at location: %(dbloc)s. Is the right location selected and PCH running?", "database": ""}' % {
|
||||
"dbloc": dbloc}
|
||||
# def test_pushalot(self, authorization_token=None):
|
||||
# self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
#
|
||||
# if None is not authorization_token and starify(authorization_token, True):
|
||||
# authorization_token = sickbeard.PUSHALOT_AUTHORIZATIONTOKEN
|
||||
#
|
||||
# return notifiers.NotifierFactory().get('PUSHALOT').test_notify(authorization_token)
|
||||
|
||||
def trakt_authenticate(self, pin=None, account=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
@ -1063,7 +1018,7 @@ class Home(MainHandler):
|
|||
return json.dumps({'result': 'Not found: Account to delete'})
|
||||
return json.dumps({'result': 'Not found: Invalid account id'})
|
||||
|
||||
def loadShowNotifyLists(self, *args, **kwargs):
|
||||
def load_show_notify_lists(self, *args, **kwargs):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
my_db = db.DBConnection()
|
||||
|
@ -1086,6 +1041,51 @@ class Home(MainHandler):
|
|||
|
||||
return json.dumps(response)
|
||||
|
||||
def test_slack(self, channel=None, as_authed=False, bot_name=None, icon_url=None, access_token=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
return notifiers.NotifierFactory().get('SLACK').test_notify(
|
||||
channel=channel, as_authed='true' == as_authed,
|
||||
bot_name=bot_name, icon_url=icon_url, access_token=access_token)
|
||||
|
||||
def test_discordapp(self, as_authed=False, username=None, icon_url=None, as_tts=False, access_token=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
return notifiers.NotifierFactory().get('DISCORDAPP').test_notify(
|
||||
as_authed='true' == as_authed, username=username, icon_url=icon_url,
|
||||
as_tts='true' == as_tts, access_token=access_token)
|
||||
|
||||
def test_gitter(self, room_name=None, access_token=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
return notifiers.NotifierFactory().get('GITTER').test_notify(
|
||||
room_name=room_name, access_token=access_token)
|
||||
|
||||
def test_twitter(self, *args, **kwargs):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
return notifiers.NotifierFactory().get('TWITTER').test_notify()
|
||||
|
||||
def twitter_step1(self, *args, **kwargs):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
return notifiers.NotifierFactory().get('TWITTER').get_authorization()
|
||||
|
||||
def twitter_step2(self, key):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
return notifiers.NotifierFactory().get('TWITTER').get_credentials(key)
|
||||
|
||||
def test_email(self, host=None, port=None, smtp_from=None, use_tls=None, user=None, pwd=None, to=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
if None is not pwd and set('*') == set(pwd):
|
||||
pwd = sickbeard.EMAIL_PASSWORD
|
||||
|
||||
host = config.clean_host(host)
|
||||
|
||||
return notifiers.NotifierFactory().get('EMAIL').test_notify(host, port, smtp_from, use_tls, user, pwd, to)
|
||||
|
||||
@staticmethod
|
||||
def save_show_email(show=None, emails=None):
|
||||
# self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
@ -1099,64 +1099,6 @@ class Home(MainHandler):
|
|||
success = True
|
||||
return json.dumps({'id': show, 'success': success})
|
||||
|
||||
def testEmail(self, host=None, port=None, smtp_from=None, use_tls=None, user=None, pwd=None, to=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
if None is not pwd and set('*') == set(pwd):
|
||||
pwd = sickbeard.EMAIL_PASSWORD
|
||||
host = config.clean_host(host)
|
||||
|
||||
if notifiers.email_notifier.test_notify(host, port, smtp_from, use_tls, user, pwd, to):
|
||||
return 'Success. Test email sent. Check inbox.'
|
||||
return 'ERROR: %s' % notifiers.email_notifier.last_err
|
||||
|
||||
def testNMA(self, nma_api=None, nma_priority=0):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
if None is not nma_api and starify(nma_api, True):
|
||||
nma_api = sickbeard.NMA_API
|
||||
|
||||
result = notifiers.nma_notifier.test_notify(nma_api, nma_priority)
|
||||
if result:
|
||||
return 'Test NMA notice sent successfully'
|
||||
else:
|
||||
return 'Test NMA notice failed'
|
||||
|
||||
def testPushalot(self, authorizationToken=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
if None is not authorizationToken and starify(authorizationToken, True):
|
||||
authorizationToken = sickbeard.PUSHALOT_AUTHORIZATIONTOKEN
|
||||
|
||||
result = notifiers.pushalot_notifier.test_notify(authorizationToken)
|
||||
if result:
|
||||
return 'Pushalot notification succeeded. Check your Pushalot clients to make sure it worked'
|
||||
else:
|
||||
return 'Error sending Pushalot notification'
|
||||
|
||||
def testPushbullet(self, accessToken=None, device_iden=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
if None is not accessToken and starify(accessToken, True):
|
||||
accessToken = sickbeard.PUSHBULLET_ACCESS_TOKEN
|
||||
|
||||
return notifiers.pushbullet_notifier.test_notify(accessToken, device_iden)
|
||||
|
||||
def getPushbulletDevices(self, accessToken=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
if None is not accessToken and starify(accessToken, True):
|
||||
accessToken = sickbeard.PUSHBULLET_ACCESS_TOKEN
|
||||
|
||||
return notifiers.pushbullet_notifier.get_devices(accessToken)
|
||||
|
||||
def testSlack(self, access_token=None, channel=None, as_user=False, bot_name=None, icon_url=None):
|
||||
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
|
||||
|
||||
result = notifiers.slack_notifier.test_notify(channel, 'true' == as_user, bot_name, icon_url, access_token)
|
||||
return ('Error sending notification, Slack response: "%s"' % result,
|
||||
'Successful test notice sent. (Note: Slack clients display icon once in a sequence)')[True is result]
|
||||
|
||||
def viewchanges(self):
|
||||
|
||||
t = PageTemplate(headers=self.request.headers, file='viewchanges.tmpl')
|
||||
|
@ -1345,14 +1287,14 @@ class Home(MainHandler):
|
|||
t.submenu.append(
|
||||
{'title': 'Force Full Update', 'path': t.force_update})
|
||||
t.submenu.append({'title': 'Update show in Emby',
|
||||
'path': 'home/updateEMBY%s' %
|
||||
'path': 'home/update_emby%s' %
|
||||
(INDEXER_TVDB == showObj.indexer and ('?show=%s' % showObj.indexerid) or '/'),
|
||||
'requires': self.haveEMBY})
|
||||
t.submenu.append({'title': 'Update show in Kodi',
|
||||
'path': 'home/updateKODI?showName=%s' % urllib.quote_plus(
|
||||
'path': 'home/update_kodi?show_name=%s' % urllib.quote_plus(
|
||||
showObj.name.encode('utf-8')), 'requires': self.haveKODI})
|
||||
t.submenu.append({'title': 'Update show in XBMC',
|
||||
'path': 'home/updateXBMC?showName=%s' % urllib.quote_plus(
|
||||
'path': 'home/update_xbmc?show_name=%s' % urllib.quote_plus(
|
||||
showObj.name.encode('utf-8')), 'requires': self.haveXBMC})
|
||||
t.submenu.append({'title': 'Media Renamer', 'path': 'home/testRename?show=%d' % showObj.indexerid})
|
||||
if sickbeard.USE_SUBTITLES and not sickbeard.showQueueScheduler.action.isBeingSubtitled(
|
||||
|
@ -2039,16 +1981,16 @@ class Home(MainHandler):
|
|||
|
||||
self.redirect('/home/displayShow?show=' + str(showObj.indexerid))
|
||||
|
||||
def updateEMBY(self, show=None):
|
||||
def update_emby(self, show=None):
|
||||
|
||||
if notifiers.emby_notifier.update_library(
|
||||
sickbeard.helpers.findCertainShow(sickbeard.showList,helpers.tryInt(show, None)), force=True):
|
||||
if notifiers.NotifierFactory().get('EMBY').update_library(
|
||||
sickbeard.helpers.findCertainShow(sickbeard.showList, helpers.tryInt(show, None))):
|
||||
ui.notifications.message('Library update command sent to Emby host(s): ' + sickbeard.EMBY_HOST)
|
||||
else:
|
||||
ui.notifications.error('Unable to contact one or more Emby host(s): ' + sickbeard.EMBY_HOST)
|
||||
self.redirect('/home/')
|
||||
|
||||
def updateKODI(self, showName=None):
|
||||
def update_kodi(self, show_name=None):
|
||||
|
||||
# only send update to first host in the list -- workaround for kodi sql backend users
|
||||
if sickbeard.KODI_UPDATE_ONLYFIRST:
|
||||
|
@ -2057,29 +1999,14 @@ class Home(MainHandler):
|
|||
else:
|
||||
host = sickbeard.KODI_HOST
|
||||
|
||||
if notifiers.kodi_notifier.update_library(showName=showName, force=True):
|
||||
if notifiers.NotifierFactory().get('KODI').update_library(show_name=show_name):
|
||||
ui.notifications.message('Library update command sent to Kodi host(s): ' + host)
|
||||
else:
|
||||
ui.notifications.error('Unable to contact one or more Kodi host(s): ' + host)
|
||||
self.redirect('/home/')
|
||||
|
||||
def updateXBMC(self, showName=None):
|
||||
|
||||
# only send update to first host in the list -- workaround for xbmc sql backend users
|
||||
if sickbeard.XBMC_UPDATE_ONLYFIRST:
|
||||
# only send update to first host in the list -- workaround for xbmc sql backend users
|
||||
host = sickbeard.XBMC_HOST.split(',')[0].strip()
|
||||
else:
|
||||
host = sickbeard.XBMC_HOST
|
||||
|
||||
if notifiers.xbmc_notifier.update_library(showName=showName):
|
||||
ui.notifications.message('Library update command sent to XBMC host(s): ' + host)
|
||||
else:
|
||||
ui.notifications.error('Unable to contact one or more XBMC host(s): ' + host)
|
||||
self.redirect('/home/')
|
||||
|
||||
def updatePLEX(self, *args, **kwargs):
|
||||
result = notifiers.plex_notifier.update_library()
|
||||
def update_plex(self, *args, **kwargs):
|
||||
result = notifiers.NotifierFactory().get('PLEX').update_library()
|
||||
if 'Fail' not in result:
|
||||
ui.notifications.message(
|
||||
'Library update command sent to', 'Plex Media Server host(s): ' + sickbeard.PLEX_SERVER_HOST.replace(',', ', '))
|
||||
|
@ -2087,6 +2014,21 @@ class Home(MainHandler):
|
|||
ui.notifications.error('Unable to contact', 'Plex Media Server host(s): ' + result)
|
||||
self.redirect('/home/')
|
||||
|
||||
# def update_xbmc(self, show_name=None):
|
||||
#
|
||||
# # only send update to first host in the list -- workaround for xbmc sql backend users
|
||||
# if sickbeard.XBMC_UPDATE_ONLYFIRST:
|
||||
# # only send update to first host in the list -- workaround for xbmc sql backend users
|
||||
# host = sickbeard.XBMC_HOST.split(',')[0].strip()
|
||||
# else:
|
||||
# host = sickbeard.XBMC_HOST
|
||||
#
|
||||
# if notifiers.NotifierFactory().get('XBMC').update_library(show_name=show_name):
|
||||
# ui.notifications.message('Library update command sent to XBMC host(s): ' + host)
|
||||
# else:
|
||||
# ui.notifications.error('Unable to contact one or more XBMC host(s): ' + 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:
|
||||
|
@ -5711,55 +5653,64 @@ class ConfigNotifications(Config):
|
|||
'b64': base64.urlsafe_b64encode(location)})
|
||||
return t.respond()
|
||||
|
||||
def saveNotifications(self,
|
||||
use_emby=None, emby_update_library=None, emby_host=None, emby_apikey=None,
|
||||
use_kodi=None, kodi_always_on=None, kodi_notify_onsnatch=None, kodi_notify_ondownload=None,
|
||||
kodi_notify_onsubtitledownload=None, kodi_update_onlyfirst=None,
|
||||
kodi_update_library=None, kodi_update_full=None,
|
||||
kodi_host=None, kodi_username=None, kodi_password=None,
|
||||
use_xbmc=None, xbmc_always_on=None, xbmc_notify_onsnatch=None, xbmc_notify_ondownload=None,
|
||||
xbmc_notify_onsubtitledownload=None, xbmc_update_onlyfirst=None,
|
||||
xbmc_update_library=None, xbmc_update_full=None,
|
||||
xbmc_host=None, xbmc_username=None, xbmc_password=None,
|
||||
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_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,
|
||||
prowl_notify_onsubtitledownload=None, prowl_api=None, prowl_priority=0,
|
||||
use_twitter=None, twitter_notify_onsnatch=None, twitter_notify_ondownload=None,
|
||||
twitter_notify_onsubtitledownload=None,
|
||||
use_boxcar2=None, boxcar2_notify_onsnatch=None, boxcar2_notify_ondownload=None,
|
||||
boxcar2_notify_onsubtitledownload=None, boxcar2_accesstoken=None, boxcar2_sound=None,
|
||||
use_pushover=None, pushover_notify_onsnatch=None, pushover_notify_ondownload=None,
|
||||
pushover_notify_onsubtitledownload=None, pushover_userkey=None, pushover_apikey=None,
|
||||
pushover_priority=None, pushover_device=None, pushover_sound=None, pushover_device_list=None,
|
||||
use_libnotify=None, libnotify_notify_onsnatch=None, libnotify_notify_ondownload=None,
|
||||
libnotify_notify_onsubtitledownload=None,
|
||||
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_pin=None,
|
||||
trakt_remove_watchlist=None, trakt_use_watchlist=None, trakt_method_add=None,
|
||||
trakt_start_paused=None, trakt_sync=None,
|
||||
trakt_default_indexer=None, trakt_remove_serieslist=None, trakt_collection=None, trakt_accounts=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,
|
||||
use_nma=None, nma_notify_onsnatch=None, nma_notify_ondownload=None,
|
||||
nma_notify_onsubtitledownload=None, nma_api=None, nma_priority=0,
|
||||
use_pushalot=None, pushalot_notify_onsnatch=None, pushalot_notify_ondownload=None,
|
||||
pushalot_notify_onsubtitledownload=None, pushalot_authorizationtoken=None,
|
||||
use_pushbullet=None, pushbullet_notify_onsnatch=None, pushbullet_notify_ondownload=None,
|
||||
pushbullet_notify_onsubtitledownload=None, pushbullet_access_token=None,
|
||||
pushbullet_device_iden=None, pushbullet_device_list=None,
|
||||
use_email=None, email_notify_onsnatch=None, email_notify_ondownload=None,
|
||||
email_notify_onsubtitledownload=None, email_host=None, email_port=25, email_from=None,
|
||||
email_tls=None, email_user=None, email_password=None, email_list=None, email_show_list=None,
|
||||
email_show=None, use_slack=None, slack_notify_onsnatch=None, slack_notify_ondownload=None,
|
||||
slack_access_token=None, slack_channel=None, slack_as_user=None, slack_bot_name=None, slack_icon_url=None, **kwargs):
|
||||
def save_notifications(
|
||||
self,
|
||||
use_emby=None, emby_update_library=None, emby_host=None, emby_apikey=None,
|
||||
use_kodi=None, kodi_always_on=None, kodi_update_library=None, kodi_update_full=None,
|
||||
kodi_update_onlyfirst=None, kodi_host=None, kodi_username=None, kodi_password=None,
|
||||
kodi_notify_onsnatch=None, kodi_notify_ondownload=None, kodi_notify_onsubtitledownload=None,
|
||||
use_plex=None, plex_update_library=None, plex_username=None, plex_password=None, plex_server_host=None,
|
||||
plex_notify_onsnatch=None, plex_notify_ondownload=None, plex_notify_onsubtitledownload=None, plex_host=None,
|
||||
# use_xbmc=None, xbmc_always_on=None, xbmc_notify_onsnatch=None, xbmc_notify_ondownload=None,
|
||||
# xbmc_notify_onsubtitledownload=None, xbmc_update_onlyfirst=None,
|
||||
# xbmc_update_library=None, xbmc_update_full=None,
|
||||
# xbmc_host=None, xbmc_username=None, xbmc_password=None,
|
||||
use_nmj=None, nmj_host=None, nmj_database=None, nmj_mount=None,
|
||||
use_nmjv2=None, nmjv2_host=None, nmjv2_dbloc=None, nmjv2_database=None,
|
||||
use_synoindex=None, use_synologynotifier=None, synologynotifier_notify_onsnatch=None,
|
||||
synologynotifier_notify_ondownload=None, synologynotifier_notify_onsubtitledownload=None,
|
||||
use_pytivo=None, pytivo_host=None, pytivo_share_name=None, pytivo_tivo_name=None,
|
||||
# pytivo_notify_onsnatch=None, pytivo_notify_ondownload=None, pytivo_notify_onsubtitledownload=None,
|
||||
# pytivo_update_library=None,
|
||||
|
||||
use_boxcar2=None, boxcar2_notify_onsnatch=None, boxcar2_notify_ondownload=None,
|
||||
boxcar2_notify_onsubtitledownload=None, boxcar2_access_token=None, boxcar2_sound=None,
|
||||
use_pushbullet=None, pushbullet_notify_onsnatch=None, pushbullet_notify_ondownload=None,
|
||||
pushbullet_notify_onsubtitledownload=None, pushbullet_access_token=None, pushbullet_device_iden=None,
|
||||
use_pushover=None, pushover_notify_onsnatch=None, pushover_notify_ondownload=None,
|
||||
pushover_notify_onsubtitledownload=None, pushover_userkey=None, pushover_apikey=None,
|
||||
pushover_priority=None, pushover_device=None, pushover_sound=None, pushover_device_list=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,
|
||||
prowl_notify_onsubtitledownload=None, prowl_api=None, prowl_priority=0,
|
||||
use_nma=None, nma_notify_onsnatch=None, nma_notify_ondownload=None,
|
||||
nma_notify_onsubtitledownload=None, nma_api=None, nma_priority=0,
|
||||
use_libnotify=None, libnotify_notify_onsnatch=None, libnotify_notify_ondownload=None,
|
||||
libnotify_notify_onsubtitledownload=None,
|
||||
# use_pushalot=None, pushalot_notify_onsnatch=None, pushalot_notify_ondownload=None,
|
||||
# pushalot_notify_onsubtitledownload=None, pushalot_authorizationtoken=None,
|
||||
|
||||
use_trakt=None,
|
||||
# trakt_pin=None, trakt_remove_watchlist=None, trakt_use_watchlist=None, trakt_method_add=None,
|
||||
# trakt_start_paused=None, trakt_sync=None, trakt_default_indexer=None, trakt_remove_serieslist=None,
|
||||
# trakt_collection=None, trakt_accounts=None,
|
||||
use_slack=None, slack_notify_onsnatch=None, slack_notify_ondownload=None,
|
||||
slack_notify_onsubtitledownload=None, slack_access_token=None, slack_channel=None,
|
||||
slack_as_authed=None, slack_bot_name=None, slack_icon_url=None,
|
||||
use_discordapp=None, discordapp_notify_onsnatch=None, discordapp_notify_ondownload=None,
|
||||
discordapp_notify_onsubtitledownload=None, discordapp_access_token=None,
|
||||
discordapp_as_authed=None, discordapp_username=None, discordapp_icon_url=None,
|
||||
discordapp_as_tts=None,
|
||||
use_gitter=None, gitter_notify_onsnatch=None, gitter_notify_ondownload=None,
|
||||
gitter_notify_onsubtitledownload=None, gitter_access_token=None, gitter_room=None,
|
||||
use_twitter=None, twitter_notify_onsnatch=None, twitter_notify_ondownload=None,
|
||||
twitter_notify_onsubtitledownload=None,
|
||||
use_email=None, email_notify_onsnatch=None, email_notify_ondownload=None,
|
||||
email_notify_onsubtitledownload=None, email_host=None, email_port=25, email_from=None,
|
||||
email_tls=None, email_user=None, email_password=None, email_list=None,
|
||||
# email_show_list=None, email_show=None,
|
||||
**kwargs):
|
||||
|
||||
results = []
|
||||
|
||||
|
@ -5795,18 +5746,18 @@ class ConfigNotifications(Config):
|
|||
if set('*') != set(kodi_password):
|
||||
sickbeard.KODI_PASSWORD = kodi_password
|
||||
|
||||
sickbeard.USE_XBMC = config.checkbox_to_value(use_xbmc)
|
||||
sickbeard.XBMC_ALWAYS_ON = config.checkbox_to_value(xbmc_always_on)
|
||||
sickbeard.XBMC_NOTIFY_ONSNATCH = config.checkbox_to_value(xbmc_notify_onsnatch)
|
||||
sickbeard.XBMC_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(xbmc_notify_ondownload)
|
||||
sickbeard.XBMC_NOTIFY_ONSUBTITLEDOWNLOAD = config.checkbox_to_value(xbmc_notify_onsubtitledownload)
|
||||
sickbeard.XBMC_UPDATE_LIBRARY = config.checkbox_to_value(xbmc_update_library)
|
||||
sickbeard.XBMC_UPDATE_FULL = config.checkbox_to_value(xbmc_update_full)
|
||||
sickbeard.XBMC_UPDATE_ONLYFIRST = config.checkbox_to_value(xbmc_update_onlyfirst)
|
||||
sickbeard.XBMC_HOST = config.clean_hosts(xbmc_host)
|
||||
sickbeard.XBMC_USERNAME = xbmc_username
|
||||
if set('*') != set(xbmc_password):
|
||||
sickbeard.XBMC_PASSWORD = xbmc_password
|
||||
# sickbeard.USE_XBMC = config.checkbox_to_value(use_xbmc)
|
||||
# sickbeard.XBMC_ALWAYS_ON = config.checkbox_to_value(xbmc_always_on)
|
||||
# sickbeard.XBMC_NOTIFY_ONSNATCH = config.checkbox_to_value(xbmc_notify_onsnatch)
|
||||
# sickbeard.XBMC_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(xbmc_notify_ondownload)
|
||||
# sickbeard.XBMC_NOTIFY_ONSUBTITLEDOWNLOAD = config.checkbox_to_value(xbmc_notify_onsubtitledownload)
|
||||
# sickbeard.XBMC_UPDATE_LIBRARY = config.checkbox_to_value(xbmc_update_library)
|
||||
# sickbeard.XBMC_UPDATE_FULL = config.checkbox_to_value(xbmc_update_full)
|
||||
# sickbeard.XBMC_UPDATE_ONLYFIRST = config.checkbox_to_value(xbmc_update_onlyfirst)
|
||||
# sickbeard.XBMC_HOST = config.clean_hosts(xbmc_host)
|
||||
# sickbeard.XBMC_USERNAME = xbmc_username
|
||||
# if set('*') != set(xbmc_password):
|
||||
# sickbeard.XBMC_PASSWORD = xbmc_password
|
||||
|
||||
sickbeard.USE_PLEX = config.checkbox_to_value(use_plex)
|
||||
sickbeard.PLEX_NOTIFY_ONSNATCH = config.checkbox_to_value(plex_notify_onsnatch)
|
||||
|
@ -5845,7 +5796,7 @@ class ConfigNotifications(Config):
|
|||
sickbeard.BOXCAR2_NOTIFY_ONSNATCH = config.checkbox_to_value(boxcar2_notify_onsnatch)
|
||||
sickbeard.BOXCAR2_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(boxcar2_notify_ondownload)
|
||||
sickbeard.BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD = config.checkbox_to_value(boxcar2_notify_onsubtitledownload)
|
||||
key = boxcar2_accesstoken.strip()
|
||||
key = boxcar2_access_token.strip()
|
||||
if not starify(key, True):
|
||||
sickbeard.BOXCAR2_ACCESSTOKEN = key
|
||||
sickbeard.BOXCAR2_SOUND = boxcar2_sound
|
||||
|
@ -5888,8 +5839,8 @@ class ConfigNotifications(Config):
|
|||
synologynotifier_notify_onsubtitledownload)
|
||||
|
||||
sickbeard.USE_TRAKT = config.checkbox_to_value(use_trakt)
|
||||
# sickbeard.traktCheckerScheduler.silent = not sickbeard.USE_TRAKT
|
||||
sickbeard.TRAKT_UPDATE_COLLECTION = build_config(**kwargs)
|
||||
# sickbeard.traktCheckerScheduler.silent = not sickbeard.USE_TRAKT
|
||||
# sickbeard.TRAKT_DEFAULT_INDEXER = int(trakt_default_indexer)
|
||||
# sickbeard.TRAKT_SYNC = config.checkbox_to_value(trakt_sync)
|
||||
# sickbeard.TRAKT_USE_WATCHLIST = config.checkbox_to_value(trakt_use_watchlist)
|
||||
|
@ -5901,12 +5852,30 @@ class ConfigNotifications(Config):
|
|||
sickbeard.USE_SLACK = config.checkbox_to_value(use_slack)
|
||||
sickbeard.SLACK_NOTIFY_ONSNATCH = config.checkbox_to_value(slack_notify_onsnatch)
|
||||
sickbeard.SLACK_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(slack_notify_ondownload)
|
||||
sickbeard.SLACK_NOTIFY_ONSUBTITLEDOWNLOAD = config.checkbox_to_value(slack_notify_onsubtitledownload)
|
||||
sickbeard.SLACK_ACCESS_TOKEN = slack_access_token
|
||||
sickbeard.SLACK_CHANNEL = slack_channel
|
||||
sickbeard.SLACK_AS_USER = config.checkbox_to_value(slack_as_user)
|
||||
sickbeard.SLACK_AS_AUTHED = config.checkbox_to_value(slack_as_authed)
|
||||
sickbeard.SLACK_BOT_NAME = slack_bot_name
|
||||
sickbeard.SLACK_ICON_URL = slack_icon_url
|
||||
|
||||
sickbeard.USE_DISCORDAPP = config.checkbox_to_value(use_discordapp)
|
||||
sickbeard.DISCORDAPP_NOTIFY_ONSNATCH = config.checkbox_to_value(discordapp_notify_onsnatch)
|
||||
sickbeard.DISCORDAPP_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(discordapp_notify_ondownload)
|
||||
sickbeard.DISCORDAPP_NOTIFY_ONSUBTITLEDOWNLOAD = config.checkbox_to_value(discordapp_notify_onsubtitledownload)
|
||||
sickbeard.DISCORDAPP_ACCESS_TOKEN = discordapp_access_token
|
||||
sickbeard.DISCORDAPP_AS_AUTHED = config.checkbox_to_value(discordapp_as_authed)
|
||||
sickbeard.DISCORDAPP_USERNAME = discordapp_username
|
||||
sickbeard.DISCORDAPP_ICON_URL = discordapp_icon_url
|
||||
sickbeard.DISCORDAPP_AS_TTS = config.checkbox_to_value(discordapp_as_tts)
|
||||
|
||||
sickbeard.USE_GITTER = config.checkbox_to_value(use_gitter)
|
||||
sickbeard.GITTER_NOTIFY_ONSNATCH = config.checkbox_to_value(gitter_notify_onsnatch)
|
||||
sickbeard.GITTER_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(gitter_notify_ondownload)
|
||||
sickbeard.GITTER_NOTIFY_ONSUBTITLEDOWNLOAD = config.checkbox_to_value(gitter_notify_onsubtitledownload)
|
||||
sickbeard.GITTER_ACCESS_TOKEN = gitter_access_token
|
||||
sickbeard.GITTER_ROOM = gitter_room
|
||||
|
||||
sickbeard.USE_EMAIL = config.checkbox_to_value(use_email)
|
||||
sickbeard.EMAIL_NOTIFY_ONSNATCH = config.checkbox_to_value(email_notify_onsnatch)
|
||||
sickbeard.EMAIL_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(email_notify_ondownload)
|
||||
|
@ -5921,13 +5890,13 @@ class ConfigNotifications(Config):
|
|||
sickbeard.EMAIL_LIST = email_list
|
||||
|
||||
sickbeard.USE_PYTIVO = config.checkbox_to_value(use_pytivo)
|
||||
sickbeard.PYTIVO_NOTIFY_ONSNATCH = config.checkbox_to_value(pytivo_notify_onsnatch)
|
||||
sickbeard.PYTIVO_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(pytivo_notify_ondownload)
|
||||
sickbeard.PYTIVO_NOTIFY_ONSUBTITLEDOWNLOAD = config.checkbox_to_value(pytivo_notify_onsubtitledownload)
|
||||
sickbeard.PYTIVO_UPDATE_LIBRARY = config.checkbox_to_value(pytivo_update_library)
|
||||
sickbeard.PYTIVO_HOST = config.clean_host(pytivo_host)
|
||||
sickbeard.PYTIVO_SHARE_NAME = pytivo_share_name
|
||||
sickbeard.PYTIVO_TIVO_NAME = pytivo_tivo_name
|
||||
# sickbeard.PYTIVO_NOTIFY_ONSNATCH = config.checkbox_to_value(pytivo_notify_onsnatch)
|
||||
# sickbeard.PYTIVO_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(pytivo_notify_ondownload)
|
||||
# sickbeard.PYTIVO_NOTIFY_ONSUBTITLEDOWNLOAD = config.checkbox_to_value(pytivo_notify_onsubtitledownload)
|
||||
# sickbeard.PYTIVO_UPDATE_LIBRARY = config.checkbox_to_value(pytivo_update_library)
|
||||
|
||||
sickbeard.USE_NMA = config.checkbox_to_value(use_nma)
|
||||
sickbeard.NMA_NOTIFY_ONSNATCH = config.checkbox_to_value(nma_notify_onsnatch)
|
||||
|
@ -5938,13 +5907,13 @@ class ConfigNotifications(Config):
|
|||
sickbeard.NMA_API = key
|
||||
sickbeard.NMA_PRIORITY = nma_priority
|
||||
|
||||
sickbeard.USE_PUSHALOT = config.checkbox_to_value(use_pushalot)
|
||||
sickbeard.PUSHALOT_NOTIFY_ONSNATCH = config.checkbox_to_value(pushalot_notify_onsnatch)
|
||||
sickbeard.PUSHALOT_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(pushalot_notify_ondownload)
|
||||
sickbeard.PUSHALOT_NOTIFY_ONSUBTITLEDOWNLOAD = config.checkbox_to_value(pushalot_notify_onsubtitledownload)
|
||||
key = pushalot_authorizationtoken.strip()
|
||||
if not starify(key, True):
|
||||
sickbeard.PUSHALOT_AUTHORIZATIONTOKEN = key
|
||||
# sickbeard.USE_PUSHALOT = config.checkbox_to_value(use_pushalot)
|
||||
# sickbeard.PUSHALOT_NOTIFY_ONSNATCH = config.checkbox_to_value(pushalot_notify_onsnatch)
|
||||
# sickbeard.PUSHALOT_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(pushalot_notify_ondownload)
|
||||
# sickbeard.PUSHALOT_NOTIFY_ONSUBTITLEDOWNLOAD = config.checkbox_to_value(pushalot_notify_onsubtitledownload)
|
||||
# key = pushalot_authorizationtoken.strip()
|
||||
# if not starify(key, True):
|
||||
# sickbeard.PUSHALOT_AUTHORIZATIONTOKEN = key
|
||||
|
||||
sickbeard.USE_PUSHBULLET = config.checkbox_to_value(use_pushbullet)
|
||||
sickbeard.PUSHBULLET_NOTIFY_ONSNATCH = config.checkbox_to_value(pushbullet_notify_onsnatch)
|
||||
|
|