Add fanart to Episodes View, Display Show, Edit Show, and Media Renamer page.

Add "Maximum fanart image files per show to cache" to config General/Interface.
Add populate images when the daily show updater is run with a default maximum 3 images per show.
Change force full update in a show will replace existing images with new.
Add fanart livepanel to lower right of Episodes View and Display Show page.
Add highlight panel red until button is clicked a few times.
Add flick through multiple background images on Episodes View and Display Show page.
Add persistent move poster image to right hand side or hide on Display Show page (multi-click the eye).
Add persistent translucency of background images on Episodes View and Display Show page.
Add persistent fanart rating to avoid art completely, random display, random from a group, or display fave always.
Add persistent views of the show detail on Display Show page.
Add persistent views on Episodes View.
Add persistent button to collapse and expand card images on Episode View/Layout daybyday.
Add non persistent "Open gear" and "Full fanart" image views to Episodes View and Display Show page.
Add "smart" selection of fanart image to display on Episode view.
Change insert [!] and change text shade of ended shows in drop down show list on Display Show page.
Change button graphic for next and previous show of show list on Display Show page.
Add logic to hide some livepanel buttons until artwork becomes available or in other circumstances.
Add "(Ended)" where appropriate to show title on Display Show page.
Add links to fanart.tv where appropriate on Display Show page.
Change use tense for label "Airs" or "Aired" depending on if show ended.
Change display "No files" instead of "0 files" and "Upgrade once" instead of "End upgrade on first match".
Add persistent button to newest season to "Show all" episodes.
Add persistent button to all shown seasons to "Hide most" episodes.
Add button to older seasons to toggle "Show Season n" or "Show Specials" with "Hide..." episodes.
Add season level status counts next to each season header on display show page
Add sorting to season table headers on display show page
Add filename and size to quality badge on display show page, removed its redundant "downloaded" text
Remove redundant "Add show" buttons
Change combine the NFO and TBN columns into a single Meta column
Change reduce screen estate used by episode numbers columns
Change improve clarity of text on Add Show page.
Add "Reset fanart ratings" to show Edit/Other tab.
Add fanart usage to show Edit/Other tab.
Add fanart keys guide to show Edit/Other tab.
Change add placeholder tip to "Alternative release name(s)" on show Edit.
Change add placeholder tip to search box on shows Search.
Change hide Anime tips on show Edit when selecting its mutually exclusive options.
Change label "End upgrade on first match" to "Upgrade once" on show Edit.
Change improve performance rendering displayShow.
Add total episodes to start of show description (excludes specials if those are hidden).
Add "Add show" actions i.e. "Search", "Trakt cards", "IMDb cards", and "Anime" to Shows menu.
Add "Import (existing)" action to Tools menu.
Change SD quality from red to dark green, 2160p UHD 4K is red.
Change relocate the functions of Logs & Errors to the right side Tools menu -> View Log File.
Add warning indicator to the Tools menu in different colour depending on error count (green through red).
Change View Log error item output from reversed to natural order.
Change View Log add a typeface and some colour to improve readability.
Change View Log/Errors only display "Clear Errors" button when there are errors to clear.
Change improve performance of View Log File.
This commit is contained in:
JackDandy 2016-02-28 23:43:40 +00:00
parent fe0aee6440
commit 6fcf80c02d
80 changed files with 6403 additions and 2403 deletions

View file

@ -201,6 +201,56 @@
* Change sab API request to prevent naming mismatch
* Change update rTorrent systems
* Change logger to properly cleanup used resources
* Add fanart to Episodes View, Display Show, and Edit Show page
* Add path used for fanart images <Cache Dir>/images/fanart (<Cache Dir> value on Help page)
* Add populate images when the daily show updater is run with default maximum 3 images per show
* Change force full update in a show will replace existing images with new
* Add "Maximum fanart image files per show to cache" to config General/Interface
* Add fanart livepanel to lower right of Episodes View and Display Show page
* Add highlight panel red on Episodes view until button is clicked a few times
* Add flick through multiple background images on Episodes View and Display Show page
* Add persistent move poster image to right hand side or hide on Display Show page (multi-click the eye)
* Add persistent translucency of background images on Episodes View and Display Show page
* Add persistent fanart rating to avoid art completely, random display, random from a group, or display fave always
* Add persistent views of the show detail on Display Show page
* Add persistent views on Episodes View
* Add persistent button to collapse and expand card images on Episode View/Layout daybyday
* Add non persistent "Open gear" and "Backart only" image views to Episodes View and Display Show page
* Add "smart" selection of fanart image to display on Episode view
* Change insert [!] and change text shade of ended shows in drop down show list on Display Show page
* Change button graphic for next and previous show of show list on Display Show page
* Add logic to hide some livepanel buttons until artwork becomes available or in other circumstances
* Add "(Ended)" where appropriate to show title on Display Show page
* Change use tense for label "Airs" or "Aired" depending on if show ended
* Change display "No files" instead of "0 files" and "Upgrade once" instead of "End upgrade on first match"
* Add persistent button to newest season to "Show all" episodes
* Add persistent button to all shown seasons to "Hide most" episodes
* Add button to older seasons to toggle "Show Season n" or "Show Specials" with "Hide..." episodes
* Add season level status counts next to each season header on display show page
* Add sorting to season table headers on display show page
* Add filename and size to quality badge on display show page, removed its redundant "downloaded" text
* Remove redundant "Add show" buttons
* Change combine the NFO and TBN columns into a single Meta column
* Change reduce screen estate used by episode numbers columns
* Change improve clarity of text on Add Show page
* Change rename Edit show/"Post-Processing" tab to "Other"
* Add "Reset fanart ratings" to show Edit/Other tab
* Add fanart keys guide to show Edit/Other tab
* Change add placeholder tip to "Alternative release name(s)" on show Edit
* Change add placeholder tip to search box on shows Search
* Change hide Anime tips on show Edit when selecting its mutually exclusive options
* Change label "End upgrade on first match" to "Upgrade once" on show Edit
* Change improve performance rendering displayShow
* Add total episodes to start of show description (excludes specials if those are hidden)
* Add "Add show" actions i.e. "Search", "Trakt cards", "IMDb cards", and "Anime" to Shows menu
* Add "Import (existing)" action to Tools menu
* Change SD quality from red to dark green, 2160p UHD 4K is red
* Change relocate the functions of Logs & Errors to the right side Tools menu -> View Log File
* Add warning indicator to the Tools menu in different colour depending on error count (green through red)
* Change View Log error item output from reversed to natural order
* Change View Log File add a typeface and some colour to improve readability
* Change View Log File/Errors only display "Clear Errors" button when there are errors to clear
* Change improve performance of View Log File
[develop changelog]
* Change send nzb data to NZBGet for Anizb instead of url
@ -371,7 +421,7 @@
* Fix post processing season pack folders
* Fix saving torrent provider option "Seed until ratio" after recent refactor
* Change white text in light theme on Manage / Episode Status Management page to black for better readability
* Change displayShow page episode colours when a minimum quality is met with "End upgrade on first match"
* Change displayShow page episode colours when a minimum quality is met with "Upgrade once"
* Add seed time per provider for torrent clients that support seed time per torrent, i.e. currently only uTorrent
* Remove seed time display for Transmission in config/Torrent Search page because the torrent client doesn't support it
* Add PreToMe torrent provider
@ -559,7 +609,7 @@
* Change Trakt url to fix baseline uses (e.g. add from trending)
* Fix edit on show page for shows that have anime enabled in mass edit
* Fix issue parsing items in ToktoToshokan provider
* Change to only show option "End upgrade on first match" on edit show page if quality custom is selected
* Change to only show option "Upgrade once" on edit show page if quality custom is selected
* Change label "Show is grouped in" in edit show page to "Show is in group" and move the section higher
* Fix post processing of anime with version tags
* Change accept SD titles that contain audio quality

View file

@ -12,6 +12,7 @@ Libs with customisations...
/lib/pynma/pynma.py
/lib/requests/packages/urllib3/connectionpool.py
/lib/requests/packages/urllib3/util/ssl_.py
/lib/tmdb_api/tmdb_api.py
/lib/tornado
/lib/tvdb/tvdb_api.py
/lib/tvdb_api/tvdb_api.py
/lib/tzlocal/unix.py

View file

@ -1,6 +1,24 @@
/* =======================================================================
inc_top.tmpl
========================================================================== */
.navbar-default .navbar-nav .logger.errors.n,
pre .prelight{
color:#c3ed9b
}
.navbar-default .navbar-nav .logger.errors.nn,
pre .prelight2{
color:#f6ff41
}
.navbar-default .navbar-nav .logger.errors.nnn,
pre .prelight-num{
color:#ffba57
}
.navbar-default .navbar-nav .logger.errors.nnnn{
color:#ff6d5e
}
[class^="icon-"],
[class*=" icon-"]{
@ -41,21 +59,21 @@ inc_top.tmpl
.ui-widget-content{
background:#606060;
border:1px solid #111;
color:#fff
color:#ddd
}
.ui-widget-content a{
color:#2D8FBF
color:#2d8fbf
}
.ui-widget-content a:hover{
color:#09A2FF
color:#09a2ff
}
.ui-widget-header{
background:#3d3d3d;
border:1px solid #111;
color:#fff
color:#ddd
}
.ui-state-default,
@ -103,7 +121,7 @@ inc_top.tmpl
}
.ui-state-default .ui-icon{
background-image:url('../css/lib/images/ui-icons_09a2ff_256x240.png')
background-image:url("../css/lib/images/ui-icons_09a2ff_256x240.png")
}
.ui-state-hover .ui-icon,
@ -133,12 +151,12 @@ inc_top.tmpl
}
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited{
color:#140F06;
color:#140f06;
text-decoration:none
}
.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited{
color:#fff;
color:#ddd;
text-decoration:none
}
@ -180,13 +198,6 @@ inc_top.tmpl
border-top-right-radius:5px
}
#SubMenu{
padding-right:20px;
clear:both;
font-size:12px;
float:right
}
.upgrade-notification{
width:600px;
text-align:center;
@ -201,7 +212,7 @@ inc_bottom.tmpl
========================================================================== */
.footerhighlight{
color:#09A2FF
color:#09a2ff
}
/* =======================================================================
@ -219,7 +230,7 @@ home.tmpl
.ui-font,
a.ui-font{
text-shadow:0 0 0.1em #000;
color:#fff
color:#ddd
}
#show-list .show-card{
@ -265,14 +276,14 @@ a.ui-font{
}
td.tvShow a{
color:#fff;
color:#ddd;
text-decoration:none
}
td.tvShow a:hover span,
td.tvShow a:hover{
cursor:pointer;
color:#09A2FF
color:#09a2ff
}
/* =======================================================================
@ -302,9 +313,8 @@ home_newShow.tmpl
}
#addRootDirTable td label .filepath,
.grey-text{
color:#999
}
.grey-text{color:#999}
.highlight-text{color:#fff}
#newShowPortal #displayText .show-name,
#newShowPortal #displayText .show-dest,
@ -314,7 +324,7 @@ home_newShow.tmpl
#newShowPortal #displayText .show-name,
#newShowPortal #displayText .show-dest{
color:rgb(255, 255, 255)
color:#ddd
}
/* =======================================================================
@ -343,9 +353,28 @@ home_postprocess.tmpl
========================================================================== */
/* =======================================================================
displayShow.tmpl and episodeView.tmpl
========================================================================== */
#episode-view.back-art .controlsBlock,
#episode-view.back-art .h2footer,
#episode-view.back-art.daybyday h1.header,
#episode-view.back-art.pro .daybyday-show,
#display-show.back-art #change-show,
#display-show.back-art #change-status,
#display-show.back-art #no-episode-data,
body.back-art .footer,
#display-show.back-art .sickbeardTable{
background:rgba(0, 0, 0, 0.5)
}
/* =======================================================================
displayShow.tmpl
========================================================================== */
body#display-show.back-art{
color:#ddd
}
tr.seasonheader{
text-align:left;
@ -355,51 +384,88 @@ tr.seasonheader{
#prevShow,
#nextShow,
#topcontrol{
-webkit-filter:"none";
-webkit-filter:none;
filter:none
}
.tvshowImg{
#posterCol .tvshowImg{
border:1px solid #111
}
.display-details{
background-color:#3d3d3d;
border:1px solid #111
body#display-show.back-art .displayshow-wrapper .label a{
color:#ddd
}
body#display-show.back-art .displayshow-wrapper .label a:hover,
body#display-show.back-art .displayshow-wrapper .label a:focus{
color:#aaa
}
.pro .details-title{
color:#999
#edit-show.back-art .ui-tabs-nav > :not(.ui-tabs-active){
background:rgba(0,0,0,0.2)
}
.back-art.pro .details-title{
color:#bbb
#edit-show.back-art .ui-tabs-nav .ui-tabs-active,
#edit-show.back-art .ui-tabs .ui-tabs-panel{
background:rgba(0,0,0,0.8) !important
}
#edit-show.back-art .header,
#display-show.back-art #showCol{
background:rgba(0,0,0,0.5);
border:1px solid rgba(0,0,0,0.5)
}
#livepanel .over-layer0,
#showCol{
background-color:#3d3d3d
}
#livepanel .over-layer0,
#livepanel .over-layer1,
#showCol{
border-color:#111
}
.back-art #livepanel .over-layer0{
background:rgba(0,0,0,0.5);
border-color:rgba(0,0,0,0.5)
}
.pro .details-title,
.pro .hint{
color:#aaa
}
.back-art #details-top .label,
.back-art #details-bottom .label{
background-color:#15528F
background-color:#15528f
}
#details-top .label,
#details-bottom .label{
background-color:#15528F
background-color:#15528f
}
.paused-highlight{
color:#09A2FF
color:#09a2ff
}
.sickbeardTable th{
background-color:#15528f
}
.back-art th.row-seasonheader h3,
.displayshow-wrapper .sickbeardTable th.row-seasonheader{
color:#fff
color:#ddd
}
.back-art.pro .sickbeardTable .seasoncols th,
.translucent.pro .sickbeardTable .seasoncols th{
.back-art.pro.ii .navbar-default,
.back-art.pro.ii .day-of-week .day-number,
.translucent.pro.ii .day-of-week .day-number,
.back-art.pro.ii .sickbeardTable .seasoncols th,
.translucent.pro.ii .sickbeardTable .seasoncols th,
.back-art.pro.ii .sickbeardTable thead .seasoncols td,
.translucent.pro.ii .sickbeardTable thead .seasoncols td{
background-color:rgba(21, 82, 143, 0.5)
}
@ -408,19 +474,19 @@ episodeView.tmpl
========================================================================== */
h2.day, h2.network{
color:#FFF;
color:#ddd;
text-shadow:-1px -1px 0 rgba(0, 0, 0, 0.3);
background-color:#15528F
background-color:#15528f
}
.tvshowDiv{
border:1px solid #ccc;
background:#fff;
background:#ddd;
color:#000
}
.tvshowDiv a:hover{
color:#09A2FF
color:#09a2ff
}
.tvshowTitle a{
@ -450,12 +516,12 @@ h2.day, h2.network{
#showListTable td.tvShow a:hover{
cursor:pointer;
color:#09A2FF
color:#09a2ff
}
.day-of-week .day-number{
background-color:#15528F;
color:#fff
background-color:#15528f;
color:#ddd
}
.today .day-number .number, .today .day-number .month, .today .day-number .day{
@ -477,7 +543,7 @@ h2.day, h2.network{
.day-of-week .text .airtime,
.day-of-week .text .episode,
.day-of-week .text .episode .name{
color:#fff
color:#ddd
}
.day-of-week .text .episode .season,
@ -506,14 +572,14 @@ h2.day, h2.network{
}
.carousel-control{
color:#297AB8;
color:#297ab8;
filter:alpha(opacity=100);
opacity:1
}
.carousel-control:hover,
.carousel-control:focus{
color:#15528F;
color:#15528f;
filter:alpha(opacity=100);
opacity:1
}
@ -524,8 +590,8 @@ h2.day, h2.network{
}
.carousel-indicators .active{
background:#8DBEEE;
border-color:#8DBEEE
background:#8dbeee;
border-color:#8dbeee
}
/* =======================================================================
@ -554,13 +620,16 @@ config*.tmpl
}
.testNotification{
border:1px dotted #CCC
border:1px dotted #ccc
}
#provider_order_list li,
#service_order_list li{
color:#ddd
}
#service_order_list li{
background:#333 !important;
color:#fff
}
.infoTableSeperator{
@ -581,14 +650,23 @@ config_postProcessing.tmpl
border-color:#111
}
.back-art #config .episode-sample{
background:rgba(34,34,34,0.8);
border-color:rgba(17,17,17,0.8)
}
.back-art #config .example{
background:rgba(33,33,33,0.8);
border-color:rgba(11,11,11,0.8);
line-height: 24px
}
.Key{
background-color:#3d3d3d;
border:1px solid #111
}
.Key th, .tableHeader{
color:#fff;
background:#15528F
color:#ddd;
background:#15528f
}
.Key tr{
@ -605,17 +683,17 @@ config_notifications.tmpl
div.metadata_options{
background:#333;
color:#fff;
color:#ddd;
border:1px solid #111
}
div.metadata_options label:hover{
color:#fff;
background-color:#15528F
color:#ddd;
background-color:#15528f
}
div.metadata_options label{
color:#fff
color:#ddd
}
div.metadata_example{
@ -623,7 +701,7 @@ div.metadata_example{
}
div.metadata_example label{
color:#fff
color:#ddd
}
div.metadataDiv .disabled{
@ -631,8 +709,8 @@ div.metadataDiv .disabled{
}
.warning{
border-color:#F89406;
background:url("../images/warning16.png") no-repeat right 5px center #fff
border-color:#f89406;
background:url("../images/warning16.png") no-repeat right 5px center #ddd
}
.solid-border{
@ -648,7 +726,7 @@ manage*.tmpl
========================================================================== */
.separator{
color:#fff
color:#ddd
}
a.whitelink{
@ -660,7 +738,7 @@ a.whitelink{
========================================================================== */
#error-404 path{
fill:#fff
fill:#ddd
}
/* =======================================================================
@ -668,7 +746,7 @@ Global
========================================================================== */
span.path{
color:#09A2FF;
color:#09a2ff;
background-color:#333
}
@ -687,7 +765,7 @@ body{
body,
.show-date{
color:#fff
color:#ddd
}
input, textarea, select, .uneditable-input{
@ -697,24 +775,24 @@ input, textarea, select, .uneditable-input{
/* navbar styling */
.navbar-default{
background-color:#15528F;
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#297AB8', endColorstr='#15528F');
background-image:-ms-linear-gradient(top, #297AB8 0%, #15528F 100%);
background-image:-moz-linear-gradient(top, #297AB8 0%, #15528F 100%);
background-image:-o-linear-gradient(top, #297AB8 0%, #15528F 100%);
background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #297AB8), color-stop(1, #15528F));
background-image:-webkit-linear-gradient(top, #297AB8 0%, #15528F 100%);
background-image:linear-gradient(to bottom, #297AB8 0%, #15528F 100%);
background-color:#15528f;
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#297ab8", endColorstr="#15528f");
background-image:-ms-linear-gradient(top, #297ab8 0%, #15528f 100%);
background-image:-moz-linear-gradient(top, #297ab8 0%, #15528f 100%);
background-image:-o-linear-gradient(top, #297ab8 0%, #15528f 100%);
background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #297ab8), color-stop(1, #15528f));
background-image:-webkit-linear-gradient(top, #297ab8 0%, #15528f 100%);
background-image:linear-gradient(to bottom, #297ab8 0%, #15528f 100%);
border-color:#3e3f3a
}
.navbar-default .navbar-brand{
color:#fff
color:#ddd
}
.navbar-default .navbar-brand:hover,
.navbar-default .navbar-brand:focus{
color:#fff;
color:#ddd;
background-color:transparent
}
@ -728,14 +806,14 @@ input, textarea, select, .uneditable-input{
.navbar-default .navbar-nav > li > a:hover,
.navbar-default .navbar-nav > li > a:focus{
color:#fff;
color:#ddd;
background-color:#124477
}
.navbar-default .navbar-nav > .active > a,
.navbar-default .navbar-nav > .active > a:hover,
.navbar-default .navbar-nav > .active > a:focus{
color:#fff;
color:#ddd;
background-color:#124477
}
@ -768,7 +846,7 @@ input, textarea, select, .uneditable-input{
.navbar-default .navbar-nav > .open > a:hover,
.navbar-default .navbar-nav > .open > a:focus{
background-color:#124477;
color:#fff
color:#ddd
}
@media (max-width:767px){
@ -777,13 +855,13 @@ input, textarea, select, .uneditable-input{
}
.navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,
.navbar-default .navbar-nav .open .dropdown-menu > li > a:focus{
color:#fff;
color:#ddd;
background-color:transparent
}
.navbar-default .navbar-nav .open .dropdown-menu > .active > a,
.navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,
.navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus{
color:#fff;
color:#ddd;
background-color:#124477
}
.navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,
@ -799,7 +877,7 @@ input, textarea, select, .uneditable-input{
}
.navbar-default .navbar-link:hover{
color:#fff
color:#ddd
}
.navbar-default .btn-link{
@ -819,13 +897,13 @@ fieldset[disabled] .navbar-default .btn-link:focus{
}
.dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus{
color:#fff;
color:#ddd;
text-decoration:none;
background-color:#15528F
background-color:#15528f
}
.dropdown-menu > li > a{
color:#fff
color:#ddd
}
.dropdown-menu{
@ -834,26 +912,29 @@ fieldset[disabled] .navbar-default .btn-link:focus{
box-shadow:0 6px 12px rgba(0, 0, 0, 0.176)
}
.img-trakt-16{background-image:url("../images/addshows/trakt16-white.png")}
.img-import-16{background-image:url("../images/addshows/add-existing16-white.png")}
.form-control{
color:#000
}
.btn{
color:#fff;
color:#ddd;
text-shadow:0 1px 1px rgba(0, 0, 0, 0.75);
background-color:#2672B6;
*background-color:#2672B6;
background-image:-ms-linear-gradient(top, #297AB8, #15528F);
background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#297AB8), to(#15528F));
background-image:-webkit-linear-gradient(top, #297AB8, #15528F);
background-image:-o-linear-gradient(top, #297AB8, #15528F);
background-image:linear-gradient(top, #297AB8, #15528F);
background-image:-moz-linear-gradient(top, #297AB8, #15528F);
background-color:#2672b6;
*background-color:#2672b6;
background-image:-ms-linear-gradient(top, #297ab8, #15528f);
background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#297ab8), to(#15528f));
background-image:-webkit-linear-gradient(top, #297ab8, #15528f);
background-image:-o-linear-gradient(top, #297ab8, #15528f);
background-image:linear-gradient(top, #297ab8, #15528f);
background-image:-moz-linear-gradient(top, #297ab8, #15528f);
border:1px solid #111;
border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
border-color:#111 #111 #111;
border-bottom-color:#111;
filter:progid:dximagetransform.microsoft.gradient(startColorstr='#297AB8', endColorstr='#15528F', GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(startcolorstr="#297ab8", endcolorstr="#15528f", gradienttype=0);
filter:progid:dximagetransform.microsoft.gradient(enabled=false);
-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.0), 0 1px 2px rgba(0, 0, 0, 0.05);
-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.0), 0 1px 2px rgba(0, 0, 0, 0.05);
@ -865,21 +946,21 @@ fieldset[disabled] .navbar-default .btn-link:focus{
.btn.active,
.btn.disabled,
.btn[disabled]{
background-color:#2672B6;
*background-color:#2672B6;
color:#fff
background-color:#2672b6;
*background-color:#2672b6;
color:#ddd
}
.btn:active,
.btn.active{
background-color:#ccc \9;
color:#fff
color:#ddd
}
.btn:hover{
color:#fff;
background-color:#2672B6;
*background-color:#2672B6;
color:#ddd;
background-color:#2672b6;
*background-color:#2672b6;
background-position:0 -150px;
-webkit-transition:background-position 0.0s linear;
-moz-transition:background-position 0.0s linear;
@ -892,15 +973,15 @@ fieldset[disabled] .navbar-default .btn-link:focus{
outline:thin dotted #333;
outline:5px auto -webkit-focus-ring-color;
outline-offset:-2px;
color:#fff
color:#ddd
}
.btn.active,
.btn:active{
background-color:#2672B6;
background-color:#2672B6 \9;
background-color:#2672b6;
background-color:#2672b6 \9;
background-image:none;
color:#fff;
color:#ddd;
outline:0;
-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
@ -910,7 +991,7 @@ fieldset[disabled] .navbar-default .btn-link:focus{
.btn.disabled,
.btn[disabled]{
cursor:default;
background:#15528F none;
background:#15528f none;
opacity:0.65;
filter:alpha(opacity=65);
-webkit-box-shadow:none;
@ -940,13 +1021,15 @@ fieldset[disabled] .navbar-default .btn-link:focus{
.btn-warning:hover,
.btn-danger,
.btn-danger:hover,
.ui-widget-content .btn-danger,
.ui-widget-content .btn-danger:hover,
.btn-success,
.btn-success:hover,
.btn-info,
.btn-info:hover,
.btn-inverse,
.btn-inverse:hover{
color:#fff;
color:#ddd;
text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25)
}
@ -971,7 +1054,7 @@ fieldset[disabled] .navbar-default .btn-link:focus{
background-repeat:repeat-x;
border-color:#0055cc #0055cc #003580;
border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter:progid:dximagetransform.microsoft.gradient(startColorstr='#0088cc', endColorstr='#0055cc', GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(startColorstr="#0088cc", endColorstr="#0055cc", GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(enabled=false)
}
@ -1001,7 +1084,7 @@ fieldset[disabled] .navbar-default .btn-link:focus{
background-repeat:repeat-x;
border-color:#f89406 #f89406 #ad6704;
border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(startColorstr="#fbb450", endColorstr="#f89406", GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(enabled=false)
}
@ -1031,7 +1114,7 @@ fieldset[disabled] .navbar-default .btn-link:focus{
background-repeat:repeat-x;
border-color:#bd362f #bd362f #802420;
border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#bd362f', GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(startColorstr="#ee5f5b", endColorstr="#bd362f", GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(enabled=false)
}
@ -1061,7 +1144,7 @@ fieldset[disabled] .navbar-default .btn-link:focus{
background-repeat:repeat-x;
border-color:#51a351 #51a351 #387038;
border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter:progid:dximagetransform.microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(startColorstr="#62c462", endColorstr="#51a351", GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(enabled=false)
}
@ -1091,7 +1174,7 @@ fieldset[disabled] .navbar-default .btn-link:focus{
background-repeat:repeat-x;
border-color:#2f96b4 #2f96b4 #1f6377;
border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter:progid:dximagetransform.microsoft.gradient(startColorstr='#5bc0de', endColorstr='#2f96b4', GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(startColorstr="#5bc0de", endColorstr="#2f96b4", GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(enabled=false)
}
@ -1121,7 +1204,7 @@ fieldset[disabled] .navbar-default .btn-link:focus{
background-repeat:repeat-x;
border-color:#222 #222 #000;
border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter:progid:dximagetransform.microsoft.gradient(startColorstr='#555555', endColorstr='#222222', GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(startColorstr="#555555", endColorstr="#222222", GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(enabled=false)
}
@ -1160,9 +1243,9 @@ fieldset[disabled] .navbar-default .btn-link:focus{
.navbar-default .navbar-nav > .active > a{
background-color:transparent;
-webkit-box-shadow:inset 0px -5px 0px 0px #8DBEEE;
-moz-box-shadow:inset 0px -5px 0px 0px #8DBEEE;
box-shadow:inset 0px -5px 0px 0px #8DBEEE
-webkit-box-shadow:inset 0px -5px 0px 0px #8dbeee;
-moz-box-shadow:inset 0px -5px 0px 0px #8dbeee;
box-shadow:inset 0px -5px 0px 0px #8dbeee
}
.navbar-fixed-top{
@ -1171,7 +1254,7 @@ fieldset[disabled] .navbar-default .btn-link:focus{
}
pre{
color:#fff;
color:#ddd;
background-color:#3d3d3d;
border-color:#111
}
@ -1193,7 +1276,7 @@ input sizing (for config pages)
#showfilter optgroup option,
#editAProvider optgroup option{
color:#222;
background-color:#fff
background-color:#ddd
}
/* =======================================================================
@ -1206,7 +1289,7 @@ browser.css
#fileBrowserDialog ul li a:hover{
color:#09a2ff;
background: rgb(61, 61, 61) none
background:rgb(61, 61, 61) none
}
.ui-menu .ui-menu-item{
@ -1214,11 +1297,11 @@ browser.css
}
.ui-menu .ui-menu-item-alternate{
background-color:#fff
background-color:#ddd
}
.ui-autocomplete .ui-menu-item .ui-state-focus{
color:#fff;
color:#ddd;
background:none;
background-color:#0a246a
}
@ -1229,11 +1312,11 @@ formWizard
.step,
legend.legendStep{
color:#fff
color:#ddd
}
div.stepsguide .step p{
border-color:#23AFDC
border-color:#23afdc
}
.disabledstep{
@ -1241,7 +1324,7 @@ div.stepsguide .step p{
}
#newShowPortal #addShowForm .stepsguide .disabledstep:hover > .smalltext{
color:#ccc;
color:#ccc
}
.stepDiv #searchResults div .exists-db{
@ -1249,16 +1332,16 @@ div.stepsguide .step p{
}
div.stepsguide .disabledstep p{
border-color:#1178B3
border-color:#1178b3
}
div.formpaginate .prev, div.formpaginate .next{
color:#fff;
background:#2265A1
color:#ddd;
background:#2265a1
}
#customQualityWrapper .tip-text p{
color:#888
color:#999
}
/* =======================================================================
@ -1276,11 +1359,11 @@ pnotify.css
}
.ui-pnotify-title{
color:#fff
color:#ddd
}
.ui-pnotify-text{
color:#fff
color:#ddd
}
/* =======================================================================
@ -1288,7 +1371,7 @@ tablesorter.css
========================================================================== */
.tablesorter{
color:#fff;
color:#ddd;
background-color:#333
}
@ -1303,25 +1386,26 @@ tablesorter.css
border-left:none
}
.tablesorter th{
color:#fff;
.tablesorter th,
.sickbeardTable thead .seasoncols td{
color:#ddd;
text-shadow:-1px -1px 0 rgba(0, 0, 0, 0.3);
background-color:#15528F
background-color:#15528f
}
.tablesorter .tablesorter-header{
background:#15528F url(data:image/gif;base64,R0lGODlhFQAJAIAAAP///////yH5BAEAAAEALAAAAAAVAAkAAAIXjI+AywnaYnhUMoqt3gZXPmVg94yJVQAAOw==) no-repeat center right;
/* background-image:url(../images/tablesorter/bg.gif) */
background:#15528f url("data:image/gif;base64,R0lGODlhFQAJAIAAAP///////yH5BAEAAAEALAAAAAAVAAkAAAIXjI+AywnaYnhUMoqt3gZXPmVg94yJVQAAOw==") no-repeat center right;
/* background-image:url("../images/tablesorter/bg.gif") */
}
.tablesorter thead .tablesorter-headerDesc{
background:#297AB8 url(data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjB+gC+jP2ptn0WskLQA7) no-repeat center right;
/* background-image:url(../images/tablesorter/asc.gif) */
background:#297ab8 url("data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjB+gC+jP2ptn0WskLQA7") no-repeat center right;
/* background-image:url("../images/tablesorter/asc.gif") */
}
.tablesorter thead .tablesorter-headerAsc{
background:#297AB8 url(data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjI8Bya2wnINUMopZAQA7) no-repeat center right;
/* background-image:url(../images/tablesorter/desc.gif) */
background:#297ab8 url("data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjI8Bya2wnINUMopZAQA7") no-repeat center right;
/* background-image:url("../images/tablesorter/desc.gif") */
}
thead.tablesorter-stickyHeader{
@ -1329,11 +1413,16 @@ thead.tablesorter-stickyHeader{
border-bottom:2px solid #222
}
.ui-state-default.row-odd{
background-color:#363636
}
/* Zebra Widget - row alternating colors */
.tablesorter tr.odd, .sickbeardTable tr.odd{
background-color:#333
}
.ui-state-default.row-even,
.tablesorter tr.even, .sickbeardTable tr.even{
background-color:#2e2e2e
}
@ -1346,7 +1435,7 @@ thead.tablesorter-stickyHeader{
}
.tablesorter tfoot tr{
color:#fff;
color:#ddd;
text-align:center;
text-shadow:-1px -1px 0 rgba(0, 0, 0, 0.3);
background-color:#333;
@ -1354,7 +1443,7 @@ thead.tablesorter-stickyHeader{
}
.tablesorter tfoot a{
color:#fff
color:#ddd
}
#showListTable tbody{
@ -1368,7 +1457,7 @@ token-input.css
ul.token-input-list{
border:1px solid #ccc;
background-color:#fff
background-color:#ddd
}
ul.token-input-list li input{
@ -1387,7 +1476,7 @@ li.token-input-token span{
li.token-input-selected-token{
background-color:#08844e;
color:#fff
color:#ddd
}
li.token-input-selected-token span{
@ -1395,7 +1484,7 @@ li.token-input-selected-token span{
}
div.token-input-dropdown{
background-color:#fff;
background-color:#ddd;
color:#000;
border-left-color:#ccc;
border-right-color:#ccc;
@ -1407,7 +1496,7 @@ div.token-input-dropdown p{
}
div.token-input-dropdown ul li{
background-color:#fff
background-color:#ddd
}
div.token-input-dropdown ul li.token-input-dropdown-item{
@ -1415,7 +1504,7 @@ div.token-input-dropdown ul li.token-input-dropdown-item{
}
div.token-input-dropdown ul li.token-input-dropdown-item2{
background-color:#fff
background-color:#ddd
}
div.token-input-dropdown ul li.token-input-selected-dropdown-item{
@ -1427,7 +1516,7 @@ jquery.confirm.css
========================================================================== */
#confirmOverlay{
background:url('../images/bg.gif');
background:url("../images/bg.gif");
background:-moz-linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)) repeat-x rgba(0, 0, 0, 0.5);
background:-webkit-gradient(linear, 0% 0%, 0% 100%, from(rgba(0, 0, 0, 0.5)), to(rgba(0, 0, 0, 0.5))) repeat-x rgba(0, 0, 0, 0.5);
z-index:100000
@ -1440,19 +1529,19 @@ jquery.confirm.css
}
#confirmBox h1{
background-color:#15528F;
background-color:#15528f;
border-bottom:1px solid #111;
color:#fff;
color:#ddd;
text-shadow:0 1px 1px rgba(0, 0, 0, 0.75)
}
#confirmBox p{
color:#fff;
color:#ddd;
text-shadow:0 1px 1px rgba(0, 0, 0, 0.75)
}
#confirmBox .button{
color:#fff;
color:#ddd;
text-shadow:0 1px 1px rgba(0, 0, 0, 0.75);
border:1px solid #111;
border-radius:3px;
@ -1465,17 +1554,17 @@ jquery.confirm.css
}
#confirmBox .green{
background-color:#3F7636
background-color:#3f7636
}
#confirmBox .green:hover{
background-color:#48873E
background-color:#48873e
}
#confirmBox .red{
background-color:#8D2D2B
background-color:#8d2d2b
}
#confirmBox .red:hover{
background-color:#A13331
background-color:#a13331
}

Binary file not shown.

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 191 KiB

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

Binary file not shown.

View file

@ -853,3 +853,21 @@ button.ui-button::-moz-focus-inner {
filter: Alpha(Opacity=35);
border-radius: 8px;
}
.ui-tooltip {
padding:3px 6px;
position:absolute;
z-index:9999;
max-width:300px;
-webkit-box-shadow:1px 1px 3px 1px rgba(0,0,0,.15);
-moz-box-shadow:1px 1px 3px 1px rgba(0,0,0,.15);
box-shadow:1px 1px 3px 1px rgba(0,0,0,.15)
}
body .ui-tooltip {
font-size: 10px;
border-radius: 5px 5px 5px 5px;
border: 1px solid rgb(241, 208, 49);
background-color: rgb(255, 255, 163);
color: rgb(85, 85, 85)
}

View file

@ -1,14 +1,32 @@
/* =======================================================================
inc_top.tmpl
========================================================================== */
.navbar-default .navbar-nav .logger.errors.n,
pre .prelight{
color:#6f8c53
}
.navbar-default .navbar-nav .logger.errors.nn,
pre .prelight2{
color:#b7b82c
}
.navbar-default .navbar-nav .logger.errors.nnn,
pre .prelight-num{
color:#eaab52
}
.navbar-default .navbar-nav .logger.errors.nnnn{
color:#ff6d5e
}
[class^="icon-"],
[class*=" icon-"]{
background-image:url("../images/glyphicons-halflings.png")
background-image:url("../images/glyphicons-halflings-white.png")
}
.icon-white{
background-image:url("../images/glyphicons-halflings-white.png")
background-image:url("../images/glyphicons-halflings.png")
}
.ui-autocomplete-loading{
@ -43,7 +61,7 @@ inc_top.tmpl
}
.ui-widget-content a:hover{
color:#09A2FF
color:#09a2ff
}
.ui-widget-header{
@ -54,7 +72,7 @@ inc_top.tmpl
.ui-widget-content .ui-state-default,
.ui-widget-header .ui-state-default{
background:#fff;
border:1px solid #CCC
border:1px solid #ccc
}
.ui-state-hover,
@ -69,7 +87,7 @@ inc_top.tmpl
.ui-state-active,
.ui-widget-content .ui-state-active,
.ui-widget-header .ui-state-active{
background:#F7F7F7
background:#f7f7f7
}
.ui-state-highlight,
@ -124,7 +142,7 @@ inc_top.tmpl
}
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited{
color:#140F06;
color:#140f06;
text-decoration:none
}
@ -149,8 +167,8 @@ inc_top.tmpl
}
.ui-tabs .ui-tabs-panel{
background-color:#F7F7F7 !important;
border:1px solid #CCC !important
background-color:#f7f7f7 !important;
border:1px solid #ccc !important
}
.ui-tabs .ui-tabs-nav li.ui-tabs-active{
@ -184,19 +202,30 @@ inc_bottom.tmpl
========================================================================== */
.footerhighlight{
color:#428BCA
color:#428bca
}
body#display-show.back-art .displayshow-wrapper a,
body#display-show.back-art .footerhighlight a,
body#display-show.back-art .footerhighlight{
color:#c7db40
color:#288536
/* color:#c7db40
*/
}
body#display-show.back-art .displayshow-wrapper a:hover,
body#display-show.back-art .displayshow-wrapper a:focus,
body#display-show.back-art .footerhighlight a:hover,
body#display-show.back-art .footerhighlight a:focus{
color:#7C8119
/* color:#c7db40
*/
}
body#display-show.back-art .displayshow-wrapper .label a{
color:#c7db40
}
body#display-show.back-art .displayshow-wrapper .label a:hover,
body#display-show.back-art .displayshow-wrapper .label a:focus{
color:#9faf33
}
@ -219,7 +248,7 @@ a.ui-font{
}
#show-list .show-card{
background-color:#DFDACF;
background-color:#dfdacf;
color:#666;
border:1px solid #111
}
@ -269,7 +298,7 @@ td.tvShow a{
td.tvShow a:hover span,
td.tvShow a:hover{
cursor:pointer;
color:#428BCA
color:#428bca
}
/* =======================================================================
@ -299,9 +328,8 @@ home_newShow.tmpl
}
#addRootDirTable td label .filepath,
.grey-text{
color:#666
}
.grey-text{color:#666}
.highlight-text{color:#000}
#newShowPortal #displayText .show-name,
#newShowPortal #displayText .show-dest,
@ -319,7 +347,7 @@ home_addExistingShow.tmpl
========================================================================== */
ul#rootDirStaticList li{
background:url('../css/lib/images/ui-bg_highlight-soft_75_efefef_1x100.png') repeat-x scroll 50% 50% #EFEFEF
background:url("../css/lib/images/ui-bg_highlight-soft_75_efefef_1x100.png") repeat-x scroll 50% 50% #efefef
}
/* =======================================================================
@ -327,7 +355,7 @@ home_browseShows.tmpl
========================================================================== */
#browse-list .show-card{
background-color:#DFDACF;
background-color:#dfdacf;
border:1px solid #111
}
@ -340,6 +368,21 @@ home_postprocess.tmpl
========================================================================== */
/* =======================================================================
displayShow.tmpl and episodeView.tmpl
========================================================================== */
#episode-view.back-art .controlsBlock,
#episode-view.back-art .h2footer,
#episode-view.back-art.daybyday h1.header,
#episode-view.back-art.pro .daybyday-show,
#display-show.back-art #change-show,
#display-show.back-art #change-status,
#display-show.back-art #no-episode-data,
body.back-art .footer,
#display-show.back-art .sickbeardTable{
background-color: rgba(239, 239, 239, 0.8)
}
/* =======================================================================
displayShow.tmpl
========================================================================== */
@ -358,23 +401,41 @@ tr.seasonheader{
filter:url("data:image/svg+xml;utf8,<svg version='1.1' xmlns='http://www.w3.org/2000/svg' height='0'><filter id='greyscale'><feColorMatrix type='matrix' values='0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0' /></filter></svg>#greyscale")
}
.tvshowImg{
border:1px solid #ccc
#posterCol .tvshowImg{
border: 1px solid rgba(239, 239, 239, 0.8);
}
.display-details{
background-color:#efefef;
border:1px solid #dfdede
#edit-show.back-art .ui-tabs-nav > :not(.ui-tabs-active){
background:rgba(239, 239, 239,0.4)
}
.pro .details-title{
#edit-show.back-art .ui-tabs-nav .ui-tabs-active,
#edit-show.back-art .ui-tabs .ui-tabs-panel{
background:rgba(239, 239, 239,0.8) !important
}
#edit-show.back-art .header,
#display-show.back-art #showCol{
background:rgba(239, 239, 239, 0.8);
border:1px solid rgba(239, 239, 239, 0.8)
}
#livepanel .over-layer0,
#showCol{
background-color:#efefef
}
#livepanel .over-layer0,
#livepanel .over-layer1,
#showCol{
border-color:#dfdede
}
.pro .details-title,
.pro .hint{
color:#666
}
.back-art.pro .details-title{
color:#bbb
}
.back-art #details-top .label,
.back-art #details-bottom .label{
background-color:#215f2f
@ -386,19 +447,24 @@ tr.seasonheader{
}
.paused-highlight{
color:#C7DB40
color:#c7db40
}
.sickbeardTable th{
background-color:#333
}
body#display-show.back-art,
.displayshow-wrapper .sickbeardTable th.row-seasonheader{
color:#000
}
.translucent.pro .sickbeardTable th{
background-color:rgba(51, 51, 51, 0.5)
.back-art.pro.ii .navbar-default,
.back-art.pro.ii .day-of-week .day-number,
.translucent.pro.ii .day-of-week .day-number,
.back-art.pro.ii .sickbeardTable .seasoncols th,
.translucent.pro.ii .sickbeardTable .seasoncols th{
background-color:rgba(51, 51, 51, 0.7)
}
/* =======================================================================
@ -406,7 +472,7 @@ episodeView.tmpl
========================================================================== */
h2.day, h2.network{
color:#FFF;
color:#fff;
text-shadow:-1px -1px 0 rgba(0, 0, 0, 0.3);
background-color:#333
}
@ -417,7 +483,7 @@ h2.day, h2.network{
}
.tvshowDiv a:hover{
color:#428BCA
color:#428bca
}
.tvshowTitle a{
@ -451,15 +517,15 @@ h2.day, h2.network{
}
.odd .daybyday-show{
background-color:#F5F1E4
background-color:#f5f1e4
}
.even .daybyday-show{
background-color:#DFDACF
background-color:#dfdacf
}
.day-of-week .poster img{
border-color:#CCC
border-color:#ccc
}
.over-layer0{
@ -484,13 +550,25 @@ h2.day, h2.network{
.day-of-week .text .episode .season,
.day-of-week .text .episode .number{
color:rgb(9, 133, 225)
color:#0985e1
}
.day-of-week .text .episode{
color:#888
}
.daybyday-show .state{
height:5px
}
.daybyday-show .listing-current{
border:1px solid #1d5068
}
.daybyday-show .listing-overdue{
border:1px solid #890000
}
.episodeview-daybyday .time .time-hr-min,
.episodeview-daybyday .time .time-am-pm{
color:#666
@ -502,8 +580,8 @@ h2.day, h2.network{
}
.carousel-indicators .active{
background:#C7DB40;
border-color:#C7DB40
background:#c7db40;
border-color:#c7db40
}
/* =======================================================================
@ -532,7 +610,7 @@ config*.tmpl
}
.testNotification{
border:1px dotted #CCC
border:1px dotted #ccc
}
.infoTableSeperator{
@ -553,6 +631,16 @@ config_postProcessing.tmpl
border-color:rgb(204, 204, 204)
}
.back-art #config .episode-sample{
background:rgba(255,255,255,0.8);
border-color:rgba(204,204,204,0.8)
}
.back-art #config .example{
background:rgba(239,239,239,0.8);
border-color:rgba(204,204,204,0.8);
line-height: 24px
}
.Key{
background-color:#f4f4f4;
border:1px solid #ccc
@ -602,7 +690,7 @@ div.metadataDiv .disabled{
}
.warning{
border-color:#F89406;
border-color:#f89406;
background:url("../images/warning16.png") no-repeat right 5px center #fff
}
@ -665,7 +753,7 @@ input, textarea, select, .uneditable-input{
/* navbar styling */
.navbar-default{
background-color:#333;
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#555555', endColorstr='#333333');
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#555555", endColorstr="#333333");
background-image:-ms-linear-gradient(top, #555555 0%, #333333 100%);
background-image:-moz-linear-gradient(top, #555555 0%, #333333 100%);
background-image:-o-linear-gradient(top, #555555 0%, #333333 100%);
@ -792,7 +880,7 @@ fieldset[disabled] .navbar-default .btn-link:focus{
}
.dropdown-menu{
background-color:#F5F1E4;
background-color:#f5f1e4;
border:1px solid rgba(0, 0, 0, 0.15);
box-shadow:0 6px 12px rgba(0, 0, 0, 0.176)
}
@ -816,7 +904,7 @@ fieldset[disabled] .navbar-default .btn-link:focus{
border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
border-color:#e6e6e6 #e6e6e6 #bfbfbf;
border-bottom-color:#b3b3b3;
filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(startColorstr="#ffffff", endColorstr="#e6e6e6", GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(enabled=false);
-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
@ -899,6 +987,8 @@ fieldset[disabled] .navbar-default .btn-link:focus{
.btn-warning:hover,
.btn-danger,
.btn-danger:hover,
.ui-widget-content .btn-danger,
.ui-widget-content .btn-danger:hover,
.btn-success,
.btn-success:hover,
.btn-info,
@ -930,7 +1020,7 @@ fieldset[disabled] .navbar-default .btn-link:focus{
background-repeat:repeat-x;
border-color:#0055cc #0055cc #003580;
border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter:progid:dximagetransform.microsoft.gradient(startColorstr='#0088cc', endColorstr='#0055cc', GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(startColorstr="#0088cc", endColorstr="#0055cc", GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(enabled=false)
}
@ -960,7 +1050,7 @@ fieldset[disabled] .navbar-default .btn-link:focus{
background-repeat:repeat-x;
border-color:#f89406 #f89406 #ad6704;
border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(startColorstr="#fbb450", endColorstr="#f89406", GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(enabled=false)
}
@ -990,7 +1080,7 @@ fieldset[disabled] .navbar-default .btn-link:focus{
background-repeat:repeat-x;
border-color:#bd362f #bd362f #802420;
border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#bd362f', GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(startColorstr="#ee5f5b", endColorstr="#bd362f", GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(enabled=false)
}
@ -1020,7 +1110,7 @@ fieldset[disabled] .navbar-default .btn-link:focus{
background-repeat:repeat-x;
border-color:#51a351 #51a351 #387038;
border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter:progid:dximagetransform.microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(startColorstr="#62c462", endColorstr="#51a351", GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(enabled=false)
}
@ -1050,7 +1140,7 @@ fieldset[disabled] .navbar-default .btn-link:focus{
background-repeat:repeat-x;
border-color:#2f96b4 #2f96b4 #1f6377;
border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter:progid:dximagetransform.microsoft.gradient(startColorstr='#5bc0de', endColorstr='#2f96b4', GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(startColorstr="#5bc0de", endColorstr="#2f96b4", GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(enabled=false)
}
@ -1080,7 +1170,7 @@ fieldset[disabled] .navbar-default .btn-link:focus{
background-repeat:repeat-x;
border-color:#222 #222 #000;
border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter:progid:dximagetransform.microsoft.gradient(startColorstr='#555555', endColorstr='#222222', GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(startColorstr="#555555", endColorstr="#222222", GradientType=0);
filter:progid:dximagetransform.microsoft.gradient(enabled=false)
}
@ -1112,16 +1202,16 @@ fieldset[disabled] .navbar-default .btn-link:focus{
right:12px;
display:inline-block;
border-right:6px solid transparent;
border-bottom:6px solid #F5F1E4;
border-bottom:6px solid #f5f1e4;
border-left:6px solid transparent;
content:""
}
.navbar-default .navbar-nav > .active > a{
background-color:transparent;
-webkit-box-shadow:inset 0px -5px 0px 0px #C7DB40;
-moz-box-shadow:inset 0px -5px 0px 0px #C7DB40;
box-shadow:inset 0px -5px 0px 0px #C7DB40
-webkit-box-shadow:inset 0px -5px 0px 0px #c7db40;
-moz-box-shadow:inset 0px -5px 0px 0px #c7db40;
box-shadow:inset 0px -5px 0px 0px #c7db40
}
.navbar-fixed-top{
@ -1131,7 +1221,7 @@ fieldset[disabled] .navbar-default .btn-link:focus{
pre{
color:#000;
background-color:#F5F5F5;
background-color:#f5f5f5;
border-color:#ccc
}
@ -1161,7 +1251,7 @@ browser.css
#fileBrowserDialog ul li a:hover{
color:#00f;
background: rgb(220, 220, 220) none
background:rgb(220, 220, 220) none
}
.ui-menu .ui-menu-item{
@ -1200,7 +1290,7 @@ div.stepsguide .disabledstep p{
}
#newShowPortal #addShowForm .stepsguide .disabledstep:hover > .smalltext{
color:#8a775e;
color:#8a775e
}
.stepDiv #searchResults div .exists-db{
@ -1243,18 +1333,18 @@ tablesorter.css
}
.tablesorter .tablesorter-header{
background:#333 url(data:image/gif;base64,R0lGODlhFQAJAIAAAP///////yH5BAEAAAEALAAAAAAVAAkAAAIXjI+AywnaYnhUMoqt3gZXPmVg94yJVQAAOw==) no-repeat center right;
/* background-image:url(../images/tablesorter/bg.gif) */
background:#333 url("data:image/gif;base64,R0lGODlhFQAJAIAAAP///////yH5BAEAAAEALAAAAAAVAAkAAAIXjI+AywnaYnhUMoqt3gZXPmVg94yJVQAAOw==") no-repeat center right;
/* background-image:url("../images/tablesorter/bg.gif" */
}
.tablesorter thead .tablesorter-headerDesc{
background:#555 url(data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjB+gC+jP2ptn0WskLQA7) no-repeat center right;
/* background-image:url(../images/tablesorter/asc.gif) */
background:#555 url("data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjB+gC+jP2ptn0WskLQA7") no-repeat center right;
/* background-image:url("../images/tablesorter/asc.gif" */
}
.tablesorter thead .tablesorter-headerAsc{
background:#555 url(data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjI8Bya2wnINUMopZAQA7) no-repeat center right;
/* background-image:url(../images/tablesorter/desc.gif) */
background:#555 url("data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjI8Bya2wnINUMopZAQA7") no-repeat center right;
/* background-image:url("../images/tablesorter/desc.gif" */
}
thead.tablesorter-stickyHeader{
@ -1263,10 +1353,12 @@ thead.tablesorter-stickyHeader{
}
/* Zebra Widget - row alternating colors */
.ui-state-default.row-odd,
.tablesorter tr.odd, .sickbeardTable tr.odd{
background-color:#f5f1e4
}
.ui-state-default.row-even,
.tablesorter tr.even, .sickbeardTable tr.even{
background-color:#dfdacf
}
@ -1355,14 +1447,14 @@ jquery.confirm.css
========================================================================== */
#confirmOverlay{
background:url('../images/bg.gif');
background:url("../images/bg.gif");
background:-moz-linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)) repeat-x rgba(0, 0, 0, 0.5);
background:-webkit-gradient(linear, 0% 0%, 0% 100%, from(rgba(0, 0, 0, 0.5)), to(rgba(0, 0, 0, 0.5))) repeat-x rgba(0, 0, 0, 0.5);
z-index:100000
}
#confirmBox{
background:#F5F1E4;
background:#f5f1e4;
border:1px solid #111;
box-shadow:0 0 12px 0 rgba(0, 0, 0, 0.175)
}
@ -1392,17 +1484,17 @@ jquery.confirm.css
}
#confirmBox .green{
background-color:#3F7636
background-color:#3f7636
}
#confirmBox .green:hover{
background-color:#48873E
background-color:#48873e
}
#confirmBox .red{
background-color:#8D2D2B
background-color:#8d2d2b
}
#confirmBox .red:hover{
background-color:#A13331
background-color:#a13331
}

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 691 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 477 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 436 B

After

Width:  |  Height:  |  Size: 531 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 263 B

After

Width:  |  Height:  |  Size: 693 B

View file

@ -2,12 +2,14 @@
#from sickbeard import db
#from sickbeard.helpers import anon_url
#import os.path
<% def sg_var(varname, default=False): return getattr(sickbeard, varname, default) %>#slurp#
<% def sg_str(varname, default=''): return getattr(sickbeard, varname, default) %>#slurp#
##
#set global $title = 'Configuration'
#set global $header = 'Configuration'
#set global $sbPath = '..'
#set global $topmenu = 'config'
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_top.tmpl')
##
#if $varExists('header')
<h1 class='header'>$header</h1>
@ -20,23 +22,25 @@
<tr>
<td class="infoTableHeader">Version: </td>
<td class="infoTableCell">
BRANCH: #echo $sickbeard.BRANCH or 'UNKNOWN'# / COMMIT: #echo $sickbeard.CUR_COMMIT_HASH or 'UNKNOWN'#<br />
BRANCH: #echo $sg_str('BRANCH') or 'UNKNOWN'# / COMMIT: #echo $sg_str('CUR_COMMIT_HASH') or 'UNKNOWN'#<br />
<em class="red-text">This is BETA software</em><br />
#if not $sickbeard.VERSION_NOTIFY:
#if not $sg_var('VERSION_NOTIFY'):
You don't have version checking turned on, see "Check software updates" in Config > General.
#end if
</td>
</tr>
<tr><td class="infoTableHeader">Config file:</td><td class="infoTableCell">$sickbeard.CONFIG_FILE</td></tr>
<tr><td class="infoTableHeader">Config file:</td><td class="infoTableCell">$sg_str('CONFIG_FILE')</td></tr>
<tr><td class="infoTableHeader">Database file:</td><td class="infoTableCell">$db.dbFilename()</td></tr>
<tr><td class="infoTableHeader">Cache Dir:</td><td class="infoTableCell">$sickbeard.CACHE_DIR</td></tr>
<tr><td class="infoTableHeader">Arguments:</td><td class="infoTableCell"><%= (sickbeard.MY_ARGS, 'None used')[0 == len(sickbeard.MY_ARGS)] %></td></tr>
<tr><td class="infoTableHeader">Web Root:</td><td class="infoTableCell">$sickbeard.WEB_ROOT</td></tr>
<tr><td class="infoTableHeader">Cache Dir:</td><td class="infoTableCell">$sg_str('CACHE_DIR')</td></tr>
<tr><td class="infoTableHeader">Arguments:</td><td class="infoTableCell">#echo $sg_var('MY_ARGS') or 'None used'#</td></tr>
<tr><td class="infoTableHeader">Web Root:</td><td class="infoTableCell">$sg_str('WEB_ROOT')</td></tr>
<tr><td class="infoTableHeader">Python Version:</td><td class="infoTableCell">$sys.version[:120]</td></tr>
<tr class="infoTableSeperator"><td class="infoTableHeader"><i class="icon16-sg"></i> Homepage</td><td class="infoTableCell"><a href="<%= anon_url('https://github.com/SickGear/SickGear/wiki') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">https://github.com/SickGear/SickGear/wiki</a></td></tr>
<tr><td class="infoTableHeader"><i class="icon16-github"></i> Source</td><td class="infoTableCell"><a href="<%= anon_url('https://github.com/SickGear/SickGear/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">https://github.com/SickGear/SickGear/</a></td></tr>
<tr><td class="infoTableHeader"><i class="icon16-mirc"></i> Internet Relay Chat</td><td class="infoTableCell"><a href="irc://irc.freenode.net/#SickGear" rel="noreferrer"><i>#SickGear</i> on <i>irc.freenode.net</i></a></td></tr>
<tr class="infoTableSeperator"><td class="infoTableHeader">Powered by</td><td class="infoTableCell">Python, HTML5, jQuery, SQLite, TheTVDB, Trakt.tv, Fanart.tv, TMDb, GitHub</td></tr>
<tr><td class="infoTableHeader">&nbsp;</td><td class="infoTableHeader">This project uses the TMDb API but is not endorsed or certified by TMDb.</td></tr>
</table>
</div>
#include $os.path.join($sickbeard.PROG_DIR,'gui/slick/interfaces/default/inc_bottom.tmpl')
#include $os.path.join($sg_str('PROG_DIR'),'gui/slick/interfaces/default/inc_bottom.tmpl')

View file

@ -10,6 +10,8 @@
#from sickbeard.helpers import anon_url
#from sickbeard.logger import reverseNames as file_logging_presets
#from sickbeard.helpers import maybe_plural
<% def sg_var(varname, default=False): return getattr(sickbeard, varname, default) %>#slurp#
<% def sg_str(varname, default=''): return getattr(sickbeard, varname, default) %>#slurp#
##
#set global $title = 'Config - General'
#set global $header = 'General Configuration'
@ -17,7 +19,7 @@
#set global $topmenu = 'config'
##
#import os.path
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_top.tmpl')
#if $varExists('header')
<h1 class="header">$header</h1>
@ -29,8 +31,8 @@
#set $selected = ' selected="selected"'
##
#set $indexer = 0
#if $sickbeard.INDEXER_DEFAULT
#set $indexer = $sickbeard.INDEXER_DEFAULT
#if $sg_var('INDEXER_DEFAULT')
#set $indexer = $sg_var('INDEXER_DEFAULT')
#end if
<script type="text/javascript" src="$sbRoot/js/config.js?v=$sbPID"></script>
@ -62,7 +64,7 @@
<label for="launch_browser">
<span class="component-title">Launch browser</span>
<span class="component-desc">
<input type="checkbox" name="launch_browser" id="launch_browser"#echo ('', $checked)[$sickbeard.LAUNCH_BROWSER]#>
<input type="checkbox" name="launch_browser" id="launch_browser"#echo ('', $checked)[$sg_var('LAUNCH_BROWSER')]#>
<p>open the SickGear home page on startup</p>
</span>
</label>
@ -72,8 +74,8 @@
<label for="update_shows_on_start">
<span class="component-title">Update shows on startup</span>
<span class="component-desc">
<input type="checkbox" name="update_shows_on_start" id="update_shows_on_start"#echo ('', $checked)[$sickbeard.UPDATE_SHOWS_ON_START]#>
<p>with show data; episode plot, images, air and end dates, etc. Disable for a quicker startup. Show data is scheduled to update during hour <span class="show_update_hour_value">$sickbeard.SHOW_UPDATE_HOUR</span></p>
<input type="checkbox" name="update_shows_on_start" id="update_shows_on_start"#echo ('', $checked)[$sg_var('UPDATE_SHOWS_ON_START')]#>
<p>with show data; episode plot, images, air and end dates, etc. Disable for a quicker startup. Show data is scheduled to update during hour <span class="show_update_hour_value">$sg_var('SHOW_UPDATE_HOUR', 3)</span></p>
</span>
</label>
</div>
@ -82,7 +84,7 @@
<label for="show_update_hour">
<span class="component-title">Update shows during hour</span>
<span class="component-desc">
<input type="number" name="show_update_hour" id="show_update_hour" value="$sickbeard.SHOW_UPDATE_HOUR" class="form-control input-sm input75">
<input type="number" name="show_update_hour" id="show_update_hour" value="$sg_var('SHOW_UPDATE_HOUR', 3)" class="form-control input-sm input75">
<p>(0 ... 23) with show data; episode plot, images, air and end dates, etc.</p>
</span>
</label>
@ -92,11 +94,11 @@
<span class="component-title">Send to trash for actions</span>
<span class="component-desc">
<label for="trash_remove_show" class="nextline-block">
<input type="checkbox" name="trash_remove_show" id="trash_remove_show"#echo ('', $checked)[$sickbeard.TRASH_REMOVE_SHOW]#>
<input type="checkbox" name="trash_remove_show" id="trash_remove_show"#echo ('', $checked)[$sg_var('TRASH_REMOVE_SHOW')]#>
<p>when using show "Remove" and delete files</p>
</label>
<label for="trash_rotate_logs" class="nextline-block">
<input type="checkbox" name="trash_rotate_logs" id="trash_rotate_logs"#echo ('', $checked)[$sickbeard.TRASH_ROTATE_LOGS]#>
<input type="checkbox" name="trash_rotate_logs" id="trash_rotate_logs"#echo ('', $checked)[$sg_var('TRASH_ROTATE_LOGS')]#>
<p>on scheduled deletes of the oldest log files</p>
</label>
<div class="clear-left"><p>selected actions use trash (recycle bin) instead of the default permanent delete</p></div>
@ -107,7 +109,7 @@
<label for="log_dir">
<span class="component-title">Log file folder location</span>
<span class="component-desc">
<input type="text" name="log_dir" id="log_dir" value="$sickbeard.ACTUAL_LOG_DIR" class="form-control input-sm input350">
<input type="text" name="log_dir" id="log_dir" value="$sg_str('ACTUAL_LOG_DIR')" class="form-control input-sm input350">
</span>
</label>
</div>
@ -119,7 +121,7 @@
<select id="indexer_default" name="indexer_default" class="form-control input-sm">
<option value="0"#echo ('', $selected)[0 == $indexer]#>All Indexers</option>
#for $indexer in $indexers
<option value="$indexer"#echo ('', $selected)[$indexer == $sickbeard.INDEXER_DEFAULT]#>$sickbeard.indexerApi().indexers[$indexer]</option>
<option value="$indexer"#echo ('', $selected)[$indexer == $sg_var('INDEXER_DEFAULT', 0)]#>$sickbeard.indexerApi().indexers[$indexer]</option>
#end for
</select>
<span>as the default selection when adding new shows</span>
@ -131,7 +133,7 @@
<label for="indexer_timeout">
<span class="component-title">Timeout show indexer at</span>
<span class="component-desc">
<input type="text" name="indexer_timeout" id="indexer_timeout" value="$sickbeard.INDEXER_TIMEOUT" class="form-control input-sm input75">
<input type="text" name="indexer_timeout" id="indexer_timeout" value="$sg_var('INDEXER_TIMEOUT', 20)" class="form-control input-sm input75">
<p>seconds of inactivity when finding new shows (default:10)</p>
</span>
</label>
@ -142,7 +144,7 @@
<span class="component-title">Show root directories</span>
<span class="component-desc">
<p>where the files of shows are located</p>
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_rootDirs.tmpl')
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_rootDirs.tmpl')
</span>
</label>
</div>
@ -162,7 +164,7 @@
<label for="version_notify">
<span class="component-title">Check software updates</span>
<span class="component-desc">
<input type="checkbox" name="version_notify" id="version_notify"#echo ('', $checked)[$sickbeard.VERSION_NOTIFY]#>
<input type="checkbox" name="version_notify" id="version_notify"#echo ('', $checked)[$sg_var('VERSION_NOTIFY', True)]#>
<p>and display notifications when updates are available.
Checks are run on startup and at the frequency set below*</p>
</span>
@ -173,7 +175,7 @@
<label for="auto_update">
<span class="component-title">Automatically update</span>
<span class="component-desc">
<input type="checkbox" name="auto_update" id="auto_update"#echo ('', $checked)[$sickbeard.AUTO_UPDATE]#>
<input type="checkbox" name="auto_update" id="auto_update"#echo ('', $checked)[$sg_var('AUTO_UPDATE')]#>
<p>fetch and install software updates.
Updates are run on startup and in the background at the frequency set below<sup>1</sup></p>
</span>
@ -184,7 +186,7 @@
<label>
<span class="component-title">Check the server every<sup>1</sup></span>
<span class="component-desc">
<input type="text" name="update_frequency" id="update_frequency" value="$sickbeard.UPDATE_FREQUENCY" class="form-control input-sm input75">
<input type="text" name="update_frequency" id="update_frequency" value="$sg_var('UPDATE_FREQUENCY', 12)" class="form-control input-sm input75">
<p>hours for software updates (default:12)</p>
</span>
</label>
@ -194,7 +196,7 @@
<label for="notify_on_update">
<span class="component-title">Notify on software update</span>
<span class="component-desc">
<input type="checkbox" name="notify_on_update" id="notify_on_update"#echo ('', $checked)[$sickbeard.NOTIFY_ON_UPDATE]#>
<input type="checkbox" name="notify_on_update" id="notify_on_update"#echo ('', $checked)[$sg_var('NOTIFY_ON_UPDATE')]#>
<p>send a message to all enabled notifiers when SickGear has been updated</p>
</span>
</label>
@ -222,8 +224,8 @@
<span class="component-title">Display theme:</span>
<span class="component-desc">
<select id="theme_name" name="theme_name" class="form-control input-sm">
<option value="dark"#echo ('', $selected)['dark' == $sickbeard.THEME_NAME]#>Dark</option>
<option value="light"#echo ('', $selected)['light' == $sickbeard.THEME_NAME]#>Light</option>
<option value="dark"#echo ('', $selected)['dark' == $sg_str('THEME_NAME', 'dark')]#>Dark</option>
<option value="light"#echo ('', $selected)['light' == $sg_str('THEME_NAME')]#>Light</option>
</select>
<span class="red-text">for appearance to take effect, save then refresh your browser</span>
</span>
@ -235,32 +237,43 @@
<span class="component-title">Use as default home page:</span>
<span class="component-desc">
<select id="default_home" name="default_home" class="form-control input-sm">
<option value="shows"#echo ('', $selected)['shows' == $sickbeard.DEFAULT_HOME]#>Shows</option>
<option value="episodes"#echo ('', $selected)['episodes' == $sickbeard.DEFAULT_HOME]#>Episodes</option>
<option value="history"#echo ('', $selected)['history' == $sickbeard.DEFAULT_HOME]#>History</option>
<option value="shows"#echo ('', $selected)['shows' == $sg_str('DEFAULT_HOME')]#>Shows</option>
<option value="episodes"#echo ('', $selected)['episodes' == $sg_str('DEFAULT_HOME')]#>Episodes</option>
<option value="history"#echo ('', $selected)['history' == $sg_str('DEFAULT_HOME')]#>History</option>
</select>
</span>
</label>
</div>
#if not hasattr($sickbeard, 'FANART_LIMIT')#<span class="red-text">Restart SickGear to reveal new options here</span>#else#
<div class="field-pair">
<label for="fanart_limit">
<span class="component-title">Maximum fanart image files</span>
<span class="component-desc">
<input type="text" name="fanart_limit" id="fanart_limit" value="$sg_var('FANART_LIMIT', 3)" class="form-control input-sm input75">
<span>per show to cache (0 ... 500)</span>
</span>
</label>
</div>
#end if
<div class="field-pair">
<label for="showlist_tagview">
<span class="component-title">Group show list shows into:</span>
<span class="component-desc">
<select id="showlist_tagview" name="showlist_tagview" class="form-control input-sm">
<option value="standard"#echo ('', $selected)['standard' == $sickbeard.SHOWLIST_TAGVIEW]#>One Show List</option>
<option value="anime"#echo ('', $selected)['anime' == $sickbeard.SHOWLIST_TAGVIEW]#>Show / Anime Lists</option>
<option value="custom"#echo ('', $selected)['custom' == $sickbeard.SHOWLIST_TAGVIEW]#>Custom Lists</option>
<option value="standard"#echo ('', $selected)['standard' == $sg_str('SHOWLIST_TAGVIEW', 'standard')]#>One Show List</option>
<option value="anime"#echo ('', $selected)['anime' == $sg_str('SHOWLIST_TAGVIEW')]#>Show / Anime Lists</option>
<option value="custom"#echo ('', $selected)['custom' == $sg_str('SHOWLIST_TAGVIEW')]#>Custom Lists</option>
</select>
#set $hidden = ' class="hidden"'
<span id="showlist_tagview_standard"#echo ('', $hidden)['standard' not in $sickbeard.SHOWLIST_TAGVIEW]#>that contains all shows (default)</span>
<span id="showlist_tagview_anime"#echo ('', $hidden)['anime' not in $sickbeard.SHOWLIST_TAGVIEW]#>two groups, the show list and anime</span>
<span id="showlist_tagview_custom"#echo ('', $hidden)['custom' not in $sickbeard.SHOWLIST_TAGVIEW]#>multiple custom<sup>1</sup> named groups and a "Show List"</span>
<span id="showlist_tagview_standard"#echo ('', $hidden)['standard' not in $sg_str('SHOWLIST_TAGVIEW', 'standard')]#>that contains all shows (default)</span>
<span id="showlist_tagview_anime"#echo ('', $hidden)['anime' not in $sg_str('SHOWLIST_TAGVIEW')]#>two groups, the show list and anime</span>
<span id="showlist_tagview_custom"#echo ('', $hidden)['custom' not in $sg_str('SHOWLIST_TAGVIEW')]#>multiple custom<sup>1</sup> named groups and a "Show List"</span>
</span>
</label>
</div>
<div id="showlist_tagview_custom_config" class="field-pair#if $sickbeard.SHOWLIST_TAGVIEW != 'custom' then ' hidden' else ''#">
<div id="showlist_tagview_custom_config" class="field-pair#if $sg_str('SHOWLIST_TAGVIEW') != 'custom' then ' hidden' else ''#">
<label for="show_tags">
<span class="component-title">Group names for show list<sup>1</sup></span>
<span class="component-desc">
@ -275,7 +288,7 @@
<label for="home_search_focus">
<span class="component-title">Give show list search focus</span>
<span class="component-desc">
<input type="checkbox" name="home_search_focus" id="home_search_focus"#echo ('', $checked)[$sickbeard.HOME_SEARCH_FOCUS]#>
<input type="checkbox" name="home_search_focus" id="home_search_focus"#echo ('', $checked)[$sg_var('HOME_SEARCH_FOCUS', True)]#>
<p>page refresh on "Show List" will start search box focused</p>
</span>
</label>
@ -285,7 +298,7 @@
<label for="use_imdb_info">
<span class="component-title">Enable IMDb info</span>
<span class="component-desc">
<input type="checkbox" name="use_imdb_info" id="use_imdb_info"#echo ('', $checked)[$sickbeard.USE_IMDB_INFO]#>
<input type="checkbox" name="use_imdb_info" id="use_imdb_info"#echo ('', $checked)[$sg_var('USE_IMDB_INFO', True)]#>
<p>for ui links, display show; ratings, country flag, year, runtime, and genre tags</p>
</span>
</label>
@ -298,7 +311,7 @@
<span class="component-desc">
<select id="imdb-accounts" class="pull-left form-control input-sm">
<option value="new" selected="selected">Add watchlist ...</option>
#for $i, $v in $enumerate($sickbeard.IMDB_ACCOUNTS)
#for $i, $v in $enumerate($sg_str('IMDB_ACCOUNTS'))
#if not $i % 2
#set $id = $v
#else
@ -326,7 +339,7 @@
<label for="sort_article">
<span class="component-title">Sort with "The", "A", "An"</span>
<span class="component-desc">
<input type="checkbox" name="sort_article" id="sort_article"#echo ('', $checked)[$sickbeard.SORT_ARTICLE]#>
<input type="checkbox" name="sort_article" id="sort_article"#echo ('', $checked)[$sg_var('SORT_ARTICLE')]#>
<p>include articles ("The", "A", "An") when sorting show lists</p>
</span>
</label>
@ -340,16 +353,16 @@
<label for="fuzzy_dating">
<span class="component-title">Display fuzzy dates</span>
<span class="component-desc">
<input type="checkbox" name="fuzzy_dating" id="fuzzy_dating" class="viewIf datePresets"#echo ('', $checked)[$sickbeard.FUZZY_DATING == True]#>
<input type="checkbox" name="fuzzy_dating" id="fuzzy_dating" class="viewIf datePresets"#echo ('', $checked)[$sg_var('FUZZY_DATING') == True]#>
<p>move absolute dates into tooltips and display e.g. "Last Thu", "On Tue"</p>
</span>
</label>
</div>
<div class="field-pair show_if_fuzzy_dating#echo (' metadataDiv', '')[$sickbeard.FUZZY_DATING]#">
<div class="field-pair show_if_fuzzy_dating#echo (' metadataDiv', '')[$sg_var('FUZZY_DATING')]#">
<label for="trim_zero">
<span class="component-title">Trim date and time</span>
<span class="component-desc">
<input type="checkbox" name="trim_zero" id="trim_zero"#echo ('', $checked)[True == $sickbeard.TRIM_ZERO]#>
<input type="checkbox" name="trim_zero" id="trim_zero"#echo ('', $checked)[True == $sg_var('TRIM_ZERO')]#>
<p>display minimalist date and time i.e. <del>02:00</del> = 2:00, <del>02:00pm</del> = 2pm, <del>03 Jan</del> = 3 Jan</p>
</span>
</label>
@ -359,16 +372,16 @@
<label for="date_presets">
<span class="component-title">Date style:</span>
<span class="component-desc">
<select class="form-control input-sm#echo (' metadataDiv', '')[$sickbeard.FUZZY_DATING]#" id="date_presets#echo ('_na', '')[$sickbeard.FUZZY_DATING]#" name="date_preset#echo ('_na', '')[$sickbeard.FUZZY_DATING]#">
<select class="form-control input-sm#echo (' metadataDiv', '')[$sg_var('FUZZY_DATING')]#" id="date_presets#echo ('_na', '')[$sg_var('FUZZY_DATING')]#" name="date_preset#echo ('_na', '')[$sg_var('FUZZY_DATING')]#">
#for $cur_preset in $date_presets
<option value="$cur_preset" #echo ('', $selected)[$cur_preset == $sickbeard.DATE_PRESET or ('%x' == $sickbeard.DATE_PRESET and '$cur_preset' == '%a, %b %d, %Y')]#>$datetime.datetime($datetime.datetime.now().year, 12, 31, 14, 30, 47).strftime($cur_preset)</option>
<option value="$cur_preset" #echo ('', $selected)[$cur_preset == $sg_str('DATE_PRESET', '%x') or ('%x' == $sg_str('DATE_PRESET', '%x') and '$cur_preset' == '%a, %b %d, %Y')]#>$datetime.datetime($datetime.datetime.now().year, 12, 31, 14, 30, 47).strftime($cur_preset)</option>
#end for
</select>
<select class="form-control input-sm#echo ('', ' metadataDiv')[$sickbeard.FUZZY_DATING]#" id="date_presets#echo ('', '_na')[$sickbeard.FUZZY_DATING]#" name="date_preset#echo ('', '_na')[$sickbeard.FUZZY_DATING]#">
<option value="%x"#echo ('', $selected)['%x' == $sickbeard.DATE_PRESET]#>Use System Default</option>
<select class="form-control input-sm#echo ('', ' metadataDiv')[$sg_var('FUZZY_DATING')]#" id="date_presets#echo ('', '_na')[$sg_var('FUZZY_DATING')]#" name="date_preset#echo ('', '_na')[$sg_var('FUZZY_DATING')]#">
<option value="%x"#echo ('', $selected)['%x' == $sg_str('DATE_PRESET', '%x')]#>Use System Default</option>
#for $cur_preset in $date_presets
<option value="$cur_preset"#echo ('', $selected)[$cur_preset == $sickbeard.DATE_PRESET]#>$datetime.datetime($datetime.datetime.now().year, 12, 31, 14, 30, 47).strftime($cur_preset)</option>
<option value="$cur_preset"#echo ('', $selected)[$cur_preset == $sg_str('DATE_PRESET', '%x')]#>$datetime.datetime($datetime.datetime.now().year, 12, 31, 14, 30, 47).strftime($cur_preset)</option>
#end for
</select>
</span>
@ -381,8 +394,8 @@
<span class="component-desc">
<select id="time_presets" name="time_preset" class="form-control input-sm">
#for $cur_preset in $time_presets
#set $show_seconds = not $sickbeard.FUZZY_DATING
<option value="$cur_preset"#echo ('', $selected)[$cur_preset == $sickbeard.TIME_PRESET_W_SECONDS]#>$sbdatetime.now().sbftime(show_seconds=$show_seconds, t_preset=$cur_preset)</option>
#set $show_seconds = not $sg_var('FUZZY_DATING')
<option value="$cur_preset"#echo ('', $selected)[$cur_preset == $sg_str('TIME_PRESET_W_SECONDS', '%I:%M:%S %p')]#>$sbdatetime.now().sbftime(show_seconds=$show_seconds, t_preset=$cur_preset)</option>
#end for
</select>
<span id="trim_info_seconds"><b>note:</b> seconds are only shown on the History page</span>
@ -394,10 +407,10 @@
<span class="component-title">Timezone:</span>
<span class="component-desc">
<label for="local" class="space-right">
<input type="radio" name="timezone_display" id="local" value="local"#echo ('', $checked)['local' == $sickbeard.TIMEZONE_DISPLAY]#>local
<input type="radio" name="timezone_display" id="local" value="local"#echo ('', $checked)['local' == $sg_str('TIMEZONE_DISPLAY')]#>local
</label>
<label for="network">
<input type="radio" name="timezone_display" id="network" value="network"#echo ('', $checked)['network' == $sickbeard.TIMEZONE_DISPLAY]#>network
<input type="radio" name="timezone_display" id="network" value="network"#echo ('', $checked)['network' == $sg_str('TIMEZONE_DISPLAY', 'network')]#>network
</label>
<div class="clear-left"><p>display dates and times in either your timezone or the shows network timezone</p></div>
</span>
@ -423,7 +436,7 @@
<label for="web_username">
<span class="component-title">Username</span>
<span class="component-desc">
<input type="text" name="web_username" id="web_username" value="$sickbeard.WEB_USERNAME" class="form-control input-sm input300">
<input type="text" name="web_username" id="web_username" value="$sg_str('WEB_USERNAME')" class="form-control input-sm input300">
<p>blank for none</p>
</span>
</label>
@ -433,7 +446,7 @@
<label for="web_password">
<span class="component-title">Password</span>
<span class="component-desc">
<input type="password" autocomplete="nope" name="web_password" id="web_password" value="#echo '*' * len($sickbeard.WEB_PASSWORD)#" class="form-control input-sm input300">
<input type="password" autocomplete="nope" name="web_password" id="web_password" value="#echo '*' * len($sg_str('WEB_PASSWORD'))#" class="form-control input-sm input300">
<p>blank for none</p>
<span class="clear-left">check autoProcessTV.cfg is set up for external apps to use post processing scripts</span>
</span>
@ -444,7 +457,7 @@
<label for="calendar_unprotected">
<span class="component-title">Unprotected calendar</span>
<span class="component-desc">
<input type="checkbox" name="calendar_unprotected" id="calendar_unprotected"#echo ('', $checked)[$sickbeard.CALENDAR_UNPROTECTED]#>
<input type="checkbox" name="calendar_unprotected" id="calendar_unprotected"#echo ('', $checked)[$sg_var('CALENDAR_UNPROTECTED')]#>
<p>permit subscribing to the calendar without username and password.
Some services like Google Calendar will only work with <b class="boldest">no</b> authentication</p>
</span>
@ -456,7 +469,7 @@
<label for="use_api">
<span class="component-title">API enable</span>
<span class="component-desc">
<input type="checkbox" name="use_api" class="enabler" id="use_api"#echo ('', $checked)[$sickbeard.USE_API]#>
<input type="checkbox" name="use_api" class="enabler" id="use_api"#echo ('', $checked)[$sg_var('USE_API')]#>
<p>permit the use of the SickGear (SickBeard) API</p>
</span>
</label>
@ -466,7 +479,7 @@
<label for="api_key">
<span class="component-title">API key</span>
<span class="component-desc">
<input type="text" name="api_key" id="api_key" value="$sickbeard.API_KEY" class="form-control input-sm input300" readonly="readonly">
<input type="text" name="api_key" id="api_key" value="$sg_str('API_KEY')" class="form-control input-sm input300" readonly="readonly">
<input class="btn btn-inline" type="button" id="generate_new_apikey" value="Generate">
<div class="clear-left"><p>used to give 3rd party programs limited access to SickGear</p></div>
</span>
@ -478,7 +491,7 @@
<label for="web_port">
<span class="component-title">HTTP port</span>
<span class="component-desc">
<input type="text" name="web_port" id="web_port" value="$sickbeard.WEB_PORT" class="form-control input-sm input100">
<input type="text" name="web_port" id="web_port" value="$sg_var('WEB_PORT', 8081)" class="form-control input-sm input100">
<p>web port to access and browse SickGear (default:8081)</p>
</span>
</label>
@ -488,7 +501,7 @@
<label for="web_log">
<span class="component-title">HTTP logs</span>
<span class="component-desc">
<input type="checkbox" name="web_log" id="web_log"#echo ('', $checked)[$sickbeard.WEB_LOG]#>
<input type="checkbox" name="web_log" id="web_log"#echo ('', $checked)[$sg_var('WEB_LOG')]#>
<p>enable logs from the internal web server</p>
</span>
</label>
@ -498,7 +511,7 @@
<label for="enable_https">
<span class="component-title">SSL enable</span>
<span class="component-desc">
<input type="checkbox" name="enable_https" class="enabler" id="enable_https"#echo ('', $checked)[$sickbeard.ENABLE_HTTPS]#>
<input type="checkbox" name="enable_https" class="enabler" id="enable_https"#echo ('', $checked)[$sg_var('ENABLE_HTTPS')]#>
<p>use a HTTPS address to access the web interface</p>
</span>
</label>
@ -508,7 +521,7 @@
<label for="https_cert">
<span class="component-title">HTTPS certificate</span>
<span class="component-desc">
<input type="text" name="https_cert" id="https_cert" value="$sickbeard.HTTPS_CERT" class="form-control input-sm input300">
<input type="text" name="https_cert" id="https_cert" value="$sg_str('HTTPS_CERT')" class="form-control input-sm input300">
<div class="clear-left"><p>file name or path to a <b class="boldest">server.crt</b> certificate file</p></div>
</span>
</label>
@ -517,7 +530,7 @@
<label for="https_key">
<span class="component-title">HTTPS key</span>
<span class="component-desc">
<input type="text" name="https_key" id="https_key" value="$sickbeard.HTTPS_KEY" class="form-control input-sm input300">
<input type="text" name="https_key" id="https_key" value="$sg_str('HTTPS_KEY')" class="form-control input-sm input300">
<div class="clear-left"><p>file name or path to a <b class="boldest">server.key</b> file</p></div>
</span>
</label>
@ -528,7 +541,7 @@
<label for="web_ipv6">
<span class="component-title">Listen on IPv6</span>
<span class="component-desc">
<input type="checkbox" name="web_ipv6" id="web_ipv6"#echo ('', $checked)[$sickbeard.WEB_IPV6]#>
<input type="checkbox" name="web_ipv6" id="web_ipv6"#echo ('', $checked)[$sg_var('WEB_IPV6')]#>
<p>attempt binding to any available IPv6 address</p>
</span>
</label>
@ -538,7 +551,7 @@
<label for="handle_reverse_proxy">
<span class="component-title">Reverse proxy headers</span>
<span class="component-desc">
<input type="checkbox" name="handle_reverse_proxy" id="handle_reverse_proxy"#echo ('', $checked)[$sickbeard.HANDLE_REVERSE_PROXY]#>
<input type="checkbox" name="handle_reverse_proxy" id="handle_reverse_proxy"#echo ('', $checked)[$sg_var('HANDLE_REVERSE_PROXY')]#>
<p>accept the following reverse proxy headers (advanced)...<br />(X-Forwarded-For, X-Forwarded-Host, and X-Forwarded-Proto)</p>
</span>
</label>
@ -574,7 +587,7 @@
</label>
</div>
#if $sickbeard.BRANCH != 'master'
#if $sg_str('BRANCH', 'master') != 'master'
<div class="field-pair">
<label>
<span class="component-title">Pull request:</span>
@ -593,7 +606,7 @@
<label for="git_remote">
<span class="component-title">Git remote for branch</span>
<span class="component-desc">
<input type="text" name="git_remote" id="git_remote" value="$sickbeard.GIT_REMOTE" class="form-control input-sm input300">
<input type="text" name="git_remote" id="git_remote" value="$sg_str('GIT_REMOTE')" class="form-control input-sm input300">
<div class="clear-left"><p>default:origin. Access repo configured remotes (save then refresh browser)</p></div>
</span>
</label>
@ -603,7 +616,7 @@
<label>
<span class="component-title">Git executable path</span>
<span class="component-desc">
<input type="text" name="git_path" value="$sickbeard.GIT_PATH" class="form-control input-sm input300">
<input type="text" name="git_path" value="$sg_str('GIT_PATH')" class="form-control input-sm input300">
<div class="clear-left"><p>only needed if OS is unable to locate git from env</p></div>
</span>
</label>
@ -615,7 +628,7 @@
<span class="component-desc">
<select id="cpu_presets" name="cpu_preset" class="form-control input-sm">
#for $cur_preset in $sorted($cpu_presets.items(), key=$operator.itemgetter(1), reverse=True)
<option value="$cur_preset[0]"#echo ('', $selected)[$cur_preset[0] == $sickbeard.CPU_PRESET]#>$cur_preset[0].capitalize()</option>
<option value="$cur_preset[0]"#echo ('', $selected)[$cur_preset[0] == $sg_str('CPU_PRESET', 'DISABLED')]#>$cur_preset[0].capitalize()</option>
#end for
</select>
<span>Disabled (default). High is lower and Low is higher CPU use</span>
@ -627,7 +640,7 @@
<label>
<span class="component-title">Anonymous redirect</span>
<span class="component-desc">
<input type="text" name="anon_redirect" value="$sickbeard.ANON_REDIRECT" class="form-control input-sm input300">
<input type="text" name="anon_redirect" value="$sg_str('ANON_REDIRECT')" class="form-control input-sm input300">
<div class="clear-left"><p>backlink protection via anonymizer service, must end in "?"</p></div>
</span>
</label>
@ -637,7 +650,7 @@
<label for="encryption_version">
<span class="component-title">Encrypt passwords</span>
<span class="component-desc">
<input type="checkbox" name="encryption_version" id="encryption_version"#echo ('', $checked)[$sickbeard.ENCRYPTION_VERSION]#>
<input type="checkbox" name="encryption_version" id="encryption_version"#echo ('', $checked)[$sg_var('ENCRYPTION_VERSION')]#>
<p>in the <code>config.ini</code> file.
<b>Warning:</b> Passwords must only contain <a target="_blank" href="<%= anon_url('http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters') %>">ASCII characters</a></p>
</span>
@ -648,7 +661,7 @@
<label>
<span class="component-title">Proxy host</span>
<span class="component-desc">
<input type="text" name="proxy_setting" value="$sickbeard.PROXY_SETTING" class="form-control input-sm input300">
<input type="text" name="proxy_setting" value="$sg_str('PROXY_SETTING')" class="form-control input-sm input300">
<p>blank to disable</p>
<div class="clear-left"><p>proxy address for connecting to providers (use 'PAC:Url' for PAC support)</p></div>
</span>
@ -659,7 +672,7 @@
<label for="proxy_indexers">
<span class="component-title">Use proxy for indexers</span>
<span class="component-desc">
<input type="checkbox" name="proxy_indexers" id="proxy_indexers"#echo ('', $checked)[True == $sickbeard.PROXY_INDEXERS]#>
<input type="checkbox" name="proxy_indexers" id="proxy_indexers"#echo ('', $checked)[True == $sg_var('PROXY_INDEXERS')]#>
<p>use proxy host for TV info source connections</p>
</span>
</label>
@ -677,7 +690,7 @@
#set $level_title = $level.title().upper()
#set $level_count -= 1
#set $level_text = '%s%s' % ($level.title(), (('', ' only')[0 == $level_count], ' and the next%s level%s' % ((' ' + str($level_count), '')[1 == $level_count], maybe_plural($level_count)))[0 < $level_count])
<option value="$level_title"#echo ('', $selected)[$level_title == $sickbeard.FILE_LOGGING_PRESET]#>$level_text</option>
<option value="$level_title"#echo ('', $selected)[$level_title == $sg_str('FILE_LOGGING_PRESET', 'DB')]#>$level_text</option>
#end for
</select>
<span>(default: Db)</span>
@ -691,7 +704,7 @@
</div><!-- /component-group3 //-->
<br/>
<h6 class="pull-right"><b>All non-absolute folder locations are relative to <span class="path">$sickbeard.DATA_DIR</span></b></h6>
<h6 class="pull-right"><b>All non-absolute folder locations are relative to <span class="path">$sg_str('DATA_DIR')</span></b></h6>
<input type="submit" class="btn pull-left config_submitter button" value="Save Changes">
</div><!-- /config-components -->
@ -709,4 +722,4 @@
//-->
</script>
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_bottom.tmpl')

View file

@ -104,17 +104,21 @@
$x.providerType == $GenericProvider.TORRENT and $sickbeard.USE_TORRENTS]
#set $cur_name = $cur_provider.get_id()
#set $cur_url = $cur_provider.url
#set $show_type = $sickbeard.USE_NZBS and $sickbeard.USE_TORRENTS and $GenericProvider.NZB == $cur_provider.providerType
#set $bad_url = not $cur_url and cur_provider.is_enabled()
#set $tip = ($cur_provider.name + ('', ' (enable for link)')[not $cur_url and not cur_provider.is_enabled()],
'Site Down')[$bad_url]
#set $state = ('', ' <span class="red-text">(Site Down?)</span>')[$bad_url]
<li class="ui-state-default" id="$cur_name">
<li class="ui-state-default row-#echo ('odd', 'even')[$show_type or not $sickbeard.USE_TORRENTS]#" id="$cur_name">
<input type="checkbox" id="enable_$cur_name" class="provider_enabler" <%= html_checked if cur_provider.is_enabled() else '' %>/>
<a class="imgLink" #if $cur_url#href="<%= anon_url(cur_url) %>" onclick="window.open(this.href,'_blank');return false;"#else#name=""#end if# rel="noreferrer"><img src="$sbRoot/images/providers/$cur_provider.image_name()" alt="$tip" title="$tip" width="16" height="16" style="vertical-align:middle" /></a>
<span style="vertical-align:middle">$cur_provider.name$state</span>
#if $cur_provider.is_public_access() and type($cur_provider).__name__ not in ['TorrentRssProvider']
<span style="font-size:10px;vertical-align:top;font-weight:normal">(PA)</span>
#end if#
#if $show_type
<span style="font-size:10px;vertical-align:top;font-weight:normal">($cur_provider.providerType)</span>
#end if#
#if not $cur_provider.supports_backlog#*#end if#
<span class="ui-icon ui-icon-arrowthick-2-n-s pull-right" style="margin-top:3px"></span>
</li>

View file

@ -1,75 +1,73 @@
#import sickbeard
#import datetime
#import re
#from sickbeard import subtitles, sbdatetime, network_timezones
#import sickbeard.helpers
#from sickbeard.common import *
#from sickbeard.helpers import anon_url
#from lib import subliminal
#from sickbeard import indexerApi, indexermapper, network_timezones
#from sickbeard.common import Overview, qualityPresets, qualityPresetStrings, \
Quality, statusStrings, WANTED, SKIPPED, ARCHIVED, IGNORED, FAILED, DOWNLOADED
#from sickbeard.helpers import anon_url, get_size, human, maybe_plural
#from sickbeard.indexers.indexer_config import INDEXER_TVDB, INDEXER_IMDB
<% def sg_var(varname, default=False): return getattr(sickbeard, varname, default) %>#slurp#
<% def sg_str(varname, default=''): return getattr(sickbeard, varname, default) %>#slurp#
##
#set global $title = $show.name
#set global $topmenu = 'home'
#set $exceptions_string = ', '.join($show.exceptions)
#set global $page_body_attr = 'display-show'
#set $css = $getVar('css', 'reg')
#set $has_art = $getVar('has_art', None)
#set $restart = 'Restart SickGear for new features on this page'
#set $show_message = ($show_message, $restart)[None is $has_art]
#set global $page_body_attr = 'display-show" class="' + $css
#set theme_suffix = ('', '-dark')['dark' == $sg_str('THEME_NAME', 'dark')]
##
#import os.path, os
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
<script type="text/javascript" src="$sbRoot/js/lib/jquery.bookmarkscroll.js?v=$sbPID"></script>
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_top.tmpl')
<input type="hidden" id="sbRoot" value="$sbRoot">
<script>
<!--
var config = {
hasArt: #echo ('!0', '!1')[not $has_art]#,
TVShowList: [${tvshow_id_csv}],
useIMDbInfo: #echo ('!1', '!0')[$sg_var('USE_IMDB_INFO')]
}
\$.SickGear.config = {
useFuzzy: #echo ('!1', '!0')[$sg_var('FUZZY_DATING')]#,
#if $sg_var('FUZZY_DATING')##slurp#
dateFormat: '$sg_str('DATE_PRESET', '%x')',
timeFormat: '$sg_str('TIME_PRESET', '%I:%M %p')',
fuzzyTrimZero: #echo ('!1', '!0')[$sg_var('TRIM_ZERO')]#
#end if##slurp#
}
//-->
</script>
<script type="text/javascript" src="$sbRoot/js/displayShow.js?v=$sbPID"></script>
<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?v=$sbPID"></script>
<script type="text/javascript" src="$sbRoot/js/sceneExceptionsTooltip.js?v=$sbPID"></script>
#if $sickbeard.USE_IMDB_INFO
#if $sg_var('USE_IMDB_INFO')
<script type="text/javascript" src="$sbRoot/js/ratingTooltip.js?v=$sbPID"></script>
#end if
<script type="text/javascript" src="$sbRoot/js/ajaxEpSearch.js?v=$sbPID"></script>
<script type="text/javascript" src="$sbRoot/js/ajaxEpSubtitles.js?v=$sbPID"></script>
<script type="text/javascript" src="$sbRoot/js/lib/jquery.bookmarkscroll.js?v=$sbPID"></script>
<script type="text/javascript" src="$sbRoot/js/lib/jquery.collapser.min.js?v=$sbPID"></script>
<script type="text/javascript" charset="utf-8">
<!--
\$(document).ready(function(){
#set $fuzzydate = 'airdate'
#if $sickbeard.FUZZY_DATING
fuzzyMoment({
containerClass: '.${fuzzydate}',
dateHasTime: !1,
dateFormat: '${sickbeard.DATE_PRESET}',
timeFormat: '${sickbeard.TIME_PRESET}',
trimZero: #echo ('!1', '!0')[$sickbeard.TRIM_ZERO]#
});
<style>
.bfr{position:absolute;left:-999px;top:-999px}.bfr img{width:16px;height:16px}.spinner{display:inline-block;width:16px;height:16px;background:url(${sbRoot}/images/loading16${theme_suffix}.gif) no-repeat 0 0}
.images i{margin-right:6px;margin-top:5px}.hide{display:none}
.tvshowImg {border:1px solid transparent;min-width:226px;min-hieght:332px}
</style>
<div class="bfr"><img src="$sbRoot/images/loading16${theme_suffix}.gif" /></div>
<div id="background-container">
#if $has_art
<ul>
#for $k, ($image, $rating) in enumerate($fanart)
<li class="#echo ' '.join((x for x in ({10:'group', 20:'fave', 30:'avoid'}.get($rating, ''), ('', 'background first-load')[$start_image == $k]) if x)) #" style="background-image:url($sbRoot/showPoster/?show=$show.indexerid&which=fanart_$image)"></li>
#end for
</ul>
#end if
#raw
$('.addQTip').each(function () {
$(this).css({'cursor':'help', 'text-shadow':'0px 0px 0.5px #666'});
$(this).qtip({
show: {solo:!0},
position: {viewport:$(window), my:'left center', adjust:{ y: -10, x: 2 }},
style: {classes:'qtip-rounded qtip-shadow qtip-maxwidth'}
});
});
#end raw
#if $sickbeard.USE_IMDB_INFO
\$.fn.generateStars = function() {
return this.each(function(i,e){\$(e).html(\$('<span/>').width(\$(e).text()*12));});
};
\$('.imdbstars').generateStars();
#end if
TVShowList = [${tvshow_id_csv}]
});
//-->
</script>
</div>
<div class="displayshow-wrapper reg all">
<div class="background-container">
<div style="" class="background"></div>
</div>
<div class="pull-left form-inline">
<div class="displayshow-wrapper">
<div id="change-show" class="pull-left form-inline">
Change show:
#set $displayshowlist = []
#set $cur_sel = 0
@ -80,18 +78,19 @@
#set void = $displayshowlist.append('\t\t\t<optgroup label="%s">' % $curShowType)
#end if
#for $curShow in $curShowList
#set void = $displayshowlist.append('\t\t\t<option value="%s"%s>%s</option>' % ($curShow.indexerid, ('', ' selected="selected"')[$curShow == $show], $curShow.name))
#set $show_ended = '' != $curShow.status and $curShow.status in ['ended', 'Ended', 'Canceled']
#set void = $displayshowlist.append('\t\t\t<option %svalue="%s"%s>%s</option>' % (('', 'class="ended" ')[$show_ended], $curShow.indexerid, ('', ' selected="selected"')[$curShow == $show], $curShow.name))
#end for
#if 1 < len($sortedShowLists)
#set void = $displayshowlist.append('\t\t\t</optgroup>')
#end if
#end if
#end for
##
<div class="navShow"><img id="prevShow" src="$sbRoot/images/prev.png" alt="&lt;&lt;" title="$prev_title" class="addQTip" /></div>
<div id="prevShow" class="navShow addQTip" title="$prev_title">&nbsp;</div>
<select id="pickShow" class="form-control form-control-inline input-sm">
#echo '\n'.join($displayshowlist)#
</select>
<div class="navShow"><img id="nextShow" src="$sbRoot/images/next.png" alt="&gt;&gt;" title="$next_title" class="addQTip" /></div>
<div id="nextShow" class="navShow addQTip" title="$next_title">&nbsp;</div>
</div>
<div class="clearfix" style="margin-bottom:15px"></div>
@ -104,52 +103,46 @@
#end if
<div class="display-show-container">
<div id="posterCol" class="hidden-xs">
<a href="$sbRoot/showPoster/?show=$show.indexerid&amp;which=poster" rel="dialog" title="View poster for $show.name">
<img src="$sbRoot/showPoster/?show=$show.indexerid&amp;which=poster_thumb" class="tvshowImg" alt="" />
<a href="$sbRoot/showPoster/?show=$show.indexerid&amp;which=poster" rel="dialog">
<img src="$sbRoot/showPoster/?show=$show.indexerid&amp;which=poster_thumb" title="View poster for $show.name" class="tvshowImg addQTip" alt="">
</a>
</div>
<div id="showCol" class="display-details">
<div id="showCol">
#if int($show.paused)
<div class="paused paused-highlight">
<i class="sgicon-pause paused-outline"></i>
</div>
#end if
<div id="details-wrapper">
<div id="details-right">
#if 0 < len($seasonResults)
##There is a special/season_0?##
#set $season_special = (0, 1)[0 == int($seasonResults[-1]['season'])]
##
#if not $sickbeard.DISPLAY_SHOW_SPECIALS and $season_special
$seasonResults.pop(-1)
#end if
<div class="details-wrapper">
<div class="details-right top">
#if 0 < len($seasons)
#set $show_ended = '' != $show.status and $show.status in ['ended', 'Ended', 'Canceled']
<div>
#if $season_special
#if $getVar('has_special', 0 == $seasons[-1][0])
<span class="details-title">Specials</span>
<span class="details-info">#if sickbeard.DISPLAY_SHOW_SPECIALS#<a href="#season-0">View</a><span style="margin:0 10px">-</span>#end if#<a class="inner" href="$sbRoot/toggleDisplayShowSpecials/?show=$show.indexerid">#echo ('Show', 'Hide')[sickbeard.DISPLAY_SHOW_SPECIALS]#</a></span>
<span class="details-info">#if $sg_var('DISPLAY_SHOW_SPECIALS')#<a href="#season-0">View</a><span style="margin:0 7px">-</span>#end if#<a class="inner" href="$sbRoot/toggleDisplayShowSpecials/?show=$show.indexerid">#echo ('Show', 'Hide')[$sg_var('DISPLAY_SHOW_SPECIALS')]#</a></span>
#end if
</div>
#set $many_seasons = 12 < len($seasonResults)
#set $many_seasons = 12 < len($seasons)
<div class="details-seasons">
<span class="details-title#echo ('', ' combo-seasons')[$many_seasons]#">Season</span>
<span class="details-info">
#set $season_list = [s[0] for s in $seasons]
#if $many_seasons
<select id="seasonJump" class="form-control form-control-inline input-sm">
<option value="jump">Jump to season</option>
#for $seasonNum in $seasonResults
#if 0 == int($seasonNum['season'])
#continue
#for $number in $season_list
#if 0 != $number
<option value="#season-$number">Season $number</option>
#end if
<option value="#season-$seasonNum['season']">Season $seasonNum['season']</option>
#end for
</select>
#else:
#for $seasonNum in $seasonResults
#if 0 == int($seasonNum['season'])
#continue
#else
#for $number in $season_list
#if 0 != $number
<a href="#season-$number">$number</a>
#end if
<a href="#season-$seasonNum['season']">$seasonNum['season']</a>
#end for
#end if
</span>
@ -159,12 +152,12 @@
<div id="details-top">
<div id="showtitle" data-showname="$show.name">
<h2 class="title" id="scene_exception_$show.indexerid"><span>$show.name</span></h2>
<h2 class="title" id="scene_exception_$show.indexerid"><span>$show.name</span>#echo ('', '<em id="title-status"> (ended)</em>')[$show_ended]#</h2>
#set $genres_done = False
#if $sickbeard.USE_IMDB_INFO and 'genres' in $show.imdb_info and '' != $show.imdb_info['genres']
#if $sg_var('USE_IMDB_INFO') and 'genres' in $show.imdb_info and '' != $show.imdb_info['genres']
#for $imdbgenre in $show.imdb_info['genres'].split('|')
#set $genres_done = True
<span class="label"><a href="<%= anon_url('http://www.imdb.com/search/title?at=0&genres=', imdbgenre.lower().replace('-','_'),'&amp;sort=moviemeter,asc&amp;title_type=tv_series') %>" target="_blank" title="View other popular $imdbgenre shows on imdb.com">$imdbgenre.replace('Sci-Fi','Science-Fiction')</a></span>
<span class="label"><a href="<%= anon_url('http://www.imdb.com/search/title?at=0&genres=', imdbgenre.lower().replace('-','_'),'&amp;sort=moviemeter,asc&amp;title_type=tv_series') %>" target="_blank" title="View other popular $imdbgenre shows on imdb.com" class="addQTip">$imdbgenre.replace('Sci-Fi','Science-Fiction')</a></span>
#end for
#end if
#if not $genres_done and $show.genre
@ -180,45 +173,47 @@
</div>
</div>
<div id="details-wrapper">
<div id="details-right">
<div class="details-wrapper">
<div class="details-right">
<div>
<span class="details-title">Links</span>
<span class="details-info">
#set $tvdb_id = None
#for $src_id, $src_name in $sickbeard.indexerApi().all_indexers.iteritems()
#if sickbeard.indexerApi($src_id).config.get('defunct') and $src_id != $show.indexer
#for $src_id, $src_name in $indexerApi().all_indexers.iteritems()
#if $indexerApi($src_id).config.get('defunct') and $src_id != $show.indexer
#continue
#end if
#if $src_id in $show.ids and $show.ids[$src_id].get('id', 0) > 0 and $sickbeard.indexermapper.MapStatus.NOT_FOUND != $show.ids[$src_id]['status']
#if $src_id in $show.ids and $show.ids[$src_id].get('id', 0) > 0 and $indexermapper.MapStatus.NOT_FOUND != $show.ids[$src_id]['status']
#if $INDEXER_TVDB == $src_id
#set $tvdb_id = $show.ids[$src_id]['id']
#end if
#if $INDEXER_IMDB == $src_id and not $sickbeard.USE_IMDB_INFO
#if $INDEXER_IMDB == $src_id and not $sg_var('USE_IMDB_INFO')
#continue
#end if
#if not sickbeard.indexerApi($src_id).config.get('defunct')
<a class="service" href="$anon_url(sickbeard.indexerApi($src_id).config['show_url'], $show.ids[$src_id]['id'])" onclick="window.open(this.href, '_blank'); return !1;" title="View $src_name info in new tab">
#if not $indexerApi($src_id).config.get('defunct')
<a class="service addQTip" href="$anon_url($indexerApi($src_id).config['show_url'], $show.ids[$src_id]['id'])" rel="noreferrer" onclick="window.open(this.href, '_blank'); return !1;" title="View $src_name info in new tab">
#else#
<a class="service" href="$sbRoot/home/editShow?show=$show.indexerid#core-component-group3" title="Edit related show IDs">
<a class="service addQTip" href="$sbRoot/home/editShow?show=$show.indexerid#core-component-group3" title="Edit related show IDs">
#end if#
<img alt="$src_name" height="16" width="16" src="$sbRoot/images/$sickbeard.indexerApi($src_id).config['icon']" />
<img alt="$src_name" height="16" width="16" src="$sbRoot/images/$indexerApi($src_id).config['icon']" />
</a>
#end if
#end for
##if $tvdb_id
## <a class="service" href="$anon_url('https://fanart.tv/series/', $tvdb_id)" onclick="window.open(this.href, '_blank'); return !1;" title="View Fanart.tv info in new tab"><img alt="Fanart.tv" height="16" width="16" src="$sbRoot/images/fanart.png" /></a>
##end if
#if $has_art and $tvdb_id
<a class="service addQTip" href="$anon_url('https://fanart.tv/series/', $tvdb_id)" rel="noreferrer" onclick="window.open(this.href, '_blank'); return !1;" title="View Fanart.tv info in new tab">
<img alt="Fanart.tv" height="16" width="16" src="$sbRoot/images/fanart.png" />
</a>
#end if
#if $xem_numbering or $xem_absolute_numbering
<a class="service" href="$anon_url('http://thexem.de/search?q=', $show.name)" rel="noreferrer" onclick="window.open(this.href, '_blank'); return !1;" title="View XEM info in new tab"><img alt="[xem]" height="16" width="16" src="$sbRoot/images/xem.png" /></a>
<a class="service addQTip" href="$anon_url('http://thexem.de/search?q=', $show.name)" rel="noreferrer" onclick="window.open(this.href, '_blank'); return !1;" title="View XEM info in new tab"><img alt="[xem]" height="16" width="16" src="$sbRoot/images/xem.png" /></a>
#end if
</span>
</div>
#set $startyear, $flags, $runtime = (None, False, None)
#if $sickbeard.USE_IMDB_INFO and $show.imdbid
#if $sg_var('USE_IMDB_INFO') and $show.imdbid
#if 'year' in $show.imdb_info
#set $startyear = $show.imdb_info['year']
#set $startyear = $show.imdb_info['year'] or None
#end if
#set $flags = 'country_codes' in $show.imdb_info and '' != $show.imdb_info['country_codes']
#if 'runtimes' in $show.imdb_info
@ -231,7 +226,7 @@
#if None is $runtime and $show.runtime
#set $runtime = $show.runtime
#end if
#if None is not $startyear or $flags
#if $startyear or $flags
<div>
<span class="details-title">Premiered</span>
<span class="details-info">
@ -249,7 +244,7 @@
#set $showairs = '%s%s' % ($show.airs.replace('y', 'y,'),
('', ' <span class="red-text" style="font-weight:bold">(invalid timeformat)</span>')[not $network_timezones.test_timeformat($show.airs)])
<div>
<span class="details-title">Airs</span>
<span class="details-title">Air#echo ('s', 'ed')[$show_ended]#</span>
<span class="details-info">$showairs</span>
</div>
#end if
@ -266,18 +261,18 @@
<span class="details-info">$runtime minutes</span>
</div>
#end if
#if '' != $show.status
#if $show.status
<div>
<span class="details-title">Status</span>
<span class="details-info">$show.status</span>
</div>
#end if
#if $sickbeard.USE_IMDB_INFO and 'rating' in $show.imdb_info
#if $sg_var('USE_IMDB_INFO') and 'rating' in $show.imdb_info
<div>
<span class="details-title">IMDb rating</span>
<span class="details-info">
#if '' != $show.imdb_info['votes']
#if $show.imdb_info['votes']
#set $rating_tip = '%s of 10 stars<br />%s votes' % (str($show.imdb_info['rating']), str($show.imdb_info['votes']))
<span class="imdbstars" qtip-content="$rating_tip">$show.imdb_info['rating']</span>
#else
@ -295,7 +290,7 @@
<span class="quality $qualityPresetStrings[$show.quality]">$qualityPresetStrings[$show.quality]</span>
</span>
</div>
#else:
#else
#if $anyQualities
<div>
<span class="details-title">Initial</span>
@ -316,21 +311,37 @@
</div>
<div id="details-left">
<div class="details-plot#echo ('', ' no-plot')['' == $show.overview]#">
#echo ('No plot overview available', $show.overview)['' != $show.overview]#
#set $has_overview = '' != $show.overview
#set $ep_tally = ('', '(<span class="hint">%s episodes</span>)' % $ep_counts['eps_all'])[0 < $ep_counts['eps_all']]
<div class="#echo ('no', 'details')[$has_overview]#-plot">
$ep_tally
#if $has_overview
$show.overview
#else
#echo ('', '<br /><br />')[any($ep_tally)]
#if $show_ended
#if $varExists('force_update')
A <a href="/$force_update" title="Trigger force full update">force full update</a> may return a plot overview for this ended show
#else
Restart SickGear to get a new link here for this ended show
#end if
#else
No plot overview available
#end if
#end if
</div>
<div id="details-bottom">
<span class="label addQTip" title="Info language, $show.lang"><img src="$sbRoot/images/flags/${show.lang}.png" width="16" height="11" alt="" style="margin-top:-1px" /></span>
<span class="label addQTip" title="Location#echo (' no longer exists" style="background-color:#8f1515"', '"')[$showLoc[1]]#>$showLoc[0]</span>
<span class="label addQTip" title="Size">$sickbeard.helpers.human(sickbeard.helpers.get_size($showLoc[0]))</span>
#set $filecount = sum([$c for $k, $c in $epCounts['videos'].items()])
<span class="label addQTip" title="Videos">$filecount file$sickbeard.helpers.maybe_plural($filecount)</span>
<span class="label addQTip" title="Size">$human($get_size($showLoc[0]))</span>
#set $filecount = sum([$c for $k, $c in $ep_counts['videos'].items()])
<span class="label addQTip" title="Videos">#echo ('No', $filecount)[0 < $filecount]# file$maybe_plural($filecount)</span>
#if $show.paused
<span class="label label-paused">Paused</span>
#end if
#if ($anyQualities + $bestQualities) and int($show.archive_firstmatch)
<span class="label">End upgrade on first match</span>
<span class="label">Upgrade once</span>
#end if
#if $show.exceptions
<span class="label addQTip" title="$exceptions_string.replace(', ', '<br />')">Scene names</span>
@ -341,7 +352,7 @@
#if $show.rls_require_words
<span class="label addQTip" title="#echo $show.rls_require_words.replace(',', '<br />')#">Required words</span>
#end if
#if $show.flatten_folders or $sickbeard.NAMING_FORCE_FOLDERS
#if $show.flatten_folders or $sg_var('NAMING_FORCE_FOLDERS')
<span class="label">Flat folders</span>
#end if
#if int($show.air_by_date)
@ -353,7 +364,7 @@
#if int($show.scene)
<span class="label">Scene numbering</span>
#end if
#if $sickbeard.USE_SUBTITLES and int($show.subtitles)
#if $sg_var('USE_SUBTITLES') and int($show.subtitles)
<span class="label">Subtitles</span>
#end if
#if int($show.is_sports)
@ -363,10 +374,10 @@
<span class="label">Anime</span>
#end if
#if $bwl and $bwl.whitelist
<span class="label addQTip" title="#echo ', '.join($bwl.whitelist).replace(',', '<br />')#">Wanted group$sickbeard.helpers.maybe_plural(len($bwl.whitelist))</span>
<span class="label addQTip" title="#echo ', '.join($bwl.whitelist).replace(',', '<br />')#">Wanted group$maybe_plural(len($bwl.whitelist))</span>
#end if
#if $bwl and $bwl.blacklist
<span class="label addQTip" title="#echo ', '.join($bwl.blacklist).replace(',', '<br />')#">Unwanted group$sickbeard.helpers.maybe_plural(len($bwl.blacklist))</span>
<span class="label addQTip" title="#echo ', '.join($bwl.blacklist).replace(',', '<br />')#">Unwanted group$maybe_plural(len($bwl.blacklist))</span>
#end if
</div>
</div>
@ -376,7 +387,7 @@
<div class="clearfix"></div>
<div class="pull-left">
<div id="change-status" class="pull-left">
<p style="margin-bottom:5px">Change selected episodes to</p>
<select id="statusSelect" class="form-control form-control-inline input-sm">
#for $curStatus in [$WANTED, $SKIPPED, $ARCHIVED, $IGNORED, $FAILED] + sorted($Quality.DOWNLOADED)
@ -392,12 +403,12 @@
</div>
<div class="pull-right clearfix" id="checkboxControls">
<div style="padding-bottom:5px">
<label for="wanted"><span class="wanted"><input type="checkbox" id="wanted" checked="checked"> Wanted: <b>$epCounts[$Overview.WANTED]</b></span></label>
<label for="qual"><span class="qual"><input type="checkbox" id="qual" checked="checked"> Low quality: <b>$epCounts[$Overview.QUAL]</b></span></label>
<label for="good"><span class="good"><input type="checkbox" id="good" checked="checked"> Downloaded: <b>$epCounts[$Overview.GOOD]</b></span></label>
<label for="skipped"><span class="skipped"><input type="checkbox" id="skipped" checked="checked"> Skipped: <b>$epCounts[$Overview.SKIPPED]</b></span></label>
<label for="snatched"><span class="snatched"><input type="checkbox" id="snatched" checked="checked"> Snatched: <b>$epCounts[$Overview.SNATCHED]</b></span></label>
<div style="padding-bottom:5px" class="addQTip" title="Filter Episodes">
<label for="good"><span class="good"><input type="checkbox" id="good" checked="checked"> Downloaded: <b>$ep_counts[$Overview.GOOD]</b></span></label>
<label for="snatched"><span class="snatched"><input type="checkbox" id="snatched" checked="checked"> Snatched: <b>$ep_counts[$Overview.SNATCHED]</b></span></label>
<label for="wanted"><span class="wanted"><input type="checkbox" id="wanted" checked="checked"> Wanted: <b>$ep_counts[$Overview.WANTED]</b></span></label>
<label for="qual"><span class="qual"><input type="checkbox" id="qual" checked="checked"> Low Quality: <b>$ep_counts[$Overview.QUAL]</b></span></label>
<label for="skipped"><span class="skipped"><input type="checkbox" id="skipped" checked="checked"> Skipped: <b>$ep_counts[$Overview.SKIPPED]</b></span></label>
</div>
<div class="pull-right" >
@ -407,207 +418,94 @@
</div>
<div class="clearfix"></div>
#set $curSeason = -1
#set $odd = 0
#set $scene, $scene_anime = (False, False)
#if not $show.air_by_date and not $show.is_sports and not $show.is_anime and $show.is_scene
#set $scene = True
#elif not $show.air_by_date and not $show.is_sports and $show.is_anime and $show.is_scene
#set $scene_anime = True
#end if
##
#if 0 == len($sqlResults)
<div style="margin-top:50px">
<h3>Episodes do not exist for this show at the associated indexer
<a class="service" href="$anon_url(sickbeard.indexerApi($show.indexer).config['show_url'], $show.indexerid)" onclick="window.open(this.href, '_blank'); return !1;" title="View $sickbeard.indexerApi($show.indexer).name info in new tab">$sickbeard.indexerApi($show.indexer).name</a>
#if not len($seasons)
<div id="no-episode-data">
<h3>No episode details at TV info source
<a class="service" href="$anon_url($indexerApi($show.indexer).config['show_url'], $show.indexerid)" onclick="window.open(this.href, '_blank'); return !1;" title="View $indexerApi($show.indexer).name info in new tab">$indexerApi($show.indexer).name</a>
</h3>
</div>
#else:
#set $network_timezone = $network_timezones.get_network_timezone($show.network)
#set $network_time = $network_timezones.parse_time($show.airs)
#for $epResult in $sqlResults
#set $epStr = '%sx%s' % ($epResult['season'], $epResult['episode'])
#if not $epStr in $epCats or (0 == int($epResult['season']) and not $sickbeard.DISPLAY_SHOW_SPECIALS)
#continue
#else
#set $working_season = -1
#set $odd = 0
#set $scene, $scene_anime = (False, False)
#if not $show.air_by_date and not $show.is_sports and not $show.is_anime and $show.is_scene
#set $scene = True
#elif not $show.air_by_date and not $show.is_sports and $show.is_anime and $show.is_scene
#set $scene_anime = True
#end if
##
#if $curSeason != int($epResult['season'])
#if 0 <= $curSeason
#for $season, $episodes in $seasons
#for $ep in $episodes
#if None is not $ep
#set $ep_str = '%sx%s' % ($season, $ep['episode'])
#if $ep_str not in $ep_cats or (0 == $season and not $sg_var('DISPLAY_SHOW_SPECIALS'))
#continue
#end if
#end if
#if $working_season != $season
#if 0 <= $working_season
</tbody>
</table>
#end if
<table class="sickbeardTable" cellspacing="0" border="0" cellpadding="0">
<tr id="season-$epResult['season']">
<th class="row-seasonheader" colspan="13">
<button id="showseason-$epResult['season']" type="button" class="btn btn-default pull-right#echo '%s%s' % (('', ' display-season')[int($epResult['season']) in $display_seasons], ('', ' latest-season')[$latest_season == int($epResult['season'])])#" data-toggle="collapse" data-target="#collapseSeason-$epResult['season']">Show episodes<span class="sgicon-arrowdown" style="margin-left:4px"></span></button>
#set $videos = 'none' if $epResult['season'] not in $epCounts['videos'] else $epCounts['videos'][$epResult['season']]
#set $archived = False if $epResult['season'] not in $epCounts['archived'] else $epCounts['archived'][$epResult['season']]
<h3><a name="season-$epResult['season']"></a>#if 0 == int($epResult['season']) then 'Specials' else 'Season ' + str($epResult['season'])
<span class="season-status"><b>[</b> <span class="footerhighlight">$videos</span> / <span class="footerhighlight">$epCounts['totals'][$epResult['season']]</span><span class="archived-count">#echo ('', '&nbsp;with <span class="footerhighlight">%s</span> archived' % $archived)[0 < $archived]#</span> <b>]</b></span>
</h3>
</th>
</tr>
<tbody id="collapseSeason-$epResult['season']" class="collapse#echo '%s%s' % (('', ' display-season')[int($epResult['season']) in $display_seasons], ('', ' latest-season')[$latest_season == int($epResult['season'])])#">
<tr id="season-$epResult['season']-cols" class="seasoncols">
<th class="col-checkbox"><input type="checkbox" class="seasonCheck" id="$epResult['season']"></th>
<th class="col-metadata">NFO</th>
<th class="col-metadata">TBN</th>
<th class="col-ep">Episode</th>
#if $show.is_anime
<th class="col-ep">Absolute</th>
#end if
#if $scene
<th class="col-ep">Scene</th>
#end if
#if $scene_anime
<th class="col-ep">Scene absolute</th>
#end if
<th class="col-name">Name</th>
<th class="col-airdate">Airdate</th>
#if $sickbeard.USE_SUBTITLES and $show.subtitles
<th class="col-subtitles">Subtitles</th>
#end if
<th class="col-status">Status</th>
<th class="col-search">Search</th>
</tr>
#set $curSeason = int($epResult['season'])
#end if
#set $epLoc = $epResult['location']
#set never_aired = 0 < $curSeason and 1 == int($epResult['airdate'])
<tr class="#echo ($Overview.overviewStrings[$epCats[$epStr]], 'airdate-never')[$never_aired]##echo ('', ' archived')[ARCHIVED == int($epResult['status'])]# season-$curSeason seasonstyle">
<td class="col-checkbox">
#if $UNAIRED != int($epResult['status']) and not $never_aired
<input type="checkbox" class="epCheck" id="#echo $epStr#" name="#echo $epStr#">
#end if
</td>
<td align="center"><img src="$sbRoot/images/#if int($epResult['hasnfo']) then 'nfo.gif" alt="Yes" title="Yes' else 'nfo-no.gif" alt="No" title="No'#" width="23" height="11" /></td>
<td align="center"><img src="$sbRoot/images/#if int($epResult['hastbn']) then 'tbn.gif" alt="Yes" title="Yes' else 'tbn-no.gif" alt="No" title="No'#" width="23" height="11" /></td>
<td align="center">
#if $epLoc and $show._location and $epLoc.lower().startswith($show._location.lower())
#set $epLoc = $epLoc[len($show._location)+1:]
#elif $epLoc and (not $epLoc.lower().startswith($show._location.lower()) or not $show._location)
#set $epLoc = $epLoc
#end if
#if '' != $epLoc and None != $epLoc
<span title="$epLoc - <strong>$sickbeard.helpers.human($epResult['file_size'])</strong>" class="addQTip">$epResult["episode"]</span>
#else
$epResult['episode']
#end if
</td>
#if $show.is_anime
<td align="center">$epResult['absolute_number']</td>
#end if
#if $scene
#set $dfltSeas, $dfltEpis = (0, 0) if ($epResult['season'], $epResult['episode']) not in $xem_numbering else $xem_numbering[($epResult['season'], $epResult['episode'])]
<td align="center">
<input type="text" placeholder="#echo '%sx%s' % ($dfltSeas, $dfltEpis)#" size="6" maxlength="8"
class="sceneSeasonXEpisode form-control input-scene" data-for-season="$epResult['season']" data-for-episode="$epResult['episode']"
id="#echo 'sceneSeasonXEpisode_%s_%s_%s' % ($show.indexerid, $epResult['season'], $epResult['episode'])#"
title="Change the value here if scene numbering differs from the indexer episode numbering"
#if ($epResult['season'], $epResult['episode']) in $scene_numbering
#set $scSeas, $scEpis = $scene_numbering[($epResult['season'], $epResult['episode'])]
value="#echo '%sx%s' % ($scSeas, $scEpis)#"
#else
value=""
#end if
style="padding:0; text-align:center; max-width:60px">
</td>
#elif $scene_anime
#set $dfltAbsolute = 0 if $epResult['absolute_number'] not in $xem_absolute_numbering else $xem_absolute_numbering[$epResult['absolute_number']]
<td align="center">
<input type="text" placeholder="$dfltAbsolute" size="6" maxlength="8"
class="sceneAbsolute form-control input-scene" data-for-absolute="$epResult['absolute_number']"
id="#echo 'sceneAbsolute_%s_%s' % ($show.indexerid, $epResult['absolute_number'])#"
title="Change the value here if scene absolute numbering differs from the indexer absolute numbering"
#if $epResult['absolute_number'] in $scene_absolute_numbering
value="$scene_absolute_numbering[$epResult['absolute_number']]"
#else
value=""
#end if
style="padding:0; text-align:center; max-width:60px" />
</td>
#end if
<td class="col-name">
<img src="$sbRoot/images/info32.png" width="16" height="16" alt="" class="plotInfo#echo '%s" />' %\
('None', ('" id="plot_info_%s_%s_%s' % ($show.indexerid, $epResult['season'], $epResult['episode'])))[None is not $epResult['description'] and '' != $epResult['description']]#
<%= '<em class="tba grey-text">TBA</em>' if not epResult['name'] or 'TBA' == epResult['name'] else epResult['name'] %>
</td>
<td class="col-airdate">
<span class="${fuzzydate}"#if $sickbeard.FUZZY_DATING# data-fulldate="$sbdatetime.sbdatetime.sbfdate(dt=$datetime.date.fromordinal($epResult['airdate']), d_preset='%A, %B %d, %Y')"#end if#>#if 1 == int($epResult['airdate']) then 'never' else $sbdatetime.sbdatetime.sbfdate($sbdatetime.sbdatetime.convert_to_setting($network_timezones.parse_date_time($epResult['airdate'], $network_time, $network_timezone)))#</span>
</td>
#if $sickbeard.USE_SUBTITLES and $show.subtitles
<td class="col-subtitles" align="center">
#if $epResult['subtitles']
#for $sub_lang in subliminal.language.language_list($epResult['subtitles'].split(','))
#if '' != sub_lang.alpha2
<img src="$sbRoot/images/flags/${sub_lang.alpha2}.png" width="16" height="11" alt="${sub_lang}" />
#end if
#end for
#end if
</td>
#end if
#set $working_season = $season
#set $curStatus, $curQuality = $Quality.splitCompositeStatus(int($epResult['status']))
#if Quality.NONE != $curQuality
<td class="col-status">$statusStrings[$curStatus] <span class="quality $Quality.qualityStrings[$curQuality].replace('720p','HD720p').replace('1080p','HD1080p').replace('RawHD TV', 'RawHD').replace('HD TV', 'HD720p')">$Quality.qualityStrings[$curQuality]</span></td>
#else:
<td class="col-status">$statusStrings[$curStatus]</td>
#end if
#set $human_season = ('Season %s' % $season, 'Specials')[0 == $season]
## one off variable migration, on next version apply... (s/$getVar('display_seasons', [])/[]/),
<table class="sickbeardTable#echo '%s%s%s' % (('', ' season-min')[$season in $getVar('season_min', $getVar('display_seasons', []))], ('', ' latest-season')[$latest_season == $season], ('', ' open')[$season in $getVar('other_seasons', [])])#">
<thead>
<tr>
<th class="row-seasonheader" colspan="13">
#if None is $has_art#<span class="red-text pull-right">Restart SickGear to reveal new options here</span>
#elif not (1 == $lowest_season and 1 == $latest_season and $latest_season >= $highest_season)
<button id="showseason-$season" type="button" class="btn btn-default pull-right"><span class="onshow">Show $human_season.lower()<i class="sgicon-arrowdown"></i></span><span class="onhide">Hide $human_season.lower()<i class="sgicon-arrowup"></i></span></button>
<button type="button" class="btn btn-default pull-right allseasons"><span class="onshow">Show all</span><span class="onhide">Hide most</span><i class="icon-glyph"></i></button>
<span class="images pull-right hide"><i class="spinner"></i></span>
#end if
#set $videos = '0' if $season not in $ep_counts['videos'] else $ep_counts['videos'][$season]
#set $season_stats = $ep_counts['status'].get($season, {})
#set $snatched = $season_stats.get($Overview.SNATCHED, None)
#set $wanted = $season_stats.get($Overview.WANTED, None)
#set $qual = $season_stats.get($Overview.QUAL, None)
#set $archived = False if $season not in $ep_counts['archived'] else $ep_counts['archived'][$season]
<h3>$human_season<a id="season-$season" name="season-$season"></a>
#if None is not $has_art
<span class="season-status"><span class="good status-badge">&nbsp;D: <strong>$videos</strong>&nbsp;</span>#if snatched#<span class="snatched status-badge">&nbsp;S: <strong>$snatched</strong>&nbsp;</span>#end if##if $wanted#<span class="wanted status-badge">&nbsp;W: <strong>$wanted</strong>&nbsp;</span>#end if##if $qual#<span class="qual status-badge">&nbsp;LQ: <strong>$qual</strong>&nbsp;</span>#end if#&nbsp;of&nbsp;<span class="footerhighlight">$ep_counts['totals'][$season]</span><span class="archived-count">#echo ('', '&nbsp;with <span class="footerhighlight">%s</span> archived' % $archived)[0 < $archived]#</span></span>
#end if
</h3>
</th>
</tr>
#set $attr_title_ep = ('', ' (Anime #)')[$show.is_anime]
<tr id="season-$season-cols" class="seasoncols collapse tablesorter-headerRow">
<td class="col-checkbox tablesorter-no-sort"><input type="checkbox" class="seasonCheck" id="$season"></td>
<th class="col-metadata tablesorter-no-sort">Meta</th>
<th class="col-ep tablesorter-ep-num"><span title="Ep #$attr_title_ep">#</span></th>#if $scene or $scene_anime
<th class="col-ep tablesorter-ep-scene" style="padding-right:18px">Scene#if $scene_anime# absolute#end if#</th>#end if#
<th class="col-name">Name</th>
<th class="col-airdate tablesorter-airdate">Air Date</th>#if $sg_var('USE_SUBTITLES') and $show.subtitles
<th class="col-subtitles tablesorter-no-sort">Subtitles</th>#end if
<th class="col-status">Status</th>
<th class="col-search tablesorter-no-sort">Search</th>
</tr>
</thead>
<td class="col-search">
#if 0 != int($epResult['season'])
#if (int($epResult['status']) in $Quality.SNATCHED or int($epResult['status']) in $Quality.DOWNLOADED) and $sickbeard.USE_FAILED_DOWNLOADS
<a class="epRetry" id="#echo $epStr#" name="#echo $epStr#" href="$sbRoot/home/retryEpisode?show=$show.indexerid&amp;season=$epResult['season']&amp;episode=$epResult['episode']"><img src="$sbRoot/images/search16.png" height="16" alt="retry" title="Retry download" /></a>
#else:
<a class="epSearch" id="#echo $epStr#" name="#echo $epStr#" href="$sbRoot/home/searchEpisode?show=$show.indexerid&amp;season=$epResult['season']&amp;episode=$epResult['episode']"><img src="$sbRoot/images/search16.png" width="16" height="16" alt="search" title="Manual search" /></a>
#end if
#end if
<tbody class="collapse">
#end if
#if None is $ep
</tbody>
#continue
#end if
#if $sickbeard.USE_SUBTITLES and $show.subtitles and len(set(str($epResult['subtitles']).split(',')).intersection(set($subtitles.wantedLanguages()))) < len($subtitles.wantedLanguages()) and $epResult['location']
<a class="epSubtitlesSearch" href="$sbRoot/home/searchEpisodeSubtitles?show=$show.indexerid&amp;season=$epResult['season']&amp;episode=$epResult['episode']"><img src="$sbRoot/images/closed_captioning.png" height="16" alt="search subtitles" title="Search subtitles" /></a>
#end if
</td>
</tr>
#end for
#set global $episode = $ep
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_displayShow.tmpl')
#end for
#end for
</tbody>
</table>
#end if
<script type="text/javascript" charset="utf-8">
#raw
$(document).ready(function(){
$('.details-plot').collapser({
mode: 'lines',
truncate: 10,
showText: '<span class="pull-right moreless"><i class="sgicon-arrowdown" style="margin-right:2px"></i>more</span>',
hideText: '<span class="pull-right moreless"><i class="sgicon-arrowup" style="margin-right:2px"></i>less</span>',
showClass: 'show-class'
});
$('button[data-target*="collapseSeason-"]').each(function(k,v){
var tbl = $($(this).attr('data-target')),
btn = $('#' + $(this).attr('id'));
tbl.on('hide.bs.collapse', function () { btn.html('Show episodes<span class="sgicon-arrowdown" style="margin-left:4px"></span>'); })
tbl.on('show.bs.collapse', function () { btn.html('Hide episodes<span class="sgicon-arrowup" style="margin-left:4px"></span>'); })
});
#end raw
});
</script>
</div>
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
#if None is not $has_art
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_livepanel.tmpl')
#end if
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_bottom.tmpl')

View file

@ -1,29 +1,32 @@
#import sickbeard
#import lib.adba as adba
#from sickbeard import common
#from sickbeard import exceptions
#from sickbeard import scene_exceptions
#from sickbeard import (blackandwhitelist, common, exceptions, helpers, scene_exceptions)
#from sickbeard.helpers import anon_url
#from sickbeard.indexers.indexer_config import INDEXER_TVDB
#import sickbeard.blackandwhitelist
<% def sg_var(varname, default=False): return getattr(sickbeard, varname, default) %>#slurp#
<% def sg_str(varname, default=''): return getattr(sickbeard, varname, default) %>#slurp#
##
#set global $title = 'Edit ' + $show.name
#set global $header = $show.name
#set global $sbPath = '..'
#set global $topmenu = 'home'
#set global $page_body_attr = 'edit-show'
#set $css = $getVar('css', 'reg')
#set $has_art = $getVar('has_art', None)
#set $restart = 'Restart SickGear for new features on this page'
#set $show_message = (None, $restart)[None is $has_art]
#set global $page_body_attr = 'edit-show" class="' + $css
##
#import os.path
#from urllib import quote_plus
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_top.tmpl')
<script>
var config = {showLang: '$show.lang', showIsAnime: #echo ('!1','!0')[$show.is_anime]#}
</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>
var config = {showLang: '$show.lang', showIsAnime: #echo ['!1','!0'][$show.is_anime]#}
</script>
<script type="text/javascript" src="$sbRoot/js/livepanel.js?v=$sbPID"></script>
#if $varExists('header')
<h1 class="header"><span class="grey-text">Edit</span> $header</h1>
<h1 class="header"><span class="grey-text">Edit&nbsp;</span>$header</h1>
#else
<h1 class="title">$title</h1>
#end if
@ -31,6 +34,21 @@
##
#set $html_checked = ' checked="checked"'
#set $html_disabled = ' disabled="disabled"'
<div id="background-container">
#if $has_art
<ul>
#for $k, ($image, $rating) in enumerate($fanart)
<li class="#echo ' '.join((x for x in ({10:'group', 20:'fave', 30:'avoid'}.get($rating, ''), ('', 'background first-load')[$start_image == $k]) if x)) #" style="background-image:url($sbRoot/showPoster/?show=$show.indexerid&which=fanart_$image)"></li>
#end for
</ul>
#end if
</div>
#if $show_message
<div class="alert alert-info">
$show_message
</div>
#end if
<div id="config">
<div id="config-content" class="linefix container">
<form action="editShow" method="post" id="editShow" style="width:894px">
@ -48,20 +66,20 @@
<div class="field-pair">
<label for="paused">
<span class="component-title">Paused</span>
<span class="component-title">Pause searching releases</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>
<p>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#">
<div class="field-pair#if $sg_str('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:
#for $tag in $sg_var('SHOW_TAGS', []):
<option value="$tag" #if $tag == $show.tag then 'selected="selected"' else ''#>$tag#echo ('', ' (default)')['Show List' == $tag]#</option>
#end for
</select>
@ -72,9 +90,9 @@
<div class="field-pair">
<span class="label-container">
<span class="component-title">Alternative show name(s)</span>
<span class="component-title">Alternative release name(s)</span>
<span class="component-desc">
<input type="text" id="SceneName" class="form-control form-control-inline input-sm input200">
<input type="text" id="SceneName" class="form-control form-control-inline input-sm input200" placeholder="Enter one title here, then 'Add'">
<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:
@ -84,8 +102,8 @@
#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>
<p style="float:left"><span class="add-tip">Enter one.. </span>e.g. Show, Show (2016), or The Show (US)</p>
<p class="clear-left note">searching and post-processing require the alternatives if "Show not found" errors are in the logs</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>
@ -96,7 +114,7 @@
#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>
<span><p class="note">#if $show.is_anime#S* = Any series. #end if#The original name is used along<br />with this case insensitive list</p></span>
<div>
<input id="removeSceneName" value="Remove" class="btn pull-left" type="button" style="margin-top: 10px;"/>
</div>
@ -130,7 +148,7 @@
#set $qualities = $common.Quality.splitQuality(int($show.quality))
#set global $anyQualities = $qualities[0]
#set global $bestQualities = $qualities[1]
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_qualityChooser.tmpl')
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_qualityChooser.tmpl')
#if $anyQualities + $bestQualities
<div class="field-pair show-if-quality-custom" style="display:none">
@ -213,7 +231,7 @@
#if $show.is_anime
#import sickbeard.blackandwhitelist
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_blackwhitelist.tmpl')
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_blackwhitelist.tmpl')
<script type="text/javascript" src="$sbRoot/js/blackwhite.js?v=$sbPID"></script>
#end if
</div><!-- /component-group2 //-->
@ -233,7 +251,7 @@
<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]#>
<input type="checkbox" name="flatten_folders" id="flatten_folders"#echo ('', $html_checked)[$show.flatten_folders and not $sg_var('NAMING_FORCE_FOLDERS')]##echo ('', $html_disabled)[$sg_var('NAMING_FORCE_FOLDERS')]#>
<p>prevent creating season folders to group files</p>
</span>
</label>
@ -243,8 +261,27 @@
<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>
<input type="checkbox" name="subtitles" id="subtitles"#echo ('', $html_checked)[$show.subtitles and $sg_var('USE_SUBTITLES')]##echo ($html_disabled, '')[$sg_var('USE_SUBTITLES')]#>
<p#if not $sg_var('USE_SUBTITLES')# class="grey-text"><del#end if#>download episode subtitles for this show#if not $sg_var('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 class="field-pair">
<label for="reset_fanart">
<span class="component-title">Reset fanart ratings</span>
<span class="component-desc">
#if $num_ratings
<input type="checkbox" name="reset_fanart" id="reset_fanart">
<p>delete <span class="grey-text">$num_ratings</span> fanart rating$helpers.maybe_plural($num_ratings) for "<span class="grey-text">$show.name</span>"</p>
#else
<p>no fanart ratings to delete for "<span class="grey-text">$show.name</span>"</p>
#end if
<p>
<span class="grey-text">fanart usage:&nbsp;</span>maximum $sg_var('FANART_LIMIT', 3) fanart images downloaded per show, <a href="$sbRoot/config/general/#core-component-group2">change limit</a><br />
<span class="grey-text">fanart keys:&nbsp;</span>hold down Ctrl+Alt (mac: Command+Option) and then press any of... left or right to change image, and<br />
's' to change and save ratings where 'a'/down = avoid, 'f' = fave, 'g'/up = group (repeat keypress to toggle rating)
with the livepanel not in 's'ave mode; up = change view mode, down = toggle translucency
</span>
</label>
</div>
@ -327,7 +364,7 @@
<p>for unlocked IDs</p>
</span>
<span id="save-wait" class="component-desc hide">
<span><img src="$sbRoot/images/loading16#echo ('', '-dark')['dark' == $sickbeard.THEME_NAME]#.gif" style="margin-right:6px">Saving...</span>
<span><img src="$sbRoot/images/loading16#echo ('', '-dark')['dark' == $sg_str('THEME_NAME', 'dark')]#.gif" style="margin-right:6px">Saving...</span>
</span>
</div>
</div>
@ -344,4 +381,4 @@
</div>
</div>
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_bottom.tmpl')

File diff suppressed because it is too large Load diff

View file

@ -6,15 +6,17 @@
#from sickbeard import history
#from sickbeard import providers
#from sickbeard.providers import generic
<% def sg_var(varname, default=False): return getattr(sickbeard, varname, default) %>#slurp#
<% def sg_str(varname, default=''): return getattr(sickbeard, varname, default) %>#slurp#
##
#set global $title = 'History'
#set global $header = 'History'
#set global $sbPath = '..'
#set global $topmenu = 'history'
#set $layout = $sickbeard.HISTORY_LAYOUT
#set $layout = $sg_str('HISTORY_LAYOUT', 'detailed')
##
#import os.path
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_top.tmpl')
<script type="text/javascript">
<!--
@ -61,13 +63,13 @@
});
#set $fuzzydate = 'airdate'
#if $sickbeard.FUZZY_DATING
#if $sg_var('FUZZY_DATING')
fuzzyMoment({
containerClass: '.${fuzzydate}',
dateHasTime: !0,
dateFormat: '${sickbeard.DATE_PRESET}',
timeFormat: '${sickbeard.TIME_PRESET_W_SECONDS}',
trimZero: #echo ('!1', '!0')[$sickbeard.TRIM_ZERO]#,
dateFormat: '$sg_str('DATE_PRESET', '%x')',
timeFormat: '$sg_str('TIME_PRESET_W_SECONDS', '%I:%M:%S %p')',
trimZero: #echo ('!1', '!0')[$sg_var('TRIM_ZERO')]#,
dtGlue: ', '
});
#end if
@ -93,8 +95,8 @@
<span style="margin-left:5px">Layout:
<select name="HistoryLayout" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value">
<option value="$sbRoot/setHistoryLayout/?layout=compact"#echo ('', $html_selected)['compact' == $sickbeard.HISTORY_LAYOUT]#>Compact</option>
<option value="$sbRoot/setHistoryLayout/?layout=detailed"#echo ('', $html_selected)['detailed' == $sickbeard.HISTORY_LAYOUT]#>Detailed</option>
<option value="$sbRoot/setHistoryLayout/?layout=compact"#echo ('', $html_selected)['compact' == $sg_str('HISTORY_LAYOUT')]#>Compact</option>
<option value="$sbRoot/setHistoryLayout/?layout=detailed"#echo ('', $html_selected)['detailed' == $sg_str('HISTORY_LAYOUT', 'detailed')]#>Detailed</option>
</select>
</span>
</div>
@ -121,10 +123,10 @@
<tbody>
#for $hItem in $historyResults
#set $curStatus, $curQuality = $Quality.splitCompositeStatus(int($hItem['action']))
#set $data_name = (re.sub('^((?:A(?!\s+to)n?)|The)\s(\w)', r'\2', $hItem['show_name']), $hItem['show_name'])[$sickbeard.SORT_ARTICLE]
#set $data_name = (re.sub('^((?:A(?!\s+to)n?)|The)\s(\w)', r'\2', $hItem['show_name']), $hItem['show_name'])[$sg_var('SORT_ARTICLE')]
#set $display_name = '<span data-name="%s">%s - S%02iE%02i</span>' % (
$data_name,
(re.sub('^((?:A(?!\s+to)n?)|The)\s(\w)', r'<span class="article">\1</span> \2', $hItem['show_name']), $hItem['show_name'])[$sickbeard.SORT_ARTICLE],
(re.sub('^((?:A(?!\s+to)n?)|The)\s(\w)', r'<span class="article">\1</span> \2', $hItem['show_name']), $hItem['show_name'])[$sg_var('SORT_ARTICLE')],
int(hItem['season']), int(hItem['episode']))
<tr>
#set $curdatetime = $datetime.datetime.strptime(str($hItem['date']), $history.dateFormat)
@ -156,7 +158,7 @@
#end if
#end if
</td>
<td><span class="hide">$curQuality</span><span class="quality $Quality.qualityStrings[$curQuality].replace('720p', 'HD720p').replace('1080p', 'HD1080p').replace('RawHD TV', 'RawHD').replace('HD TV', 'HD720p')">$Quality.qualityStrings[$curQuality]</span></td>
<td><span class="hide">$curQuality</span><span class="quality $Quality.get_quality_css($curQuality)">$Quality.qualityStrings[$curQuality]</span></td>
</tr>
#end for
@ -164,10 +166,10 @@
<thead>
<tr>
<th class="nowrap">Time</th>
<th width="#echo '3%s%%' % ('5', '0')[sickbeard.USE_SUBTITLES]#">Episode</th>
<th width="#echo '3%s%%' % ('5', '0')[$sg_var('USE_SUBTITLES')]#">Episode</th>
<th>Snatched</th>
<th>Downloaded</th>
#if sickbeard.USE_SUBTITLES
#if $sg_var('USE_SUBTITLES')
<th>Subtitled</th>
#end if
<th width="14%">Quality</th>
@ -183,10 +185,10 @@
<tbody>
#for $hItem in $compactResults
#set $curdatetime = $datetime.datetime.strptime(str($hItem['actions'][0]['time']), $history.dateFormat)
#set $data_name = (re.sub('^((?:A(?!\s+to)n?)|The)\s(\w)', r'\2', $hItem['show_name']), $hItem['show_name'])[$sickbeard.SORT_ARTICLE]
#set $data_name = (re.sub('^((?:A(?!\s+to)n?)|The)\s(\w)', r'\2', $hItem['show_name']), $hItem['show_name'])[$sg_var('SORT_ARTICLE')]
#set $display_name = '<span data-name="%s">%s - S%02iE%02i</span>' % (
$data_name,
(re.sub('^((?:A(?!\s+to)n?)|The)\s(\w)', r'<span class="article">\1</span> \2', $hItem['show_name']), $hItem['show_name'])[$sickbeard.SORT_ARTICLE],
(re.sub('^((?:A(?!\s+to)n?)|The)\s(\w)', r'<span class="article">\1</span> \2', $hItem['show_name']), $hItem['show_name'])[$sg_var('SORT_ARTICLE')],
int(hItem['season']), int(hItem['episode']))
#set $prov_list = []
#set $down_list = []
@ -245,7 +247,7 @@
<td>
#echo ' '.join($down_list)#
</td>
#if sickbeard.USE_SUBTITLES
#if $sg_var('USE_SUBTITLES')
<td>
#for $action in reversed($hItem['actions'])
#set $curStatus, $curQuality = $Quality.splitCompositeStatus(int($action['action']))
@ -258,7 +260,7 @@
#end for
</td>
#end if
<td quality="$curQuality"><span class="quality $Quality.qualityStrings[$curQuality].replace('720p', 'HD720p').replace('1080p', 'HD1080p').replace('RawHD TV', 'RawHD').replace('HD TV', 'HD720p')">$Quality.qualityStrings[$curQuality]</span></td>
<td quality="$curQuality"><span class="quality $Quality.get_quality_css($curQuality)">$Quality.qualityStrings[$curQuality]</span></td>
</tr>
#end for
@ -266,4 +268,4 @@
</tbody>
</table>
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_bottom.tmpl')

View file

@ -2,6 +2,8 @@
#import datetime
#from sickbeard.common import *
#from sickbeard import sbdatetime, network_timezones
<% def sg_var(varname, default=False): return getattr(sickbeard, varname, default) %>#slurp#
<% def sg_str(varname, default=''): return getattr(sickbeard, varname, default) %>#slurp#
##
#set global $title = 'Home'
#set global $header = 'Show List'
@ -11,18 +13,18 @@
#set fuzzydate = 'airdate'
##
#import os.path
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_top.tmpl')
<script>
var config = {
isPoster: #echo ['!1','!0']['poster' == $sickbeard.HOME_LAYOUT]#,
sortArticle: #echo ['!1','!0'][$sickbeard.SORT_ARTICLE]#,
homeSearchFocus: #echo ['!1','!0'][$sickbeard.HOME_SEARCH_FOCUS]#,
fuzzyDating: #echo ['!1','!0'][$sickbeard.FUZZY_DATING]#,
timeZero: #echo ['!1','!0'][$sickbeard.TRIM_ZERO]#,
datePreset: "$sickbeard.DATE_PRESET",
timePreset: "$sickbeard.TIME_PRESET",
posterSortby: "$sickbeard.POSTER_SORTBY",
posterSortdir: #echo ['!1','!0'][$sickbeard.POSTER_SORTDIR]#,
isPoster: #echo ['!1','!0']['poster' == $sg_var('HOME_LAYOUT')]#,
sortArticle: #echo ['!1','!0'][$sg_var('SORT_ARTICLE')]#,
homeSearchFocus: #echo ['!1','!0'][$sg_var('HOME_SEARCH_FOCUS', True)]#,
fuzzyDating: #echo ['!1','!0'][$sg_var('FUZZY_DATING')]#,
timeZero: #echo ['!1','!0'][$sg_var('TRIM_ZERO')]#,
datePreset: "$sg_str('DATE_PRESET', '%x')",
timePreset: "$sg_str('TIME_PRESET', '%I:%M %p')",
posterSortby: "$sg_var('POSTER_SORTBY')",
posterSortdir: #echo ['!1','!0'][$sg_var('POSTER_SORTDIR', True)]#,
fuzzydate: ".$fuzzydate",
};
</script>
@ -33,33 +35,33 @@
#set $tab = 1
#set $selected = ' selected="selected"'
#if 'poster' == $layout
<div>
<div id="top-row">
<span>Sort By:
<select id="postersort" class="form-control form-control-inline input-sm" tabindex="$tab#set $tab += 1#">
<option value="name" data-sort="$sbRoot/setPosterSortBy/?sort=name"#echo $selected if 'name' == sickbeard.POSTER_SORTBY else ''#>Name</option>
<option value="date" data-sort="$sbRoot/setPosterSortBy/?sort=date"#echo $selected if 'date' == sickbeard.POSTER_SORTBY else ''#>Next Episode</option>
<option value="network" data-sort="$sbRoot/setPosterSortBy/?sort=network"#echo $selected if 'network' == sickbeard.POSTER_SORTBY else ''#>Network</option>
<option value="progress" data-sort="$sbRoot/setPosterSortBy/?sort=progress"#echo $selected if 'progress' == sickbeard.POSTER_SORTBY else ''#>Progress</option>
<option value="quality" data-sort="$sbRoot/setPosterSortBy/?sort=quality"#echo $selected if 'quality' == sickbeard.POSTER_SORTBY else ''#>Quality</option>
<option value="name" data-sort="$sbRoot/setPosterSortBy/?sort=name"#echo $selected if 'name' == $sg_str('POSTER_SORTBY', 'name') else ''#>Name</option>
<option value="date" data-sort="$sbRoot/setPosterSortBy/?sort=date"#echo $selected if 'date' == $sg_str('POSTER_SORTBY') else ''#>Next Episode</option>
<option value="network" data-sort="$sbRoot/setPosterSortBy/?sort=network"#echo $selected if 'network' == $sg_str('POSTER_SORTBY') else ''#>Network</option>
<option value="progress" data-sort="$sbRoot/setPosterSortBy/?sort=progress"#echo $selected if 'progress' == $sg_str('POSTER_SORTBY') else ''#>Progress</option>
<option value="quality" data-sort="$sbRoot/setPosterSortBy/?sort=quality"#echo $selected if 'quality' == $sg_str('POSTER_SORTBY') else ''#>Quality</option>
</select>
</span>
<span style="margin-left:5px">Sort Order:
<select id="postersortdirection" class="form-control form-control-inline input-sm" tabindex="$tab#set $tab += 1#">
<option value="true" data-sort="$sbRoot/setPosterSortDir/?direction=1"#echo $selected if 1 == sickbeard.POSTER_SORTDIR else ''#>Asc</option>
<option value="false" data-sort="$sbRoot/setPosterSortDir/?direction=0"#echo $selected if 0 == sickbeard.POSTER_SORTDIR else ''#>Desc</option>
<option value="true" data-sort="$sbRoot/setPosterSortDir/?direction=1"#echo $selected if 1 == $sg_var('POSTER_SORTDIR', 1) else ''#>Asc</option>
<option value="false" data-sort="$sbRoot/setPosterSortDir/?direction=0"#echo $selected if 0 == $sg_var('POSTER_SORTDIR') else ''#>Desc</option>
</select>
</span>
<span style="margin-left:5px">Layout:
<span style="margin-left:5px" class="no-marginr">Layout:
#else
<span class="pull-right">Layout:
<span id="top-row" class="pull-right no-marginr">Layout:
#end if
<select style="margin-bottom:10px" name="layout" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;" tabindex="$tab#set $tab += 1#">
<option value="$sbRoot/setHomeLayout/?layout=poster"#echo $selected if 'poster' == sickbeard.HOME_LAYOUT else ''#>Poster</option>
<option value="$sbRoot/setHomeLayout/?layout=small"#echo $selected if 'small' == sickbeard.HOME_LAYOUT else ''#>Small Poster</option>
<option value="$sbRoot/setHomeLayout/?layout=banner"#echo $selected if 'banner' == sickbeard.HOME_LAYOUT else ''#>Banner</option>
<option value="$sbRoot/setHomeLayout/?layout=simple"#echo $selected if 'simple' == sickbeard.HOME_LAYOUT else ''#>Simple</option>
<option value="$sbRoot/setHomeLayout/?layout=poster"#echo $selected if 'poster' == $sg_str('HOME_LAYOUT', 'poster') else ''#>Poster</option>
<option value="$sbRoot/setHomeLayout/?layout=small"#echo $selected if 'small' == $sg_str('HOME_LAYOUT') else ''#>Small Poster</option>
<option value="$sbRoot/setHomeLayout/?layout=banner"#echo $selected if 'banner' == $sg_str('HOME_LAYOUT') else ''#>Banner</option>
<option value="$sbRoot/setHomeLayout/?layout=simple"#echo $selected if 'simple' == $sg_str('HOME_LAYOUT') else ''#>Simple</option>
</select>
</span>
@ -89,7 +91,7 @@
##
#for $curLoadingShow in $sickbeard.showQueueScheduler.action.loadingShowList
##
#if $curLoadingShow.show != None and $curLoadingShow.show in $sickbeard.showList
#if $curLoadingShow.show != None and $curLoadingShow.show in $sg_str('showList')
#continue
#end if
##
@ -116,7 +118,7 @@
#set $cur_total = 0
#set $download_stat_tip = ''
#set $display_status = $curShow.status
#set $display_name = (re.sub('^((?:A(?!\s+to)n?)|The)\s(\w)', r'<span class="article">\1</span> \2', $curShow.name), $curShow.name)[$sickbeard.SORT_ARTICLE]
#set $display_name = (re.sub('^((?:A(?!\s+to)n?)|The)\s(\w)', r'<span class="article">\1</span> \2', $curShow.name), $curShow.name)[$sg_var('SORT_ARTICLE')]
#if None is not $display_status
#if re.search(r'(?i)(?:new|returning)\s*series', $curShow.status)
#set $display_status = 'Continuing'
@ -272,7 +274,7 @@
<tbody>
#for $curLoadingShow in $sickbeard.showQueueScheduler.action.loadingShowList
#if $curLoadingShow.show != None and $curLoadingShow.show in $sickbeard.showList
#if $curLoadingShow.show != None and $curLoadingShow.show in $sg_str('showList')
#continue
#end if
##
@ -302,7 +304,7 @@
#set $cur_downloaded = 0
#set $cur_total = 0
#set $download_stat_tip = ''
#set $display_name = (re.sub('^((?:A(?!\s+to)n?)|The)\s(\w)', r'<span class="article">\1</span> \2', $curShow.name), $curShow.name)[$sickbeard.SORT_ARTICLE]
#set $display_name = (re.sub('^((?:A(?!\s+to)n?)|The)\s(\w)', r'<span class="article">\1</span> \2', $curShow.name), $curShow.name)[$sg_var('SORT_ARTICLE')]
##
#if $curShow.indexerid in $show_stat
#set $cur_airs_next = $show_stat[$curShow.indexerid]['ep_airs_next']
@ -437,4 +439,4 @@
##
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_bottom.tmpl')

View file

@ -1,22 +1,24 @@
#import sickbeard
#from sickbeard.common import *
<% def sg_var(varname, default=False): return getattr(sickbeard, varname, default) %>#slurp#
<% def sg_str(varname, default=''): return getattr(sickbeard, varname, default) %>#slurp#
##
#set global $title = 'Existing Show'
#set global $header = 'Existing Show'
#set global $title = 'Import'
#set global $header = $title
#set global $sbPath = '../..'
#set global $statpath = '../..'
#set global $topmenu = 'home'
##
#import os.path
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_top.tmpl')
<script type="text/javascript" charset="utf-8">
<!--
var config = { sortArticle: #echo ['!1','!0'][$sickbeard.SORT_ARTICLE]# }
var config = { sortArticle: #echo ['!1','!0'][$sg_var('SORT_ARTICLE')]# }
\$.sgSid = '$kwargs.get('sid', '')';
\$.sgHashDir = '$kwargs.get('hash_dir', '')';
\$(document).ready(function(){
\$('#tabs').tabs({ collapsible: !0, selected: #echo ('0','-1')[any($sickbeard.ROOT_DIRS)]# });
\$('#tabs').tabs({ collapsible: !0, selected: #echo ('0', '-1')[any($sg_str('ROOT_DIRS'))]# });
});
//-->
</script>
@ -31,8 +33,9 @@ var config = { sortArticle: #echo ['!1','!0'][$sickbeard.SORT_ARTICLE]# }
<h1 class="title">$title</h1>
#end if
<image class="preload-image" style="position:absolute;top:-999px" src="$sbRoot/images/loading32<%= '-dark' if 'dark' == sickbeard.THEME_NAME else '' %>.gif" width="32" height="32" border="0">
<image class="preload-image" style="position:absolute;top:-999px" src="$sbRoot/images/loading32#echo ('', '-dark')['dark' == $sg_str('THEME_NAME', 'dark')]#.gif" width="32" height="32" border="0">
<h3>Existing show folders</h3>
<form id="addShowForm" method="post" action="$sbRoot/home/addShows/addNewShow" accept-charset="utf-8">
<span#if $kwargs.get('hash_dir', None)# class="hide"#end if#>
@ -50,12 +53,12 @@ var config = { sortArticle: #echo ['!1','!0'][$sickbeard.SORT_ARTICLE]# }
</ul>
<div id="tabs-1" class="existingtabs">
<div style="width: 430px; margin: 0px auto">
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_rootDirs.tmpl')
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_rootDirs.tmpl')
</div>
</div>
<div id="tabs-2">
<div class="stepDiv">
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_addShowOptions.tmpl')
<div class="stepDiv" style="padding-top:25px">
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_addShowOptions.tmpl')
</div>
</div>
</div>
@ -87,4 +90,4 @@ var config = { sortArticle: #echo ['!1','!0'][$sickbeard.SORT_ARTICLE]# }
</form>
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_bottom.tmpl')

View file

@ -18,36 +18,36 @@
<div id="addShowPortal">
<a class="btn btn-large" href="$sbRoot/home/addShows/new_show/">
<div class="button"><div class="icon-addnewshow square-32"></div></div>
<div class="button"><span style="font-size:32px"><i class="sgicon-addshow"></i></span></div>
<div class="buttontext">
<h3>Add new show</h3>
<p>Search a TV database for a show.</p>
<h3>Search</h3>
<p>find show at TV info source</p>
</div>
</a>
<a class="btn btn-large" href="$sbRoot/home/addShows/trakt_default/">
<div class="button"><div class="icon-addrecommendedshow square-32"></div></div>
<div class="button"><span style="font-size:32px"><i class="sgicon-trakt"></i></span></div>
<div class="buttontext">
<h3>Add from Trakt</h3>
<p>Browse trends, recommended and more.</p>
<h3>Trakt cards</h3>
<p>trends, tailored suggestions...</p>
</div>
</a>
<div style="clear:both;font-size:2px">&nbsp;</div>
<a class="btn btn-large" href="$sbRoot/home/addShows/existing_shows/">
<a class="btn btn-large" href="$sbRoot/home/addShows/import_shows/">
<div class="button"><div class="icon-addexistingshow square-32"></div></div>
<div class="buttontext">
<h3>Add existing shows</h3>
<p>Scan parent folders to import into SickGear.</p>
<h3>Import</h3>
<p>existing shows</p>
</div>
</a>
<a class="btn btn-large" href="$sbRoot/home/addShows/imdb_default/">
<div class="button"><div class="img-imdb square-32"></div></div>
<div class="button"><span style="font-size:32px"><i class="sgicon-imdb"></i></span></div>
<div class="buttontext">
<h3>Add from IMDb</h3>
<p>Browse popular for a show to add.</p>
<h3>IMDb cards</h3>
<p>popular decades, watchlists...</p>
</div>
</a>
@ -57,16 +57,16 @@
<a class="btn btn-large" href="$sbRoot/home/addShows/anime_default/">
<div class="button"><div class="img-anime square-32"></div></div>
<div class="buttontext">
<h3>Add anime show</h3>
<p>Browse anime to add.</p>
<h3>Anime cards</h3>
<p>browse anime to add</p>
</div>
</a>
#else
<div class="buttontext" style="margin:0px 7px 14px;padding:0 5px 0 28px">
<div class="buttontext" style="margin:0px 7px 14px;padding:0 5px 0 25px">
<div class="button"><div class="img-anime square-32"></div></div>
<div class="buttontext">
<h3>Add anime show</h3>
<p>To use, enable AniDB in <a href="$sbRoot/config/anime/">Config/Anime</a>.</p>
<h3>Anime cards</h3>
<p>to use, enable AniDB <a href="$sbRoot/config/anime/">here</a></p>
</div>
</div>
#end if

View file

@ -5,6 +5,8 @@
#from sickbeard.common import *
#from sickbeard import sbdatetime
#from sickbeard.helpers import anon_url
<% def sg_var(varname, default=False): return getattr(sickbeard, varname, default) %>#slurp#
<% def sg_str(varname, default=''): return getattr(sickbeard, varname, default) %>#slurp#
##
#set global $title='Browse %s Shows' % $browse_type
#set global $header='Browse Shows'
@ -13,7 +15,7 @@
#set global $page_body_attr = 'browse-list'
##
#import os.path
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_top.tmpl')
<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?v=$sbPID"></script>
@ -51,7 +53,7 @@
premiered: '[data-premiered] parseInt',
name: function( itemElem ) {
var name = \$( itemElem ).attr('data-name') || '';
#if not $sickbeard.SORT_ARTICLE:
#if not $sg_var('SORT_ARTICLE'):
name = name.replace(/^(?:(?:A(?!\s+to)n?)|The)\s(\w)/i, '$1');
#end if
return name.toLowerCase();
@ -128,21 +130,6 @@
<option value="*" selected="selected">All<%= ' (%d)' % count_all_shows %></option>
<option value=".notinlibrary">Not In Library<%= ' (%d)' % (count_all_shows - count_inlibrary) %></option>
<option value=".inlibrary">In Library<%= ' (%d)' % count_inlibrary %></option>
#if $browse_type in ('IMDb', 'Trakt', 'AniDB')
<optgroup label="Other Services">
#if 'IMDb' == $browse_type
<option value="trakt_default">Trakt</option>
#elif 'Trakt' == $browse_type
<option value="imdb_default">IMDb</option>
#elif 'AniDB' == $browse_type
<option value="imdb_default">IMDb</option>
<option value="trakt_default">Trakt</option>
#end if
#if $sickbeard.USE_ANIDB and $browse_type in ('IMDb', 'Trakt')
<option value="anime_default">AniDB</option>
#end if
</optgroup>
#end if
#set $selected = ' class="selected"'
#if 'Trakt' == $browse_type
<optgroup label="Trakt">
@ -162,11 +149,11 @@
<option value="trakt_played?period=year"#echo ('', selected)['played-year' == $mode]#>Most Played</option>
<option value="trakt_collected?period=year"#echo ('', selected)['collected-year' == $mode]#>Most Collected</option>
</optgroup>
#if any($sickbeard.TRAKT_ACCOUNTS)
#if any($sg_var('TRAKT_ACCOUNTS', []))
<optgroup label="Trakt recommended">
#for $account in $sickbeard.TRAKT_ACCOUNTS
#if $sickbeard.TRAKT_ACCOUNTS[$account].active and $sickbeard.TRAKT_ACCOUNTS[$account].name
<option value="trakt_recommended?account=$account"#echo ('', selected)[('recommended-%s' % $account) == $mode]#>for $sickbeard.TRAKT_ACCOUNTS[$account].name</option>
#for $account in $sg_var('TRAKT_ACCOUNTS')
#if $sg_var('TRAKT_ACCOUNTS').get($account).active and $sg_var('TRAKT_ACCOUNTS').get($account).name
<option value="trakt_recommended?account=$account"#echo ('', selected)[('recommended-%s' % $account) == $mode]#>for $sg_var('TRAKT_ACCOUNTS').get($account).name</option>
#end if
#end for
#else
@ -186,8 +173,8 @@
#end if
#if not hasattr($sickbeard, 'IMDB_ACCOUNTS')#<optgroup label="Restart SickGear to reveal"><option>new options after restart</option></optgroup>#else#
<optgroup label="IMDb Watchlists">
#if any($sickbeard.IMDB_ACCOUNTS)
#for $i, $v in $enumerate($sickbeard.IMDB_ACCOUNTS)
#if any($sg_var('IMDB_ACCOUNTS', []))
#for $i, $v in $enumerate($sg_var('IMDB_ACCOUNTS'))
#if not $i % 2
#set $id = $v
#elif not $v.startswith('(Off) ')
@ -256,11 +243,11 @@
</div>
<div class="show-title">
<%= ((re.sub('^((?:A(?!\s+to)n?)|The)\s(\w)', r'<span class="article">\1</span> \2', this_show['title']), this_show['title'])[sickbeard.SORT_ARTICLE], '<span>&nbsp;</span>')['' == this_show['title']] %>
#echo ((re.sub('^((?:A(?!\s+to)n?)|The)\s(\w)', r'<span class="article">\1</span> \2', $this_show['title']), $this_show['title'])[$sg_var('SORT_ARTICLE')], '<span>&nbsp;</span>')['' == $this_show['title']]#
</div>
<div class="clearfix">
<p>$this_show['rating']% <img src="$sbRoot/images/heart.png"><i>$this_show['votes'] votes</i></p>
<p>$this_show['rating']%<i class="heart icon-glyph"></i><i>$this_show['votes'] votes</i></p>
#if 'url_tvdb' in $this_show and $this_show['url_tvdb']:
<a class="service" href="<%= anon_url(this_show['url_tvdb']) %>" onclick="window.open(this.href, '_blank'); return false;"
title="View <span class='boldest'>tvdb</span> detail for <span style='color: rgb(66, 139, 202)'>$title_html</span>">
@ -269,7 +256,7 @@
<div class="browse-add-show-holder">
#if ':' in $this_show['show_id']:
<p style="line-height: 1.5; padding: 2px 5px 3px" title="<%= '%s added' % ('TVRage', 'theTVDB')['1' == this_show['show_id'][:1]] %>">In library</p>
<p style="line-height:1.5;padding:2px 5px 3px" title="#echo '%s added' % $sickbeard.indexerApi(this_show['show_id'][:1]).config.get('name')#">In library</p>
#else
<a href="$sbRoot/home/addShows/add${browse_type}Show?indexer_id=${this_show['show_id']}&amp;showName=${urllib.quote($this_show['title'].encode("utf-8"))}" class="btn btn-xs">Add Show</a>
#end if
@ -308,4 +295,4 @@ window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes
//-->
</script>
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_bottom.tmpl')

View file

@ -1,14 +1,16 @@
#import sickbeard
#from sickbeard.helpers import anon_url
##
#set global $header = 'New Show'
#set global $title = 'New Show'
#set global $header = 'Add from TV info source'
#set global $title = $header
#set global $sbPath = '../..'
#set global $statpath = '../..'
#set global $topmenu = 'home'
<% def sg_var(varname, default=False): return getattr(sickbeard, varname, default) %>#slurp#
<% def sg_str(varname, default=''): return getattr(sickbeard, varname, default) %>#slurp#
##
#import os.path
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_top.tmpl')
#set indexer_count = len([$i for $i in $sickbeard.indexerApi().indexers if $sickbeard.indexerApi(i).config.get('active', False) and not $sickbeard.indexerApi(i).config.get('defunct', False)]) + 1
@ -27,7 +29,7 @@
<h1 class="title">$title</h1>
#end if
<image class="preload-image" style="position:absolute;top:-999px" src="$sbRoot/images/loading32<%= '-dark' if 'dark' == sickbeard.THEME_NAME else '' %>.gif" width="32" height="32" border="0">
<image class="preload-image" style="position:absolute;top:-999px" src="$sbRoot/images/loading32#echo ('', '-dark')['dark' == $sg_str('THEME_NAME', 'dark')]#.gif" width="32" height="32" border="0">
<div id="newShowPortal">
@ -40,7 +42,7 @@
<legend class="legendStep"><p>#if $use_provided_info#Using known show information#else#Find show at TV info source#end if#</p></legend>
<div class="stepDiv">
<input type="hidden" id="indexer_timeout" value="$sickbeard.INDEXER_TIMEOUT" />
<input type="hidden" id="indexer_timeout" value="$sg_var('INDEXER_TIMEOUT', 20)" />
<input type="hidden" id="indexer_count" value="$indexer_count" />
#if $use_provided_info
@ -58,7 +60,7 @@
</style>
<input type="hidden" id="providedIndexer" value="$provided_indexer" />
#end if
<input type="text" id="nameToSearch" value="$default_show_name" class="form-control form-control-inline input-sm input350" />
<input type="text" id="nameToSearch" value="$default_show_name" placeholder="Enter a show name, TVDB ID, IMDb Url, or IMDb ID" class="form-control form-control-inline input-sm input350" />
&nbsp;
<span style="float:right">
<select name="indexerLang" id="indexerLangSelect" class="form-control form-control-inline input-sm">
@ -77,7 +79,7 @@
<input class="btn btn-inline" type="button" id="searchName" value="Search" />
</span>
<br />
<p style="margin:5px 0 15px">Enter show name, TVDB ID, IMDb Url, or IMDb ID.&nbsp;&nbsp;<b>*</b>SickGear supports english, language is used for show/episode data</p>
<p style="margin:5px 0 15px"><span class="add-tip">Enter show name, TVDB ID, IMDb Url, or IMDb ID.&nbsp;&nbsp;</span><b>*</b>SickGear supports english, language is used for show/episode data</p>
<div id="searchResults" style="height: 100%"></div>
#end if
@ -94,7 +96,7 @@
Pre-chosen Destination Folder: <b>$provided_show_dir</b> <br />
<input type="hidden" id="fullShowPath" name="fullShowPath" value="$provided_show_dir" /><br />
#else
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_rootDirs.tmpl')
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_rootDirs.tmpl')
#end if
</div>
<div style="clear:both">&nbsp;</div>
@ -103,7 +105,7 @@
<fieldset class="sectionwrap step-three" style="visibility:hidden">
<legend class="legendStep"><p>Set custom options</p></legend>
<div class="stepDiv">
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_addShowOptions.tmpl')
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_addShowOptions.tmpl')
</div>
<div style="clear:both">&nbsp;</div>
</fieldset>
@ -128,4 +130,4 @@
</div></div>
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_bottom.tmpl')

View file

@ -1,14 +1,25 @@
#import sickbeard
#from sickbeard.common import *
#from sickbeard import subtitles
<div class="stepDiv linefix">
##
#set $checked = ' checked="checked"'
#set $selected = ' selected="selected"'
##
<% def sg_var(varname, default=False): return getattr(sickbeard, varname, default) %>#slurp#
<% def sg_str(varname, default=''): return getattr(sickbeard, varname, default) %>#slurp#
<div class="stepDiv linefix">
<div style="float:right;margin:-30px -15px 20px 15px;font-size:12px;line-height:27px;">
<span class="grey-text">To reuse options below when adding more shows&nbsp;<input class="btn btn-inline" type="button" id="saveDefaultsButton" value="Save as defaults" disabled="disabled" /></span>
</div>
<div class="field-pair">
<label for="statusSelect">
<span class="component-title">Initial episode status</span>
<span class="component-desc">
<select name="defaultStatus" id="statusSelect" class="form-control form-control-inline input-sm">
#for $curStatus in [$SKIPPED, $WANTED, $ARCHIVED, $IGNORED]:
<option value="$curStatus" #if $sickbeard.STATUS_DEFAULT == $curStatus then 'selected="selected"' else ''#>$statusStrings[$curStatus]</option>
<option value="$curStatus"#if $sg_var('STATUS_DEFAULT', SKIPPED) == $curStatus then $selected else ''#>$statusStrings[$curStatus]</option>
#end for
</select>
<span>set the initial status of missing episodes</span>
@ -21,10 +32,10 @@
</div>
<div class="field-pair">
#set $qualities = $Quality.splitQuality($sickbeard.QUALITY_DEFAULT)
#set $qualities = $Quality.splitQuality($sg_var('QUALITY_DEFAULT', SD))
#set global $anyQualities = $qualities[0]
#set global $bestQualities = $qualities[1]
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_qualityChooser.tmpl')
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_qualityChooser.tmpl')
</div>
#try:
@ -33,14 +44,14 @@
<span class="component-title">From season 1 forward, set</span>
<span class="component-desc">
<label for="wanted_begin" style="padding-bottom:10px">
<input type="number" name="wanted_begin" id="wanted_begin" value="$sickbeard.WANTED_BEGIN_DEFAULT" class="form-control input-sm input75">
<input type="number" name="wanted_begin" id="wanted_begin" value="$sg_str('WANTED_BEGIN_DEFAULT')" class="form-control input-sm input75">
<span>episodes as wanted (10 ... 0, and where -1 is whole first season)</span>
</label>
</span>
<span class="component-title">From latest going back, set</span>
<span class="component-desc">
<label for="wanted_latest">
<input type="number" name="wanted_latest" id="wanted_latest" value="$sickbeard.WANTED_LATEST_DEFAULT" class="form-control input-sm input75">
<input type="number" name="wanted_latest" id="wanted_latest" value="$sg_str('WANTED_LATEST_DEFAULT')" class="form-control input-sm input75">
<span>episodes as wanted (10 ... 0, and where -1 is whole latest season)</span>
</label>
</span>
@ -50,13 +61,13 @@
#pass
#end try
<div class="field-pair #if $sickbeard.SHOWLIST_TAGVIEW != 'custom' then 'hidden' else ''#" style="margin-top:10px">
<div class="field-pair #if $sg_str('SHOWLIST_TAGVIEW') != 'custom' then 'hidden' else ''#" style="margin-top:10px">
<label for="tag">
<span class="component-title">Place show 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 == $getattr(sickbeard, 'SHOW_TAG_DEFAULT', $getattr(sickbeard, 'DEFAULT_SHOW_TAG', None)) then 'selected="selected"' else ''#>$tag</option>
#for $tag in $sg_var('SHOW_TAGS', []):
<option value="$tag"#if $tag == $getattr(sickbeard, 'SHOW_TAG_DEFAULT', $getattr(sickbeard, 'DEFAULT_SHOW_TAG', None)) then $selected else ''#>$tag</option>
#end for
</select>
<span>and display on the show list page under this section</span>
@ -68,7 +79,7 @@
<label for="flatten_folders">
<span class="component-title">Flat folder structure</span>
<span class="component-desc">
<input class="cb" type="checkbox" name="flatten_folders" id="flatten_folders" #if $sickbeard.FLATTEN_FOLDERS_DEFAULT then "checked=\"checked\"" else ""# />
<input class="cb" type="checkbox" name="flatten_folders" id="flatten_folders"#if $sg_var('FLATTEN_FOLDERS_DEFAULT') then $checked else ''# />
<p>do not create sub folders</p>
</span>
</label>
@ -78,18 +89,18 @@
<label for="scene">
<span class="component-title">Scene numbering</span>
<span class="component-desc">
<input type="checkbox" name="scene" id="scene" #if $sickbeard.SCENE_DEFAULT then "checked=\"checked\"" else ""# />
<input type="checkbox" name="scene" id="scene"#if $sg_var('SCENE_DEFAULT') then $checked else ''# />
<p>search for episodes numbered by scene groups instead of by the TV network<span id="scene-maps-found" style="display:none" class="grey-text"> (scene numbers found)</span></p>
</span>
</label>
</div>
#if $sickbeard.USE_SUBTITLES:
#if $sg_var('USE_SUBTITLES'):
<div class="field-pair alt">
<label for="subtitles">
<span class="component-title">Subtitles</span>
<span class="component-desc">
<input type="checkbox" name="subtitles" id="subtitles" #if $sickbeard.SUBTITLES_DEFAULT then "checked=\"checked\"" else ""# />
<input type="checkbox" name="subtitles" id="subtitles"#if $sg_var('SUBTITLES_DEFAULT') then $checked else ''# />
<p>download subtitles for this show</p>
</span>
</label>
@ -101,27 +112,18 @@
<label for="anime">
<span class="component-title">Show is anime</span>
<span class="component-desc">
<input type="checkbox" name="anime" id="anime" #if $sickbeard.ANIME_DEFAULT or $kwargs.get('is_anime') then "checked=\"checked\"" else ""# />
<input type="checkbox" name="anime" id="anime"#if $sg_var('ANIME_DEFAULT') or $kwargs.get('is_anime') then $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>
#end if
<div class="field-pair alt" style="margin-top:20px">
<label for="saveDefaultsButton">
<span class="component-title">Save options as defaults</span>
<span class="component-desc">
<input class="btn btn-inline" type="button" id="saveDefaultsButton" value="Save Defaults" disabled="disabled" />
<p>reuse the above options when adding more shows</p>
</span>
</label>
</div>
</div>
#if $enable_anime_options
#import sickbeard.blackandwhitelist
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_blackwhitelist.tmpl')
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_blackwhitelist.tmpl')
#else
<input type="hidden" name="anime" id="anime" value="0" />
#end if

View file

@ -3,6 +3,8 @@
#import re
#from sickbeard import db, sbdatetime
#from sickbeard.common import *
<% def sg_var(varname, default=False): return getattr(sickbeard, varname, default) %>#slurp#
<% def sg_str(varname, default=''): return getattr(sickbeard, varname, default) %>#slurp#
##
</div><!-- /content -->
</div><!-- /contentWrapper -->
@ -26,8 +28,8 @@
##
#set $sql_result = $my_db.select($sql_statement)
##
#set $shows_total = len($sickbeard.showList)
#set $shows_active = len([show for show in $sickbeard.showList if 0 == show.paused and 'Ended' != show.status])
#set $shows_total = len($sg_str('showList'))
#set $shows_active = len([show for show in $sg_str('showList') if 0 == show.paused and 'Ended' != show.status])
##
#if $sql_result:
#set $ep_snatched = $sql_result[0]['ep_snatched']

View file

@ -0,0 +1,125 @@
#import datetime
#import sickbeard
#from sickbeard import network_timezones, sbdatetime, subtitles
#from sickbeard.common import Overview, Quality, statusStrings, ARCHIVED, UNAIRED, SUBTITLED
#from lib import subliminal
<% def sg_var(varname, default=False): return getattr(sickbeard, varname, default) %>#slurp#
<% def sg_str(varname, default=''): return getattr(sickbeard, varname, default) %>#slurp#
#######
#set $network_timezone = $network_timezones.get_network_timezone($show.network)
#set $network_time = $network_timezones.parse_time($show.airs)
#set $odd = 0
#set $fuzzydate = 'airdate'
#set $scene, $scene_anime = (False, False)
#if not $show.air_by_date and not $show.is_sports and not $show.is_anime and $show.is_scene
#set $scene = True
#elif not $show.air_by_date and not $show.is_sports and $show.is_anime and $show.is_scene
#set $scene_anime = True
#end if
#set $attr_title_ep = ('', ' (Anime #)')[$show.is_anime]
##
#set $eps = $getVar('episodes', None) or [$episode]
#for $ep in $eps
#set $ep_str = '%sx%s' % ($ep['season'], $ep['episode'])
#set $epLoc = $ep['location']
#set never_aired = 0 < int($ep['season']) and 1 == int($ep['airdate'])
<tr class="#echo ($Overview.overviewStrings[$ep_cats[$ep_str]], 'airdate-never')[$never_aired]##echo ('', ' archived')[ARCHIVED == int($ep['status'])]#">
<td class="col-checkbox">
#if $UNAIRED != int($ep['status']) and not $never_aired
<input type="checkbox" class="epCheck" id="$ep_str" name="$ep_str">
#end if
</td>
#set $nfo, $nfo_img = (('No', '-no'), ('Yes', ''))[int($ep['hasnfo'])]
#set $tbn, $tbn_img = (('No', '-no'), ('Yes', ''))[int($ep['hastbn'])]
<td align="center" class="meta"><img src="$sbRoot/images/nfo${nfo_img}.gif" alt="$nfo" title="$nfo" width="23" height="11" /><br />
<img src="$sbRoot/images/tbn${tbn_img}.gif" alt="$tbn" title="$tbn" width="23" height="11" /></td>
#if $epLoc and $show._location and $epLoc.lower().startswith($show._location.lower())
#set $epLoc = $epLoc[len($show._location)+1:]
#elif $epLoc and (not $epLoc.lower().startswith($show._location.lower()) or not $show._location)
#set $epLoc = $epLoc
#end if
#set $episode = str($ep['episode']) + ('', ' (%s)' % $ep['absolute_number'])[$show.is_anime]
#set $downloaded = ('', '<strong>file</strong> %s<br /><strong>size</strong> %s' % ($epLoc, $sickbeard.helpers.human($ep['file_size'])))['' != $epLoc and None != $epLoc]
#set $attr=(('', 'Ep # %s' % $attr_title_ep)[bool($attr_title_ep)], $downloaded)[any($downloaded)]
<td align="center">
<span style="white-space:nowrap"#if $attr# class="addQTip" title="$attr"#end if#>$episode</span>
</td>
#slurp
#if $scene
#set $dfltSeas, $dfltEpis = (0, 0) if ($ep['season'], $ep['episode']) not in $xem_numbering else $xem_numbering[($ep['season'], $ep['episode'])]
<td align="center">
<input type="text" placeholder="#echo '%sx%s' % ($dfltSeas, $dfltEpis)#" size="6" maxlength="8"
class="sceneSeasonXEpisode form-control input-scene" data-for-season="$ep['season']" data-for-episode="$ep['episode']"
id="sceneSeasonXEpisode_#echo '%s_%s_%s' % ($show.indexerid, $ep['season'], $ep['episode'])#"
title="Change the value here if scene numbering differs from the indexer episode numbering"
#if ($ep['season'], $ep['episode']) in $scene_numbering
#set $scSeas, $scEpis = $scene_numbering[($ep['season'], $ep['episode'])]
value="#echo '%sx%s' % ($scSeas, $scEpis)#"
#else
value=""
#end if
style="padding:0; text-align:center; max-width:60px">
</td>
#elif $scene_anime
#set $dfltAbsolute = 0 if $ep['absolute_number'] not in $xem_absolute_numbering else $xem_absolute_numbering[$ep['absolute_number']]
<td align="center">
<input type="text" placeholder="$dfltAbsolute" size="6" maxlength="8"
class="sceneAbsolute form-control input-scene" data-for-absolute="$ep['absolute_number']"
id="sceneAbsolute_#echo '%s_%s' % ($show.indexerid, $ep['absolute_number'])#"
title="Change the value here if scene absolute numbering differs from the indexer absolute numbering"
#if $ep['absolute_number'] in $scene_absolute_numbering
value="$scene_absolute_numbering[$ep['absolute_number']]"
#else
value=""
#end if
style="padding:0; text-align:center; max-width:60px" />
</td>
#end if
#slurp
<td class="col-name">
<img src="$sbRoot/images/info32.png" width="16" height="16" alt="" class="plotInfo#echo '%s" />' %\
('None opacity40', ('" id="plot_info_%s_%s_%s' % ($show.indexerid, $ep['season'], $ep['episode'])))[None is not $ep['description'] and '' != $ep['description']]#
#if not $ep['name'] or 'TBA' == $ep['name']#<em class="tba grey-text">TBA</em>#else#$ep['name']#end if#
</td>
<td class="col-airdate">
<span class="$fuzzydate" data-airdate="$ep['airdate']"#if $sg_var('FUZZY_DATING')# data-fulldate="$sbdatetime.sbdatetime.sbfdate(dt=$datetime.date.fromordinal($ep['airdate']), d_preset='%A, %B %d, %Y')"#end if#>#if 1 == int($ep['airdate']) then 'never' else $sbdatetime.sbdatetime.sbfdate($sbdatetime.sbdatetime.convert_to_setting($network_timezones.parse_date_time($ep['airdate'], $network_time, $network_timezone)))#</span>
</td>
#if $sg_var('USE_SUBTITLES') and $show.subtitles
<td class="col-subtitles" align="center">
#if $ep['subtitles']
#for $sub_lang in subliminal.language.language_list($ep['subtitles'].split(','))
#if '' != sub_lang.alpha2
<img src="$sbRoot/images/flags/${sub_lang.alpha2}.png" width="16" height="11" alt="${sub_lang}" />
#end if
#end for
#end if
</td>
#end if
#slurp
#set $curStatus, $curQuality = $Quality.splitCompositeStatus(int($ep['status']))
#if Quality.NONE != $curQuality
<td class="col-status">#if SUBTITLED == $curStatus#<span class="addQTip" title="$statusStrings[$curStatus]"><i class="sgicon-subtitles" style="vertical-align:middle"></i></span>#else#$statusStrings[$curStatus].replace('Downloaded', '')#end if# <span class="quality $Quality.get_quality_css($curQuality)#if $downloaded# addQTip" title="$downloaded#end if#">$Quality.qualityStrings[$curQuality]</span></td>
#else
<td class="col-status">$statusStrings[$curStatus]</td>
#end if
<td class="col-search">
#if 0 != int($ep['season'])
#if (int($ep['status']) in $Quality.SNATCHED or int($ep['status']) in $Quality.DOWNLOADED) and $sg_var('USE_FAILED_DOWNLOADS')
<a class="epRetry" id="$ep_str" name="$ep_str" href="$sbRoot/home/retryEpisode?show=$show.indexerid&amp;season=$ep['season']&amp;episode=$ep['episode']"><img src="$sbRoot/images/search16.png" height="16" alt="retry" title="Retry download" /></a>
#else
<a class="epSearch" id="$ep_str" name="$ep_str" href="$sbRoot/home/searchEpisode?show=$show.indexerid&amp;season=$ep['season']&amp;episode=$ep['episode']"><img src="$sbRoot/images/search16.png" width="16" height="16" alt="search" title="Manual search" /></a>
#end if
#end if
#slurp
#if $sg_var('USE_SUBTITLES') and $show.subtitles and len(set(str($ep['subtitles']).split(',')).intersection(set($subtitles.wantedLanguages()))) < len($subtitles.wantedLanguages()) and $ep['location']
<a class="epSubtitlesSearch" href="$sbRoot/home/searchEpisodeSubtitles?show=$show.indexerid&amp;season=$ep['season']&amp;episode=$ep['episode']"><img src="$sbRoot/images/closed_captioning.png" height="16" alt="search subtitles" title="Search subtitles" /></a>
#end if
</td>
</tr>
#end for

View file

@ -0,0 +1,59 @@
#import sickbeard
#from sickbeard.helpers import anon_url
<% def mainvar(varname, default=False): return getattr(sickbeard, varname, default) %>
<% def mainstr(varname, default=''): return getattr(sickbeard, varname, default) %>
##
#set $panel_title = {'viewart0': 'Poster <em>left</em>', 'viewart1': 'Poster <em>right</em>', 'viewart2': 'No poster',
'viewart3': 'Open gear', 'viewart4': 'Backart only',
'translucent_on': 'Translucency <em>on</em>', 'translucent_off': 'Translucency <em>off</em>',
'rateart0': 'Random (default)', 'rateart1': 'Group random',
'rateart2': 'Always display', 'rateart3': 'Avoid image',
'backart_on': 'Backart <em>on</em>', 'backart_off': 'Backart <em>off</em>',
'viewmode0': 'Regular view', 'viewmode1': 'Proview I', 'viewmode2': 'Proview II',
'viewmode3': 'Set/Save art random/avoids'}
#set $init_title_translucent = $panel_title['translucent_' + ('off', 'on')[$mainvar('DISPLAY_SHOW_BACKGROUND_TRANSLUCENT')]]
#set $init_title_backart = $panel_title['backart_' + ('off', 'on')[$mainvar('DISPLAY_SHOW_BACKGROUND') and $has_art]]
#set $init_title_view = $panel_title['viewmode%s' % ((1, 0)[not $mainvar('DISPLAY_SHOW_VIEWMODE')], $mainvar('DISPLAY_SHOW_VIEWMODE'))[$mainvar('DISPLAY_SHOW_BACKGROUND') and $has_art]]
##
<script>
config.panelTitles = $panel_title;
</script>
<script type="text/javascript" src="$sbRoot/js/livepanel.js?v=$sbPID"></script>
<div id="livepanel" class="off $getVar('fanart_panel', 'highlight2')">
<span class="over-layer0">
<span class="art-toggle oneof">
<i class="icon-glyph"></i>
<i class="icon-glyph"></i>
</span>
<span class="art-toggle-all"><i class="icon-glyph"></i></span>
<span class="art-toggle">
<i class="icon-glyph"></i>
<i class="icon-glyph rate-art"></i>
</span>
<i class="icon-glyph"></i>
<span class="art-toggle-all"><i class="icon-glyph last"></i></span>
</span>
<span id="viewmodes" class="over-layer1">
<span class="art-toggle oneof">
<a id="art-next" title="<span style='white-space:nowrap'>Next view</span>" href="#"><i class="icon-glyph"></i></a>
<a id="art-prev" title="<span style='white-space:nowrap'>Previous view</span>" href="#"><i class="icon-glyph"></i></a>
</span>
<span class="art-toggle-all"><a id="viewart" title="Poster left" href="#"><i class="icon-glyph"></i></a></span>
<span class="art-toggle">
<a id="translucent" title="$init_title_translucent" href="#"><i class="icon-glyph"></i></a>
<a id="rate-art" title="Random (default)" href="#"><i class="icon-glyph"></i></a>
</span>
#if $has_art
<a id="back-art" title="$init_title_backart" href="#"><i class="icon-glyph image"></i></a>
#else
#try
#set $link = $anon_url('https://fanart.tv/?s=', $clean_show_name, '&sect=1')
<a id="back-art" title="No art! Force full update or add one to fanart.tv if none available" href="$link" rel="noreferrer" onclick="window.open(this.href, '_blank'); return !1;"><i class="icon-glyph fatv"></i></a>
#except
<a id="back-art" title="No art available!" href="#"><i class="icon-glyph image"></i></a>
#end try
#end if
<span class="art-toggle-all"><a id="proview" title="$init_title_view" href="#"><i class="icon-glyph"></i></a></span>
</span>
</div>

View file

@ -5,7 +5,7 @@
<div class="field-pair">
<label for="qualityPreset" class="clearfix">
#set $overall_quality = $Quality.combineQualities($anyQualities, $bestQualities)
<span class="component-title input">Quality</span>
<span class="component-title input">Quality to download</span>
<span class="component-desc">
#set $selected = None
<select id="qualityPreset" name="quality_preset" class="form-control form-control-inline input-sm">
@ -14,7 +14,7 @@
<option value="$curPreset"#echo ('', $html_selected)[$curPreset == $overall_quality]##echo ('', ' style="padding-left:15px"')[$qualityPresetStrings[$curPreset].endswith('0p') and 'UHD' not in $qualityPresetStrings[$curPreset]]#>$qualityPresetStrings[$curPreset]</option>
#end for
</select>
<span>preferred episode quality to download</span>
<span>tip: select a quality then "Custom" for a default selection</span>
</span>
</label>
</div>
@ -22,8 +22,9 @@
<div id="customQualityWrapper">
<div id="customQuality" class="show-if-quality-custom" style="display:none">
<div class="component-group-desc tip-text">
<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>
<p>An <em class="highlight-text">Initial</em> quality downloads before any <em class="highlight-text">Upgrade to</em> selections are considered.</p>
<p>Deselect all <em class="highlight-text">Upgrade to</em> qualities to keep the first best <em class="highlight-text">Initial</em> release found.</p>
<p>All found <em class="highlight-text">Upgrade to</em> qualities download until the best.</p>
</div>
<span class="component-desc">
@ -48,6 +49,7 @@
#end for
</select><br />
<span>Ctrl + Click = toggle a quality</span>
<br /><span>Ctrl + Click = toggle a quality</span>
</div>
<div style="line-height:normal;padding-top:50px" id="quality-notes" class="tip-text">

View file

@ -1,6 +1,8 @@
#import sickbeard
#import urllib
#from sickbeard.helpers import anon_url
<% def sg_var(varname, default=False): return getattr(sickbeard, varname, default) %>#slurp#
<% def sg_str(varname, default=''): return getattr(sickbeard, varname, default) %>#slurp#
#slurp
<!DOCTYPE html>
<html>
@ -35,6 +37,7 @@
<meta name="msapplication-TileColor" content="#2b5797">
<meta name="msapplication-TileImage" content="$sbRoot/images/ico/mstile-144x144.png">
<meta name="msapplication-config" content="$sbRoot/css/browserconfig.xml">
<meta name="theme-color" content="#echo '#%s' % ('333', '15528F')['dark' == $sg_str('THEME_NAME', 'dark')]#">
<link rel="stylesheet" type="text/css" href="$sbRoot/css/lib/bootstrap.css?v=$sbPID"/>
<link rel="stylesheet" type="text/css" href="$sbRoot/css/browser.css?v=$sbPID" />
@ -42,7 +45,7 @@
<link rel="stylesheet" type="text/css" href="$sbRoot/css/lib/jquery.qtip-2.2.1.min.css?v=$sbPID"/>
<link rel="stylesheet" type="text/css" href="$sbRoot/css/lib/pnotify.custom.min.css?v=$sbPID" />
<link rel="stylesheet" type="text/css" href="$sbRoot/css/style.css?v=$sbPID"/>
<link rel="stylesheet" type="text/css" href="$sbRoot/css/${sickbeard.THEME_NAME}.css?v=$sbPID" />
<link rel="stylesheet" type="text/css" href="$sbRoot/css/${sg_str('THEME_NAME', 'dark')}.css?v=$sbPID" />
<script type="text/javascript" src="$sbRoot/js/lib/jquery-1.8.3.min.js?v=$sbPID"></script>
<script type="text/javascript" src="$sbRoot/js/lib/bootstrap.min.js?v=$sbPID"></script>
@ -63,13 +66,13 @@
<script type="text/javascript" src="$sbRoot/js/lib/jquery.confirm.js?v=$sbPID"></script>
<script type="text/javascript" src="$sbRoot/js/script.js?v=$sbPID"></script>
<script type="text/javascript" src="$sbRoot/js/inc_top.js?v=$sbPID"></script>
#if $sickbeard.FUZZY_DATING
#if $sg_var('FUZZY_DATING')
<script type="text/javascript" src="$sbRoot/js/moment/moment.min.js?v=$sbPID"></script>
<script type="text/javascript" src="$sbRoot/js/fuzzyMoment.js?v=$sbPID"></script>
#end if
<script type="text/javascript" charset="utf-8">
<!--
var sbRoot = '$sbRoot', anonURL = '$sickbeard.ANON_REDIRECT', themeSpinner = '#echo ('', '-dark')['dark' == $sickbeard.THEME_NAME]#',
var sbRoot = '$sbRoot', anonURL = '$sg_str('ANON_REDIRECT')', themeSpinner = '#echo ('', '-dark')['dark' == $sg_str('THEME_NAME', 'dark')]#',
top_image_html = '<img src="$sbRoot/images/top.gif" width="31" height="11" alt="Jump to top" />', topmenu = '$topmenu';
\$.SickGear = {Root: '${sbRoot}', PID: '${sbPID}'};
//-->
@ -86,6 +89,10 @@
#except
#pass
#end try
#if not any([x in $body_attr for x in ['back-art', 'pro', 'ii']])
#set $parts = $body_attr.split('class="')
#set $body_attr = ('class="%s '.join($parts), $parts[0] + ' class="%s"')[1 == len($parts)] % {0: '', 1: 'pro', 2: 'pro ii'}.get(getattr($sickbeard, 'DISPLAY_SHOW_VIEWMODE', 0))
#end if
<body$body_attr>
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container-fluid">
@ -106,8 +113,22 @@
<a href="$sbRoot/home/" class="dropdown-toggle" data-toggle="dropdown" data-delay="0" tabindex="$tab#set $tab += 1#">Shows <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="$sbRoot/home/showlistView/" tabindex="$tab#set $tab += 1#"><i class="sgicon-home"></i>Show List</a></li>
<li><a href="$sbRoot/home/addShows/" tabindex="$tab#set $tab += 1#"><i class="sgicon-addshow"></i>Add Shows</a></li>
<li><a href="$sbRoot/home/postprocess/" tabindex="$tab#set $tab += 1#"><i class="sgicon-postprocess"></i>Manual Post-Processing</a></li>
<li class="divider"></li>
<li class="menu-item-noicon opacity60">Add show...</li>
<li><a href="$sbRoot/home/addShows/new_show/" tabindex="$tab#set $tab += 1#"><i class="sgicon-addshow"></i>Search
<div class="menu-item-desc opacity60">find show at TV info source</div></a></li>
<li><a href="$sbRoot/home/addShows/trakt_default/" tabindex="$tab#set $tab += 1#"><i class="sgicon-trakt"></i>Trakt Cards
<div class="menu-item-desc opacity60">trends, tailored suggestions...</div></a></li>
<li><a href="$sbRoot/home/addShows/imdb_default/" tabindex="$tab#set $tab += 1#"><i class="sgicon-imdb"></i>IMDb Cards
<div class="menu-item-desc opacity60">popular decades, watchlists...</div></a></li>
#if $sg_var('USE_ANIDB')
<li><a href="$sbRoot/home/addShows/anime_default/" tabindex="$tab#set $tab += 1#"><div class="img-anime-16 square-16"></div>Anime Cards
<div class="menu-item-desc opacity60">browse anime to add</div></a></li>
#else
<li><a href="$sbRoot/config/anime/" class="opacity60" tabindex="$tab#set $tab += 1#"><div class="img-anime-16 square-16"></div>Anime Cards
<div class="menu-item-desc">to use, first enable AniDB here</div></a></li>
#end if
</ul>
</li>
@ -127,35 +148,27 @@
<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/episodeStatuses/" tabindex="$tab#set $tab += 1#"><i class="sgicon-episodestatus"></i>Episode Status Management</a></li>
#if hasattr($sickbeard, 'USE_EMBY') and $sickbeard.USE_EMBY and $sickbeard.EMBY_HOST != '' and $sickbeard.EMBY_APIKEY != ''
#if hasattr($sickbeard, 'USE_EMBY') and $sg_var('USE_EMBY') and $sg_str('EMBY_HOST') != '' and $sg_str('EMBY_APIKEY') != ''
<li><a href="$sbRoot/home/updateEMBY/" tabindex="$tab#set $tab += 1#"><i class="sgicon-emby"></i>Update Emby</a></li>
#end if
#if hasattr($sickbeard, 'USE_KODI') and $sickbeard.USE_KODI and $sickbeard.KODI_HOST != ''
#if hasattr($sickbeard, 'USE_KODI') and $sg_var('USE_KODI') and $sg_str('KODI_HOST') != ''
<li><a href="$sbRoot/home/updateKODI/" tabindex="$tab#set $tab += 1#"><i class="sgicon-kodi"></i>Update Kodi</a></li>
#end if
#if hasattr($sickbeard, 'USE_XBMC') and $sickbeard.USE_XBMC and $sickbeard.XBMC_HOST != ''
#if hasattr($sickbeard, 'USE_XBMC') and $sg_var('USE_XBMC') and $sg_str('XBMC_HOST') != ''
<li><a href="$sbRoot/home/updateXBMC/" tabindex="$tab#set $tab += 1#"><i class="sgicon-xbmc"></i>Update XBMC</a></li>
#end if
#if hasattr($sickbeard, 'USE_PLEX') and $sickbeard.USE_PLEX and $sickbeard.PLEX_SERVER_HOST != ''
#if hasattr($sickbeard, 'USE_PLEX') and $sg_var('USE_PLEX') and $sg_str('PLEX_SERVER_HOST') != ''
<li><a href="$sbRoot/home/updatePLEX/" tabindex="$tab#set $tab += 1#"><i class="sgicon-plex"></i>Update PLEX</a></li>
#end if
#if hasattr($sickbeard, 'USE_FAILED_DOWNLOADS') and $sickbeard.USE_FAILED_DOWNLOADS
#if hasattr($sickbeard, 'USE_FAILED_DOWNLOADS') and $sg_var('USE_FAILED_DOWNLOADS')
<li><a href="$sbRoot/manage/failedDownloads/" tabindex="$tab#set $tab += 1#"><i class="sgicon-failed"></i>Failed Downloads</a></li>
#end if
#if hasattr($sickbeard, 'USE_SUBTITLES') and $sickbeard.USE_SUBTITLES
#if hasattr($sickbeard, 'USE_SUBTITLES') and $sg_var('USE_SUBTITLES')
<li><a href="$sbRoot/manage/subtitleMissed/" tabindex="$tab#set $tab += 1#"><i class="sgicon-subtitles"></i>Missed Subtitle Management</a></li>
#end if
</ul>
</li>
<li id="NAVerrorlogs" class="dropdown">
<a href="$sbRoot/errorlogs/" class="dropdown-toggle" data-toggle="dropdown" data-delay="0" tabindex="$tab#set $tab += 1#">$logPageTitle <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="$sbRoot/errorlogs/" tabindex="$tab#set $tab += 1#"><i class="sgicon-errorlog"></i>View Log (Errors)</a></li>
<li><a href="$sbRoot/errorlogs/viewlog/" tabindex="$tab#set $tab += 1#"><i class="sgicon-log"></i>View Log</a></li>
</ul>
</li>
<li id="NAVconfig" class="dropdown">
<a href="$sbRoot/config/" class="dropdown-toggle" data-toggle="dropdown" data-delay="0" tabindex="$tab#set $tab += 1#"><img src="$sbRoot/images/menu/system18.png" class="navbaricon hidden-xs" /><b class="caret hidden-xs"></b><span class="visible-xs">Config <b class="caret"></b></span></a>
<ul class="dropdown-menu">
@ -171,12 +184,22 @@
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" data-delay="0" tabindex="$tab#set $tab += 1#"><img src="$sbRoot/images/menu/system18-2.png" class="navbaricon hidden-xs" /><b class="caret hidden-xs"></b><span class="visible-xs">System <b class="caret"></b></span></a>
#set num_errors = $getVar('$log_num_errors', None)
#set $err_class = ('', ' errors ' + (len('%s' % $num_errors ) * 'n')[0:4])[any([$num_errors])]
<a href="#" class="dropdown-toggle" data-toggle="dropdown" data-delay="0" tabindex="$tab#set $tab += 1#"><img src="$sbRoot/images/menu/system18-2.png" class="navbaricon hidden-xs" /><b class="caret hidden-xs"></b><span class="visible-xs">System <b class="caret"></b></span><span class="logger bar$err_class"><i class="sgicon-warning"></i></span></a>
<ul class="dropdown-menu">
<li><a href="$sbRoot/manage/manageSearches/forceVersionCheck" tabindex="$tab#set $tab += 1#"><i class="sgicon-updatecheck"></i>Check for Updates</a></li>
<li><a href="$sbRoot/home/viewchanges" tabindex="$tab#set $tab += 1#"><i class="sgicon-log"></i>View Changes</a></li>
<li><a href="$sbRoot/home/viewchanges" tabindex="$tab#set $tab += 1#"><i class="sgicon-changes"></i>View Changes</a></li>
<li class="divider"></li>
#if $sickbeard.WEB_USERNAME or $sickbeard.WEB_PASSWORD
#if $err_class
<li><a href="$sbRoot/errorlogs/" tabindex="$tab#set $tab += 1#"><span class="logger item$err_class"><i class="sgicon-warning"></i></span></i>View Errors</a></li>
#end if
<li><a href="$sbRoot/errorlogs/viewlog/" tabindex="$tab#set $tab += 1#"><i class="sgicon-log"></i>View Log File</a></li>
<li class="divider"></li>
<li><a href="$sbRoot/home/addShows/import_shows/" tabindex="$tab#set $tab += 1#"><i class="sgicon-import"></i>Import
<div class="menu-item-desc opacity60">existing shows</div></a></li>
<li class="divider"></li>
#if $sg_str('WEB_USERNAME') or $sg_str('WEB_PASSWORD')
<li><a href="$sbRoot/logout" class="confirm logout" tabindex="$tab#set $tab += 1#"><i class="sgicon-logout"></i>Logout</a></li>
#end if
<li><a href="$sbRoot/home/restart/?pid=$sbPID" class="confirm restart" tabindex="$tab#set $tab += 1#"><i class="sgicon-restart"></i>Restart</a></li>
@ -224,9 +247,9 @@
</p>
</div>
#end if
#if $sickbeard.NEWEST_VERSION_STRING
#if $sg_str('NEWEST_VERSION_STRING')
<div class="alert alert-success upgrade-notification" role="alert">
<span>$sickbeard.NEWEST_VERSION_STRING</span>
<span>$sg_str('NEWEST_VERSION_STRING')</span>
</div>
#end if
##

View file

@ -1,6 +1,8 @@
#import sickbeard
#from sickbeard import common
#from sickbeard import exceptions
<% def sg_var(varname, default=False): return getattr(sickbeard, varname, default) %>#slurp#
<% def sg_str(varname, default=''): return getattr(sickbeard, varname, default) %>#slurp#
##
#set global $title = 'Mass Edit'
#set global $header = 'Mass Edit'
@ -8,7 +10,7 @@
#set global $topmenu = 'manage'
##
#import os.path
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_top.tmpl')
#if None is not $quality_value:
#set $initial_quality = int($quality_value)
@ -88,7 +90,7 @@
#set $isDisabled = $isSelected
#if $archive_firstmatch_value##set $isDisabled = ''##else##set $isEnabled = ''##end if#
<div class="optionWrapper clearfix">
<span class="selectTitle">End upgrade on first match</span>
<span class="selectTitle">Upgrade once</span>
<div class="selectChoices">
<select id="edit_archive_firstmatch" name="archive_firstmatch" class="form-control form-control-inline input-sm">
<option value="keep">&lt; keep &gt;</option>
@ -165,12 +167,12 @@
</div><br />
</div>
<div class="optionWrapper #if $sickbeard.SHOWLIST_TAGVIEW != 'custom' then 'hidden' else ''#">
<div class="optionWrapper #if $sg_str('SHOWLIST_TAGVIEW') != 'custom' then 'hidden' else ''#">
<span class="selectTitle">Show is grouped in</span>
<div class="selectChoices">
<select id="edit_tag" name="tag" class="form-control form-control-inline input-sm">
<option value="keep">&lt; keep &gt;</option>
#for $tag in $sickbeard.SHOW_TAGS:
#for $tag in $sg_var('SHOW_TAGS', []):
<option value="$tag" #if $tag_value == $tag then $selected else ''#>$tag#echo ('', ' (default)')['Show List' == $tag]#</option>
#end for
</select>
@ -194,4 +196,4 @@
//-->
</script>
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_bottom.tmpl')

View file

@ -2,97 +2,128 @@
#from sickbeard import common
#from sickbeard import exceptions
##
#set global $title = 'Test Rename ' + $show.name
#set global $header = '<a href="' + $sbRoot + '/home/displayShow?show=%d">%s</a>' % ($show.indexerid, $show.name)
#set global $title = 'Media Renamer ' + $show.name
#set global $header = $show.name
#set global $sbPath = '..'
#set global $topmenu = 'home'
#set global $page_body_attr = 'rename'
#set $css = $getVar('css', 'reg')
#set $has_art = $getVar('has_art', None)
#set global $page_body_attr = 'edit-show" class="' + $css
##
#import os.path
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
<script type="text/javascript" src="$sbRoot/js/testRename.js"></script>
<script type="text/javascript" src="$sbRoot/js/livepanel.js?v=$sbPID"></script>
#if $varExists('header')
<h1 class="header">$header</h1>
<h1 class="header"><span class="grey-text">Media Renamer&nbsp;</span>$header</h1>
#else
<h1 class="title">$title</h1>
#end if
<input type="hidden" id="showID" value="$show.indexerid" />
<script type="text/javascript" src="$sbRoot/js/testRename.js"></script>
<h3>Preview of the proposed name changes</h3>
<blockquote>
#if int($show.air_by_date) == 1 and $sickbeard.NAMING_CUSTOM_ABD:
$sickbeard.NAMING_ABD_PATTERN
#elif int($show.sports) == 1 and $sickbeard.NAMING_CUSTOM_SPORTS:
$sickbeard.NAMING_SPORTS_PATTERN
#else
$sickbeard.NAMING_PATTERN
<div id="background-container">
#if $has_art
<ul>
#for $k, ($image, $rating) in enumerate($fanart)
<li class="#echo ' '.join((x for x in ({10:'group', 20:'fave', 30:'avoid'}.get($rating, ''), ('', 'background first-load')[$start_image == $k]) if x)) #" style="background-image:url($sbRoot/showPoster/?show=$show.indexerid&which=fanart_$image)"></li>
#end for
</ul>
#end if
</blockquote>
</div>
##
<div id="config">
<div id="config-content" class="linefix container">
<input type="hidden" id="showID" value="$show.indexerid">
<div id="config-components">
<ul>
<li><a href="#core-component-group1">Preview</a></li>
</ul>
<div id="core-component-group1" class="component-group">
#if 1 == int($show.air_by_date) and $sickbeard.NAMING_CUSTOM_ABD:
#set $type = 'Air-by-date'
#set $pattern = $sickbeard.NAMING_ABD_PATTERN
#elif 1 == int($show.sports) and $sickbeard.NAMING_CUSTOM_SPORTS:
#set $type = 'Sports'
#set $pattern = $sickbeard.NAMING_SPORTS_PATTERN
#else
#set $type = 'Single episode'
#set $pattern = $sickbeard.NAMING_PATTERN
#end if
<h3>Proposed changes of existing files</h3>
<div class="component-group-desc episode-sample">
<h3>$type sample:</h3>
</div>
<div class="example" style="margin-bottom:30px">
<span class="jumbo">$pattern</span>&nbsp;<a href="$sbRoot/config/postProcessing/#core-component-group2">edit pattern</a>
</div>
<div>
<button class="btn seriesCheck">Select All Episodes</button>
<button class="btn clearAll">Clear All</button>
</div>
#set $curSeason = -1
#set $odd = False
<div class="clearfix padbottom">
<button class="btn seriesCheck">Select All Episodes</button>
<button class="btn clearAll">Clear All</button>
</div>
<input type="submit" value="Rename Selected" class="btn btn-success"> <a href="$sbRoot/home/displayShow?show=$show.indexerid" class="btn btn-danger">Cancel Rename</a>
<table id="testRenameTable" class="sickbeardTable" border="0">
#set $first_season = True
<table id="testRenameTable" class="sickbeardTable" border="0">
##
#for $cur_ep_obj in $ep_obj_list
#set $curLoc = $cur_ep_obj.location[len($cur_ep_obj.show.location)+1:]
#set $curExt = $curLoc.split('.')[-1]
#set $newLoc = $cur_ep_obj.proper_path() + '.' + $curExt
#if int($cur_ep_obj.season) != $curSeason:
<thead>
<tr class="seasonheader" id="season-$cur_ep_obj.season">
<td colspan="4" class="text-left">
<h2>#if 0 == int($cur_ep_obj.season) then 'Specials' else 'Season %s' % $cur_ep_obj.season#</h2>
</td>
</tr>
<tr class="seasoncols" id="season-$cur_ep_obj.season-cols">
<th class="col-checkbox"><input type="checkbox" class="seasonCheck" id="$cur_ep_obj.season" /></th>
<th class="text-nowrap">Episode</th>
<th class="col-name">Old Location</th>
<th class="col-name">New Location</th>
</tr>
</thead>
#set $curSeason = int($cur_ep_obj.season)
#if $curSeason != int($cur_ep_obj.season):
#set $curSeason = int($cur_ep_obj.season)
#if not $first_season
</tbody>
#end if
#set $first_season = False
<thead>
<tr class="seasonheader" id="season-$cur_ep_obj.season">
<td colspan="3" class="text-left">
<h2>#if 0 == int($cur_ep_obj.season) then 'Specials' else 'Season %s' % $cur_ep_obj.season#</h2>
</td>
</tr>
<tr class="seasoncols" id="season-$cur_ep_obj.season-cols" style="line-height:26px">
<th class="col-checkbox"><input type="checkbox" class="seasonCheck" id="$cur_ep_obj.season"></th>
<th class="text-nowrap">Episode</th>
<th class="col-name">Old Location / New Location</th>
</tr>
</thead>
<tbody>
#end if
##
#set $odd = not $odd
#set $epStr = str($cur_ep_obj.season) + 'x' + str($cur_ep_obj.episode)
#set $curLoc = $cur_ep_obj.location[len($cur_ep_obj.show.location) + 1:]
#set $newLoc = '%s.%s' % ($cur_ep_obj.proper_path(), $curLoc.split('.')[-1])
#set $epList = sorted([cur_ep_obj.episode] + [x.episode for x in cur_ep_obj.relatedEps])
#if 1 < len($epList)
#set $epList = [$min($epList), $max($epList)]
#end if
<tbody>
<tr class="season-$curSeason #if $curLoc == $newLoc#good#else#wanted#end if# seasonstyle">
#if $curLoc != $newLoc
<td class="col-checkbox">
<input type="checkbox" class="epCheck" id="<%=str(cur_ep_obj.season) + 'x' + str(cur_ep_obj.episode)%>" name="<%=str(cur_ep_obj.season) + 'x' + str(cur_ep_obj.episode) %>">
</td>
#else
<td></td>
#end if
<td class="col-ep text-nowrap"><%= '-'.join(map(str, epList)) %></td>
<td class="col-name text-left">$curLoc</td>
<td class="col-name text-left">$newLoc</td>
</tr>
</tbody>
<tr class="opacity60 #if $curLoc == $newLoc#good#else#wanted#end if#">
#if $curLoc != $newLoc
#set $epStr = '%sx%s' % ($cur_ep_obj.season, $cur_ep_obj.episode)
<td class="col-checkbox" rowspan="2" style="line-height:30px">
<input type="checkbox" class="epCheck" id="$epStr" name="$epStr">
</td>
#else
<td rowspan="2">&nbsp;</td>
#end if
<td class="col-ep text-nowrap" rowspan="2">#echo '-'.join(map(str, epList))#</td>
<td style="text-align:left">now: $curLoc.replace('/', '/ ').replace('\\', ' \\ ')</td>
</tr>
<tr class="#if $curLoc == $newLoc#good#else#wanted#end if#">
<td style="text-align:left">new: $newLoc.replace('/', '/ ').replace('\\', ' \\ ')<br />
test: $cur_ep_obj.proper_path().replace('/', '/ ').replace('\\', ' \\ ')</td>
</tr>
#end for
</tbody>
</table>
</table>
<div style="clear:both;margin-top:20px">
<input type="submit" value="Rename Selected" class="btn btn-success"> <a href="$sbRoot/home/displayShow?show=$show.indexerid" class="btn btn-danger">Cancel Rename</a>
</div>
</div>
</div>
</div>
</div>
<div style="clear:both;margin-top:20px">
<input type="submit" value="Rename Selected" class="btn btn-success"> <a href="$sbRoot/home/displayShow?show=$show.indexerid" class="btn btn-danger">Cancel Rename</a>
</div>
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')

View file

@ -2,28 +2,35 @@
#from sickbeard import classes
#from sickbeard.common import *
#from sickbeard.logger import reverseNames
#from sickbeard.helpers import maybe_plural
##
#set global $header = 'Log File'
#set global $title = 'Logs'
#set global $sbPath = '..'
#set global $topmenu = 'errorlogs'
#set $log_level_var = None is $getVar('min_level', None) and 'minLevel' or 'min_level'
#set $log_level = $getVar($log_level_var, 20)
##
#import os.path
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
#if $varExists('header')
<h1 class="header">$header</h1>
#else
#else
<h1 class="title">$title</h1>
#end if
<div class="h2footer pull-right">Minimum logging level to display:
<div class="h2footer pull-right">
<select name="minLevel" id="minLevel" class="form-control form-control-inline input-sm">
#set $levels = $reverseNames.keys()
#set void = $levels.sort(lambda x,y: cmp($reverseNames[$x], $reverseNames[$y]))
#set $level_count = len($levels)
#for $level in $levels
<option value="$reverseNames[$level]" #if $minLevel == $reverseNames[$level] then 'selected="selected"' else ''#>$level.title()</option>
#set $level_count -= 1
#set $level_text = '%s%s' % ($level.title(), (('', ' only')[0 == $level_count], ' and the next%s level%s' % ((' ' + str($level_count), '')[1 == $level_count], maybe_plural($level_count)))[0 < $level_count])
<option value="$reverseNames[$level]"#if $log_level == $reverseNames[$level]# selected="selected" class="selected"#end if#>$level_text</option>
#end for
</select>
</div>
@ -35,7 +42,7 @@
<!--
\$(document).ready(function(){
\$('#minLevel').change(function(){
window.location.href = '$sbRoot/errorlogs/viewlog/?minLevel=' + \$(this).val()
window.location.href = '$sbRoot/errorlogs/viewlog/?$log_level_var=' + \$(this).val()
});
window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes
@ -43,4 +50,4 @@
//-->
</script>
#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

@ -468,7 +468,7 @@ $(document).ready(function () {
at: 'top center'
},
style: {
classes: 'qtip-rounded qtip-shadow'
classes: 'qtip-dark qtip-rounded qtip-shadow'
}
});
$('.custom-pattern,#unpack').qtip({

View file

@ -40,7 +40,6 @@ $(document).ready(function(){
$.getJSON(sbRoot + '/config/providers/getNewznabCategories', params,
function(data){
updateNewznabCaps( data, selectedProvider );
//console.debug(data.tv_categories);
});
}
@ -217,7 +216,6 @@ $(document).ready(function(){
if (rootObject.name == searchFor) {
found = true;
}
//console.log(rootObject.name + ' while searching for: ' + searchFor);
});
return found;
};
@ -247,20 +245,20 @@ $(document).ready(function(){
$.fn.newznabProvidersCapabilities.forEach(function (newzNabCap) {
$.sgd && console.log('array found:' + (newzNabCap.categories instanceof Array ? 'yes': 'no'));
if (newzNabCap.name && newzNabCap.name == selectedProvider[0] && newzNabCap.categories instanceof Array) {
if (newzNabCap.name && newzNabCap.name == selectedProvider[0] && newzNabCap.categories instanceof Array) {
newzNabCap.categories.forEach(function (category_set) {
if (category_set.id && category_set.name) {
catName = category_set.name.replace(/Docu([^\w]|$)(.*?)/i, 'Documentary$1');
newCapOptions.push({
value: category_set.id,
text: catName + ' (' + category_set.id + ')'
});
});
}
});
$('#newznab_cap').replaceOptions(newCapOptions);
hasCats = !!newCapOptions.length
}
});
}
});
$('#nn-loadcats').removeClass('show').addClass('hide');
if (hasCats) {
$.sgd && console.log('hasCats');
@ -270,7 +268,7 @@ $(document).ready(function(){
$.sgd && console.log('noCats');
$('#nn-cats').removeClass('show').addClass('hide');
$('#nn-nocats').removeClass('hide').addClass('show');
}
}
} else {
$.sgd && console.log('errCats');
// error - no caps
@ -412,7 +410,6 @@ $(document).ready(function(){
});
$(this).on('click', '#newznab_cat_update', function(){
//console.debug('Clicked Button');
//Maybe check if there is anything selected?
$('#newznab_cat option').each(function() {
@ -428,7 +425,6 @@ $(document).ready(function(){
if($(this).attr('selected') == 'selected')
{
var selected_cat = $(this).val();
//console.debug(selected_cat);
newOptions.push({text: selected_cat, value: selected_cat})
};
});

View file

@ -1,264 +1,317 @@
$(document).ready(function () {
/** @namespace $.SickGear.Root */
/** @namespace config.TVShowList */
/** @namespace config.useIMDbInfo */
/** @namespace $.SickGear.config.useFuzzy */
/** @namespace $.SickGear.config.dateFormat */
/** @namespace $.SickGear.config.timeFormat */
/** @namespace $.SickGear.config.fuzzyTrimZero */
$(document).ready(function() {
$('#sbRoot').ajaxEpSearch({'colorRow': true});
// handle the show selection dropbox
$('#pickShow').change(function() {
var val = $(this).attr('value');
if (val != 0)
window.location.href = $.SickGear.Root + '/home/displayShow?show=' + val;
});
$('#sbRoot').ajaxEpSubtitlesSearch();
$('#prevShow, #nextShow').on('click', function() {
var select$ = $('#pickShow'),
index = $.inArray(select$.find('option:selected').val()*1, config.TVShowList);
//noinspection JSUnresolvedVariable
select$.find('option[value="' + config.TVShowList[('nextShow' === $(this).attr('id')
? (index < config.TVShowList.length - 1 ? index + 1 : 0)
: (0 < index ? index - 1 : config.TVShowList.length - 1))] + '"]').attr('selected', 'selected');
select$.change();
return !1;
});
$('#seasonJump').change(function () {
var id = $(this).val();
if (id && id != 'jump') {
$('html,body').animate({scrollTop: $(id).offset().top}, 'slow');
location.hash = id;
}
$(this).val('jump');
});
$('#seasonJump').change(function() {
var id = $(this).val();
if (id && 'jump' != id) {
$('html,body').animate({scrollTop: $(id).offset().top}, 'slow');
location.hash = id;
}
$(this).val('jump');
});
$('#prevShow, #nextShow').click(function () {
var select$ = $('#pickShow'),
index = $.inArray(select$.find('option:selected').val()*1, TVShowList);
select$.find('option[value="' + TVShowList[('nextShow' === $(this).attr('id')
? (index < TVShowList.length - 1 ? index + 1 : 0)
: (0 < index ? index - 1 : TVShowList.length - 1))] + '"]').attr('selected', 'selected');
select$.change();
return false;
});
$('.details-plot').collapser({
mode: 'lines',
truncate: 10,
showText: '<span class="pull-right moreless"><i class="sgicon-arrowdown" style="margin-right:2px"></i>more</span>',
hideText: '<span class="pull-right moreless"><i class="sgicon-arrowup" style="margin-right:2px"></i>less</span>',
showClass: 'show-class'
});
$('#changeStatus').click(function () {
var sbRoot = $('#sbRoot').val();
var epArr = new Array()
if (config.useIMDbInfo){
$.fn.generateStars = function() {
return this.each(function(i,e){$(e).html($('<span/>').width($(e).text()*12));});
};
$('.imdbstars').generateStars();
}
$('.epCheck').each(function () {
$('#changeStatus').on('click', function() {
var epArr = [];
if (this.checked == true) {
epArr.push($(this).attr('id'))
}
$('.epCheck').each(function() {
this.checked && epArr.push($(this).attr('id'))
});
if (epArr.length)
window.location.href = $.SickGear.Root + '/home/setStatus?show=' + $('#showID').attr('value') +
'&eps=' + epArr.join('|') + '&status=' + $('#statusSelect').attr('value');
});
});
// show/hide different types of rows when the checkboxes are changed
var el = $('#checkboxControls').find('input');
el.change(function() {
$(this).showHideRows($(this).attr('id'));
});
if (epArr.length == 0)
return false;
// initially show/hide all the rows according to the checkboxes
el.each(function() {
var status = this.checked;
$('tr.' + $(this).attr('id')).each(function() {
status && $(this).show() || $(this).hide();
});
});
url = sbRoot + '/home/setStatus?show=' + $('#showID').attr('value') + '&eps=' + epArr.join('|') + '&status=' + $('#statusSelect').attr('value');
window.location.href = url
$.fn.showHideRows = function(whichClass) {
});
var status = $('#checkboxControls > input, #' + whichClass).prop('checked');
$('tr.' + whichClass).each(function() {
status && $(this).show() || $(this).hide();
});
$('.seasonCheck').click(function () {
var seasCheck = this;
var seasNo = $(seasCheck).attr('id');
// hide season headers with no episodes under them
$('tr.seasonheader').each(function() {
var numRows = 0;
var seasonNo = $(this).attr('id');
$('tr.' + seasonNo + ' :visible').each(function() {
numRows++
});
var el = $('#' + seasonNo + '-cols');
if (0 == numRows) {
$(this).hide();
el.hide();
} else {
$(this).show();
el.show();
}
$('.epCheck:visible').each(function () {
var epParts = $(this).attr('id').split('x');
});
};
if (epParts[0] == seasNo) {
this.checked = seasCheck.checked
}
});
});
function checkState(state){
$('.epCheck:visible, .seasonCheck:visible').prop('checked', state)
}
// selects all visible episode checkboxes.
$('.seriesCheck').on('click', function() { checkState(!0); });
var lastCheck = null;
$('.epCheck').click(function (event) {
// clears all visible episode checkboxes and the season selectors
$('.clearAll').on('click', function() { checkState(!1); });
if (!lastCheck || !event.shiftKey) {
lastCheck = this;
return;
}
function setEpisodeSceneNumbering(forSeason, forEpisode, sceneSeason, sceneEpisode) {
var showId = $('#showID').val(), indexer = $('#indexer').val();
var check = this;
var found = 0;
if ('' === sceneSeason) sceneSeason = null;
if ('' === sceneEpisode) sceneEpisode = null;
$('.epCheck').each(function () {
switch (found) {
case 2:
return false;
case 1:
this.checked = lastCheck.checked;
}
$.getJSON($.SickGear.Root + '/home/setSceneNumbering',
{
'show': showId,
'indexer': indexer,
'forSeason': forSeason,
'forEpisode': forEpisode,
'sceneSeason': sceneSeason,
'sceneEpisode': sceneEpisode
},
function(data) {
// Set the values we get back
$('#sceneSeasonXEpisode_' + showId + '_' + forSeason + '_' + forEpisode).val(
(null === data.sceneSeason || null === data.sceneEpisode)
? '' : data.sceneSeason + 'x' + data.sceneEpisode);
if (!data.success)
alert(data.errorMessage ? data.errorMessage : 'Update failed.');
}
);
}
if (this == check || this == lastCheck)
found++;
});
function setAbsoluteSceneNumbering(forAbsolute, sceneAbsolute) {
var showId = $('#showID').val(), indexer = $('#indexer').val();
lastClick = this;
});
if ('' === sceneAbsolute)
sceneAbsolute = null;
// selects all visible episode checkboxes.
$('.seriesCheck').click(function () {
$('.epCheck:visible').each(function () {
this.checked = true
});
$('.seasonCheck:visible').each(function () {
this.checked = true
})
});
$.getJSON($.SickGear.Root + '/home/setSceneNumbering',
{
'show': showId,
'indexer': indexer,
'forAbsolute': forAbsolute,
'sceneAbsolute': sceneAbsolute
},
function(data) {
// Set the values we get back
$('#sceneAbsolute_' + showId + '_' + forAbsolute).val(
(null === data.sceneAbsolute) ? '' : data.sceneAbsolute);
if (!data.success)
alert(data.errorMessage ? data.errorMessage : 'Update failed.');
}
);
}
// clears all visible episode checkboxes and the season selectors
$('.clearAll').click(function () {
$('.epCheck:visible').each(function () {
this.checked = false
});
$('.seasonCheck:visible').each(function () {
this.checked = false
});
});
function qTips(select$){
select$.each(function() {
$(this).qtip({
show: {solo:true},
position: {viewport:$(window), my:'left center', adjust:{y:-10, x:2}},
style: {classes:'qtip-dark qtip-rounded qtip-shadow qtip-maxwidth'}
});
});
}
qTips($('.addQTip'));
// handle the show selection dropbox
$('#pickShow').change(function () {
var sbRoot = $('#sbRoot').val();
var val = $(this).attr('value');
if (val == 0)
return;
url = sbRoot + '/home/displayShow?show=' + val;
window.location.href = url
});
function table_init(table$) {
$('#sbRoot').ajaxEpSearch({'colorRow': true});
$('#sbRoot').ajaxEpSubtitlesSearch();
// show/hide different types of rows when the checkboxes are changed
$("#checkboxControls input").change(function (e) {
var whichClass = $(this).attr('id');
$(this).showHideRows(whichClass);
});
if ($.SickGear.config.useFuzzy) {
fuzzyMoment({
containerClass: '.airdate',
dateHasTime: !1,
dateFormat: $.SickGear.config.dateFormat,
timeFormat: $.SickGear.config.timeFormat,
trimZero: $.SickGear.config.fuzzyTrimZero
});
}
// initially show/hide all the rows according to the checkboxes
$("#checkboxControls input").each(function (e) {
var status = this.checked;
$("tr." + $(this).attr('id')).each(function (e) {
if (status) {
$(this).show();
} else {
$(this).hide();
}
});
});
table$.each(function (i, obj) {
$(obj).has('tbody.collapse tr').tablesorter({
widgets: ['zebra'],
selectorHeaders: '> thead tr.tablesorter-headerRow th',
textExtraction: {
'.tablesorter-ep-num': function(node) {
var n = /(\d+)\)?$/img.exec(''+$(node).find('span').text()); return (null == n ? '' : n[1]); },
'.tablesorter-ep-scene': function(node) {
var n = $(node).find('input'); return n.val() || n.attr('placeholder'); },
'.tablesorter-airdate': function(node) { return $(node).find('span').attr('data-airdate') || ''; }
},
headers: {
'.tablesorter-no-sort': {sorter: !1, parser: !1},
'.tablesorter-ep-num': {sorter: 'digit'},
'.tablesorter-airdate': {sorter: 'digit'}
}
});
$.fn.showHideRows = function (whichClass) {
$(obj).find('.seasonCheck').on('click', function() {
var seasCheck = this, seasNo = $(seasCheck).attr('id');
var status = $('#checkboxControls > input, #' + whichClass).prop('checked');
$("tr." + whichClass).each(function (e) {
if (status) {
$(this).show();
} else {
$(this).hide();
}
});
$(obj).find('.epCheck:visible').each(function() {
var epParts = $(this).attr('id').split('x');
if (epParts[0] == seasNo)
this.checked = seasCheck.checked
// hide season headers with no episodes under them
$('tr.seasonheader').each(function () {
var numRows = 0;
var seasonNo = $(this).attr('id');
$('tr.' + seasonNo + ' :visible').each(function () {
numRows++
});
if (numRows == 0) {
$(this).hide();
$('#' + seasonNo + '-cols').hide()
} else {
$(this).show();
$('#' + seasonNo + '-cols').show()
}
});
});
});
};
var lastCheck = null;
$(obj).find('.epCheck').on('click', function(event) {
function setEpisodeSceneNumbering(forSeason, forEpisode, sceneSeason, sceneEpisode) {
var sbRoot = $('#sbRoot').val();
var showId = $('#showID').val();
var indexer = $('#indexer').val();
if (!lastCheck || !event.shiftKey) {
lastCheck = this;
return;
}
if (sceneSeason === '') sceneSeason = null;
if (sceneEpisode === '') sceneEpisode = null;
var check = this, found = 0;
$(obj).find('.epCheck').each(function() {
switch(found) {
case 2:
return !1;
case 1:
this.checked = lastCheck.checked;
}
(this == check || this == lastCheck) && found++;
});
lastCheck = this;
});
$.getJSON(sbRoot + '/home/setSceneNumbering',
{
'show': showId,
'indexer': indexer,
'forSeason': forSeason,
'forEpisode': forEpisode,
'sceneSeason': sceneSeason,
'sceneEpisode': sceneEpisode
},
function (data) {
// Set the values we get back
if (data.sceneSeason === null || data.sceneEpisode === null) {
$('#sceneSeasonXEpisode_' + showId + '_' + forSeason + '_' + forEpisode).val('');
}
else {
$('#sceneSeasonXEpisode_' + showId + '_' + forSeason + '_' + forEpisode).val(data.sceneSeason + 'x' + data.sceneEpisode);
}
if (!data.success) {
if (data.errorMessage) {
alert(data.errorMessage);
} else {
alert('Update failed.');
}
}
}
);
}
qTips($(obj).find('.addQTip'));
plotter($(obj).find('.plotInfo'));
function setAbsoluteSceneNumbering(forAbsolute, sceneAbsolute) {
var sbRoot = $('#sbRoot').val();
var showId = $('#showID').val();
var indexer = $('#indexer').val();
$(obj).find('.sceneSeasonXEpisode').change(function() {
// Strip non-numeric characters
$(this).val($(this).val().replace(/[^0-9xX]*/g, ''));
if (sceneAbsolute === '') sceneAbsolute = null;
var forSeason = $(this).attr('data-for-season'),
forEpisode = $(this).attr('data-for-episode'),
m = $(this).val().match(/^(\d+)x(\d+)$/i),
sceneSeason = m && m[1] || null, sceneEpisode = m && m[2] || null;
$.getJSON(sbRoot + '/home/setSceneNumbering',
{
'show': showId,
'indexer': indexer,
'forAbsolute': forAbsolute,
'sceneAbsolute': sceneAbsolute
},
function (data) {
// Set the values we get back
if (data.sceneAbsolute === null) {
$('#sceneAbsolute_' + showId + '_' + forAbsolute).val('');
}
else {
$('#sceneAbsolute_' + showId + '_' + forAbsolute).val(data.sceneAbsolute);
}
if (!data.success) {
if (data.errorMessage) {
alert(data.errorMessage);
} else {
alert('Update failed.');
}
}
}
);
}
setEpisodeSceneNumbering(forSeason, forEpisode, sceneSeason, sceneEpisode);
});
$('.sceneSeasonXEpisode').change(function () {
// Strip non-numeric characters
$(this).val($(this).val().replace(/[^0-9xX]*/g, ''));
var forSeason = $(this).attr('data-for-season');
var forEpisode = $(this).attr('data-for-episode');
var showId = $('#showID').val();
var indexer = $('#indexer').val();
$(obj).find('.sceneAbsolute').change(function() {
// Strip non-numeric characters
$(this).val($(this).val().replace(/[^0-9xX]*/g, ''));
//var sceneEpisode = $('#sceneEpisode_' + showId + '_' + forSeason +'_' + forEpisode).val();
var m = $(this).val().match(/^(\d+)x(\d+)$/i);
var sceneSeason = null, sceneEpisode = null;
if (m) {
sceneSeason = m[1];
sceneEpisode = m[2];
}
setEpisodeSceneNumbering(forSeason, forEpisode, sceneSeason, sceneEpisode);
});
var forAbsolute = $(this).attr('data-for-absolute'),
m = $(this).val().match(/^(\d{1,3})$/i),
sceneAbsolute = m && m[1] || null;
$('.sceneAbsolute').change(function () {
// Strip non-numeric characters
$(this).val($(this).val().replace(/[^0-9xX]*/g, ''));
var forAbsolute = $(this).attr('data-for-absolute');
var showId = $('#showID').val();
var indexer = $('#indexer').val();
setAbsoluteSceneNumbering(forAbsolute, sceneAbsolute);
});
});
}
table_init($('.sickbeardTable'));
var m = $(this).val().match(/^(\d{1,3})$/i);
var sceneAbsolute = null;
if (m) {
sceneAbsolute = m[1];
}
setAbsoluteSceneNumbering(forAbsolute, sceneAbsolute);
});
$.SickGear.season = [];
$.SickGear.run = !1;
$('button[id*="showseason-"]').on('click', function() {
var that = this, this$ = $('#' + this.id), table$ = this$.parents('.sickbeardTable');
});
if (0 < table$.find('tbody').find('tr').length) {
table$.toggleClass('open');
} else {
table$.find('span.images').toggleClass('hide');
this$.toggleClass('hide');
function fetchSeason() {
if (0 == $.SickGear.season.length)
return;
var season = $.SickGear.season[0];
$.SickGear.season.shift();
$.getJSON($.SickGear.Root + '/home/display_season', {'show': $('#showID').val(), 'season': season},
function(data) {
if (!data.success) {
alert('Season listing failed.');
} else {
table$.find('tbody').html(data.success);
table_init(table$);
}
table$.toggleClass('open');
this$.toggleClass('hide');
table$.find('span.images').toggleClass('hide');
fetchSeason()
}
);
}
$.SickGear.season.push(this.id);
var result = [];
$.each($.SickGear.season, function(i, e) {
if (-1 == $.inArray(e, result)) result.push(e);
});
$.SickGear.season = result;
if (!$.SickGear.run && 1 == $.SickGear.season.length) $.SickGear.run = !0 && fetchSeason();
}
return !1;
});
$('button.allseasons').on('click', function() {
$('table.sickbeardTable:not(.display-season)').each(function() {
$(this).find('button[id*="showseason-"]').click();
});
var liveStates = $('#display-show');
return liveStates.toggleClass('min'), $.get($.SickGear.Root + '/live_panel/?allseasons='
+ String.prototype.toLowerCase.apply(+liveStates.hasClass('min'))), !1;
});
});

View file

@ -1,3 +1,4 @@
/** @namespace $.SickGear.Root */
/** @namespace config.showLang */
/** @namespace config.showIsAnime */
/*globals $, config, sbRoot, generate_bwlist*/
@ -7,13 +8,13 @@ $(document).ready(function () {
$('#location').fileBrowser({title: 'Select Show Location'});
function htmlFlag(lang) {
return ' class="flag" style="background-image:url(' + sbRoot + '/images/flags/' + lang + '.png)"'
return ' class="flag" style="background-image:url(' + $.SickGear.Root + '/images/flags/' + lang + '.png)"'
}
$.getJSON(sbRoot + '/home/addShows/getIndexerLanguages', {}, function (data) {
$.getJSON($.SickGear.Root + '/home/addShows/getIndexerLanguages', {}, function (data) {
var result = '', currentLangAdded = '', selected = ' selected="selected"';
if (0 == data.results.length) {
if (!data.results.length) {
result = '<option value="' + config.showLang + '"' + selected + htmlFlag(config.showLang) + '>'
+ config.showLang + '</option>';
} else {
@ -47,13 +48,13 @@ $(document).ready(function () {
return allExceptions
}
$('#submit').click(function () {
$('#submit').on('click', function () {
$('#exceptions_list').val(getExceptions());
if (config.showIsAnime)
generate_bwlist();
});
$('#addSceneName').click(function () {
$('#addSceneName').on('click', function () {
var elSceneName = $('#SceneName'), elSceneNameSeason = $('#SceneNameSeason'),
sceneEx = elSceneName.val(), sceneExSeason = elSceneNameSeason.val();
@ -72,7 +73,7 @@ $(document).ready(function () {
return option.appendTo($('#exceptions_list'));
});
$('#removeSceneName').click(function () {
$('#removeSceneName').on('click', function () {
$('#exceptions_list').find('option:selected').remove();
$(this).toggle_SceneException();
@ -99,8 +100,8 @@ $(document).ready(function () {
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'); }
function isABD() { uncheck(elAnime); uncheck(elScene); $('#blackwhitelist, #anime-options').fadeOut('fast', 'linear'); }
function isSports() { uncheck(elAnime); uncheck(elScene); $('#blackwhitelist, #anime-options').fadeOut('fast', 'linear'); }
if (checked(elAnime)) { isAnime(); }
if (checked(elScene)) { isScene(); }

View file

@ -21,7 +21,6 @@ function initActions() {
$('#SubMenu a:contains("General")').addClass('btn').html('<i class="sgicon-config"></i>General');
$('#SubMenu a:contains("Episode Status")').addClass('btn').html('<i class="sgicon-episodestatus"></i>Episode Status Management');
$('#SubMenu a:contains("Missed Subtitle")').addClass('btn').html('<i class="sgicon-subtitles"></i>Missed Subtitles');
$('#SubMenu a[href$="/home/addShows/"]').addClass('btn').html('<i class="sgicon-addshow"></i>Add Show');
$('#SubMenu a:contains("Processing")').addClass('btn').html('<i class="sgicon-postprocess"></i>Post-Processing');
$('#SubMenu a:contains("Manage Searches")').addClass('btn').html('<i class="sgicon-search"></i>Manage Searches');
$('#SubMenu a:contains("Manage Torrents")').addClass('btn').html('<i class="sgicon-bittorrent"></i>Manage Torrents');
@ -40,4 +39,5 @@ $(document).ready(function(){
initActions();
$('#NAV' + topmenu).addClass('active');
$('.dropdown-toggle').dropdownHover();
(/undefined/i.test(document.createElement('input').placeholder)) && $('body').addClass('no-placeholders');
});

View file

@ -5,7 +5,7 @@ a.o.afterShow.call(a.e,a)};e.isFunction(a.o.beforeShow)&&a.o.beforeShow.call(a.e
k.animate({height:h},f,function(){k.height("auto");g()});c.removeClass(a.o.hideClass).addClass(a.o.showClass);break;case "block":a.blockMode(c,"show",f,g)}a.status=1;if(!0==a.o.lockHide)return c.find("[data-ctrl]").remove(),"";if("block"==a.mode)c.off("click.coll").on("click.coll",function(b){b.preventDefault();a.hide(c)});else 0!=c.find("[data-ctrl]").length||e.isFunction(a.o.controlBtn)||c.append(a.ctrlHtml),a.ctrlBtn=e.isFunction(a.o.controlBtn)?a.o.controlBtn.call(a.e):e(c.find("[data-ctrl]")),
a.ctrlBtn.off("click.coll").on("click.coll",function(b){b.preventDefault();a.hide(c)}).html(a.o.hideText)},hide:function(b,f){var a=this,c=e(b);"undefined"===typeof f&&(f=a.o.speed);var g=function(){e.isFunction(a.o.afterHide)&&a.o.afterHide.call(a.e,a)};e.isFunction(a.o.beforeHide)&&a.o.beforeHide.call(a.e,a);c.find("[data-ctrl]").remove();switch(a.mode){case "chars":var d=e.trim(c.text());a.remaining.chars=d.length-a.o.truncate;d.length>a.o.truncate&&(c.data("tHTML",c.html()),d=a.pad(d.slice(0,
a.o.truncate),d.slice(a.o.truncate,d.length)),c.html(d).removeClass(a.o.showClass).addClass(a.o.hideClass),g());break;case "words":d=e.trim(c.text());d=d.split(" ");a.remaining.words=d.length-a.o.truncate;d.length>a.o.truncate&&(c.data("tHTML",c.html()),d=a.pad(d.slice(0,a.o.truncate).join(" "),d.slice(a.o.truncate,d.length).join(" ")),c.html(d).removeClass(a.o.showClass).addClass(a.o.hideClass),g());break;case "lines":0==c.children("div").length&&c.wrapInner("<div>");d=c.children("div").css("height",
"");d.html(d.text());var h=d.height();"undefined"===typeof c.data("lHeight")?(temp=d.clone(),lHeight=temp.text("a").insertAfter(d).height(),c.data("lHeight",lHeight),d.next().remove()):lHeight=c.data("lHeight");lines=h/lHeight;a.remaining.lines=lines-a.o.truncate;0<a.remaining.lines&&(d.css("overflow","hidden"),d.animate({height:lHeight*a.o.truncate},f).data("tHeight",h),c.removeClass(a.o.showClass).addClass(a.o.hideClass),0!=c.find("[data-ctrl]").length||e.isFunction(a.o.controlBtn)||c.append(a.ctrlHtml),
"");t=d.html();d.html(d.text());var h=d.height();"undefined"===typeof c.data("lHeight")?(temp=d.clone(),lHeight=temp.text("a").insertAfter(d).height(),c.data("lHeight",lHeight),d.next().remove()):lHeight=c.data("lHeight");lines=h/lHeight;a.remaining.lines=lines-a.o.truncate;d.html(t);0<a.remaining.lines&&(d.css("overflow","hidden"),d.animate({height:lHeight*a.o.truncate},f).data("tHeight",h),c.removeClass(a.o.showClass).addClass(a.o.hideClass),0!=c.find("[data-ctrl]").length||e.isFunction(a.o.controlBtn)||c.append(a.ctrlHtml),
g());break;case "block":a.blockMode(c,"hide",f,g)}a.status=0;"block"==a.mode?c.unbind("click.coll").bind("click.coll",function(b){b.preventDefault();a.show(c)}):(a.ctrlBtn=e.isFunction(a.o.controlBtn)?a.o.controlBtn.call(a.e):e(c.find("[data-ctrl]")),a.ctrlBtn.off("click.coll").on("click.coll",function(b){b.preventDefault();a.show(c)}).html(a.o.showText),g=a.o.showText,d={chars:["character","characters"],words:["word","words"],lines:["lines","lines"]},g=g.replace("%s",a.remaining[a.mode]+(1==a.remaining[a.mode]?
" "+d[a.mode][0]:" "+d[a.mode][1])),a.ctrlBtn.html(g))},pad:function(b,f){return b+'<span class="coll-ellipsis">'+this.o.ellipsis+"</span>"+(e.isFunction(this.o.ctrlBtn)?"":this.ctrlHtml)+'<span class="coll-hidden" style="display:none">'+f+"</span>"},blockMode:function(b,f,a,c){var g=["fadeOut","slideUp","fadeIn","slideDown"],d="fade"==this.o.effect?0:1,g="hide"==f?g[d]:g[d+2];if(e.isFunction(this.o.target))this.o.target.call(this.e)[g](a,c);else if(e.fn[this.o.target])e(b)[this.o.target]()[g](a,
c);"show"==f?(b.removeClass(this.o.showClass).addClass(this.o.hideClass),this.o.changeText&&b.text(this.o.hideText)):(b.removeClass(this.o.hideClass).addClass(this.o.showClass),this.o.changeText&&b.text(this.o.showText))},reInit:function(b){b.find("[data-ctrl]").remove();b.html(this.e.data("oCnt"));0==this.status?this.hide(b,0):this.show(b,0)}};e.fn.collapser=function(b){return this.each(function(){e.data(this,"collapser")||e.data(this,"collapser",new l(this,b))})}})(jQuery,window,document);

View file

@ -10,7 +10,7 @@ var scrolltotop={
//scrollto: Keyword (Integer, or "Scroll_to_Element_ID"). How far to scroll document up when control is clicked on (0=top).
setting: {startline:100, scrollto: 0, scrollduration:1000, fadeduration:[500, 100]},
controlHTML: top_image_html,//set in inc_top.tmpl so it can be $sbRooted
controlattrs: {offsetx:10, offsety:10}, //offset of control relative to right/ bottom of window corner
controlattrs: {offsetx:5, offsety:5}, //offset of control relative to right/ bottom of window corner
anchorkeyword: '#top', //Enter href value of HTML anchors on the page that should also act as "Scroll Up" links
state: {isvisible:false, shouldvisible:false},
@ -56,7 +56,7 @@ var scrolltotop={
mainobj.$body=(window.opera)? (document.compatMode=="CSS1Compat"? $('html') : $('body')) : $('html,body')
mainobj.$control=$('<div id="topcontrol">'+mainobj.controlHTML+'</div>')
.css({position:mainobj.cssfixedsupport? 'fixed' : 'absolute', bottom:mainobj.controlattrs.offsety, right:mainobj.controlattrs.offsetx, opacity:0, cursor:'pointer'})
.attr({title:'Scroll Back to Top'})
.attr({title:'Scroll to Top'})
.click(function(){mainobj.scrollup(); return false})
.appendTo('body')
if (document.all && !window.XMLHttpRequest && mainobj.$control.text()!='') //loose check for IE6 and below, plus whether control contains any text

296
gui/slick/js/livepanel.js Normal file
View file

@ -0,0 +1,296 @@
/** @namespace $.SickGear.Root */
/** @namespace config.hasArt */
/** @namespace config.panelTitles */
$(document).ready(function() {
var panel$ = $('#livepanel'),
pTitle = config.panelTitles || [],
isEpisodeView = !!$('#episode-view').length,
liveStates$ = $(isEpisodeView ? '#episode-view' : '#display-show'),
jqTooltipUsed = /(?!undefined)/i.test(typeof($('body').tooltip)),
group = 'group', fave = 'fave', avoid = 'avoid', ratingVerbs = [group, fave, avoid].join(' ');
panel$.removeClass('off');
$('#viewart').on('click', function() {
var state = 0, on = '', result = !1;
if (isEpisodeView) {
if (isSet('open-gear')) {
state = 4; on = 'viewart';
} else if (!isSet('viewart')) {
state = 3; on = 'open-gear';
}
} else if (!isSet('back-art')) {
if (!isSet('poster-right')) {
state = 1; on = 'poster-right';
}
} else if (isSet('open-gear')) {
state = 4; on = 'viewart';
} else if (isSet('poster-off')) {
state = 3; on = 'open-gear';
} else if (isSet('poster-right')) {
state = 2; on = 'poster-off';
} else if (!isSet('viewart')) {
state = 1; on = 'poster-right';
}
liveStates$.removeClass('poster-right poster-off open-gear viewart').addClass(on);
refreshTitles($(this).attr('id'));
send('viewart=' + state);
var container = [];
$.each($('[id^=day]'), function() { container.push($('#' + $(this).attr('id'))) });
$.each(container, function() { $(this).isotope('layout') });
return result;
});
$('#back-art,#translucent').on('click', function() {
var result = !1,
highlight = panel$.hasClass('highlight-off') ||
panel$.hasClass('highlight2') && panel$.removeClass('highlight2').addClass('highlight1') ||
panel$.hasClass('highlight1') && panel$.removeClass('highlight1').addClass('highlight') ||
panel$.removeClass('highlight').addClass('highlight-off');
if (config.hasArt) {
var elid = $(this).attr('id');
liveStates$.toggleClass(elid);
refreshTitles(elid);
send(elid.replace('-', '') + '=' + String.prototype.toLowerCase.apply(+isSet(elid)));
}
return result;
});
$('#proview').on('click', function() {
var state = 0, on = 'reg', result = !1;
if (!isEpisodeView && isSet('viewart')) {
liveStates$.toggleClass('allart');
} else {
if (isSet('reg')) {
state = 1; on = 'pro';
} else if(isSet('back-art') && !isSet('allart')) {
if (isSet('ii')) {
state = 3; on = 'pro ii allart';
} else if (isSet('pro')) {
state = 2; on = 'pro ii';
}
}
liveStates$.removeClass('reg pro ii allart').addClass(on);
send('viewmode=' + state);
}
maybeBackground();
refreshTitles($(this).attr('id'));
return result;
});
/*
* back art related
*/
function maybeArrows() {
var backArts$ = $('#background-container'), result = !0;
if (isSet('allart')
|| (!isSet(fave) &&
(1 < backArts$.find('li.' + group).length ||
(1 != backArts$.find('li.' + group).length && 1 < backArts$.find('li').not('.' + group + ',.' + avoid).length)))
|| (isEpisodeView &&
1 < (backArts$.find('li.' + group).length + backArts$.find('li.' + fave).length +
backArts$.find('li').not('.' + group + ',.' + avoid).length))) {
liveStates$.removeClass('oneof');
} else {
liveStates$.addClass('oneof');
}
return result
}
function setArt(dir) {
var backArts$ = $('#background-container'), curArt$ = backArts$.find('li.background'),
faveArt$ = backArts$.find('li.' + fave), result = !0,
newArt$, init = !1, noArt = function(el) { return /undefined/i.test(typeof(el.css('background-image'))); },
viewable = !isSet('allart') && !!backArts$.find('li.' + group).length ? (isEpisodeView ? '': '.' + group) : '',
mayAvoid = !isSet('allart') ? '.' + avoid : '.showall',
artBefore$ = curArt$.prevAll(viewable).not(mayAvoid),
artAfter$ = curArt$.nextAll(viewable).not(mayAvoid);
switch (dir) {
case 'next':
if (noArt(newArt$ = artAfter$.first()) && noArt(newArt$ = artBefore$.last())
&& noArt(newArt$ = curArt$))
newArt$ = null;
break;
case 'prev':
if (noArt(newArt$ = artBefore$.first()) && noArt(newArt$ = artAfter$.last())
&& noArt(newArt$ = curArt$))
newArt$ = null;
break;
case 'init':
init = !0;
if (noArt(newArt$ = curArt$))
newArt$ = null;
break;
case fave:
newArt$ = faveArt$;
break;
}
if (!init || (null == newArt$))
curArt$.addClass('background-rem').removeClass('background')
.fadeOut(800, 'linear', function() {$(this).removeClass('background-rem')});
if (null !== newArt$) {
newArt$.addClass('background').fadeIn(800, 'linear', function () {
$(this).removeClass('first-load')
});
liveStates$.removeClass(ratingVerbs).addClass(
newArt$.hasClass(group) && group || newArt$.hasClass(fave) && fave || newArt$.hasClass(avoid) && avoid || '');
}
maybeArrows();
refreshTitles();
return result;
}
setArt('init');
function maybeBackground() {
var backArts$ = $('#background-container'), result = !0;
if (isSet('allart')) {
if (!backArts$.find('li.background').length) {
backArts$.find('li').first().hide().addClass('background')
.fadeIn(400, 'linear', function() {$(this).removeClass('first-load')});
}
} else {
if (backArts$.find('li.' + fave).not('.background').length) {
setArt(fave);
} else if (!!backArts$.find('li.' + avoid).length
&& backArts$.find('li.' + avoid).length == backArts$.find('li').length) {
backArts$.find('li.' + avoid).fadeOut(800, 'linear', function () {
$(this).removeClass('background')
});
} else if (backArts$.find('li.background.' + avoid).length) {
setArt('next');
}
}
maybeArrows();
return result;
}
$('#art-next,#art-prev').on('click', function() {
return (!(setArt('art-prev' === $(this).attr('id') ? 'prev' : 'next')));
});
function key(e, kCode){
return e.hasOwnProperty('ctrlKey') && e.ctrlKey && e.hasOwnProperty('altKey') && e.altKey && (kCode == e.which)
}
$(document).on('keyup', function(e) {
var left = key(e, 37), up = key(e, 38), right = key(e, 39), down = key(e, 40),
s = key(e, 83), a = key(e, 65), f = key(e, 70), g = key(e, 71);
return (
(!isSet('oneof') && ((left && setArt('prev')) || (right && setArt('next'))))
|| (s && liveStates$.toggleClass('allart') && maybeBackground() && refreshTitles('proview'))
|| (g && setGroup()) || (up && setGroup() && (!isSet('allart') && $('#viewart').click() || !0))
|| (a && setAvoid()) || (down && setAvoid() && (!isSet('allart') && $('#translucent').click() || !0))
|| (f && setFave())
);
});
function rate(state, rating) {
var result = !0;
if (isSet('allart')) {
var rated = rating && isSet(rating);
liveStates$.removeClass(ratingVerbs);
if (rated) {
state = 0;
rating = '';
} else
liveStates$.addClass(rating);
var curArt$ = $('#background-container').find('li.background'),
art = /\?([^"]+)"/i.exec(curArt$.css('background-image'));
if (null != art) {
send('rate=' + state + '&' + art[1]);
curArt$.removeClass().addClass((!!rating.length ? rating + ' ' : '') + 'background');
}
maybeBackground();
refreshTitles('rate-art');
}
return result;
}
function setAvoid() {return rate(30, avoid);}
function setFave() {return rate(20, fave);}
function setGroup() {return rate(10, group);}
function setRnd() {return rate(0, '');}
$('#rate-art').on('click', function() {
return isSet('allart') &&
((isSet(fave) && setAvoid()) || (isSet(group) && setFave()) || (!isSet(avoid) && setGroup()) || setRnd()) || !0;
});
/*
* support functions
*/
function isSet(name) {return liveStates$.hasClass(name)}
function send(value) {
return $.get($.SickGear.Root + '/live_panel/?' + value + '&pg=' + (isEpisodeView ? 'ev' : 'ds'))}
if (jqTooltipUsed) {
panel$.find('a[title]').tooltip({placement: 'top', html: !0});
}
function refreshTitle(target$, title, refreshAll) {
return jqTooltipUsed
? target$.attr('data-original-title', title.replace(/<[\/]?em>/g, '')).tooltip('fixTitle') && refreshAll //|| target$.tooltip('show')
: target$.attr('title', title);
}
function refreshTitles(id) {
if (!$('#livepanel').length) return;
var refreshAll = /undefined/i.test(typeof(id)), elId = !refreshAll && id.replace('#', '') || id, result = !0;
if ('viewart' === elId || refreshAll) {
refreshTitle($('#viewart'),
isSet('poster-right') ? pTitle['viewart1']
: (isSet('back-art') ?
(isSet('viewart') ? pTitle['viewart4']
: (isSet('open-gear') ? pTitle['viewart3']
: (isSet('poster-off') ? pTitle['viewart2']
: (isEpisodeView ? pTitle['viewmode0'] : pTitle['viewart0']))))
: (isEpisodeView ? pTitle['viewmode0'] : pTitle['viewart0'])),
refreshAll);
}
if ('translucent' === elId || refreshAll) {
refreshTitle($('#translucent'), isSet('translucent') ? pTitle['translucent_on'] : pTitle['translucent_off'],
refreshAll);
}
if (config.hasArt && ('back-art' === elId || refreshAll)) {
refreshTitle($('#back-art'), isSet('back-art') ? pTitle['backart_on'] : pTitle['backart_off'],
refreshAll);
}
if ('rate-art' === elId || refreshAll) {
refreshTitle($('#rate-art'),
isSet(avoid) ? pTitle['rateart3']
: (isSet(fave) ? pTitle['rateart2']
: (isSet(group) ? pTitle['rateart1']
: pTitle['rateart0'])),
refreshAll);
}
if ('proview' === elId || refreshAll) {
refreshTitle($('#proview'),
isSet('back-art') ?
(isSet('allart') ? pTitle['viewmode3']
: (isSet('ii') ? pTitle['viewmode2']
: (isSet('pro') ? pTitle['viewmode1']
: pTitle['viewmode0'])))
: (isSet('pro') ? pTitle['viewmode1']
: pTitle['viewmode0']),
refreshAll);
}
return result;
}
refreshTitles();
});

View file

@ -1,46 +1,47 @@
$(function () {
$('.plotInfo, .plot-daybyday').each(function () {
var match = $(this).attr('id').match(/^plot(?:_info_|-)((\d+)_(\d+)[_x](\d+))$/);
var showName = $('#show-' + match[1]).attr('data-rawname');
$(this).qtip({
content: {
text: function(event, api) {
// deferred object ensuring the request is only made once
$.ajax({
url: $('#sbRoot').val() + '/home/plotDetails',
type: 'GET',
data: {
show: match[2],
episode: match[4],
season: match[3]
}
})
.then(function(content) {
// Set the tooltip content upon successful retrieval
api.set('content.text', ('undefined' === typeof(showName) ? ''
: ('' !== content ? '<b class="boldest">' + showName + '</b>' : showName))
+ ('' !== content ? ' ' + content : ''));
}, function(xhr, status, error) {
// Upon failure... set the tooltip content to the status and error value
api.set('content.text', status + ': ' + error);
});
return 'Loading...'; // Set initial text
}
},
show: {
solo: true
},
position: {
viewport: $(window),
my: 'left center',
adjust: {
y: -10,
x: 0
}
},
style: {
classes: 'qtip-rounded qtip-shadow'
}
});
});
});
var plotter = function(select$) {
select$.each(function() {
var match = $(this).attr('id').match(/^plot(?:_info_|-)((\d+)_(\d+)[_x](\d+))$/);
var showName = $('#show-' + match[1]).attr('data-rawname');
$(this).qtip({
content: {
text: function(event, api) {
// deferred object ensuring the request is only made once
$.ajax({
url: $.SickGear.Root + '/home/plotDetails',
type: 'GET',
data: {
show: match[2],
episode: match[4],
season: match[3]
}
})
.then(function(content) {
// Set the tooltip content upon successful retrieval
api.set('content.text', ('undefined' === typeof(showName) ? ''
: ('' !== content ? '<b class="boldest">' + showName + '</b>' : showName))
+ ('' !== content ? ' ' + content : ''));
}, function(xhr, status, error) {
// Upon failure... set the tooltip content to the status and error value
api.set('content.text', status + ': ' + error);
});
return 'Loading...'; // Set initial text
}
},
show: {
solo: true
},
position: {
viewport: $(window),
my: 'left center',
adjust: {
y: -10,
x: 0
}
},
style: {
classes: 'qtip-dark qtip-rounded qtip-shadow'
}
});
});
};
$(function () { plotter($('.plotInfo, .plot-daybyday')) });

View file

@ -19,7 +19,7 @@ $(function () {
}
},
style: {
classes: 'qtip-rounded qtip-shadow'
classes: 'qtip-dark qtip-rounded qtip-shadow'
}
});
});

View file

@ -6,7 +6,7 @@ $(function () {
text: function(event, api) {
// deferred object ensuring the request is only made once
$.ajax({
url: $('#sbRoot').val() + '/home/sceneExceptions',
url: $.SickGear.Root + '/home/sceneExceptions',
type: 'GET',
data: {
show: match[1]
@ -35,7 +35,7 @@ $(function () {
}
},
style: {
classes: 'qtip-rounded qtip-shadow'
classes: 'qtip-dark qtip-rounded qtip-shadow'
}
});
});

98
lib/fanart/__init__.py Normal file
View file

@ -0,0 +1,98 @@
__author__ = 'Andrea De Marco <24erre@gmail.com>'
__version__ = '1.4.0'
__classifiers__ = [
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Software Development :: Libraries',
]
__copyright__ = "2012, %s " % __author__
__license__ = """
Copyright %s.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or implied.
See the License for the specific language governing permissions and
limitations under the License.
""" % __copyright__
__docformat__ = 'restructuredtext en'
__doc__ = """
:abstract: Python interface to fanart.tv API
:version: %s
:author: %s
:contact: http://z4r.github.com/
:date: 2012-04-04
:copyright: %s
""" % (__version__, __author__, __license__)
def values(obj):
return [v for k, v in obj.__dict__.iteritems() if not k.startswith('_')]
BASEURL = 'http://webservice.fanart.tv/v3/%s/%s?api_key=%s'
class FORMAT(object):
JSON = 'JSON'
XML = 'XML'
PHP = 'PHP'
class WS(object):
MUSIC = 'music'
MOVIE = 'movies'
TV = 'tv'
class TYPE(object):
ALL = 'all'
class TV(object):
ART = 'clearart'
LOGO = 'clearlogo'
CHARACTER = 'characterart'
THUMB = 'tvthumb'
SEASONTHUMB = 'seasonthumb'
BACKGROUND = 'showbackground'
HDLOGO = 'hdtvlogo'
HDART = 'hdclearart'
POSTER = 'tvposter'
BANNER = 'tvbanner'
class MUSIC(object):
DISC = 'cdart'
LOGO = 'musiclogo'
BACKGROUND = 'artistbackground'
COVER = 'albumcover'
THUMB = 'artistthumb'
class MOVIE(object):
ART = 'movieart'
LOGO = 'movielogo'
DISC = 'moviedisc'
POSTER = 'movieposter'
BACKGROUND = 'moviebackground'
HDLOGO = 'hdmovielogo'
HDART = 'hdmovieclearart'
BANNER = 'moviebanner'
THUMB = 'moviethumb'
FORMAT_LIST = values(FORMAT)
WS_LIST = values(WS)
TYPE_LIST = values(TYPE.MUSIC) + values(TYPE.TV) + values(TYPE.MOVIE) + [TYPE.ALL]
MUSIC_TYPE_LIST = values(TYPE.MUSIC) + [TYPE.ALL]
TV_TYPE_LIST = values(TYPE.TV) + [TYPE.ALL]
MOVIE_TYPE_LIST = values(TYPE.MOVIE) + [TYPE.ALL]

86
lib/fanart/core.py Normal file
View file

@ -0,0 +1,86 @@
import requests
import re
import lib.fanart as fanart
from sickbeard.bs4_parser import BS4Parser
from .errors import ResponseFanartError
class Request(object):
def __init__(self, apikey, tvdb_id, ws=fanart.WS.TV):
self._apikey = apikey
self._tvdb_id = tvdb_id
self._ws = ws
self._response = None
self._web_url = 'https://fanart.tv/series/%s'
self._assets_url = 'https://assets.fanart.tv'
def __str__(self):
return fanart.BASEURL % (self._ws, self._tvdb_id, self._apikey)
def response(self):
try:
response = requests.get(str(self))
rjson = response.json()
image_type = u'showbackground'
rhtml = self.scrape_web(image_type)
if not isinstance(rjson, dict) and 0 == len(rhtml[image_type]):
raise Exception(response.text)
if 0 < len(rhtml[image_type]):
items = {image_type: []}
for item1 in rhtml[image_type]:
use_item = True
for k, item2 in enumerate(rjson[image_type] or []):
if '00' == item2['lang']: # adjust api data of no language to a default
rjson[image_type][k]['lang'] = u'en'
if item1['id'] == item2['id']:
use_item = False
break
if use_item:
items[image_type] += [item1]
rjson[image_type] += items[image_type]
return rjson
except Exception as e:
raise ResponseFanartError(str(e))
def scrape_web(self, image_type):
try:
data = requests.get(self._web_url % self._tvdb_id)
if not data:
return
with BS4Parser(data.text, features=['html5lib', 'permissive']) as html:
ul_item = html.find('ul', attrs={'class': image_type})
if ul_item:
li_items = ul_item('li')
if li_items:
image_urls = {image_type: []}
for li_item in li_items:
image_id = None
item = li_item.find('a', attrs={'class': 'download'}).get('href')
if item:
match = re.search(r'image=(\d+)', item, re.I)
if match:
image_id = u'%s' % match.group(1)
item = li_item.find('a', attrs={'rel': image_type}).get('href')
image_url = (u'%s%s' % (self._assets_url, item), None)[None is item]
item = li_item.find('div', attrs={'class': 'votes'}).get_text()
image_likes = (item, 0)[None is item]
item = li_item.find('div', attrs={'class': 'metrics'}).get_text()
image_lang = u'None found'
if item:
match = re.search(r'Language:\s*(\w+)', item, re.I)
if match:
image_lang = u'%s' % (match.group(1)[0:2:].lower(), 'en')['None' == match.group(1)]
if not (None is image_id or None is image_url):
image_urls[image_type].append({u'id': image_id, u'url': image_url, u'likes': image_likes, u'lang': image_lang})
return image_urls
except Exception, e:
pass

11
lib/fanart/errors.py Normal file
View file

@ -0,0 +1,11 @@
class FanartError(Exception):
def __str__(self):
return ', '.join(map(str, self.args))
def __repr__(self):
name = self.__class__.__name__
return '%s%r' % (name, self.args)
class ResponseFanartError(FanartError):
pass

46
lib/fanart/immutable.py Normal file
View file

@ -0,0 +1,46 @@
class Immutable(object):
_mutable = False
def __setattr__(self, name, value):
if self._mutable or name == '_mutable':
super(Immutable, self).__setattr__(name, value)
else:
raise TypeError("Can't modify immutable instance")
def __delattr__(self, name):
if self._mutable:
super(Immutable, self).__delattr__(name)
else:
raise TypeError("Can't modify immutable instance")
def __eq__(self, other):
return hash(self) == hash(other)
def __hash__(self):
return hash(repr(self))
def __repr__(self):
return '%s(%s)' % (
self.__class__.__name__,
', '.join(['{0}={1}'.format(k, repr(v)) for k, v in self])
)
def __iter__(self):
l = self.__dict__.keys()
l.sort()
for k in l:
if not k.startswith('_'):
yield k, getattr(self, k)
@staticmethod
def mutablemethod(f):
def func(self, *args, **kwargs):
if isinstance(self, Immutable):
old_mutable = self._mutable
self._mutable = True
res = f(self, *args, **kwargs)
self._mutable = old_mutable
else:
res = f(self, *args, **kwargs)
return res
return func

68
lib/fanart/items.py Normal file
View file

@ -0,0 +1,68 @@
import json
import os
import requests
from .core import Request
from .immutable import Immutable
class LeafItem(Immutable):
KEY = NotImplemented
@Immutable.mutablemethod
def __init__(self, id, url, likes):
self.id = int(id)
self.url = url
self.likes = int(likes)
self._content = None
@classmethod
def from_dict(cls, resource):
return cls(**dict([(str(k), v) for k, v in resource.iteritems()]))
@classmethod
def extract(cls, resource):
return [cls.from_dict(i) for i in resource.get(cls.KEY, {})]
@Immutable.mutablemethod
def content(self):
if not self._content:
self._content = requests.get(self.url).content
return self._content
def __str__(self):
return self.url
class ResourceItem(Immutable):
WS = NotImplemented
request_cls = Request
@classmethod
def from_dict(cls, map):
raise NotImplementedError
@classmethod
def get(cls, id):
map = cls.request_cls(
apikey=os.environ.get('FANART_APIKEY'),
tvdb_id=id,
ws=cls.WS
).response()
return cls.from_dict(map)
def json(self, **kw):
return json.dumps(
self,
default=lambda o: dict([(k, v) for k, v in o.__dict__.items() if not k.startswith('_')]),
**kw
)
class CollectableItem(Immutable):
@classmethod
def from_dict(cls, key, map):
raise NotImplementedError
@classmethod
def collection_from_dict(cls, map):
return [cls.from_dict(k, v) for k, v in map.iteritems()]

108
lib/fanart/tv.py Normal file
View file

@ -0,0 +1,108 @@
import lib.fanart as fanart
from .items import LeafItem, Immutable, ResourceItem
__all__ = (
'CharacterItem',
'ArtItem',
'LogoItem',
'BackgroundItem',
'SeasonItem',
'ThumbItem',
'HdLogoItem',
'HdArtItem',
'PosterItem',
'BannerItem',
'TvShow',
)
class TvItem(LeafItem):
@Immutable.mutablemethod
def __init__(self, id, url, likes, lang):
super(TvItem, self).__init__(id, url, likes)
self.lang = lang
class SeasonedTvItem(TvItem):
@Immutable.mutablemethod
def __init__(self, id, url, likes, lang, season):
super(SeasonedTvItem, self).__init__(id, url, likes, lang)
self.season = 0 if season == 'all' else int(season or 0)
class CharacterItem(TvItem):
KEY = fanart.TYPE.TV.CHARACTER
class ArtItem(TvItem):
KEY = fanart.TYPE.TV.ART
class LogoItem(TvItem):
KEY = fanart.TYPE.TV.LOGO
class BackgroundItem(SeasonedTvItem):
KEY = fanart.TYPE.TV.BACKGROUND
class SeasonItem(SeasonedTvItem):
KEY = fanart.TYPE.TV.SEASONTHUMB
class ThumbItem(TvItem):
KEY = fanart.TYPE.TV.THUMB
class HdLogoItem(TvItem):
KEY = fanart.TYPE.TV.HDLOGO
class HdArtItem(TvItem):
KEY = fanart.TYPE.TV.HDART
class PosterItem(TvItem):
KEY = fanart.TYPE.TV.POSTER
class BannerItem(TvItem):
KEY = fanart.TYPE.TV.BANNER
class TvShow(ResourceItem):
WS = fanart.WS.TV
@Immutable.mutablemethod
def __init__(self, name, tvdbid, backgrounds, characters, arts, logos, seasons, thumbs, hdlogos, hdarts, posters,
banners):
self.name = name
self.tvdbid = tvdbid
self.backgrounds = backgrounds
self.characters = characters
self.arts = arts
self.logos = logos
self.seasons = seasons
self.thumbs = thumbs
self.hdlogos = hdlogos
self.hdarts = hdarts
self.posters = posters
self.banners = banners
@classmethod
def from_dict(cls, resource):
assert len(resource) == 1, 'Bad Format Map'
name, resource = resource.items()[0]
return cls(
name=name,
tvdbid=resource['thetvdb_id'],
backgrounds=BackgroundItem.extract(resource),
characters=CharacterItem.extract(resource),
arts=ArtItem.extract(resource),
logos=LogoItem.extract(resource),
seasons=SeasonItem.extract(resource),
thumbs=ThumbItem.extract(resource),
hdlogos=HdLogoItem.extract(resource),
hdarts=HdArtItem.extract(resource),
posters=PosterItem.extract(resource),
banners=BannerItem.extract(resource),
)

View file

@ -19,26 +19,24 @@ class TMDB:
url = TMDB.url + '/' + path + '?api_key=' + TMDB.api_key
if method == 'GET':
headers = {'Accept': 'application/json'}
content = requests.get(url, params=params, headers=headers).content
content = requests.get(url, params=params, headers=headers, verify=False).content
elif method == 'POST':
for key in params.keys():
url += '&' + key + '=' + params[key]
headers = {'Content-Type': 'application/json', \
'Accept': 'application/json'}
content = requests.post(url, data=json.dumps(json_body), \
headers=headers).content
content = requests.post(url, data=json.dumps(json_body), headers=headers, verify=False).content
elif method == 'DELETE':
for key in params.keys():
url += '&' + key + '=' + params[key]
headers = {'Content-Type': 'application/json', \
'Accept': 'application/json'}
content = requests.delete(url, data=json.dumps(json_body), \
headers=headers).content
content = requests.delete(url, data=json.dumps(json_body), headers=headers, verify=False).content
else:
raise Exception('method: ' + method + ' not supported.')
response = json.loads(content.decode('utf-8'))
return response
#
# Set attributes to dictionary values.
# - e.g.
@ -81,7 +79,7 @@ class TMDB:
response = TMDB._request('GET', path, params)
TMDB._set_attrs_to_values(self, response)
return response
# optional parameters: page, language
def lists(self, params={}):
path = 'account' + '/' + str(self.session_id) + '/lists'
@ -121,7 +119,7 @@ class TMDB:
response = TMDB._request('GET', path, params)
TMDB._set_attrs_to_values(self, response)
return response
# required JSON body: movie_id, movie_watchlist
def movie_watchlist_post(self, json_body):
path = 'account' + '/' + str(json_body['movie_id']) + \
@ -175,7 +173,7 @@ class TMDB:
response = TMDB._request('GET', path, params)
TMDB._set_attrs_to_values(self, response)
return response
# optional parameters: page, start_date, end_date
def person(self, params={}):
path = 'person/changes'
@ -218,7 +216,7 @@ class TMDB:
response = TMDB._request('GET', path)
TMDB._set_attrs_to_values(self, response)
return response
# optional parameters: page, language
def movies(self, params={}):
path = 'company' + '/' + str(self.id) + '/movies'
@ -249,9 +247,9 @@ class TMDB:
def __init__(self):
pass
# optional parameters: page, language, sort_by, include_adult, year,
# primary_release_year, vote_count.gte, vote_average.gte, with_genres,
# release_date.gte, release_date.lte, certification_country,
# optional parameters: page, language, sort_by, include_adult, year,
# primary_release_year, vote_count.gte, vote_average.gte, with_genres,
# release_date.gte, release_date.lte, certification_country,
# certification.lte, with_companies
def movie(self, params):
path = 'discover/movie'
@ -297,7 +295,7 @@ class TMDB:
response = TMDB._request('GET', path, params)
TMDB._set_attrs_to_values(self, response)
return response
# optional parameters: page, language, include_all_movies, include_adult
def movies(self, params={}):
path = 'genre' + '/' + str(self.id) + '/movies'
@ -318,7 +316,7 @@ class TMDB:
response = TMDB._request('GET', path)
TMDB._set_attrs_to_values(self, response)
return response
#
# Keywords
# http://docs.themoviedb.apiary.io/#keywords
@ -378,7 +376,7 @@ class TMDB:
response = TMDB._request('POST', path, params, json_body)
TMDB._set_attrs_to_values(self, response)
return response
# required JSON body: media_id
def remove_item(self, json_body):
path = 'list' + '/' + str(self.id) + '/remove_item'
@ -460,21 +458,21 @@ class TMDB:
response = TMDB._request('GET', path, params)
TMDB._set_attrs_to_values(self, response)
return response
# optional parameters: page, language
def reviews(self, params={}):
path = 'movie' + '/' + str(self.id) + '/reviews'
response = TMDB._request('GET', path, params)
TMDB._set_attrs_to_values(self, response)
return response
# optional parameters: page, language
def lists(self, params={}):
path = 'movie' + '/' + str(self.id) + '/lists'
response = TMDB._request('GET', path, params)
TMDB._set_attrs_to_values(self, response)
return response
# optional parameters: start_date, end_date
def changes(self, params={}):
path = 'movie' + '/' + str(self.id) + '/changes'
@ -501,7 +499,7 @@ class TMDB:
response = TMDB._request('GET', path, params)
TMDB._set_attrs_to_values(self, response)
return response
# optional parameters: page, language
def popular(self, params={}):
path = 'movie/popular'
@ -629,7 +627,7 @@ class TMDB:
pass
# required parameters: query
# optional parameters: page, language, include_adult, year,
# optional parameters: page, language, include_adult, year,
# primary_release_year, search_type
def movie(self, params):
path = 'search/movie'
@ -699,7 +697,7 @@ class TMDB:
response = TMDB._request('GET', path, params)
TMDB._set_attrs_to_values(self, response)
return response
# optional parameter: language
def credits(self, params={}):
path = 'tv' + '/' + str(self.id) + '/credits'

View file

@ -35,7 +35,7 @@ import uuid
sys.path.insert(1, os.path.abspath('../lib'))
from sickbeard import helpers, encodingKludge as ek
from sickbeard import db, logger, naming, metadata, providers, scene_exceptions, scene_numbering, \
from sickbeard import db, image_cache, logger, naming, metadata, providers, scene_exceptions, scene_numbering, \
scheduler, auto_post_processer, search_queue, search_propers, search_recent, search_backlog, \
show_queue, show_updater, subtitles, traktChecker, version_checker, indexermapper, classes
from sickbeard.config import CheckSection, check_setting_int, check_setting_str, ConfigMigrator, minimax
@ -423,13 +423,28 @@ EMAIL_LIST = None
GUI_NAME = None
DEFAULT_HOME = None
FANART_LIMIT = None
FANART_PANEL = None
FANART_RATINGS = {}
HOME_LAYOUT = None
HISTORY_LAYOUT = None
POSTER_SORTBY = None
POSTER_SORTDIR = None
DISPLAY_SHOW_VIEWMODE = 0
DISPLAY_SHOW_BACKGROUND = False
DISPLAY_SHOW_BACKGROUND_TRANSLUCENT = False
DISPLAY_SHOW_VIEWART = 0
DISPLAY_SHOW_MINIMUM = True
DISPLAY_SHOW_SPECIALS = False
EPISODE_VIEW_VIEWMODE = 0
EPISODE_VIEW_BACKGROUND = False
EPISODE_VIEW_BACKGROUND_TRANSLUCENT = False
EPISODE_VIEW_LAYOUT = None
EPISODE_VIEW_SORT = None
EPISODE_VIEW_DISPLAY_PAUSED = False
EPISODE_VIEW_POSTERS = True
EPISODE_VIEW_MISSED_RANGE = None
HISTORY_LAYOUT = None
FUZZY_DATING = False
TRIM_ZERO = False
DATE_PRESET = None
@ -437,8 +452,6 @@ TIME_PRESET = None
TIME_PRESET_W_SECONDS = None
TIMEZONE_DISPLAY = None
THEME_NAME = None
POSTER_SORTBY = None
POSTER_SORTDIR = None
USE_SUBTITLES = False
SUBTITLES_LANGUAGES = []
@ -462,6 +475,7 @@ REQUIRE_WORDS = ''
CALENDAR_UNPROTECTED = False
TMDB_API_KEY = 'edc5f123313769de83a71e157758030b'
FANART_API_KEY = '3728ca1a2a937ba0c93b6e63cc86cecb'
# to switch between staging and production TRAKT environment
TRAKT_STAGING = False
@ -518,13 +532,16 @@ def initialize(console_logging=True):
# Views
global GUI_NAME, HOME_LAYOUT, POSTER_SORTBY, POSTER_SORTDIR, DISPLAY_SHOW_SPECIALS, \
EPISODE_VIEW_LAYOUT, EPISODE_VIEW_SORT, EPISODE_VIEW_DISPLAY_PAUSED, \
EPISODE_VIEW_MISSED_RANGE, HISTORY_LAYOUT
EPISODE_VIEW_MISSED_RANGE, EPISODE_VIEW_POSTERS, FANART_PANEL, FANART_RATINGS, \
EPISODE_VIEW_VIEWMODE, EPISODE_VIEW_BACKGROUND, EPISODE_VIEW_BACKGROUND_TRANSLUCENT, \
DISPLAY_SHOW_VIEWMODE, DISPLAY_SHOW_BACKGROUND, DISPLAY_SHOW_BACKGROUND_TRANSLUCENT, \
DISPLAY_SHOW_VIEWART, DISPLAY_SHOW_MINIMUM, DISPLAY_SHOW_SPECIALS, HISTORY_LAYOUT
# Gen Config/Misc
global LAUNCH_BROWSER, UPDATE_SHOWS_ON_START, SHOW_UPDATE_HOUR, \
TRASH_REMOVE_SHOW, TRASH_ROTATE_LOGS, ACTUAL_LOG_DIR, LOG_DIR, INDEXER_TIMEOUT, ROOT_DIRS, \
VERSION_NOTIFY, AUTO_UPDATE, UPDATE_FREQUENCY, NOTIFY_ON_UPDATE
# Gen Config/Interface
global THEME_NAME, DEFAULT_HOME, SHOWLIST_TAGVIEW, SHOW_TAGS, \
global THEME_NAME, DEFAULT_HOME, FANART_LIMIT, SHOWLIST_TAGVIEW, SHOW_TAGS, \
HOME_SEARCH_FOCUS, USE_IMDB_INFO, IMDB_ACCOUNTS, SORT_ARTICLE, FUZZY_DATING, TRIM_ZERO, \
DATE_PRESET, TIME_PRESET, TIME_PRESET_W_SECONDS, TIMEZONE_DISPLAY, \
WEB_USERNAME, WEB_PASSWORD, CALENDAR_UNPROTECTED, USE_API, API_KEY, WEB_PORT, WEB_LOG, \
@ -611,6 +628,8 @@ def initialize(console_logging=True):
'pyTivo', 'NMA', 'Pushalot', 'Pushbullet', 'Subtitles'):
CheckSection(CFG, stanza)
update_config = False
# wanted branch
BRANCH = check_setting_str(CFG, 'General', 'branch', '')
@ -645,6 +664,14 @@ def initialize(console_logging=True):
THEME_NAME = check_setting_str(CFG, 'GUI', 'theme_name', 'dark')
GUI_NAME = check_setting_str(CFG, 'GUI', 'gui_name', 'slick')
DEFAULT_HOME = check_setting_str(CFG, 'GUI', 'default_home', 'home')
FANART_LIMIT = check_setting_int(CFG, 'GUI', 'fanart_limit', 3)
FANART_PANEL = check_setting_str(CFG, 'GUI', 'fanart_panel', 'highlight2')
FANART_RATINGS = check_setting_str(CFG, 'GUI', 'fanart_ratings', None)
if None is not FANART_RATINGS:
FANART_RATINGS = ast.literal_eval(FANART_RATINGS or '{}')
else:
FANART_RATINGS = ast.literal_eval(check_setting_str(CFG, 'GUI', 'backart_ratings', None) or '{}')
update_config |= image_cache.ImageCache().clean_fanart()
USE_IMDB_INFO = bool(check_setting_int(CFG, 'GUI', 'use_imdb_info', 1))
IMDB_ACCOUNTS = CFG.get('GUI', []).get('imdb_accounts', [IMDB_DEFAULT_LIST_ID, IMDB_DEFAULT_LIST_NAME])
HOME_SEARCH_FOCUS = bool(check_setting_int(CFG, 'General', 'home_search_focus', HOME_SEARCH_FOCUS))
@ -1042,16 +1069,27 @@ def initialize(console_logging=True):
METADATA_KODI = check_setting_str(CFG, 'General', 'metadata_kodi', '0|0|0|0|0|0|0|0|0|0')
HOME_LAYOUT = check_setting_str(CFG, 'GUI', 'home_layout', 'poster')
HISTORY_LAYOUT = check_setting_str(CFG, 'GUI', 'history_layout', 'detailed')
DISPLAY_SHOW_SPECIALS = bool(check_setting_int(CFG, 'GUI', 'display_show_specials', 1))
EPISODE_VIEW_LAYOUT = check_setting_str(CFG, 'GUI', 'episode_view_layout', 'banner')
EPISODE_VIEW_SORT = check_setting_str(CFG, 'GUI', 'episode_view_sort', 'time')
EPISODE_VIEW_DISPLAY_PAUSED = bool(check_setting_int(CFG, 'GUI', 'episode_view_display_paused', 0))
EPISODE_VIEW_MISSED_RANGE = check_setting_int(CFG, 'GUI', 'episode_view_missed_range', 7)
POSTER_SORTBY = check_setting_str(CFG, 'GUI', 'poster_sortby', 'name')
POSTER_SORTDIR = check_setting_int(CFG, 'GUI', 'poster_sortdir', 1)
DISPLAY_SHOW_VIEWMODE = check_setting_int(CFG, 'GUI', 'display_show_viewmode', 0)
DISPLAY_SHOW_BACKGROUND = bool(check_setting_int(CFG, 'GUI', 'display_show_background', 0))
DISPLAY_SHOW_BACKGROUND_TRANSLUCENT = bool(check_setting_int(
CFG, 'GUI', 'display_show_background_translucent', 0))
DISPLAY_SHOW_VIEWART = check_setting_int(CFG, 'GUI', 'display_show_viewart', 0)
DISPLAY_SHOW_MINIMUM = bool(check_setting_int(CFG, 'GUI', 'display_show_minimum', 1))
DISPLAY_SHOW_SPECIALS = bool(check_setting_int(CFG, 'GUI', 'display_show_specials', 0))
EPISODE_VIEW_VIEWMODE = check_setting_int(CFG, 'GUI', 'episode_view_viewmode', 0)
EPISODE_VIEW_BACKGROUND = bool(check_setting_int(CFG, 'GUI', 'episode_view_background', 0))
EPISODE_VIEW_BACKGROUND_TRANSLUCENT = bool(check_setting_int(
CFG, 'GUI', 'episode_view_background_translucent', 0))
EPISODE_VIEW_LAYOUT = check_setting_str(CFG, 'GUI', 'episode_view_layout', 'daybyday')
EPISODE_VIEW_SORT = check_setting_str(CFG, 'GUI', 'episode_view_sort', 'time')
EPISODE_VIEW_DISPLAY_PAUSED = bool(check_setting_int(CFG, 'GUI', 'episode_view_display_paused', 1))
EPISODE_VIEW_POSTERS = bool(check_setting_int(CFG, 'GUI', 'episode_view_posters', 1))
EPISODE_VIEW_MISSED_RANGE = check_setting_int(CFG, 'GUI', 'episode_view_missed_range', 7)
HISTORY_LAYOUT = check_setting_str(CFG, 'GUI', 'history_layout', 'detailed')
# initialize NZB and TORRENT providers
providerList = providers.makeProviderList()
@ -1134,7 +1172,9 @@ def initialize(console_logging=True):
nzb_prov.enable_backlog = bool(check_setting_int(CFG, prov_id_uc, prov_id + '_enable_backlog', 1))
if not os.path.isfile(CONFIG_FILE):
logger.log(u'Unable to find \'' + CONFIG_FILE + '\', all settings will be default!', logger.DEBUG)
logger.log(u'Unable to find \'%s\', all settings will be default!' % CONFIG_FILE, logger.DEBUG)
save_config()
elif update_config:
save_config()
# start up all the threads
@ -1199,8 +1239,8 @@ def initialize(console_logging=True):
showUpdateScheduler = scheduler.Scheduler(
show_updater.ShowUpdater(),
cycleTime=datetime.timedelta(hours=1),
threadName='SHOWUPDATER',
start_time=datetime.time(hour=SHOW_UPDATE_HOUR),
threadName='SHOWUPDATER',
prevent_cycle_run=showQueueScheduler.action.isShowUpdateRunning) # 3AM
# searchers
@ -1213,8 +1253,8 @@ def initialize(console_logging=True):
recentSearchScheduler = scheduler.Scheduler(
search_recent.RecentSearcher(),
cycleTime=update_interval,
threadName='RECENTSEARCHER',
run_delay=update_now if RECENTSEARCH_STARTUP else datetime.timedelta(minutes=5),
threadName='RECENTSEARCHER',
prevent_cycle_run=searchQueueScheduler.action.is_recentsearch_in_progress)
if [x for x in providers.sortedProviderList() if x.is_active() and
@ -1235,8 +1275,8 @@ def initialize(console_logging=True):
backlogSearchScheduler = search_backlog.BacklogSearchScheduler(
search_backlog.BacklogSearcher(),
cycleTime=datetime.timedelta(minutes=get_backlog_cycle_time()),
threadName='BACKLOG',
run_delay=datetime.timedelta(minutes=backlogdelay),
threadName='BACKLOG',
prevent_cycle_run=searchQueueScheduler.action.is_standard_backlog_in_progress)
propers_searcher = search_propers.ProperSearcher()
@ -1251,9 +1291,9 @@ def initialize(console_logging=True):
properFinderScheduler = scheduler.Scheduler(
propers_searcher,
cycleTime=update_interval,
threadName='FINDPROPERS',
start_time=run_at,
run_delay=update_interval,
start_time=run_at,
threadName='FINDPROPERS',
prevent_cycle_run=searchQueueScheduler.action.is_propersearch_in_progress)
# processors
@ -1264,10 +1304,8 @@ def initialize(console_logging=True):
silent=not PROCESS_AUTOMATICALLY)
"""
traktCheckerScheduler = scheduler.Scheduler(
traktChecker.TraktChecker(),
cycleTime=datetime.timedelta(hours=1),
threadName='TRAKTCHECKER',
silent=not USE_TRAKT)
traktChecker.TraktChecker(), cycleTime=datetime.timedelta(hours=1),
threadName='TRAKTCHECKER', silent=not USE_TRAKT)
"""
subtitlesFinderScheduler = scheduler.Scheduler(
subtitles.SubtitlesFinder(),
@ -1389,17 +1427,17 @@ def save_config():
# For passwords you must include the word `password` in the item_name and
# add `helpers.encrypt(ITEM_NAME, ENCRYPTION_VERSION)` in save_config()
new_config['General'] = {}
new_config['General']['config_version'] = CONFIG_VERSION
new_config['General']['branch'] = BRANCH
new_config['General']['git_remote'] = GIT_REMOTE
new_config['General']['cur_commit_hash'] = CUR_COMMIT_HASH
new_config['General']['cur_commit_branch'] = CUR_COMMIT_BRANCH
new_config['General']['config_version'] = CONFIG_VERSION
new_config['General']['encryption_version'] = int(ENCRYPTION_VERSION)
new_config['General']['log_dir'] = ACTUAL_LOG_DIR if ACTUAL_LOG_DIR else 'Logs'
new_config['General']['file_logging_preset'] = FILE_LOGGING_PRESET if FILE_LOGGING_PRESET else 'DB'
new_config['General']['socket_timeout'] = SOCKET_TIMEOUT
new_config['General']['web_port'] = WEB_PORT
new_config['General']['web_host'] = WEB_HOST
new_config['General']['web_port'] = WEB_PORT
new_config['General']['web_ipv6'] = int(WEB_IPV6)
new_config['General']['web_log'] = int(WEB_LOG)
new_config['General']['web_root'] = WEB_ROOT
@ -1753,6 +1791,9 @@ def save_config():
new_config['GUI']['gui_name'] = GUI_NAME
new_config['GUI']['theme_name'] = THEME_NAME
new_config['GUI']['default_home'] = DEFAULT_HOME
new_config['GUI']['fanart_limit'] = FANART_LIMIT
new_config['GUI']['fanart_panel'] = FANART_PANEL
new_config['GUI']['fanart_ratings'] = '%s' % FANART_RATINGS
new_config['GUI']['use_imdb_info'] = int(USE_IMDB_INFO)
new_config['GUI']['imdb_accounts'] = IMDB_ACCOUNTS
new_config['GUI']['fuzzy_dating'] = int(FUZZY_DATING)
@ -1765,17 +1806,30 @@ def save_config():
new_config['GUI']['showlist_tagview'] = SHOWLIST_TAGVIEW
new_config['GUI']['home_layout'] = HOME_LAYOUT
new_config['GUI']['history_layout'] = HISTORY_LAYOUT
new_config['GUI']['poster_sortby'] = POSTER_SORTBY
new_config['GUI']['poster_sortdir'] = POSTER_SORTDIR
new_config['GUI']['display_show_viewmode'] = int(DISPLAY_SHOW_VIEWMODE)
new_config['GUI']['display_show_background'] = int(DISPLAY_SHOW_BACKGROUND)
new_config['GUI']['display_show_background_translucent'] = int(DISPLAY_SHOW_BACKGROUND_TRANSLUCENT)
new_config['GUI']['display_show_viewart'] = int(DISPLAY_SHOW_VIEWART)
new_config['GUI']['display_show_minimum'] = int(DISPLAY_SHOW_MINIMUM)
new_config['GUI']['display_show_specials'] = int(DISPLAY_SHOW_SPECIALS)
new_config['GUI']['episode_view_viewmode'] = int(EPISODE_VIEW_VIEWMODE)
new_config['GUI']['episode_view_background'] = int(EPISODE_VIEW_BACKGROUND)
new_config['GUI']['episode_view_background_translucent'] = int(EPISODE_VIEW_BACKGROUND_TRANSLUCENT)
new_config['GUI']['episode_view_layout'] = EPISODE_VIEW_LAYOUT
new_config['GUI']['episode_view_sort'] = EPISODE_VIEW_SORT
new_config['GUI']['episode_view_display_paused'] = int(EPISODE_VIEW_DISPLAY_PAUSED)
new_config['GUI']['episode_view_posters'] = int(EPISODE_VIEW_POSTERS)
new_config['GUI']['episode_view_missed_range'] = int(EPISODE_VIEW_MISSED_RANGE)
new_config['GUI']['poster_sortby'] = POSTER_SORTBY
new_config['GUI']['poster_sortdir'] = POSTER_SORTDIR
new_config['GUI']['show_tags'] = ','.join(SHOW_TAGS)
new_config['GUI']['showlist_tagview'] = SHOWLIST_TAGVIEW
new_config['GUI']['show_tag_default'] = SHOW_TAG_DEFAULT
new_config['GUI']['history_layout'] = HISTORY_LAYOUT
new_config['Subtitles'] = {}
new_config['Subtitles']['use_subtitles'] = int(USE_SUBTITLES)

View file

@ -120,6 +120,11 @@ class Quality:
FAILED: 'Failed',
SNATCHED_BEST: 'Snatched (Best)'}
@staticmethod
def get_quality_css(quality):
return (Quality.qualityStrings[quality].replace('2160p', 'UHD2160p').replace('1080p', 'HD1080p')
.replace('720p', 'HD720p').replace('HD TV', 'HD720p').replace('RawHD TV', 'RawHD'))
@staticmethod
def _getStatusStrings(status):
toReturn = {}

View file

@ -363,16 +363,14 @@ def make_dirs(path):
parents
"""
logger.log(u"Checking if the path " + path + " already exists", logger.DEBUG)
if not ek.ek(os.path.isdir, path):
# Windows, create all missing folders
if os.name == 'nt' or os.name == 'ce':
if os.name in ('nt', 'ce'):
try:
logger.log(u"Folder " + path + " doesn't exist, creating it", logger.DEBUG)
logger.log(u'Path %s doesn\'t exist, creating it' % path, logger.DEBUG)
ek.ek(os.makedirs, path)
except (OSError, IOError) as e:
logger.log(u"Failed creating " + path + " : " + ex(e), logger.ERROR)
logger.log(u'Failed creating %s : %s' % (path, ex(e)), logger.ERROR)
return False
# not Windows, create all missing folders and set permissions
@ -389,14 +387,14 @@ def make_dirs(path):
continue
try:
logger.log(u"Folder " + sofar + " doesn't exist, creating it", logger.DEBUG)
logger.log(u'Path %s doesn\'t exist, creating it' % sofar, logger.DEBUG)
ek.ek(os.mkdir, sofar)
# use normpath to remove end separator, otherwise checks permissions against itself
chmodAsParent(ek.ek(os.path.normpath, sofar))
# do the library update for synoindex
notifiers.synoindex_notifier.addFolder(sofar)
except (OSError, IOError) as e:
logger.log(u"Failed creating " + sofar + " : " + ex(e), logger.ERROR)
logger.log(u'Failed creating %s : %s' % (sofar, ex(e)), logger.ERROR)
return False
return True
@ -799,6 +797,18 @@ def md5_for_file(filename, block_size=2 ** 16):
return None
def md5_for_text(text):
result = None
try:
md5 = hashlib.md5()
md5.update(str(text))
raw_md5 = md5.hexdigest()
result = raw_md5[17:] + raw_md5[9:17] + raw_md5[0:9]
except (StandardError, Exception):
pass
return result
def get_lan_ip():
"""
Simple function to get LAN localhost_ip
@ -959,6 +969,7 @@ def validateShow(show, season=None, episode=None):
try:
lINDEXER_API_PARMS = sickbeard.indexerApi(show.indexer).api_params.copy()
lINDEXER_API_PARMS['dvdorder'] = 0 != show.dvdorder
if indexer_lang and not indexer_lang == 'en':
lINDEXER_API_PARMS['language'] = indexer_lang

View file

@ -16,17 +16,28 @@
# You should have received a copy of the GNU General Public License
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
import datetime
import fnmatch
import glob
import os.path
import re
import shutil
import time
import sickbeard
from sickbeard import helpers, logger, exceptions
from sickbeard import encodingKludge as ek
from sickbeard import db
from sickbeard.metadata.generic import GenericMetadata
from lib.hachoir_parser import createParser
from lib.hachoir_metadata import extractMetadata
from lib.send2trash import send2trash
try:
import zlib
except:
pass
class ImageCache:
@ -36,12 +47,21 @@ class ImageCache:
def __del__(self):
pass
def _cache_dir(self):
@staticmethod
def _cache_dir():
"""
Builds up the full path to the image cache directory
"""
return ek.ek(os.path.abspath, ek.ek(os.path.join, sickbeard.CACHE_DIR, 'images'))
def _fanart_dir(self, indexer_id=None):
"""
Builds up the full path to the fanart image cache directory
"""
args = [os.path.join, self._cache_dir(), 'fanart'] + \
(None is not indexer_id and [str(indexer_id).split('.')[0]] or [])
return ek.ek(os.path.abspath, ek.ek(*args))
def _thumbnails_dir(self):
"""
Builds up the full path to the thumbnails image cache directory
@ -53,94 +73,111 @@ class ImageCache:
Builds up the path to a poster cache for a given Indexer ID
returns: a full path to the cached poster file for the given Indexer ID
indexer_id: ID of the show to use in the file name
"""
poster_file_name = str(indexer_id) + '.poster.jpg'
return ek.ek(os.path.join, self._cache_dir(), poster_file_name)
return ek.ek(os.path.join, self._cache_dir(), '%s.poster.jpg' % indexer_id)
def banner_path(self, indexer_id):
"""
Builds up the path to a banner cache for a given Indexer ID
returns: a full path to the cached banner file for the given Indexer ID
indexer_id: ID of the show to use in the file name
"""
banner_file_name = str(indexer_id) + '.banner.jpg'
return ek.ek(os.path.join, self._cache_dir(), banner_file_name)
return ek.ek(os.path.join, self._cache_dir(), '%s.banner.jpg' % indexer_id)
def fanart_path(self, indexer_id):
"""
Builds up the path to a fanart cache for a given Indexer ID
returns: a full path to the cached fanart file for the given Indexer ID
indexer_id: ID of the show to use in the file name
"""
return ek.ek(os.path.join, self._fanart_dir(indexer_id), '%s.fanart.jpg' % indexer_id)
def poster_thumb_path(self, indexer_id):
"""
Builds up the path to a poster cache for a given Indexer ID
returns: a full path to the cached poster file for the given Indexer ID
indexer_id: ID of the show to use in the file name
"""
posterthumb_file_name = str(indexer_id) + '.poster.jpg'
return ek.ek(os.path.join, self._thumbnails_dir(), posterthumb_file_name)
return ek.ek(os.path.join, self._thumbnails_dir(), '%s.poster.jpg' % indexer_id)
def banner_thumb_path(self, indexer_id):
"""
Builds up the path to a poster cache for a given Indexer ID
returns: a full path to the cached poster file for the given Indexer ID
indexer_id: ID of the show to use in the file name
"""
bannerthumb_file_name = str(indexer_id) + '.banner.jpg'
return ek.ek(os.path.join, self._thumbnails_dir(), bannerthumb_file_name)
return ek.ek(os.path.join, self._thumbnails_dir(), '%s.banner.jpg' % indexer_id)
@staticmethod
def has_file(image_file):
"""
Returns true if a image_file exists
"""
result = []
for filename in ek.ek(glob.glob, image_file):
result.append(ek.ek(os.path.isfile, filename) and filename)
logger.log(u'Found cached %s' % filename, logger.DEBUG)
not any(result) and logger.log(u'No cache for %s' % image_file, logger.DEBUG)
return any(result)
def has_poster(self, indexer_id):
"""
Returns true if a cached poster exists for the given Indexer ID
"""
poster_path = self.poster_path(indexer_id)
logger.log(u"Checking if file " + str(poster_path) + " exists", logger.DEBUG)
return ek.ek(os.path.isfile, poster_path)
return self.has_file(self.poster_path(indexer_id))
def has_banner(self, indexer_id):
"""
Returns true if a cached banner exists for the given Indexer ID
"""
banner_path = self.banner_path(indexer_id)
logger.log(u"Checking if file " + str(banner_path) + " exists", logger.DEBUG)
return ek.ek(os.path.isfile, banner_path)
return self.has_file(self.banner_path(indexer_id))
def has_fanart(self, indexer_id):
"""
Returns true if a cached fanart exists for the given Indexer ID
"""
return self.has_file(self.fanart_path(indexer_id))
def has_poster_thumbnail(self, indexer_id):
"""
Returns true if a cached poster thumbnail exists for the given Indexer ID
"""
poster_thumb_path = self.poster_thumb_path(indexer_id)
logger.log(u"Checking if file " + str(poster_thumb_path) + " exists", logger.DEBUG)
return ek.ek(os.path.isfile, poster_thumb_path)
return self.has_file(self.poster_thumb_path(indexer_id))
def has_banner_thumbnail(self, indexer_id):
"""
Returns true if a cached banner exists for the given Indexer ID
"""
banner_thumb_path = self.banner_thumb_path(indexer_id)
logger.log(u"Checking if file " + str(banner_thumb_path) + " exists", logger.DEBUG)
return ek.ek(os.path.isfile, banner_thumb_path)
return self.has_file(self.banner_thumb_path(indexer_id))
BANNER = 1
POSTER = 2
BANNER_THUMB = 3
POSTER_THUMB = 4
FANART = 5
def which_type(self, path):
"""
Analyzes the image provided and attempts to determine whether it is a poster or banner.
returns: BANNER, POSTER if it concluded one or the other, or None if the image was neither (or didn't exist)
Analyzes the image provided and attempts to determine whether it is a poster, banner or fanart.
returns: BANNER, POSTER, FANART or None if image type is not detected or doesn't exist
path: full path to the image
"""
if not ek.ek(os.path.isfile, path):
logger.log(u"Couldn't check the type of " + str(path) + " cause it doesn't exist", logger.WARNING)
logger.log(u'File does not exist to determine image type of %s' % path, logger.WARNING)
return None
# use hachoir to parse the image for us
@ -148,66 +185,105 @@ class ImageCache:
img_metadata = extractMetadata(img_parser)
if not img_metadata:
logger.log(u"Unable to get metadata from " + str(path) + ", not using your existing image", logger.DEBUG)
logger.log(u'Unable to extract metadata from %s, not using existing image' % path, logger.DEBUG)
return None
img_ratio = float(img_metadata.get('width')) / float(img_metadata.get('height'))
img_parser.stream._input.close()
msg_success = u'Treating image as %s'\
+ u' with extracted aspect ratio from %s' % path
# most posters are around 0.68 width/height ratio (eg. 680/1000)
if 0.55 < img_ratio < 0.8:
logger.log(msg_success % 'poster', logger.DEBUG)
return self.POSTER
# most banners are around 5.4 width/height ratio (eg. 758/140)
elif 5 < img_ratio < 6:
logger.log(msg_success % 'banner', logger.DEBUG)
return self.BANNER
# most fanart are around 1.7 width/height ratio (eg. 1280/720 or 1920/1080)
elif 1.7 < img_ratio < 1.8:
if 500 < img_metadata.get('width'):
logger.log(msg_success % 'fanart', logger.DEBUG)
return self.FANART
logger.log(u'Image found with fanart aspect ratio but less than 500 pixels wide, skipped', logger.WARNING)
return None
else:
logger.log(u"Image has size ratio of " + str(img_ratio) + ", unknown type", logger.WARNING)
logger.log(u'Image not useful with size ratio %s, skipping' % img_ratio, logger.WARNING)
return None
def _cache_image_from_file(self, image_path, img_type, indexer_id):
def should_refresh(self, image_type=None, provider='local'):
my_db = db.DBConnection('cache.db', row_type='dict')
sql_results = my_db.select('SELECT time FROM lastUpdate WHERE provider = ?',
['imsg_%s_%s' % ((image_type, self.FANART)[None is image_type], provider)])
if sql_results:
minutes_freq = 60 * 3
# daily_freq = 60 * 60 * 23
freq = minutes_freq
now_stamp = int(time.mktime(datetime.datetime.today().timetuple()))
the_time = int(sql_results[0]['time'])
return now_stamp - the_time > freq
return True
def set_last_refresh(self, image_type=None, provider='local'):
my_db = db.DBConnection('cache.db')
my_db.upsert('lastUpdate',
{'time': int(time.mktime(datetime.datetime.today().timetuple()))},
{'provider': 'imsg_%s_%s' % ((image_type, self.FANART)[None is image_type], provider)})
def _cache_image_from_file(self, image_path, img_type, indexer_id, move_file=False):
"""
Takes the image provided and copies it to the cache folder
returns: bool representing success
image_path: path to the image we're caching
img_type: BANNER or POSTER
Takes the image provided and copies or moves it to the cache folder
returns: full path to cached file or None
image_path: path to the image to cache
img_type: BANNER, POSTER, or FANART
indexer_id: id of the show this image belongs to
move_file: True if action is to move the file else file should be copied
"""
# generate the path based on the type & indexer_id
fanart_subdir = []
if img_type == self.POSTER:
dest_path = self.poster_path(indexer_id)
elif img_type == self.BANNER:
dest_path = self.banner_path(indexer_id)
elif img_type == self.FANART:
with open(image_path, mode='rb') as resource:
crc = '%05X' % (zlib.crc32(resource.read()) & 0xFFFFFFFF)
fanart_subdir = [self._fanart_dir(indexer_id)]
dest_path = self.fanart_path(indexer_id).replace('.fanart.jpg', '.%s.fanart.jpg' % crc)
else:
logger.log(u"Invalid cache image type: " + str(img_type), logger.ERROR)
logger.log(u'Invalid cache image type: ' + str(img_type), logger.ERROR)
return False
# make sure the cache folder exists before we try copying to it
if not ek.ek(os.path.isdir, self._cache_dir()):
logger.log(u"Image cache directory doesn't exist, creating it at " + str(self._cache_dir()))
ek.ek(os.makedirs, self._cache_dir())
for cache_dir in [self._cache_dir(), self._thumbnails_dir(), self._fanart_dir()] + fanart_subdir:
helpers.make_dirs(cache_dir)
if not ek.ek(os.path.isdir, self._thumbnails_dir()):
logger.log(u"Thumbnails cache directory doesn't exist, creating it at " + str(self._thumbnails_dir()))
ek.ek(os.makedirs, self._thumbnails_dir())
logger.log(u'%sing from %s to %s' % (('Copy', 'Mov')[move_file], image_path, dest_path))
if move_file:
helpers.moveFile(image_path, dest_path)
else:
helpers.copyFile(image_path, dest_path)
logger.log(u"Copying from " + image_path + " to " + dest_path)
helpers.copyFile(image_path, dest_path)
return ek.ek(os.path.isfile, dest_path) and dest_path or None
return True
def _cache_image_from_indexer(self, show_obj, img_type):
def _cache_image_from_indexer(self, show_obj, img_type, num_files=0, max_files=500):
"""
Retrieves an image of the type specified from indexer and saves it to the cache folder
returns: bool representing success
show_obj: TVShow object that we want to cache an image for
img_type: BANNER or POSTER
img_type: BANNER, POSTER, or FANART
"""
# generate the path based on the type & indexer_id
@ -217,6 +293,9 @@ class ImageCache:
elif img_type == self.BANNER:
img_type_name = 'banner'
dest_path = self.banner_path(show_obj.indexerid)
elif img_type == self.FANART:
img_type_name = 'fanart_all'
dest_path = self.fanart_path(show_obj.indexerid).replace('fanart.jpg', '*')
elif img_type == self.POSTER_THUMB:
img_type_name = 'poster_thumb'
dest_path = self.poster_thumb_path(show_obj.indexerid)
@ -224,70 +303,205 @@ class ImageCache:
img_type_name = 'banner_thumb'
dest_path = self.banner_thumb_path(show_obj.indexerid)
else:
logger.log(u"Invalid cache image type: " + str(img_type), logger.ERROR)
logger.log(u'Invalid cache image type: ' + str(img_type), logger.ERROR)
return False
# retrieve the image from indexer using the generic metadata class
#TODO: refactor
metadata_generator = GenericMetadata()
img_data = metadata_generator._retrieve_show_image(img_type_name, show_obj)
result = metadata_generator._write_image(img_data, dest_path)
if img_type == self.FANART:
image_urls = metadata_generator.retrieve_show_image(img_type_name, show_obj)
if None is image_urls:
return False
crcs = []
for cache_file_name in ek.ek(glob.glob, dest_path):
with open(cache_file_name, mode='rb') as resource:
crc = '%05X' % (zlib.crc32(resource.read()) & 0xFFFFFFFF)
if crc not in crcs:
crcs += [crc]
success = 0
count_urls = len(image_urls)
sources = []
for image_url in image_urls or []:
img_data = helpers.getURL(image_url)
if None is img_data:
continue
crc = '%05X' % (zlib.crc32(img_data) & 0xFFFFFFFF)
if crc in crcs:
count_urls -= 1
continue
crcs += [crc]
img_source = (((('', 'tvdb')['thetvdb.com' in image_url],
'tvrage')['tvrage.com' in image_url],
'fatv')['fanart.tv' in image_url],
'tmdb')['tmdb' in image_url]
img_xtra = ''
if 'tmdb' == img_source:
match = re.search(r'(?:.*\?(\d+$))?', image_url, re.I | re.M)
if match and None is not match.group(1):
img_xtra = match.group(1)
file_desc = '%s.%03d%s.%s' % (
show_obj.indexerid, num_files, ('.%s%s' % (img_source, img_xtra), '')['' == img_source], crc)
cur_file_path = self.fanart_path(file_desc)
result = metadata_generator.write_image(img_data, cur_file_path)
if result and self.FANART != self.which_type(cur_file_path):
try:
ek.ek(os.remove, cur_file_path)
except OSError as e:
logger.log(u'Unable to remove %s: %s / %s' % (cur_file_path, repr(e), str(e)), logger.WARNING)
continue
if img_source:
sources += [img_source]
num_files += (0, 1)[result]
success += (0, 1)[result]
if num_files > max_files:
break
if count_urls:
total = len(ek.ek(glob.glob, dest_path))
logger.log(u'Saved %s of %s fanart images%s. Cached %s of max %s fanart file%s'
% (success, count_urls,
('', ' from ' + ', '.join([x for x in list(set(sources))]))[0 < len(sources)],
total, sickbeard.FANART_LIMIT, helpers.maybe_plural(total)))
return bool(count_urls) and not bool(count_urls - success)
img_data = metadata_generator.retrieve_show_image(img_type_name, show_obj)
if None is img_data:
return False
result = metadata_generator.write_image(img_data, dest_path)
if result:
logger.log(u'Saved image type %s' % img_type_name)
return result
def fill_cache(self, show_obj):
def clean_fanart(self):
ratings_found = False
fanarts = ek.ek(glob.glob, '%s.jpg' % self._fanart_dir('*'))
if fanarts:
logger.log(u'Reorganising fanart cache files', logger.DEBUG)
for image_path in fanarts:
image_path_parts = ek.ek(os.path.basename, image_path).split('.')
dest_path = self._cache_image_from_file(image_path, self.FANART, '.'.join(image_path_parts[0:-2]), True)
if None is not dest_path:
src_file_id = '.'.join(image_path_parts[1:-2])
rating = sickbeard.FANART_RATINGS.get(image_path_parts[0], {}).get(src_file_id, None)
if None is not rating:
ratings_found = True
dest_file_id = str('.'.join(ek.ek(os.path.basename, dest_path).split('.')[1:-2]))
sickbeard.FANART_RATINGS[image_path_parts[0]][dest_file_id] = rating
del (sickbeard.FANART_RATINGS[image_path_parts[0]][src_file_id])
return ratings_found
def fill_cache(self, show_obj, force=False):
"""
Caches all images for the given show. Copies them from the show dir if possible, or
downloads them from indexer if they aren't in the show dir.
show_obj: TVShow object to cache images for
"""
logger.log(u"Checking if we need any cache images for show " + str(show_obj.indexerid), logger.DEBUG)
show_id = '%s' % show_obj.indexerid
# check if the images are already cached or not
need_images = {self.POSTER: not self.has_poster(show_obj.indexerid),
self.BANNER: not self.has_banner(show_obj.indexerid),
self.POSTER_THUMB: not self.has_poster_thumbnail(show_obj.indexerid),
self.BANNER_THUMB: not self.has_banner_thumbnail(show_obj.indexerid)}
# check if any images are cached
need_images = {self.POSTER: not self.has_poster(show_id),
self.BANNER: not self.has_banner(show_id),
self.FANART: 0 < sickbeard.FANART_LIMIT and (force or not self.has_fanart(show_id + '.001.*')),
# use limit? shows less than a limit of say 50 would fail to fulfill images every day
# '.%03d.*' % sickbeard.FANART_LIMIT
self.POSTER_THUMB: not self.has_poster_thumbnail(show_id),
self.BANNER_THUMB: not self.has_banner_thumbnail(show_id)}
if not need_images[self.POSTER] and not need_images[self.BANNER] and not need_images[self.POSTER_THUMB] and not \
need_images[self.BANNER_THUMB]:
logger.log(u"No new cache images needed, not retrieving new ones")
if not any(need_images.values()):
logger.log(u'%s: No new cache images needed. Done.' % show_id)
return
# check the show dir for poster or banner images and use them
if need_images[self.POSTER] or need_images[self.BANNER]:
try:
for cur_provider in sickbeard.metadata_provider_dict.values():
logger.log(u"Checking if we can use the show image from the " + cur_provider.name + " metadata",
logger.DEBUG)
if ek.ek(os.path.isfile, cur_provider.get_poster_path(show_obj)):
cur_file_name = os.path.abspath(cur_provider.get_poster_path(show_obj))
cur_file_type = self.which_type(cur_file_name)
void = False
if not void and need_images[self.FANART]:
action = ('delete', 'trash')[sickbeard.TRASH_REMOVE_SHOW]
if cur_file_type == None:
logger.log(u"Unable to retrieve image type, not using the image from " + str(cur_file_name),
logger.WARNING)
cache_path = self.fanart_path(show_id).replace('%s.fanart.jpg' % show_id, '')
# num_images = len(fnmatch.filter(os.listdir(cache_path), '*.jpg'))
for cache_dir in ek.ek(glob.glob, cache_path):
if show_id in sickbeard.FANART_RATINGS:
del (sickbeard.FANART_RATINGS[show_id])
logger.log(u'Attempt to %s purge cache file %s' % (action, cache_dir), logger.DEBUG)
try:
if sickbeard.TRASH_REMOVE_SHOW:
send2trash(cache_dir)
else:
shutil.rmtree(cache_dir)
except OSError as e:
logger.log(u'Unable to %s %s: %s / %s' % (action, cache_dir, repr(e), str(e)), logger.WARNING)
try:
checked_files = []
crcs = []
for cur_provider in sickbeard.metadata_provider_dict.values():
# check the show dir for poster or banner images and use them
needed = []
if any([need_images[self.POSTER], need_images[self.BANNER]]):
needed += [[False, cur_provider.get_poster_path(show_obj)]]
if need_images[self.FANART]:
needed += [[True, cur_provider.get_fanart_path(show_obj)]]
if 0 == len(needed):
break
logger.log(u'Checking for images from optional %s metadata' % cur_provider.name, logger.DEBUG)
for all_meta_provs, path_file in needed:
if path_file in checked_files:
continue
checked_files += [path_file]
if ek.ek(os.path.isfile, path_file):
cache_file_name = os.path.abspath(path_file)
with open(cache_file_name, mode='rb') as resource:
crc = '%05X' % (zlib.crc32(resource.read()) & 0xFFFFFFFF)
if crc in crcs:
continue
crcs += [crc]
cur_file_type = self.which_type(cache_file_name)
if None is cur_file_type:
continue
logger.log(u"Checking if image " + cur_file_name + " (type " + str(
cur_file_type) + " needs metadata: " + str(need_images[cur_file_type]), logger.DEBUG)
logger.log(u'Checking if image %s (type %s needs metadata: %s)'
% (cache_file_name, str(cur_file_type),
('No', 'Yes')[True is need_images[cur_file_type]]), logger.DEBUG)
if cur_file_type in need_images and need_images[cur_file_type]:
logger.log(
u"Found an image in the show directory that doesn't exist in the cache, caching it: " + cur_file_name + ", type " + str(
cur_file_type), logger.DEBUG)
self._cache_image_from_file(cur_file_name, cur_file_type, show_obj.indexerid)
need_images[cur_file_type] = False
except exceptions.ShowDirNotFoundException:
logger.log(u"Unable to search for images in show directory because it doesn't exist", logger.WARNING)
if need_images.get(cur_file_type):
need_images[cur_file_type] = (
(need_images[cur_file_type] + 1, 1)[isinstance(need_images[cur_file_type], bool)],
False)[not all_meta_provs]
if self.FANART == cur_file_type and \
(not sickbeard.FANART_LIMIT or sickbeard.FANART_LIMIT < need_images[cur_file_type]):
continue
logger.log(u'Caching image found in the show directory to the image cache: %s, type %s'
% (cache_file_name, cur_file_type), logger.DEBUG)
# download from indexer for missing ones
for cur_image_type in [self.POSTER, self.BANNER, self.POSTER_THUMB, self.BANNER_THUMB]:
logger.log(u"Seeing if we still need an image of type " + str(cur_image_type) + ": " + str(
need_images[cur_image_type]), logger.DEBUG)
if cur_image_type in need_images and need_images[cur_image_type]:
self._cache_image_from_indexer(show_obj, cur_image_type)
self._cache_image_from_file(cache_file_name, cur_file_type, '%s%s' % (
show_id, ('.%03d' % need_images[cur_file_type], '')[
isinstance(need_images[cur_file_type], bool)]))
logger.log(u"Done cache check")
except exceptions.ShowDirNotFoundException:
logger.log(u'Unable to search for images in show directory because it doesn\'t exist', logger.WARNING)
# download missing ones from indexer
for image_type, name_type in [[self.POSTER, 'Poster'], [self.BANNER, 'Banner'], [self.FANART, 'Fanart'],
[self.POSTER_THUMB, 'Poster Thumb'], [self.BANNER_THUMB, 'Banner Thumb']]:
max_files = (500, sickbeard.FANART_LIMIT)[self.FANART == image_type]
if not max_files or max_files < need_images[image_type]:
continue
logger.log(u'Seeing if we still need an image of type %s: %s'
% (name_type, ('No', 'Yes')[True is need_images[image_type]]), logger.DEBUG)
if need_images[image_type]:
file_num = (need_images[image_type] + 1, 1)[isinstance(need_images[image_type], bool)]
if file_num <= max_files:
self._cache_image_from_indexer(show_obj, image_type, file_num, max_files)
logger.log(u'Done cache check')

View file

@ -37,6 +37,7 @@ try:
except ImportError:
pass
# ERROR = 40, WARNING = 30, INFO = 20, DEBUG = 10
ERROR = logging.ERROR
WARNING = logging.WARNING
MESSAGE = logging.INFO
@ -184,6 +185,37 @@ class SBRotatingLogHandler(object):
else:
sys.exit(1)
@staticmethod
def reverse_readline(filename, buf_size=4096):
"""a generator that returns the lines of a file in reverse order"""
with open(filename) as fh:
segment = None
offset = 0
fh.seek(0, os.SEEK_END)
file_size = remaining_size = fh.tell()
while remaining_size > 0:
offset = min(file_size, offset + buf_size)
fh.seek(file_size - offset)
buf = fh.read(min(remaining_size, buf_size))
remaining_size -= buf_size
lines = buf.split('\n')
# the first line of the buffer is probably not a complete line so
# we'll save it and append it to the last line of the next buffer
# we read
if segment is not None:
# if the previous chunk starts right from the beginning of line
# do not concat the segment to the last line of new chunk
# instead, yield the segment first
if buf[-1] is not '\n':
lines[-1] += segment
else:
yield segment + '\n'
segment = lines[0]
for index in range(len(lines) - 1, 0, -1):
if len(lines[index]):
yield lines[index] + '\n'
yield None is not segment and segment + '\n' or ''
class DispatchingFormatter:
def __init__(self, formatters, default_formatter):
@ -263,7 +295,10 @@ class TimedCompressedRotatingFileHandler(TimedRotatingFileHandler):
if 0 < self.backupCount:
# find the oldest log file and delete it
all_names = encodingKludge.ek(glob.glob, file_name + '_*')
# phase out files named sickbeard.log in favour of sickgear.logs over backup_count days
all_names = encodingKludge.ek(glob.glob, file_name + '_*') + \
encodingKludge.ek(glob.glob, encodingKludge.ek(os.path.join, encodingKludge.ek(
os.path.dirname, file_name), 'sickbeard_*'))
if len(all_names) > self.backupCount:
all_names.sort()
self.delete_logfile(all_names[0])

View file

@ -38,9 +38,13 @@ from sickbeard import logger
from sickbeard import encodingKludge as ek
from sickbeard.exceptions import ex
from sickbeard.show_name_helpers import allPossibleShowNames
from sickbeard.indexers import indexer_config
from six import iteritems
from lib.tmdb_api.tmdb_api import TMDB
from lib.fanart.core import Request as fanartRequest
import lib.fanart as fanart
class GenericMetadata():
@ -149,21 +153,21 @@ class GenericMetadata():
def _has_episode_thumb(self, ep_obj):
location = self.get_episode_thumb_path(ep_obj)
result = location != None and ek.ek(os.path.isfile, location)
result = None is not location and ek.ek(os.path.isfile, location)
if location:
logger.log(u"Checking if " + location + " exists: " + str(result), logger.DEBUG)
return result
def _has_season_poster(self, show_obj, season):
location = self.get_season_poster_path(show_obj, season)
result = location != None and ek.ek(os.path.isfile, location)
result = None is not location and ek.ek(os.path.isfile, location)
if location:
logger.log(u"Checking if " + location + " exists: " + str(result), logger.DEBUG)
return result
def _has_season_banner(self, show_obj, season):
location = self.get_season_banner_path(show_obj, season)
result = location != None and ek.ek(os.path.isfile, location)
result = None is not location and ek.ek(os.path.isfile, location)
if location:
logger.log(u"Checking if " + location + " exists: " + str(result), logger.DEBUG)
return result
@ -742,30 +746,27 @@ class GenericMetadata():
def _retrieve_show_image(self, image_type, show_obj, which=None):
"""
Gets an image URL from theTVDB.com and TMDB.com, downloads it and returns the data.
Gets an image URL from theTVDB.com, fanart.tv and TMDB.com, downloads it and returns the data.
If type is fanart, multiple image src urls are returned instead of a single data image.
image_type: type of image to retrieve (currently supported: fanart, poster, banner)
image_type: type of image to retrieve (currently supported: fanart, poster, banner, poster_thumb, banner_thumb)
show_obj: a TVShow object to use when searching for the image
which: optional, a specific numbered poster to look for
Returns: the binary image data if available, or else None
"""
image_url = None
indexer_lang = show_obj.lang
try:
# There's gotta be a better way of doing this but we don't wanna
# change the language value elsewhere
lINDEXER_API_PARMS = sickbeard.indexerApi(show_obj.indexer).api_params.copy()
lINDEXER_API_PARMS['banners'] = True
lINDEXER_API_PARMS['dvdorder'] = 0 != show_obj.dvdorder
if indexer_lang and not indexer_lang == 'en':
if indexer_lang and not 'en' == indexer_lang:
lINDEXER_API_PARMS['language'] = indexer_lang
if show_obj.dvdorder != 0:
lINDEXER_API_PARMS['dvdorder'] = True
t = sickbeard.indexerApi(show_obj.indexer).indexer(**lINDEXER_API_PARMS)
indexer_show_obj = t[show_obj.indexerid]
except (sickbeard.indexer_error, IOError) as e:
@ -773,30 +774,59 @@ class GenericMetadata():
show_obj.indexer).name + ", not downloading images: " + ex(e), logger.ERROR)
return None
if image_type not in ('fanart', 'poster', 'banner', 'poster_thumb', 'banner_thumb'):
return_links = False
if 'fanart_all' == image_type:
return_links = True
image_type = 'fanart'
if image_type not in ('poster', 'banner', 'fanart', 'poster_thumb', 'banner_thumb'):
logger.log(u"Invalid image type " + str(image_type) + ", couldn't find it in the " + sickbeard.indexerApi(
show_obj.indexer).name + " object", logger.ERROR)
return None
if image_type == 'poster_thumb':
image_urls = []
init_url = None
if 'poster_thumb' == image_type:
if getattr(indexer_show_obj, 'poster', None) is not None:
image_url = re.sub('posters', '_cache/posters', indexer_show_obj['poster'])
elif image_type == 'banner_thumb':
if image_url:
image_urls.append(image_url)
for item in self._fanart_urls_from_show(show_obj, image_type, indexer_lang, True) or []:
image_urls.append(item[2])
if 0 == len(image_urls):
for item in self._tmdb_image_url(show_obj, image_type) or []:
image_urls.append(item[2])
elif 'banner_thumb' == image_type:
if getattr(indexer_show_obj, 'banner', None) is not None:
image_url = re.sub('graphical', '_cache/graphical', indexer_show_obj['banner'])
if image_url:
image_urls.append(image_url)
for item in self._fanart_urls_from_show(show_obj, image_type, indexer_lang, True) or []:
image_urls.append(item[2])
else:
for item in self._fanart_urls_from_show(show_obj, image_type, indexer_lang) or []:
image_urls.append(item[2])
if getattr(indexer_show_obj, image_type, None) is not None:
image_url = indexer_show_obj[image_type]
if image_url:
image_urls.append(image_url)
if 'poster' == image_type:
init_url = image_url
# Try and get posters and fanart from TMDB
if image_url is None:
if image_type in ('poster', 'poster_thumb'):
image_url = self._retrieve_show_images_from_tmdb(show_obj, poster=True)
elif image_type == 'fanart':
image_url = self._retrieve_show_images_from_tmdb(show_obj, backdrop=True)
if 0 == len(image_urls) or 'fanart' == image_type:
for item in self._tmdb_image_url(show_obj, image_type) or []:
image_urls.append('%s?%s' % (item[2], item[0]))
if image_url:
image_data = metadata_helpers.getShowImage(image_url, which)
image_data = len(image_urls) or None
if image_data:
if return_links:
return image_urls
else:
image_data = metadata_helpers.getShowImage((init_url, image_urls[0])[None is init_url], which)
if None is not image_data:
return image_data
return None
@ -818,15 +848,12 @@ class GenericMetadata():
# There's gotta be a better way of doing this but we don't wanna
# change the language value elsewhere
lINDEXER_API_PARMS = sickbeard.indexerApi(show_obj.indexer).api_params.copy()
lINDEXER_API_PARMS['banners'] = True
lINDEXER_API_PARMS['dvdorder'] = 0 != show_obj.dvdorder
if indexer_lang and not indexer_lang == 'en':
lINDEXER_API_PARMS['language'] = indexer_lang
if show_obj.dvdorder != 0:
lINDEXER_API_PARMS['dvdorder'] = True
t = sickbeard.indexerApi(show_obj.indexer).indexer(**lINDEXER_API_PARMS)
indexer_show_obj = t[show_obj.indexerid]
except (sickbeard.indexer_error, IOError) as e:
@ -874,8 +901,8 @@ class GenericMetadata():
# There's gotta be a better way of doing this but we don't wanna
# change the language value elsewhere
lINDEXER_API_PARMS = sickbeard.indexerApi(show_obj.indexer).api_params.copy()
lINDEXER_API_PARMS['banners'] = True
lINDEXER_API_PARMS['dvdorder'] = 0 != show_obj.dvdorder
if indexer_lang and not indexer_lang == 'en':
lINDEXER_API_PARMS['language'] = indexer_lang
@ -950,10 +977,10 @@ class GenericMetadata():
except:
indexer = None
if showXML.findtext('tvdbid') != None:
if None is not showXML.findtext('tvdbid'):
indexer_id = int(showXML.findtext('tvdbid'))
indexer = INDEXER_TVDB
elif showXML.findtext('id') != None:
elif None is not showXML.findtext('id'):
indexer_id = int(showXML.findtext('id'))
try:
indexer = INDEXER_TVDB if [s for s in showXML.findall('.//*') if s.text and s.text.find('thetvdb.com') != -1] else indexer
@ -975,7 +1002,15 @@ class GenericMetadata():
return (indexer_id, name, indexer)
def _retrieve_show_images_from_tmdb(self, show, backdrop=False, poster=False):
@staticmethod
def _tmdb_image_url(show, image_type):
types = {'poster': 'poster_path',
'banner': None,
'fanart': 'backdrop_path',
'fanart_all': 'backdrops',
'poster_thumb': 'poster_path',
'banner_thumb': None}
# get TMDB configuration info
tmdb = TMDB(sickbeard.TMDB_API_KEY)
config = tmdb.Configuration()
@ -984,21 +1019,90 @@ class GenericMetadata():
sizes = response['images']['poster_sizes']
def size_str_to_int(x):
return float("inf") if x == 'original' else int(x[1:])
return float('inf') if x == 'original' else int(x[1:])
max_size = max(sizes, key=size_str_to_int)
try:
search = tmdb.Search()
for show_name in set(allPossibleShowNames(show)):
for result in search.collection({'query': show_name})['results'] + search.tv({'query': show_name})[
'results']:
if backdrop and result['backdrop_path']:
return "{0}{1}{2}".format(base_url, max_size, result['backdrop_path'])
elif poster and result['poster_path']:
return "{0}{1}{2}".format(base_url, max_size, result['poster_path'])
itemlist = []
for (src, name) in [(indexer_config.INDEXER_TVDB, 'tvdb'), (indexer_config.INDEXER_IMDB, 'imdb'),
(indexer_config.INDEXER_TVRAGE, 'tvrage')]:
is_id = show.ids.get(src, {}).get('id', None)
if not is_id:
continue
except Exception as e:
result = tmdb.Find(is_id).info({'external_source': '%s_id' % name})
if 'tv_results' not in result or not len(result['tv_results']):
continue
tmdb_show = result['tv_results'][0]
if 'fanart' == image_type:
tv_obj = tmdb.TV(tmdb_show['id'])
rjson_info = tv_obj.info({'append_to_response': 'images', 'include_image_language': 'en,null'})
rjson_img = rjson_info['images']
for art in rjson_img[types['fanart_all']] or []:
if 'vote_average' not in art or 'file_path' not in art:
continue
art_likes = art['vote_average']
url = u'%s%s%s' % (base_url, max_size, art['file_path'])
itemlist += [[tmdb_show['id'], art_likes, url]]
itemlist.sort(lambda a, b: cmp((a[1]), (b[1])), reverse=True)
return itemlist
elif tmdb_show[types[image_type]]:
return [[tmdb_show['id'], tmdb_show['vote_average'], '%s%s%s' % (base_url, max_size, tmdb_show[types[image_type]])]]
except (StandardError, Exception):
pass
logger.log(u"Could not find any posters or background for " + show.name, logger.DEBUG)
logger.log(u'Could not find any %s images on TMDB for %s' % (image_type, show.name), logger.DEBUG)
def _fanart_urls_from_show(self, show, image_type='banner', lang='en', thumb=False):
try:
tvdb_id = show.ids.get(indexer_config.INDEXER_TVDB, {}).get('id', None)
if tvdb_id:
return self._fanart_urls(tvdb_id, image_type, lang, thumb)
except (StandardError, Exception):
pass
logger.log(u'Could not find any %s images on Fanart.tv for %s' % (image_type, show.name), logger.DEBUG)
@staticmethod
def _fanart_urls(tvdb_id, image_type='banner', lang='en', thumb=False):
types = {'poster': fanart.TYPE.TV.POSTER,
'banner': fanart.TYPE.TV.BANNER,
'fanart': fanart.TYPE.TV.BACKGROUND,
'poster_thumb': fanart.TYPE.TV.POSTER,
'banner_thumb': fanart.TYPE.TV.BANNER}
try:
if tvdb_id:
request = fanartRequest(apikey=sickbeard.FANART_API_KEY, tvdb_id=tvdb_id)
resp = request.response()
itemlist = []
for art in resp[types[image_type]]:
if ('url' in art and 10 > len(art['url']))\
or ('lang' in art and lang != art['lang']):
continue
try:
art_id = int(art['id'])
art_likes = int(art['likes'])
url = (art['url'], re.sub('/fanart/', '/preview/', art['url']))[thumb]
except:
continue
itemlist += [[art_id, art_likes, url]]
itemlist.sort(lambda a, b: cmp((a[1], a[0]), (b[1], b[0])), reverse=True)
return itemlist
except Exception as e:
raise
def retrieve_show_image(self, image_type, show_obj, which=None):
return self._retrieve_show_image(image_type=image_type, show_obj=show_obj, which=which)
def write_image(self, image_data, image_path):
return self._write_image(image_data=image_data, image_path=image_path)

View file

@ -21,22 +21,18 @@ from sickbeard import logger
def getShowImage(url, imgNum=None):
image_data = None # @UnusedVariable
if url == None:
if None is url:
return None
# if they provided a fanart number try to use it instead
if imgNum != None:
tempURL = url.split('-')[0] + "-" + str(imgNum) + ".jpg"
else:
tempURL = url
temp_url = url if None is imgNum else url.split('-')[0] + '-' + str(imgNum) + '.jpg'
logger.log(u"Fetching image from " + tempURL, logger.DEBUG)
logger.log(u'Fetching image from ' + temp_url, logger.DEBUG)
image_data = helpers.getURL(tempURL)
if image_data is None:
logger.log(u"There was an error trying to retrieve the image, aborting", logger.ERROR)
image_data = helpers.getURL(temp_url)
if None is image_data:
logger.log(u'There was an error trying to retrieve the image, aborting', logger.ERROR)
return
return image_data

View file

@ -403,7 +403,7 @@ class GenericProvider:
'udp://tracker.internetwarriors.net:1337', 'udp://tracker.internetwarriors.net:1337/announce',
'udp://tracker.leechers-paradise.org:6969', 'udp://tracker.leechers-paradise.org:6969/announce',
'udp://tracker.opentrackr.org:1337/announce', 'udp://tracker.torrent.eu.org:451/announce',
'udp://tracker.trackerfix.com:80/announce'])) or None)
'udp://tracker.trackerfix.com:80/announce', 'udp://tracker.zer0day.to:1337/announce'])) or None)
def get_show(self, item, **kwargs):
return None

View file

@ -138,7 +138,7 @@ class ShowQueue(generic_queue.GenericQueue):
return queueItemObj
def refreshShow(self, show, force=False, scheduled_update=False, after_update=False,
priority=generic_queue.QueuePriorities.HIGH, **kwargs):
priority=generic_queue.QueuePriorities.HIGH, force_image_cache=False, **kwargs):
if self.isBeingRefreshed(show) and not force:
raise exceptions.CantRefreshException('This show is already being refreshed, not refreshing again.')
@ -149,7 +149,8 @@ class ShowQueue(generic_queue.GenericQueue):
logger.DEBUG)
return
queueItemObj = QueueItemRefresh(show, force=force, scheduled_update=scheduled_update, priority=priority, **kwargs)
queueItemObj = QueueItemRefresh(show, force=force, scheduled_update=scheduled_update, priority=priority,
force_image_cache=force_image_cache, **kwargs)
self.add_item(queueItemObj)
@ -487,7 +488,8 @@ class QueueItemAdd(ShowQueueItem):
class QueueItemRefresh(ShowQueueItem):
def __init__(self, show=None, force=False, scheduled_update=False, priority=generic_queue.QueuePriorities.HIGH, **kwargs):
def __init__(self, show=None, force=False, scheduled_update=False, priority=generic_queue.QueuePriorities.HIGH,
force_image_cache=False, **kwargs):
ShowQueueItem.__init__(self, ShowQueueActions.REFRESH, show, scheduled_update)
# do refreshes first because they're quick
@ -496,6 +498,8 @@ class QueueItemRefresh(ShowQueueItem):
# force refresh certain items
self.force = force
self.force_image_cache = force_image_cache
self.kwargs = kwargs
def run(self):
@ -507,7 +511,7 @@ class QueueItemRefresh(ShowQueueItem):
self.show.writeMetadata()
#if self.force:
# self.show.updateMetadata()
self.show.populateCache()
self.show.populateCache(self.force_image_cache)
# Load XEM data to DB for show
if self.show.indexerid in sickbeard.scene_exceptions.xem_ids_list[self.show.indexer]:
@ -652,7 +656,7 @@ class QueueItemUpdate(ShowQueueItem):
if self.priority != generic_queue.QueuePriorities.NORMAL:
self.kwargs['priority'] = self.priority
sickbeard.showQueueScheduler.action.refreshShow(self.show, self.force, self.scheduled_update, after_update=True,
**self.kwargs)
force_image_cache=self.force_web, **self.kwargs)
class QueueItemForceUpdate(QueueItemUpdate):

View file

@ -17,18 +17,10 @@
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
import datetime
import os
import sickbeard
from sickbeard import logger
from sickbeard import exceptions
from sickbeard import ui
from sickbeard import logger, exceptions, ui, db, network_timezones, failed_history
from sickbeard.exceptions import ex
from sickbeard import encodingKludge as ek
from sickbeard import db
from sickbeard import network_timezones
from sickbeard import failed_history
class ShowUpdater:
@ -109,7 +101,7 @@ class ShowUpdater:
logger.log(
u'Not updating episodes for show ' + curShow.name + ' because it\'s marked as ended and ' +
'last/next episode is not within the grace period.', logger.DEBUG)
cur_queue_item = sickbeard.showQueueScheduler.action.refreshShow(curShow, True, True)
cur_queue_item = sickbeard.showQueueScheduler.action.refreshShow(curShow, True, True, force_image_cache=True)
pi_list.append(cur_queue_item)

View file

@ -60,7 +60,7 @@ from sickbeard import encodingKludge as ek
from common import Quality, Overview, statusStrings
from common import DOWNLOADED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, ARCHIVED, IGNORED, UNAIRED, WANTED, SKIPPED, \
UNKNOWN, FAILED
UNKNOWN, FAILED, SUBTITLED
from common import NAMING_DUPLICATE, NAMING_EXTEND, NAMING_LIMITED_EXTEND, NAMING_SEPARATED_REPEAT, \
NAMING_LIMITED_EXTEND_E_PREFIXED
@ -503,12 +503,10 @@ class TVShow(object):
t = sickbeard.indexerApi(self.indexer).indexer(**lINDEXER_API_PARMS)
cachedShow = t[self.indexerid]
cachedSeasons = {}
if None is cachedShow:
logger.log('No cache showdata to parse from %s' % sickbeard.indexerApi(self.indexer).name)
return scannedEps
cachedSeasons = {}
for curResult in sqlResults:
deleteEp = False
@ -1072,19 +1070,26 @@ class TVShow(object):
sickbeard.showList = [x for x in sickbeard.showList if int(x.indexerid) != self.indexerid]
# clear the cache
image_cache_dir = ek.ek(os.path.join, sickbeard.CACHE_DIR, 'images')
for path, dirs, files in ek.ek(os.walk, image_cache_dir):
for filename in ek.ek(fnmatch.filter, files, '%s.*' % self.indexerid):
cache_file = ek.ek(os.path.join, path, filename)
logger.log('Attempt to %s cache file %s' % (action, cache_file))
try:
if sickbeard.TRASH_REMOVE_SHOW:
ek.ek(send2trash, cache_file)
else:
ek.ek(os.remove, cache_file)
ic = image_cache.ImageCache()
for cache_obj in ek.ek(glob.glob, ic.poster_path(self.indexerid).replace('poster.jpg', '*')) \
+ ek.ek(glob.glob, ic.poster_thumb_path(self.indexerid).replace('poster.jpg', '*')) \
+ ek.ek(glob.glob, ic.fanart_path(self.indexerid).replace('%s.fanart.jpg' % self.indexerid, '')):
cache_dir = ek.ek(os.path.isdir, cache_obj)
logger.log('Attempt to %s cache %s %s' % (action, cache_dir and 'dir' or 'file', cache_obj))
try:
if sickbeard.TRASH_REMOVE_SHOW:
ek.ek(send2trash, cache_obj)
elif cache_dir:
ek.ek(shutil.rmtree, cache_obj)
else:
ek.ek(os.remove, cache_obj)
except OSError as e:
logger.log('Unable to %s %s: %s / %s' % (action, cache_file, repr(e), str(e)), logger.WARNING)
except OSError as e:
logger.log('Unable to %s %s: %s / %s' % (action, cache_obj, repr(e), str(e)), logger.WARNING)
show_id = '%s' % self.indexerid
if show_id in sickbeard.FANART_RATINGS:
del sickbeard.FANART_RATINGS[show_id]
# remove entire show folder
if full:
@ -1092,7 +1097,7 @@ class TVShow(object):
logger.log('Attempt to %s show folder %s' % (action, self._location))
# check first the read-only attribute
file_attribute = ek.ek(os.stat, self.location)[0]
if (not file_attribute & stat.S_IWRITE):
if not file_attribute & stat.S_IWRITE:
# File is read-only, so make it writeable
logger.log('Attempting to make writeable the read only folder %s' % self._location, logger.DEBUG)
try:
@ -1114,11 +1119,11 @@ class TVShow(object):
except OSError as e:
logger.log('Unable to %s %s: %s / %s' % (action, self._location, repr(e), str(e)), logger.WARNING)
def populateCache(self):
def populateCache(self, force=False):
cache_inst = image_cache.ImageCache()
logger.log('Checking & filling cache for show %s' % self.name)
cache_inst.fill_cache(self)
cache_inst.fill_cache(self, force)
def refreshDir(self):
@ -1233,7 +1238,23 @@ class TVShow(object):
try:
helpers.moveFile(cache_file, new_cachefile)
except Exception as e:
logger.log('Unable to rename %s to %s: %s / %s' % (cache_file, new_cachefile, repr(e), str(e)), logger.WARNING)
logger.log('Unable to rename %s to %s: %s / %s' % (cache_file, new_cachefile, repr(e), str(e)),
logger.WARNING)
ic = image_cache.ImageCache()
cache_dir = ic.fanart_path(old_indexerid).replace('%s.fanart.jpg' % old_indexerid, '').rstrip('\/')
new_cache_dir = ic.fanart_path(self.indexerid).replace('%s.fanart.jpg' % self.indexerid, '').rstrip('\/')
try:
helpers.moveFile(cache_dir, new_cache_dir)
except Exception as e:
logger.log('Unable to rename %s to %s: %s / %s' % (cache_dir, new_cache_dir, repr(e), str(e)),
logger.WARNING)
rating = sickbeard.FANART_RATINGS.get('%s' % old_indexerid)
if rating:
del sickbeard.FANART_RATINGS['%s' % old_indexerid]
sickbeard.FANART_RATINGS['%s' % self.indexerid] = rating
sickbeard.save_config()
name_cache.buildNameCache(self)
@ -1294,27 +1315,21 @@ class TVShow(object):
myDB.upsert('imdb_info', newValueDict, controlValueDict)
def __str__(self):
toReturn = ''
toReturn += 'indexerid: %s\n' % self.indexerid
toReturn += 'indexer: %s\n' % self.indexer
toReturn += 'name: %s\n' % self.name
toReturn += 'location: %s\n' % self._location
if self.network:
toReturn += 'network: %s\n' % self.network
if self.airs:
toReturn += 'airs: %s\n' % self.airs
if self.status:
toReturn += 'status: %s\n' % self.status
toReturn += 'startyear: %s\n' % self.startyear
if self.genre:
toReturn += 'genre: %s\n' % self.genre
toReturn += 'classification: %s\n' % self.classification
toReturn += 'runtime: %s\n' % self.runtime
toReturn += 'quality: %s\n' % self.quality
toReturn += 'scene: %s\n' % self.is_scene
toReturn += 'sports: %s\n' % self.is_sports
toReturn += 'anime: %s\n' % self.is_anime
return toReturn
return 'indexerid: %s\n' % self.indexerid \
+ 'indexer: %s\n' % self.indexerid \
+ 'name: %s\n' % self.name \
+ 'location: %s\n' % self._location \
+ ('', 'network: %s\n' % self.network)[self.network] \
+ ('', 'airs: %s\n' % self.airs)[self.airs] \
+ ('', 'status: %s\n' % self.status)[self.status] \
+ 'startyear: %s\n' % self.startyear \
+ ('', 'genre: %s\n' % self.genre)[self.genre] \
+ 'classification: %s\n' % self.classification \
+ 'runtime: %s\n' % self.runtime \
+ 'quality: %s\n' % self.quality \
+ 'scene: %s\n' % self.is_scene \
+ 'sports: %s\n' % self.is_sports \
+ 'anime: %s\n' % self.is_anime
def wantEpisode(self, season, episode, quality, manualSearch=False):
@ -1398,7 +1413,7 @@ class TVShow(object):
return Overview.SKIPPED
if status in (UNAIRED, UNKNOWN):
return Overview.UNAIRED
if status in Quality.DOWNLOADED + Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.FAILED + Quality.SNATCHED_BEST:
if status in [SUBTITLED] + Quality.DOWNLOADED + Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.FAILED + Quality.SNATCHED_BEST:
if FAILED == status:
return Overview.WANTED
@ -1493,7 +1508,8 @@ class TVEpisode(object):
release_group = property(lambda self: self._release_group, dirty_setter('_release_group'))
def _set_location(self, new_location):
logger.log('Setter sets location to %s' % new_location, logger.DEBUG)
log_vals = (('clears', ''), ('sets', ' to ' + new_location))[any(new_location)]
logger.log(u'Setter %s location%s' % log_vals, logger.DEBUG)
# self._location = newLocation
dirty_setter('_location')(self, new_location)
@ -1981,18 +1997,16 @@ class TVEpisode(object):
def __str__(self):
toReturn = ''
toReturn += '%s - %sx%s - %s\n' % (self.show.name, self.season, self.episode, self.name)
toReturn += 'location: %s\n' % self.location
toReturn += 'description: %s\n' % self.description
toReturn += 'subtitles: %s\n' % ','.join(self.subtitles)
toReturn += 'subtitles_searchcount: %s\n' % self.subtitles_searchcount
toReturn += 'subtitles_lastsearch: %s\n' % self.subtitles_lastsearch
toReturn += 'airdate: %s (%s)\n' % (self.airdate.toordinal(), self.airdate)
toReturn += 'hasnfo: %s\n' % self.hasnfo
toReturn += 'hastbn: %s\n' % self.hastbn
toReturn += 'status: %s\n' % self.status
return toReturn
return '%s - %sx%s - %s\n' % (self.show.name, self.season, self.episode, self.name) \
+ 'location: %s\n' % self.location \
+ 'description: %s\n' % self.description \
+ 'subtitles: %s\n' % ','.join(self.subtitles) \
+ 'subtitles_searchcount: %s\n' % self.subtitles_searchcount \
+ 'subtitles_lastsearch: %s\n' % self.subtitles_lastsearch \
+ 'airdate: %s (%s)\n' % (self.airdate.toordinal(), self.airdate) \
+ 'hasnfo: %s\n' % self.hasnfo \
+ 'hastbn: %s\n' % self.hastbn \
+ 'status: %s\n' % self.status
def createMetaFiles(self):
@ -2067,7 +2081,7 @@ class TVEpisode(object):
'(SELECT scene_episode FROM tv_episodes WHERE indexer = ? AND showid = ? AND season = ? AND episode = ?));',
[self.show.indexer, self.show.indexerid, self.season, self.episode, self.indexerid, self.indexer, self.name,
self.description,
",".join([sub for sub in self.subtitles]), self.subtitles_searchcount, self.subtitles_lastsearch,
','.join([sub for sub in self.subtitles]), self.subtitles_searchcount, self.subtitles_lastsearch,
self.airdate.toordinal(), self.hasnfo, self.hastbn, self.status, self.location, self.file_size,
self.release_name, self.is_proper, self.show.indexerid, self.season, self.episode,
self.absolute_number, self.version, self.release_group,

View file

@ -1235,50 +1235,65 @@ class CMD_Logs(ApiCall):
ApiCall.__init__(self, handler, args, kwargs)
def run(self):
""" view sickbeard's log """
""" view sickgear's log """
# 10 = Debug / 20 = Info / 30 = Warning / 40 = Error
minLevel = logger.reverseNames[str(self.min_level).upper()]
data = []
if os.path.isfile(logger.sb_log_instance.log_file_path):
with ek.ek(open, logger.sb_log_instance.log_file_path) as f:
data = f.readlines()
min_level = logger.reverseNames[str(self.min_level).upper()]
max_lines = 50
regex = "^(\d\d\d\d)\-(\d\d)\-(\d\d)\s*(\d\d)\:(\d\d):(\d\d)\s*([A-Z]+)\s*(.+?)\s*\:\:\s*(.*)$"
finalData = []
final_data = []
normal_data = []
truncate = []
repeated = None
num_lines = 0
numLines = 0
lastLine = False
numToShow = min(50, len(data))
if os.path.isfile(logger.sb_log_instance.log_file_path):
for x in logger.sb_log_instance.reverse_readline(logger.sb_log_instance.log_file_path):
for x in reversed(data):
x = x.decode('utf-8')
match = re.match(regex, x)
x = x.decode('utf-8')
match = re.match(regex, x)
if match:
level = match.group(7)
if level not in logger.reverseNames:
normal_data = []
continue
if match:
level = match.group(7)
if level not in logger.reverseNames:
lastLine = False
continue
if logger.reverseNames[level] >= min_level:
if truncate and not normal_data and truncate[0] == match.group(8) + match.group(9):
truncate += [match.group(8) + match.group(9)]
repeated = x
continue
if 1 < len(truncate):
final_data[-1] = repeated.strip() + ' (... %s repeat lines)\n' % len(truncate)
truncate = [match.group(8) + match.group(9)]
final_data.append(x)
if any(normal_data):
final_data += ['%02s) %s' % (n + 1, x) for n, x in enumerate(normal_data[::-1])] + \
['<br />']
num_lines += len(normal_data)
normal_data = []
else:
normal_data = []
continue
if logger.reverseNames[level] >= minLevel:
lastLine = True
finalData.append(x.rstrip("\n"))
else:
lastLine = False
continue
if not any(normal_data) and not any([x.strip()]):
continue
elif lastLine:
finalData.append("AA" + x)
normal_data.append(re.sub(r'\r?\n', '<br />', x.replace('<', '&lt;').replace('>', '&gt;')))
numLines += 1
num_lines += 1
if numLines >= numToShow:
break
if num_lines >= max_lines:
break
return _responds(RESULT_SUCCESS, final_data)
return _responds(RESULT_SUCCESS, finalData)
class CMD_PostProcess(ApiCall):
_help = {"desc": "Manual postprocess TV Download Dir",

View file

@ -22,6 +22,7 @@ from __future__ import with_statement
import base64
import datetime
import dateutil.parser
import glob
import itertools
import os
import random
@ -78,20 +79,18 @@ except ImportError:
class PageTemplate(Template):
def __init__(self, headers, *args, **kwargs):
self.sbHost = (re.match('(?msx)^' + (('[^:]+', '\[.*\]')['[' == headers['Host'][0]]), headers['Host']).group(0),
headers.get('X-Forwarded-Host', ''))['X-Forwarded-Host' in headers]
self.sbHost = headers.get('X-Forwarded-Host')
if None is self.sbHost:
sbHost = headers.get('Host') or 'localhost'
self.sbHost = re.match('(?msx)^' + (('[^:]+', '\[.*\]')['[' == sbHost[0]]), sbHost).group(0)
self.sbHttpPort = sickbeard.WEB_PORT
self.sbHttpsPort = (self.sbHttpPort, headers.get('X-Forwarded-Port', ''))['X-Forwarded-Port' in headers]
self.sbHttpsPort = headers.get('X-Forwarded-Port') or self.sbHttpPort
self.sbRoot = sickbeard.WEB_ROOT
self.sbHttpsEnabled = (sickbeard.ENABLE_HTTPS, 'https' == headers.get('X-Forwarded-Proto', ''))[
'X-Forwarded-Proto' in headers]
self.sbHttpsEnabled = 'https' == headers.get('X-Forwarded-Proto') or sickbeard.ENABLE_HTTPS
self.sbHandleReverseProxy = sickbeard.HANDLE_REVERSE_PROXY
self.sbThemeName = sickbeard.THEME_NAME
log_page_title = 'Logs &amp; Errors'
if len(classes.ErrorViewer.errors):
log_page_title += ' (%s)' % len(classes.ErrorViewer.errors)
self.logPageTitle = log_page_title
self.log_num_errors = len(classes.ErrorViewer.errors)
self.sbPID = str(sickbeard.PID)
self.menu = [
{'title': 'Home', 'key': 'home'},
@ -99,7 +98,6 @@ class PageTemplate(Template):
{'title': 'History', 'key': 'history'},
{'title': 'Manage', 'key': 'manage'},
{'title': 'Config', 'key': 'config'},
{'title': log_page_title, 'key': 'errorlogs'},
]
kwargs['file'] = os.path.join(sickbeard.PROG_DIR, 'gui/%s/interfaces/default/' %
@ -133,30 +131,34 @@ class BaseHandler(RequestHandler):
def get_current_user(self, *args, **kwargs):
if sickbeard.WEB_USERNAME or sickbeard.WEB_PASSWORD:
return self.get_secure_cookie('sickgear-session')
else:
return True
return self.get_secure_cookie('sickgear-session-%s' % helpers.md5_for_text(sickbeard.WEB_PORT))
return True
def showPoster(self, show=None, which=None, api=None):
# Redirect initial poster/banner thumb to default images
if which[0:6] == 'poster':
if 'poster' == which[0:6]:
default_image_name = 'poster.png'
else:
elif 'banner' == which[0:6]:
default_image_name = 'banner.png'
else:
default_image_name = 'backart.png'
static_image_path = os.path.join('/images', default_image_name)
if show and sickbeard.helpers.findCertainShow(sickbeard.showList, int(show)):
cache_obj = image_cache.ImageCache()
image_file_name = None
if which == 'poster':
if 'poster' == which:
image_file_name = cache_obj.poster_path(show)
if which == 'poster_thumb':
elif 'poster_thumb' == which:
image_file_name = cache_obj.poster_thumb_path(show)
if which == 'banner':
elif 'banner' == which:
image_file_name = cache_obj.banner_path(show)
if which == 'banner_thumb':
elif 'banner_thumb' == which:
image_file_name = cache_obj.banner_thumb_path(show)
elif 'fanart' == which[0:6]:
image_file_name = cache_obj.fanart_path('%s%s' % (
show, re.sub('.*?fanart_(\d+(?:\.\w{1,20})?\.(?:\w{5,8})).*', r'.\1', which, 0, re.I)))
if ek.ek(os.path.isfile, image_file_name):
static_image_path = image_file_name
@ -188,7 +190,8 @@ class LoginHandler(BaseHandler):
if (self.get_argument('username') == username) and (self.get_argument('password') == password):
remember_me = int(self.get_argument('remember_me', default=0) or 0)
self.set_secure_cookie('sickgear-session', sickbeard.COOKIE_SECRET, expires_days=30 if remember_me > 0 else None)
self.set_secure_cookie('sickgear-session-%s' % helpers.md5_for_text(sickbeard.WEB_PORT),
sickbeard.COOKIE_SECRET, expires_days=30 if remember_me > 0 else None)
self.redirect(self.get_argument('next', '/home/'))
else:
next_arg = '&next=' + self.get_argument('next', '/home/')
@ -197,7 +200,7 @@ class LoginHandler(BaseHandler):
class LogoutHandler(BaseHandler):
def get(self, *args, **kwargs):
self.clear_cookie('sickgear-session')
self.clear_cookie('sickgear-session-%s' % helpers.md5_for_text(sickbeard.WEB_PORT))
self.redirect('/login/')
@ -251,10 +254,10 @@ class CalendarHandler(BaseHandler):
ical += 'BEGIN:VEVENT%s' % crlf\
+ 'DTSTART:%sT%sZ%s' % (air_date_time.strftime('%Y%m%d'), air_date_time.strftime('%H%M%S'), crlf)\
+ 'DTEND:%sT%sZ%s' % (air_date_time_end.strftime('%Y%m%d'), air_date_time_end.strftime('%H%M%S'), crlf)\
+ 'SUMMARY:%s - %sx%s - %s%s' % (show['show_name'], str(episode['season']), str(episode['episode']), episode['name'], crlf)\
+ 'UID:%s-%s-%s-E%sS%s%s' % (appname, str(datetime.date.today().isoformat()), show['show_name'].replace(' ', '-'), str(episode['episode']), str(episode['season']), crlf)\
+ 'DESCRIPTION:%s on %s' % ((show['airs'] or '(Unknown airs)'), (show['network'] or 'Unknown network'))\
+ ('' if not episode['description'] else '%s%s' % (nl, episode['description'].splitlines()[0]))\
+ u'SUMMARY:%s - %sx%s - %s%s' % (show['show_name'], episode['season'], episode['episode'], episode['name'], crlf)\
+ u'UID:%s-%s-%s-E%sS%s%s' % (appname, datetime.date.today().isoformat(), show['show_name'].replace(' ', '-'), episode['episode'], episode['season'], crlf)\
+ u'DESCRIPTION:%s on %s' % ((show['airs'] or '(Unknown airs)'), (show['network'] or 'Unknown network'))\
+ ('' if not episode['description'] else u'%s%s' % (nl, episode['description'].splitlines()[0]))\
+ '%sEND:VEVENT%s' % (crlf, crlf)
# Ending the iCal
@ -389,21 +392,6 @@ class MainHandler(WebHandler):
sickbeard.POSTER_SORTDIR = int(direction)
sickbeard.save_config()
def setHistoryLayout(self, layout):
if layout not in ('compact', 'detailed'):
layout = 'detailed'
sickbeard.HISTORY_LAYOUT = layout
self.redirect('/history/')
def toggleDisplayShowSpecials(self, show):
sickbeard.DISPLAY_SHOW_SPECIALS = not sickbeard.DISPLAY_SHOW_SPECIALS
self.redirect('/home/displayShow?show=' + show)
def setEpisodeViewLayout(self, layout):
if layout not in ('poster', 'banner', 'list', 'daybyday'):
layout = 'banner'
@ -425,6 +413,15 @@ class MainHandler(WebHandler):
self.redirect('/episodeView/')
def setEpisodeViewCards(self, redir=0, *args, **kwargs):
sickbeard.EPISODE_VIEW_POSTERS = not sickbeard.EPISODE_VIEW_POSTERS
sickbeard.save_config()
if int(redir):
self.redirect('/episodeView/')
def setEpisodeViewSort(self, sort, redir=1):
if sort not in ('time', 'network', 'show'):
sort = 'time'
@ -497,18 +494,40 @@ class MainHandler(WebHandler):
return (remove_article(value.lower()), value.lower())[sickbeard.SORT_ARTICLE]
# add localtime to the dict
cache_obj = image_cache.ImageCache()
t = PageTemplate(headers=self.request.headers, file='episodeView.tmpl')
t.fanart = {}
for index, item in enumerate(sql_results):
sql_results[index]['localtime'] = sbdatetime.sbdatetime.convert_to_setting(network_timezones.parse_date_time(item['airdate'],
item['airs'], item['network']))
sql_results[index]['data_show_name'] = value_maybe_article(item['show_name'])
sql_results[index]['data_network'] = value_maybe_article(item['network'])
sql_results.sort(sorts[sickbeard.EPISODE_VIEW_SORT])
show_id = item['showid']
if show_id in t.fanart:
continue
t = PageTemplate(headers=self.request.headers, file='episodeView.tmpl')
t.next_week = datetime.datetime.combine(next_week_dt, datetime.time(tzinfo=network_timezones.sb_timezone))
t.today = datetime.datetime.now(network_timezones.sb_timezone)
t.sql_results = sql_results
for img in ek.ek(glob.glob, cache_obj.fanart_path(show_id).replace('fanart.jpg', '*')) or []:
match = re.search(r'\.(\d+(?:\.\w*)?\.(?:\w{5,8}))\.fanart\.', img, re.I)
if not match:
continue
fanart = [(match.group(1), sickbeard.FANART_RATINGS.get(str(show_id), {}).get(match.group(1), ''))]
if show_id not in t.fanart:
t.fanart[show_id] = fanart
else:
t.fanart[show_id] += fanart
for show in t.fanart:
fanart_rating = [(n, v) for n, v in t.fanart[show] if 20 == v]
if fanart_rating:
t.fanart[show] = fanart_rating
else:
rnd = [(n, v) for (n, v) in t.fanart[show] if 30 != v]
grouped = [(n, v) for (n, v) in rnd if 10 == v]
if grouped:
t.fanart[show] = [grouped[random.randint(0, len(grouped) - 1)]]
elif rnd:
t.fanart[show] = [rnd[random.randint(0, len(rnd) - 1)]]
# Allow local overriding of layout parameter
if layout and layout in ('banner', 'daybyday', 'list', 'poster'):
@ -516,8 +535,77 @@ class MainHandler(WebHandler):
else:
t.layout = sickbeard.EPISODE_VIEW_LAYOUT
t.has_art = bool(len(t.fanart))
t.css = ' '.join([t.layout] +
([], [('landscape', 'portrait')[sickbeard.EPISODE_VIEW_POSTERS]])['daybyday' == t.layout] +
([], ['back-art'])[sickbeard.EPISODE_VIEW_BACKGROUND and t.has_art] +
([], ['translucent'])[sickbeard.EPISODE_VIEW_BACKGROUND_TRANSLUCENT] +
[{0: 'reg', 1: 'pro', 2: 'pro ii'}.get(sickbeard.EPISODE_VIEW_VIEWMODE)])
t.fanart_panel = sickbeard.FANART_PANEL
sql_results.sort(sorts[sickbeard.EPISODE_VIEW_SORT])
t.next_week = datetime.datetime.combine(next_week_dt, datetime.time(tzinfo=network_timezones.sb_timezone))
t.today = datetime.datetime.now(network_timezones.sb_timezone)
t.sql_results = sql_results
return t.respond()
def live_panel(self, *args, **kwargs):
if 'allseasons' in kwargs:
sickbeard.DISPLAY_SHOW_MINIMUM = bool(config.minimax(kwargs['allseasons'], 0, 0, 1))
elif 'rate' in kwargs:
which = kwargs['which'].replace('fanart_', '')
rating = int(kwargs['rate'])
if rating:
sickbeard.FANART_RATINGS.setdefault(kwargs['show'], {}).update({which: rating})
elif sickbeard.FANART_RATINGS.get(kwargs['show'], {}).get(which):
del sickbeard.FANART_RATINGS[kwargs['show']][which]
if not sickbeard.FANART_RATINGS[kwargs['show']]:
del sickbeard.FANART_RATINGS[kwargs['show']]
else:
translucent = bool(config.minimax(kwargs.get('translucent'), 0, 0, 1))
backart = bool(config.minimax(kwargs.get('backart'), 0, 0, 1))
viewmode = config.minimax(kwargs.get('viewmode'), 0, 0, 2)
if 'ds' == kwargs.get('pg', None):
if 'viewart' in kwargs:
sickbeard.DISPLAY_SHOW_VIEWART = config.minimax(kwargs['viewart'], 0, 0, 2)
elif 'translucent' in kwargs:
sickbeard.DISPLAY_SHOW_BACKGROUND_TRANSLUCENT = translucent
elif 'backart' in kwargs:
sickbeard.DISPLAY_SHOW_BACKGROUND = backart
elif 'viewmode' in kwargs:
sickbeard.DISPLAY_SHOW_VIEWMODE = viewmode
elif 'ev' == kwargs.get('pg', None):
if 'translucent' in kwargs:
sickbeard.EPISODE_VIEW_BACKGROUND_TRANSLUCENT = translucent
elif 'backart' in kwargs:
sickbeard.EPISODE_VIEW_BACKGROUND = backart
sickbeard.FANART_PANEL = 'highlight-off' == sickbeard.FANART_PANEL and 'highlight-off' or \
'highlight2' == sickbeard.FANART_PANEL and 'highlight1' or \
'highlight1' == sickbeard.FANART_PANEL and 'highlight' or 'highlight-off'
elif 'viewmode' in kwargs:
sickbeard.EPISODE_VIEW_VIEWMODE = viewmode
sickbeard.save_config()
def toggleDisplayShowSpecials(self, show):
sickbeard.DISPLAY_SHOW_SPECIALS = not sickbeard.DISPLAY_SHOW_SPECIALS
self.redirect('/home/displayShow?show=' + show)
def setHistoryLayout(self, layout):
if layout not in ('compact', 'detailed'):
layout = 'detailed'
sickbeard.HISTORY_LAYOUT = layout
self.redirect('/history/')
def _genericMessage(self, subject, message):
t = PageTemplate(headers=self.request.headers, file='genericMessage.tmpl')
t.submenu = self.HomeMenu()
@ -529,7 +617,6 @@ class MainHandler(WebHandler):
class Home(MainHandler):
def HomeMenu(self):
return [
{'title': 'Add Shows', 'path': 'home/addShows/', },
{'title': 'Manual Post-Processing', 'path': 'home/postprocess/'},
{'title': 'Update Emby', 'path': 'home/updateEMBY/', 'requires': self.haveEMBY},
{'title': 'Update Kodi', 'path': 'home/updateKODI/', 'requires': self.haveKODI},
@ -606,6 +693,13 @@ class Home(MainHandler):
if 0 == len(t.showlists):
t.showlists.append(['container0', 'Show List', sickbeard.showList])
else:
items = []
default = 0
for index, group in enumerate(t.showlists):
items += group[2]
default = (default, index)['Show List' == group[1]]
t.showlists[default][2] += [show for show in sickbeard.showList if show not in items]
if 'simple' != sickbeard.HOME_LAYOUT:
t.network_images = {}
@ -1143,6 +1237,44 @@ class Home(MainHandler):
else:
self.redirect('/home/')
def display_season(self, show=None, season=None):
response = {'success': False}
show_obj = None
if show:
show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, helpers.tryInt(show, -1))
if not show_obj:
return json.dumps(response)
re_season = re.compile('(?i)^showseason-(\d+)$')
season = None if not any(re_season.findall(season)) else \
helpers.tryInt(re_season.findall(season)[0], None)
if None is season:
return json.dumps(response)
t = PageTemplate(headers=self.request.headers, file='inc_displayShow.tmpl')
t.show = show_obj
my_db = db.DBConnection()
sql_results = my_db.select('SELECT * FROM tv_episodes WHERE showid = ? AND season = ? ORDER BY episode DESC',
[show_obj.indexerid, season])
t.episodes = sql_results
ep_cats = {}
for row in sql_results:
status_overview = show_obj.getOverview(int(row['status']))
if status_overview:
ep_cats['%sx%s' % (season, row['episode'])] = status_overview
t.ep_cats = ep_cats
args = (int(show_obj.indexerid), int(show_obj.indexer))
t.scene_numbering = get_scene_numbering_for_show(*args)
t.xem_numbering = get_xem_numbering_for_show(*args)
t.scene_absolute_numbering = get_scene_absolute_numbering_for_show(*args)
t.xem_absolute_numbering = get_xem_absolute_numbering_for_show(*args)
return json.dumps({'success': t.respond()})
def displayShow(self, show=None):
if show is None:
@ -1153,17 +1285,6 @@ class Home(MainHandler):
if showObj is None:
return self._genericMessage('Error', 'Show not in show list')
myDB = db.DBConnection()
seasonResults = myDB.select(
'SELECT DISTINCT season FROM tv_episodes WHERE showid = ? ORDER BY season desc',
[showObj.indexerid]
)
sqlResults = myDB.select(
'SELECT * FROM tv_episodes WHERE showid = ? ORDER BY season DESC, episode DESC',
[showObj.indexerid]
)
t = PageTemplate(headers=self.request.headers, file='displayShow.tmpl')
t.submenu = [{'title': 'Edit', 'path': 'home/editShow?show=%d' % showObj.indexerid}]
@ -1195,13 +1316,14 @@ class Home(MainHandler):
elif sickbeard.showQueueScheduler.action.isInSubtitleQueue(showObj): # @UndefinedVariable
show_message = 'This show is queued and awaiting subtitles download.'
t.force_update = 'home/updateShow?show=%d&amp;force=1&amp;web=1' % showObj.indexerid
if not sickbeard.showQueueScheduler.action.isBeingAdded(showObj): # @UndefinedVariable
if not sickbeard.showQueueScheduler.action.isBeingUpdated(showObj): # @UndefinedVariable
t.submenu.append(
{'title': 'Remove', 'path': 'home/deleteShow?show=%d' % showObj.indexerid, 'confirm': True})
t.submenu.append({'title': 'Re-scan files', 'path': 'home/refreshShow?show=%d' % showObj.indexerid})
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': t.force_update})
t.submenu.append({'title': 'Update show in Emby',
'path': 'home/updateEMBY%s' %
(INDEXER_TVDB == showObj.indexer and ('?show=%s' % showObj.indexerid) or '/'),
@ -1219,56 +1341,81 @@ class Home(MainHandler):
{'title': 'Download Subtitles', 'path': 'home/subtitleShow?show=%d' % showObj.indexerid})
t.show = showObj
t.sqlResults = sqlResults
t.seasonResults = seasonResults
t.show_message = show_message
epCounts = {}
epCats = {}
epCounts[Overview.SKIPPED] = 0
epCounts[Overview.WANTED] = 0
epCounts[Overview.QUAL] = 0
epCounts[Overview.GOOD] = 0
epCounts[Overview.UNAIRED] = 0
epCounts[Overview.SNATCHED] = 0
epCounts['videos'] = {}
epCounts['archived'] = {}
epCounts['totals'] = {}
highest_season = 0
latest_season = 0
ep_counts = {}
ep_cats = {}
ep_counts[Overview.SKIPPED] = 0
ep_counts[Overview.WANTED] = 0
ep_counts[Overview.QUAL] = 0
ep_counts[Overview.GOOD] = 0
ep_counts[Overview.UNAIRED] = 0
ep_counts[Overview.SNATCHED] = 0
ep_counts['videos'] = {}
ep_counts['status'] = {}
ep_counts['archived'] = {}
ep_counts['totals'] = {}
ep_counts['eps_most'] = 0
ep_counts['eps_all'] = 0
t.latest_season = 0
t.has_special = False
for curResult in sqlResults:
curEpCat = showObj.getOverview(int(curResult['status']))
if curEpCat:
epCats[str(curResult['season']) + 'x' + str(curResult['episode'])] = curEpCat
epCounts[curEpCat] += 1
if '' != curResult['location']:
if curResult['season'] not in epCounts['videos']:
epCounts['videos'][curResult['season']] = 1
my_db = db.DBConnection()
for row in my_db.select('SELECT season, count(*) AS cnt FROM tv_episodes WHERE showid = ?'
+ ' GROUP BY season', [showObj.indexerid]):
ep_counts['totals'][row['season']] = row['cnt']
if None is not ep_counts['totals'].get(0, None):
t.has_special = True
if not sickbeard.DISPLAY_SHOW_SPECIALS:
del(ep_counts['totals'][0])
ep_counts['eps_all'] = sum(ep_counts['totals'].values())
ep_counts['eps_most'] = max(ep_counts['totals'].values())
all_seasons = sorted(ep_counts['totals'].keys(), reverse=True)
t.lowest_season, t.highest_season = all_seasons[-1], all_seasons[0]
# 55 == seasons 1-10 and excludes the random season 0
force_display_show_minimum = 30 < ep_counts['eps_most'] or 55 < sum(ep_counts['totals'].keys())
display_show_minimum = sickbeard.DISPLAY_SHOW_MINIMUM or force_display_show_minimum
for row in my_db.select('SELECT max(season) as latest FROM tv_episodes WHERE showid = ?'
+ ' and 1000 < airdate and ? < status', [showObj.indexerid, UNAIRED]):
t.latest_season = row['latest']
t.season_min = ([], [1])[2 < t.latest_season] + [t.latest_season]
t.other_seasons = (list(set(all_seasons) - set(t.season_min)), [])[display_show_minimum]
t.seasons = []
for x in all_seasons:
t.seasons += [(x, [None] if x not in (t.season_min + t.other_seasons) else my_db.select(
'SELECT * FROM tv_episodes WHERE showid = ? AND season = ? ORDER BY episode DESC',
[showObj.indexerid, x]))]
for row in my_db.select('SELECT season, episode, status FROM tv_episodes WHERE showid = ? AND season IN (%s)' %
','.join(['?'] * len(t.season_min + t.other_seasons)),
[showObj.indexerid] + t.season_min + t.other_seasons):
status_overview = showObj.getOverview(row['status'])
if status_overview:
ep_cats['%sx%s' % (row['season'], row['episode'])] = status_overview
t.ep_cats = ep_cats
for row in my_db.select('SELECT season, count(*) AS cnt, status FROM tv_episodes WHERE showid = ?'
+ ' GROUP BY season, status', [showObj.indexerid]):
status_overview = showObj.getOverview(row['status'])
if status_overview:
ep_counts[status_overview] += row['cnt']
if ARCHIVED == row['status']:
ep_counts['archived'].setdefault(row['season'], row['cnt'])
else:
epCounts['videos'][curResult['season']] += 1
if curResult['season'] not in epCounts['totals']:
epCounts['totals'][curResult['season']] = 1
else:
epCounts['totals'][curResult['season']] += 1
if ARCHIVED == curResult['status']:
if curResult['season'] not in epCounts['archived']:
epCounts['archived'][curResult['season']] = 1
else:
epCounts['archived'][curResult['season']] += 1
if highest_season < curResult['season'] and 1000 < curResult['airdate'] and UNAIRED < curResult['status']:
highest_season = curResult['season']
ep_counts['status'].setdefault(row['season'], {status_overview: row['cnt']})
if 0 < len(epCounts['totals']):
latest_season = int(sorted(epCounts['totals'])[-1::][0])
display_seasons = []
if 1 < highest_season:
display_seasons += [1]
display_seasons += [highest_season]
for row in my_db.select('SELECT season, count(*) AS cnt FROM tv_episodes WHERE showid = ?'
+ ' AND \'\' != location GROUP BY season', [showObj.indexerid]):
ep_counts['videos'][row['season']] = row['cnt']
t.ep_counts = ep_counts
t.sortedShowLists = self.sorted_show_lists()
tvshows = []
tvshow_names = []
cur_sel = None
@ -1291,19 +1438,42 @@ class Home(MainHandler):
if showObj.is_anime:
t.bwl = showObj.release_groups
t.epCounts = epCounts
t.epCats = epCats
t.display_seasons = display_seasons
t.latest_season = latest_season
showObj.exceptions = scene_exceptions.get_scene_exceptions(showObj.indexerid)
t.fanart = []
cache_obj = image_cache.ImageCache()
for img in ek.ek(glob.glob, cache_obj.fanart_path(showObj.indexerid).replace('fanart.jpg', '*')) or []:
match = re.search(r'\.(\d+(?:\.(\w*?(\d*)))?\.(?:\w{5,8}))\.fanart\.', img, re.I)
if match and match.group(1):
t.fanart += [(match.group(1), sickbeard.FANART_RATINGS.get(show, {}).get(match.group(1), ''))]
t.start_image = None
ratings = [v for n, v in t.fanart]
if 20 in ratings:
t.start_image = ratings.index(20)
else:
rnd = [(x, v) for x, (n, v) in enumerate(t.fanart) if 30 != v]
grouped = [n for (n, v) in rnd if 10 == v]
if grouped:
t.start_image = grouped[random.randint(0, len(grouped) - 1)]
elif rnd:
t.start_image = rnd[random.randint(0, len(rnd) - 1)][0]
t.has_art = bool(len(t.fanart))
t.css = ' '.join(([], ['back-art'])[sickbeard.DISPLAY_SHOW_BACKGROUND and t.has_art] +
([], ['translucent'])[sickbeard.DISPLAY_SHOW_BACKGROUND_TRANSLUCENT] +
{0: [], 1: ['poster-right'], 2: ['poster-off']}.get(sickbeard.DISPLAY_SHOW_VIEWART) +
([], ['min'])[display_show_minimum] +
([], ['min-force'])[force_display_show_minimum] +
[{0: 'reg', 1: 'pro', 2: 'pro ii'}.get(sickbeard.DISPLAY_SHOW_VIEWMODE)])
t.clean_show_name = urllib.quote_plus(sickbeard.indexermapper.clean_show_name(showObj.name))
indexerid = int(showObj.indexerid)
indexer = int(showObj.indexer)
t.all_scene_exceptions = showObj.exceptions
t.scene_numbering = get_scene_numbering_for_show(indexerid, indexer)
t.xem_numbering = get_xem_numbering_for_show(indexerid, indexer)
t.scene_absolute_numbering = get_scene_absolute_numbering_for_show(indexerid, indexer)
t.xem_numbering = get_xem_numbering_for_show(indexerid, indexer)
t.xem_absolute_numbering = get_xem_absolute_numbering_for_show(indexerid, indexer)
return t.respond()
@ -1320,6 +1490,28 @@ class Home(MainHandler):
results = filter(lambda x: x.tag == tag, sickbeard.showList)
if results:
sorted_show_lists.append([tag, sorted(results, lambda x, y: cmp(titler(x.name), titler(y.name)))])
# handle orphaned shows
if len(sickbeard.showList) != sum([len(x[1]) for x in sorted_show_lists]):
used_ids = set()
for x in sorted_show_lists:
for y in x[1]:
used_ids |= {y.indexerid}
showlist = dict()
all_ids = set(x.indexerid for x in sickbeard.showList)
for iid in list(all_ids - used_ids):
try:
show = helpers.findCertainShow(sickbeard.showList, iid)
except (StandardError, Exception):
pass
if show:
if show.tag in showlist:
showlist[show.tag] += [show]
else:
showlist[show.tag] = [show]
sorted_show_lists += [[key, shows] for key, shows in showlist.items()]
elif 'anime' == sickbeard.SHOWLIST_TAGVIEW:
shows = []
anime = []
@ -1342,7 +1534,7 @@ class Home(MainHandler):
result = myDB.select(
'SELECT description FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ?',
(int(show), int(season), int(episode)))
return result[0]['description'] if result else 'Episode not found.'
return 'Episode not found.' if not result else (result[0]['description'] or '')[:250:]
def sceneExceptions(self, show):
exceptionsList = sickbeard.scene_exceptions.get_all_scene_exceptions(show)
@ -1351,10 +1543,8 @@ class Home(MainHandler):
out = []
for season, names in iter(sorted(iteritems(exceptionsList))):
if season == -1:
season = '*'
out.append('S' + str(season) + ': ' + ', '.join(names))
return '<br/>'.join(out)
out.append('S%s: %s' % ((season, '*')[-1 == season], ',<br />\n'.join(names)))
return '---------<br />\n'.join(out)
def switchIndexer(self, indexerid, indexer, mindexerid, mindexer, set_pause=False, mark_wanted=False):
indexer = helpers.tryInt(indexer)
@ -1473,11 +1663,38 @@ class Home(MainHandler):
ui.notifications.message('Mapping Reloaded')
return json.dumps({k: {r: w for r, w in v.iteritems() if 'date' != r} for k, v in show_obj.ids.iteritems()})
@staticmethod
def fanart_tmpl(t):
t.fanart = []
cache_obj = image_cache.ImageCache()
for img in ek.ek(glob.glob, cache_obj.fanart_path(t.show.indexerid).replace('fanart.jpg', '*')) or []:
match = re.search(r'\.(\d+(?:\.(\w*?(\d*)))?\.(?:\w{5,8}))\.fanart\.', img, re.I)
if match and match.group(1):
t.fanart += [(match.group(1),
sickbeard.FANART_RATINGS.get(str(t.show.indexerid), {}).get(match.group(1), ''))]
t.start_image = None
ratings = [v for n, v in t.fanart]
if 20 in ratings:
t.start_image = ratings.index(20)
else:
rnd = [(x, v) for x, (n, v) in enumerate(t.fanart) if 30 != v]
grouped = [n for (n, v) in rnd if 10 == v]
if grouped:
t.start_image = grouped[random.randint(0, len(grouped) - 1)]
elif rnd:
t.start_image = rnd[random.randint(0, len(rnd) - 1)][0]
t.has_art = bool(len(t.fanart))
t.css = ' '.join(([], ['back-art'])[sickbeard.DISPLAY_SHOW_BACKGROUND and t.has_art] +
([], ['translucent'])[sickbeard.DISPLAY_SHOW_BACKGROUND_TRANSLUCENT] +
[{0: 'reg', 1: 'pro', 2: 'pro ii'}.get(sickbeard.DISPLAY_SHOW_VIEWMODE)])
def editShow(self, show=None, location=None, anyQualities=[], bestQualities=[], exceptions_list=[],
flatten_folders=None, paused=None, directCall=False, air_by_date=None, sports=None, dvdorder=None,
indexerLang=None, subtitles=None, archive_firstmatch=None, rls_ignore_words=None,
rls_require_words=None, anime=None, blacklist=None, whitelist=None,
scene=None, tag=None, quality_preset=None, **kwargs):
scene=None, tag=None, quality_preset=None, reset_fanart=None, **kwargs):
if show is None:
errString = 'Invalid show ID: ' + str(show)
@ -1528,6 +1745,10 @@ class Home(MainHandler):
t.show = showObj
t.show_has_scene_map = showObj.indexerid in sickbeard.scene_exceptions.xem_ids_list[showObj.indexer]
# noinspection PyTypeChecker
self.fanart_tmpl(t)
t.num_ratings = len(sickbeard.FANART_RATINGS.get(str(t.show.indexerid), {}))
return t.respond()
flatten_folders = config.checkbox_to_value(flatten_folders)
@ -1540,6 +1761,10 @@ class Home(MainHandler):
anime = config.checkbox_to_value(anime)
subtitles = config.checkbox_to_value(subtitles)
if config.checkbox_to_value(reset_fanart) and sickbeard.FANART_RATINGS.get(show):
del sickbeard.FANART_RATINGS[show]
sickbeard.save_config()
if indexerLang and indexerLang in sickbeard.indexerApi(showObj.indexer).indexer().config['valid_languages']:
indexer_lang = indexerLang
else:
@ -1955,14 +2180,17 @@ class Home(MainHandler):
if cur_ep_obj.location:
if cur_ep_obj.relatedEps:
# do we have one of multi-episodes in the rename list already
have_already = False
for cur_related_ep in cur_ep_obj.relatedEps + [cur_ep_obj]:
if cur_related_ep in ep_obj_rename_list:
have_already = True
break
if not have_already:
ep_obj_rename_list.append(cur_ep_obj)
ep_status, ep_qual = Quality.splitCompositeStatus(cur_related_ep.status)
if not ep_qual:
continue
ep_obj_rename_list.append(cur_ep_obj)
else:
ep_status, ep_qual = Quality.splitCompositeStatus(cur_ep_obj.status)
if not ep_qual:
continue
ep_obj_rename_list.append(cur_ep_obj)
if ep_obj_rename_list:
@ -1974,6 +2202,9 @@ class Home(MainHandler):
t.ep_obj_list = ep_obj_rename_list
t.show = showObj
# noinspection PyTypeChecker
self.fanart_tmpl(t)
return t.respond()
def doRename(self, show=None, eps=None):
@ -3214,7 +3445,7 @@ class NewHomeAddShows(Home):
return t.respond()
def existing_shows(self, *args, **kwargs):
def import_shows(self, *args, **kwargs):
"""
Prints out the page to add existing shows from a root dir
"""
@ -3269,7 +3500,7 @@ class NewHomeAddShows(Home):
logger.log('Unable to add show due to show selection. Not enough arguments: %s' % (repr(series_pieces)),
logger.ERROR)
ui.notifications.error('Unknown error. Unable to add show due to problem with show selection.')
return self.redirect('/home/addShows/existing_shows/')
return self.redirect('/home/addShows/import_shows/')
indexer = int(series_pieces[0])
indexer_id = int(series_pieces[2])
@ -3292,7 +3523,7 @@ class NewHomeAddShows(Home):
# blanket policy - if the dir exists you should have used 'add existing show' numbnuts
if ek.ek(os.path.isdir, show_dir) and not fullShowPath:
ui.notifications.error('Unable to add show', u'Found existing folder: ' + show_dir)
return self.redirect('/home/addShows/existing_shows?sid=%s&hash_dir=%s' % (indexer_id, abs(hash(show_dir))))
return self.redirect('/home/addShows/import_shows?sid=%s&hash_dir=%s' % (indexer_id, abs(hash(show_dir))))
# don't create show dir if config says not to
if sickbeard.ADD_SHOWS_WO_DIR:
@ -4381,34 +4612,41 @@ class ConfigGeneral(Config):
proxy_setting=None, proxy_indexers=None, anon_redirect=None, git_path=None, git_remote=None, calendar_unprotected=None,
fuzzy_dating=None, trim_zero=None, date_preset=None, date_preset_na=None, time_preset=None,
indexer_timeout=None, rootDir=None, theme_name=None, default_home=None, use_imdb_info=None,
show_tags=None, showlist_tagview=None):
fanart_limit=None, show_tags=None, showlist_tagview=None):
results = []
# Misc
sickbeard.LAUNCH_BROWSER = config.checkbox_to_value(launch_browser)
config.change_VERSION_NOTIFY(config.checkbox_to_value(version_notify))
sickbeard.AUTO_UPDATE = config.checkbox_to_value(auto_update)
sickbeard.NOTIFY_ON_UPDATE = config.checkbox_to_value(notify_on_update)
# sickbeard.LOG_DIR is set in config.change_LOG_DIR()
sickbeard.UPDATE_SHOWS_ON_START = config.checkbox_to_value(update_shows_on_start)
sickbeard.SHOW_UPDATE_HOUR = config.minimax(show_update_hour, 3, 0, 23)
sickbeard.TRASH_REMOVE_SHOW = config.checkbox_to_value(trash_remove_show)
sickbeard.TRASH_ROTATE_LOGS = config.checkbox_to_value(trash_rotate_logs)
if not config.change_LOG_DIR(log_dir, web_log):
results += ['Unable to create directory ' + os.path.normpath(log_dir) + ', log directory not changed.']
if indexer_default:
sickbeard.INDEXER_DEFAULT = config.to_int(indexer_default)
if not sickbeard.indexerApi(sickbeard.INDEXER_DEFAULT).config['active']:
sickbeard.INDEXER_DEFAULT = INDEXER_TVDB
if indexer_timeout:
sickbeard.INDEXER_TIMEOUT = config.to_int(indexer_timeout)
# Updates
config.change_VERSION_NOTIFY(config.checkbox_to_value(version_notify))
sickbeard.AUTO_UPDATE = config.checkbox_to_value(auto_update)
config.change_UPDATE_FREQUENCY(update_frequency)
sickbeard.LAUNCH_BROWSER = config.checkbox_to_value(launch_browser)
sickbeard.HOME_SEARCH_FOCUS = config.checkbox_to_value(home_search_focus)
sickbeard.USE_IMDB_INFO = config.checkbox_to_value(use_imdb_info)
sickbeard.SORT_ARTICLE = config.checkbox_to_value(sort_article)
sickbeard.CPU_PRESET = cpu_preset
sickbeard.FILE_LOGGING_PRESET = file_logging_preset
sickbeard.NOTIFY_ON_UPDATE = config.checkbox_to_value(notify_on_update)
# Interface
sickbeard.THEME_NAME = theme_name
sickbeard.DEFAULT_HOME = default_home
sickbeard.FANART_LIMIT = config.minimax(fanart_limit, 3, 0, 500)
sickbeard.SHOWLIST_TAGVIEW = showlist_tagview
# 'Show List' is the must have default fallback. Tags in use that are removed from config ui are restored, not deleted.
# Deduped list order preservation is key to feature function.
myDB = db.DBConnection()
sql_results = myDB.select('SELECT DISTINCT tag FROM tv_shows')
# 'Show List' is the must have default fallback. Tags in use that are removed from config ui are restored,
# not deleted. Deduped list order preservation is key to feature function.
my_db = db.DBConnection()
sql_results = my_db.select('SELECT DISTINCT tag FROM tv_shows')
new_names = [u'' + v.strip() for v in (show_tags.split(u','), [])[None is show_tags] if v.strip()]
orphans = [item for item in [v['tag'] for v in sql_results or []] if item not in new_names]
cleanser = []
@ -4419,75 +4657,60 @@ class ConfigGeneral(Config):
sickbeard.SHOW_TAGS = [dedupe.setdefault(item, item) for item in (cleanser + new_names + [u'Show List'])
if item not in dedupe]
logger.log_set_level()
sickbeard.ANON_REDIRECT = anon_redirect
sickbeard.PROXY_SETTING = proxy_setting
sickbeard.PROXY_INDEXERS = config.checkbox_to_value(proxy_indexers)
sickbeard.GIT_PATH = git_path
sickbeard.GIT_REMOTE = git_remote
sickbeard.CALENDAR_UNPROTECTED = config.checkbox_to_value(calendar_unprotected)
# sickbeard.LOG_DIR is set in config.change_LOG_DIR()
sickbeard.WEB_PORT = config.to_int(web_port)
sickbeard.WEB_IPV6 = config.checkbox_to_value(web_ipv6)
# sickbeard.WEB_LOG is set in config.change_LOG_DIR()
sickbeard.ENCRYPTION_VERSION = config.checkbox_to_value(encryption_version)
sickbeard.HOME_SEARCH_FOCUS = config.checkbox_to_value(home_search_focus)
sickbeard.USE_IMDB_INFO = config.checkbox_to_value(use_imdb_info)
sickbeard.SORT_ARTICLE = config.checkbox_to_value(sort_article)
sickbeard.FUZZY_DATING = config.checkbox_to_value(fuzzy_dating)
sickbeard.TRIM_ZERO = config.checkbox_to_value(trim_zero)
if date_preset:
sickbeard.DATE_PRESET = date_preset
if time_preset:
sickbeard.TIME_PRESET_W_SECONDS = time_preset
sickbeard.TIME_PRESET = sickbeard.TIME_PRESET_W_SECONDS.replace(u':%S', u'')
sickbeard.TIMEZONE_DISPLAY = timezone_display
# Web interface
reload_page = False
if sickbeard.WEB_USERNAME != web_username:
sickbeard.WEB_USERNAME = web_username
reload_page = True
if set('*') != set(web_password):
sickbeard.WEB_PASSWORD = web_password
reload_page = True
sickbeard.FUZZY_DATING = config.checkbox_to_value(fuzzy_dating)
sickbeard.TRIM_ZERO = config.checkbox_to_value(trim_zero)
if date_preset:
sickbeard.DATE_PRESET = date_preset
if indexer_default:
sickbeard.INDEXER_DEFAULT = config.to_int(indexer_default)
if not sickbeard.indexerApi(sickbeard.INDEXER_DEFAULT).config['active']:
sickbeard.INDEXER_DEFAULT = INDEXER_TVDB
if indexer_timeout:
sickbeard.INDEXER_TIMEOUT = config.to_int(indexer_timeout)
if time_preset:
sickbeard.TIME_PRESET_W_SECONDS = time_preset
sickbeard.TIME_PRESET = sickbeard.TIME_PRESET_W_SECONDS.replace(u':%S', u'')
sickbeard.TIMEZONE_DISPLAY = timezone_display
if not config.change_LOG_DIR(log_dir, web_log):
results += ['Unable to create directory ' + os.path.normpath(log_dir) + ', log directory not changed.']
sickbeard.CALENDAR_UNPROTECTED = config.checkbox_to_value(calendar_unprotected)
sickbeard.USE_API = config.checkbox_to_value(use_api)
sickbeard.API_KEY = api_key
sickbeard.WEB_PORT = config.to_int(web_port)
# sickbeard.WEB_LOG is set in config.change_LOG_DIR()
sickbeard.ENABLE_HTTPS = config.checkbox_to_value(enable_https)
if not config.change_HTTPS_CERT(https_cert):
results += [
'Unable to create directory ' + os.path.normpath(https_cert) + ', https cert directory not changed.']
if not config.change_HTTPS_KEY(https_key):
results += [
'Unable to create directory ' + os.path.normpath(https_key) + ', https key directory not changed.']
sickbeard.WEB_IPV6 = config.checkbox_to_value(web_ipv6)
sickbeard.HANDLE_REVERSE_PROXY = config.checkbox_to_value(handle_reverse_proxy)
sickbeard.THEME_NAME = theme_name
sickbeard.DEFAULT_HOME = default_home
# Advanced
sickbeard.GIT_REMOTE = git_remote
sickbeard.GIT_PATH = git_path
sickbeard.CPU_PRESET = cpu_preset
sickbeard.ANON_REDIRECT = anon_redirect
sickbeard.ENCRYPTION_VERSION = config.checkbox_to_value(encryption_version)
sickbeard.PROXY_SETTING = proxy_setting
sickbeard.PROXY_INDEXERS = config.checkbox_to_value(proxy_indexers)
sickbeard.FILE_LOGGING_PRESET = file_logging_preset
# sickbeard.LOG_DIR is set in config.change_LOG_DIR()
logger.log_set_level()
sickbeard.save_config()
if len(results) > 0:
if 0 < len(results):
for v in results:
logger.log(v, logger.ERROR)
ui.notifications.error('Error(s) Saving Configuration',
@ -4496,7 +4719,7 @@ class ConfigGeneral(Config):
ui.notifications.message('Configuration Saved', ek.ek(os.path.join, sickbeard.CONFIG_FILE))
if reload_page:
self.clear_cookie('sickgear-session')
self.clear_cookie('sickgear-session-%s' % helpers.md5_for_text(sickbeard.WEB_PORT))
self.write('reload')
@staticmethod
@ -4994,6 +5217,7 @@ class ConfigProviders(Config):
cur_name, cur_url, cur_key, cur_cat = curNewznabProviderStr.split('|')
cur_url = config.clean_url(cur_url)
cur_key = cur_key.strip()
if starify(cur_key, True):
cur_key = ''
@ -5552,8 +5776,10 @@ class UI(MainHandler):
class ErrorLogs(MainHandler):
@staticmethod
def ErrorLogsMenu():
return [{'title': 'Download Log', 'path': 'errorlogs/downloadlog/'},
{'title': 'Clear Errors', 'path': 'errorlogs/clearerrors/'},]
if len(classes.ErrorViewer.errors):
return [{'title': 'Download Log', 'path': 'errorlogs/downloadlog/'},
{'title': 'Clear Errors', 'path': 'errorlogs/clearerrors/'},]
return [{'title': 'Download Log', 'path': 'errorlogs/downloadlog/'},]
def index(self, *args, **kwargs):
@ -5583,56 +5809,73 @@ class ErrorLogs(MainHandler):
except (StandardError, Exception):
return
def viewlog(self, minLevel=logger.MESSAGE, maxLines=500):
def viewlog(self, min_level=logger.MESSAGE, max_lines=500):
t = PageTemplate(headers=self.request.headers, file='viewlogs.tmpl')
t.submenu = self.ErrorLogsMenu
minLevel = int(minLevel)
data = []
if os.path.isfile(logger.sb_log_instance.log_file_path):
with ek.ek(open, logger.sb_log_instance.log_file_path) as f:
data = f.readlines()
min_level = int(min_level)
regex = '^(\d\d\d\d)\-(\d\d)\-(\d\d)\s*(\d\d)\:(\d\d):(\d\d)\s*([A-Z]+)\s*(.+?)\s*\:\:\s*(.*)$'
finalData = []
final_data = []
normal_data = []
truncate = []
repeated = None
num_lines = 0
numLines = 0
lastLine = False
numToShow = min(maxLines, len(data))
if os.path.isfile(logger.sb_log_instance.log_file_path):
for x in logger.sb_log_instance.reverse_readline(logger.sb_log_instance.log_file_path):
for x in reversed(data):
x = x.decode('utf-8', 'replace')
match = re.match(regex, x)
x = x.decode('utf-8', 'replace')
match = re.match(regex, x)
if match:
level = match.group(7)
if level not in logger.reverseNames:
normal_data = []
continue
if match:
level = match.group(7)
if level not in logger.reverseNames:
lastLine = False
continue
if logger.reverseNames[level] >= min_level:
if truncate and not normal_data and truncate[0] == match.group(8) + match.group(9):
truncate += [match.group(8) + match.group(9)]
repeated = x
continue
if 1 < len(truncate):
final_data[-1] = repeated.strip() + \
' <span class="grey-text">(...%s repeat lines)</span>\n' % len(truncate)
truncate = [match.group(8) + match.group(9)]
final_data.append(x.replace(
' Starting SickGear', ' <span class="prelight2">Starting SickGear</span>'))
if any(normal_data):
final_data += ['<code><span class="prelight">'] + \
['<span class="prelight-num">%02s)</span> %s' % (n + 1, x)
for n, x in enumerate(normal_data[::-1])] + \
['</span></code><br />']
num_lines += len(normal_data)
normal_data = []
else:
normal_data = []
continue
if logger.reverseNames[level] >= minLevel:
lastLine = True
finalData.append(x)
else:
lastLine = False
continue
if not any(normal_data) and not any([x.strip()]):
continue
elif lastLine:
finalData.append('AA' + x)
normal_data.append(re.sub(r'\r?\n', '<br />', x.replace('<', '&lt;').replace('>', '&gt;')))
numLines += 1
num_lines += 1
if numLines >= numToShow:
break
if num_lines >= max_lines:
break
result = ''.join(finalData)
result = ''.join(final_data)
t.logLines = result
t.minLevel = minLevel
t.min_level = min_level
return t.respond()