Add Emby notifier to config/Notifications (thanks to Supremicus for the jumpstart) with a homebrew description.

This commit is contained in:
JackDandy 2016-02-19 17:38:38 +00:00
parent 5d169de30d
commit df4e61c661
27 changed files with 1045 additions and 620 deletions

View file

@ -35,6 +35,7 @@
* Add search show Name to Show List Layout: Poster * Add search show Name to Show List Layout: Poster
* Change indicate when not sorting with article by dimming ("The", "A", "An") on Show List, Episode, History, * Change indicate when not sorting with article by dimming ("The", "A", "An") on Show List, Episode, History,
Mass Update, Add with Browse and from Existing views Mass Update, Add with Browse and from Existing views
* Add Emby notifier to config/Notifications
### 0.11.6 (2016-02-18 23:10:00 UTC) ### 0.11.6 (2016-02-18 23:10:00 UTC)

View file

@ -163,16 +163,19 @@ inc_top.tmpl
.ui-tabs .ui-tabs-panel{ .ui-tabs .ui-tabs-panel{
background-color:#3d3d3d !important; background-color:#3d3d3d !important;
border:1px solid #111 !important border:1px solid #111 !important;
border-top-width:0 !important
} }
.ui-tabs .ui-tabs-nav li.ui-tabs-active{ .ui-tabs .ui-tabs-nav li.ui-tabs-active{
border-bottom:1px solid #3d3d3d !important;
border-top-left-radius:5px; border-top-left-radius:5px;
border-top-right-radius:5px border-top-right-radius:5px
} }
.ui-tabs-nav > :not(.ui-tabs-active){ .ui-tabs-nav > :not(.ui-tabs-active){
background:#333; background:#333;
border-bottom:2px solid #222 !important;
border-top-left-radius:5px; border-top-left-radius:5px;
border-top-right-radius:5px border-top-right-radius:5px
} }

Binary file not shown.

View file

@ -42,4 +42,5 @@
<glyph unicode="&#xe620;" d="M729.285 747.346c-9.132-30.831-28.073-51.848-37.205-82.679-132.709 71.811-295.755 38.606-380.324-41.339-120.632-114.044-110.831-335.021 0-458.869 104.56-116.85 295.774-154.495 525.012-140.554-38.264-16.616-85.433-33.193-140.556-37.206-82.799-6.027-196.406-14.449-276.974 0-39.848 7.146-91.010 43.376-119.884 66.144-167.865 132.35-233.101 424.197-74.409 611.825 100.933 119.34 325.516 182.221 504.34 82.678zM526.722 916.838c-1.84-32.609-8.412-60.484-8.267-95.081-248.806 3.894-421.217-176.218-421.663-421.663-0.358-197.764 115.293-316.215 235.636-409.261-183.138-3.013-286.509 139.053-318.316 305.912-36.184 189.848 44.123 381.295 157.092 487.807 82.29 77.591 202.286 138.542 355.518 132.286zM1018.661 164.459c-23.41-31.934-62.158-88.67-107.481-103.35-72.378-23.441-259.309-5.489-326.582 8.268-171.37 35.052-339.531 130.938-330.718 338.985 6.652 157.072 126.784 274.099 293.511 268.708 114.164-3.691 186.047-59.381 239.768-148.822-26.79-14.546-53.652-29.025-78.545-45.473-95.176 170.786-386.294 111.183-359.652-115.754 4.832-41.173 24.663-73.938 45.473-99.213 113.596-137.978 395.536-131.411 624.226-103.349z" /> <glyph unicode="&#xe620;" d="M729.285 747.346c-9.132-30.831-28.073-51.848-37.205-82.679-132.709 71.811-295.755 38.606-380.324-41.339-120.632-114.044-110.831-335.021 0-458.869 104.56-116.85 295.774-154.495 525.012-140.554-38.264-16.616-85.433-33.193-140.556-37.206-82.799-6.027-196.406-14.449-276.974 0-39.848 7.146-91.010 43.376-119.884 66.144-167.865 132.35-233.101 424.197-74.409 611.825 100.933 119.34 325.516 182.221 504.34 82.678zM526.722 916.838c-1.84-32.609-8.412-60.484-8.267-95.081-248.806 3.894-421.217-176.218-421.663-421.663-0.358-197.764 115.293-316.215 235.636-409.261-183.138-3.013-286.509 139.053-318.316 305.912-36.184 189.848 44.123 381.295 157.092 487.807 82.29 77.591 202.286 138.542 355.518 132.286zM1018.661 164.459c-23.41-31.934-62.158-88.67-107.481-103.35-72.378-23.441-259.309-5.489-326.582 8.268-171.37 35.052-339.531 130.938-330.718 338.985 6.652 157.072 126.784 274.099 293.511 268.708 114.164-3.691 186.047-59.381 239.768-148.822-26.79-14.546-53.652-29.025-78.545-45.473-95.176 170.786-386.294 111.183-359.652-115.754 4.832-41.173 24.663-73.938 45.473-99.213 113.596-137.978 395.536-131.411 624.226-103.349z" />
<glyph unicode="&#xe621;" d="M4.283 787.656c7.274-57.855 34.374-98.399 78.67-119.196 38.761-18.203 94.99-7.272 147.8-11.918 121.794-10.718 187.114-118.102 185.942-214.548-1.327-109.177-85.133-194.234-185.942-202.633-34.385-2.865-67.842 5.006-107.273 0-70.178-8.906-108.898-56.879-119.195-138.263 87.799 4.418 170.766-5.942 247.926 0 125.395 9.656 202.723 87.278 259.842 164.488 57.558-77.95 135.042-155.379 262.226-164.488 76.165-5.456 158.659 4.056 245.54 0-5.646 62.881-31.961 102.074-73.9 123.963-42.755 22.31-99.255 8.868-154.953 14.301-242.313 23.646-239.659 396.828 4.766 417.183 36.153 3.011 70.608-4.252 104.892 0 72.445 8.981 109.527 59.474 119.194 138.264-87.783-4.638-170.775 6.243-247.925 0-125.192-10.134-202.138-87.567-259.843-164.49-56.746 78.254-140.256 155.752-262.225 164.49-71.555 5.126-152.863-3.755-243.154 0-2.777-0.399-2.588-3.768-2.385-7.153h-0.003z" /> <glyph unicode="&#xe621;" d="M4.283 787.656c7.274-57.855 34.374-98.399 78.67-119.196 38.761-18.203 94.99-7.272 147.8-11.918 121.794-10.718 187.114-118.102 185.942-214.548-1.327-109.177-85.133-194.234-185.942-202.633-34.385-2.865-67.842 5.006-107.273 0-70.178-8.906-108.898-56.879-119.195-138.263 87.799 4.418 170.766-5.942 247.926 0 125.395 9.656 202.723 87.278 259.842 164.488 57.558-77.95 135.042-155.379 262.226-164.488 76.165-5.456 158.659 4.056 245.54 0-5.646 62.881-31.961 102.074-73.9 123.963-42.755 22.31-99.255 8.868-154.953 14.301-242.313 23.646-239.659 396.828 4.766 417.183 36.153 3.011 70.608-4.252 104.892 0 72.445 8.981 109.527 59.474 119.194 138.264-87.783-4.638-170.775 6.243-247.925 0-125.192-10.134-202.138-87.567-259.843-164.49-56.746 78.254-140.256 155.752-262.225 164.49-71.555 5.126-152.863-3.755-243.154 0-2.777-0.399-2.588-3.768-2.385-7.153h-0.003z" />
<glyph unicode="&#xe622;" d="M141.308 212.259c-74.194 0-134.474-60.515-134.474-134.262 0-74.15 60.285-133.986 134.472-133.986 74.459 0 134.675 59.84 134.675 133.986-0.001 73.747-60.21 134.262-134.673 134.262zM6.988 609.88v-193.637c126.078 0 244.648-49.305 333.939-138.637 89.178-89.113 138.41-208.223 138.41-334.772h194.488c0 367.842-299.23 667.046-666.837 667.046zM7.217 953.166v-193.735c449.698 0 815.72-366.379 815.72-816.597h194.229c0 556.934-453.123 1010.332-1009.949 1010.332z" /> <glyph unicode="&#xe622;" d="M141.308 212.259c-74.194 0-134.474-60.515-134.474-134.262 0-74.15 60.285-133.986 134.472-133.986 74.459 0 134.675 59.84 134.675 133.986-0.001 73.747-60.21 134.262-134.673 134.262zM6.988 609.88v-193.637c126.078 0 244.648-49.305 333.939-138.637 89.178-89.113 138.41-208.223 138.41-334.772h194.488c0 367.842-299.23 667.046-666.837 667.046zM7.217 953.166v-193.735c449.698 0 815.72-366.379 815.72-816.597h194.229c0 556.934-453.123 1010.332-1009.949 1010.332z" />
</font></defs></svg> <glyph unicode="&#xe900;" d="M1016.803 487.017l-269.009 269.004-40.713-40.711-237.49 237.489-269.009-269.007 42.409-42.409-235.794-235.791 269.009-269.006 40.712 40.713 234.099-234.099 269.009 269.006-39.017 39.017 235.794 235.794zM396.084 255.013v382.247l327.964-189.976-327.964-192.271z" />
</font></defs></svg>

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Binary file not shown.

View file

@ -126,14 +126,14 @@ fonts
font-style:italic font-style:italic
} }
/* Droid Sans */ /* SickGear Icons */
@font-face{ @font-face{
font-family:'sgicons'; font-family:'sgicons';
src:url('fonts/sgicons.eot'); src:url('fonts/sgicons.eot?v1');
src:url('fonts/sgicons.eot?#iefix') format('embedded-opentype'), src:url('fonts/sgicons.eot?v1#iefix') format('embedded-opentype'),
url('fonts/sgicons.woff') format('woff'), url('fonts/sgicons.woff?v1') format('woff'),
url('fonts/sgicons.ttf') format('truetype'), url('fonts/sgicons.ttf?v1') format('truetype'),
url('fonts/sgicons.svg#sgicons') format('svg'); url('fonts/sgicons.svg?v1#sgicons') format('svg');
font-weight:normal; font-weight:normal;
font-style:normal font-style:normal
} }
@ -298,15 +298,18 @@ inc_top.tmpl
.ui-tabs .ui-tabs-panel{ .ui-tabs .ui-tabs-panel{
background-color:#F7F7F7 !important; background-color:#F7F7F7 !important;
border:1px solid #CCC !important border:1px solid #CCC !important;
border-top-width:0 !important
} }
.ui-tabs .ui-tabs-nav li.ui-tabs-active{ .ui-tabs .ui-tabs-nav li.ui-tabs-active{
border-bottom:1px solid #F7F7F7 !important;
border-top-left-radius:5px; border-top-left-radius:5px;
border-top-right-radius:5px border-top-right-radius:5px
} }
.ui-tabs-nav > :not(.ui-tabs-active){ .ui-tabs-nav > :not(.ui-tabs-active){
border-bottom:1px solid transparent !important;
border-top-left-radius:5px; border-top-left-radius:5px;
border-top-right-radius:5px border-top-right-radius:5px
} }
@ -499,6 +502,10 @@ inc_top.tmpl
content:"\e621" content:"\e621"
} }
.sgicon-emby:before {
content: "\e900"
}
/* ======================================================================= /* =======================================================================
inc_bottom.tmpl inc_bottom.tmpl
========================================================================== */ ========================================================================== */
@ -946,6 +953,7 @@ div.formpaginate{
margin-right:6px margin-right:6px
} }
#edit-show #customQualityWrapper div.component-group-desc p,
#addShowForm #customQualityWrapper div.component-group-desc p{ #addShowForm #customQualityWrapper div.component-group-desc p{
font-size:14px font-size:14px
} }
@ -1007,6 +1015,9 @@ div.formpaginate{
padding:0 0 0 15px padding:0 0 0 15px
} }
#edit-show #blackwhitelist,
#edit-show #blackwhitelist h4,
#edit-show #blackwhitelist p,
#addShowForm #blackwhitelist, #addShowForm #blackwhitelist,
#addShowForm #blackwhitelist h4, #addShowForm #blackwhitelist h4,
#addShowForm #blackwhitelist p{ #addShowForm #blackwhitelist p{
@ -1662,6 +1673,7 @@ td.col-search{
padding:15px 0 0 padding:15px 0 0
} }
#edit-show #config span.component-desc,
#addShowForm #editShow.stepDiv span.component-desc{ #addShowForm #editShow.stepDiv span.component-desc{
width:639px width:639px
} }
@ -2152,6 +2164,10 @@ config*.tmpl
min-height:200px min-height:200px
} }
#edit-show .component-group{
padding:25px 30px
}
.component-item{ .component-item{
border-bottom:1px dotted #666; border-bottom:1px dotted #666;
min-height:200px min-height:200px
@ -2291,6 +2307,10 @@ select .selected:before{
margin-bottom:10px margin-bottom:10px
} }
#editShow .field-pair #SceneException h4{
margin-bottom:5px
}
#editShow .field-pair #customQuality h4{ #editShow .field-pair #customQuality h4{
margin-bottom:1px margin-bottom:1px
} }
@ -2816,6 +2836,13 @@ div.blackwhitelist.white select,
div.blackwhitelist.black select{ div.blackwhitelist.black select{
height:110px height:110px
} }
#edit-show div.blackwhitelist.pool{
width:330px;
height:265px;
margin-left:248px;
float:none
}
#edit-show div.blackwhitelist.pool select,
div.blackwhitelist.pool, div.blackwhitelist.pool,
div.blackwhitelist.pool select{ div.blackwhitelist.pool select{
width:330px; width:330px;
@ -3379,6 +3406,12 @@ label{
font-weight:normal font-weight:normal
} }
.label-container{
display: inline-block;
max-width: 100%;
margin-bottom: 5px
}
pre{ pre{
border:1px solid border:1px solid
} }
@ -3433,6 +3466,7 @@ input sizing (for config pages)
margin-top:-4px margin-top:-4px
} }
#config .input350,
.input350{ .input350{
width:350px; width:350px;
margin-top:-4px margin-top:-4px
@ -3610,6 +3644,7 @@ div.formpaginate .prev, div.formpaginate .next{
} }
/* step 3 related */ /* step 3 related */
#edit-show #customQualityWrapper #customQuality,
#customQuality{ #customQuality{
display:block; display:block;
padding:0 0 10px 0; padding:0 0 10px 0;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -31,6 +31,176 @@
</ul> </ul>
<div id="tabs-1"> <div id="tabs-1">
<div class="component-group">
<div class="component-group-desc">
<img class="notifier-icon" src="$sbRoot/images/notifiers/emby.png" alt="" title="Emby" />
<h3><a href="<%= anon_url('http://emby.media/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Emby</a></h3>
<p>Have a central media database with strong user management, e.g. for improved Kodi profile(s). Gain deep viewing and granular control on any device, e.g. replace Plex entirely, Emby + Kodi > Plex.</p>
</div>
<fieldset class="component-group-list">
<div class="field-pair">
#if not hasattr($sickbeard, 'EMBY_UPDATE_LIBRARY')#<span class="red-text">Restart SickGear to reveal new options here</span>#else#
<label class="cleafix" for="use_emby">
<span class="component-title">Enable</span>
<span class="component-desc">
<input type="checkbox" class="enabler" name="use_emby" id="use_emby" #if $sickbeard.USE_EMBY then 'checked="checked"' else ''#>
<p>should SickGear enable features for Emby server(s) ?</p>
</span>
</label>
</div>
<div id="content_use_emby">
<div class="field-pair">
<label for="emby_update_library">
<span class="component-title">Update media library</span>
<span class="component-desc">
<input type="checkbox" name="emby_update_library" id="emby_update_library" #if $sickbeard.EMBY_UPDATE_LIBRARY then 'checked="checked"' else ''# />
<p>push Emby to refresh a show that is post-processed</p>
</span>
</label>
</div>
<div class="field-pair">
<label for="emby_host">
<span class="component-title">Host(s) running Emby</span>
<span class="component-desc">
<input type="text" name="emby_host" id="emby_host" value="$sickbeard.EMBY_HOST" class="form-control input-sm input250">
<div class="clear-left"><p>IP:Port [, IP:Port] (e.g. 192.168.0.1:8096, 192.168.1.2:8096)</p></div>
</span>
</label>
</div>
<div class="field-pair">
<label for="emby_apikey">
<span class="component-title">API key(s)</span>
<span class="component-desc">
<input type="text" name="emby_apikey" id="emby_apikey" value="<%= starify(sickbeard.EMBY_APIKEY) %>" class="form-control input-sm input250">
<p>(comma separated)</p>
<div class="clear-left"><p>one key per host from Emby/Manage Server/Advanced/Security</p></div>
</span>
</label>
</div>
<div class="testNotification" id="testEMBY-result">Click below to test.</div>
<input class="btn" type="button" value="Test Emby" id="testEMBY">
<input type="submit" class="config_submitter btn" value="Save Changes">
#end if
</div><!-- /content_use_emby //-->
</fieldset>
</div><!-- /emby component-group //-->
<div class="component-group">
<div class="component-group-desc">
<img class="notifier-icon" src="$sbRoot/images/notifiers/kodi.png" alt="" title="Kodi" />
<h3><a href="<%= anon_url('http://kodi.tv/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Kodi</a></h3>
<p>Kodi (formerly known as XBMC) is an award-winning free and open source (GPL) software media player and entertainment hub.</p>
</div>
<fieldset class="component-group-list">
<div class="field-pair">
<label class="cleafix" for="use_kodi">
<span class="component-title">Enable</span>
<span class="component-desc">
<input type="checkbox" class="enabler" name="use_kodi" id="use_kodi" #if $sickbeard.USE_KODI then 'checked="checked"' else ''# />
<p>should SickGear send Kodi commands ?</p>
</span>
</label>
</div>
<div id="content_use_kodi">
<div class="field-pair">
<label for="kodi_always_on">
<span class="component-title">Always on</span>
<span class="component-desc">
<input type="checkbox" name="kodi_always_on" id="kodi_always_on" #if $sickbeard.KODI_ALWAYS_ON then 'checked="checked"' else ''# />
<p>log errors when unreachable</p>
</span>
</label>
</div>
<div class="field-pair">
<label for="kodi_update_library">
<span class="component-title">Update shows known to Kodi</span>
<span class="component-desc">
<input type="checkbox" name="kodi_update_library" id="kodi_update_library" #if $sickbeard.KODI_UPDATE_LIBRARY then 'checked="checked"' else ''# />
<p>push Kodi to refresh a show that is post-processed</p>
</span>
</label>
</div>
<div class="field-pair">
<label for="kodi_update_full">
<span class="component-title">Perform full library update</span>
<span class="component-desc">
<input type="checkbox" name="kodi_update_full" id="kodi_update_full" #if $sickbeard.KODI_UPDATE_FULL then 'checked="checked"' else ''# />
<p>only when "Update shows" fails. Tip: enables Kodi to add new shows</p>
</span>
</label>
</div>
<div class="field-pair">
<label for="kodi_update_onlyfirst">
<span class="component-title">Only update first host</span>
<span class="component-desc">
<input type="checkbox" name="kodi_update_onlyfirst" id="kodi_update_onlyfirst" #if $sickbeard.KODI_UPDATE_ONLYFIRST then 'checked="checked"' else ''# />
<p>only send library updates to the first active host</p>
</span>
</label>
</div>
<div class="field-pair">
<label for="kodi_host">
<span class="component-title">Host(s) running Kodi</span>
<span class="component-desc">
<input type="text" name="kodi_host" id="kodi_host" value="$sickbeard.KODI_HOST" class="form-control input-sm input350" />
<div class="clear-left"><p>IP:Port [, IP:Port] (e.g. 192.168.0.1:8080, 192.168.1.2:8080)</p></div>
</span>
</label>
</div>
<div class="field-pair">
<label for="kodi_username">
<span class="component-title">Kodi web server username</span>
<span class="component-desc">
<input type="text" name="kodi_username" id="kodi_username" value="$sickbeard.KODI_USERNAME" class="form-control input-sm input250" />
<p>(blank for none)</p>
<div class="clear-left"><p>in Kodi System/Settings/Services/Web server</p></div>
</span>
</label>
</div>
<div class="field-pair">
<label for="kodi_password">
<span class="component-title">Kodi web server password</span>
<span class="component-desc">
<input type="password" name="kodi_password" id="kodi_password" value="#echo '*' * len($sickbeard.KODI_PASSWORD)#" class="form-control input-sm input250" />
<p>(blank for none)</p>
<div class="clear-left"><p>in Kodi System/Settings/Services/Web server</p></div>
</span>
</label>
</div>
<div class="field-pair">
<label for="kodi_notify_onsnatch">
<span class="component-title">Notify on snatch</span>
<span class="component-desc">
<input type="checkbox" name="kodi_notify_onsnatch" id="kodi_notify_onsnatch" #if $sickbeard.KODI_NOTIFY_ONSNATCH then 'checked="checked"' else ''# />
<p>download start notification</p>
</span>
</label>
</div>
<div class="field-pair">
<label for="kodi_notify_ondownload">
<span class="component-title">Notify on download</span>
<span class="component-desc">
<input type="checkbox" name="kodi_notify_ondownload" id="kodi_notify_ondownload" #if $sickbeard.KODI_NOTIFY_ONDOWNLOAD then 'checked="checked"' else ''# />
<p>download finish notification</p>
</span>
</label>
</div>
<div class="field-pair">
<label for="kodi_notify_onsubtitledownload">
<span class="component-title">Notify on subtitle download</span>
<span class="component-desc">
<input type="checkbox" name="kodi_notify_onsubtitledownload" id="kodi_notify_onsubtitledownload" #if $sickbeard.KODI_NOTIFY_ONSUBTITLEDOWNLOAD then 'checked="checked"' else ''# />
<p>subtitle downloaded notification</p>
</span>
</label>
</div>
<div class="testNotification" id="testKODI-result">Click below to test.</div>
<input class="btn" type="button" value="Test Kodi" id="testKODI" />
<input type="submit" class="config_submitter btn" value="Save Changes" />
</div><!-- /content_use_kodi //-->
</fieldset>
</div><!-- /kodi component-group //-->
<div class="component-group"> <div class="component-group">
<div class="component-group-desc"> <div class="component-group-desc">
@ -156,122 +326,6 @@
</div><!-- /xbmc component-group //--> </div><!-- /xbmc component-group //-->
<div class="component-group">
<div class="component-group-desc">
<img class="notifier-icon" src="$sbRoot/images/notifiers/kodi.png" alt="" title="Kodi" />
<h3><a href="<%= anon_url('http://kodi.tv/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Kodi</a></h3>
<p>Kodi (formerly known as XBMC) is an award-winning free and open source (GPL) software media player and entertainment hub.</p>
</div>
<fieldset class="component-group-list">
<div class="field-pair">
<label class="cleafix" for="use_kodi">
<span class="component-title">Enable</span>
<span class="component-desc">
<input type="checkbox" class="enabler" name="use_kodi" id="use_kodi" #if $sickbeard.USE_KODI then 'checked="checked"' else ''# />
<p>should SickGear send Kodi commands ?</p>
</span>
</label>
</div>
<div id="content_use_kodi">
<div class="field-pair">
<label for="kodi_always_on">
<span class="component-title">Always on</span>
<span class="component-desc">
<input type="checkbox" name="kodi_always_on" id="kodi_always_on" #if $sickbeard.KODI_ALWAYS_ON then 'checked="checked"' else ''# />
<p>log errors when unreachable ?</p>
</span>
</label>
</div>
<div class="field-pair">
<label for="kodi_notify_onsnatch">
<span class="component-title">Notify on snatch</span>
<span class="component-desc">
<input type="checkbox" name="kodi_notify_onsnatch" id="kodi_notify_onsnatch" #if $sickbeard.KODI_NOTIFY_ONSNATCH then 'checked="checked"' else ''# />
<p>send a notification when a download starts ?</p>
</span>
</label>
</div>
<div class="field-pair">
<label for="kodi_notify_ondownload">
<span class="component-title">Notify on download</span>
<span class="component-desc">
<input type="checkbox" name="kodi_notify_ondownload" id="kodi_notify_ondownload" #if $sickbeard.KODI_NOTIFY_ONDOWNLOAD then 'checked="checked"' else ''# />
<p>send a notification when a download finishes ?</p>
</span>
</label>
</div>
<div class="field-pair">
<label for="kodi_notify_onsubtitledownload">
<span class="component-title">Notify on subtitle download</span>
<span class="component-desc">
<input type="checkbox" name="kodi_notify_onsubtitledownload" id="kodi_notify_onsubtitledownload" #if $sickbeard.KODI_NOTIFY_ONSUBTITLEDOWNLOAD then 'checked="checked"' else ''# />
<p>send a notification when subtitles are downloaded ?</p>
</span>
</label>
</div>
<div class="field-pair">
<label for="kodi_update_library">
<span class="component-title">Update shows known to Kodi</span>
<span class="component-desc">
<input type="checkbox" name="kodi_update_library" id="kodi_update_library" #if $sickbeard.KODI_UPDATE_LIBRARY then 'checked="checked"' else ''# />
<p>with changes under the path of processed shows ?</p>
</span>
</label>
</div>
<div class="field-pair">
<label for="kodi_update_full">
<span class="component-title">Perform full library update</span>
<span class="component-desc">
<input type="checkbox" name="kodi_update_full" id="kodi_update_full" #if $sickbeard.KODI_UPDATE_FULL then 'checked="checked"' else ''# />
<p>if "Update shows" fails (e.g. to have Kodi find newly added shows)</p>
</span>
</label>
</div>
<div class="field-pair">
<label for="kodi_update_onlyfirst">
<span class="component-title">Only update first host</span>
<span class="component-desc">
<input type="checkbox" name="kodi_update_onlyfirst" id="kodi_update_onlyfirst" #if $sickbeard.KODI_UPDATE_ONLYFIRST then 'checked="checked"' else ''# />
<p>only send library updates to the first active host ?</p>
</span>
</label>
</div>
<div class="field-pair">
<label for="kodi_host">
<span class="component-title">Host(s) running Kodi</span>
<span class="component-desc">
<input type="text" name="kodi_host" id="kodi_host" value="$sickbeard.KODI_HOST" class="form-control input-sm input350" />
<div class="clear-left"><p>IP:Port [, IP:Port] (e.g. 192.168.0.1:8080, 192.168.1.2:8080)</p></div>
</span>
</label>
</div>
<div class="field-pair">
<label for="kodi_username">
<span class="component-title">Kodi web server username</span>
<span class="component-desc">
<input type="text" name="kodi_username" id="kodi_username" value="$sickbeard.KODI_USERNAME" class="form-control input-sm input250" />
<p>(blank for none)</p>
<div class="clear-left"><p>in Kodi System/Settings/Services/Web server</p></div>
</span>
</label>
</div>
<div class="field-pair">
<label for="kodi_password">
<span class="component-title">Kodi web server password</span>
<span class="component-desc">
<input type="password" name="kodi_password" id="kodi_password" value="#echo '*' * len($sickbeard.KODI_PASSWORD)#" class="form-control input-sm input250" />
<p>(blank for none)</p>
<div class="clear-left"><p>in Kodi System/Settings/Services/Web server</p></div>
</span>
</label>
</div>
<div class="testNotification" id="testKODI-result">Click below to test.</div>
<input class="btn" type="button" value="Test Kodi" id="testKODI" />
<input type="submit" class="config_submitter btn" value="Save Changes" />
</div><!-- /content_use_kodi //-->
</fieldset>
</div><!-- /kodi component-group //-->
<div class="component-group"> <div class="component-group">
<div class="component-group-desc"> <div class="component-group-desc">
<img class="notifier-icon" src="$sbRoot/images/notifiers/plex.png" alt="" title="Plex Media Server" /> <img class="notifier-icon" src="$sbRoot/images/notifiers/plex.png" alt="" title="Plex Media Server" />
@ -396,7 +450,6 @@
</fieldset> </fieldset>
</div><!-- /plex component-group --> </div><!-- /plex component-group -->
<div class="component-group"> <div class="component-group">
<div class="component-group-desc"> <div class="component-group-desc">
<img class="notifier-icon" src="$sbRoot/images/notifiers/nmj.png" alt="" title="Networked Media Jukebox" /> <img class="notifier-icon" src="$sbRoot/images/notifiers/nmj.png" alt="" title="Networked Media Jukebox" />

View file

@ -6,9 +6,10 @@
#import sickbeard.blackandwhitelist #import sickbeard.blackandwhitelist
## ##
#set global $title = 'Edit ' + $show.name #set global $title = 'Edit ' + $show.name
#set global $header = 'Edit ' + $show.name #set global $header = $show.name
#set global $sbPath = '..' #set global $sbPath = '..'
#set global $topmenu = 'home' #set global $topmenu = 'home'
#set global $page_body_attr = 'edit-show'
## ##
#import os.path #import os.path
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl') #include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
@ -16,13 +17,10 @@
<script type="text/javascript" src="$sbRoot/js/qualityChooser.js?v=$sbPID"></script> <script type="text/javascript" src="$sbRoot/js/qualityChooser.js?v=$sbPID"></script>
<script type="text/javascript" src="$sbRoot/js/editShow.js?v=$sbPID"></script> <script type="text/javascript" src="$sbRoot/js/editShow.js?v=$sbPID"></script>
<script> <script>
var config = { var config = {showLang: '$show.lang', showIsAnime: #echo ['!1','!0'][$show.is_anime]#}
show_lang: "$show.lang",
show_isanime: #echo ['!1','!0'][$show.is_anime]#
}
</script> </script>
#if $varExists('header') #if $varExists('header')
<h1 class="header">$header</h1> <h1 class="header"><span class="grey-text">Edit</span> $header</h1>
#else #else
<h1 class="title">$title</h1> <h1 class="title">$title</h1>
#end if #end if
@ -30,209 +28,233 @@
## ##
#set $html_checked = ' checked="checked"' #set $html_checked = ' checked="checked"'
#set $html_disabled = ' disabled="disabled"' #set $html_disabled = ' disabled="disabled"'
<form action="editShow" method="post" id="addShowForm"> <div id="config">
<input type="hidden" name="show" value="$show.indexerid"> <div id="config-content" class="linefix container">
<form action="editShow" method="post" id="editShow" style="width:894px">
<input type="hidden" name="show" value="$show.indexerid">
<div id="editShow" class="stepDiv linefix"> <div id="config-components">
<ul>
<li><a href="#core-component-group1">Common</a></li>
<li><a href="#core-component-group2">Search</a></li>
<li><a href="#core-component-group3">Post-Processing</a></li>
</ul>
<div class="field-pair"> <div id="core-component-group1" class="component-group">
<label for="paused">
<span class="component-title">Paused</span>
<span class="component-desc">
<input type="checkbox" name="paused" id="paused"#if 1 == $show.paused then $html_checked else ''#>
<p>enable to pause searching providers for show episodes</p>
</span>
</label>
</div>
<div class="field-pair"> <div class="field-pair">
<label for="paused">
<span class="component-title">Paused</span>
<span class="component-desc">
<input type="checkbox" name="paused" id="paused"#echo ('', $html_checked)[$show.paused]#>
<p>#echo ('enable to not', 'disable to')[$show.paused]# search or match releases for <b class="boldest grey-text">$show.name</b></p>
</span>
</label>
</div>
<div class="field-pair#if $sickbeard.SHOWLIST_TAGVIEW != 'custom'# hidden#end if#">
<label for="tag">
<span class="component-title">Group show under</span>
<span class="component-desc">
<select name="tag" id="tag" class="form-control form-control-inline input-sm">
#for $tag in $sickbeard.SHOW_TAGS:
<option value="$tag" #if $tag == $show.tag then 'selected="selected"' else ''#>$tag#echo ('', ' (default)')['Show List' == $tag]#</option>
#end for
</select>
<span>on the "Show List" page</span>
</span>
</label>
</div>
<div class="field-pair">
<span class="label-container">
<span class="component-title">Alternative show name(s)</span>
<span class="component-desc">
<input type="text" id="SceneName" class="form-control form-control-inline input-sm input200">
<select id="SceneNameSeason" class="form-control form-control-inline input-sm input100" style="#echo ('visibility:hidden','float:left')[$show.anime]#">
<option value="-1">Series</option>
#if $show.anime:
#for $season in $seasonResults:
<option value="$season[0]">Season $season[0]</option>
#end for
#end if
</select>
<input class="btn btn-inline" type="button" value="Add" id="addSceneName">
<p style="float:left">e.g. The Show, The Show (2016), The Show (US)</p>
<p class="clear-left note">post-processing or searching may require an alternative if a "Show not found" error is reported</p>
</span>
<span id="SceneException" class="component-desc" style="display:none">
<h4 class="grey-text">Alternative a.k.a scene exceptions list (multi-selectable)</h4>
<select id="exceptions_list" name="exceptions_list" multiple="multiple" class="input350" style="min-height:90px; float:left" >
#for $cur_exception_season in $show.exceptions:
#for $cur_exception in $show.exceptions[$cur_exception_season]:
<option value="$cur_exception_season|$cur_exception">#if $show.is_anime#S#echo ($cur_exception_season, '*')[$cur_exception_season == -1]#: #end if#$cur_exception</option>
#end for
#end for
</select>
<span><p class="note">#if $show.is_anime#S* = Any series. #end if#This case insensitive list overrides the original name to process</p></span>
<div>
<input id="removeSceneName" value="Remove" class="btn pull-left" type="button" style="margin-top: 10px;"/>
</div>
</span>
</span>
</div>
<div class="field-pair">
<label for="rls_ignore_words">
<span class="component-title">Ignore result with any word</span>
<span class="component-desc">
<input type="text" name="rls_ignore_words" id="rls_ignore_words" value="$show.rls_ignore_words" class="form-control form-control-inline input-sm input350">
<p>e.g. [[regex:]word1, word2, ..., word_n, regex_n]</p>
<p class="note">ignore search result <em class="grey-text">if its title contains any</em> of these comma seperated words or regular expressions</p>
</span>
</label>
</div>
<div class="field-pair">
<label for="rls_require_words">
<span class="component-title">Require at least one word</span>
<span class="component-desc">
<input type="text" name="rls_require_words" id="rls_require_words" value="$show.rls_require_words" class="form-control form-control-inline input-sm input350">
<p>e.g. [[regex:]word1, word2, ..., word_n, regex_n]</p>
<p class="note">ignore search result <em class="grey-text">unless its title contains one</em> of these comma seperated words or regular expressions</p>
</span>
</label>
</div>
<div class="field-pair">
#set $qualities = $common.Quality.splitQuality(int($show.quality)) #set $qualities = $common.Quality.splitQuality(int($show.quality))
#set global $anyQualities = $qualities[0] #set global $anyQualities = $qualities[0]
#set global $bestQualities = $qualities[1] #set global $bestQualities = $qualities[1]
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_qualityChooser.tmpl') #include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_qualityChooser.tmpl')
#if $anyQualities + $bestQualities #if $anyQualities + $bestQualities
<div class="field-pair show-if-quality-custom"> <div class="field-pair show-if-quality-custom" style="display:none">
<label for="archive_firstmatch"> <label for="archive_firstmatch">
<span class="component-title">End upgrade on first match</span> <span class="component-title">Upgrade once</span>
<span class="component-desc"> <span class="component-desc">
<input type="checkbox" name="archive_firstmatch" id="archive_firstmatch"#if $show.archive_firstmatch == 1 then $html_checked else ''#> <input type="checkbox" name="archive_firstmatch" id="archive_firstmatch"#echo ('', $html_checked)[$show.archive_firstmatch]#>
<p>mark an episode complete after the first best match is found from the <em>Upgrade to</em> quality list</p> <p>stop upgrading after matching the first best <em>Upgrade to</em> quality</p>
</span> </span>
</label> </label>
</div>
#end if
</div>
<div class="field-pair">
<label for="SceneName">
<span class="component-title">Scene exception</span>
<span class="component-desc">
<input type="text" id="SceneName" class="form-control form-control-inline input-sm input200">
<select id="SceneNameSeason" class="form-control form-control-inline input-sm input100" style="#echo ('visibility:hidden','float:left')[$show.anime]#">
<option value="-1">Series</option>
#if $show.anime:
#for $season in $seasonResults:
<option value="$season[0]">Season $season[0]</option>
#end for
#end if
</select>
<input class="btn btn-inline" type="button" value="Add" id="addSceneName">
#set $addSceneNameText = ('', ' or seasons')[$show.anime]
<p class="clear-left note">add alternative release names$addSceneNameText found on search providers for <b class="boldest grey-text">$show.name</b></p>
</span>
<span class="component-desc">
<div id="SceneException">
<h4 class="grey-text">Exceptions list (multi-selectable)</h4>
<select id="exceptions_list" name="exceptions_list" multiple="multiple" class="input350" style="min-height:90px; float:left" >
#for $cur_exception_season in $show.exceptions:
#for $cur_exception in $show.exceptions[$cur_exception_season]:
<option value="$cur_exception_season|$cur_exception">S#echo ($cur_exception_season, '*')[$cur_exception_season == -1]#: $cur_exception</option>
#end for
#end for
</select>
<span><p class="note">this list overrides the original name<br />to search, it doesn't append to it</p></span>
<div>
<input id="removeSceneName" value="Remove" class="btn pull-left" type="button" style="margin-top: 10px;"/>
</div> </div>
#end if
</div> </div>
</span>
</label>
<div style="clear:right">&nbsp;</div>
</div>
<div class="field-pair"> </div><!-- /component-group1 //-->
<label for="rls_ignore_words">
<span class="component-title">Ignore result with any word</span>
<span class="component-desc">
<input type="text" name="rls_ignore_words" id="rls_ignore_words" value="$show.rls_ignore_words" class="form-control form-control-inline input-sm input350">
<p>e.g. [[regex:]word1, word2, ..., word_n, regex_n]</p>
<p class="note">ignore search result <em class="grey-text">if its title contains any</em> of these comma seperated words or regular expressions</p>
</span>
</label>
</div>
<div class="field-pair"> <div id="core-component-group2" class="component-group">
<label for="rls_require_words">
<span class="component-title">Require at least one word</span>
<span class="component-desc">
<input type="text" name="rls_require_words" id="rls_require_words" value="$show.rls_require_words" class="form-control form-control-inline input-sm input350">
<p>e.g. [[regex:]word1, word2, ..., word_n, regex_n]</p>
<p class="note">ignore search result <em class="grey-text">unless its title contains one</em> of these comma seperated words or regular expressions</p>
</span>
</label>
</div>
<div class="field-pair"> <div class="field-pair">
<label for="location"> <label for="air_by_date">
<span class="component-title">Location for files</span> <span class="component-title">Air by date release names</span>
<span class="component-desc"> <span class="component-desc">
<input type="text" name="location" id="location" value="$show._location" class="form-control form-control-inline input-sm input350"> <input type="checkbox" name="air_by_date" id="air_by_date"#echo ('', $html_checked)[$show.air_by_date]#>
</span> <p>#echo ('enable if releases contain dates', 'disable for episodic releases')[$show.air_by_date]#, example: <em class="grey-text">Show.#echo ('03.02.2010', 'S02E03')[$show.air_by_date]#</em></p>
</label> </span>
</div> </label>
</div>
<div class="field-pair"> <div class="field-pair">
<label for="flatten_folders"> <label for="scene">
<span class="component-title">Flat folder structure</span> <span class="component-title">Scene numbering</span>
<span class="component-desc"> <span class="component-desc">
<input type="checkbox" name="flatten_folders" id="flatten_folders"#if 1 == $show.flatten_folders and not $sickbeard.NAMING_FORCE_FOLDERS then $html_checked else ''##if $sickbeard.NAMING_FORCE_FOLDERS then $html_disabled else ''#> <input type="checkbox" name="scene" id="scene"#echo ('', $html_checked)[$show.scene]#>
<p>enable to prevent creating the folders normally used to group seasons</p> <p>find episodes numbered by release groups instead of the TV network <em class="grey-text">(#if $show_has_scene_map then 'scene/manual numbers' else 'manual numbers only '# available)</em></p>
</span> </span>
</label> </label>
</div> </div>
<div class="field-pair"> <div class="field-pair">
<label for="air_by_date"> <label for="dvdorder">
<span class="component-title">Air by date episode names</span> <span class="component-title">Use DVD titles and numbers</span>
<span class="component-desc"> <span class="component-desc">
<input type="checkbox" name="air_by_date" id="air_by_date"#if 1 == $show.air_by_date then $html_checked else ''#> <input type="checkbox" name="dvdorder" id="dvdorder"#echo ('', $html_checked)[$show.dvdorder]#>
<p>enable if episode releases are named ... <em class="grey-text">Show.03.02.2010</em> instead of <em class="grey-text">Show.S02E03</em></p> <p>#echo ('enable to use DVD title and episode ordering', 'disable to use TV network title, number and aired order')[$show.dvdorder]#.&nbsp;
</span> After changing this setting, a "force full update" is essential, and existing episodes must be manually renamed</p>
</label> </span>
</div> </label>
</div>
<div class="field-pair"> <div class="field-pair">
<label for="dvdorder"> <label for="indexerLangSelectEdit">
<span class="component-title">Use DVD order</span> <span class="component-title">Info language</span>
<span class="component-desc"> <span class="component-desc">
<input type="checkbox" name="dvdorder" id="dvdorder"#if 1 == $show.dvdorder then $html_checked else ''#> <select name="indexerLang" id="indexerLangSelectEdit" class="form-control form-control-inline input-sm"></select>
<p>for episode titles, numbering etc. instead of the order the show aired on the network</p> <span>fetch show information in this language</span>
</span> </span>
</label> </label>
</div> </div>
<div class="field-pair"> <div class="field-pair">
<label for="scene"> <label for="sports">
<span class="component-title">Scene numbering</span> <span class="component-title">Show is sports</span>
<span class="component-desc"> <span class="component-desc">
<input type="checkbox" name="scene" id="scene"#if $show.scene == 1 then $html_checked else ''#> <input type="checkbox" name="sports" id="sports"#echo ('', $html_checked)[$show.sports]#>
<p>search for episodes numbered by scene groups instead of by the TV network <em class="grey-text">(#if $show_has_scene_map then 'scene/manual numbers' else 'manual numbers only '# available)</em></p> <p>treat this show as a sporting or MMA event</p>
</span> </span>
</label> </label>
</div> </div>
<div class="field-pair" style="margin-bottom:10px"> <div class="field-pair">
<label for="indexerLangSelectEdit"> <label for="anime">
<span class="component-title">Info language</span> <span class="component-title">Show is anime</span>
<span class="component-desc"> <span class="component-desc">
<select name="indexerLang" id="indexerLangSelectEdit" class="form-control form-control-inline input-sm"></select> <input type="checkbox" name="anime" id="anime"#echo ('', $html_checked)[$show.is_anime]#>
<span>attempt to fetch show data and episode filenames in this language</span> <p>releases for this show are named like ... <em class="grey-text">Show.265</em> instead of <em class="grey-text">Show.S02E03</em></p>
</span> #if not $show.is_anime#<span id="anime-options" style="display:none">Update Show then edit again to view additional options here</span>#end if#
</label> </span>
</div> </label>
</div>
<div class="field-pair"> #if $show.is_anime
<label for="subtitles">
<span class="component-title">Subtitles</span>
<span class="component-desc">
<input type="checkbox" name="subtitles" id="subtitles"#if 1 == $show.subtitles and $sickbeard.USE_SUBTITLES then $html_checked else ''##if not $sickbeard.USE_SUBTITLES then $html_disabled else ''#>
<p#if not $sickbeard.USE_SUBTITLES then ' class="grey-text"><del' else ''#>attempt to download episode subtitles for this show#if not $sickbeard.USE_SUBTITLES then '</del> ... (<span class="red-text">note: first <a href="%s/config/subtitles/">enable the subtitle system here</a></span>)' % $sbRoot else ''#</p>
</span>
</label>
</div>
<div class="field-pair#if $sickbeard.SHOWLIST_TAGVIEW != 'custom' then ' hidden' else ''#" style="margin-bottom:10px">
<label for="tag">
<span class="component-title">Show is in group</span>
<span class="component-desc">
<select name="tag" id="tag" class="form-control form-control-inline input-sm">
#for $tag in $sickbeard.SHOW_TAGS:
<option value="$tag" #if $tag == $show.tag then 'selected="selected"' else ''#>$tag#echo ('', ' (default)')['Show List' == $tag]#</option>
#end for
</select>
<span>and is displayed on the show list page under this section</span>
</span>
</label>
</div>
<div class="field-pair">
<label for="sports">
<span class="component-title">Show is sports</span>
<span class="component-desc">
<input type="checkbox" name="sports" id="sports"#if 1 == $show.sports then $html_checked else ''#>
<p>enable to treat this show as a sporting or MMA event</p>
</span>
</label>
</div>
<div class="field-pair">
<label for="anime">
<span class="component-title">Show is anime</span>
<span class="component-desc">
<input type="checkbox" name="anime" id="anime"#if $show.is_anime then $html_checked else ''#>
<p>enable if this show is anime and episode releases are named ... <em class="grey-text">Show.265</em> instead of <em class="grey-text">Show.S02E03</em></p>
</span>
</label>
</div>
#if $show.is_anime:
#import sickbeard.blackandwhitelist #import sickbeard.blackandwhitelist
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_blackwhitelist.tmpl') #include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_blackwhitelist.tmpl')
<script type="text/javascript" src="$sbRoot/js/blackwhite.js?v=$sbPID"></script> <script type="text/javascript" src="$sbRoot/js/blackwhite.js?v=$sbPID"></script>
#end if #end if
<input type="submit" id="submit" value="Submit" class="btn btn-primary" /> </div><!-- /component-group2 //-->
</form>
<div id="core-component-group3" class="component-group">
<div class="field-pair">
<label for="location">
<span class="component-title">Location for files</span>
<span class="component-desc">
<input type="text" name="location" id="location" value="$show._location" class="form-control form-control-inline input-sm input350">
</span>
</label>
</div>
<div class="field-pair">
<label for="flatten_folders">
<span class="component-title">Flat folder hierarchy</span>
<span class="component-desc">
<input type="checkbox" name="flatten_folders" id="flatten_folders"#echo ('', $html_checked)[$show.flatten_folders and not $sickbeard.NAMING_FORCE_FOLDERS]##echo ('', $html_disabled)[$sickbeard.NAMING_FORCE_FOLDERS]#>
<p>prevent creating season folders to group files</p>
</span>
</label>
</div>
<div class="field-pair">
<label for="subtitles">
<span class="component-title">Subtitles</span>
<span class="component-desc">
<input type="checkbox" name="subtitles" id="subtitles"#echo ('', $html_checked)[$show.subtitles and $sickbeard.USE_SUBTITLES]##echo ($html_disabled, '')[$sickbeard.USE_SUBTITLES]#>
<p#if not $sickbeard.USE_SUBTITLES# class="grey-text"><del#end if#>download episode subtitles for this show#if not $sickbeard.USE_SUBTITLES#</del> ... (<span class="red-text">note: first <a href="$sbRoot/config/subtitles/">enable the subtitle system here</a></span>)#end if#</p>
</span>
</label>
</div>
</div><!-- /component-group3 //-->
</div>
<div style="margin-top:15px">
<input type="submit" id="submit" value="Update Show" class="btn btn-primary" />
<a href="$sbRoot/home/displayShow?show=$show.indexerid" class="btn btn-primary" style="margin-left:10px">Cancel Edit</a>
</div>
</form>
</div>
</div> </div>
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl') #include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')

View file

@ -1,6 +1,7 @@
#import sickbeard #import sickbeard
#from sickbeard.common import Quality, qualityPresets, qualityPresetStrings #from sickbeard.common import Quality, qualityPresets, qualityPresetStrings
#set $html_selected = ' selected="selected"'
<div class="field-pair"> <div class="field-pair">
<label for="qualityPreset" class="clearfix"> <label for="qualityPreset" class="clearfix">
#set $overall_quality = $Quality.combineQualities($anyQualities, $bestQualities) #set $overall_quality = $Quality.combineQualities($anyQualities, $bestQualities)
@ -9,8 +10,8 @@
#set $selected = None #set $selected = None
<select id="qualityPreset" name="quality_preset" class="form-control form-control-inline input-sm"> <select id="qualityPreset" name="quality_preset" class="form-control form-control-inline input-sm">
<option value="0">Custom</option> <option value="0">Custom</option>
#for $curPreset in sorted($qualityPresets): #for $curPreset in $qualityPresets:
<option value="$curPreset" #if $curPreset == $overall_quality then "selected=\"selected\"" else ""# #if $qualityPresetStrings[$curPreset].endswith("0p") then "style=\"padding-left: 15px;\"" else ""#>$qualityPresetStrings[$curPreset]</option> <option value="$curPreset"#echo ('', $html_selected)[$curPreset == $overall_quality]##echo ('', ' style="padding-left:15px"')[$qualityPresetStrings[$curPreset].endswith('0p')]#>$qualityPresetStrings[$curPreset]</option>
#end for #end for
</select> </select>
<span>preferred episode quality to download</span> <span>preferred episode quality to download</span>
@ -19,18 +20,19 @@
</div> </div>
<div id="customQualityWrapper"> <div id="customQualityWrapper">
<div id="customQuality" class="show-if-quality-custom"> <div id="customQuality" class="show-if-quality-custom" style="display:none">
<div class="component-group-desc"> <div class="component-group-desc">
<p>An <em>Initial</em> quality episode must be found before an <em>Upgrade to</em> selection is considered.</p> <p>An <em>Initial</em> quality episode must be found before an <em>Upgrade to</em> selection is considered.</p>
<p>Upgrades continue until the highest selected of <em>Upgrade to</em> is matched.</p>
</div> </div>
<span class="component-desc"> <span class="component-desc">
<div style="float:left; padding-right: 40px"> <div style="float:left;padding-right:40px">
<h4 class="jumbo">Initial</h4> <h4 class="jumbo">Initial</h4>
#set $anyQualityList = filter(lambda x: x > $Quality.NONE, $Quality.qualityStrings) #set $anyQualityList = filter(lambda x: x > $Quality.NONE, $Quality.qualityStrings)
<select id="anyQualities" name="anyQualities" multiple="multiple" size="$len($anyQualityList)" class="form-control form-control-inline input-sm"> <select id="anyQualities" name="anyQualities" multiple="multiple" size="$len($anyQualityList)" class="form-control form-control-inline input-sm">
#for $curQuality in sorted($anyQualityList): #for $curQuality in sorted($anyQualityList):
<option value="$curQuality" #if $curQuality in $anyQualities then "selected=\"selected\"" else ""#>$Quality.qualityStrings[$curQuality]</option> <option value="$curQuality"#echo ('', $html_selected)[$curQuality in $anyQualities]#>$Quality.qualityStrings[$curQuality]</option>
#end for #end for
</select> </select>
</div> </div>
@ -40,7 +42,7 @@
#set $bestQualityList = filter(lambda x: x > $Quality.SDTV and x < $Quality.UNKNOWN, $Quality.qualityStrings) #set $bestQualityList = filter(lambda x: x > $Quality.SDTV and x < $Quality.UNKNOWN, $Quality.qualityStrings)
<select id="bestQualities" name="bestQualities" multiple="multiple" size="$len($bestQualityList)" class="form-control form-control-inline input-sm"> <select id="bestQualities" name="bestQualities" multiple="multiple" size="$len($bestQualityList)" class="form-control form-control-inline input-sm">
#for $curQuality in sorted($bestQualityList): #for $curQuality in sorted($bestQualityList):
<option value="$curQuality" #if $curQuality in $bestQualities then "selected=\"selected\"" else ""#>$Quality.qualityStrings[$curQuality]</option> <option value="$curQuality"#echo ('', $html_selected)[$curQuality in $bestQualities]#>$Quality.qualityStrings[$curQuality]</option>
#end for #end for
</select> </select>
</div> </div>

View file

@ -1,5 +1,6 @@
#import sickbeard #import sickbeard
#import urllib #import urllib
#from sickbeard.helpers import anon_url
#slurp #slurp
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
@ -125,19 +126,22 @@
<li><a href="$sbRoot/manage/manageSearches/" tabindex="$tab#set $tab += 1#"><i class="sgicon-search"></i>Manage Searches</a></li> <li><a href="$sbRoot/manage/manageSearches/" tabindex="$tab#set $tab += 1#"><i class="sgicon-search"></i>Manage Searches</a></li>
<li><a href="$sbRoot/manage/showQueueOverview/" tabindex="$tab#set $tab += 1#"><i class="sgicon-showqueue"></i>Show Queue Overview</a></li> <li><a href="$sbRoot/manage/showQueueOverview/" tabindex="$tab#set $tab += 1#"><i class="sgicon-showqueue"></i>Show Queue Overview</a></li>
<li><a href="$sbRoot/manage/episodeStatuses/" tabindex="$tab#set $tab += 1#"><i class="sgicon-episodestatus"></i>Episode Status Management</a></li> <li><a href="$sbRoot/manage/episodeStatuses/" tabindex="$tab#set $tab += 1#"><i class="sgicon-episodestatus"></i>Episode Status Management</a></li>
#if $sickbeard.USE_PLEX and $sickbeard.PLEX_SERVER_HOST != '' #if hasattr($sickbeard, 'USE_EMBY') and $sickbeard.USE_EMBY and $sickbeard.EMBY_HOST != '' and $sickbeard.EMBY_APIKEY != ''
<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/updateEMBY/" tabindex="$tab#set $tab += 1#"><i class="sgicon-emby"></i>Update Emby</a></li>
#end if #end if
#if $sickbeard.USE_XBMC and $sickbeard.XBMC_HOST != '' #if hasattr($sickbeard, 'USE_KODI') and $sickbeard.USE_KODI and $sickbeard.KODI_HOST != ''
<li><a href="$sbRoot/home/updateXBMC/" tabindex="$tab#set $tab += 1#"><i class="sgicon-xbmc"></i>Update XBMC</a></li>
#end if
#if $sickbeard.USE_KODI and $sickbeard.KODI_HOST != ''
<li><a href="$sbRoot/home/updateKODI/" tabindex="$tab#set $tab += 1#"><i class="sgicon-kodi"></i>Update Kodi</a></li> <li><a href="$sbRoot/home/updateKODI/" tabindex="$tab#set $tab += 1#"><i class="sgicon-kodi"></i>Update Kodi</a></li>
#end if #end if
#if $sickbeard.USE_FAILED_DOWNLOADS #if hasattr($sickbeard, 'USE_XBMC') and $sickbeard.USE_XBMC and $sickbeard.XBMC_HOST != ''
<li><a href="$sbRoot/home/updateXBMC/" tabindex="$tab#set $tab += 1#"><i class="sgicon-xbmc"></i>Update XBMC</a></li>
#end if
#if hasattr($sickbeard, 'USE_PLEX') and $sickbeard.USE_PLEX and $sickbeard.PLEX_SERVER_HOST != ''
<li><a href="$sbRoot/home/updatePLEX/" tabindex="$tab#set $tab += 1#"><i class="sgicon-plex"></i>Update PLEX</a></li>
#end if
#if hasattr($sickbeard, 'USE_FAILED_DOWNLOADS') and $sickbeard.USE_FAILED_DOWNLOADS
<li><a href="$sbRoot/manage/failedDownloads/" tabindex="$tab#set $tab += 1#"><i class="sgicon-failed"></i>Failed Downloads</a></li> <li><a href="$sbRoot/manage/failedDownloads/" tabindex="$tab#set $tab += 1#"><i class="sgicon-failed"></i>Failed Downloads</a></li>
#end if #end if
#if $sickbeard.USE_SUBTITLES #if hasattr($sickbeard, 'USE_SUBTITLES') and $sickbeard.USE_SUBTITLES
<li><a href="$sbRoot/manage/subtitleMissed/" tabindex="$tab#set $tab += 1#"><i class="sgicon-subtitles"></i>Missed Subtitle Management</a></li> <li><a href="$sbRoot/manage/subtitleMissed/" tabindex="$tab#set $tab += 1#"><i class="sgicon-subtitles"></i>Missed Subtitle Management</a></li>
#end if #end if
</ul> </ul>
@ -213,11 +217,10 @@
#end if #end if
## ##
#if sys.version_info < (2, 7, 9): #if sys.version_info < (2, 7, 9):
<div class="alert alert-danger upgrade-notification" role="alert"> <div class="alert alert-danger upgrade-notification upgrade-py" role="alert">
<span> <p>As per notify 29 Nov 2015, SickGear no longer supports Python 2.7.8 and older<br />
SickGear will be dropping support for Python 2.7.8 and below. We recommend updating to latest version: Please upgrade to Python <b>2.7.9</b> or newer (not 3.x.x): <a href="<%= anon_url('https://www.python.org/downloads/') %>" onclick="window.open(this.href);return !1;">Download here</a>
<a href="https://www.python.org/downloads/" onclick="window.open(this.href); return false;">Download here</a> </p>
</span>
</div> </div>
#end if #end if
#if $sickbeard.NEWEST_VERSION_STRING #if $sickbeard.NEWEST_VERSION_STRING

View file

@ -37,22 +37,30 @@
}); });
}); });
$('#testXBMC').click(function () { $('#testEMBY').click(function () {
var xbmc_host = $.trim($('#xbmc_host').val()); var emby_host = $('#emby_host').val();
var xbmc_username = $.trim($('#xbmc_username').val()); var emby_apikey = $('#emby_apikey').val();
var xbmc_password = $.trim($('#xbmc_password').val()); if (!emby_host || !emby_apikey) {
if (!xbmc_host) { $('#testEMBY-result').html('Please fill out the necessary fields above.');
$('#testXBMC-result').html('Please fill out the necessary fields above.'); if (!emby_host) {
$('#xbmc_host').addClass('warning'); $('#emby_host').addClass('warning');
} else {
$('#emby_host').removeClass('warning');
}
if (!emby_apikey) {
$('#emby_apikey').addClass('warning');
} else {
$('#emby_apikey').removeClass('warning');
}
return; return;
} }
$('#xbmc_host').removeClass('warning'); $('#emby_host, #emby_apikey').removeClass('warning');
$(this).prop('disabled', true); $(this).prop('disabled', true);
$('#testXBMC-result').html(loading); $('#testEMBY-result').html(loading);
$.get(sbRoot + '/home/testXBMC', {'host': xbmc_host, 'username': xbmc_username, 'password': xbmc_password}) $.get(sbRoot + '/home/testEMBY', {'host': emby_host, 'apikey': emby_apikey})
.done(function (data) { .done(function (data) {
$('#testXBMC-result').html(data); $('#testEMBY-result').html(data);
$('#testXBMC').prop('disabled', false); $('#testEMBY').prop('disabled', false);
}); });
}); });
@ -75,6 +83,25 @@
}); });
}); });
$('#testXBMC').click(function () {
var xbmc_host = $.trim($('#xbmc_host').val());
var xbmc_username = $.trim($('#xbmc_username').val());
var xbmc_password = $.trim($('#xbmc_password').val());
if (!xbmc_host) {
$('#testXBMC-result').html('Please fill out the necessary fields above.');
$('#xbmc_host').addClass('warning');
return;
}
$('#xbmc_host').removeClass('warning');
$(this).prop('disabled', true);
$('#testXBMC-result').html(loading);
$.get(sbRoot + '/home/testXBMC', {'host': xbmc_host, 'username': xbmc_username, 'password': xbmc_password})
.done(function (data) {
$('#testXBMC-result').html(data);
$('#testXBMC').prop('disabled', false);
});
});
// show instructions for plex when enabled // show instructions for plex when enabled
$('#use_plex').click(function() { $('#use_plex').click(function() {
if ( $(this).is(':checked') ) { if ( $(this).is(':checked') ) {

View file

@ -2,81 +2,72 @@
$(document).ready(function () { $(document).ready(function () {
$.getJSON(sbRoot + '/home/addShows/getIndexerLanguages', {}, function (data) {
var resultStr, flag, selected, current_lang_added = '';
if (data.results.length === 0) {
flag = ' class="flag" style="background-image:url(' + sbRoot + '/images/flags/' + config.show_lang + '.png)"';
resultStr = '<option value="' + config.show_lang + '" selected="selected"' + flag + '>' + config.show_lang + '</option>';
} else {
current_lang_added = false;
$.each(data.results, function (index, obj) {
if (obj === config.show_lang) {
selected = ' selected="selected"';
current_lang_added = true;
}
else {
selected = '';
}
flag = ' class="flag" style="background-image:url(' + sbRoot + '/images/flags/' + obj + '.png);"';
resultStr += '<option value="' + obj + '"' + selected + flag + '>' + obj + '</option>';
});
if (!current_lang_added) {
resultStr += '<option value=" ' + config.show_lang + '" selected="selected"> ' + config.show_lang + '</option>';
}
}
$('#indexerLangSelectEdit').html(resultStr);
});
var all_exceptions = [];
$('#location').fileBrowser({title: 'Select Show Location'}); $('#location').fileBrowser({title: 'Select Show Location'});
$('#submit').click(function () { function htmlFlag(lang) {
all_exceptions = []; return ' class="flag" style="background-image:url(' + sbRoot + '/images/flags/' + lang + '.png)"'
}
$('#exceptions_list').find('option').each (function () { $.getJSON(sbRoot + '/home/addShows/getIndexerLanguages', {}, function (data) {
all_exceptions.push($(this).val()); var result = '', currentLangAdded = '', selected = ' selected="selected"';
if (0 == data.results.length) {
result = '<option value="' + config.showLang + '"' + selected + htmlFlag(config.showLang) + '>'
+ config.showLang + '</option>';
} else {
currentLangAdded = !1;
$.each(data.results, function (index, strLang) {
var htmlSelected = '';
if (strLang === config.showLang) {
currentLangAdded = !0;
htmlSelected = selected;
}
result += '<option value="' + strLang + '"' + htmlSelected + htmlFlag(strLang) + '>'
+ strLang + '</option>';
});
if (!currentLangAdded)
result += '<option value="' + config.showLang + '" ' + selected + '>' + config.showLang + '</option>';
}
$('#indexerLangSelectEdit').html(result);
});
function getExceptions() {
var allExceptions = [];
$('#exceptions_list').find('option').each(function () {
allExceptions.push($(this).val());
}); });
$('#exceptions_list').val(all_exceptions); return allExceptions
if (config.show_isanime) { }
$('#submit').click(function () {
$('#exceptions_list').val(getExceptions());
if (config.showIsAnime)
generate_bwlist(); generate_bwlist();
}
}); });
$('#addSceneName').click(function () { $('#addSceneName').click(function () {
var scene_ex = $('#SceneName').val(); var elSceneName = $('#SceneName'), elSceneNameSeason = $('#SceneNameSeason'),
var scene_ex_season = $('#SceneNameSeason').val(); sceneEx = elSceneName.val(), sceneExSeason = elSceneNameSeason.val();
var option = $('<option>');
all_exceptions = [];
$('#exceptions_list').find('option').each (function () { elSceneName.val('');
all_exceptions.push($(this).val()); elSceneNameSeason.val('');
});
$('#SceneName').val(''); if (-1 < $.inArray(sceneExSeason + '|' + sceneEx, getExceptions()) || ('' === sceneEx))
$('#SceneNameSeason').val('');
if ($.inArray(scene_ex_season + '|' + scene_ex, all_exceptions) > -1 || (scene_ex === '')) {
return; return;
}
$('#SceneException').show();
option.attr('value', scene_ex_season + '|' + scene_ex); $('#SceneException').fadeIn('fast', 'linear');
if (scene_ex_season === "-1") {
option.html('S*: ' + scene_ex); var option = $('<option>');
} option.attr('value', sceneExSeason + '|' + sceneEx);
else { option.html((config.showIsAnime ? 'S' + ('-1' === sceneExSeason ? '*' : sceneExSeason) + ': ' : '') + sceneEx);
option.html('S' + scene_ex_season + ': ' + scene_ex);
} return option.appendTo($('#exceptions_list'));
return option.appendTo('#exceptions_list');
}); });
$('#removeSceneName').click(function () { $('#removeSceneName').click(function () {
@ -86,21 +77,41 @@ $(document).ready(function () {
}); });
$.fn.toggle_SceneException = function () { $.fn.toggle_SceneException = function () {
all_exceptions = []; var elSceneException = $('#SceneException');
$('#exceptions_list').find('option').each (function () { if (0 == getExceptions().length)
all_exceptions.push($(this).val()); elSceneException.fadeOut('fast', 'linear');
}); else
elSceneException.fadeIn('fast', 'linear');
if ('' === all_exceptions) {
$('#SceneException').hide();
}
else {
$('#SceneException').show();
}
}; };
$(this).toggle_SceneException(); $(this).toggle_SceneException();
var elABD = $('#air_by_date'), elScene = $('#scene'), elSports = $('#sports'), elAnime = $('#anime');
}); function uncheck(el){el.prop('checked', !1)}
function checked(el){return el.prop('checked')}
function isAnime(){
uncheck(elABD); uncheck(elSports);
if (config.showIsAnime){ $('#blackwhitelist').fadeIn('fast', 'linear'); } return !0; }
function isScene(){ uncheck(elABD); uncheck(elSports); }
function isABD(){ uncheck(elAnime); uncheck(elScene); $('#blackwhitelist').fadeOut('fast', 'linear'); }
function isSports(){ uncheck(elAnime); uncheck(elScene); $('#blackwhitelist').fadeOut('fast', 'linear'); }
if (checked(elAnime)) { isAnime(); }
if (checked(elScene)) { isScene(); }
if (checked(elABD)) { isABD(); }
if (checked(elSports)) { isSports() }
elAnime.on('click', function() {
if (checked(elAnime))
isAnime() && !config.showIsAnime && $('#anime-options').fadeIn('fast', 'linear');
else
$('#blackwhitelist, #anime-options').fadeOut('fast', 'linear');
});
elScene.on('click', function() { isScene(); });
elABD.on('click', function() { isABD(); });
elSports.on('click', function() { isSports() });
});

View file

@ -27,10 +27,12 @@ function initActions() {
$('#SubMenu a:contains("Show Queue Overview")').addClass('btn').html('<i class="sgicon-showqueue"></i>Show Queue Overview'); $('#SubMenu a:contains("Show Queue Overview")').addClass('btn').html('<i class="sgicon-showqueue"></i>Show Queue Overview');
$('#SubMenu a[href$="/manage/failedDownloads/"]').addClass('btn').html('<i class="sgicon-failed"></i>Failed Downloads'); $('#SubMenu a[href$="/manage/failedDownloads/"]').addClass('btn').html('<i class="sgicon-failed"></i>Failed Downloads');
$('#SubMenu a:contains("Notification")').addClass('btn').html('<i class="sgicon-notification"></i>Notifications'); $('#SubMenu a:contains("Notification")').addClass('btn').html('<i class="sgicon-notification"></i>Notifications');
$('#SubMenu a:contains("Update show in XBMC")').addClass('btn').html('<i class="sgicon-xbmc"></i>Update show in XBMC'); $('#SubMenu a[href$="/home/updateEMBY/"]').addClass('btn').html('<i class="sgicon-emby"></i>Update Emby');
$('#SubMenu a[href$="/home/updateXBMC/"]').addClass('btn').html('<i class="sgicon-xbmc"></i>Update XBMC');
$('#SubMenu a:contains("Update show in Kodi")').addClass('btn').html('<i class="sgicon-kodi"></i>Update show in Kodi');
$('#SubMenu a[href$="/home/updateKODI/"]').addClass('btn').html('<i class="sgicon-kodi"></i>Update Kodi'); $('#SubMenu a[href$="/home/updateKODI/"]').addClass('btn').html('<i class="sgicon-kodi"></i>Update Kodi');
$('#SubMenu a[href$="/home/updateXBMC/"]').addClass('btn').html('<i class="sgicon-xbmc"></i>Update XBMC');
$('#SubMenu a:contains("Update show in Emby")').addClass('btn').html('<i class="sgicon-emby"></i>Update show in Emby');
$('#SubMenu a:contains("Update show in Kodi")').addClass('btn').html('<i class="sgicon-kodi"></i>Update show in Kodi');
$('#SubMenu a:contains("Update show in XBMC")').addClass('btn').html('<i class="sgicon-xbmc"></i>Update show in XBMC');
} }
$(document).ready(function(){ $(document).ready(function(){

View file

@ -2,7 +2,7 @@ function setFromPresets (preset) {
var elCustomQuality = $('.show-if-quality-custom'), var elCustomQuality = $('.show-if-quality-custom'),
selected = 'selected'; selected = 'selected';
if (preset = parseInt(preset)) { if (preset = parseInt(preset)) {
elCustomQuality.hide(); elCustomQuality.fadeOut('fast', 'linear');
var upgrade = !0; var upgrade = !0;
$('#anyQualities, #bestQualities').find('option').each(function() { $('#anyQualities, #bestQualities').find('option').each(function() {
@ -18,7 +18,7 @@ function setFromPresets (preset) {
$(this).attr(selected, ((preset & parseInt($(this).val())) ? selected : false)); $(this).attr(selected, ((preset & parseInt($(this).val())) ? selected : false));
}); });
} else } else
elCustomQuality.show(); elCustomQuality.fadeIn('fast', 'linear');
} }
$(document).ready(function() { $(document).ready(function() {
@ -30,4 +30,4 @@ $(document).ready(function() {
}); });
setFromPresets(elQualityPreset.find(selected).val()); setFromPresets(elQualityPreset.find(selected).val());
}); });

View file

@ -42,8 +42,10 @@ from indexers.indexer_config import INDEXER_TVDB
from indexers.indexer_api import indexerApi from indexers.indexer_api import indexerApi
from indexers.indexer_exceptions import indexer_shownotfound, indexer_exception, indexer_error, \ from indexers.indexer_exceptions import indexer_shownotfound, indexer_exception, indexer_error, \
indexer_episodenotfound, indexer_attributenotfound, indexer_seasonnotfound, indexer_userabort, indexerExcepts indexer_episodenotfound, indexer_attributenotfound, indexer_seasonnotfound, indexer_userabort, indexerExcepts
from sickbeard.exceptions import ex
from sickbeard.providers.generic import GenericProvider from sickbeard.providers.generic import GenericProvider
from sickbeard import encodingKludge as ek from sickbeard import encodingKludge as ek
from lib.adba.aniDBerrors import (AniDBError, AniDBBannedError)
from lib.configobj import ConfigObj from lib.configobj import ConfigObj
from lib.libtrakt import TraktAPI from lib.libtrakt import TraktAPI
import trakt_helpers import trakt_helpers
@ -253,17 +255,10 @@ TORRENT_HIGH_BANDWIDTH = False
TORRENT_LABEL = '' TORRENT_LABEL = ''
TORRENT_VERIFY_CERT = False TORRENT_VERIFY_CERT = False
USE_XBMC = False USE_EMBY = False
XBMC_ALWAYS_ON = True EMBY_UPDATE_LIBRARY = False
XBMC_NOTIFY_ONSNATCH = False EMBY_HOST = None
XBMC_NOTIFY_ONDOWNLOAD = False EMBY_APIKEY = None
XBMC_NOTIFY_ONSUBTITLEDOWNLOAD = False
XBMC_UPDATE_LIBRARY = False
XBMC_UPDATE_FULL = False
XBMC_UPDATE_ONLYFIRST = False
XBMC_HOST = ''
XBMC_USERNAME = None
XBMC_PASSWORD = None
USE_KODI = False USE_KODI = False
KODI_ALWAYS_ON = True KODI_ALWAYS_ON = True
@ -277,6 +272,18 @@ KODI_HOST = ''
KODI_USERNAME = None KODI_USERNAME = None
KODI_PASSWORD = None KODI_PASSWORD = None
USE_XBMC = False
XBMC_ALWAYS_ON = True
XBMC_NOTIFY_ONSNATCH = False
XBMC_NOTIFY_ONDOWNLOAD = False
XBMC_NOTIFY_ONSUBTITLEDOWNLOAD = False
XBMC_UPDATE_LIBRARY = False
XBMC_UPDATE_FULL = False
XBMC_UPDATE_ONLYFIRST = False
XBMC_HOST = ''
XBMC_USERNAME = None
XBMC_PASSWORD = None
USE_PLEX = False USE_PLEX = False
PLEX_NOTIFY_ONSNATCH = False PLEX_NOTIFY_ONSNATCH = False
PLEX_NOTIFY_ONDOWNLOAD = False PLEX_NOTIFY_ONDOWNLOAD = False
@ -488,12 +495,15 @@ def initialize(consoleLogging=True):
SAB_USERNAME, SAB_PASSWORD, SAB_APIKEY, SAB_CATEGORY, SAB_HOST, \ SAB_USERNAME, SAB_PASSWORD, SAB_APIKEY, SAB_CATEGORY, SAB_HOST, \
NZBGET_USERNAME, NZBGET_PASSWORD, NZBGET_CATEGORY, NZBGET_PRIORITY, NZBGET_HOST, NZBGET_USE_HTTPS, backlogSearchScheduler, \ NZBGET_USERNAME, NZBGET_PASSWORD, NZBGET_CATEGORY, NZBGET_PRIORITY, NZBGET_HOST, NZBGET_USE_HTTPS, backlogSearchScheduler, \
TORRENT_USERNAME, TORRENT_PASSWORD, TORRENT_HOST, TORRENT_PATH, TORRENT_SEED_TIME, TORRENT_PAUSED, TORRENT_HIGH_BANDWIDTH, TORRENT_LABEL, TORRENT_VERIFY_CERT, \ TORRENT_USERNAME, TORRENT_PASSWORD, TORRENT_HOST, TORRENT_PATH, TORRENT_SEED_TIME, TORRENT_PAUSED, TORRENT_HIGH_BANDWIDTH, TORRENT_LABEL, TORRENT_VERIFY_CERT, \
USE_XBMC, XBMC_ALWAYS_ON, XBMC_NOTIFY_ONSNATCH, XBMC_NOTIFY_ONDOWNLOAD, XBMC_NOTIFY_ONSUBTITLEDOWNLOAD, XBMC_UPDATE_FULL, XBMC_UPDATE_ONLYFIRST, \ USE_EMBY, EMBY_UPDATE_LIBRARY, EMBY_HOST, EMBY_APIKEY, \
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,\
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, \ KODI_UPDATE_FULL, KODI_UPDATE_ONLYFIRST, KODI_UPDATE_LIBRARY, KODI_HOST, KODI_USERNAME, KODI_PASSWORD, \
USE_XBMC, XBMC_ALWAYS_ON, XBMC_NOTIFY_ONSNATCH, XBMC_NOTIFY_ONDOWNLOAD, XBMC_NOTIFY_ONSUBTITLEDOWNLOAD,\
XBMC_UPDATE_FULL, XBMC_UPDATE_ONLYFIRST, XBMC_UPDATE_LIBRARY, XBMC_HOST, XBMC_USERNAME, XBMC_PASSWORD, \
USE_PLEX, PLEX_NOTIFY_ONSNATCH, PLEX_NOTIFY_ONDOWNLOAD, PLEX_NOTIFY_ONSUBTITLEDOWNLOAD, \
PLEX_UPDATE_LIBRARY, PLEX_SERVER_HOST, PLEX_HOST, PLEX_USERNAME, PLEX_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_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, \ BACKLOG_FREQUENCY, DEFAULT_BACKLOG_FREQUENCY, MIN_BACKLOG_FREQUENCY, MAX_BACKLOG_FREQUENCY, BACKLOG_STARTUP, SKIP_REMOVED_FILES, \
PLEX_SERVER_HOST, PLEX_HOST, PLEX_USERNAME, PLEX_PASSWORD, 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, \ 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, \ 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, \ QUALITY_DEFAULT, FLATTEN_FOLDERS_DEFAULT, SUBTITLES_DEFAULT, STATUS_DEFAULT, WANTED_BEGIN_DEFAULT, WANTED_LATEST_DEFAULT, RECENTSEARCH_STARTUP, \
@ -535,8 +545,9 @@ def initialize(consoleLogging=True):
CheckSection(CFG, 'Blackhole') CheckSection(CFG, 'Blackhole')
CheckSection(CFG, 'SABnzbd') CheckSection(CFG, 'SABnzbd')
CheckSection(CFG, 'NZBget') CheckSection(CFG, 'NZBget')
CheckSection(CFG, 'XBMC') CheckSection(CFG, 'Emby')
CheckSection(CFG, 'Kodi') CheckSection(CFG, 'Kodi')
CheckSection(CFG, 'XBMC')
CheckSection(CFG, 'PLEX') CheckSection(CFG, 'PLEX')
CheckSection(CFG, 'Growl') CheckSection(CFG, 'Growl')
CheckSection(CFG, 'Prowl') CheckSection(CFG, 'Prowl')
@ -781,17 +792,10 @@ def initialize(consoleLogging=True):
TORRENT_LABEL = check_setting_str(CFG, 'TORRENT', 'torrent_label', '') TORRENT_LABEL = check_setting_str(CFG, 'TORRENT', 'torrent_label', '')
TORRENT_VERIFY_CERT = bool(check_setting_int(CFG, 'TORRENT', 'torrent_verify_cert', 0)) TORRENT_VERIFY_CERT = bool(check_setting_int(CFG, 'TORRENT', 'torrent_verify_cert', 0))
USE_XBMC = bool(check_setting_int(CFG, 'XBMC', 'use_xbmc', 0)) USE_EMBY = bool(check_setting_int(CFG, 'Emby', 'use_emby', 0))
XBMC_ALWAYS_ON = bool(check_setting_int(CFG, 'XBMC', 'xbmc_always_on', 1)) EMBY_UPDATE_LIBRARY = bool(check_setting_int(CFG, 'Emby', 'emby_update_library', 0))
XBMC_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'XBMC', 'xbmc_notify_onsnatch', 0)) EMBY_HOST = check_setting_str(CFG, 'Emby', 'emby_host', '')
XBMC_NOTIFY_ONDOWNLOAD = bool(check_setting_int(CFG, 'XBMC', 'xbmc_notify_ondownload', 0)) EMBY_APIKEY = check_setting_str(CFG, 'Emby', 'emby_apikey', '')
XBMC_NOTIFY_ONSUBTITLEDOWNLOAD = bool(check_setting_int(CFG, 'XBMC', 'xbmc_notify_onsubtitledownload', 0))
XBMC_UPDATE_LIBRARY = bool(check_setting_int(CFG, 'XBMC', 'xbmc_update_library', 0))
XBMC_UPDATE_FULL = bool(check_setting_int(CFG, 'XBMC', 'xbmc_update_full', 0))
XBMC_UPDATE_ONLYFIRST = bool(check_setting_int(CFG, 'XBMC', 'xbmc_update_onlyfirst', 0))
XBMC_HOST = check_setting_str(CFG, 'XBMC', 'xbmc_host', '')
XBMC_USERNAME = check_setting_str(CFG, 'XBMC', 'xbmc_username', '')
XBMC_PASSWORD = check_setting_str(CFG, 'XBMC', 'xbmc_password', '')
USE_KODI = bool(check_setting_int(CFG, 'Kodi', 'use_kodi', 0)) USE_KODI = bool(check_setting_int(CFG, 'Kodi', 'use_kodi', 0))
KODI_ALWAYS_ON = bool(check_setting_int(CFG, 'Kodi', 'kodi_always_on', 1)) KODI_ALWAYS_ON = bool(check_setting_int(CFG, 'Kodi', 'kodi_always_on', 1))
@ -805,6 +809,18 @@ def initialize(consoleLogging=True):
KODI_USERNAME = check_setting_str(CFG, 'Kodi', 'kodi_username', '') KODI_USERNAME = check_setting_str(CFG, 'Kodi', 'kodi_username', '')
KODI_PASSWORD = check_setting_str(CFG, 'Kodi', 'kodi_password', '') KODI_PASSWORD = check_setting_str(CFG, 'Kodi', 'kodi_password', '')
USE_XBMC = bool(check_setting_int(CFG, 'XBMC', 'use_xbmc', 0))
XBMC_ALWAYS_ON = bool(check_setting_int(CFG, 'XBMC', 'xbmc_always_on', 1))
XBMC_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'XBMC', 'xbmc_notify_onsnatch', 0))
XBMC_NOTIFY_ONDOWNLOAD = bool(check_setting_int(CFG, 'XBMC', 'xbmc_notify_ondownload', 0))
XBMC_NOTIFY_ONSUBTITLEDOWNLOAD = bool(check_setting_int(CFG, 'XBMC', 'xbmc_notify_onsubtitledownload', 0))
XBMC_UPDATE_LIBRARY = bool(check_setting_int(CFG, 'XBMC', 'xbmc_update_library', 0))
XBMC_UPDATE_FULL = bool(check_setting_int(CFG, 'XBMC', 'xbmc_update_full', 0))
XBMC_UPDATE_ONLYFIRST = bool(check_setting_int(CFG, 'XBMC', 'xbmc_update_onlyfirst', 0))
XBMC_HOST = check_setting_str(CFG, 'XBMC', 'xbmc_host', '')
XBMC_USERNAME = check_setting_str(CFG, 'XBMC', 'xbmc_username', '')
XBMC_PASSWORD = check_setting_str(CFG, 'XBMC', 'xbmc_password', '')
USE_PLEX = bool(check_setting_int(CFG, 'Plex', 'use_plex', 0)) USE_PLEX = bool(check_setting_int(CFG, 'Plex', 'use_plex', 0))
PLEX_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'Plex', 'plex_notify_onsnatch', 0)) PLEX_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'Plex', 'plex_notify_onsnatch', 0))
PLEX_NOTIFY_ONDOWNLOAD = bool(check_setting_int(CFG, 'Plex', 'plex_notify_ondownload', 0)) PLEX_NOTIFY_ONDOWNLOAD = bool(check_setting_int(CFG, 'Plex', 'plex_notify_ondownload', 0))
@ -1335,7 +1351,12 @@ def halt():
pass pass
if ADBA_CONNECTION: if ADBA_CONNECTION:
ADBA_CONNECTION.logout() try:
ADBA_CONNECTION.logout()
except AniDBBannedError as e:
logger.log(u'ANIDB Error %s' % ex(e), logger.DEBUG)
except AniDBError as e:
pass
logger.log(u'Waiting for the ANIDB CONNECTION thread to exit') logger.log(u'Waiting for the ANIDB CONNECTION thread to exit')
try: try:
ADBA_CONNECTION.join(10) ADBA_CONNECTION.join(10)
@ -1591,18 +1612,11 @@ def save_config():
new_config['TORRENT']['torrent_label'] = TORRENT_LABEL new_config['TORRENT']['torrent_label'] = TORRENT_LABEL
new_config['TORRENT']['torrent_verify_cert'] = int(TORRENT_VERIFY_CERT) new_config['TORRENT']['torrent_verify_cert'] = int(TORRENT_VERIFY_CERT)
new_config['XBMC'] = {} new_config['Emby'] = {}
new_config['XBMC']['use_xbmc'] = int(USE_XBMC) new_config['Emby']['use_emby'] = int(USE_EMBY)
new_config['XBMC']['xbmc_always_on'] = int(XBMC_ALWAYS_ON) new_config['Emby']['emby_update_library'] = int(EMBY_UPDATE_LIBRARY)
new_config['XBMC']['xbmc_notify_onsnatch'] = int(XBMC_NOTIFY_ONSNATCH) new_config['Emby']['emby_host'] = EMBY_HOST
new_config['XBMC']['xbmc_notify_ondownload'] = int(XBMC_NOTIFY_ONDOWNLOAD) new_config['Emby']['emby_apikey'] = EMBY_APIKEY
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_host'] = XBMC_HOST
new_config['XBMC']['xbmc_username'] = XBMC_USERNAME
new_config['XBMC']['xbmc_password'] = helpers.encrypt(XBMC_PASSWORD, ENCRYPTION_VERSION)
new_config['Kodi'] = {} new_config['Kodi'] = {}
new_config['Kodi']['use_kodi'] = int(USE_KODI) new_config['Kodi']['use_kodi'] = int(USE_KODI)
@ -1617,6 +1631,19 @@ def save_config():
new_config['Kodi']['kodi_username'] = KODI_USERNAME new_config['Kodi']['kodi_username'] = KODI_USERNAME
new_config['Kodi']['kodi_password'] = helpers.encrypt(KODI_PASSWORD, ENCRYPTION_VERSION) new_config['Kodi']['kodi_password'] = helpers.encrypt(KODI_PASSWORD, ENCRYPTION_VERSION)
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_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'] = {}
new_config['Plex']['use_plex'] = int(USE_PLEX) new_config['Plex']['use_plex'] = int(USE_PLEX)
new_config['Plex']['plex_notify_onsnatch'] = int(PLEX_NOTIFY_ONSNATCH) new_config['Plex']['plex_notify_onsnatch'] = int(PLEX_NOTIFY_ONSNATCH)

View file

@ -1125,6 +1125,10 @@ def getURL(url, post_data=None, params=None, headers=None, timeout=30, session=N
req_headers.update(headers) req_headers.update(headers)
session.headers.update(req_headers) session.headers.update(req_headers)
mute_connect_err = kwargs.get('mute_connect_err')
if mute_connect_err:
del(kwargs['mute_connect_err'])
# request session ssl verify # request session ssl verify
session.verify = False session.verify = False
@ -1177,7 +1181,7 @@ def getURL(url, post_data=None, params=None, headers=None, timeout=30, session=N
logger.log(u'HTTP error %s while loading URL %s' % (e.errno, e.request.url), logger.WARNING) logger.log(u'HTTP error %s while loading URL %s' % (e.errno, e.request.url), logger.WARNING)
return return
except requests.exceptions.ConnectionError as e: except requests.exceptions.ConnectionError as e:
if not kwargs.get('mute_connect_err'): if not mute_connect_err:
logger.log(u'Connection error msg:%s while loading URL %s' % (e.message, e.request.url), logger.WARNING) logger.log(u'Connection error msg:%s while loading URL %s' % (e.message, e.request.url), logger.WARNING)
return return
except requests.exceptions.ReadTimeout as e: except requests.exceptions.ReadTimeout as e:

View file

@ -16,8 +16,9 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with SickGear. If not, see <http://www.gnu.org/licenses/>. # along with SickGear. If not, see <http://www.gnu.org/licenses/>.
import xbmc import emby
import kodi import kodi
import xbmc
import plex import plex
import nmj import nmj
import nmjv2 import nmjv2
@ -40,8 +41,9 @@ from lib import libtrakt
import emailnotify import emailnotify
# home theater / nas # home theater / nas
xbmc_notifier = xbmc.XBMCNotifier() emby_notifier = emby.EmbyNotifier()
kodi_notifier = kodi.KodiNotifier() kodi_notifier = kodi.KodiNotifier()
xbmc_notifier = xbmc.XBMCNotifier()
plex_notifier = plex.PLEXNotifier() plex_notifier = plex.PLEXNotifier()
nmj_notifier = nmj.NMJNotifier() nmj_notifier = nmj.NMJNotifier()
nmjv2_notifier = nmjv2.NMJv2Notifier() nmjv2_notifier = nmjv2.NMJv2Notifier()
@ -64,8 +66,8 @@ email_notifier = emailnotify.EmailNotifier()
notifiers = [ notifiers = [
libnotify_notifier, # Libnotify notifier goes first because it doesn't involve blocking on network activity. libnotify_notifier, # Libnotify notifier goes first because it doesn't involve blocking on network activity.
xbmc_notifier,
kodi_notifier, kodi_notifier,
xbmc_notifier,
plex_notifier, plex_notifier,
nmj_notifier, nmj_notifier,
nmjv2_notifier, nmjv2_notifier,

170
sickbeard/notifiers/emby.py Normal file
View file

@ -0,0 +1,170 @@
# coding=utf-8
#
# 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
class EmbyNotifier:
def __init__(self):
self.sg_logo_url = 'https://raw.githubusercontent.com/SickGear/SickGear/master/gui/slick/images/ico/' + \
'apple-touch-icon-precomposed.png'
self.response = None
self.test_mode = False
def _notify_emby(self, msg, hosts=None, apikeys=None):
""" Internal wrapper for the test_notify function
Args:
msg: Message body of the notice to send
Returns:
2-Tuple True if msg successfully sent otherwise False, Failure message string or None
"""
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')
return False
from sickbeard.indexers.indexer_config import INDEXER_TVDB
args = show and INDEXER_TVDB == show.indexer \
and dict(post_json={'TvdbId': '%s' % show.indexerid}) or dict(data=None)
mode_to_log = show and 'show "%s"' % show.name or 'all shows'
total_success = True
for i, cur_host in enumerate(hosts):
self.response = None
# noinspection PyArgumentList
response = sickbeard.helpers.getURL(
'http://%s/emby/Library/Series/Updated' % cur_host,
headers={'Content-type': 'application/json', 'X-MediaBrowser-Token': keys[i]},
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)
continue
elif self.response and 401 == self.response.get('status_code'):
self._log(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)
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)
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)
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
def _check_config(self, hosts=None, apikeys=None):
from sickbeard.helpers import starify
hosts, keys = hosts or sickbeard.EMBY_HOST, apikeys or 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()]
new_keys = []
has_old_key = False
for key in keys:
if starify(key, True):
has_old_key = True
else:
new_keys += [key]
apikeys = (new_keys, [x.strip() for x in sickbeard.EMBY_APIKEY.split(',') if x.strip()] + new_keys)[has_old_key]
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)
return False, False, message
return hosts, apikeys, 'OK'
@staticmethod
def _log(msg, log_level=logger.WARNING):
logger.log(u'Emby: %s' % msg, log_level)
##############################################################################
# Public functions
##############################################################################
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

View file

@ -48,6 +48,26 @@ class KodiNotifier:
self.prefix = '' self.prefix = ''
self.test_mode = False 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): def _get_kodi_version(self, host):
""" Return Kodi JSON-RPC API version (odd # = dev, even # = stable) """ Return Kodi JSON-RPC API version (odd # = dev, even # = stable)
@ -67,7 +87,8 @@ class KodiNotifier:
6 | v12 (Frodo) / v13 (Gotham) 6 | v12 (Frodo) / v13 (Gotham)
""" """
response = self._send_to_kodi_json(host, dict(method='JSONRPC.Version'), 10) timeout = 10
response = self._send_to_kodi_json(host, dict(method='JSONRPC.Version'), timeout)
if self.response and 401 == self.response.get('status_code'): if self.response and 401 == self.response.get('status_code'):
return False return False
@ -77,13 +98,13 @@ class KodiNotifier:
# fallback to legacy HTTPAPI method # fallback to legacy HTTPAPI method
test_command = {'command': 'Help'} test_command = {'command': 'Help'}
if self._send_to_kodi(host, test_command): if self._send_to_kodi(host, test_command, timeout):
# return fake version number to use the legacy method # return fake version number to use the legacy method
return 1 return 1
if self.response and 404 == self.response.get('status_code'): if self.response and 404 == self.response.get('status_code'):
self.prefix = 'xbmc' self.prefix = 'xbmc'
if self._send_to_kodi(host, test_command): if self._send_to_kodi(host, test_command, timeout):
# return fake version number to use the legacy method # return fake version number to use the legacy method
return 1 return 1
@ -125,7 +146,7 @@ class KodiNotifier:
elif not api_version: elif not api_version:
total_success = False total_success = False
message += ['Fail: No supported Kodi found at %s' % cur_host] message += ['Fail: No supported Kodi found at %s' % cur_host]
self._maybe_log_failed_detection(cur_host) self._maybe_log_failed_detection(cur_host, 'connect and detect version for')
else: else:
if 4 >= api_version: if 4 >= api_version:
self._log(u'Detected %sversion <= 11, using HTTP API' self._log(u'Detected %sversion <= 11, using HTTP API'
@ -141,12 +162,62 @@ class KodiNotifier:
'message': '%s' % msg, 'message': '%s' % msg,
'image': '%s' % self.sg_logo_url}) 'image': '%s' % self.sg_logo_url})
response_notify = __method_send(cur_host, command) response_notify = __method_send(cur_host, command, 10)
if response_notify: if response_notify:
message += ['%s: %s' % ((response_notify, 'OK')['OK' in response_notify], cur_host)] message += ['%s: %s' % ((response_notify, 'OK')['OK' in response_notify], cur_host)]
return total_success, '<br />\n'.join(message) return total_success, '<br />\n'.join(message)
def _update_library(self, show_name=None):
""" 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.
Uses a list of comma delimited hosts where only one is updated, the first to respond with success. This is a
workaround for SQL backend users because updating multiple clients causes duplicate entries.
Future plan is to revisit how host/ip/username/pw/options are stored so that this may become more flexible.
Args:
show_name: Name of a TV show to target for a library update
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')
return False
# either update each host, or only attempt to update until one successful result
result = 0
only_first = dict(show='', first='', first_note='')
show_name and only_first.update(show=' for show;"%s"' % show_name)
sickbeard.KODI_UPDATE_ONLYFIRST and only_first.update(dict(
first=' first', first_note=' in line with the "Only update first host"%s' % ' setting'))
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'))
if self.response and 401 == self.response.get('status_code'):
self._log(u'Failed to authenticate with %s' % cur_host, logger.DEBUG)
continue
if not response:
self._maybe_log_failed_detection(cur_host)
continue
if self._send_update_library(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)
else:
self._maybe_log_failed_detection(cur_host)
result += 1
if sickbeard.KODI_UPDATE_ONLYFIRST:
return True
# 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_update_library(self, host, show_name=None):
""" Internal wrapper for the update library function """ Internal wrapper for the update library function
@ -164,7 +235,7 @@ class KodiNotifier:
api_version = self._get_kodi_version(host) api_version = self._get_kodi_version(host)
if api_version: if api_version:
# try to update just the show, if it fails, do full update if enabled # try to update just the show, if it fails, do full update if enabled
__method_update = (self._update_library, self._update_library_json)[4 < api_version] __method_update = (self._update, self._update_json)[4 < api_version]
if __method_update(host, show_name): if __method_update(host, show_name):
return True return True
@ -181,7 +252,7 @@ class KodiNotifier:
# Legacy HTTP API (pre Kodi 12) methods # Legacy HTTP API (pre Kodi 12) methods
############################################################################## ##############################################################################
def _send_to_kodi(self, host, command): def _send_to_kodi(self, host, command, timeout=30):
""" Handle communication to Kodi servers via HTTP API """ Handle communication to Kodi servers via HTTP API
Args: Args:
@ -203,11 +274,12 @@ class KodiNotifier:
args['auth'] = (self.username or sickbeard.KODI_USERNAME, self.password or sickbeard.KODI_PASSWORD) args['auth'] = (self.username or sickbeard.KODI_USERNAME, self.password or sickbeard.KODI_PASSWORD)
url = 'http://%s/%sCmds/%sHttp' % (host, self.prefix or 'kodi', self.prefix or 'kodi') url = 'http://%s/%sCmds/%sHttp' % (host, self.prefix or 'kodi', self.prefix or 'kodi')
response = sickbeard.helpers.getURL(url=url, params=command, hooks=dict(response=self.cb_response), **args) response = sickbeard.helpers.getURL(url=url, params=command,
timeout=timeout, hooks=dict(response=self.cb_response), **args)
return response or False return response or False
def _update_library(self, host=None, show_name=None): def _update(self, host=None, show_name=None):
""" Handle updating Kodi host via HTTP API """ Handle updating Kodi host via HTTP API
Update the video library for a specific tv show if passed, otherwise update the whole library if option enabled. Update the video library for a specific tv show if passed, otherwise update the whole library if option enabled.
@ -323,12 +395,7 @@ class KodiNotifier:
% (json.dumps(response['error']), host, json.dumps(command)), logger.ERROR) % (json.dumps(response['error']), host, json.dumps(command)), logger.ERROR)
return result return result
# noinspection PyUnusedLocal def _update_json(self, host=None, show_name=None):
def cb_response(self, r, *args, **kwargs):
self.response = dict(status_code=r.status_code)
return r
def _update_library_json(self, host=None, show_name=None):
""" Handle updating Kodi host via HTTP JSON-RPC """ Handle updating Kodi host via HTTP JSON-RPC
Update the video library for a specific tv show if passed, otherwise update the whole library if option enabled. Update the video library for a specific tv show if passed, otherwise update the whole library if option enabled.
@ -410,99 +477,49 @@ class KodiNotifier:
return True return True
def _maybe_log_failed_detection(self, host):
self._maybe_log(u'Failed to detect version for %s, check configuration.' % host)
def _maybe_log(self, msg, log_level=None):
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 always logs)' or ''),
log_level)
@staticmethod
def _log(msg, log_level=logger.WARNING):
logger.log(u'Kodi: %s' % msg, log_level)
############################################################################## ##############################################################################
# Public functions which will call the JSON or Legacy HTTP API methods # Public functions which will call the JSON or Legacy HTTP API methods
############################################################################## ##############################################################################
def notify_snatch(self, ep_name): def notify_snatch(self, ep_name):
if sickbeard.KODI_NOTIFY_ONSNATCH: if sickbeard.KODI_NOTIFY_ONSNATCH:
self._notify_kodi(ep_name, common.notifyStrings[common.NOTIFY_SNATCH]) self._notify_kodi(ep_name, common.notifyStrings[common.NOTIFY_SNATCH])
def notify_download(self, ep_name): def notify_download(self, ep_name):
if sickbeard.KODI_NOTIFY_ONDOWNLOAD: if sickbeard.KODI_NOTIFY_ONDOWNLOAD:
self._notify_kodi(ep_name, common.notifyStrings[common.NOTIFY_DOWNLOAD]) self._notify_kodi(ep_name, common.notifyStrings[common.NOTIFY_DOWNLOAD])
def notify_subtitle_download(self, ep_name, lang): def notify_subtitle_download(self, ep_name, lang):
if sickbeard.KODI_NOTIFY_ONSUBTITLEDOWNLOAD: if sickbeard.KODI_NOTIFY_ONSUBTITLEDOWNLOAD:
self._notify_kodi('%s: %s' % (ep_name, lang), common.notifyStrings[common.NOTIFY_SUBTITLE_DOWNLOAD]) self._notify_kodi('%s: %s' % (ep_name, lang), common.notifyStrings[common.NOTIFY_SUBTITLE_DOWNLOAD])
def notify_git_update(self, new_version='??'): def notify_git_update(self, new_version='??'):
if sickbeard.USE_KODI: if sickbeard.USE_KODI:
update_text = common.notifyStrings[common.NOTIFY_GIT_UPDATE_TEXT] update_text = common.notifyStrings[common.NOTIFY_GIT_UPDATE_TEXT]
title = common.notifyStrings[common.NOTIFY_GIT_UPDATE] title = common.notifyStrings[common.NOTIFY_GIT_UPDATE]
self._notify_kodi('%s %s' % (update_text, new_version), title) self._notify_kodi('%s %s' % (update_text, new_version), title)
def test_notify(self, host, username, password): def test_notify(self, host, username, password):
self.test_mode, self.username, self.password = True, username, password
return self._notify_kodi('Testing SickGear Kodi notifier', 'Test Notification', kodi_hosts=host)
def update_library(self, showName=None): 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 """ 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. :param showName: Name of a TV show
:param force: True force update process
Uses a list of comma delimited hosts where only one is updated, the first to respond with success. This is a Returns: None if no processing done, True if processing succeeded with no issues else False if any issues found
workaround for SQL backend users because updating multiple clients causes duplicate entries.
Future plan is to revisit how host/ip/username/pw/options are stored so that this may become more flexible.
Args:
showName: Name of a TV show to target for a library update
Returns:
True or False
""" """
if sickbeard.USE_KODI and (sickbeard.KODI_UPDATE_LIBRARY or force):
if sickbeard.USE_KODI and sickbeard.KODI_UPDATE_LIBRARY: return self._update_library(showName)
if not sickbeard.KODI_HOST:
self._log(u'No Kodi hosts specified, check your settings', logger.DEBUG)
return False
# either update each host, or only attempt to update until one successful result
result = 0
only_first = dict(show='', first='', first_note='')
showName and only_first.update(show=' for show;"%s"' % showName)
sickbeard.KODI_UPDATE_ONLYFIRST and only_first.update(dict(
first=' first', first_note=' in line with the "Only update first host"%s' % ' setting'))
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'))
if self.response and 401 == self.response.get('status_code'):
self._log(u'Failed to authenticate with %s' % cur_host, logger.DEBUG)
continue
if not response:
self._maybe_log_failed_detection(cur_host)
continue
if self._send_update_library(cur_host, showName):
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)
else:
self._maybe_log_failed_detection(cur_host)
result += 1
if sickbeard.KODI_UPDATE_ONLYFIRST:
return True
# needed for the 'update kodi' submenu command as it only cares of the final result vs the individual ones
return 0 == result
notifier = KodiNotifier notifier = KodiNotifier

View file

@ -1035,12 +1035,15 @@ class PostProcessor(object):
# send notifications # send notifications
notifiers.notify_download(ep_obj._format_pattern('%SN - %Sx%0E - %EN - %QN')) notifiers.notify_download(ep_obj._format_pattern('%SN - %Sx%0E - %EN - %QN'))
# do the library update for XBMC # do the library update for Emby
notifiers.xbmc_notifier.update_library(ep_obj.show.name) notifiers.emby_notifier.update_library(ep_obj.show)
# do the library update for Kodi # do the library update for Kodi
notifiers.kodi_notifier.update_library(ep_obj.show.name) 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 # do the library update for Plex
notifiers.plex_notifier.update_library(ep_obj) notifiers.plex_notifier.update_library(ep_obj)

View file

@ -42,7 +42,7 @@ def search_propers():
logger.log(u'Beginning search for new propers') logger.log(u'Beginning search for new propers')
age_shows, age_anime = 2, 14 age_shows, age_anime = sickbeard.BACKLOG_DAYS + 2, 14
aired_since_shows = datetime.datetime.today() - datetime.timedelta(days=age_shows) aired_since_shows = datetime.datetime.today() - datetime.timedelta(days=age_shows)
aired_since_anime = datetime.datetime.today() - datetime.timedelta(days=age_anime) aired_since_anime = datetime.datetime.today() - datetime.timedelta(days=age_anime)
recent_shows, recent_anime = _recent_history(aired_since_shows, aired_since_anime) recent_shows, recent_anime = _recent_history(aired_since_shows, aired_since_anime)
@ -100,7 +100,7 @@ def _get_proper_list(aired_since_shows, recent_shows, recent_anime):
name = _generic_name(x.name) name = _generic_name(x.name)
if name not in propers: if name not in propers:
try: try:
parse_result = np.parse(x.title) parse_result = np.parse(x.name)
if parse_result.series_name and parse_result.episode_numbers and \ if parse_result.series_name and parse_result.episode_numbers and \
parse_result.show.indexerid in recent_shows + recent_anime: parse_result.show.indexerid in recent_shows + recent_anime:
logger.log(u'Found new proper: ' + x.name, logger.DEBUG) logger.log(u'Found new proper: ' + x.name, logger.DEBUG)

View file

@ -599,7 +599,6 @@ class NZBProvider(object, GenericProvider):
index = 0 index = 0
alt_search = ('nzbs_org' == self.get_id()) alt_search = ('nzbs_org' == self.get_id())
term_items_found = False
do_search_alt = False do_search_alt = False
search_terms = [] search_terms = []
@ -614,21 +613,16 @@ class NZBProvider(object, GenericProvider):
regex += [terms] regex += [terms]
proper_check = re.compile(r'(?i)(%s)' % '|'.join(regex)) proper_check = re.compile(r'(?i)(%s)' % '|'.join(regex))
urls = []
while index < len(search_terms): while index < len(search_terms):
search_params = {'q': search_terms[index], 'maxage': 4} search_params = {'q': search_terms[index], 'maxage': sickbeard.BACKLOG_DAYS + 2}
if alt_search: if alt_search:
if do_search_alt: if do_search_alt:
search_params['t'] = 'search'
index += 1 index += 1
if term_items_found: do_search_alt = not do_search_alt
do_search_alt = True
term_items_found = False
else:
if do_search_alt:
search_params['t'] = 'search'
do_search_alt = (True, False)[do_search_alt]
else: else:
index += 1 index += 1
@ -637,8 +631,9 @@ class NZBProvider(object, GenericProvider):
(title, url) = self._title_and_url(item) (title, url) = self._title_and_url(item)
if not proper_check.search(title): if not proper_check.search(title) or url in urls:
continue continue
urls.append(url)
if 'published_parsed' in item and item['published_parsed']: if 'published_parsed' in item and item['published_parsed']:
result_date = item.published_parsed result_date = item.published_parsed
@ -651,10 +646,8 @@ class NZBProvider(object, GenericProvider):
if not search_date or search_date < result_date: if not search_date or search_date < result_date:
search_result = classes.Proper(title, url, result_date, self.show) search_result = classes.Proper(title, url, result_date, self.show)
results.append(search_result) results.append(search_result)
term_items_found = True
do_search_alt = False
time.sleep(0.2) time.sleep(0.5)
return results return results

View file

@ -533,20 +533,25 @@ class Home(MainHandler):
return [ return [
{'title': 'Add Shows', 'path': 'home/addShows/', }, {'title': 'Add Shows', 'path': 'home/addShows/', },
{'title': 'Manual Post-Processing', 'path': 'home/postprocess/'}, {'title': 'Manual Post-Processing', 'path': 'home/postprocess/'},
{'title': 'Update XBMC', 'path': 'home/updateXBMC/', 'requires': self.haveXBMC}, {'title': 'Update Emby', 'path': 'home/updateEMBY/', 'requires': self.haveEMBY},
{'title': 'Update Kodi', 'path': 'home/updateKODI/', 'requires': self.haveKODI}, {'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 Plex', 'path': 'home/updatePLEX/', 'requires': self.havePLEX},
{'title': 'Restart', 'path': 'home/restart/?pid=' + str(sickbeard.PID), 'confirm': True}, {'title': 'Restart', 'path': 'home/restart/?pid=' + str(sickbeard.PID), 'confirm': True},
{'title': 'Shutdown', 'path': 'home/shutdown/?pid=' + str(sickbeard.PID), 'confirm': True}, {'title': 'Shutdown', 'path': 'home/shutdown/?pid=' + str(sickbeard.PID), 'confirm': True},
] ]
@staticmethod @staticmethod
def haveXBMC(): def haveEMBY():
return sickbeard.USE_XBMC and sickbeard.XBMC_UPDATE_LIBRARY return sickbeard.USE_EMBY
@staticmethod @staticmethod
def haveKODI(): def haveKODI():
return sickbeard.USE_KODI and sickbeard.KODI_UPDATE_LIBRARY return sickbeard.USE_KODI
@staticmethod
def haveXBMC():
return sickbeard.USE_XBMC and sickbeard.XBMC_UPDATE_LIBRARY
@staticmethod @staticmethod
def havePLEX(): def havePLEX():
@ -771,6 +776,29 @@ class Home(MainHandler):
else: else:
return 'Error sending tweet' return 'Error sending tweet'
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): def testXBMC(self, host=None, username=None, password=None):
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store') self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
@ -789,19 +817,6 @@ class Home(MainHandler):
return finalResult return finalResult
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 testPMC(self, host=None, username=None, password=None): def testPMC(self, host=None, username=None, password=None):
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store') self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
@ -1184,12 +1199,16 @@ class Home(MainHandler):
t.submenu.append({'title': 'Re-scan files', 'path': 'home/refreshShow?show=%d' % showObj.indexerid}) t.submenu.append({'title': 'Re-scan files', 'path': 'home/refreshShow?show=%d' % showObj.indexerid})
t.submenu.append( t.submenu.append(
{'title': 'Force Full Update', 'path': 'home/updateShow?show=%d&amp;force=1&amp;web=1' % showObj.indexerid}) {'title': 'Force Full Update', 'path': 'home/updateShow?show=%d&amp;force=1&amp;web=1' % showObj.indexerid})
t.submenu.append({'title': 'Update show in XBMC', t.submenu.append({'title': 'Update show in Emby',
'path': 'home/updateXBMC?showName=%s' % urllib.quote_plus( 'path': 'home/updateEMBY%s' %
showObj.name.encode('utf-8')), 'requires': self.haveXBMC}) (INDEXER_TVDB == showObj.indexer and ('?show=%s' % showObj.indexerid) or '/'),
'requires': self.haveEMBY})
t.submenu.append({'title': 'Update show in Kodi', t.submenu.append({'title': 'Update show in Kodi',
'path': 'home/updateKODI?showName=%s' % urllib.quote_plus( 'path': 'home/updateKODI?showName=%s' % urllib.quote_plus(
showObj.name.encode('utf-8')), 'requires': self.haveKODI}) showObj.name.encode('utf-8')), 'requires': self.haveKODI})
t.submenu.append({'title': 'Update show in XBMC',
'path': 'home/updateXBMC?showName=%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}) t.submenu.append({'title': 'Media Renamer', 'path': 'home/testRename?show=%d' % showObj.indexerid})
if sickbeard.USE_SUBTITLES and not sickbeard.showQueueScheduler.action.isBeingSubtitled( if sickbeard.USE_SUBTITLES and not sickbeard.showQueueScheduler.action.isBeingSubtitled(
showObj) and showObj.subtitles: showObj) and showObj.subtitles:
@ -1617,6 +1636,30 @@ class Home(MainHandler):
self.redirect('/home/displayShow?show=' + str(showObj.indexerid)) self.redirect('/home/displayShow?show=' + str(showObj.indexerid))
def updateEMBY(self, show=None):
if notifiers.emby_notifier.update_library(
sickbeard.helpers.findCertainShow(sickbeard.showList,helpers.tryInt(show, None)), force=True):
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):
# only send update to first host in the list -- workaround for kodi sql backend users
if sickbeard.KODI_UPDATE_ONLYFIRST:
# only send update to first host in the list -- workaround for kodi sql backend users
host = sickbeard.KODI_HOST.split(',')[0].strip()
else:
host = sickbeard.KODI_HOST
if notifiers.kodi_notifier.update_library(showName=showName, force=True):
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): def updateXBMC(self, showName=None):
# only send update to first host in the list -- workaround for xbmc sql backend users # only send update to first host in the list -- workaround for xbmc sql backend users
@ -1632,21 +1675,6 @@ class Home(MainHandler):
ui.notifications.error('Unable to contact one or more XBMC host(s): ' + host) ui.notifications.error('Unable to contact one or more XBMC host(s): ' + host)
self.redirect('/home/') self.redirect('/home/')
def updateKODI(self, showName=None):
# only send update to first host in the list -- workaround for kodi sql backend users
if sickbeard.KODI_UPDATE_ONLYFIRST:
# only send update to first host in the list -- workaround for kodi sql backend users
host = sickbeard.KODI_HOST.split(',')[0].strip()
else:
host = sickbeard.KODI_HOST
if notifiers.kodi_notifier.update_library(showName=showName):
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 updatePLEX(self, *args, **kwargs): def updatePLEX(self, *args, **kwargs):
result = notifiers.plex_notifier.update_library() result = notifiers.plex_notifier.update_library()
if None is result: if None is result:
@ -4873,14 +4901,16 @@ class ConfigNotifications(Config):
'b64': base64.urlsafe_b64encode(location)}) 'b64': base64.urlsafe_b64encode(location)})
return t.respond() return t.respond()
def saveNotifications(self, use_xbmc=None, xbmc_always_on=None, xbmc_notify_onsnatch=None, def saveNotifications(self,
xbmc_notify_ondownload=None, use_emby=None, emby_update_library=None, emby_host=None, emby_apikey=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_kodi=None, kodi_always_on=None, kodi_notify_onsnatch=None, kodi_notify_ondownload=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_notify_onsubtitledownload=None, kodi_update_onlyfirst=None,
kodi_update_full=None, kodi_host=None, kodi_username=None, kodi_password=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, use_plex=None, plex_notify_onsnatch=None, plex_notify_ondownload=None,
plex_notify_onsubtitledownload=None, plex_update_library=None, plex_notify_onsubtitledownload=None, plex_update_library=None,
plex_server_host=None, plex_host=None, plex_username=None, plex_password=None, plex_server_host=None, plex_host=None, plex_username=None, plex_password=None,
@ -4922,18 +4952,24 @@ class ConfigNotifications(Config):
results = [] results = []
sickbeard.USE_XBMC = config.checkbox_to_value(use_xbmc) sickbeard.USE_EMBY = config.checkbox_to_value(use_emby)
sickbeard.XBMC_ALWAYS_ON = config.checkbox_to_value(xbmc_always_on) sickbeard.EMBY_UPDATE_LIBRARY = config.checkbox_to_value(emby_update_library)
sickbeard.XBMC_NOTIFY_ONSNATCH = config.checkbox_to_value(xbmc_notify_onsnatch) sickbeard.EMBY_HOST = config.clean_hosts(emby_host)
sickbeard.XBMC_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(xbmc_notify_ondownload) keys_changed = False
sickbeard.XBMC_NOTIFY_ONSUBTITLEDOWNLOAD = config.checkbox_to_value(xbmc_notify_onsubtitledownload) all_keys = []
sickbeard.XBMC_UPDATE_LIBRARY = config.checkbox_to_value(xbmc_update_library) old_keys = [x.strip() for x in sickbeard.EMBY_APIKEY.split(',') if x.strip()]
sickbeard.XBMC_UPDATE_FULL = config.checkbox_to_value(xbmc_update_full) new_keys = [x.strip() for x in emby_apikey.split(',') if x.strip()]
sickbeard.XBMC_UPDATE_ONLYFIRST = config.checkbox_to_value(xbmc_update_onlyfirst) for key in new_keys:
sickbeard.XBMC_HOST = config.clean_hosts(xbmc_host) if not starify(key, True):
sickbeard.XBMC_USERNAME = xbmc_username keys_changed = True
if set('*') != set(xbmc_password): all_keys += [key]
sickbeard.XBMC_PASSWORD = xbmc_password continue
for x in old_keys:
if key.startswith(x[0:3]) and key.endswith(x[-4:]):
all_keys += [x]
break
if keys_changed or (len(all_keys) != len(old_keys)):
sickbeard.EMBY_APIKEY = ','.join(all_keys)
sickbeard.USE_KODI = config.checkbox_to_value(use_kodi) sickbeard.USE_KODI = config.checkbox_to_value(use_kodi)
sickbeard.KODI_ALWAYS_ON = config.checkbox_to_value(kodi_always_on) sickbeard.KODI_ALWAYS_ON = config.checkbox_to_value(kodi_always_on)
@ -4948,6 +4984,19 @@ class ConfigNotifications(Config):
if set('*') != set(kodi_password): if set('*') != set(kodi_password):
sickbeard.KODI_PASSWORD = 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_PLEX = config.checkbox_to_value(use_plex) sickbeard.USE_PLEX = config.checkbox_to_value(use_plex)
sickbeard.PLEX_NOTIFY_ONSNATCH = config.checkbox_to_value(plex_notify_onsnatch) sickbeard.PLEX_NOTIFY_ONSNATCH = config.checkbox_to_value(plex_notify_onsnatch)
sickbeard.PLEX_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(plex_notify_ondownload) sickbeard.PLEX_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(plex_notify_ondownload)