From 0fc796c78f8ad238acc28d3a4ef15fb757554787 Mon Sep 17 00:00:00 2001 From: Supremicus Date: Sat, 13 Feb 2016 21:36:02 +1000 Subject: [PATCH] Add Emby notifier --- CHANGES.md | 1 + gui/slick/css/fonts/sgicons.eot | Bin 8504 -> 8596 bytes gui/slick/css/fonts/sgicons.svg | 1 + gui/slick/css/fonts/sgicons.ttf | Bin 8340 -> 8432 bytes gui/slick/css/fonts/sgicons.woff | Bin 8416 -> 8508 bytes gui/slick/css/style.css | 16 +-- gui/slick/images/notifiers/emby.png | Bin 0 -> 1312 bytes .../default/config_notifications.tmpl | 41 ++++++++ gui/slick/interfaces/default/inc_top.tmpl | 3 + gui/slick/js/configNotifications.js | 27 +++++ gui/slick/js/inc_top.js | 1 + sickbeard/__init__.py | 18 +++- sickbeard/notifiers/__init__.py | 2 + sickbeard/notifiers/emby.py | 99 ++++++++++++++++++ sickbeard/postProcessor.py | 3 + sickbeard/webserve.py | 34 ++++++ 16 files changed, 238 insertions(+), 8 deletions(-) create mode 100644 gui/slick/images/notifiers/emby.png create mode 100644 sickbeard/notifiers/emby.py diff --git a/CHANGES.md b/CHANGES.md index 8715e8a5..25f512b6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -30,6 +30,7 @@ * Allow episode status "Skipped" to be changed to "Downloaded" * Allow found "Skipped" episode files to be set "Unknown" quality * Add CPU throttling preset "Disabled" to config/General/Advanced Settings +* Add Emby notifier ### 0.11.5 (2016-02-01 19:40:00 UTC) diff --git a/gui/slick/css/fonts/sgicons.eot b/gui/slick/css/fonts/sgicons.eot index 7e046292f71f9017d93408de8bbf11b896604621..dd64fd4079f948b5d42a70b27f7a69127db81ed2 100644 GIT binary patch delta 1794 zcmYjReQZ-z6hHUf_Pws{UfZ=@*U@dSeRSP`4EE7>;x@jF%qUEVA!vwDSltL)JNJPg zW*wrEppuMBkj$7UG5%o`Av1B{oP!M^F-BZ4VZ?6{7+)bwP)OfOgKsc^h(5OWXDr&YW6*3*|ll-`duAtl9L}k3oQ<^JtH>qC)tZP5~5S49sqA z@7{I*4Bw($gYx@r$xX4|ftMDed=BN+?XhhgFuxjZ0F;!WLQ2HiEuJ(45t|{9~d$bRxkmFn1FOC zFa?^TX_+|b5@aQ!{aI-6gfRNy?0PMtF$edQYy$B39exKvdYVY;Z`@)c)zVvO`wU^e z!9LDOqej3L<#6E~A~@u*?3NvbDk@beQHYY#RE_kKjpR+W=L7x2)K@iKQ_LoeuLq5g z2W~8iHL#Kp5<<~X)D!gx9wLXiy~-GUUfsuf`SN-WR%vJGXLJaRxVCIW=z&wk3GN_~ z(su4_`3b;v8??JLP4|EkN?lrlTGkEQA*9#2?_@4p)A)zbX=T z)ReNZoMukh!{(Te4*qOjNSpY~3d4SUJ%Zcs4&qYfu;1gT4$~rajMR^)>bUwEH?&CQ z6@@HGrFuttm5~t|QOA{(a(V>+2vW-sa^` zavVqdm}n0$t36l(*nmqLr`PE)0cl*Qg|4U{~_AsZK*lv46sqV^a-_ZN?IC!BN zz$rVT#k9Jrfs4u_R<7VeQ75}Z91uCTtC-XK4-;%YT+@I;F)Bq) zW)*qu0gAIWjR$W&ZCAu9MvIt0XscU16kA9U_-ezNt>n zy{1l=W%O4_@5pH8Iw0PG$gT@3*4ygtfW?rxIVLWCxmyP_TpObb{R%}BJ6JrID>JZD z#qY9pCEli=xB~7m?m6xg3bd6TH>@#!WW33{_-_7RVX3fJm@oxQ=W+^j&Y8O{g_c9s zGHac6D0gk{NzowA!-V>6R0h|;j2x$l`Tfq62#2$`6afLXx{p3-0&K`)ia3(R2E;vC zY{cW?&SD<*PiCQ`s6 z8EW7|c0_Xf*Tg%!HYXEOWkn^dfjD$R7i@+kBrvWLDxgyL(#gF#-qO<+>%<&!EZhTa uI7FSgrSS1&qFZW-C*qy4?s&7bal7=yrsRrbGEu>Pm!gE?&Un}YzwjSU&a-6z delta 1688 zcmYjReQXp(6n`_bw|mzfdw2AwdrNykYoT|yy(07jiUCXwNT}b@9&M#0lqjNE(Bp1f)i?x9gj`s?6rS`Mvk% zy_q-f?VRWsTjgrj0Zxp#($eMA-D^kMJIT_K_1ee>!5r%ZfHO$lJp-A+aM$;H07x~; zom(M-yO6ftqGhb7^`=hJ5m%rm41^Z-H9H;|}(m9`7s%x+o!5{(|G6|7AbSLC6lhiLJD zVH%nd@DNt8naxOn6mpL3kp1{=CP(d^?-!YBA9RE=VqXLPlk1hZ3>8p`nTWjtI#c3G zJRMJm(jg^843i!!oM+G5S03YsBYt8 zFhL0y)2<{Svhu~p;Lyq}OJ=K&SS_sMN0e&5!Cg*6 zj6dgIL$e$CNA6~pwU2S1yOQ5=e_G{-<$#Sr^q8y#f|W@<(PRcvbp{{z^fImRE{~Rd zMqu<}*>d)z0wG*!>iC6}xTb5hcxJ=YLxH53WHt7A(lRm8Zco~W=vbRA7Yc1;WiGcL zb%luu)?`l>a)oavFcr*wiF1?v#lC{2(88Zl+la#7RG0B-b$OKvcp>Fj`n&7^7Da~y zbigL)<1wGXQ@#K(c$ZJ-eMNNGSIcDU2LISs!zeqzZ~0>UcVBIULeQCHzp*o*LJJ;R z(@o>Q^bDTxo9s1vgm3k?)>4q2DRz&ILl{y36-FQ(VaY@*O&cmRVhuE&HVD7quV+7y zUHqPZO%6LyL6^VLMZ(glSdc2Hff&T09vU%(C?zqIni0|rGZ_#}7mT6G)CFrH$OV}$ zu})#PEtTxGO2hu9IMZDPF^*W4Ky2LdvOfzu^ZE5_^Le`JM;!K*JdSk;P7096wwIl2 z>}Ncf1`mp#g`^s&5mTuaj8Ku2*+?)WGu4Vx1hpzP8o3o1s1?7OgjZ(am1(dU_Mz|n zej-le=+XHr@hU1#M#Hcd7bKd}twgj-ynz@oSy74Tp-_Y*`N7~W<|iNWd%@|^3tA^3 zSso7UAdUQJ=)&mJRbj$=s_OYW3ohYbJ~|w3V?i%O@XV0LM#MhEHWXbrRb$ZR!~8}~ z2cA$o0!VASPhWn)x8g38OQM%xdhvC9?n*O%FOsHl;Q7c~-9m?@3S!(Q*a$P^I2k8b zX*GS6Zl+(dGB(Vnq+ZwOE=#VHUsY7)Ddm)M+r8R7>sjHMEjwAhqI}ZZ>FxGjp0{t_ zjQT)v|2X`LF~y+#Wxay`vCx~tjxE|mU#qZY4l~4y=CFjgYYw~cjZo*X3<|cl$V;3w z1wG>7l02vpF-y1+Tnah_TVN{&&%$;Xf+wI4Ucz~6 rQI2BAHo#JTDQZ;XE%o8jov;lu#U=N-@m4)effbWJu1g$AJkI|BY-g1F diff --git a/gui/slick/css/fonts/sgicons.svg b/gui/slick/css/fonts/sgicons.svg index c3a6f563..36968466 100644 --- a/gui/slick/css/fonts/sgicons.svg +++ b/gui/slick/css/fonts/sgicons.svg @@ -42,4 +42,5 @@ + \ No newline at end of file diff --git a/gui/slick/css/fonts/sgicons.ttf b/gui/slick/css/fonts/sgicons.ttf index 595bb99ef80456c9dda636ee700598db54d4d863..c6ce308a1ce59c3ee69cb08210c05b90127b006f 100644 GIT binary patch delta 1749 zcmZuxdu&r>6hG&F?Y-@`wkvIS-CWn(UP?D0Wv{k?dr&2SQJl<#fFMw|x-Hq-xd#t1 z+k7We3Gx{<7!xJNKhXHd{9_vmcdL&-Y;*4UopZi( z&UemtzVD6r&3Hc`0OY~|Feq<$Y(b@CmRL-hLqwaJIue}|HRE>x(9o@KNpyD-&nI~s zaZ5}4_9st%wecp&0|3QqT9b)p%P&6!0lZ^mkG7IR`jSlml#mb1ZtdvVwg*h#l3Y#l zyY1-8gb!UXI_UxQ&VsF-S+;f#d`0hl&-iftxs0JH;bHTOK{Lz(57kFGD3cVDLa|Uxj>(dYYDCzn9cLT# z-Ta)ntX6+>w%fx^! zSRJjZEbzERH83a0w~EdDg7`U`4J-H+v6z1<<)VupmX@QJiRSw9!PM%yuZJ1q)!_eVNrcl>(@?<&>1brTxY+t15ge%{2N<6&#^D! zOYMsbL8MUycAE{*5GrJj!zxJ7rucrl;$*ZnFV1YO7OUqVzhGDLnL4Ar4=baFmh1MQ zFm;KSJKVh9Va8)T?s(V-l+~RXW!Kmtut5!l^!Ov{KXvjC9T7pd@QaQ*hp|-5e9iu3 z2cZzEcsRd+dbTWoKJMea`OA|zAd&ShSxsQ2U`}dCA$Y+LK_~-o8K;bqDvv6A)QF3S zh9%-8IY{Ir0}}TWm)NZ-JMNg8{?*f0*rI9L)N2Od<<7!d42+HGd(NIcO9Lxrer7*X zPB-O<()O#vf*P<39(RdQRaqxQFc20Z)paD4kkT%we9+kuU^FA6G$V(|F9AEq@4r4q zjG^LLapdb=Z~6-1X|h*FV|8d8d?<|6S;>yZG{_~$UG6Oc4)Isr-w&%wl%h6c9VTR#iK_CO5rNSHGTqrR&ntKzPx@Tn_)91O@d8X%m_!b z*hIKDi_LT=JXtJ~{-G?+fg%_*o-&h_Gx?w3DS^)?$ybz?zVJFsR&oVYDhvCncJpK3RFNCDzaW& z_o+)8lP$gNi7pD8q!_)>PSe-*zhRf9Q$0#cGL`H~^dy^=&D)g+o6;-O=~S54Dluf! NP1~iIIFa$_{{=INpIiU{ delta 1643 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 L_JSH^BdYfwh`pYy delta 1642 zcmZuyeQXp(6n`^w-R*sJ@0Q-))q1_&-cl~bT4=A^E0*@CARp!wB%l}*?wi+lRBe0mvY}$q3YL{(5{M zJ0f&va9mkNU|?wc8PuHzh-?zLx^A<3us_=e5DlPByFkTmZ*CBks9THUg#z+t?B~JZ zy=;g5zXiv!B3j|*uIz9>>M#I#NMJ;H zV}5sTWG|Y0e#ed>V=mx6(oN^$JVWZ!RHzU^lp_BixS#`j59UPiArPC3JC@_14r9$h zkrU@etxI_DbL1(*4G`8w<)}Cw)X+g<|FEVw0LFyyG7s?;A23W^unro#G@!4?0~;jdAcS5oq)AO^thy{!BA3fZ8z1nMF#J6 z_c6_SmtS$8CNMGP>10pH5JpQ=$0sZ&G+k@J#0^sqt4TA->g-FTYjSdhJ!QW{&!%n3 zveKj{pFfB?YjTpc+f!EF`f?I;!_8RY++=^UPhlx^ajK+=%zKn&d|c_QQ2=)c3!mL) zFW@QZ&v0vGFP@oHQ z&~($dldZvj@SE(geT=Jt?kWnBbD90k&Ot4-@vVUnH3%OKG_mi zDE4%pCEAffK`?f%;A4MSqtny;Nmc2YUU%)9ePUWPtbNJ3&VIz@XmFpnF{G`T8gYec z!6;3(c2l#3#G9$v){UbmY87g<@DJ4C265k9xGx3V7Y%yh0Q%nTC*rhBBp3AH22`J5 zwXmNn!3at6#ld|nK;GvkgI6a;wG1ISelfI%wD4s3n+Z}=OSreDiLburd%V*Vk=is1 z`XGvngA`U778%xsGf(@M-v+Ns6bf8zyfOZ7&(}s% zGy(j@=-WJ2ul3B*2z{9L(odL&?PZr;eHCXa3eqC!Raud@$fx96m8&Y}-OJtco)g~X z-YH+kx6LUl)6?o;9GM7Nc;uH%P zU#T|OF1|x9H)=%85--Ip@lwnZFU3T@3LQ#8OD`mJURHK41s#C_*nz=wunR`uap;F< qvENY~$FSm>VJXkYj0kMRR|7B#Ly#?++~vmSVkrs?egFKP)9gAFGT zOmspiKJptb92Q0mJJWo>xqEKrnzb65M*9j}*|-{5gtJA>fM)~B1$Y1fXAleRjVKN{ z5V%9Q&{4^{mdCJfvnUrKDZ{C3+ zKLQsa%KY5&7~8j%!xIu(ZeJ0wUaqe)BF1`^CsF=8Hgy8e&z!ffT*$ts%w0VXo(~_G z2-b$9#%GGE3UI5EDH8v-1e`d=mIg(ul6O4&5ZgCQ0R!~4OGAE4C=_va(2BHg_wQk!j%9+^=oR2A1C>t$(WAPSUF$~WxD z8C2?BjcHXnkhJeUQR5v#P^0=wu64jZ)dtpm0;>HaYU?gG-TPeeI>iE<{t27>-8AP_ zyioTNdaYjcSXX>lXsl4Wk*uqXj%B2t9kP1bbm3A3#RlvQHHidfHCC^ve|y0;CIUiv zYD}Jn8IPbox&Vu-8xb+0Ig+@2{Wh{OpBqu*awC&Y3d8KEriXRdX&bdq&uXfFWBztO zIqBW>xKHr#j7KqhS{)q~L*4W`EU8&a&e3r`(T$8_(Q{xT36TbigG(9bx)uxt5ost} z(ePGmn{Vjx;DrSF@B!yV=HaoKPas7EJ=Wz>=$nVjHIAXgaKET~ruOw&&(HkQx1I8) z)}8q%3zeZluejCwf+N|JYDt|>H;L)h6^76mysVtG8*k}y>ETe{L44P}1ATNJh6s;k z?}J-c)4SpwgU!J}P>SS*#3zCJl72&|t(qF<^54f3P7j>Ij?Nae-}r;15(tZ;37MyQ zt(JJ_U=#64|ICEOT*Td(Ed$zIBZj?SbK0Ls!g7{0hfK6|Y{joVtzlb8j!{Wjh(UkK(Hb7s<}G_oRazMC#ryy|&hz8tD3}>2 znvr?HG?(3%JgNVVkpf0%(aol(N^D$|u`Qk^tohn6-z2W%@bqazivl93V-kar)Fh~f zcJJ|x$+Fo5uH~>M9oGUw=u>lc9(P|})ZJCS#Iw>HL`a(ioRZ=$&Lo#?=rR|thm^US z#2@(ibsNk0v(Qs%89kZqw{hgX;~#eaaA}Pe)Cyco-!X;L)>CWS6OHfCWj-PEJKku% zE8Mh$feSXS9O&KTI_?LIkLy&fkhmc}y=cZh2J^F)|NTzRNAk}U4z6lj_t&w%*RUY9 z6cJ?yvL9vovu}bV0#lPVshA3ZlW}35Pr!@IO<@&|n5H=`b3OY$ +
+
+ +

Emby

+

Emby is a home media server built using other popular open source technologies.

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