Merge branch 'release/0.6.0'

This commit is contained in:
Adam 2015-01-18 13:06:48 +08:00
commit 663cb0691b
83 changed files with 2766 additions and 2249 deletions

View file

@ -1,3 +1,43 @@
### 0.6.0 (2015-01-18 05:05:00 UTC)
* Add network logos BBC Canada, Crackle, El Rey Network, SKY Atlantic, and Watch
* Change Yahoo! screen network logo
* Add and update Discovery Network's channel logos
* Add A&E Network International/Scripps Networks International channel logos
* Remove non required duplicate network logos
* Add lowercase PM to the General Config/Interface/Time style selection
* Change General Config/Interface/Trim zero padding to Trim date and time, now handles 2:00 pm > 2 pm
* Fix trim zero of military time hour to not use 12 hr time
* Change ThePirateBay to use oldpiratebay as a temporary fix
* Change Search Settings/Torrent/Deluge option texts for improved understanding
* Fix Womble's Index searching (ssl disabled for now, old categories are the new active ones again)
* Fix Add From Trending Show page to work with Trakt changes
* Add anime unit test cases (port from lad1337/sickbeard)
* Fix normal tv show regex (port from midgetspy/sickbeard)
* Fix anime regex (port from lad1337/sickbeard)
* Add pull request checkout option to General Config/Advanced Settings
* Add BTN api call parameter debug logging
* Fix anime searches on BTN provider
* Change replace "Daily-Search" with "Recent-Search"
* Add daily search to recent search renaming to config migration code
* Fix 'NoneType' object is not iterable in trakt module
* Add log message for when trakt does not return a watchlist
* Change Coming Episodes calendar view to a fluid layout, change episode layout design, and add day and month in column headers
* Add isotope plug-in to Coming Episodes calendar view to enable sort columns by Date, Network, and Show name
* Add imagesLoaded plug-in to prevent layout breakage by calling isotope to update content after a page auto-refresh
* Change Coming Episodes to "Episodes" page (API endpoint is not renamed)
* Add coming episodes to episode view renaming to config migration code
* Change Layout term "Calender" to "Day by Day" on Episodes page
* Fix saving of sort modes to config file on Episodes page
* Add qTip episode plots to "Day by Day" on Episodes page
* Add article sorting to networks on Episodes page
* Add toggle sort direction and multidimensional sort to isotope on Episodes page
* Add text "[paused]" where appropriate to shows on layout Day by Day on Episodes page
* Change Epsiodes page auto refresh from 10 to 30 mins
* Add UI tweaks
* Fix progress bars disappearing on home page
### 0.5.0 (2014-12-21 11:40:00 UTC)
* Fix searches freezing due to unescaped ignored or required words

View file

@ -149,22 +149,18 @@ inc_top.tmpl
margin-bottom: -15px;
}
#contentWrapper {
/* background: url("../images/bg.png") repeat 0 0 transparent; */
[class^="icon-"],
[class*=" icon-"] {
background-image: url("../images/glyphicons-halflings.png");
}
[class^="icon-"],
[class*=" icon-"] {
background-image: url("../images/glyphicons-halflings.png");
.icon-white {
background-image: url("../images/glyphicons-halflings-white.png");
}
.icon-white {
background-image: url("../images/glyphicons-halflings-white.png");
}
.dropdown-menu li > a:hover > [class^="menu-icon-"],
.dropdown-menu li > a:hover > [class*=" menu-icon-"] {
background-image: url("../images/menu/menu-icons-white.png");
.dropdown-menu li > a:hover > [class^="menu-icon-"],
.dropdown-menu li > a:hover > [class*=" menu-icon-"] {
background-image: url("../images/menu/menu-icons-white.png");
}
.infoTableHeader .icon16-sb {
@ -172,29 +168,29 @@ inc_top.tmpl
}
.ui-autocomplete-loading {
background: white url("../images/loading16.gif") right center no-repeat;
background: white url("../images/loading16.gif") right center no-repeat;
}
.browserDialog.busy .ui-dialog-buttonpane {
background: url("../images/loading.gif") 10px 50% no-repeat !important;
.browserDialog.busy .ui-dialog-buttonpane {
background: url("../images/loading.gif") 10px 50% no-repeat !important;
}
.ui-progressbar .ui-progressbar-overlay {
background: url("../css/lib/images/animated-overlay.gif");
.ui-progressbar .ui-progressbar-overlay {
background: url("../css/lib/images/animated-overlay.gif");
}
.ui-dialog,
.ui-dialog-buttonpane {
background: #2a2a2a !important;;
.ui-dialog,
.ui-dialog-buttonpane {
background: #2a2a2a !important;;
}
.ui-accordion-content,
.ui-tabs-panel {
background: #ededed !important;
background-image: none !important;
.ui-accordion-content,
.ui-tabs-panel {
background: #ededed !important;
background-image: none !important;
}
.ui-widget-content {
.ui-widget-content {
background: #606060;
border: 1px solid #111;
color: #fff;
@ -208,82 +204,82 @@ inc_top.tmpl
color: #09A2FF;
}
.ui-widget-header {
.ui-widget-header {
background: #3d3d3d;
border: 1px solid #111;
color: #fff;
}
.ui-state-default,
.ui-widget-content .ui-state-default,
.ui-widget-header .ui-state-default {
.ui-state-default,
.ui-widget-content .ui-state-default,
.ui-widget-header .ui-state-default {
border: 1px solid #111;
}
.ui-state-hover,
.ui-widget-content .ui-state-hover,
.ui-widget-header .ui-state-hover,
.ui-state-focus,
.ui-widget-content .ui-state-focus,
.ui-widget-header .ui-state-focus {
background: #3d3d3d;
}
.ui-state-active,
.ui-widget-content .ui-state-active,
.ui-widget-header .ui-state-active {
.ui-state-hover,
.ui-widget-content .ui-state-hover,
.ui-widget-header .ui-state-hover,
.ui-state-focus,
.ui-widget-content .ui-state-focus,
.ui-widget-header .ui-state-focus {
background: #3d3d3d;
}
.ui-state-highlight,
.ui-widget-content .ui-state-highlight,
.ui-widget-header .ui-state-highlight {
background: #fbf9ee url("../css/lib/images/ui-bg_glass_55_fbf9ee_1x400.png") 50% 50% repeat-x;
.ui-state-active,
.ui-widget-content .ui-state-active,
.ui-widget-header .ui-state-active {
background: #3d3d3d;
}
.ui-state-error,
.ui-widget-content .ui-state-error,
.ui-widget-header .ui-state-error {
background: #fef1ec url("../css/lib/images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x;
.ui-state-highlight,
.ui-widget-content .ui-state-highlight,
.ui-widget-header .ui-state-highlight {
background: #fbf9ee url("../css/lib/images/ui-bg_glass_55_fbf9ee_1x400.png") 50% 50% repeat-x;
}
.ui-icon,
.ui-widget-content .ui-icon {
background-image: url("../css/lib/images/ui-icons_ffffff_256x240.png");
.ui-state-error,
.ui-widget-content .ui-state-error,
.ui-widget-header .ui-state-error {
background: #fef1ec url("../css/lib/images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x;
}
.ui-widget-header .ui-icon {
background-image: url("../css/lib/images/ui-icons_222222_256x240.png");
.ui-icon,
.ui-widget-content .ui-icon {
background-image: url("../css/lib/images/ui-icons_ffffff_256x240.png");
}
.ui-state-default .ui-icon {
background-image: url('../css/lib/images/ui-icons_09a2ff_256x240.png');
.ui-widget-header .ui-icon {
background-image: url("../css/lib/images/ui-icons_222222_256x240.png");
}
.ui-state-hover .ui-icon,
.ui-state-focus .ui-icon {
background-image: url("../css/lib/images/ui-icons_222222_256x240.png");
.ui-state-default .ui-icon {
background-image: url('../css/lib/images/ui-icons_09a2ff_256x240.png');
}
.ui-state-active .ui-icon {
background-image: url("../css/lib/images/ui-icons_8c291d_256x240.png");
.ui-state-hover .ui-icon,
.ui-state-focus .ui-icon {
background-image: url("../css/lib/images/ui-icons_222222_256x240.png");
}
.ui-state-highlight .ui-icon {
background-image: url("../css/lib/images/ui-icons_2e83ff_256x240.png");
.ui-state-active .ui-icon {
background-image: url("../css/lib/images/ui-icons_8c291d_256x240.png");
}
.ui-state-error .ui-icon,
.ui-state-error-text .ui-icon {
background-image: url("../css/lib/images/ui-icons_cd0a0a_256x240.png");
.ui-state-highlight .ui-icon {
background-image: url("../css/lib/images/ui-icons_2e83ff_256x240.png");
}
.ui-widget-overlay {
background: #000000 url("../css/lib/images/ui-bg_flat_0_000000_40x100.png") 50% 50% repeat-x;
.ui-state-error .ui-icon,
.ui-state-error-text .ui-icon {
background-image: url("../css/lib/images/ui-icons_cd0a0a_256x240.png");
}
.ui-widget-shadow {
background: #000000 url("../css/lib/images/ui-bg_flat_0_000000_40x100.png") 50% 50% repeat-x;
.ui-widget-overlay {
background: #000000 url("../css/lib/images/ui-bg_flat_0_000000_40x100.png") 50% 50% repeat-x;
}
.ui-widget-shadow {
background: #000000 url("../css/lib/images/ui-bg_flat_0_000000_40x100.png") 50% 50% repeat-x;
}
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited {
@ -301,21 +297,21 @@ inc_top.tmpl
}
.ui-tabs {
padding: 0px;
padding: 0;
background: none;
border-width: 0px;
border-width: 0;
}
.ui-tabs .ui-tabs-nav {
padding-left: 0px;
padding-left: 0;
background: transparent;
border-width: 0px 0px 0px 0px;
-moz-border-radius: 0px;
-webkit-border-radius: 0px;
border-radius: 0px;
border-width: 0 0 0 0;
-moz-border-radius: 0;
-webkit-border-radius: 0;
border-radius: 0;
}
.ui-tabs .ui-tabs-panel {
.ui-tabs .ui-tabs-panel {
background-color: #3d3d3d !important;
border: 1px solid #111 !important;
}
@ -331,15 +327,6 @@ inc_top.tmpl
border-top-right-radius: 5px;
}
#content {
width: 95%;
min-width: 875px;
padding: 15px;
margin-left: auto;
margin-right: auto;
clear: both;
}
#SubMenu {
padding-right: 20px;
clear: both;
@ -348,12 +335,12 @@ inc_top.tmpl
}
.upgrade-notification {
width: 600px;
text-align: center;
margin-left: auto;
margin-right: auto;
margin-top: 50px;
margin-bottom: 0px;
width: 600px;
text-align: center;
margin-left: auto;
margin-right: auto;
margin-top: 50px;
margin-bottom: 0;
}
[class^="menu-icon-"], [class*=" menu-icon-"] {
@ -379,83 +366,83 @@ inc_top.tmpl
}
.menu-icon-addshow {
background-position: 0px 0px;
background-position: 0 0;
}
.menu-icon-anime {
background-position: -21px 0px;
background-position: -21px 0;
}
.menu-icon-backlog-view {
background-position: -42px 0px;
background-position: -42px 0;
}
.menu-icon-backlog {
background-position: -63px 0px;
background-position: -63px 0;
}
.menu-icon-bittorrent {
background-position: -84px 0px;
background-position: -84px 0;
}
.menu-icon-config-index {
background-position: -105px 0px;
background-position: -105px 0;
}
.menu-icon-config {
background-position: -126px 0px;
background-position: -126px 0;
}
.menu-icon-failed-download {
background-position: -147px 0px;
background-position: -147px 0;
}
.menu-icon-home {
background-position: -168px 0px;
background-position: -168px 0;
}
.menu-icon-manage {
background-position: -189px 0px;
background-position: -189px 0;
}
.menu-icon-manage-searches {
background-position: -210px 0px;
background-position: -210px 0;
}
.menu-icon-poster {
background-position: -231px 0px;
background-position: -231px 0;
}
.menu-icon-postprocess {
background-position: -252px 0px;
background-position: -252px 0;
}
.menu-icon-restart {
background-position: -273px 0px;
background-position: -273px 0;
}
.menu-icon-shutdown {
background-position: -294px 0px;
background-position: -294px 0;
}
.menu-icon-update {
background-position: -315px 0px;
background-position: -315px 0;
}
.menu-icon-viewlog-errors {
background-position: -336px 0px;
background-position: -336px 0;
}
.menu-icon-viewlog {
background-position: -357px 0px;
background-position: -357px 0;
}
.menu-icon-xbmc {
background-position: -378px 0px;
background-position: -378px 0;
}
.menu-icon-help {
background-position: -399px 0px;
background-position: -399px 0;
}
[class^="submenu-icon-"], [class*=" submenu-icon-"] {
@ -465,27 +452,27 @@ inc_top.tmpl
}
.submenu-icon-anime {
background-position: -21px 0px;
background-position: -21px 0;
}
.submenu-icon-bittorrent {
background-position: -84px 0px;
background-position: -84px 0;
}
.submenu-icon-failed-download {
background-position: -147px 0px;
background-position: -147px 0;
}
.submenu-icon-restart {
background-position: -273px 0px;
background-position: -273px 0;
}
.submenu-icon-shutdown {
background-position: -294px 0px;
background-position: -294px 0;
}
.submenu-icon-xbmc {
background-position: -378px 0px;
background-position: -378px 0;
}
/* =======================================================================
@ -622,42 +609,42 @@ home.tmpl
}
.show .ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br {
border-bottom-right-radius: 0px;
border-bottom-right-radius: 0;
}
.show .ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl {
border-bottom-left-radius: 0px;
border-bottom-left-radius: 0;
}
.show .ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr {
border-top-right-radius: 0px;
border-top-right-radius: 0;
}
.show .ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl {
border-top-left-radius: 0px;
border-top-left-radius: 0;
}
.show .ui-widget-content {
border-top: 1px solid #111;
border-bottom: 1px solid #111;
border-left: 0px;
border-right: 0px;
border-left: 0;
border-right: 0;
}
.show .progress-80 {
border-radius: 0px;
border-radius: 0;
}
.show .progress-60 {
border-radius: 0px;
border-radius: 0;
}
.show .progress-40 {
border-radius: 0px;
border-radius: 0;
}
.show .progress-20 {
border-radius: 0px;
border-radius: 0;
}
.show-title {
@ -665,7 +652,7 @@ home.tmpl
overflow: hidden;
white-space: nowrap;
font-size: 11px;
margin: 4px 4px 0px 4px;
margin: 4px 4px 0 4px;
}
.show-title:after {
@ -674,7 +661,7 @@ home.tmpl
position: absolute;
width: 20px;
height: 100%;
top: 0;
top: 0;
right: 0;
background-image: -webkit-linear-gradient(right, rgba(51, 51, 51, 1), rgba(51, 51, 51, 0));
background-image: -moz-linear-gradient(right, rgba(51, 51, 51, 1), rgba(51, 51, 51, 0));
@ -688,7 +675,7 @@ home.tmpl
overflow: hidden;
white-space: nowrap;
font-size: 11px;
margin: 0px 4px 4px 4px;
margin: 0 4px 4px 4px;
}
.show-date:after {
@ -697,7 +684,7 @@ home.tmpl
position: absolute;
width: 20px;
height: 100%;
top: 0;
top: 0;
right: 0;
background-image: -webkit-linear-gradient(right, rgba(51, 51, 51, 1), rgba(51, 51, 51, 0));
background-image: -moz-linear-gradient(right, rgba(51, 51, 51, 1), rgba(51, 51, 51, 0));
@ -706,8 +693,8 @@ home.tmpl
background-image: linear-gradient(to left, rgba(51, 51, 51, 1), rgba(51, 51, 51, 0));
}
.show-table {
text-align:center;
.show-table {
text-align:center;
vertical-align:middle;
width: 33%
}
@ -745,7 +732,7 @@ home.tmpl
#sort-by {
display: inline;
list-style-type: none;
list-style-type: none;
padding: 0;
margin-left: 5px;
}
@ -815,20 +802,20 @@ home_trendingShows.tmpl
padding-bottom: 4px;
}
.traktContainer p {
.traktContainer p {
padding-top: 2px;
}
.traktContainer p img {
.traktContainer p img {
position: relative;
top: -2px;
}
.traktContainer p, .traktContainer i {
.traktContainer p, .traktContainer i {
white-space: nowrap;
font-size: 12px;
overflow: hidden;
/* text-shadow: 1px 1px 0px #000;*/
/* text-shadow: 1px 1px 0 #000;*/
padding-left: 4px;
margin: 0;
}
@ -864,7 +851,7 @@ home_postprocess.tmpl
/* =======================================================================
displayShow.tmpl
displayShow.tmpl
========================================================================== */
#showCol {
@ -894,7 +881,7 @@ h1.title {
}
.displayspecials {
position: relative;
position: relative;
top: -24px;
}
@ -902,7 +889,7 @@ h1.title {
cursor: default;
}
#showinfo {
#showinfo {
display: inline-block;
position: relative;
top: -3px;
@ -928,7 +915,7 @@ ul.tags li {
border: 1px solid #111;
color: #FFF;
font: 14px/18px "Open Sans", "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif;
text-shadow: 0px 1px rgba(0, 0, 0, 0.8);
text-shadow: 0 1px rgba(0, 0, 0, 0.8);
float: left;
}
@ -1066,7 +1053,7 @@ span.snatched b {
white-space: nowrap;
}
.sickbeardTable th,
.sickbeardTable th,
.sickbeardTable td {
border-top: 1px solid #222;
border-left: 1px solid #222;
@ -1168,7 +1155,7 @@ td.col-search {
}
/* =======================================================================
comingEpisodes.tmpl
episodeView.tmpl
========================================================================== */
.sort_data {
@ -1227,7 +1214,7 @@ h2.day, h2.network {
letter-spacing: 1px;
color: #FFF;
text-align: center;
text-shadow: -1px -1px 0px rgba(0, 0, 0, 0.3);
text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.3);
background-color: #15528F;
}
@ -1236,7 +1223,7 @@ h2.day, h2.network {
clear: both;
border: 1px solid #ccc;
margin: auto;
padding: 0px;
padding: 0;
text-align: left;
width: 750px;
border-radius: 5px;
@ -1325,17 +1312,42 @@ h2.day, h2.network {
color: #09A2FF;
}
table.cal-odd {
.day-of-week .day-number {
background-color: #15528F;
color: #fff;
}
.today .day-number .number, .today .day-number .month, .today .day-number .day {
color: #8dbeee;
}
.odd .daybyday-show {
background-color: #333;
}
table.cal-even {
.even .daybyday-show {
background-color: #3d3d3d;
}
.calendarShow .text .airtime {
color:#fff
.daybyday-show .episode-blank {
background-color: rgba(0,0,0,.1);
border-radius: 3px;
}
.calendarShow .text .episode-title {
color:#aaa
.day-of-week .poster img {
border-color: #111;
}
.day-of-week .text .airtime,
.day-of-week .text .episode,
.day-of-week .text .episode .name {
color: #fff;
}
.day-of-week .text .episode .season,
.day-of-week .text .episode .number {
color: rgb(9, 162, 255);
}
.day-of-week .text .episode {
color: rgb(141, 190, 238);
}
/* =======================================================================
@ -1372,12 +1384,7 @@ config*.tmpl
}
#config div.field-pair {
padding: 12px 0px;
}
#config div.field-pair input {
float: left;
margin-right: 6px;
padding: 12px 0;
}
#config .nocheck, #config div #customQuality, .metadataDiv {
@ -1415,14 +1422,14 @@ select .selected {
list-style-type: none;
}
#provider_order_list,
#provider_order_list,
#service_order_list {
width: 250px;
padding-left: 20px;
list-style-type: none;
}
#provider_order_list li,
#provider_order_list li,
#service_order_list li {
background: #333 !important;
color: #fff;
@ -1439,13 +1446,13 @@ select .selected {
width: 220px !important;
}
.infoTableHeader,
.infoTableHeader,
.infoTableCell {
padding: 5px;
}
.infoTableSeperator {
border-top: 1px dotted #666666;
.infoTableSeperator {
border-top: 1px dotted #666666;
}
/* =======================================================================
@ -1562,7 +1569,7 @@ td.tableright {
text-align: left;
vertical-align: middle;
width: 225px;
padding: 6px 0px;
padding: 6px 0;
}
.optionWrapper div.selectChoices {
@ -1596,7 +1603,6 @@ Global
========================================================================== */
span.path {
padding: 3px 6px;
color: #09A2FF;
background-color: #333;
}
@ -1608,13 +1614,13 @@ span.path {
span.quality {
font: 12px/13px "Open Sans", verdana, sans-serif;
background-image:-webkit-linear-gradient(top, rgba(255,255,255,0.08),rgba(255,255,255,0) 50%,rgba(0,0,0,0) 50%,rgba(0,0,0,0.25));
background-image:-moz-linear-gradient(top, rgba(255,255,255,0.08),rgba(255,255,255,0) 50%,rgba(0,0,0,0) 50%,rgba(0,0,0,0.25));
background-image:-o-linear-gradient(top, rgba(255,255,255,0.08),rgba(255,255,255,0) 50%,rgba(0,0,0,0) 50%,rgba(0,0,0,0.25));
background-image:linear-gradient(to bottom, rgba(255,255,255,0.08),rgba(255,255,255,0) 50%,rgba(0,0,0,0) 50%,rgba(0,0,0,0.25));
-webkit-box-shadow:inset 0 1px rgba(255,255,255,0.1),inset 0 -1px 3px rgba(0,0,0,0.3),inset 0 0 0 1px rgba(255,255,255,0.08),0 1px 2px rgba(0,0,0,0.15);
box-shadow:inset 0 1px rgba(255,255,255,0.1),inset 0 -1px 3px rgba(0,0,0,0.3),inset 0 0 0 1px rgba(255,255,255,0.08),0 1px 2px rgba(0,0,0,0.15);
text-shadow: 0px 1px rgba(0, 0, 0, 0.8);
background-image:-webkit-linear-gradient(top, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 50%,rgba(0, 0, 0, 0.25));
background-image:-moz-linear-gradient(top, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 50%,rgba(0, 0, 0, 0.25));
background-image:-o-linear-gradient(top, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 50%,rgba(0, 0, 0, 0.25));
background-image:linear-gradient(to bottom, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 50%,rgba(0, 0, 0, 0.25));
-webkit-box-shadow:inset 0 1px rgba(255, 255, 255, 0.1),inset 0 -1px 3px rgba(0, 0, 0, 0.3),inset 0 0 0 1px rgba(255, 255, 255, 0.08),0 1px 2px rgba(0, 0, 0, 0.15);
box-shadow:inset 0 1px rgba(255, 255, 255, 0.1),inset 0 -1px 3px rgba(0, 0, 0, 0.3),inset 0 0 0 1px rgba(255, 255, 255, 0.08),0 1px 2px rgba(0, 0, 0, 0.15);
text-shadow: 0 1px rgba(0, 0, 0, 0.8);
color: #FFFFFF;
display: inline-block;
padding: 2px 4px;
@ -1705,7 +1711,7 @@ option.flag {
div.blackwhitelist{
float:left;
text-align: center;
text-align: center;
}
div.blackwhitelist input {
@ -1734,7 +1740,7 @@ div.blackwhitelist span {
}
div.blackwhitelist.anidb, div.blackwhitelist.manual {
margin: 7px 0px;
margin: 7px 0;
}
@ -1742,8 +1748,8 @@ div.blackwhitelist.anidb, div.blackwhitelist.manual {
bootstrap Overrides
========================================================================== */
body {
padding-top: 60px;
body {
padding-top: 60px;
overflow-y: scroll;
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
color: #fff;
@ -1751,7 +1757,7 @@ body {
}
input[type="radio"] {
margin: 2px 0px 0px;
margin: 2px 0 0;
line-height: normal;
}
@ -1766,7 +1772,7 @@ input, textarea, select, .uneditable-input {
}
.navbar-brand {
padding: 0px;
padding: 0;
}
/* navbar styling */
@ -1906,7 +1912,7 @@ fieldset[disabled] .navbar-default .btn-link:focus {
.dropdown-menu {
background-color: #333;
border: 1px solid rgba(0, 0, 0, 0.15);
box-shadow: 0px 6px 12px rgba(0, 0, 0, 0.176);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.176);
}
.form-control {
@ -2384,10 +2390,10 @@ pnotify.css
background-image: -o-linear-gradient(#333, #3d3d3d) !important;
filter: progid:dximagetransform.microsoft.gradient(startColorstr=#333, endColorstr=#3d3d3d) !important;
-ms-filter: progid:dximagetransform.microsoft.gradient(startColorstr=#333, endColorstr=#3d3d3d) !important;
-moz-box-shadow: 0px 0px 2px #000;
-webkit-box-shadow: 0px 0px 2px #000;
-o-box-shadow: 0px 0px 2px #000;
box-shadow: 0px 0px 2px #000;
-moz-box-shadow: 0 0 2px #000;
-webkit-box-shadow: 0 0 2px #000;
-o-box-shadow: 0 0 2px #000;
box-shadow: 0 0 2px #000;
}
.ui-pnotify-title {
@ -2425,7 +2431,7 @@ tablesorter.css
.tablesorter th {
color: #fff;
text-shadow: -1px -1px 0 rgba(0,0,0,0.3);
text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.3);
background-color: #15528F;
}
@ -2496,7 +2502,7 @@ thead.tablesorter-stickyHeader {
.tablesorter tfoot tr {
color: #fff;
text-align: center;
text-shadow: -1px -1px 0 rgba(0,0,0,0.3);
text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.3);
background-color: #333;
border-collapse: collapse;
}
@ -2648,10 +2654,19 @@ span.token-input-delete-token {
background-color: rgb(40, 40, 40);
}
/*.episodeview-daybyday .time .time-min,*/
.episodeview-daybyday .time .time-hr-min,
.episodeview-daybyday .time .time-am-pm,
#addRootDirTable td label .filepath,
.grey-text {
color:#999
}
/*.episodeview-daybyday .time .time-hr-min{
display:none
}
.episodeview-daybyday .time .time-min{
font-size: 11px;
}*/
#newShowPortal #displayText .show-name,
#newShowPortal #displayText .show-dest,
@ -2674,8 +2689,8 @@ jquery.confirm.css
top: 0;
left: 0;
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);
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;
}
@ -2687,7 +2702,7 @@ jquery.confirm.css
top: 50%;
margin: -130px 0 0 -230px;
border: 1px solid #111;
box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.175);
box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.175);
}
#confirmBox h1,
@ -2701,13 +2716,13 @@ jquery.confirm.css
color: #fff;
margin: 0;
font-size: 22px;
text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.75);
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.75);
}
#confirmBox p {
padding-top: 20px;
color: #fff;
text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.75);
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.75);
}
#confirmButtons {
@ -2722,7 +2737,7 @@ jquery.confirm.css
display: inline-block;
color: #fff;
text-align:center;
text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.75);
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.75);
background-clip: padding-box;
border: 1px solid #111;
border-radius: 3px;
@ -2730,12 +2745,12 @@ jquery.confirm.css
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
background-image: -webkit-linear-gradient(top, rgba(255,255,255,0.08),rgba(255,255,255,0) 50%,rgba(0,0,0,0) 51%,rgba(0,0,0,0.25));
background-image: -moz-linear-gradient(top, rgba(255,255,255,0.08),rgba(255,255,255,0) 50%,rgba(0,0,0,0) 51%,rgba(0,0,0,0.25));
background-image: -o-linear-gradient(top, rgba(255,255,255,0.08),rgba(255,255,255,0) 50%,rgba(0,0,0,0) 51%,rgba(0,0,0,0.25));
background-image: linear-gradient(to bottom, rgba(255,255,255,0.08),rgba(255,255,255,0) 50%,rgba(0,0,0,0) 51%,rgba(0,0,0,0.25));
-webkit-box-shadow: inset 0 1px rgba(255,255,255,0.1),inset 0 -1px 3px rgba(0,0,0,0.3),inset 0 0 0 1px rgba(255,255,255,0.08),0 1px 2px rgba(0,0,0,0.15);
box-shadow: inset 0 1px rgba(255,255,255,0.1),inset 0 -1px 3px rgba(0,0,0,0.3),inset 0 0 0 1px rgba(255,255,255,0.08),0 1px 2px rgba(0,0,0,0.15);
background-image: -webkit-linear-gradient(top, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 51%,rgba(0, 0, 0, 0.25));
background-image: -moz-linear-gradient(top, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 51%,rgba(0, 0, 0, 0.25));
background-image: -o-linear-gradient(top, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 51%,rgba(0, 0, 0, 0.25));
background-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 51%,rgba(0, 0, 0, 0.25));
-webkit-box-shadow: inset 0 1px rgba(255, 255, 255, 0.1),inset 0 -1px 3px rgba(0, 0, 0, 0.3),inset 0 0 0 1px rgba(255, 255, 255, 0.08),0 1px 2px rgba(0, 0, 0, 0.15);
box-shadow: inset 0 1px rgba(255, 255, 255, 0.1),inset 0 -1px 3px rgba(0, 0, 0, 0.3),inset 0 0 0 1px rgba(255, 255, 255, 0.08),0 1px 2px rgba(0, 0, 0, 0.15);
}
#confirmBox .button:last-child {

View file

@ -149,11 +149,7 @@ inc_top.tmpl
margin-bottom: -15px;
}
#contentWrapper {
/* background: url("../images/bg.png") repeat 0 0 transparent; */
}
[class^="icon-"],
[class^="icon-"],
[class*=" icon-"] {
background-image: url("../images/glyphicons-halflings.png");
}
@ -289,18 +285,18 @@ inc_top.tmpl
}
.ui-tabs {
padding: 0px;
padding: 0;
background: none;
border-width: 0px;
border-width: 0;
}
.ui-tabs .ui-tabs-nav {
padding-left: 0px;
padding-left: 0;
background: transparent;
border-width: 0px 0px 0px 0px;
-moz-border-radius: 0px;
-webkit-border-radius: 0px;
border-radius: 0px;
border-width: 0 0 0 0;
-moz-border-radius: 0;
-webkit-border-radius: 0;
border-radius: 0;
}
.ui-tabs .ui-tabs-panel {
@ -318,15 +314,6 @@ inc_top.tmpl
border-top-right-radius: 5px;
}
#content {
width: 95%;
min-width: 875px;
padding: 15px;
margin-left: auto;
margin-right: auto;
clear: both;
}
#SubMenu {
padding-right: 20px;
clear: both;
@ -340,7 +327,7 @@ inc_top.tmpl
margin-left: auto;
margin-right: auto;
margin-top: 50px;
margin-bottom: 0px;
margin-bottom: 0;
}
[class^="menu-icon-"], [class*=" menu-icon-"] {
@ -366,83 +353,83 @@ inc_top.tmpl
}
.menu-icon-addshow {
background-position: 0px 0px;
background-position: 0 0;
}
.menu-icon-anime {
background-position: -21px 0px;
background-position: -21px 0;
}
.menu-icon-backlog-view {
background-position: -42px 0px;
background-position: -42px 0;
}
.menu-icon-backlog {
background-position: -63px 0px;
background-position: -63px 0;
}
.menu-icon-bittorrent {
background-position: -84px 0px;
background-position: -84px 0;
}
.menu-icon-config-index {
background-position: -105px 0px;
background-position: -105px 0;
}
.menu-icon-config {
background-position: -126px 0px;
background-position: -126px 0;
}
.menu-icon-failed-download {
background-position: -147px 0px;
background-position: -147px 0;
}
.menu-icon-home {
background-position: -168px 0px;
background-position: -168px 0;
}
.menu-icon-manage {
background-position: -189px 0px;
background-position: -189px 0;
}
.menu-icon-manage-searches {
background-position: -210px 0px;
background-position: -210px 0;
}
.menu-icon-poster {
background-position: -231px 0px;
background-position: -231px 0;
}
.menu-icon-postprocess {
background-position: -252px 0px;
background-position: -252px 0;
}
.menu-icon-restart {
background-position: -273px 0px;
background-position: -273px 0;
}
.menu-icon-shutdown {
background-position: -294px 0px;
background-position: -294px 0;
}
.menu-icon-update {
background-position: -315px 0px;
background-position: -315px 0;
}
.menu-icon-viewlog-errors {
background-position: -336px 0px;
background-position: -336px 0;
}
.menu-icon-viewlog {
background-position: -357px 0px;
background-position: -357px 0;
}
.menu-icon-xbmc {
background-position: -378px 0px;
background-position: -378px 0;
}
.menu-icon-help {
background-position: -399px 0px;
background-position: -399px 0;
}
[class^="submenu-icon-"], [class*=" submenu-icon-"] {
@ -452,27 +439,27 @@ inc_top.tmpl
}
.submenu-icon-anime {
background-position: -21px 0px;
background-position: -21px 0;
}
.submenu-icon-bittorrent {
background-position: -84px 0px;
background-position: -84px 0;
}
.submenu-icon-failed-download {
background-position: -147px 0px;
background-position: -147px 0;
}
.submenu-icon-restart {
background-position: -273px 0px;
background-position: -273px 0;
}
.submenu-icon-shutdown {
background-position: -294px 0px;
background-position: -294px 0;
}
.submenu-icon-xbmc {
background-position: -378px 0px;
background-position: -378px 0;
}
/* =======================================================================
@ -609,42 +596,42 @@ home.tmpl
}
.show .ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br {
border-bottom-right-radius: 0px;
border-bottom-right-radius: 0;
}
.show .ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl {
border-bottom-left-radius: 0px;
border-bottom-left-radius: 0;
}
.show .ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr {
border-top-right-radius: 0px;
border-top-right-radius: 0;
}
.show .ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl {
border-top-left-radius: 0px;
border-top-left-radius: 0;
}
.show .ui-widget-content {
border-top: 1px solid #111;
border-bottom: 1px solid #111;
border-left: 0px;
border-right: 0px;
border-left: 0;
border-right: 0;
}
.show .progress-80 {
border-radius: 0px;
border-radius: 0;
}
.show .progress-60 {
border-radius: 0px;
border-radius: 0;
}
.show .progress-40 {
border-radius: 0px;
border-radius: 0;
}
.show .progress-20 {
border-radius: 0px;
border-radius: 0;
}
.show-title {
@ -652,7 +639,7 @@ home.tmpl
overflow: hidden;
white-space: nowrap;
font-size: 11px;
margin: 4px 4px 0px 4px;
margin: 4px 4px 0 4px;
}
.show-title:after {
@ -675,7 +662,7 @@ home.tmpl
overflow: hidden;
white-space: nowrap;
font-size: 11px;
margin: 0px 4px 4px 4px;
margin: 0 4px 4px 4px;
}
.show-date:after {
@ -815,7 +802,7 @@ home_trendingShows.tmpl
white-space: nowrap;
font-size: 12px;
overflow: hidden;
/* text-shadow: 1px 1px 0px #000;*/
/* text-shadow: 1px 1px 0 #000;*/
padding-left: 4px;
margin: 0;
}
@ -917,7 +904,7 @@ ul.tags li {
border: 1px solid #111;
color: #FFF;
font: 14px/18px "Open Sans", "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif;
text-shadow: 0px 1px rgba(0, 0, 0, 0.8);
text-shadow: 0 1px rgba(0, 0, 0, 0.8);
float: left;
}
@ -1074,7 +1061,7 @@ tr.seasonheader {
padding-top: 10px;
text-align: left;
border: none;
color: #fff;
color: #000;
}
th.col-checkbox,
@ -1157,7 +1144,7 @@ td.col-search {
}
/* =======================================================================
comingEpisodes.tmpl
episodeView.tmpl
========================================================================== */
.sort_data {
@ -1219,7 +1206,7 @@ h2.day, h2.network {
letter-spacing: 1px;
color: #FFF;
text-align: center;
text-shadow: -1px -1px 0px rgba(0, 0, 0, 0.3);
text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.3);
background-color: #333;
}
@ -1228,7 +1215,7 @@ h2.day, h2.network {
clear: both;
border: 1px solid #ccc;
margin: auto;
padding: 0px;
padding: 0;
text-align: left;
width: 750px;
border-radius: 5px;
@ -1307,17 +1294,39 @@ h2.day, h2.network {
vertical-align: middle;
}
table.cal-odd {
background-color: #ddd;
.day-of-week .day-number {
background-color: #333;
color: #fff;
}
table.cal-even {
background-color: #d2d2d2;
.today .day-number .number, .today .day-number .month, .today .day-number .day {
color: #c7db40;
}
.calendarShow .text .airtime {
color:#000
.odd .daybyday-show {
background-color: #F5F1E4;
}
.calendarShow .text .episode-title {
color:#888
.even .daybyday-show {
background-color: #DFDACF;
}
.day-of-week .poster img {
border-color: #CCC;
}
.day-of-week .text .airtime,
.day-of-week .text .episode,
.day-of-week .text .episode .name {
color: #000;
}
.day-of-week .text .episode .season,
.day-of-week .text .episode .number {
color: rgb(9, 133, 225);
/*color: #3d3d3d;*/
}
.day-of-week .text .episode {
color: #888;
}
/* =======================================================================
@ -1355,12 +1364,7 @@ config*.tmpl
}
#config div.field-pair {
padding: 12px 0px;
}
#config div.field-pair input {
float: left;
margin-right: 6px;
padding: 12px 0;
}
#config .nocheck, #config div #customQuality, .metadataDiv {
@ -1538,7 +1542,7 @@ td.tableright {
text-align: left;
vertical-align: middle;
width: 225px;
padding: 6px 0px;
padding: 6px 0;
}
.optionWrapper div.selectChoices {
@ -1572,7 +1576,6 @@ Global
========================================================================== */
span.path {
padding: 3px 6px;
color: #8b0000;
background-color: #f5f1e4;
}
@ -1588,13 +1591,13 @@ span.path {
span.quality {
font: 12px/13px "Open Sans", verdana, sans-serif;
background-image:-webkit-linear-gradient(top, rgba(255,255,255,0.08),rgba(255,255,255,0) 50%,rgba(0,0,0,0) 50%,rgba(0,0,0,0.25));
background-image:-moz-linear-gradient(top, rgba(255,255,255,0.08),rgba(255,255,255,0) 50%,rgba(0,0,0,0) 50%,rgba(0,0,0,0.25));
background-image:-o-linear-gradient(top, rgba(255,255,255,0.08),rgba(255,255,255,0) 50%,rgba(0,0,0,0) 50%,rgba(0,0,0,0.25));
background-image:linear-gradient(to bottom, rgba(255,255,255,0.08),rgba(255,255,255,0) 50%,rgba(0,0,0,0) 50%,rgba(0,0,0,0.25));
-webkit-box-shadow:inset 0 1px rgba(255,255,255,0.1),inset 0 -1px 3px rgba(0,0,0,0.3),inset 0 0 0 1px rgba(255,255,255,0.08),0 1px 2px rgba(0,0,0,0.15);
box-shadow:inset 0 1px rgba(255,255,255,0.1),inset 0 -1px 3px rgba(0,0,0,0.3),inset 0 0 0 1px rgba(255,255,255,0.08),0 1px 2px rgba(0,0,0,0.15);
text-shadow: 0px 1px rgba(0, 0, 0, 0.8);
background-image:-webkit-linear-gradient(top, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 50%,rgba(0, 0, 0, 0.25));
background-image:-moz-linear-gradient(top, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 50%,rgba(0, 0, 0, 0.25));
background-image:-o-linear-gradient(top, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 50%,rgba(0, 0, 0, 0.25));
background-image:linear-gradient(to bottom, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 50%,rgba(0, 0, 0, 0.25));
-webkit-box-shadow:inset 0 1px rgba(255, 255, 255, 0.1),inset 0 -1px 3px rgba(0, 0, 0, 0.3),inset 0 0 0 1px rgba(255, 255, 255, 0.08),0 1px 2px rgba(0, 0, 0, 0.15);
box-shadow:inset 0 1px rgba(255, 255, 255, 0.1),inset 0 -1px 3px rgba(0, 0, 0, 0.3),inset 0 0 0 1px rgba(255, 255, 255, 0.08),0 1px 2px rgba(0, 0, 0, 0.15);
text-shadow: 0 1px rgba(0, 0, 0, 0.8);
color: #FFFFFF;
display: inline-block;
padding: 2px 4px;
@ -1685,7 +1688,7 @@ option.flag {
div.blackwhitelist{
float:left;
text-align: center;
text-align: center;
}
div.blackwhitelist input {
@ -1714,7 +1717,7 @@ div.blackwhitelist span {
}
div.blackwhitelist.anidb, div.blackwhitelist.manual {
margin: 7px 0px;
margin: 7px 0;
}
@ -1722,15 +1725,15 @@ div.blackwhitelist.anidb, div.blackwhitelist.manual {
bootstrap Overrides
========================================================================== */
body {
padding-top: 60px;
body {
padding-top: 60px;
overflow-y: scroll;
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
color: #000;
}
input[type="radio"] {
margin: 2px 0px 0px;
margin: 2px 0 0;
line-height: normal;
}
@ -1745,7 +1748,7 @@ input, textarea, select, .uneditable-input {
}
.navbar-brand {
padding: 0px;
padding: 0;
}
/* navbar styling */
@ -1884,7 +1887,7 @@ fieldset[disabled] .navbar-default .btn-link:focus {
.dropdown-menu {
background-color: #F5F1E4;
border: 1px solid rgba(0, 0, 0, 0.15);
box-shadow: 0px 6px 12px rgba(0, 0, 0, 0.176);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.176);
}
.form-control {
@ -2378,7 +2381,7 @@ tablesorter.css
.tablesorter th {
color: #fff;
text-shadow: -1px -1px 0 rgba(0,0,0,0.3);
text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.3);
background-color: #333;
}
@ -2449,7 +2452,7 @@ thead.tablesorter-stickyHeader {
.tablesorter tfoot tr {
color: #fff;
text-align: center;
text-shadow: -1px -1px 0 rgba(0,0,0,0.3);
text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.3);
background-color: #333;
border-collapse: collapse;
}
@ -2596,6 +2599,8 @@ span.token-input-delete-token {
background-color: rgb(245, 245, 245);
}
.episodeview-daybyday .time .time-min,
.episodeview-daybyday .time .time-am-pm,
#addRootDirTable td label .filepath,
.grey-text {
color:#666
@ -2622,8 +2627,8 @@ jquery.confirm.css
top: 0;
left: 0;
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);
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;
}
@ -2635,7 +2640,7 @@ jquery.confirm.css
top: 50%;
margin: -130px 0 0 -230px;
border: 1px solid #111;
box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.175);
box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.175);
}
#confirmBox h1,
@ -2649,13 +2654,13 @@ jquery.confirm.css
color: #fff;
margin: 0;
font-size: 22px;
text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.75);
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.75);
}
#confirmBox p {
padding-top: 20px;
color: #000;
text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.75);
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
}
#confirmButtons {
@ -2670,7 +2675,7 @@ jquery.confirm.css
display: inline-block;
color: #fff;
text-align:center;
text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.75);
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.75);
background-clip: padding-box;
border: 1px solid #111;
border-radius: 3px;
@ -2678,12 +2683,12 @@ jquery.confirm.css
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
background-image: -webkit-linear-gradient(top, rgba(255,255,255,0.08),rgba(255,255,255,0) 50%,rgba(0,0,0,0) 51%,rgba(0,0,0,0.25));
background-image: -moz-linear-gradient(top, rgba(255,255,255,0.08),rgba(255,255,255,0) 50%,rgba(0,0,0,0) 51%,rgba(0,0,0,0.25));
background-image: -o-linear-gradient(top, rgba(255,255,255,0.08),rgba(255,255,255,0) 50%,rgba(0,0,0,0) 51%,rgba(0,0,0,0.25));
background-image: linear-gradient(to bottom, rgba(255,255,255,0.08),rgba(255,255,255,0) 50%,rgba(0,0,0,0) 51%,rgba(0,0,0,0.25));
-webkit-box-shadow: inset 0 1px rgba(255,255,255,0.1),inset 0 -1px 3px rgba(0,0,0,0.3),inset 0 0 0 1px rgba(255,255,255,0.08),0 1px 2px rgba(0,0,0,0.15);
box-shadow: inset 0 1px rgba(255,255,255,0.1),inset 0 -1px 3px rgba(0,0,0,0.3),inset 0 0 0 1px rgba(255,255,255,0.08),0 1px 2px rgba(0,0,0,0.15);
background-image: -webkit-linear-gradient(top, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 51%,rgba(0, 0, 0, 0.25));
background-image: -moz-linear-gradient(top, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 51%,rgba(0, 0, 0, 0.25));
background-image: -o-linear-gradient(top, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 51%,rgba(0, 0, 0, 0.25));
background-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 51%,rgba(0, 0, 0, 0.25));
-webkit-box-shadow: inset 0 1px rgba(255, 255, 255, 0.1),inset 0 -1px 3px rgba(0, 0, 0, 0.3),inset 0 0 0 1px rgba(255, 255, 255, 0.08),0 1px 2px rgba(0, 0, 0, 0.15);
box-shadow: inset 0 1px rgba(255, 255, 255, 0.1),inset 0 -1px 3px rgba(0, 0, 0, 0.3),inset 0 0 0 1px rgba(255, 255, 255, 0.08),0 1px 2px rgba(0, 0, 0, 0.15);
}
#confirmBox .button:last-child {

View file

@ -149,11 +149,14 @@ inc_top.tmpl
margin-bottom: -15px;
}
#contentWrapper {
/* background: url("../images/bg.png") repeat 0 0 transparent; */
#contentWrapper .episodeview-banner,
#contentWrapper .episodeview-daybyday,
#contentWrapper .episodeview-list,
#contentWrapper .episodeview-poster{
padding-top:45px;
}
[class^="icon-"],
[class^="icon-"],
[class*=" icon-"] {
background-image: url("../images/glyphicons-halflings.png");
}
@ -285,18 +288,18 @@ inc_top.tmpl
}
.ui-tabs {
padding: 0px;
padding: 0;
background: none;
border-width: 0px;
border-width: 0;
}
.ui-tabs .ui-tabs-nav {
padding-left: 0px;
padding-left: 0;
background: transparent;
border-width: 0px 0px 0px 0px;
-moz-border-radius: 0px;
-webkit-border-radius: 0px;
border-radius: 0px;
border-width: 0 0 0 0;
-moz-border-radius: 0;
-webkit-border-radius: 0;
border-radius: 0;
}
.ui-tabs .ui-tabs-panel {
@ -336,7 +339,7 @@ inc_top.tmpl
margin-left: auto;
margin-right: auto;
margin-top: 50px;
margin-bottom: 0px;
margin-bottom: 0;
}
[class^="menu-icon-"], [class*=" menu-icon-"] {
@ -362,83 +365,83 @@ inc_top.tmpl
}
.menu-icon-addshow {
background-position: 0px 0px;
background-position: 0 0;
}
.menu-icon-anime {
background-position: -21px 0px;
background-position: -21px 0;
}
.menu-icon-backlog-view {
background-position: -42px 0px;
background-position: -42px 0;
}
.menu-icon-backlog {
background-position: -63px 0px;
background-position: -63px 0;
}
.menu-icon-bittorrent {
background-position: -84px 0px;
background-position: -84px 0;
}
.menu-icon-config-index {
background-position: -105px 0px;
background-position: -105px 0;
}
.menu-icon-config {
background-position: -126px 0px;
background-position: -126px 0;
}
.menu-icon-failed-download {
background-position: -147px 0px;
background-position: -147px 0;
}
.menu-icon-home {
background-position: -168px 0px;
background-position: -168px 0;
}
.menu-icon-manage {
background-position: -189px 0px;
background-position: -189px 0;
}
.menu-icon-manage-searches {
background-position: -210px 0px;
background-position: -210px 0;
}
.menu-icon-poster {
background-position: -231px 0px;
background-position: -231px 0;
}
.menu-icon-postprocess {
background-position: -252px 0px;
background-position: -252px 0;
}
.menu-icon-restart {
background-position: -273px 0px;
background-position: -273px 0;
}
.menu-icon-shutdown {
background-position: -294px 0px;
background-position: -294px 0;
}
.menu-icon-update {
background-position: -315px 0px;
background-position: -315px 0;
}
.menu-icon-viewlog-errors {
background-position: -336px 0px;
background-position: -336px 0;
}
.menu-icon-viewlog {
background-position: -357px 0px;
background-position: -357px 0;
}
.menu-icon-xbmc {
background-position: -378px 0px;
background-position: -378px 0;
}
.menu-icon-help {
background-position: -399px 0px;
background-position: -399px 0;
}
[class^="submenu-icon-"], [class*=" submenu-icon-"] {
@ -448,27 +451,27 @@ inc_top.tmpl
}
.submenu-icon-anime {
background-position: -21px 0px;
background-position: -21px 0;
}
.submenu-icon-bittorrent {
background-position: -84px 0px;
background-position: -84px 0;
}
.submenu-icon-failed-download {
background-position: -147px 0px;
background-position: -147px 0;
}
.submenu-icon-restart {
background-position: -273px 0px;
background-position: -273px 0;
}
.submenu-icon-shutdown {
background-position: -294px 0px;
background-position: -294px 0;
}
.submenu-icon-xbmc {
background-position: -378px 0px;
background-position: -378px 0;
}
/* =======================================================================
@ -626,26 +629,26 @@ home.tmpl
}
.show .ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br {
border-bottom-right-radius: 0px;
border-bottom-right-radius: 0;
}
.show .ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl {
border-bottom-left-radius: 0px;
border-bottom-left-radius: 0;
}
.show .ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr {
border-top-right-radius: 0px;
border-top-right-radius: 0;
}
.show .ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl {
border-top-left-radius: 0px;
border-top-left-radius: 0;
}
.show .ui-widget-content {
border-top: 1px solid #111;
border-bottom: 1px solid #111;
border-left: 0px;
border-right: 0px;
border-left: 0;
border-right: 0;
}
.ui-progressbar .ui-progressbar-value {
height:20px
@ -661,19 +664,19 @@ home.tmpl
}
.show .progress-80 {
border-radius: 0px;
border-radius: 0;
}
.show .progress-60 {
border-radius: 0px;
border-radius: 0;
}
.show .progress-40 {
border-radius: 0px;
border-radius: 0;
}
.show .progress-20 {
border-radius: 0px;
border-radius: 0;
}
.show-title {
@ -681,7 +684,7 @@ home.tmpl
overflow: hidden;
white-space: nowrap;
font-size: 11px;
margin: 4px 4px 0px 4px;
margin: 4px 4px 0 4px;
}
.show-title:after {
@ -704,7 +707,7 @@ home.tmpl
overflow: hidden;
white-space: nowrap;
font-size: 11px;
margin: 0px 4px 4px 4px;
margin: 0 4px 4px 4px;
}
.show-date:after {
@ -791,34 +794,27 @@ home_addShows.tmpl
========================================================================== */
#addShowPortal {
width: 750px;
width: 748px;
padding: 10px 0;
margin-right: auto;
margin-left: auto;
}
#addShowPortal a {
/* padding: 10px;*/
padding: 0px 20px;
padding: 0 20px;
width: 360px;
float: left;
margin: 0 15px 15px 0;
margin: 0 7px 14px;
}
div.button {
display: table-cell;
vertical-align: middle;
/* padding-left: 10px;*/
}
div.buttontext {
display: table-cell;
/*
padding-left: 20px;
padding: 10px 15px;
*/
padding: 10px 0px 10px 15px;
padding: 10px 0 10px 15px;
text-align: left;
white-space: normal;
}
@ -826,7 +822,7 @@ div.buttontext p {
margin: 0
}
div.buttontext h3 {
margin-top: 0px;
margin-top: 0;
}
div.buttontext p {
@ -970,7 +966,6 @@ home_trendingShows.tmpl
white-space: nowrap;
font-size: 12px;
overflow: hidden;
/* text-shadow: 1px 1px 0px #000;*/
padding-left: 4px;
margin: 0;
}
@ -1104,7 +1099,7 @@ ul.tags li {
border: 1px solid #111;
color: #FFF;
font: 14px/18px "Open Sans", "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif;
text-shadow: 0px 1px rgba(0, 0, 0, 0.8);
text-shadow: 0 1px rgba(0, 0, 0, 0.8);
float: left;
}
@ -1347,7 +1342,7 @@ td.col-search {
}
/* =======================================================================
comingEpisodes.tmpl
episodeView.tmpl
========================================================================== */
.sort_data {
@ -1409,7 +1404,7 @@ h2.day, h2.network {
letter-spacing: 1px;
color: #FFF;
text-align: center;
text-shadow: -1px -1px 0px rgba(0, 0, 0, 0.3);
text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.3);
background-color: #333;
}
@ -1418,7 +1413,7 @@ h2.day, h2.network {
clear: both;
border: 1px solid #ccc;
margin: auto;
padding: 0px;
padding: 0;
text-align: left;
width: 750px;
border-radius: 5px;
@ -1497,46 +1492,87 @@ h2.day, h2.network {
vertical-align: middle;
}
.calendarWrapper {
width:1000px;
margin:0 auto;
padding:0 3px
.daybydayWrapper {
max-width: 1400px;
margin: 0 auto;
padding: 0 /*3px*/
}
.calendarTable {
float:left;
width:142px !important;
white-space:nowrap;
table-layout:fixed !important;
.day-of-week {
float: left;
width: 14.28%;
padding: 0 2px;
}
.calendarShow {
padding:0 !important
.day-of-week .day-number {
position: relative;
height: 40px;
}
.calendarShow .poster {
padding-bottom:2px
}
.calendarShow .poster img {
width:142px;
height:auto
.day-of-week .day-number .number {
position: absolute;
right: 5px;
font-weight: bold;
font-size: 32px;
line-height: 38px;
}
.calendarShow .text {
padding:0 5px 10px 5px
.day-of-week .day-number .day {
position: absolute;
left: 5px;
top: 3px;
text-transform: uppercase;
font-weight: bold;
}
.calendarShow .text .airtime,
.calendarShow .text .episode-title {
overflow:hidden;
text-overflow:ellipsis;
display:block;
font-size:11px
.day-of-week .day-number .month {
position: absolute;
left: 5px;
bottom: 3px;
text-transform: uppercase;
}
.calendarShow .show-status {
padding:5px 10px 10px;
text-align:center
.daybyday-show {
margin-top: 4px;
padding: 4px;
width: 100%
}
.day-of-week .poster img {
border: 1px solid;
border-radius: 5px;
margin-bottom: 2px;
display: block;
margin-left: auto;
margin-right: auto;
}
.day-of-week .text .airtime,
.day-of-week .text .episode {
overflow: hidden;
text-overflow: ellipsis;
display: block;
font-size: 12px;
}
.day-of-week .text .episode .season,
.day-of-week .text .episode .number {
font-weight: 900;
}
.day-of-week .text .episode .season {
margin-right: 2px;
}
.day-of-week .text .episode .number {
margin-left: 2px;
}
.day-of-week .episode-blank {
width: 250px;
height: 32px;
text-align: center;
font-style: italic;
display: table-cell;
vertical-align: middle;
font-size: 12px;
}
/* =======================================================================
@ -1591,10 +1627,13 @@ config*.tmpl
padding: 12px 0
}
.stepDiv .component-desc select,
.stepDiv .component-desc input,
#config div.field-pair select,
#config div.field-pair input {
margin-right: 6px;
}
.stepDiv .component-desc select,
.stepDiv .component-desc input {
margin-right: 15px;
}
@ -1870,7 +1909,7 @@ div.metadata_example label {
line-height: 21px;
display: block;
padding: 3px;
margin: 0px;
margin: 0;
}
div.metadata_options input {
margin-right: 3px;
@ -1883,7 +1922,7 @@ div.metadataDiv .disabled {
.notifier-icon {
float: left;
margin: 6px 4px 0px 0px;
margin: 6px 4px 0 0;
}
/* =======================================================================
@ -1915,7 +1954,7 @@ td.tableright {
text-align: left;
vertical-align: middle;
width: 225px;
padding: 6px 0px;
padding: 6px 0;
}
.optionWrapper div.selectChoices {
@ -1949,9 +1988,8 @@ Global
========================================================================== */
span.path {
padding: 3px 6px;
color: #8b0000;
background-color: #f5f1e4;
padding: 3px;
margin-left: 3px;
}
.align-left {
@ -1965,13 +2003,13 @@ span.path {
span.quality {
font: 12px/13px "Open Sans", verdana, sans-serif;
background-image:-webkit-linear-gradient(top, rgba(255,255,255,0.08),rgba(255,255,255,0) 50%,rgba(0,0,0,0) 50%,rgba(0,0,0,0.25));
background-image:-moz-linear-gradient(top, rgba(255,255,255,0.08),rgba(255,255,255,0) 50%,rgba(0,0,0,0) 50%,rgba(0,0,0,0.25));
background-image:-o-linear-gradient(top, rgba(255,255,255,0.08),rgba(255,255,255,0) 50%,rgba(0,0,0,0) 50%,rgba(0,0,0,0.25));
background-image:linear-gradient(to bottom, rgba(255,255,255,0.08),rgba(255,255,255,0) 50%,rgba(0,0,0,0) 50%,rgba(0,0,0,0.25));
-webkit-box-shadow:inset 0 1px rgba(255,255,255,0.1),inset 0 -1px 3px rgba(0,0,0,0.3),inset 0 0 0 1px rgba(255,255,255,0.08),0 1px 2px rgba(0,0,0,0.15);
box-shadow:inset 0 1px rgba(255,255,255,0.1),inset 0 -1px 3px rgba(0,0,0,0.3),inset 0 0 0 1px rgba(255,255,255,0.08),0 1px 2px rgba(0,0,0,0.15);
text-shadow: 0px 1px rgba(0, 0, 0, 0.8);
background-image:-webkit-linear-gradient(top, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 50%,rgba(0, 0, 0, 0.25));
background-image:-moz-linear-gradient(top, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 50%,rgba(0, 0, 0, 0.25));
background-image:-o-linear-gradient(top, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 50%,rgba(0, 0, 0, 0.25));
background-image:linear-gradient(to bottom, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 50%,rgba(0, 0, 0, 0.25));
-webkit-box-shadow:inset 0 1px rgba(255, 255, 255, 0.1),inset 0 -1px 3px rgba(0, 0, 0, 0.3),inset 0 0 0 1px rgba(255, 255, 255, 0.08),0 1px 2px rgba(0, 0, 0, 0.15);
box-shadow:inset 0 1px rgba(255, 255, 255, 0.1),inset 0 -1px 3px rgba(0, 0, 0, 0.3),inset 0 0 0 1px rgba(255, 255, 255, 0.08),0 1px 2px rgba(0, 0, 0, 0.15);
text-shadow: 0 1px rgba(0, 0, 0, 0.8);
color: #FFFFFF;
display: inline-block;
padding: 2px 4px;
@ -2091,7 +2129,7 @@ div.blackwhitelist span {
}
div.blackwhitelist.anidb, div.blackwhitelist.manual {
margin: 7px 0px;
margin: 7px 0;
}
@ -2111,12 +2149,12 @@ html * {
}
input[type="checkbox"] {
margin: 2px 0px 0px;
margin: 2px 0 0;
line-height: normal;
}
input[type="radio"] {
margin: 2px 0px 0px;
margin: 2px 0 0;
line-height: normal;
}
@ -2131,7 +2169,7 @@ input, textarea, select, .uneditable-input {
}
.navbar-brand {
padding: 0px;
padding: 0;
}
/* navbar styling */
@ -2258,7 +2296,7 @@ fieldset[disabled] .navbar-default .btn-link:focus {
.dropdown-menu {
background-color: #F5F1E4;
border: 1px solid rgba(0, 0, 0, 0.15);
box-shadow: 0px 6px 12px rgba(0, 0, 0, 0.176);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.176);
}
.form-control {
@ -2815,7 +2853,7 @@ div.formpaginate .prev, div.formpaginate .next {
.stepDiv.parent-folder {
padding: 15px 0 0;
width: 430px;
margin: 0px auto;
margin: 0 auto;
}
.stepDiv .nocheck {
@ -2937,7 +2975,7 @@ thead.tablesorter-stickyHeader {
.tablesorter tfoot tr {
color: #fff;
text-align: center;
text-shadow: -1px -1px 0 rgba(0,0,0,0.3);
text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.3);
background-color: #333;
border-collapse: collapse;
}
@ -3153,6 +3191,15 @@ span.token-input-delete-token {
z-index: 0;
background-image: url(../images/poster-dark.jpg)
}
.time-am-pm {
margin-left: 2px;
}
#content.episodeview-banner .time-am-pm,
#content.episodeview-poster .time-am-pm {
margin-left: 0;
}
/* =======================================================================
jquery.confirm.css
========================================================================== */
@ -3164,8 +3211,8 @@ jquery.confirm.css
top: 0;
left: 0;
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);
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;
}
@ -3177,7 +3224,7 @@ jquery.confirm.css
top: 50%;
margin: -130px 0 0 -230px;
border: 1px solid #111;
box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.175);
box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.175);
}
#confirmBox h1,
@ -3212,7 +3259,7 @@ jquery.confirm.css
display: inline-block;
color: #fff;
text-align:center;
text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.75);
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.75);
background-clip: padding-box;
border: 1px solid #111;
border-radius: 3px;
@ -3220,12 +3267,12 @@ jquery.confirm.css
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
background-image: -webkit-linear-gradient(top, rgba(255,255,255,0.08),rgba(255,255,255,0) 50%,rgba(0,0,0,0) 51%,rgba(0,0,0,0.25));
background-image: -moz-linear-gradient(top, rgba(255,255,255,0.08),rgba(255,255,255,0) 50%,rgba(0,0,0,0) 51%,rgba(0,0,0,0.25));
background-image: -o-linear-gradient(top, rgba(255,255,255,0.08),rgba(255,255,255,0) 50%,rgba(0,0,0,0) 51%,rgba(0,0,0,0.25));
background-image: linear-gradient(to bottom, rgba(255,255,255,0.08),rgba(255,255,255,0) 50%,rgba(0,0,0,0) 51%,rgba(0,0,0,0.25));
-webkit-box-shadow: inset 0 1px rgba(255,255,255,0.1),inset 0 -1px 3px rgba(0,0,0,0.3),inset 0 0 0 1px rgba(255,255,255,0.08),0 1px 2px rgba(0,0,0,0.15);
box-shadow: inset 0 1px rgba(255,255,255,0.1),inset 0 -1px 3px rgba(0,0,0,0.3),inset 0 0 0 1px rgba(255,255,255,0.08),0 1px 2px rgba(0,0,0,0.15);
background-image: -webkit-linear-gradient(top, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 51%,rgba(0, 0, 0, 0.25));
background-image: -moz-linear-gradient(top, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 51%,rgba(0, 0, 0, 0.25));
background-image: -o-linear-gradient(top, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 51%,rgba(0, 0, 0, 0.25));
background-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 51%,rgba(0, 0, 0, 0.25));
-webkit-box-shadow: inset 0 1px rgba(255, 255, 255, 0.1),inset 0 -1px 3px rgba(0, 0, 0, 0.3),inset 0 0 0 1px rgba(255, 255, 255, 0.08),0 1px 2px rgba(0, 0, 0, 0.15);
box-shadow: inset 0 1px rgba(255, 255, 255, 0.1),inset 0 -1px 3px rgba(0, 0, 0, 0.3),inset 0 0 0 1px rgba(255, 255, 255, 0.08),0 1px 2px rgba(0, 0, 0, 0.15);
}
#confirmBox .button:last-child {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View file

@ -90,8 +90,8 @@ addOption("postprocess", "process_method", "&process_method=copy");
addOption("postprocess", "type", "&type=manual")
addOption("sb.setdefaults", "Optional Param", "", 1);
addList("sb.setdefaults", "Exclude Paused Shows on ComingEps", "&future_show_paused=0", "sb.setdefaults-status");
addList("sb.setdefaults", "Include Paused Shows on ComingEps", "&future_show_paused=1", "sb.setdefaults-status");
addList("sb.setdefaults", "Exclude Paused Shows on EpisodeView", "&future_show_paused=0", "sb.setdefaults-status");
addList("sb.setdefaults", "Include Paused Shows on EpisodeView", "&future_show_paused=1", "sb.setdefaults-status");
addOption("sb.setdefaults-status", "Optional Param", "", 1);
addList("sb.setdefaults-status", "Wanted", "&status=wanted", "sb.setdefaults-opt");

View file

@ -1,549 +0,0 @@
#import sickbeard
#import datetime
#from sickbeard.common import *
#from sickbeard import sbdatetime
#from sickbeard.helpers import anon_url
#set global $title = 'Coming Episodes'
#set global $header = 'Coming Episodes'
#set global $sbPath = '..'
#set global $topmenu = 'comingEpisodes'
#import os.path
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
#set $sort = $sickbeard.COMING_EPS_SORT
<script type="text/javascript" src="$sbRoot/js/ajaxEpSearch.js?$sbPID"></script>
#if $varExists('header')
<h1 class="header">$header</h1>
#else
<h1 class="title">$title</h1>
#end if
<style type="text/css">
#SubMenu {display:none}
#contentWrapper {padding-top:30px}
</style>
<div class="h2footer pull-right">
<span>Layout:
<select name="layout" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;">
<option value="$sbRoot/setComingEpsLayout/?layout=poster" #if 'poster' == $sickbeard.COMING_EPS_LAYOUT then 'selected="selected"' else ''#>Poster</option>
<option value="$sbRoot/setComingEpsLayout/?layout=calendar" #if 'calendar' == $sickbeard.COMING_EPS_LAYOUT then 'selected="selected"' else ''#>Calendar</option>
<option value="$sbRoot/setComingEpsLayout/?layout=banner" #if 'banner' == $sickbeard.COMING_EPS_LAYOUT then 'selected="selected"' else ''#>Banner</option>
<option value="$sbRoot/setComingEpsLayout/?layout=list" #if 'list' == $sickbeard.COMING_EPS_LAYOUT then 'selected="selected"' else ''#>List</option>
</select>
</span>
&nbsp;
<span>Sort By:
<select name="sort" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;">
<option value="$sbRoot/setComingEpsSort/?sort=date" #if 'date' == $sickbeard.COMING_EPS_SORT then 'selected="selected"' else ''#>Date</option>
<option value="$sbRoot/setComingEpsSort/?sort=network" #if 'network' == $sickbeard.COMING_EPS_SORT then 'selected="selected"' else ''#>Network</option>
<option value="$sbRoot/setComingEpsSort/?sort=show" #if 'show' == $sickbeard.COMING_EPS_SORT then 'selected="selected"' else ''#>Show</option>
</select>
</span>
&nbsp;
<span>View Paused:
<select name="viewpaused" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;">
<option value="$sbRoot/toggleComingEpsDisplayPaused"<%= (' selected="selected"', '')[True == sickbeard.COMING_EPS_DISPLAY_PAUSED] %>>Hidden</option>
<option value="$sbRoot/toggleComingEpsDisplayPaused"<%= ('', ' selected="selected"')[True == sickbeard.COMING_EPS_DISPLAY_PAUSED] %>>Shown</option>
</select>
</span>
</div>
<div class="key pull-right">
#if 'calendar' != $layout:
<b>Key:</b>
<span class="listing-key listing-overdue">Missed</span>
<span class="listing-key listing-current">Current</span>
<span class="listing-key listing-default">Future</span>
<span class="listing-key listing-toofar">Distant</span>
#end if
<a class="btn btn-inline forceBacklog" href="webcal://$sbHost:$sbHttpPort/calendar">
<i class="icon-calendar icon-white"></i>Subscribe</a>
</div>
<br>
#if 'list' == $layout:
<!-- start list view //-->
<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?$sbPID"></script>
<script type="text/javascript" charset="utf-8">
<!--
\$.tablesorter.addParser({
id: 'loadingNames',
is: function(s) {
return false
},
format: function(s) {
if (0 == s.indexOf('Loading...'))
return s.replace('Loading...', '000')
#if not $sickbeard.SORT_ARTICLE:
return (s || '').replace(/^(?:(?:A(?!\s+to)n?)|The)\s(\w)/i, '$1')
#else:
return (s || '')
#end if
},
type: 'text'
});
\$.tablesorter.addParser({
id: 'quality',
is: function(s) {
return false
},
format: function(s) {
return s.replace('hd1080p', 5).replace('hd720p', 4).replace('hd', 3).replace('sd', 2).replace('any', 1).replace('best', 0).replace('custom', 7)
},
type: 'numeric'
});
\$.tablesorter.addParser({
id: 'cDate',
is: function(s) {
return false
},
format: function(s) {
return s
},
type: 'numeric'
});
\$(document).ready(function(){
#set $sort_codes = {'date': 0, 'show': 1, 'network': 4}
#if $sort not in $sort_codes:
$sort = 'date'
#end if
sortList = [[$sort_codes[$sort], 0]];
\$('#showListTable:has(tbody tr)').tablesorter({
widgets: ['stickyHeaders'],
sortList: sortList,
textExtraction: {
0: function(node) { return \$(node).find('span').text().toLowerCase() },
4: function(node) { return \$(node).find('img').attr('alt') },
5: function(node) { return \$(node).find('span').text().toLowerCase() }
},
headers: {
0: { sorter: 'cDate' },
1: { sorter: 'loadingNames' },
2: { sorter: false },
3: { sorter: false },
4: { sorter: 'loadingNames' },
5: { sorter: 'quality' },
6: { sorter: false },
7: { sorter: false },
8: { sorter: false }
}
});
\$('#sbRoot').ajaxEpSearch();
#set $fuzzydate = 'airdate'
#if $sickbeard.FUZZY_DATING:
fuzzyMoment({
containerClass : '.${fuzzydate}',
dateHasTime : true,
dateFormat : '${sickbeard.DATE_PRESET}',
timeFormat : '${sickbeard.TIME_PRESET}',
trimZero : #if $sickbeard.TRIM_ZERO then 'true' else 'false'#
});
#end if
});
//-->
</script>
#set $show_div = 'listing-default'
<input type="hidden" id="sbRoot" value="$sbRoot" />
<table id="showListTable" class="sickbeardTable tablesorter seasonstyle" cellspacing="1" border="0" cellpadding="0">
<thead>
<tr>
<th>Airdate</th>
<th>Show</th>
<th class="nowrap">Next Ep</th>
<th>Next Ep Name</th>
<th>Network</th>
<th>Quality</th>
<th>Indexers</th>
<th>Search</th>
</tr>
</thead>
<tbody style="text-shadow:none;">
#for $cur_result in $sql_results:
#set $cur_indexer = int($cur_result['indexer'])
#set $runtime = $cur_result['runtime']
#if int($cur_result['paused']) and not $sickbeard.COMING_EPS_DISPLAY_PAUSED:
#continue
#end if
#set $cur_ep_airdate = $cur_result['localtime'].date()
#if $runtime:
#set $cur_ep_enddate = $cur_result['localtime'] + datetime.timedelta(minutes = $runtime)
#if $cur_ep_enddate < $today:
#set $show_div = 'listing-overdue'
#elif $cur_ep_airdate >= $next_week.date():
#set $show_div = 'listing-toofar'
#elif $cur_ep_airdate >= $today.date() and $cur_ep_airdate < $next_week.date():
#if $cur_ep_airdate == $today.date():
#set $show_div = 'listing-current'
#else:
#set $show_div = 'listing-default'
#end if
#end if
#end if
<!-- start $cur_result['show_name'] //-->
<tr class="$show_div">
## forced to use a div to wrap airdate, the column sort went crazy with a span
<td align="center" class="nowrap">
<div class="${fuzzydate}">$sbdatetime.sbdatetime.sbfdatetime($cur_result['localtime']).decode($sickbeard.SYS_ENCODING)</div><span class="sort_data">$time.mktime($cur_result['localtime'].timetuple())</span>
</td>
<td class="tvShow"><a href="$sbRoot/home/displayShow?show=${cur_result['showid']}">$cur_result['show_name']</a>
#if int($cur_result['paused']):
<span class="pause">[paused]</span>
#end if
</td>
<td class="nowrap" align="center">
<%= 'S%02iE%02i' % (int(cur_result['season']), int(cur_result['episode'])) %>
</td>
<td>
#if $cur_result['description']:
<img alt='' src='$sbRoot/images/info32.png' height='16' width='16' class='plotInfo' id="plot_info_<%= '%s_%s_%s' % (str(cur_result['showid']), str(cur_result['season']), str(cur_result['episode'])) %>" />
#else:
<img alt="" src="$sbRoot/images/info32.png" width="16" height="16" class="plotInfoNone" />
#end if
$cur_result['name']
</td>
<td align="center">
$cur_result['network']
</td>
<td align="center">
#if int($cur_result['quality']) in $qualityPresets:
<span class="quality $qualityPresetStrings[int($cur_result['quality'])]">$qualityPresetStrings[int($cur_result['quality'])]</span>
#else:
<span class="quality Custom">Custom</span>
#end if
</td>
<td align="center" style="vertical-align: middle;">
#if $cur_result['imdb_id']:
<a href="<%= anon_url('http://www.imdb.com/title/', cur_result['imdb_id']) %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false" title="http://www.imdb.com/title/${cur_result['imdb_id']}"><img alt="[imdb]" height="16" width="16" src="$sbRoot/images/imdb.png" />
#end if
<a href="<%= anon_url(sickbeard.indexerApi(cur_indexer).config['show_url'], cur_result['showid']) %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false" title="$sickbeard.indexerApi($cur_indexer).config['show_url']${cur_result['showid']}"><img alt="$sickbeard.indexerApi($cur_indexer).name" height="16" width="16" src="$sbRoot/images/$sickbeard.indexerApi($cur_indexer).config['icon']" /></a>
</td>
<td align="center">
<a href="$sbRoot/home/searchEpisode?show=${cur_result['showid']}&amp;season=$cur_result['season']&amp;episode=$cur_result['episode']" title="Manual Search" id="forceUpdate-${cur_result['showid']}" class="forceUpdate epSearch"><img alt="[search]" height="16" width="16" src="$sbRoot/images/search16.png" id="forceUpdateImage-${cur_result['showid']}" /></a>
</td>
</tr>
<!-- end $cur_result['show_name'] //-->
#end for
</tbody>
<tfoot>
<tr>
<th rowspan="1" colspan="10" align="center">&nbsp</th>
</tr>
</tfoot>
</table>
<!-- end list view //-->
#else if $layout in ['banner', 'poster']:
<!-- start non list view //-->
<script type="text/javascript" charset="utf-8">
<!--
\$(document).ready(function(){
\$('#sbRoot').ajaxEpSearch({'size': 16, 'loadingImage': 'loading16' + themeSpinner + '.gif'});
\$('.ep_summary').hide();
\$('.ep_summaryTrigger').click(function() {
\$(this).next('.ep_summary').slideToggle('normal', function() {
\$(this).prev('.ep_summaryTrigger').attr('src', function(i, src) {
return \$(this).next('.ep_summary').is(':visible') ? src.replace('plus','minus') : src.replace('minus','plus')
});
});
});
#set $fuzzydate = 'airdate'
#if $sickbeard.FUZZY_DATING:
fuzzyMoment({
dtInline : true,
dtGlue : ' at ',
containerClass : '.${fuzzydate}',
dateHasTime : true,
dateFormat : '${sickbeard.DATE_PRESET}',
timeFormat : '${sickbeard.TIME_PRESET}',
trimZero : #if $sickbeard.TRIM_ZERO then 'true' else 'false'#
});
#end if
});
//-->
</script>
#set $cur_segment = None
#set $too_late_header = False
#set $missed_header = False
#set $today_header = False
#set $show_div = 'ep_listing listing-default'
#if 'show' == $sort:
<br /><br />
#end if
#for $cur_result in $sql_results:
#set $cur_indexer = int($cur_result['indexer'])
<!-- start $cur_result['show_name'] //-->
#if int($cur_result['paused']) and not $sickbeard.COMING_EPS_DISPLAY_PAUSED:
#continue
#end if
#set $runtime = $cur_result['runtime']
#if 'network' == $sort:
#set $show_network = $cur_result['network'] if $cur_result['network'] else 'no network'
#if $cur_segment != $show_network:
<div class="comingepheader">
<br><h2 class="network">$show_network</h2>
#set $cur_segment = $cur_result['network']
#end if
#set $cur_ep_airdate = $cur_result['localtime'].date()
#if $runtime:
#set $cur_ep_enddate = $cur_result['localtime'] + datetime.timedelta(minutes = $runtime)
#if $cur_ep_enddate < $today:
#set $show_div = 'ep_listing listing-overdue'
#elif $cur_ep_airdate >= $next_week.date():
#set $show_div = 'ep_listing listing-toofar'
#elif $cur_ep_enddate >= $today and $cur_ep_airdate < $next_week.date():
#if $cur_ep_airdate == $today.date():
#set $show_div = 'ep_listing listing-current'
#else:
#set $show_div = 'ep_listing listing-default'
#end if
#end if
#end if
#elif 'date' == $sort:
#set $cur_ep_airdate = $cur_result['localtime'].date()
#if $cur_segment != $cur_ep_airdate:
#if $runtime:
#set $cur_ep_enddate = $cur_result['localtime'] + datetime.timedelta(minutes = $runtime)
#if $cur_ep_enddate < $today and $cur_ep_airdate != $today.date() and not $missed_header:
<br /><h2 class="day">Missed</h2>
#set $missed_header = True
#elif $cur_ep_airdate >= $next_week.date() and not $too_late_header:
<br /><h2 class="day">Later</h2>
#set $too_late_header = True
#elif $cur_ep_enddate >= $today and $cur_ep_airdate < $next_week.date():
#if $cur_ep_airdate == $today.date():
<br /><h2 class="day">$sbdatetime.sbdatetime.sbfdate($cur_ep_airdate, '%A').decode($sickbeard.SYS_ENCODING).capitalize() <span style="font-size: 14px; vertical-align: top;">[Today]</span></h2>
#set $today_header = True
#else:
<br /><h2 class="day">$sbdatetime.sbdatetime.sbfdate($cur_ep_airdate, '%A').decode($sickbeard.SYS_ENCODING).capitalize()</h2>
#end if
#end if
#end if
#set $cur_segment = $cur_ep_airdate
#end if
#if $cur_ep_airdate == $today.date() and not $today_header:
<div class="comingepheader">
<br /><h2 class="day">$sbdatetime.sbdatetime.sbfdate($cur_ep_airdate, '%A').decode($sickbeard.SYS_ENCODING).capitalize() <span style="font-size: 14px; vertical-align: top;">[Today]</span></h2>
#set $today_header = True
#end if
#if $runtime:
#set $cur_ep_enddate = $cur_result['localtime'] + datetime.timedelta(minutes = $runtime)
#if $cur_ep_enddate < $today:
#set $show_div = 'ep_listing listing-overdue'
#elif $cur_ep_airdate >= $next_week.date():
#set $show_div = 'ep_listing listing-toofar'
#elif $cur_ep_enddate >= $today and $cur_ep_airdate < $next_week.date():
#if $cur_ep_airdate == $today.date():
#set $show_div = 'ep_listing listing-current'
#else:
#set $show_div = 'ep_listing listing-default'
#end if
#end if
#end if
#elif 'show' == $sort:
#set $cur_ep_airdate = $cur_result['localtime'].date()
#if $runtime:
#set $cur_ep_enddate = $cur_result['localtime'] + datetime.timedelta(minutes = $runtime)
#if $cur_ep_enddate < $today:
#set $show_div = 'ep_listing listing-overdue listingradius'
#elif $cur_ep_airdate >= $next_week.date():
#set $show_div = 'ep_listing listing-toofar listingradius'
#elif $cur_ep_enddate >= $today and $cur_ep_airdate < $next_week.date():
#if $cur_ep_airdate == $today.date():
#set $show_div = 'ep_listing listing-current listingradius'
#else:
#set $show_div = 'ep_listing listing-default listingradius'
#end if
#end if
#end if
#end if
<div class="$show_div" id="listing-${cur_result['showid']}">
<div class="tvshowDiv">
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<th #if 'banner' == $layout then 'class="nobg"' else 'rowspan="2"'# valign="top">
<a href="$sbRoot/home/displayShow?show=${cur_result['showid']}"><img alt="" class="#if 'banner' == $layout then 'bannerThumb' else 'posterThumb'#" src="$sbRoot/showPoster/?show=${cur_result['showid']}&amp;which=#if 'poster' == $layout then 'poster_thumb' else $layout#" /></a>
</th>
#if 'banner' == $layout:
</tr>
<tr>
#end if
<td class="next_episode">
<div class="clearfix">
<span class="tvshowTitle">
<a href="$sbRoot/home/displayShow?show=${cur_result['showid']}">$cur_result['show_name']
#if int($cur_result['paused']):
<span class="pause">[paused]</span>
#end if
</a></span>
<span class="tvshowTitleIcons">
#if $cur_result['imdb_id']:
<a href="<%= anon_url('http://www.imdb.com/title/', cur_result['imdb_id']) %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false" title="http://www.imdb.com/title/${cur_result['imdb_id']}"><img alt="[imdb]" height="16" width="16" src="$sbRoot/images/imdb.png" />
#end if
<a href="<%= anon_url(sickbeard.indexerApi(cur_indexer).config['show_url'], cur_result['showid']) %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false" title="$sickbeard.indexerApi($cur_indexer).config['show_url']${cur_result['showid']}"><img alt="$sickbeard.indexerApi($cur_indexer).name" height="16" width="16" src="$sbRoot/images/$sickbeard.indexerApi($cur_indexer).config['icon']" /></a>
<span><a href="$sbRoot/home/searchEpisode?show=${cur_result['showid']}&amp;season=$cur_result['season']&amp;episode=$cur_result['episode']" title="Manual Search" id="forceUpdate-${cur_result['showid']}" class="epSearch forceUpdate"><img alt="[search]" height="16" width="16" src="$sbRoot/images/search16.png" id="forceUpdateImage-${cur_result['showid']}" /></a></span>
</span>
</div>
<span class="title">Next Episode:</span> <span><%= 'S%02iE%02i' % (int(cur_result['season']), int(cur_result['episode'])) %> - $cur_result['name']</span>
<div class="clearfix">
<span class="title">Airs: </span><span class="${fuzzydate}">$sbdatetime.sbdatetime.sbfdatetime($cur_result['localtime']).decode($sickbeard.SYS_ENCODING)</span><%= ('', '<span> on %s</span>' % str(cur_result['network']))[None is not cur_result['network']] %>
</div>
<div class="clearfix">
<span class="title">Quality:</span>
#if int($cur_result['quality']) in $qualityPresets:
<span class="quality $qualityPresetStrings[int($cur_result['quality'])]">$qualityPresetStrings[int($cur_result['quality'])]</span>
#else:
<span class="quality Custom">Custom</span>
#end if
</div>
</td>
</tr>
<tr>
<td style="vertical-align: top;">
<div>
#if $cur_result['description']:
<span class="title" style="vertical-align:middle;">Plot:</span>
<img class="ep_summaryTrigger" src="$sbRoot/images/plus.png" height="16" width="16" alt="" title="Toggle Summary" /><div class="ep_summary">$cur_result['description']</div>
#else:
<span class="title ep_summaryTriggerNone" style="vertical-align:middle;">Plot:</span>
<img class="ep_summaryTriggerNone" src="$sbRoot/images/plus.png" height="16" width="16" alt="" />
#end if
</div>
</td>
</tr>
</table>
</div>
</div>
<!-- end $cur_result['show_name'] //-->
#end for
<!-- end non list view //-->
#end if
#if 'calendar' == $layout:
#set $today = datetime.date.today()
#set $dates = [$today + datetime.timedelta(days = $i) for $i in range(7)]
#set $tbl_day = 0
<br>
<br>
<div class="calendarWrapper">
<input type="hidden" id="sbRoot" value="$sbRoot" />
#for $day in $dates
#set $tbl_day += 1
<table class="sickbeardTable tablesorter calendarTable <%= 'cal-%s' % (('even', 'odd')[1 == tbl_day % 2]) %>" cellspacing="0" border="0" cellpadding="0">
<thead><tr><th>$sbdatetime.sbdatetime.sbfdate($day, '%A').decode($sickbeard.SYS_ENCODING).capitalize()</th></tr></thead>
<tbody>
#set $day_has_show = False
#for $cur_result in $sql_results:
#if int($cur_result['paused']) and not $sickbeard.COMING_EPS_DISPLAY_PAUSED:
#continue
#end if
#set $cur_indexer = int($cur_result['indexer'])
#set $runtime = $cur_result['runtime']
#set $airday = $cur_result['localtime'].date()
#if $airday == $day:
#set $day_has_show = True
#set $airtime = $sbdatetime.sbdatetime.sbftime($cur_result['localtime']).decode($sickbeard.SYS_ENCODING)
#if $sickbeard.TRIM_ZERO:
#set $airtime = re.sub(r'0(\d:\d\d)', r'\1', $airtime, 0, re.IGNORECASE | re.MULTILINE)
#end if
<tr>
<td class="calendarShow">
<div class="poster">
<a title="${cur_result['show_name']}" href="$sbRoot/home/displayShow?show=${cur_result['showid']}"><img alt="" src="$sbRoot/showPoster/?show=${cur_result['showid']}&amp;which=poster_thumb" /></a>
</div>
<div class="text">
<span class="airtime">
${airtime} on $cur_result["network"]
</span>
<span class="episode-title" title="$cur_result['name']">
<%= 'S%02iE%02i' % (int(cur_result['season']), int(cur_result['episode'])) %> - $cur_result['name']
</span>
</div>
</td> <!-- end $cur_result['show_name'] -->
</tr>
#end if
#end for
#if not $day_has_show:
<tr><td class="calendarShow"><span class="show-status">No shows for this day</span></td></tr>
#end if
</tbody>
</table>
#end for
<!-- end calender view //-->
</div>
#end if
<div class="clearfix"></div>
<script type="text/javascript" charset="utf-8">
<!--
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')

View file

@ -103,9 +103,9 @@
<span class="component-desc">
<select id="indexer_default" name="indexer_default" class="form-control input-sm">
<option value="0" #if $indexer == 0 then 'selected="selected"' else ''#>All Indexers</option>
#for $indexer in $sickbeard.indexerApi().indexers
#for $indexer in $sickbeard.indexerApi().indexers
<option value="$indexer" #if $indexer == $sickbeard.INDEXER_DEFAULT then 'selected="selected"' else ''#>$sickbeard.indexerApi().indexers[$indexer]</option>
#end for
#end for
</select>
<span>as the default selection when adding new shows</span>
</span>
@ -127,7 +127,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($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_rootDirs.tmpl')
</span>
</label>
</div>
@ -246,10 +246,10 @@
</div>
<div class="field-pair show_if_fuzzy_dating#if True == $sickbeard.FUZZY_DATING then '' else ' metadataDiv'#">
<label for="trim_zero">
<span class="component-title">Trim zero padding</span>
<span class="component-title">Trim date and time</span>
<span class="component-desc">
<input type="checkbox" name="trim_zero" id="trim_zero" #if True == $sickbeard.TRIM_ZERO then 'checked="checked"' else ''#/>
<p>remove the leading number "0" shown on hour of day, and date of month</p>
<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>
</div>
@ -259,16 +259,16 @@
<span class="component-title">Date style:</span>
<span class="component-desc">
<select class="form-control input-sm #if True == $sickbeard.FUZZY_DATING then '' else ' metadataDiv'#" id="date_presets#if True == $sickbeard.FUZZY_DATING then '' else '_na'#" name="date_preset#if True == $sickbeard.FUZZY_DATING then '' else '_na'#">
#for $cur_preset in $date_presets:
<option value="$cur_preset" #if $cur_preset == $sickbeard.DATE_PRESET or ("%x" == $sickbeard.DATE_PRESET and "$cur_preset" == '%a, %b %d, %Y') then 'selected="selected"' else ''#>$datetime.datetime($datetime.datetime.now().year, 12, 31, 14, 30, 47).strftime($cur_preset)</option>
#end for
#for $cur_preset in $date_presets:
<option value="$cur_preset" #if $cur_preset == $sickbeard.DATE_PRESET or ("%x" == $sickbeard.DATE_PRESET and "$cur_preset" == '%a, %b %d, %Y') then 'selected="selected"' else ''#>$datetime.datetime($datetime.datetime.now().year, 12, 31, 14, 30, 47).strftime($cur_preset)</option>
#end for
</select>
<select class="form-control input-sm #if True != $sickbeard.FUZZY_DATING then '' else ' metadataDiv'#" id="date_presets#if True != $sickbeard.FUZZY_DATING then '' else '_na'#" name="date_preset#if True != $sickbeard.FUZZY_DATING then '' else '_na'#">
<option value="%x" #if "%x" == $sickbeard.DATE_PRESET then 'selected="selected"' else ''#>Use System Default</option>
#for $cur_preset in $date_presets:
<option value="$cur_preset" #if $cur_preset == $sickbeard.DATE_PRESET then 'selected="selected"' else ''#>$datetime.datetime($datetime.datetime.now().year, 12, 31, 14, 30, 47).strftime($cur_preset)</option>
#end for
#for $cur_preset in $date_presets:
<option value="$cur_preset" #if $cur_preset == $sickbeard.DATE_PRESET then 'selected="selected"' else ''#>$datetime.datetime($datetime.datetime.now().year, 12, 31, 14, 30, 47).strftime($cur_preset)</option>
#end for
</select>
</span>
</label>
@ -279,11 +279,12 @@
<span class="component-title">Time style:</span>
<span class="component-desc">
<select id="time_presets" name="time_preset" class="form-control input-sm">
#for $cur_preset in $time_presets:
<option value="$cur_preset" #if $cur_preset == $sickbeard.TIME_PRESET_W_SECONDS then 'selected="selected"' else ''#>$sbdatetime.now().sbftime(show_seconds=True,t_preset=$cur_preset)</option>
#end for
#for $cur_preset in $time_presets:
#set $show_seconds = not $sickbeard.FUZZY_DATING
<option value="$cur_preset" #if $cur_preset == $sickbeard.TIME_PRESET_W_SECONDS then 'selected="selected"' else ''#>$sbdatetime.now().sbftime(show_seconds=$show_seconds, t_preset=$cur_preset)</option>
#end for
</select>
<span><b>note:</b> seconds are only shown on the History page</span>
<span id="trim_info_seconds"><b>note:</b> seconds are only shown on the History page</span>
</span>
</label>
</div>
@ -450,9 +451,9 @@
<span class="component-title">Branch version:</span>
<span class="component-desc">
<select id="branchVersion" class="form-control form-control-inline input-sm pull-left">
#for $cur_branch in $sickbeard.versionCheckScheduler.action.list_remote_branches():
#for $cur_branch in $sickbeard.versionCheckScheduler.action.list_remote_branches():
<option value="$cur_branch" #if $cur_branch == $sickbeard.BRANCH then 'selected="selected"' else ''#>$cur_branch</option>
#end for
#end for
</select>
<input class="btn btn-inline" style="margin-left: 6px;" type="button" id="branchCheckout" value="Checkout Branch">
<div class="clear-left"><p>select branch to use (restart required)</p></div>
@ -460,6 +461,24 @@
</label>
</div>
#set pulls = sickbeard.versionCheckScheduler.action.list_remote_pulls()
#if len(pulls) > 0 and $sickbeard.BRANCH != 'master':
<div class="field-pair">
<label>
<span class="component-title">Pull request:</span>
<span class="component-desc">
<select id="pullRequestVersion" class="form-control form-control-inline input-sm pull-left">
#for $cur_branch in $pulls:
<option value="$cur_branch.fetch_name()" #if $cur_branch == $sickbeard.BRANCH then 'selected="selected"' else ''#>$cur_branch</option>
#end for
</select>
<input class="btn btn-inline" style="margin-left: 6px;" type="button" id="pullRequestCheckout" value="Checkout Pull Request">
<div class="clear-left"><p>select pull request to test (restart required)</p></div>
</span>
</label>
</div>
#end if
<div class="field-pair">
<label for="git_remote">
<span class="component-title">Git remote for branch</span>
@ -485,9 +504,9 @@
<span class="component-title">CPU throttling:</span>
<span class="component-desc">
<select id="cpu_presets" name="cpu_preset" class="form-control input-sm">
#for $cur_preset in $cpu_presets:
#for $cur_preset in $cpu_presets:
<option value="$cur_preset" #if $cur_preset == $sickbeard.CPU_PRESET then 'selected="selected"' else ''#>$cur_preset.capitalize()</option>
#end for
#end for
</select>
<span>Normal (default). High is lower and Low is higher CPU use</span>
</span>
@ -551,7 +570,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 class="boldest">All non-absolute folder locations are relative to <span class="path">$sickbeard.DATA_DIR</span></b></h6>
<input type="submit" class="btn pull-left config_submitter button" value="Save Changes" />
</div><!-- /config-components -->

View file

@ -214,13 +214,13 @@
</div>
#end if
#if $hasattr($curNewznabProvider, 'enable_daily'):
#if $hasattr($curNewznabProvider, 'enable_recentsearch'):
<div class="field-pair">
<label for="${curNewznabProvider.getID()}_enable_daily">
<span class="component-title">Enable daily searches</span>
<label for="${curNewznabProvider.getID()}_enable_recentsearch">
<span class="component-title">Enable recent searches</span>
<span class="component-desc">
<input type="checkbox" name="${curNewznabProvider.getID()}_enable_daily" id="${curNewznabProvider.getID()}_enable_daily" <%= html_checked if curNewznabProvider.enable_daily else '' %>/>
<p>perform daily searches at provider</p>
<input type="checkbox" name="${curNewznabProvider.getID()}_enable_recentsearch" id="${curNewznabProvider.getID()}_enable_recentsearch" <%= html_checked if curNewznabProvider.enable_recentsearch else '' %>/>
<p>perform recent searches at provider</p>
</span>
</label>
</div>
@ -292,13 +292,13 @@
#end if
#if $hasattr($curNzbProvider, 'enable_daily'):
#if $hasattr($curNzbProvider, 'enable_recentsearch'):
<div class="field-pair">
<label for="${curNzbProvider.getID()}_enable_daily">
<span class="component-title">Enable daily searches</span>
<label for="${curNzbProvider.getID()}_enable_recentsearch">
<span class="component-title">Enable recent searches</span>
<span class="component-desc">
<input type="checkbox" name="${curNzbProvider.getID()}_enable_daily" id="${curNzbProvider.getID()}_enable_daily" <%= html_checked if curNzbProvider.enable_daily else '' %>/>
<p>enable provider to perform daily searches.</p>
<input type="checkbox" name="${curNzbProvider.getID()}_enable_recentsearch" id="${curNzbProvider.getID()}_enable_recentsearch" <%= html_checked if curNzbProvider.enable_recentsearch else '' %>/>
<p>enable provider to perform recent searches.</p>
</span>
</label>
</div>
@ -512,13 +512,13 @@
</div>
#end if
#if $hasattr($curTorrentProvider, 'enable_daily'):
#if $hasattr($curTorrentProvider, 'enable_recentsearch'):
<div class="field-pair">
<label for="${curTorrentProvider.getID()}_enable_daily">
<span class="component-title">Enable daily searches</span>
<label for="${curTorrentProvider.getID()}_enable_recentsearch">
<span class="component-title">Enable recent searches</span>
<span class="component-desc">
<input type="checkbox" name="${curTorrentProvider.getID()}_enable_daily" id="${curTorrentProvider.getID()}_enable_daily" <%= html_checked if curTorrentProvider.enable_daily else '' %>/>
<p>enable provider to perform daily searches.</p>
<input type="checkbox" name="${curTorrentProvider.getID()}_enable_recentsearch" id="${curTorrentProvider.getID()}_enable_recentsearch" <%= html_checked if curTorrentProvider.enable_recentsearch else '' %>/>
<p>enable provider to perform recent searches.</p>
</span>
</label>
</div>

View file

@ -90,10 +90,10 @@
<div class="field-pair">
<label>
<span class="component-title">Daily search frequency</span>
<span class="component-title">Recent search frequency</span>
<span class="component-desc">
<input type="text" name="dailysearch_frequency" value="$sickbeard.DAILYSEARCH_FREQUENCY" class="form-control input-sm input75" />
<p>time in minutes between searches (min. $sickbeard.MIN_DAILYSEARCH_FREQUENCY)</p>
<input type="text" name="recentsearch_frequency" value="$sickbeard.RECENTSEARCH_FREQUENCY" class="form-control input-sm input75" />
<p>time in minutes between searches (min. $sickbeard.MIN_RECENTSEARCH_FREQUENCY)</p>
</span>
</label>
</div>
@ -139,11 +139,11 @@
</div>
<div class="field-pair">
<label for="dailysearch_startup">
<span class="component-title">Daily search on startup</span>
<label for="recentsearch_startup">
<span class="component-title">Recent search on startup</span>
<span class="component-desc">
<input type="checkbox" name="dailysearch_startup" id="dailysearch_startup" <%= html_checked if sickbeard.DAILYSEARCH_STARTUP == True else '' %>/>
<p>start daily search on startup of SickGear</p>
<input type="checkbox" name="recentsearch_startup" id="recentsearch_startup" <%= html_checked if sickbeard.RECENTSEARCH_STARTUP == True else '' %>/>
<p>start recent search on startup of SickGear</p>
</span>
</label>
</div>
@ -421,6 +421,7 @@
<div class="clear-left">
<p id="host_desc_torrent">URL to your torrent client (e.g. http://localhost:8000/)</p>
<p id="host_desc_rtorrent" style="display:none"><b>Note:</b> <i>rTorrent</i> client URLs use e.g. scgi://localhost:5000/</p>
<p id="host_desc_deluge" style="display:none">URL to your Deluge WebUI (e.g. http://localhost:8112/)</p>
</div>
</span>
</label>
@ -436,7 +437,7 @@
</label>
</div>
<div class="field-pair">
<div class="field-pair" id="torrent_username_option">
<label>
<span class="component-title" id="username_title">Client username</span>
<span class="component-desc">

View file

@ -0,0 +1,695 @@
#import sickbeard
#import datetime
#from sickbeard.common import *
#from sickbeard import sbdatetime
#from sickbeard.helpers import anon_url
#set global $title = 'Episode View'
#set global $header = 'Episode View'
#set global $sbPath = '..'
#set global $topmenu = 'episodeView'
#import os.path
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
#set $sort = $sickbeard.EPISODE_VIEW_SORT
#set $table_sort_header_codes = {'time': 0, 'show': 1, 'network': 4}
#if $sort not in $table_sort_header_codes:
#set $sort = 'time'
#end if
#if 'daybyday' != $layout:
<script type="text/javascript" src="$sbRoot/js/ajaxEpSearch.js?$sbPID"></script>
#end if
#if $varExists('header')
<h1 class="header">$header</h1>
#else
<h1 class="title">$title</h1>
#end if
#if 'daybyday' == $layout:
<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?$sbPID"></script>
<script type="text/javascript" charset="utf-8">
<!--
\$(document).ready(function(){
var \$container = [];
\$.each(\$('[id^=day]'), function(){\$container.push(\$('#' + \$(this).attr('id')))});
jQuery.each(\$container, function(j) {
this.isotope({
itemSelector: '.daybyday-show',
sortBy : '$sort',
layoutMode: 'vertical',
transitionDuration: 0,
getSortData: {
network: function(itemElem) {
return \$(itemElem).attr('data-network') || '';
},
showname: function(itemElem) {
return \$(itemElem).attr('data-name') || '';
},
season: function(itemElem) {
var season = \$(itemElem).attr('data-season') || '0';
return season.length && parseInt(season, 10);
},
episode: function(itemElem) {
var episode = \$(itemElem).attr('data-episode') || '0';
return episode.length && parseInt(episode, 10);
},
time: function(itemElem) {
var time = \$(itemElem).attr('data-time') || '0';
return time.length && parseInt(time, 10);
}
}
});
});
imagesLoaded('.daybyday-show', function() {
jQuery.each(\$container, function(j) {
this.isotope('layout');
});
});
var uiSortBy = (function(sortBy) {
var sortCriteria;
switch (sortBy) {
case 'network':
sortCriteria = ['network', 'time', 'showname', 'season', 'episode'];
break;
case 'show':
sortCriteria = ['showname', 'time', 'season', 'episode'];
break;
case 'time':
default:
sortCriteria = ['time', 'showname', 'season', 'episode'];
break;
}
jQuery.each(\$container, function(j) {
this.isotope({
sortBy: sortCriteria,
sortAscending: 'asc' == \$('#sort-dir').attr('data-sort-dir')
});
});
});
\$('#sort').on('change', function() {
uiSortBy(this.value);
\$.get(this.options[this.selectedIndex].getAttribute('data-sort'));
});
\$('#sort-dir').on('click', function() {
var sortdir = \$(this).attr('data-sort-dir'),
newdir = ('asc' == sortdir ? 'desc' : 'asc');
\$(this).attr('data-sort-dir', newdir);
\$(this).attr('title', 'Click to sort ' + sortdir + 'ending');
\$(this).removeClass(sortdir).addClass(newdir);
uiSortBy(\$('#sort').val());
});
});
//-->
</script>
#end if
<style type="text/css">
#SubMenu {display:none}
#if 'daybyday' == $layout:
.caret {
cursor: pointer;
vertical-align: middle;
margin-right: 2px;
}
.asc {
border-top: 0;
border-bottom: 8px solid;
}
.desc {
border-top: 8px solid;
border-bottom: 0;
}
#end if
</style>
<div class="h2footer pull-right">
<span>Layout:
<select name="layout" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;">
<option value="$sbRoot/setEpisodeViewLayout/?layout=banner" #if 'banner' == $sickbeard.EPISODE_VIEW_LAYOUT then 'selected="selected"' else ''#>Banner</option>
<option value="$sbRoot/setEpisodeViewLayout/?layout=daybyday" #if 'daybyday' == $sickbeard.EPISODE_VIEW_LAYOUT then 'selected="selected"' else ''#>Day by Day</option>
<option value="$sbRoot/setEpisodeViewLayout/?layout=list" #if 'list' == $sickbeard.EPISODE_VIEW_LAYOUT then 'selected="selected"' else ''#>List</option>
<option value="$sbRoot/setEpisodeViewLayout/?layout=poster" #if 'poster' == $sickbeard.EPISODE_VIEW_LAYOUT then 'selected="selected"' else ''#>Poster</option>
</select>
</span>
&nbsp;
<span>Sort
#if 'daybyday' == $layout:
<span id="sort-dir" data-sort-dir="asc" class="caret asc" title="Click to sort descending">&nbsp;</span>
#end if
By
#if 'daybyday' == $layout:
<select name="sort" id="sort" class="form-control form-control-inline input-sm">
<option value="network" data-sort="$sbRoot/setEpisodeViewSort/?sort=network&redir=0" #if 'network' == $sort then 'selected="selected"' else ''#>Network</option>
<option value="show" data-sort="$sbRoot/setEpisodeViewSort/?sort=show&redir=0" #if 'show' == $sort then 'selected="selected"' else ''#>Show</option>
<option value="time" data-sort="$sbRoot/setEpisodeViewSort/?sort=time&redir=0" #if 'time' == $sort then 'selected="selected"' else ''#>Time</option>
#else
<select name="sort" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;">
<option value="$sbRoot/setEpisodeViewSort/?sort=time" #if 'time' == $sort then 'selected="selected"' else ''#>Date/Time</option>
<option value="$sbRoot/setEpisodeViewSort/?sort=network" #if 'network' == $sort then 'selected="selected"' else ''#>Network</option>
<option value="$sbRoot/setEpisodeViewSort/?sort=show" #if 'show' == $sort then 'selected="selected"' else ''#>Show</option>
#end if
</select>
</span>
&nbsp;
<span>View Paused:
<select name="viewpaused" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;">
<option value="$sbRoot/toggleEpisodeViewDisplayPaused"<%= (' selected="selected"', '')[True == sickbeard.EPISODE_VIEW_DISPLAY_PAUSED] %>>Hidden</option>
<option value="$sbRoot/toggleEpisodeViewDisplayPaused"<%= ('', ' selected="selected"')[True == sickbeard.EPISODE_VIEW_DISPLAY_PAUSED] %>>Shown</option>
</select>
</span>
</div>
<div class="key pull-right">
#if 'daybyday' != $layout:
<b>Key:</b>
<span class="listing-key listing-overdue">Missed</span>
<span class="listing-key listing-current">Current</span>
<span class="listing-key listing-default">Future</span>
<span class="listing-key listing-toofar">Distant</span>
#end if
<a class="btn btn-inline forceBacklog" href="webcal://$sbHost:$sbHttpPort/calendar">
<i class="icon-calendar icon-white"></i>Subscribe</a>
</div>
<br>
#if 'list' == $layout:
<!-- start list view //-->
<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?$sbPID"></script>
<script type="text/javascript" charset="utf-8">
<!--
\$.tablesorter.addParser({
id: 'loadingNames',
is: function(s) {
return false
},
format: function(s) {
if(0 == s.indexOf('Loading...'))
return s.replace('Loading...', '000')
return (s || '')
},
type: 'text'
});
\$.tablesorter.addParser({
id: 'quality',
is: function(s) {
return false
},
format: function(s) {
return s.replace('hd1080p', 5).replace('hd720p', 4).replace('hd', 3).replace('sd', 2).replace('any', 1).replace('best', 0).replace('custom', 7)
},
type: 'numeric'
});
\$.tablesorter.addParser({
id: 'cDate',
is: function(s) {
return false
},
format: function(s) {
return s
},
type: 'numeric'
});
\$(document).ready(function(){
sortList = [[$table_sort_header_codes[$sort], 0]];
\$('#showListTable:has(tbody tr)').tablesorter({
widgets: ['stickyHeaders'],
sortList: sortList,
textExtraction: {
0: function(node) {return \$(node).find('span').text().toLowerCase() || ''},
1: function(node) {return \$(node).find('a').attr('data-name') || ''},
4: function(node) {return \$(node).find('span').attr('data-network') || ''},
5: function(node) {return \$(node).find('span').text().toLowerCase() || ''}
},
headers: {
0: {sorter: 'cDate'},
1: {sorter: 'loadingNames'},
2: {sorter: false},
3: {sorter: false},
4: {sorter: 'loadingNames'},
5: {sorter: 'quality'},
6: {sorter: false},
7: {sorter: false},
8: {sorter: false}
}
});
\$('#sbRoot').ajaxEpSearch();
#set $fuzzydate = 'airdate'
#if $sickbeard.FUZZY_DATING:
fuzzyMoment({
containerClass : '.${fuzzydate}',
dateHasTime : true,
dateFormat : '${sickbeard.DATE_PRESET}',
timeFormat : '${sickbeard.TIME_PRESET}',
trimZero : #if $sickbeard.TRIM_ZERO then 'true' else 'false'#
});
#end if
});
//-->
</script>
#set $show_div = 'listing-default'
<input type="hidden" id="sbRoot" value="$sbRoot" />
<table id="showListTable" class="sickbeardTable tablesorter seasonstyle" cellspacing="1" border="0" cellpadding="0">
<thead>
<tr>
<th>Airdate</th>
<th>Show</th>
<th class="nowrap">Next Ep</th>
<th>Next Ep Name</th>
<th>Network</th>
<th>Quality</th>
<th>Indexers</th>
<th>Search</th>
</tr>
</thead>
<tbody style="text-shadow:none;">
#for $cur_result in $sql_results:
#set $cur_indexer = int($cur_result['indexer'])
#set $runtime = $cur_result['runtime']
#if int($cur_result['paused']) and not $sickbeard.EPISODE_VIEW_DISPLAY_PAUSED:
#continue
#end if
#set $cur_ep_airdate = $cur_result['localtime'].date()
#if $runtime:
#set $cur_ep_enddate = $cur_result['localtime'] + datetime.timedelta(minutes = $runtime)
#if $cur_ep_enddate < $today:
#set $show_div = 'listing-overdue'
#elif $cur_ep_airdate >= $next_week.date():
#set $show_div = 'listing-toofar'
#elif $cur_ep_airdate >= $today.date() and $cur_ep_airdate < $next_week.date():
#if $cur_ep_airdate == $today.date():
#set $show_div = 'listing-current'
#else:
#set $show_div = 'listing-default'
#end if
#end if
#end if
<!-- start $cur_result['show_name'] //-->
<tr class="$show_div">
## forced to use a div to wrap airdate, the column sort went crazy with a span
<td align="center" class="nowrap">
<div class="${fuzzydate}">$sbdatetime.sbdatetime.sbfdatetime($cur_result['localtime']).decode($sickbeard.SYS_ENCODING)</div><span class="sort_data">$time.mktime($cur_result['localtime'].timetuple())</span>
</td>
<td class="tvShow"><a href="$sbRoot/home/displayShow?show=${cur_result['showid']}" data-name="$cur_result['data_show_name']">$cur_result['show_name']</a>
#if int($cur_result['paused']):
<span class="pause">[paused]</span>
#end if
</td>
<td class="nowrap" align="center">
<%= 'S%02iE%02i' % (int(cur_result['season']), int(cur_result['episode'])) %>
</td>
<td>
#if $cur_result['description']:
<img alt="" src="$sbRoot/images/info32.png" height="16" width="16" class="plotInfo" id="plot_info_<%= '%s_%s_%s' % (str(cur_result['showid']), str(cur_result['season']), str(cur_result['episode'])) %>" />
#else:
<img alt="" src="$sbRoot/images/info32.png" width="16" height="16" class="plotInfoNone" />
#end if
$cur_result['name']
</td>
<td align="center">
<span data-network="$cur_result['data_network']">$cur_result['network']</span>
</td>
<td align="center">
#if int($cur_result['quality']) in $qualityPresets:
<span class="quality $qualityPresetStrings[int($cur_result['quality'])]">$qualityPresetStrings[int($cur_result['quality'])]</span>
#else:
<span class="quality Custom">Custom</span>
#end if
</td>
<td align="center" style="vertical-align: middle;">
#if $cur_result['imdb_id']:
<a href="<%= anon_url('http://www.imdb.com/title/', cur_result['imdb_id']) %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false" title="http://www.imdb.com/title/${cur_result['imdb_id']}"><img alt="[imdb]" height="16" width="16" src="$sbRoot/images/imdb.png" />
#end if
<a href="<%= anon_url(sickbeard.indexerApi(cur_indexer).config['show_url'], cur_result['showid']) %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false" title="$sickbeard.indexerApi($cur_indexer).config['show_url']${cur_result['showid']}"><img alt="$sickbeard.indexerApi($cur_indexer).name" height="16" width="16" src="$sbRoot/images/$sickbeard.indexerApi($cur_indexer).config['icon']" /></a>
</td>
<td align="center">
<a href="$sbRoot/home/searchEpisode?show=${cur_result['showid']}&amp;season=$cur_result['season']&amp;episode=$cur_result['episode']" title="Manual Search" id="forceUpdate-${cur_result['showid']}" class="forceUpdate epSearch"><img alt="[search]" height="16" width="16" src="$sbRoot/images/search16.png" id="forceUpdateImage-${cur_result['showid']}" /></a>
</td>
</tr>
<!-- end $cur_result['show_name'] //-->
#end for
</tbody>
<tfoot>
<tr>
<th rowspan="1" colspan="10" align="center">&nbsp</th>
</tr>
</tfoot>
</table>
<!-- end list view //-->
#else if $layout in ['banner', 'poster']:
<!-- start non list view //-->
<script type="text/javascript" charset="utf-8">
<!--
\$(document).ready(function(){
\$('#sbRoot').ajaxEpSearch({'size': 16, 'loadingImage': 'loading16' + themeSpinner + '.gif'});
\$('.ep_summary').hide();
\$('.ep_summaryTrigger').click(function() {
\$(this).next('.ep_summary').slideToggle('normal', function() {
\$(this).prev('.ep_summaryTrigger').attr('src', function(i, src) {
return \$(this).next('.ep_summary').is(':visible') ? src.replace('plus','minus') : src.replace('minus','plus')
});
});
});
#set $fuzzydate = 'airdate'
#if $sickbeard.FUZZY_DATING:
fuzzyMoment({
dtInline : true,
dtGlue : ' at ',
containerClass : '.${fuzzydate}',
dateHasTime : true,
dateFormat : '${sickbeard.DATE_PRESET}',
timeFormat : '${sickbeard.TIME_PRESET}',
trimZero : #if $sickbeard.TRIM_ZERO then 'true' else 'false'#
});
#end if
});
//-->
</script>
#set $cur_segment = None
#set $too_late_header = False
#set $missed_header = False
#set $today_header = False
#set $show_div = 'ep_listing listing-default'
#if 'show' == $sort:
<br /><br />
#end if
#for $cur_result in $sql_results:
#set $cur_indexer = int($cur_result['indexer'])
#if int($cur_result['paused']) and not $sickbeard.EPISODE_VIEW_DISPLAY_PAUSED:
#continue
#end if
#set $runtime = $cur_result['runtime']
#if 'network' == $sort:
#set $show_network = $cur_result['network'] if $cur_result['network'] else 'no network'
#if $cur_segment != $show_network:
<div class="episode-view-header">
<br><h2 class="network">$show_network</h2>
#set $cur_segment = $cur_result['network']
#end if
#set $cur_ep_airdate = $cur_result['localtime'].date()
#if $runtime:
#set $cur_ep_enddate = $cur_result['localtime'] + datetime.timedelta(minutes = $runtime)
#if $cur_ep_enddate < $today:
#set $show_div = 'ep_listing listing-overdue'
#elif $cur_ep_airdate >= $next_week.date():
#set $show_div = 'ep_listing listing-toofar'
#elif $cur_ep_enddate >= $today and $cur_ep_airdate < $next_week.date():
#if $cur_ep_airdate == $today.date():
#set $show_div = 'ep_listing listing-current'
#else:
#set $show_div = 'ep_listing listing-default'
#end if
#end if
#end if
#elif 'time' == $sort:
#set $cur_ep_airdate = $cur_result['localtime'].date()
#if $cur_segment != $cur_ep_airdate:
#if $runtime:
#set $cur_ep_enddate = $cur_result['localtime'] + datetime.timedelta(minutes = $runtime)
#if $cur_ep_enddate < $today and $cur_ep_airdate != $today.date() and not $missed_header:
<br /><h2 class="day">Missed</h2>
#set $missed_header = True
#elif $cur_ep_airdate >= $next_week.date() and not $too_late_header:
<br /><h2 class="day">Later</h2>
#set $too_late_header = True
#elif $cur_ep_enddate >= $today and $cur_ep_airdate < $next_week.date():
#if $cur_ep_airdate == $today.date():
<br /><h2 class="day">$sbdatetime.sbdatetime.sbfdate($cur_ep_airdate, '%A').decode($sickbeard.SYS_ENCODING).capitalize() <span style="font-size: 14px; vertical-align: top;">[Today]</span></h2>
#set $today_header = True
#else:
<br /><h2 class="day">$sbdatetime.sbdatetime.sbfdate($cur_ep_airdate, '%A').decode($sickbeard.SYS_ENCODING).capitalize()</h2>
#end if
#end if
#end if
#set $cur_segment = $cur_ep_airdate
#end if
#if $cur_ep_airdate == $today.date() and not $today_header:
<div class="episode-view-header">
<br /><h2 class="day">$sbdatetime.sbdatetime.sbfdate($cur_ep_airdate, '%A').decode($sickbeard.SYS_ENCODING).capitalize() <span style="font-size: 14px; vertical-align: top;">[Today]</span></h2>
#set $today_header = True
#end if
#if $runtime:
#set $cur_ep_enddate = $cur_result['localtime'] + datetime.timedelta(minutes = $runtime)
#if $cur_ep_enddate < $today:
#set $show_div = 'ep_listing listing-overdue'
#elif $cur_ep_airdate >= $next_week.date():
#set $show_div = 'ep_listing listing-toofar'
#elif $cur_ep_enddate >= $today and $cur_ep_airdate < $next_week.date():
#if $cur_ep_airdate == $today.date():
#set $show_div = 'ep_listing listing-current'
#else:
#set $show_div = 'ep_listing listing-default'
#end if
#end if
#end if
#elif 'show' == $sort:
#set $cur_ep_airdate = $cur_result['localtime'].date()
#if $runtime:
#set $cur_ep_enddate = $cur_result['localtime'] + datetime.timedelta(minutes = $runtime)
#if $cur_ep_enddate < $today:
#set $show_div = 'ep_listing listing-overdue listingradius'
#elif $cur_ep_airdate >= $next_week.date():
#set $show_div = 'ep_listing listing-toofar listingradius'
#elif $cur_ep_enddate >= $today and $cur_ep_airdate < $next_week.date():
#if $cur_ep_airdate == $today.date():
#set $show_div = 'ep_listing listing-current listingradius'
#else:
#set $show_div = 'ep_listing listing-default listingradius'
#end if
#end if
#end if
#end if
<!-- start $cur_result['show_name'] //-->
<div class="$show_div" id="listing-${cur_result['showid']}">
<div class="tvshowDiv">
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<th #if 'banner' == $layout then 'class="nobg"' else 'rowspan="2"'# valign="top">
<a href="$sbRoot/home/displayShow?show=${cur_result['showid']}"><img alt="" class="#if 'banner' == $layout then 'bannerThumb' else 'posterThumb'#" src="$sbRoot/showPoster/?show=${cur_result['showid']}&amp;which=#if 'poster' == $layout then 'poster_thumb' else $layout#" /></a>
</th>
#if 'banner' == $layout:
</tr>
<tr>
#end if
<td class="next_episode">
<div class="clearfix">
<span class="tvshowTitle">
<a href="$sbRoot/home/displayShow?show=${cur_result['showid']}" data-name="$cur_result['data_show_name']">$cur_result['show_name']
#if int($cur_result['paused']):
<span class="pause">[paused]</span>
#end if
</a></span>
<span class="tvshowTitleIcons">
#if $cur_result['imdb_id']:
<a href="<%= anon_url('http://www.imdb.com/title/', cur_result['imdb_id']) %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false" title="http://www.imdb.com/title/${cur_result['imdb_id']}"><img alt="[imdb]" height="16" width="16" src="$sbRoot/images/imdb.png" />
#end if
<a href="<%= anon_url(sickbeard.indexerApi(cur_indexer).config['show_url'], cur_result['showid']) %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false" title="$sickbeard.indexerApi($cur_indexer).config['show_url']${cur_result['showid']}"><img alt="$sickbeard.indexerApi($cur_indexer).name" height="16" width="16" src="$sbRoot/images/$sickbeard.indexerApi($cur_indexer).config['icon']" /></a>
<span><a href="$sbRoot/home/searchEpisode?show=${cur_result['showid']}&amp;season=$cur_result['season']&amp;episode=$cur_result['episode']" title="Manual Search" id="forceUpdate-${cur_result['showid']}" class="epSearch forceUpdate"><img alt="[search]" height="16" width="16" src="$sbRoot/images/search16.png" id="forceUpdateImage-${cur_result['showid']}" /></a></span>
</span>
</div>
<span class="title">Next Episode:</span> <span><%= 'S%02iE%02i' % (int(cur_result['season']), int(cur_result['episode'])) %> - $cur_result['name']</span>
<div class="clearfix">
<span class="title">Airdate: </span><span class="${fuzzydate}">$sbdatetime.sbdatetime.sbfdatetime($cur_result['localtime']).decode($sickbeard.SYS_ENCODING)</span><%= ('', '<span> on %s</span>' % str(cur_result['network']))[None is not cur_result['network']] %>
</div>
<div class="clearfix">
<span class="title">Quality:</span>
#if int($cur_result['quality']) in $qualityPresets:
<span class="quality $qualityPresetStrings[int($cur_result['quality'])]">$qualityPresetStrings[int($cur_result['quality'])]</span>
#else:
<span class="quality Custom">Custom</span>
#end if
</div>
</td>
</tr>
<tr>
<td style="vertical-align: top;">
<div>
#if $cur_result['description']:
<span class="title" style="vertical-align:middle;">Plot:</span>
<img class="ep_summaryTrigger" src="$sbRoot/images/plus.png" height="16" width="16" alt="" title="Toggle Summary" /><div class="ep_summary">$cur_result['description']</div>
#else:
<span class="title ep_summaryTriggerNone" style="vertical-align:middle;">Plot:</span>
<img class="ep_summaryTriggerNone" src="$sbRoot/images/plus.png" height="16" width="16" alt="" />
#end if
</div>
</td>
</tr>
</table>
</div>
</div>
<!-- end $cur_result['show_name'] //-->
#end for
<!-- end non list view //-->
#end if
#if 'daybyday' == $layout:
#set $today = datetime.date.today()
#set $dates = [$today + datetime.timedelta(days = $i) for $i in range(7)]
#set $tbl_day = 0
<input type="hidden" id="sbRoot" value="$sbRoot" />
<br>
<br>
<div class="daybydayWrapper"> <!-- style="width:1600px" -->
#for $day in $dates
#set $tbl_day += 1
#set $col_class = ''
#if 1 == $tbl_day
#set $col_class = 'today'
#end if
#set $col_class = '%s %s' % ($col_class, ('even', 'odd')[1 == tbl_day % 2])
<div class="day-of-week $col_class">
<div class="day-number">
<div class="number">$sbdatetime.sbdatetime.sbfdate($day, ' %d').decode($sickbeard.SYS_ENCODING).replace(' 0', ' ')</div>
<div class="day">
<span class="visible-lg">$sbdatetime.sbdatetime.sbfdate($day, '%A').decode($sickbeard.SYS_ENCODING).capitalize()</span>
<span class="hidden-lg">$sbdatetime.sbdatetime.sbfdate($day, '%a').decode($sickbeard.SYS_ENCODING).capitalize()</span>
</div>
<div class="month">
<span class="visible-lg">$sbdatetime.sbdatetime.sbfdate($day, '%B').decode($sickbeard.SYS_ENCODING).capitalize()</span>
<span class="hidden-lg">$sbdatetime.sbdatetime.sbfdate($day, '%b').decode($sickbeard.SYS_ENCODING).capitalize()</span>
</div>
</div>
<div id="$sbdatetime.sbdatetime.sbfdate($day, 'day%w')">
#set $day_has_show = False
#for $cur_result in $sql_results:
#if int($cur_result['paused']) and not $sickbeard.EPISODE_VIEW_DISPLAY_PAUSED:
#continue
#end if
#set $cur_indexer = int($cur_result['indexer'])
#set $runtime = $cur_result['runtime']
#set $airday = $cur_result['localtime'].date()
#if $airday == $day:
#set $day_has_show = True
#set $airtime = $sbdatetime.sbdatetime.sbftime($cur_result['localtime'], markup=True).decode($sickbeard.SYS_ENCODING)
#set $img_tag = '<img'
#set $plot_class = 'class="img-responsive'
#set $title_text = ''
#if $cur_result['description']:
#set $img_tag += ' id="plot_info_%s_%s_%s"' % (str($cur_result['showid']), str($cur_result['season']), str($cur_result['episode']))
#set $plot_class += ' plot-daybyday'
#else
#set $title_text = $cur_result['show_name']
#end if
<div id="show-$cur_result['showid']" class="daybyday-show" data-name="$cur_result['data_show_name']" data-season="$cur_result['season']" data-episode="$cur_result['episode']" data-network="$cur_result['data_network']" data-time="$time.mktime($cur_result['localtime'].timetuple())">
<div class="poster">
<a title="${title_text}" href="$sbRoot/home/displayShow?show=${cur_result['showid']}">
${img_tag} ${plot_class}" alt="" src="$sbRoot/showPoster/?show=${cur_result['showid']}&amp;which=poster_thumb" /></a>
</div>
<div class="text">
<div class="airtime">
<span class="time">${airtime}</span> <span class="network pull-right grey-text">$cur_result['network']</span>
</div>
<div class="episode" title="$cur_result['name']">
<span class="season"><%= '%i' % int(cur_result['season']) %></span>x<span class="number"><%= '%02i' % int(cur_result['episode']) %></span>
<span class="name">$cur_result['name']</span>
#if int($cur_result['paused']):
<span class="pause">[paused]</span>
#end if
</div>
</div>
</div><!-- end show-$cur_result['showid'] //-->
#end if
#end for
#if not $day_has_show:
<div class="daybyday-show">
<span class="episode-blank">No shows for this day</span>
</div>
#end if
</div>
</div>
#end for
</div>
<!-- end calender view //-->
#end if
<div class="clearfix"></div>
<script type="text/javascript" charset="utf-8">
<!--
window.setInterval('location.reload(true)', 30*60000); // Refresh every xx minutes
//-->
</script>
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')

View file

@ -366,13 +366,13 @@ $myShowList.sort(lambda x, y: cmp(x.name, y.name))
if (classvalue<20) {
classtoadd = "progress-20"
}
if (classvalue>20 && classvalue<60) {
if (classvalue>=20 && classvalue<60) {
classtoadd = "progress-40"
}
if (classvalue>40 && classvalue<80) {
if (classvalue>=40 && classvalue<80) {
classtoadd = "progress-60"
}
if (classvalue>80) {
if (classvalue>=80) {
classtoadd = "progress-80"
}
\$("\#progressbar$curShow.indexerid > .ui-progressbar-value").addClass(classtoadd);
@ -596,13 +596,13 @@ $myShowList.sort(lambda x, y: cmp(x.name, y.name))
if (classvalue<20) {
classtoadd = "progress-20"
}
if (classvalue>20 && classvalue<60) {
if (classvalue>=20 && classvalue<60) {
classtoadd = "progress-40"
}
if (classvalue>40 && classvalue<80) {
if (classvalue>=40 && classvalue<80) {
classtoadd = "progress-60"
}
if (classvalue>80) {
if (classvalue>=80) {
classtoadd = "progress-80"
}
\$("\#progressbar$curShow.indexerid > .ui-progressbar-value").addClass(classtoadd);

View file

@ -11,7 +11,7 @@
#set global $sbPath='..'
#set global $topmenu='comingEpisodes'
#set global $topmenu='episodeView'
#import os.path
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
@ -129,7 +129,7 @@
#else
#for $cur_show in $trending_shows:
#set $image = re.sub(r'(.*)(\..*?)$', r'\1-300\2', $cur_show['images']['poster'], 0, re.IGNORECASE | re.MULTILINE)
#set $image = re.sub(r'(.*)/original/(.+)$', r'\1/thumb/\2', $cur_show['images']['poster'], 0, re.IGNORECASE | re.MULTILINE)
<div class="trakt_show <%= ('notinlibrary', 'inlibrary')[':' in cur_show['show_id']] %>" data-name="$cur_show['title']" data-rating="$cur_show['ratings']['percentage']" data-votes="$cur_show['ratings']['votes']">
<div class="traktContainer">

View file

@ -62,7 +62,7 @@
)[0 < ep_snatched]
%>
&nbsp;/&nbsp;<span class="footerhighlight">$ep_total</span> episodes downloaded
| daily search: <span class="footerhighlight"><%= str(sickbeard.dailySearchScheduler.timeLeft()).split('.')[0] %></span>
| recent search: <span class="footerhighlight"><%= str(sickbeard.recentSearchScheduler.timeLeft()).split('.')[0] %></span>
| backlog search: <span class="footerhighlight">$sbdatetime.sbdatetime.sbfdate($sickbeard.backlogSearchScheduler.nextRun())</span>
</div>

View file

@ -57,6 +57,7 @@
<script type="text/javascript" src="$sbRoot/js/lib/jquery.form-3.35.js?$sbPID"></script>
<script type="text/javascript" src="$sbRoot/js/lib/jquery.ui.touch-punch-0.2.2.min.js?$sbPID"></script>
<script type="text/javascript" src="$sbRoot/js/lib/isotope.pkgd.min.js?$sbPID"></script>
<script type="text/javascript" src="$sbRoot/js/lib/imagesloaded.pkgd.min.js?$sbPID"></script>
<script type="text/javascript" src="$sbRoot/js/lib/jquery.confirm.js?$sbPID"></script>
<script type="text/javascript" src="$sbRoot/js/script.js?$sbPID"></script>
@ -149,8 +150,8 @@
</ul>
</li>
<li id="NAVcomingEpisodes">
<a href="$sbRoot/comingEpisodes/" tabindex="$tab#set $tab += 1#">Coming Episodes</a>
<li id="NAVepisodeView">
<a href="$sbRoot/episodeView/" tabindex="$tab#set $tab += 1#">Episodes</a>
</li>
<li id="NAVhistory">
@ -261,6 +262,6 @@
#except (NameError, NotFound):
#pass
#end try
#set $page_class = ('', ' class="%s"' % '_'.join($items).lower().replace(' ', '-'))[0 < len($items)]
#set $page_class = ('', ' class="%s"' % '_'.join($items).lower().replace(' ', '-').replace('_', '-'))[0 < len($items)]
<div id="contentWrapper">
<div id="content"$page_class>

View file

@ -29,9 +29,9 @@ Currently running<br />
#end if
<br />
<h3>Daily Search:</h3>
<h3>Recent Search:</h3>
<a class="btn" href="$sbRoot/manage/manageSearches/forceSearch"><i class="icon-exclamation-sign"></i> Force</a>
#if not $dailySearchStatus:
#if not $recentSearchStatus:
Not in progress<br />
#else:
In Progress<br />
@ -53,7 +53,7 @@ In Progress<br />
<h3>Search Queue:</h3>
Backlog: <i>$queueLength['backlog'] pending items</i></br>
Daily: <i>$queueLength['daily'] pending items</i></br>
Recent: <i>$queueLength['recent'] pending items</i></br>
Manual: <i>$queueLength['manual'] pending items</i></br>
Failed: <i>$queueLength['failed'] pending items</i></br>
</div>

View file

@ -1,85 +1,130 @@
$(document).ready(function(){
$('.enabler').each(function(){
if (!$(this).prop('checked'))
$('#content_' + $(this).attr('id')).hide();
});
var enabler = $('.enabler'),
viewIf = $('.viewIf');
$('.enabler').click(function(){
if ($(this).prop('checked'))
$('#content_' + $(this).attr('id')).fadeIn('fast', 'linear');
else
$('#content_' + $(this).attr('id')).fadeOut('fast', 'linear');
});
enabler.each(function(){
if (!$(this).prop('checked'))
$('#content_' + $(this).attr('id')).hide();
});
$('.viewIf').click(function(){
if ($(this).prop('checked')) {
$('.hide_if_' + $(this).attr('id')).css('display','none');
$('.show_if_' + $(this).attr('id')).fadeIn('fast', 'linear');
} else {
$('.show_if_' + $(this).attr('id')).css('display','none');
$('.hide_if_' + $(this).attr('id')).fadeIn('fast', 'linear');
}
});
enabler.click(function(){
var content_id = $('#content_' + $(this).attr('id'));
if ($(this).prop('checked'))
content_id.fadeIn('fast', 'linear');
else
content_id.fadeOut('fast', 'linear');
});
$('.datePresets').click(function(){
var def = $('#date_presets').val()
if ($(this).prop('checked') && '%x' == def) {
def = '%a, %b %d, %Y'
$('#date_use_system_default').html('1')
} else if (!$(this).prop('checked') && '1' == $('#date_use_system_default').html())
def = '%x'
viewIf.each(function(){
$(($(this).prop('checked') ? '.hide_if_' : '.show_if_') + $(this).attr('id')).hide();
});
$('#date_presets').attr('name', 'date_preset_old')
$('#date_presets').attr('id', 'date_presets_old')
viewIf.click(function(){
var if_id = '_if_' + $(this).attr('id');
if ($(this).prop('checked')) {
$('.hide' + if_id).fadeOut('fast', 'linear');
$('.show' + if_id).fadeIn('fast', 'linear');
} else {
$('.show' + if_id).fadeOut('fast', 'linear');
$('.hide' + if_id).fadeIn('fast', 'linear');
}
});
$('#date_presets_na').attr('name', 'date_preset')
$('#date_presets_na').attr('id', 'date_presets')
var ui_update_trim_zero = (function() {
var secs = ('00' + new Date().getSeconds().toString()).slice(-2),
elSecs = $('#trim_info_seconds'),
elTrimZero = $('#trim_zero');
elTrimZero.each(function() {
var checked = $(this).prop('checked') && $('#fuzzy_dating').prop('checked');
$('#date_presets_old').attr('name', 'date_preset_na')
$('#date_presets_old').attr('id', 'date_presets_na')
$('#time_presets').find('option').each(function() {
var text = ($(this).text());
$(this).text(checked
? text.replace(/(\b\d+:\d\d):\d+/mg, '$1')
: text.replace(/(\b\d+:\d\d)(?:.\d+)?/mg, '$1:' + secs));
});
});
if (def)
$('#date_presets').val(def)
});
if ($('#fuzzy_dating').prop('checked'))
if (elTrimZero.prop('checked'))
elSecs.fadeOut('fast', 'linear');
else
elSecs.fadeIn('fast', 'linear');
else
elSecs.fadeIn('fast', 'linear');
});
// bind 'myForm' and provide a simple callback function
$('#configForm').ajaxForm({
beforeSubmit: function(){
$('.config_submitter').each(function(){
$(this).attr('disabled', 'disabled');
$(this).after('<span><img src="' + sbRoot + '/images/loading16' + themeSpinner + '.gif"> Saving...</span>');
$(this).hide();
});
},
success: function(){
setTimeout('config_success()', 2000)
}
});
$('#trim_zero, #fuzzy_dating').click(function() {
ui_update_trim_zero();
});
$('#api_key').click(function(){ $('#api_key').select() });
$("#generate_new_apikey").click(function(){
$.get(sbRoot + '/config/general/generateKey',
function(data){
if (data.error != undefined) {
alert(data.error);
return;
}
$('#api_key').val(data);
});
});
ui_update_trim_zero();
$('#branchCheckout').click(function(){
url = sbRoot + '/home/branchCheckout?branch=' + $('#branchVersion').val();
window.location.href = url;
});
$('.datePresets').click(function(){
var elDatePresets = $('#date_presets'),
defaultPreset = elDatePresets.val();
if ($(this).prop('checked') && '%x' == defaultPreset) {
defaultPreset = '%a, %b %d, %Y';
$('#date_use_system_default').html('1')
} else if (!$(this).prop('checked') && '1' == $('#date_use_system_default').html())
defaultPreset = '%x';
elDatePresets.attr('name', 'date_preset_old');
elDatePresets.attr('id', 'date_presets_old');
var elDatePresets_na = $('#date_presets_na');
elDatePresets_na.attr('name', 'date_preset');
elDatePresets_na.attr('id', 'date_presets');
var elDatePresets_old = $('#date_presets_old');
elDatePresets_old.attr('name', 'date_preset_na');
elDatePresets_old.attr('id', 'date_presets_na');
if (defaultPreset)
elDatePresets.val(defaultPreset)
});
// bind 'myForm' and provide a simple callback function
$('#configForm').ajaxForm({
beforeSubmit: function(){
$('.config_submitter').each(function(){
$(this).attr('disabled', 'disabled');
$(this).after('<span><img src="' + sbRoot + '/images/loading16' + themeSpinner + '.gif"> Saving...</span>');
$(this).hide();
});
},
success: function(){
setTimeout('config_success()', 2000)
}
});
$('#api_key').click(function(){ $('#api_key').select() });
$("#generate_new_apikey").click(function(){
$.get(sbRoot + '/config/general/generateKey',
function(data){
if (data.error != undefined) {
alert(data.error);
return;
}
$('#api_key').val(data);
});
});
$('#branchCheckout').click(function(){
window.location.href = sbRoot + '/home/branchCheckout?branch=' + $('#branchVersion').val();
});
$('#pullRequestCheckout').click(function(){
window.location.href = sbRoot + '/home/pullRequestCheckout?branch=' + $('#pullRequestVersion').val();
});
});
function config_success(){
$('.config_submitter').each(function(){
$(this).removeAttr('disabled');
$(this).next().remove();
$(this).show();
});
$('#email_show').trigger('notify');
$('.config_submitter').each(function(){
$(this).removeAttr('disabled');
$(this).next().remove();
$(this).show();
});
$('#email_show').trigger('notify');
}

View file

@ -62,7 +62,9 @@ $(document).ready(function(){
$(label_warning_deluge).hide();
$(host_desc_rtorrent).hide();
$(host_desc_deluge).hide();
$(host_desc_torrent).show();
$(torrent_username_option).show();
$(torrent_verify_cert_option).hide();
$(torrent_path_option).show();
$(torrent_path_option).find('.fileBrowser').show();
@ -86,6 +88,9 @@ $(document).ready(function(){
client = 'Deluge';
$(torrent_verify_cert_option).show();
$(label_warning_deluge).show();
$(host_desc_torrent).hide();
$(host_desc_deluge).show();
$(torrent_username_option).hide();
//$('#directory_title').text(client + directory);
} else if ('download_station' == selectedProvider){
client = 'Synology DS';

View file

@ -11,152 +11,170 @@
*/
function fuzzyMoment(fmConfig) {
var containerClass = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.containerClass)) ? '.fuzzydate' : fmConfig.containerClass),
dateWithTime = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.dateHasTime)) ? false : !!fmConfig.dateHasTime),
dateFormat = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.dateFormat)) ? '' : fmConfig.dateFormat),
timeFormat = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.timeFormat)) ? '' : fmConfig.timeFormat),
trimZero = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.trimZero)) ? false : !!fmConfig.trimZero),
dtGlue = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.dtGlue)) ? '<br />' : fmConfig.dtGlue),
dtInline = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.dtInline)) ? false : fmConfig.dtInline),
var containerClass = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.containerClass)) ? '.fuzzydate' : fmConfig.containerClass),
dateWithTime = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.dateHasTime)) ? false : !!fmConfig.dateHasTime),
dateFormat = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.dateFormat)) ? '' : fmConfig.dateFormat),
timeFormat = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.timeFormat)) ? '' : fmConfig.timeFormat),
trimZero = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.trimZero)) ? false : !!fmConfig.trimZero),
dtGlue = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.dtGlue)) ? '<br />' : fmConfig.dtGlue),
dtInline = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.dtInline)) ? false : fmConfig.dtInline),
jd = (function (str) {
var token_map = ['a', 'ddd', 'A', 'dddd', 'b', 'MMM', 'B', 'MMMM', 'd', 'DD', 'm', 'MM', 'y', 'YY', 'Y', 'YYYY', 'x', 'L',
'H', 'HH', 'I', 'hh', 'M', 'mm', 'S', 'ss', 'p', 'A'],
result = '';
jd = (function (str) {
var token_map = ['a', 'ddd', 'A', 'dddd', 'b', 'MMM', 'B', 'MMMM', 'd', 'DD', 'm', 'MM', 'y', 'YY', 'Y', 'YYYY', 'x', 'L',
'H', 'HH', 'I', 'hh', 'M', 'mm', 'S', 'ss', 'p', 'A', 'P', 'a'],
result = '';
for (var i = 0; i < str.length; i++)
if (/[aAbBdmyYxHIMSp]/.test(str[i])) {
for (var t = 0; t < token_map.length; t = t + 2)
if (str[i] == token_map[t]) {
result += token_map[t + 1];
break;
}
} else if ('%' != str[i])
result += str[i];
for (var i = 0; i < str.length; i++)
if (/[aAbBdmyYxHIMSpP]/.test(str[i])) {
for (var t = 0; t < token_map.length; t = t + 2)
if (str[i] == token_map[t]) {
result += token_map[t + 1];
break;
}
} else if ('%' != str[i])
result += str[i];
return result;
}),
dateToken = jd(dateFormat),
timeToken = jd(timeFormat),
return result;
}),
dateTemplate = jd(dateFormat),
timeTemplate = jd(timeFormat),
addQTip = (function() {
$(this).css('cursor', 'help');
$(this).qtip({
show: {
solo: true
},
position: {
viewport: $(window),
my: 'left center',
adjust: {
y: -10,
x: 2
}
},
style: {
classes: 'qtip-dark qtip-rounded qtip-shadow'
}
});
});
addQTip = (function() {
$(this).css('cursor', 'help');
$(this).qtip({
show: {
solo: true
},
position: {
viewport: $(window),
my: 'left center',
adjust: {
y: -10,
x: 2
}
},
style: {
classes: 'qtip-dark qtip-rounded qtip-shadow'
}
});
});
if (trimZero) {
timeToken = timeToken.replace(/hh/ig, 'h');
dateToken = dateToken.replace(/\bDD\b/g, 'D');
}
if (trimZero) {
timeTemplate = timeTemplate.replace(/hh/g, 'h');
timeTemplate = timeTemplate.replace(/HH/g, 'H');
dateTemplate = dateTemplate.replace(/\bDD\b/g, 'D');
}
$(containerClass).each(function() {
var input = $(this).text(),
dateA = '[<span class="fd">',
dtSeparator = ' ',
timeA = '</span>]', timeB = '[' + timeA;
$(containerClass).each(function() {
var input = $(this).text(),
dateA = '[<span class="fd">',
dtSeparator = ' ',
timeA = '</span>]', timeB = '[' + timeA,
timeToken = timeTemplate;
if (dateWithTime) {
var timeMeta = input.match(/^.{6,}?([,\s]+)(\d{1,2}).(?:\d{2,2})(?:.(\d{2,2}))?(?:\s([ap]m))?$/im);
if (null != timeMeta) {
dtSeparator = (! /undefined/i.test(typeof(timeMeta[1])) ? timeMeta[1] : dtSeparator);
// adjust timeToken to num digits of input hours
timeToken = (! /undefined/i.test(typeof(timeMeta[2])) && 1 == timeMeta[2].length ? timeToken.replace(/hh/ig, 'h') : timeToken);
// adjust timeToken to use seconds if input has them
timeToken = (! /undefined/i.test(typeof(timeMeta[3])) && 2 == timeMeta[3].length ? timeToken : timeToken.replace(/.ss/, ''));
// adjust timeToken to am/pm or AM/PM if input has it
timeToken = (! /undefined/i.test(typeof(timeMeta[4])) && 2 == timeMeta[4].length ? timeToken.replace(/A$/, (/[ap]m/.test(timeMeta[4]) ? 'a' : 'A')) : timeToken);
}
timeA = '</span>' + dtGlue + '<span class="ft">]' + timeToken + '[' + timeA;
timeB = '[</span>' + dtGlue + '<span class="ft">]' + timeToken + timeB;
}
if (dateWithTime) {
var timeMeta = input.match(/([,\s]+)(\d{1,2})(?:(.)(\d\d)(?:(.)(\d\d))?)?(?:\s?([ap]m))?$/im);
if (null != timeMeta) {
dtSeparator = (! /undefined/i.test(typeof(timeMeta[1])) ? timeMeta[1] : dtSeparator);
// adjust timeToken to num digits of input hours
timeToken = (! /undefined/i.test(typeof(timeMeta[2])) && 1 == timeMeta[2].length ? timeToken.replace(/hh/ig, 'h') : timeToken);
// adjust timeToken to remove min if input has one and there is a pm
timeToken = (trimZero && ! /undefined/i.test(typeof(timeMeta[7]))
&& (/undefined/i.test(typeof(timeMeta[4]))
|| '00' == timeMeta[4]) ? timeToken.replace(/.mm/ig, '') : timeToken);
// adjust timeToken to use seconds if input has them
timeToken = (! /undefined/i.test(typeof(timeMeta[5])) && 2 == timeMeta[5].length ? timeToken : timeToken.replace(/.ss/, ''));
// adjust timeToken to am/pm or AM/PM if input has it
timeToken = (! /undefined/i.test(typeof(timeMeta[7])) && 2 == timeMeta[7].length ? timeToken.replace(/A$/, (/[ap]m/.test(timeMeta[7]) ? 'a' : 'A')) : timeToken);
}
var inputTokens = dateToken + dtSeparator + (dateWithTime ? timeToken : 'HH:mm:ss');
var token_build = (/h+/i.test(timeToken) ? timeToken.replace(/^(h+).*/i, '[<span class="time-hr">]$1[</span>]') : '');
if (/m+/i.test(timeToken)) {
token_build += (! /undefined/i.test(typeof(timeMeta[3])) ? '[<span class="time-hr-min">]' + timeMeta[3] + '[</span>]' : '')
+ (! /undefined/i.test(typeof(timeMeta[4])) ? timeToken.replace(/.*?(m+).*/i, '[<span class="time-min">]$1[</span>]') : '');
if (/s+/i.test(timeToken)) {
token_build += (! /undefined/i.test(typeof(timeMeta[5])) ? '[<span class="time-min-sec">]' + timeMeta[5] + '[</span>]' : '')
+ (! /undefined/i.test(typeof(timeMeta[6])) ? timeToken.replace(/.*?(s+).*/i, '[<span class="time-sec">]$1[</span>]') : '');
}
}
timeToken = token_build + (! /undefined/i.test(typeof(timeMeta[7])) ? timeToken.replace(/.*?[\s0-9](a).*/i, '[<span class="time-am-pm">]$1[</span>]') : '');
if (! moment(input + (dateWithTime ? '' : dtSeparator + '00:00:00'), inputTokens).isValid())
return;
timeA = '</span>' + dtGlue + '<span class="ft">]' + timeToken + '[' + timeA;
timeB = '[</span>' + dtGlue + '<span class="ft">]' + timeToken + timeB;
}
moment.lang('en', {
calendar: {
lastDay:dateA + 'Yesterday' + timeA, sameDay:dateA + 'Today' + timeA, nextDay:dateA + 'Tomorrow' + timeA,
lastWeek:dateA + 'last] ddd' + timeB, nextWeek:dateA + 'on] ddd' + timeB,
sameElse:dateA + ']ddd, MMM D YYYY[' + timeA
},
relativeTime: {
future:'in %s', past:'%s ago', s:'seconds', m:'a minute', mm:'%d minutes', h:'an hour', hh:'%d hours',
d:'a day', dd:'%d days', M:'a month', MM:'%d months', y:'a year', yy:'%d years'
}
});
var inputTokens = dateTemplate + dtSeparator + (dateWithTime ? timeToken : 'HH:mm:ss');
var airdatetime = moment(input + (dateWithTime ? '' : dtSeparator + '00:00:00'), inputTokens),
airdate = airdatetime.clone().hour(0).minute(0).second(0).millisecond(0),
today = moment({}),
day = Math.abs(airdate.diff(today, 'days')),
week = Math.abs(weekdiff = airdate.diff(today, 'week')), isPast = weekdiff < 0,
titleThis = false, qTipTime = false,
result = (0 == week ? airdatetime.calendar() : '');
if (! moment(input + (dateWithTime ? '' : dtSeparator + '00:00:00'), inputTokens).isValid())
return;
if (/\bOn\b/i.test(result)) {
var fuzzer = false, weekday = today.day();
if (3 == weekday)
fuzzer = (5 <= day);
else if (4 == weekday || 5 == weekday)
fuzzer = (4 <= day);
else
fuzzer = (6 == day);
if (fuzzer)
result = result.replace(/\bOn\b/i, 'Next');
moment.lang('en', {
calendar: {
lastDay:dateA + 'Yesterday' + timeA, sameDay:dateA + 'Today' + timeA, nextDay:dateA + 'Tomorrow' + timeA,
lastWeek:dateA + 'last] ddd' + timeB, nextWeek:dateA + 'on] ddd' + timeB,
sameElse:dateA + ']ddd, MMM D YYYY[' + timeA
},
relativeTime: {
future:'in %s', past:'%s ago', s:'seconds', m:'a minute', mm:'%d minutes', h:'an hour', hh:'%d hours',
d:'a day', dd:'%d days', M:'a month', MM:'%d months', y:'a year', yy:'%d years'
}
});
} else if (! /\b((yester|to)day\b|tomo|last\b)/i.test(result)) {
if (14 > day)
result = airdate.from(today) + (dateWithTime ? dtGlue + airdatetime.format(timeToken) : '');
else if (4 > week) {
result = (isPast ? '' : 'in ') + (1 == week ? 'a' : week) + ' week' + (1 == week ? '' : 's') + (isPast ? ' ago' : '');
qTipTime = true;
} else {
result = airdate.from(today);
qTipTime = true;
var month = airdate.diff(today, 'month');
if (1 == parseInt(airdate.year() - today.year()))
result += (dtInline ? ' ' : '<br />') + '(Next Year)';
}
titleThis = true;
}
var airdatetime = moment(input + (dateWithTime ? '' : dtSeparator + '00:00:00'), inputTokens),
airdate = airdatetime.clone().hour(0).minute(0).second(0).millisecond(0),
today = moment({}),
day = Math.abs(airdate.diff(today, 'days')),
week = Math.abs(weekdiff = airdate.diff(today, 'week')), isPast = weekdiff < 0,
titleThis = false, qTipTime = false,
result = (0 == week ? airdatetime.calendar() : '');
var n = false; // disable for prod
$(this).html(result);
if (dateWithTime && /(yester|to)day/i.test(result))
$(this).find('.fd').attr('title',(n?'1) ':'') + moment.duration(airdatetime.diff(moment(),'seconds'),'seconds').humanize(true)).each(addQTip);
else if (dateWithTime)
$(this).find('.fd').attr('title',(n?'2) ':'') + airdate.from(today)).each(addQTip);
else if (! /today/i.test(result))
$(this).find('.fd').attr('title',(n?'3) ':'') + airdate.from(today)).each(addQTip);
else
titleThis = false;
if (/\bOn\b/i.test(result)) {
var fuzzer = false, weekday = today.day();
if (3 == weekday)
fuzzer = (5 <= day);
else if (4 == weekday || 5 == weekday)
fuzzer = (4 <= day);
else
fuzzer = (6 == day);
if (fuzzer)
result = result.replace(/\bOn\b/i, 'Next');
if (titleThis)
if (dateWithTime && qTipTime)
$(this).attr('title',(n?'4) ':'') + airdatetime.format(inputTokens)).each(addQTip);
else
$(this).attr('title',(n?'5) ':'') + airdate.format(dateToken)).each(addQTip);
else
if (dateWithTime && qTipTime)
$(this).find('.ft').attr('title',(n?'6) ':'') + airdatetime.format(inputTokens)).each(addQTip);
else
$(this).find('.ft').attr('title',(n?'7) ':'') + airdate.format(dateToken)).each(addQTip);
});
} else if (! /\b((yester|to)day\b|tomo|last\b)/i.test(result)) {
if (14 > day)
result = airdate.from(today) + (dateWithTime ? dtGlue + airdatetime.format(timeToken) : '');
else if (4 > week) {
result = (isPast ? '' : 'in ') + (1 == week ? 'a' : week) + ' week' + (1 == week ? '' : 's') + (isPast ? ' ago' : '');
qTipTime = true;
} else {
result = airdate.from(today);
qTipTime = true;
var month = airdate.diff(today, 'month');
if (1 == parseInt(airdate.year() - today.year()))
result += (dtInline ? ' ' : '<br />') + '(Next Year)';
}
titleThis = true;
}
var n = false; // disable for prod
$(this).html(result);
if (dateWithTime && /(yester|to)day/i.test(result))
$(this).find('.fd').attr('title',(n?'1) ':'') + moment.duration(airdatetime.diff(moment(),'seconds'),'seconds').humanize(true)).each(addQTip);
else if (dateWithTime)
$(this).find('.fd').attr('title',(n?'2) ':'') + airdate.from(today)).each(addQTip);
else if (! /today/i.test(result))
$(this).find('.fd').attr('title',(n?'3) ':'') + airdate.from(today)).each(addQTip);
else
titleThis = false;
if (titleThis)
if (dateWithTime && qTipTime)
$(this).attr('title',(n?'4) ':'') + airdatetime.format(inputTokens)).each(addQTip);
else
$(this).attr('title',(n?'5) ':'') + airdate.format(dateTemplate)).each(addQTip);
else
if (dateWithTime && qTipTime)
$(this).find('.ft').attr('title',(n?'6) ':'') + airdatetime.format(inputTokens)).each(addQTip);
else
$(this).find('.ft').attr('title',(n?'7) ':'') + airdate.format(dateTemplate)).each(addQTip);
});
}

File diff suppressed because one or more lines are too long

View file

@ -1,5 +1,5 @@
$(function () {
$('.plotInfo').each(function () {
$('.plotInfo, .plot-daybyday').each(function () {
var match = $(this).attr('id').match(/^plot_info_(\d+)_(\d+)_(\d+)$/);
$(this).qtip({
content: {

View file

@ -42,7 +42,7 @@ from sickbeard import searchBacklog, showUpdater, versionChecker, properFinder,
from sickbeard import helpers, db, exceptions, show_queue, search_queue, scheduler, show_name_helpers
from sickbeard import logger
from sickbeard import naming
from sickbeard import dailysearcher
from sickbeard import searchRecent
from sickbeard import scene_numbering, scene_exceptions, name_cache
from indexers.indexer_api import indexerApi
from indexers.indexer_exceptions import indexer_shownotfound, indexer_exception, indexer_error, indexer_episodenotfound, \
@ -58,7 +58,7 @@ CFG = None
CONFIG_FILE = None
# This is the version of the config we EXPECT to find
CONFIG_VERSION = 5
CONFIG_VERSION = 7
# Default encryption version (0 for None)
ENCRYPTION_VERSION = 0
@ -78,7 +78,7 @@ NO_RESIZE = False
# system events
events = None
dailySearchScheduler = None
recentSearchScheduler = None
backlogSearchScheduler = None
showUpdateScheduler = None
versionCheckScheduler = None
@ -196,19 +196,19 @@ CHECK_PROPERS_INTERVAL = None
ALLOW_HIGH_PRIORITY = False
AUTOPOSTPROCESSER_FREQUENCY = None
DAILYSEARCH_FREQUENCY = None
RECENTSEARCH_FREQUENCY = None
UPDATE_FREQUENCY = None
DAILYSEARCH_STARTUP = False
RECENTSEARCH_STARTUP = False
BACKLOG_FREQUENCY = None
BACKLOG_STARTUP = False
DEFAULT_AUTOPOSTPROCESSER_FREQUENCY = 10
DEFAULT_DAILYSEARCH_FREQUENCY = 40
DEFAULT_RECENTSEARCH_FREQUENCY = 40
DEFAULT_BACKLOG_FREQUENCY = 21
DEFAULT_UPDATE_FREQUENCY = 1
MIN_AUTOPOSTPROCESSER_FREQUENCY = 1
MIN_DAILYSEARCH_FREQUENCY = 10
MIN_RECENTSEARCH_FREQUENCY = 10
MIN_BACKLOG_FREQUENCY = 10
MIN_UPDATE_FREQUENCY = 1
@ -419,10 +419,10 @@ GUI_NAME = None
HOME_LAYOUT = None
HISTORY_LAYOUT = None
DISPLAY_SHOW_SPECIALS = False
COMING_EPS_LAYOUT = None
COMING_EPS_DISPLAY_PAUSED = False
COMING_EPS_SORT = None
COMING_EPS_MISSED_RANGE = None
EPISODE_VIEW_LAYOUT = None
EPISODE_VIEW_SORT = None
EPISODE_VIEW_DISPLAY_PAUSED = False
EPISODE_VIEW_MISSED_RANGE = None
FUZZY_DATING = False
TRIM_ZERO = False
DATE_PRESET = None
@ -459,7 +459,7 @@ TRAKT_API_KEY = 'abd806c54516240c76e4ebc9c5ccf394'
__INITIALIZED__ = False
def get_backlog_cycle_time():
cycletime = DAILYSEARCH_FREQUENCY * 2 + 7
cycletime = RECENTSEARCH_FREQUENCY * 2 + 7
return max([cycletime, 720])
def initialize(consoleLogging=True):
@ -477,7 +477,7 @@ def initialize(consoleLogging=True):
PLEX_SERVER_HOST, PLEX_HOST, PLEX_USERNAME, PLEX_PASSWORD, DEFAULT_BACKLOG_FREQUENCY, MIN_BACKLOG_FREQUENCY, BACKLOG_STARTUP, SKIP_REMOVED_FILES, \
showUpdateScheduler, __INITIALIZED__, LAUNCH_BROWSER, UPDATE_SHOWS_ON_START, TRASH_REMOVE_SHOW, TRASH_ROTATE_LOGS, HOME_SEARCH_FOCUS, SORT_ARTICLE, showList, loadingShowList, \
NEWZNAB_DATA, NZBS, NZBS_UID, NZBS_HASH, INDEXER_DEFAULT, INDEXER_TIMEOUT, USENET_RETENTION, TORRENT_DIR, \
QUALITY_DEFAULT, FLATTEN_FOLDERS_DEFAULT, SUBTITLES_DEFAULT, STATUS_DEFAULT, DAILYSEARCH_STARTUP, \
QUALITY_DEFAULT, FLATTEN_FOLDERS_DEFAULT, SUBTITLES_DEFAULT, STATUS_DEFAULT, RECENTSEARCH_STARTUP, \
GROWL_NOTIFY_ONSNATCH, GROWL_NOTIFY_ONDOWNLOAD, GROWL_NOTIFY_ONSUBTITLEDOWNLOAD, TWITTER_NOTIFY_ONSNATCH, TWITTER_NOTIFY_ONDOWNLOAD, TWITTER_NOTIFY_ONSUBTITLEDOWNLOAD, \
USE_GROWL, GROWL_HOST, GROWL_PASSWORD, USE_PROWL, PROWL_NOTIFY_ONSNATCH, PROWL_NOTIFY_ONDOWNLOAD, PROWL_NOTIFY_ONSUBTITLEDOWNLOAD, PROWL_API, PROWL_PRIORITY, PROG_DIR, \
USE_PYTIVO, PYTIVO_NOTIFY_ONSNATCH, PYTIVO_NOTIFY_ONDOWNLOAD, PYTIVO_NOTIFY_ONSUBTITLEDOWNLOAD, PYTIVO_UPDATE_LIBRARY, PYTIVO_HOST, PYTIVO_SHARE_NAME, PYTIVO_TIVO_NAME, \
@ -485,12 +485,12 @@ def initialize(consoleLogging=True):
USE_PUSHALOT, PUSHALOT_NOTIFY_ONSNATCH, PUSHALOT_NOTIFY_ONDOWNLOAD, PUSHALOT_NOTIFY_ONSUBTITLEDOWNLOAD, PUSHALOT_AUTHORIZATIONTOKEN, \
USE_PUSHBULLET, PUSHBULLET_NOTIFY_ONSNATCH, PUSHBULLET_NOTIFY_ONDOWNLOAD, PUSHBULLET_NOTIFY_ONSUBTITLEDOWNLOAD, PUSHBULLET_API, PUSHBULLET_DEVICE, \
versionCheckScheduler, VERSION_NOTIFY, AUTO_UPDATE, NOTIFY_ON_UPDATE, PROCESS_AUTOMATICALLY, UNPACK, CPU_PRESET, \
KEEP_PROCESSED_DIR, PROCESS_METHOD, TV_DOWNLOAD_DIR, MIN_DAILYSEARCH_FREQUENCY, DEFAULT_UPDATE_FREQUENCY, MIN_UPDATE_FREQUENCY, UPDATE_FREQUENCY, \
KEEP_PROCESSED_DIR, PROCESS_METHOD, TV_DOWNLOAD_DIR, MIN_RECENTSEARCH_FREQUENCY, DEFAULT_UPDATE_FREQUENCY, MIN_UPDATE_FREQUENCY, UPDATE_FREQUENCY, \
showQueueScheduler, searchQueueScheduler, ROOT_DIRS, CACHE_DIR, ACTUAL_CACHE_DIR, TIMEZONE_DISPLAY, \
NAMING_PATTERN, NAMING_MULTI_EP, NAMING_ANIME_MULTI_EP, NAMING_FORCE_FOLDERS, NAMING_ABD_PATTERN, NAMING_CUSTOM_ABD, NAMING_SPORTS_PATTERN, NAMING_CUSTOM_SPORTS, NAMING_ANIME_PATTERN, NAMING_CUSTOM_ANIME, NAMING_STRIP_YEAR, \
RENAME_EPISODES, AIRDATE_EPISODES, properFinderScheduler, PROVIDER_ORDER, autoPostProcesserScheduler, \
WOMBLE, OMGWTFNZBS, OMGWTFNZBS_USERNAME, OMGWTFNZBS_APIKEY, providerList, newznabProviderList, torrentRssProviderList, \
EXTRA_SCRIPTS, USE_TWITTER, TWITTER_USERNAME, TWITTER_PASSWORD, TWITTER_PREFIX, DAILYSEARCH_FREQUENCY, \
EXTRA_SCRIPTS, USE_TWITTER, TWITTER_USERNAME, TWITTER_PASSWORD, TWITTER_PREFIX, RECENTSEARCH_FREQUENCY, \
USE_BOXCAR, BOXCAR_USERNAME, BOXCAR_PASSWORD, BOXCAR_NOTIFY_ONDOWNLOAD, BOXCAR_NOTIFY_ONSUBTITLEDOWNLOAD, BOXCAR_NOTIFY_ONSNATCH, \
USE_BOXCAR2, BOXCAR2_ACCESSTOKEN, BOXCAR2_NOTIFY_ONDOWNLOAD, BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD, BOXCAR2_NOTIFY_ONSNATCH, \
USE_PUSHOVER, PUSHOVER_USERKEY, PUSHOVER_APIKEY, PUSHOVER_NOTIFY_ONDOWNLOAD, PUSHOVER_NOTIFY_ONSUBTITLEDOWNLOAD, PUSHOVER_NOTIFY_ONSNATCH, \
@ -498,8 +498,8 @@ def initialize(consoleLogging=True):
USE_SYNOLOGYNOTIFIER, SYNOLOGYNOTIFIER_NOTIFY_ONSNATCH, SYNOLOGYNOTIFIER_NOTIFY_ONDOWNLOAD, SYNOLOGYNOTIFIER_NOTIFY_ONSUBTITLEDOWNLOAD, \
USE_EMAIL, EMAIL_HOST, EMAIL_PORT, EMAIL_TLS, EMAIL_USER, EMAIL_PASSWORD, EMAIL_FROM, EMAIL_NOTIFY_ONSNATCH, EMAIL_NOTIFY_ONDOWNLOAD, EMAIL_NOTIFY_ONSUBTITLEDOWNLOAD, EMAIL_LIST, \
USE_LISTVIEW, METADATA_XBMC, METADATA_XBMC_12PLUS, METADATA_MEDIABROWSER, METADATA_PS3, metadata_provider_dict, \
NEWZBIN, NEWZBIN_USERNAME, NEWZBIN_PASSWORD, GIT_PATH, MOVE_ASSOCIATED_FILES, POSTPONE_IF_SYNC_FILES, dailySearchScheduler, NFO_RENAME, \
GUI_NAME, HOME_LAYOUT, HISTORY_LAYOUT, DISPLAY_SHOW_SPECIALS, COMING_EPS_LAYOUT, COMING_EPS_SORT, COMING_EPS_DISPLAY_PAUSED, COMING_EPS_MISSED_RANGE, FUZZY_DATING, TRIM_ZERO, DATE_PRESET, TIME_PRESET, TIME_PRESET_W_SECONDS, THEME_NAME, \
NEWZBIN, NEWZBIN_USERNAME, NEWZBIN_PASSWORD, GIT_PATH, MOVE_ASSOCIATED_FILES, POSTPONE_IF_SYNC_FILES, recentSearchScheduler, NFO_RENAME, \
GUI_NAME, HOME_LAYOUT, HISTORY_LAYOUT, DISPLAY_SHOW_SPECIALS, EPISODE_VIEW_LAYOUT, EPISODE_VIEW_SORT, EPISODE_VIEW_DISPLAY_PAUSED, EPISODE_VIEW_MISSED_RANGE, FUZZY_DATING, TRIM_ZERO, DATE_PRESET, TIME_PRESET, TIME_PRESET_W_SECONDS, THEME_NAME, \
POSTER_SORTBY, POSTER_SORTDIR, \
METADATA_WDTV, METADATA_TIVO, METADATA_MEDE8ER, IGNORE_WORDS, REQUIRE_WORDS, CALENDAR_UNPROTECTED, CREATE_MISSING_SHOW_DIRS, \
ADD_SHOWS_WO_DIR, USE_SUBTITLES, SUBTITLES_LANGUAGES, SUBTITLES_DIR, SUBTITLES_SERVICES_LIST, SUBTITLES_SERVICES_ENABLED, SUBTITLES_HISTORY, SUBTITLES_FINDER_FREQUENCY, subtitlesFinderScheduler, \
@ -674,7 +674,7 @@ def initialize(consoleLogging=True):
ALLOW_HIGH_PRIORITY = bool(check_setting_int(CFG, 'General', 'allow_high_priority', 1))
DAILYSEARCH_STARTUP = bool(check_setting_int(CFG, 'General', 'dailysearch_startup', 1))
RECENTSEARCH_STARTUP = bool(check_setting_int(CFG, 'General', 'recentsearch_startup', 1))
BACKLOG_STARTUP = bool(check_setting_int(CFG, 'General', 'backlog_startup', 1))
SKIP_REMOVED_FILES = bool(check_setting_int(CFG, 'General', 'skip_removed_files', 0))
@ -685,10 +685,10 @@ def initialize(consoleLogging=True):
if AUTOPOSTPROCESSER_FREQUENCY < MIN_AUTOPOSTPROCESSER_FREQUENCY:
AUTOPOSTPROCESSER_FREQUENCY = MIN_AUTOPOSTPROCESSER_FREQUENCY
DAILYSEARCH_FREQUENCY = check_setting_int(CFG, 'General', 'dailysearch_frequency',
DEFAULT_DAILYSEARCH_FREQUENCY)
if DAILYSEARCH_FREQUENCY < MIN_DAILYSEARCH_FREQUENCY:
DAILYSEARCH_FREQUENCY = MIN_DAILYSEARCH_FREQUENCY
RECENTSEARCH_FREQUENCY = check_setting_int(CFG, 'General', 'recentsearch_frequency',
DEFAULT_RECENTSEARCH_FREQUENCY)
if RECENTSEARCH_FREQUENCY < MIN_RECENTSEARCH_FREQUENCY:
RECENTSEARCH_FREQUENCY = MIN_RECENTSEARCH_FREQUENCY
MIN_BACKLOG_FREQUENCY = get_backlog_cycle_time()
BACKLOG_FREQUENCY = check_setting_int(CFG, 'General', 'backlog_frequency', DEFAULT_BACKLOG_FREQUENCY)
@ -944,10 +944,10 @@ def initialize(consoleLogging=True):
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))
COMING_EPS_LAYOUT = check_setting_str(CFG, 'GUI', 'coming_eps_layout', 'banner')
COMING_EPS_DISPLAY_PAUSED = bool(check_setting_int(CFG, 'GUI', 'coming_eps_display_paused', 0))
COMING_EPS_SORT = check_setting_str(CFG, 'GUI', 'coming_eps_sort', 'date')
COMING_EPS_MISSED_RANGE = check_setting_int(CFG, 'GUI', 'coming_eps_missed_range', 7)
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)
FUZZY_DATING = bool(check_setting_int(CFG, 'GUI', 'fuzzy_dating', 0))
TRIM_ZERO = bool(check_setting_int(CFG, 'GUI', 'trim_zero', 0))
DATE_PRESET = check_setting_str(CFG, 'GUI', 'date_preset', '%x')
@ -1022,11 +1022,10 @@ def initialize(consoleLogging=True):
curTorrentProvider.getID() + '_search_fallback',
0))
if hasattr(curTorrentProvider, 'enable_daily'):
curTorrentProvider.enable_daily = bool(check_setting_int(CFG, curTorrentProvider.getID().upper(),
curTorrentProvider.getID() + '_enable_daily',
1))
if hasattr(curTorrentProvider, 'enable_recentsearch'):
curTorrentProvider.enable_recentsearch = bool(check_setting_int(CFG, curTorrentProvider.getID().upper(),
curTorrentProvider.getID() +
'_enable_recentsearch', 1))
if hasattr(curTorrentProvider, 'enable_backlog'):
curTorrentProvider.enable_backlog = bool(check_setting_int(CFG, curTorrentProvider.getID().upper(),
curTorrentProvider.getID() + '_enable_backlog',
@ -1050,11 +1049,10 @@ def initialize(consoleLogging=True):
curNzbProvider.search_fallback = bool(check_setting_int(CFG, curNzbProvider.getID().upper(),
curNzbProvider.getID() + '_search_fallback',
0))
if hasattr(curNzbProvider, 'enable_daily'):
curNzbProvider.enable_daily = bool(check_setting_int(CFG, curNzbProvider.getID().upper(),
curNzbProvider.getID() + '_enable_daily',
1))
if hasattr(curNzbProvider, 'enable_recentsearch'):
curNzbProvider.enable_recentsearch = bool(check_setting_int(CFG, curNzbProvider.getID().upper(),
curNzbProvider.getID() + '_enable_recentsearch',
1))
if hasattr(curNzbProvider, 'enable_backlog'):
curNzbProvider.enable_backlog = bool(check_setting_int(CFG, curNzbProvider.getID().upper(),
curNzbProvider.getID() + '_enable_backlog',
@ -1124,11 +1122,11 @@ def initialize(consoleLogging=True):
cycleTime=datetime.timedelta(seconds=3),
threadName="SEARCHQUEUE")
update_interval = datetime.timedelta(minutes=DAILYSEARCH_FREQUENCY)
dailySearchScheduler = scheduler.Scheduler(dailysearcher.DailySearcher(),
update_interval = datetime.timedelta(minutes=RECENTSEARCH_FREQUENCY)
recentSearchScheduler = scheduler.Scheduler(searchRecent.RecentSearcher(),
cycleTime=update_interval,
threadName="DAILYSEARCHER",
run_delay=update_now if DAILYSEARCH_STARTUP
threadName="RECENTSEARCHER",
run_delay=update_now if RECENTSEARCH_STARTUP
else update_interval)
update_interval = datetime.timedelta(minutes=BACKLOG_FREQUENCY)
@ -1181,15 +1179,15 @@ def start():
showUpdateScheduler, versionCheckScheduler, showQueueScheduler, \
properFinderScheduler, autoPostProcesserScheduler, searchQueueScheduler, \
subtitlesFinderScheduler, USE_SUBTITLES, traktCheckerScheduler, \
dailySearchScheduler, events, started
recentSearchScheduler, events, started
with INIT_LOCK:
if __INITIALIZED__:
# start sysetm events queue
events.start()
# start the daily search scheduler
dailySearchScheduler.start()
# start the recent search scheduler
recentSearchScheduler.start()
# start the backlog scheduler
backlogSearchScheduler.start()
@ -1230,7 +1228,7 @@ def halt():
showUpdateScheduler, versionCheckScheduler, showQueueScheduler, \
properFinderScheduler, autoPostProcesserScheduler, searchQueueScheduler, \
subtitlesFinderScheduler, traktCheckerScheduler, \
dailySearchScheduler, events, started
recentSearchScheduler, events, started
with INIT_LOCK:
@ -1245,10 +1243,10 @@ def halt():
except:
pass
dailySearchScheduler.stop.set()
logger.log(u"Waiting for the DAILYSEARCH thread to exit")
recentSearchScheduler.stop.set()
logger.log(u"Waiting for the RECENTSEARCH thread to exit")
try:
dailySearchScheduler.join(10)
recentSearchScheduler.join(10)
except:
pass
@ -1397,13 +1395,13 @@ def save_config():
new_config['General']['torrent_method'] = TORRENT_METHOD
new_config['General']['usenet_retention'] = int(USENET_RETENTION)
new_config['General']['autopostprocesser_frequency'] = int(AUTOPOSTPROCESSER_FREQUENCY)
new_config['General']['dailysearch_frequency'] = int(DAILYSEARCH_FREQUENCY)
new_config['General']['recentsearch_frequency'] = int(RECENTSEARCH_FREQUENCY)
new_config['General']['backlog_frequency'] = int(BACKLOG_FREQUENCY)
new_config['General']['update_frequency'] = int(UPDATE_FREQUENCY)
new_config['General']['download_propers'] = int(DOWNLOAD_PROPERS)
new_config['General']['check_propers_interval'] = CHECK_PROPERS_INTERVAL
new_config['General']['allow_high_priority'] = int(ALLOW_HIGH_PRIORITY)
new_config['General']['dailysearch_startup'] = int(DAILYSEARCH_STARTUP)
new_config['General']['recentsearch_startup'] = int(RECENTSEARCH_STARTUP)
new_config['General']['backlog_startup'] = int(BACKLOG_STARTUP)
new_config['General']['skip_removed_files'] = int(SKIP_REMOVED_FILES)
new_config['General']['quality_default'] = int(QUALITY_DEFAULT)
@ -1526,9 +1524,9 @@ def save_config():
if hasattr(curTorrentProvider, 'search_fallback'):
new_config[curTorrentProvider.getID().upper()][curTorrentProvider.getID() + '_search_fallback'] = int(
curTorrentProvider.search_fallback)
if hasattr(curTorrentProvider, 'enable_daily'):
new_config[curTorrentProvider.getID().upper()][curTorrentProvider.getID() + '_enable_daily'] = int(
curTorrentProvider.enable_daily)
if hasattr(curTorrentProvider, 'enable_recentsearch'):
new_config[curTorrentProvider.getID().upper()][curTorrentProvider.getID() + '_enable_recentsearch'] = int(
curTorrentProvider.enable_recentsearch)
if hasattr(curTorrentProvider, 'enable_backlog'):
new_config[curTorrentProvider.getID().upper()][curTorrentProvider.getID() + '_enable_backlog'] = int(
curTorrentProvider.enable_backlog)
@ -1550,9 +1548,9 @@ def save_config():
if hasattr(curNzbProvider, 'search_fallback'):
new_config[curNzbProvider.getID().upper()][curNzbProvider.getID() + '_search_fallback'] = int(
curNzbProvider.search_fallback)
if hasattr(curNzbProvider, 'enable_daily'):
new_config[curNzbProvider.getID().upper()][curNzbProvider.getID() + '_enable_daily'] = int(
curNzbProvider.enable_daily)
if hasattr(curNzbProvider, 'enable_recentsearch'):
new_config[curNzbProvider.getID().upper()][curNzbProvider.getID() + '_enable_recentsearch'] = int(
curNzbProvider.enable_recentsearch)
if hasattr(curNzbProvider, 'enable_backlog'):
new_config[curNzbProvider.getID().upper()][curNzbProvider.getID() + '_enable_backlog'] = int(
curNzbProvider.enable_backlog)
@ -1765,10 +1763,10 @@ def save_config():
new_config['GUI']['home_layout'] = HOME_LAYOUT
new_config['GUI']['history_layout'] = HISTORY_LAYOUT
new_config['GUI']['display_show_specials'] = int(DISPLAY_SHOW_SPECIALS)
new_config['GUI']['coming_eps_layout'] = COMING_EPS_LAYOUT
new_config['GUI']['coming_eps_display_paused'] = int(COMING_EPS_DISPLAY_PAUSED)
new_config['GUI']['coming_eps_sort'] = COMING_EPS_SORT
new_config['GUI']['coming_eps_missed_range'] = int(COMING_EPS_MISSED_RANGE)
new_config['GUI']['episode_view_layout'] = EPISODE_VIEW_LAYOUT
new_config['GUI']['episode_view_display_paused'] = int(EPISODE_VIEW_DISPLAY_PAUSED)
new_config['GUI']['episode_view_sort'] = EPISODE_VIEW_SORT
new_config['GUI']['episode_view_missed_range'] = int(EPISODE_VIEW_MISSED_RANGE)
new_config['GUI']['fuzzy_dating'] = int(FUZZY_DATING)
new_config['GUI']['trim_zero'] = int(TRIM_ZERO)
new_config['GUI']['date_preset'] = DATE_PRESET

View file

@ -27,6 +27,8 @@ from sickbeard import helpers
from sickbeard import logger
from sickbeard import naming
from sickbeard import db
from sickbeard import providers
from sickbeard.providers.generic import GenericProvider
naming_ep_type = ("%(seasonnumber)dx%(episodenumber)02d",
"s%(seasonnumber)02de%(episodenumber)02d",
@ -155,13 +157,13 @@ def change_AUTOPOSTPROCESSER_FREQUENCY(freq):
sickbeard.autoPostProcesserScheduler.cycleTime = datetime.timedelta(minutes=sickbeard.AUTOPOSTPROCESSER_FREQUENCY)
def change_DAILYSEARCH_FREQUENCY(freq):
sickbeard.DAILYSEARCH_FREQUENCY = to_int(freq, default=sickbeard.DEFAULT_DAILYSEARCH_FREQUENCY)
def change_RECENTSEARCH_FREQUENCY(freq):
sickbeard.RECENTSEARCH_FREQUENCY = to_int(freq, default=sickbeard.DEFAULT_RECENTSEARCH_FREQUENCY)
if sickbeard.DAILYSEARCH_FREQUENCY < sickbeard.MIN_DAILYSEARCH_FREQUENCY:
sickbeard.DAILYSEARCH_FREQUENCY = sickbeard.MIN_DAILYSEARCH_FREQUENCY
if sickbeard.RECENTSEARCH_FREQUENCY < sickbeard.MIN_RECENTSEARCH_FREQUENCY:
sickbeard.RECENTSEARCH_FREQUENCY = sickbeard.MIN_RECENTSEARCH_FREQUENCY
sickbeard.dailySearchScheduler.cycleTime = datetime.timedelta(minutes=sickbeard.DAILYSEARCH_FREQUENCY)
sickbeard.recentSearchScheduler.cycleTime = datetime.timedelta(minutes=sickbeard.RECENTSEARCH_FREQUENCY)
def change_BACKLOG_FREQUENCY(freq):
sickbeard.BACKLOG_FREQUENCY = to_int(freq, default=sickbeard.DEFAULT_BACKLOG_FREQUENCY)
@ -445,8 +447,10 @@ class ConfigMigrator():
2: 'Sync backup number with version number',
3: 'Rename omgwtfnzb variables',
4: 'Add newznab catIDs',
5: 'Metadata update'
}
5: 'Metadata update',
6: 'Rename daily search to recent search',
7: 'Rename coming episodes to episode view'
}
def migrate_config(self):
"""
@ -712,3 +716,27 @@ class ConfigMigrator():
sickbeard.METADATA_WDTV = _migrate_metadata(metadata_wdtv, 'WDTV', use_banner)
sickbeard.METADATA_TIVO = _migrate_metadata(metadata_tivo, 'TIVO', use_banner)
sickbeard.METADATA_MEDE8ER = _migrate_metadata(metadata_mede8er, 'Mede8er', use_banner)
# Migration v6: Rename daily search to recent search
def _migrate_v6(self):
sickbeard.RECENTSEARCH_FREQUENCY = check_setting_int(self.config_obj, 'General', 'dailysearch_frequency',
sickbeard.DEFAULT_RECENTSEARCH_FREQUENCY)
sickbeard.RECENTSEARCH_STARTUP = bool(check_setting_int(self.config_obj, 'General', 'dailysearch_startup', 1))
if sickbeard.RECENTSEARCH_FREQUENCY < sickbeard.MIN_RECENTSEARCH_FREQUENCY:
sickbeard.RECENTSEARCH_FREQUENCY = sickbeard.MIN_RECENTSEARCH_FREQUENCY
for curProvider in providers.sortedProviderList():
if hasattr(curProvider, 'enable_recentsearch'):
curProvider.enable_recentsearch = bool(check_setting_int(self.config_obj, curProvider.getID().upper(),
curProvider.getID() + '_enable_dailysearch', 1))
def _migrate_v7(self):
sickbeard.EPISODE_VIEW_LAYOUT = check_setting_str(self.config_obj, 'GUI', 'coming_eps_layout', 'banner')
sickbeard.EPISODE_VIEW_SORT = check_setting_str(self.config_obj, 'GUI', 'coming_eps_sort', 'time')
if 'date' == sickbeard.EPISODE_VIEW_SORT:
sickbeard.EPISODE_VIEW_SORT = 'time'
sickbeard.EPISODE_VIEW_DISPLAY_PAUSED = bool(check_setting_int(self.config_obj, 'GUI', 'coming_eps_display_paused', 0))
sickbeard.EPISODE_VIEW_MISSED_RANGE = check_setting_int(self.config_obj, 'GUI', 'coming_eps_missed_range', 7)

View file

@ -92,4 +92,27 @@ class GitHub(object):
access_API = self._access_API(
['repos', self.github_repo_user, self.github_repo, 'branches'],
params={'per_page': 100})
return access_API
return access_API
def pull_requests(self):
access_API = self._access_API(
['repos', self.github_repo_user, self.github_repo, 'pulls'],
params={'per_page': 100})
pull = []
for x in access_API:
try:
pull.append(PullRequest(x['head']['ref'], x['number']))
except:
continue
return pull
class PullRequest(object):
def __init__(self, ref, number):
self.ref = ref
self.number = number
def __repr__(self):
return '%s: %s' % (self.number, self.ref)
def fetch_name(self):
return 'pull/%s/head:pull/%s/%s' % (self.number, self.number, self.ref)

View file

@ -26,8 +26,9 @@ import os.path
import regexes
import sickbeard
from sickbeard import logger, helpers, scene_numbering, common, exceptions, scene_exceptions, encodingKludge as ek, db
from sickbeard import logger, helpers, scene_numbering, common, scene_exceptions, encodingKludge as ek, db
from dateutil import parser
from sickbeard.exceptions import ex
class NameParser(object):
@ -36,13 +37,14 @@ class NameParser(object):
ANIME_REGEX = 2
def __init__(self, file_name=True, showObj=None, tryIndexers=False, convert=False,
naming_pattern=False):
naming_pattern=False, testing=False):
self.file_name = file_name
self.showObj = showObj
self.tryIndexers = tryIndexers
self.convert = convert
self.naming_pattern = naming_pattern
self.testing = testing
if self.showObj and not self.showObj.is_anime:
self._compile_regexes(self.NORMAL_REGEX)
@ -78,7 +80,7 @@ class NameParser(object):
def _compile_regexes(self, regexMode):
if regexMode == self.ANIME_REGEX:
logger.log(u"Using ANIME regexs", logger.DEBUG)
uncompiled_regex = [regexes.anime_regexes, regexes.normal_regexes]
uncompiled_regex = [regexes.anime_regexes]
elif regexMode == self.NORMAL_REGEX:
logger.log(u"Using NORMAL regexs", logger.DEBUG)
uncompiled_regex = [regexes.normal_regexes]
@ -86,7 +88,8 @@ class NameParser(object):
logger.log(u"Using ALL regexes", logger.DEBUG)
uncompiled_regex = [regexes.normal_regexes, regexes.anime_regexes]
self.compiled_regexes = []
self.compiled_regexes = {0: [], 1: []}
index = 0
for regexItem in uncompiled_regex:
for cur_pattern_num, (cur_pattern_name, cur_pattern) in enumerate(regexItem):
try:
@ -94,7 +97,8 @@ class NameParser(object):
except re.error, errormsg:
logger.log(u"WARNING: Invalid episode_pattern, %s. %s" % (errormsg, cur_pattern))
else:
self.compiled_regexes.append((cur_pattern_num, cur_pattern_name, cur_regex))
self.compiled_regexes[index].append([cur_pattern_num, cur_pattern_name, cur_regex])
index += 1
def _parse_string(self, name):
if not name:
@ -102,233 +106,242 @@ class NameParser(object):
matches = []
bestResult = None
for regex in self.compiled_regexes:
for (cur_regex_num, cur_regex_name, cur_regex) in self.compiled_regexes[regex]:
match = cur_regex.match(name)
for (cur_regex_num, cur_regex_name, cur_regex) in self.compiled_regexes:
match = cur_regex.match(name)
if not match:
continue
result = ParseResult(name)
result.which_regex = [cur_regex_name]
result.score = 0 - cur_regex_num
named_groups = match.groupdict().keys()
if 'series_name' in named_groups:
result.series_name = match.group('series_name')
if result.series_name:
result.series_name = self.clean_series_name(result.series_name)
result.score += 1
if 'series_num' in named_groups and match.group('series_num'):
result.score += 1
if 'season_num' in named_groups:
tmp_season = int(match.group('season_num'))
if cur_regex_name == 'bare' and tmp_season in (19, 20):
continue
result.season_number = tmp_season
result.score += 1
if 'ep_num' in named_groups:
ep_num = self._convert_number(match.group('ep_num'))
if 'extra_ep_num' in named_groups and match.group('extra_ep_num'):
result.episode_numbers = range(ep_num, self._convert_number(match.group('extra_ep_num')) + 1)
result.score += 1
else:
result.episode_numbers = [ep_num]
result.score += 1
if 'ep_ab_num' in named_groups:
ep_ab_num = self._convert_number(match.group('ep_ab_num'))
if 'extra_ab_ep_num' in named_groups and match.group('extra_ab_ep_num'):
result.ab_episode_numbers = range(ep_ab_num,
self._convert_number(match.group('extra_ab_ep_num')) + 1)
result.score += 1
else:
result.ab_episode_numbers = [ep_ab_num]
result.score += 1
if 'air_date' in named_groups:
air_date = match.group('air_date')
try:
result.air_date = parser.parse(air_date, fuzzy=True).date()
result.score += 1
except:
if not match:
continue
if 'extra_info' in named_groups:
tmp_extra_info = match.group('extra_info')
result = ParseResult(name)
result.which_regex = [cur_regex_name]
result.score = 0 - cur_regex_num
# Show.S04.Special or Show.S05.Part.2.Extras is almost certainly not every episode in the season
if tmp_extra_info and cur_regex_name == 'season_only' and re.search(
r'([. _-]|^)(special|extra)s?\w*([. _-]|$)', tmp_extra_info, re.I):
continue
result.extra_info = tmp_extra_info
result.score += 1
named_groups = match.groupdict().keys()
if 'release_group' in named_groups:
result.release_group = match.group('release_group')
result.score += 1
if 'series_name' in named_groups:
result.series_name = match.group('series_name')
if result.series_name:
result.series_name = self.clean_series_name(result.series_name)
result.score += 1
if 'version' in named_groups:
# assigns version to anime file if detected using anime regex. Non-anime regex receives -1
version = match.group('version')
if version:
result.version = version
else:
result.version = 1
else:
result.version = -1
if 'series_num' in named_groups and match.group('series_num'):
result.score += 1
matches.append(result)
if 'season_num' in named_groups:
tmp_season = int(match.group('season_num'))
if cur_regex_name == 'bare' and tmp_season in (19, 20):
continue
result.season_number = tmp_season
result.score += 1
if len(matches):
# pick best match with highest score based on placement
bestResult = max(sorted(matches, reverse=True, key=lambda x: x.which_regex), key=lambda x: x.score)
if 'ep_num' in named_groups:
ep_num = self._convert_number(match.group('ep_num'))
if 'extra_ep_num' in named_groups and match.group('extra_ep_num'):
result.episode_numbers = range(ep_num, self._convert_number(match.group('extra_ep_num')) + 1)
result.score += 1
else:
result.episode_numbers = [ep_num]
result.score += 1
show = None
if not self.naming_pattern:
# try and create a show object for this result
show = helpers.get_show(bestResult.series_name, self.tryIndexers)
if 'ep_ab_num' in named_groups:
ep_ab_num = self._convert_number(match.group('ep_ab_num'))
if 'extra_ab_ep_num' in named_groups and match.group('extra_ab_ep_num'):
result.ab_episode_numbers = range(ep_ab_num,
self._convert_number(match.group('extra_ab_ep_num')) + 1)
result.score += 1
else:
result.ab_episode_numbers = [ep_ab_num]
result.score += 1
# confirm passed in show object indexer id matches result show object indexer id
if show:
if self.showObj and show.indexerid != self.showObj.indexerid:
show = None
bestResult.show = show
elif not show and self.showObj:
bestResult.show = self.showObj
# if this is a naming pattern test or result doesn't have a show object then return best result
if not bestResult.show or self.naming_pattern:
return bestResult
# get quality
bestResult.quality = common.Quality.nameQuality(name, bestResult.show.is_anime)
new_episode_numbers = []
new_season_numbers = []
new_absolute_numbers = []
# if we have an air-by-date show then get the real season/episode numbers
if bestResult.is_air_by_date:
airdate = bestResult.air_date.toordinal()
myDB = db.DBConnection()
sql_result = myDB.select(
"SELECT season, episode FROM tv_episodes WHERE showid = ? and indexer = ? and airdate = ?",
[bestResult.show.indexerid, bestResult.show.indexer, airdate])
season_number = None
episode_numbers = []
if sql_result:
season_number = int(sql_result[0][0])
episode_numbers = [int(sql_result[0][1])]
if not season_number or not len(episode_numbers):
if 'air_year' in named_groups and 'air_month' in named_groups and 'air_day' in named_groups:
year = int(match.group('air_year'))
month = int(match.group('air_month'))
day = int(match.group('air_day'))
# make an attempt to detect YYYY-DD-MM formats
if month > 12:
tmp_month = month
month = day
day = tmp_month
try:
lINDEXER_API_PARMS = sickbeard.indexerApi(bestResult.show.indexer).api_params.copy()
result.air_date = datetime.date(year, month, day)
except ValueError, e:
raise InvalidNameException(ex(e))
if bestResult.show.lang:
lINDEXER_API_PARMS['language'] = bestResult.show.lang
if 'extra_info' in named_groups:
tmp_extra_info = match.group('extra_info')
t = sickbeard.indexerApi(bestResult.show.indexer).indexer(**lINDEXER_API_PARMS)
# Show.S04.Special or Show.S05.Part.2.Extras is almost certainly not every episode in the season
if tmp_extra_info and cur_regex_name == 'season_only' and re.search(
r'([. _-]|^)(special|extra)s?\w*([. _-]|$)', tmp_extra_info, re.I):
continue
result.extra_info = tmp_extra_info
result.score += 1
epObj = t[bestResult.show.indexerid].airedOn(bestResult.air_date)[0]
if 'release_group' in named_groups:
result.release_group = match.group('release_group')
result.score += 1
season_number = int(epObj["seasonnumber"])
episode_numbers = [int(epObj["episodenumber"])]
except sickbeard.indexer_episodenotfound:
logger.log(u"Unable to find episode with date " + str(bestResult.air_date) + " for show " + bestResult.show.name + ", skipping", logger.WARNING)
episode_numbers = []
except sickbeard.indexer_error, e:
logger.log(u"Unable to contact " + sickbeard.indexerApi(bestResult.show.indexer).name + ": " + ex(e), logger.WARNING)
episode_numbers = []
if 'version' in named_groups:
# assigns version to anime file if detected using anime regex. Non-anime regex receives -1
version = match.group('version')
if version:
result.version = version
else:
result.version = 1
else:
result.version = -1
for epNo in episode_numbers:
s = season_number
e = epNo
matches.append(result)
if self.convert:
(s, e) = scene_numbering.get_indexer_numbering(bestResult.show.indexerid,
bestResult.show.indexer,
season_number,
epNo)
new_episode_numbers.append(e)
new_season_numbers.append(s)
if len(matches):
# pick best match with highest score based on placement
bestResult = max(sorted(matches, reverse=True, key=lambda x: x.which_regex), key=lambda x: x.score)
elif bestResult.show.is_anime and len(bestResult.ab_episode_numbers):
scene_season = scene_exceptions.get_scene_exception_by_name(bestResult.series_name)[1]
for epAbsNo in bestResult.ab_episode_numbers:
a = epAbsNo
show = None
if not self.naming_pattern:
# try and create a show object for this result
show = helpers.get_show(bestResult.series_name, self.tryIndexers)
if self.convert:
a = scene_numbering.get_indexer_absolute_numbering(bestResult.show.indexerid,
bestResult.show.indexer, epAbsNo,
True, scene_season)
# confirm passed in show object indexer id matches result show object indexer id
if show and not self.testing:
if self.showObj and show.indexerid != self.showObj.indexerid:
show = None
bestResult.show = show
elif not show and self.showObj:
bestResult.show = self.showObj
(s, e) = helpers.get_all_episodes_from_absolute_number(bestResult.show, [a])
if bestResult.show and bestResult.show.is_anime and len(self.compiled_regexes[1]) > 1 and regex != 1:
continue
new_absolute_numbers.append(a)
new_episode_numbers.extend(e)
new_season_numbers.append(s)
# if this is a naming pattern test or result doesn't have a show object then return best result
if not bestResult.show or self.naming_pattern:
return bestResult
elif bestResult.season_number and len(bestResult.episode_numbers):
for epNo in bestResult.episode_numbers:
s = bestResult.season_number
e = epNo
# get quality
bestResult.quality = common.Quality.nameQuality(name, bestResult.show.is_anime)
if self.convert:
(s, e) = scene_numbering.get_indexer_numbering(bestResult.show.indexerid,
bestResult.show.indexer,
bestResult.season_number,
epNo)
if bestResult.show.is_anime:
a = helpers.get_absolute_number_from_season_and_episode(bestResult.show, s, e)
if a:
new_absolute_numbers.append(a)
new_episode_numbers = []
new_season_numbers = []
new_absolute_numbers = []
new_episode_numbers.append(e)
new_season_numbers.append(s)
# if we have an air-by-date show then get the real season/episode numbers
if bestResult.is_air_by_date:
airdate = bestResult.air_date.toordinal()
myDB = db.DBConnection()
sql_result = myDB.select(
"SELECT season, episode FROM tv_episodes WHERE showid = ? and indexer = ? and airdate = ?",
[bestResult.show.indexerid, bestResult.show.indexer, airdate])
# need to do a quick sanity check heregex. It's possible that we now have episodes
# from more than one season (by tvdb numbering), and this is just too much
# for sickbeard, so we'd need to flag it.
new_season_numbers = list(set(new_season_numbers)) # remove duplicates
if len(new_season_numbers) > 1:
raise InvalidNameException("Scene numbering results episodes from "
"seasons %s, (i.e. more than one) and "
"SickGear does not support this. "
"Sorry." % (str(new_season_numbers)))
season_number = None
episode_numbers = []
# I guess it's possible that we'd have duplicate episodes too, so lets
# eliminate them
new_episode_numbers = list(set(new_episode_numbers))
new_episode_numbers.sort()
if sql_result:
season_number = int(sql_result[0][0])
episode_numbers = [int(sql_result[0][1])]
# maybe even duplicate absolute numbers so why not do them as well
new_absolute_numbers = list(set(new_absolute_numbers))
new_absolute_numbers.sort()
if not season_number or not len(episode_numbers):
try:
lINDEXER_API_PARMS = sickbeard.indexerApi(bestResult.show.indexer).api_params.copy()
if len(new_absolute_numbers):
bestResult.ab_episode_numbers = new_absolute_numbers
if bestResult.show.lang:
lINDEXER_API_PARMS['language'] = bestResult.show.lang
if len(new_season_numbers) and len(new_episode_numbers):
bestResult.episode_numbers = new_episode_numbers
bestResult.season_number = new_season_numbers[0]
t = sickbeard.indexerApi(bestResult.show.indexer).indexer(**lINDEXER_API_PARMS)
if self.convert:
logger.log(
u"Converted parsed result " + bestResult.original_name + " into " + str(bestResult).decode('utf-8',
'xmlcharrefreplace'),
logger.DEBUG)
epObj = t[bestResult.show.indexerid].airedOn(bestResult.air_date)[0]
# CPU sleep
time.sleep(0.02)
season_number = int(epObj["seasonnumber"])
episode_numbers = [int(epObj["episodenumber"])]
except sickbeard.indexer_episodenotfound:
logger.log(u"Unable to find episode with date " + str(bestResult.air_date) + " for show " + bestResult.show.name + ", skipping", logger.WARNING)
episode_numbers = []
except sickbeard.indexer_error, e:
logger.log(u"Unable to contact " + sickbeard.indexerApi(bestResult.show.indexer).name + ": " + ex(e), logger.WARNING)
episode_numbers = []
return bestResult
for epNo in episode_numbers:
s = season_number
e = epNo
if self.convert:
(s, e) = scene_numbering.get_indexer_numbering(bestResult.show.indexerid,
bestResult.show.indexer,
season_number,
epNo)
new_episode_numbers.append(e)
new_season_numbers.append(s)
elif bestResult.show.is_anime and len(bestResult.ab_episode_numbers) and not self.testing:
scene_season = scene_exceptions.get_scene_exception_by_name(bestResult.series_name)[1]
for epAbsNo in bestResult.ab_episode_numbers:
a = epAbsNo
if self.convert:
a = scene_numbering.get_indexer_absolute_numbering(bestResult.show.indexerid,
bestResult.show.indexer, epAbsNo,
True, scene_season)
(s, e) = helpers.get_all_episodes_from_absolute_number(bestResult.show, [a])
new_absolute_numbers.append(a)
new_episode_numbers.extend(e)
new_season_numbers.append(s)
elif bestResult.season_number and len(bestResult.episode_numbers) and not self.testing:
for epNo in bestResult.episode_numbers:
s = bestResult.season_number
e = epNo
if self.convert:
(s, e) = scene_numbering.get_indexer_numbering(bestResult.show.indexerid,
bestResult.show.indexer,
bestResult.season_number,
epNo)
if bestResult.show.is_anime:
a = helpers.get_absolute_number_from_season_and_episode(bestResult.show, s, e)
if a:
new_absolute_numbers.append(a)
new_episode_numbers.append(e)
new_season_numbers.append(s)
# need to do a quick sanity check heregex. It's possible that we now have episodes
# from more than one season (by tvdb numbering), and this is just too much
# for sickbeard, so we'd need to flag it.
new_season_numbers = list(set(new_season_numbers)) # remove duplicates
if len(new_season_numbers) > 1:
raise InvalidNameException("Scene numbering results episodes from "
"seasons %s, (i.e. more than one) and "
"SickGear does not support this. "
"Sorry." % (str(new_season_numbers)))
# I guess it's possible that we'd have duplicate episodes too, so lets
# eliminate them
new_episode_numbers = list(set(new_episode_numbers))
new_episode_numbers.sort()
# maybe even duplicate absolute numbers so why not do them as well
new_absolute_numbers = list(set(new_absolute_numbers))
new_absolute_numbers.sort()
if len(new_absolute_numbers):
bestResult.ab_episode_numbers = new_absolute_numbers
if len(new_season_numbers) and len(new_episode_numbers):
bestResult.episode_numbers = new_episode_numbers
bestResult.season_number = new_season_numbers[0]
if self.convert:
logger.log(
u"Converted parsed result " + bestResult.original_name + " into " + str(bestResult).decode('utf-8',
'xmlcharrefreplace'),
logger.DEBUG)
# CPU sleep
time.sleep(0.02)
return bestResult
def _combine_results(self, first, second, attr):
# if the first doesn't exist then return the second or nothing
@ -416,7 +429,7 @@ class NameParser(object):
file_name_result = self._parse_string(base_file_name)
# use only the direct parent dir
dir_name = os.path.basename(dir_name)
dir_name = ek.ek(os.path.basename, dir_name)
# parse the dirname for extra info if needed
dir_name_result = self._parse_string(dir_name)
@ -452,8 +465,12 @@ class NameParser(object):
final_result.quality = self._combine_results(file_name_result, dir_name_result, 'quality')
if not final_result.show:
raise InvalidShowException(
"Unable to parse " + name.encode(sickbeard.SYS_ENCODING, 'xmlcharrefreplace'))
if self.testing:
pass
#final_result.which_regex = []
else:
raise InvalidShowException(
"Unable to parse " + name.encode(sickbeard.SYS_ENCODING, 'xmlcharrefreplace'))
# if there's no useful info in it then raise an exception
if final_result.season_number == None and not final_result.episode_numbers and final_result.air_date == None and not final_result.ab_episode_numbers and not final_result.series_name:
@ -506,7 +523,7 @@ class ParseResult(object):
self.air_date = air_date
self.which_regex = []
self.which_regex = None
self.show = show
self.score = score
@ -530,14 +547,14 @@ class ParseResult(object):
return False
if self.ab_episode_numbers != other.ab_episode_numbers:
return False
if self.show != other.show:
return False
if self.score != other.score:
return False
if self.quality != other.quality:
return False
if self.version != other.version:
return False
#if self.show != other.show:
# return False
#if self.score != other.score:
# return False
#if self.quality != other.quality:
# return False
#if self.version != other.version:
# return False
return True
@ -556,8 +573,9 @@ class ParseResult(object):
to_return += str(self.air_date)
if self.ab_episode_numbers:
to_return += ' [ABS: ' + str(self.ab_episode_numbers) + ']'
if self.version:
to_return += ' [ANIME VER: ' + str(self.version) + ']'
if self.is_anime:
if self.version:
to_return += ' [ANIME VER: ' + str(self.version) + ']'
if self.release_group:
to_return += ' [GROUP: ' + self.release_group + ']'

View file

@ -23,29 +23,31 @@ normal_regexes = [
# Show.Name.S01E02.S01E03.Source.Quality.Etc-Group
# Show Name - S01E02 - S01E03 - S01E04 - Ep Name
'''
^(?P<series_name>.+?)[. _-]+ # Show_Name and separator
s(?P<season_num>\d+)[. _-]* # S01 and optional separator
e(?P<ep_num>\d+) # E02 and separator
([. _-]+s(?P=season_num)[. _-]* # S01 and optional separator
e(?P<extra_ep_num>\d+))+ # E03/etc and separator
[. _-]*((?P<extra_info>.+?) # Source_Quality_Etc-
((?<![. _-])(?<!WEB) # Make sure this is really the release group
-(?P<release_group>[^- ]+([. _-]\[.*\])?))?)?$ # Group
'''),
^(?P<series_name>.+?)[. _-]+ # Show_Name and separator
s(?P<season_num>\d+)[. _-]* # S01 and optional separator
e(?P<ep_num>\d+) # E02 and separator
([. _-]+s(?P=season_num)[. _-]* # S01 and optional separator
e(?P<extra_ep_num>\d+))+ # E03/etc and separator
[. _-]*((?P<extra_info>.+?) # Source_Quality_Etc-
((?<![. _-])(?<!WEB) # Make sure this is really the release group
-(?P<release_group>[^- ]+))?)?$ # Group
'''
),
('fov_repeat',
# Show.Name.1x02.1x03.Source.Quality.Etc-Group
# Show Name - 1x02 - 1x03 - 1x04 - Ep Name
'''
^(?P<series_name>.+?)[. _-]+ # Show_Name and separator
(?P<season_num>\d+)x # 1x
(?P<ep_num>\d+) # 02 and separator
([. _-]+(?P=season_num)x # 1x
(?P<extra_ep_num>\d+))+ # 03/etc and separator
[. _-]*((?P<extra_info>.+?) # Source_Quality_Etc-
((?<![. _-])(?<!WEB) # Make sure this is really the release group
-(?P<release_group>[^- ]+([. _-]\[.*\])?))?)?$ # Group
'''),
^(?P<series_name>.+?)[. _-]+ # Show_Name and separator
(?P<season_num>\d+)x # 1x
(?P<ep_num>\d+) # 02 and separator
([. _-]+(?P=season_num)x # 1x
(?P<extra_ep_num>\d+))+ # 03/etc and separator
[. _-]*((?P<extra_info>.+?) # Source_Quality_Etc-
((?<![. _-])(?<!WEB) # Make sure this is really the release group
-(?P<release_group>[^- ]+))?)?$ # Group
'''
),
('standard',
# Show.Name.S01E02.Source.Quality.Etc-Group
@ -55,15 +57,16 @@ normal_regexes = [
# Show Name - S01E02-03 - My Ep Name
# Show.Name.S01.E02.E03
'''
^((?P<series_name>.+?)[. _-]+)? # Show_Name and separator
s(?P<season_num>\d+)[. _-]* # S01 and optional separator
e(?P<ep_num>\d+) # E02 and separator
(([. _-]*e|-) # linking e/- char
(?P<extra_ep_num>(?!(1080|720|480)[pi])\d+))* # additional E03/etc
[. _-]*((?P<extra_info>.+?) # Source_Quality_Etc-
((?<![. _-])(?<!WEB) # Make sure this is really the release group
-(?P<release_group>[^- ]+([. _-]\[.*\])?))?)?$ # Group
'''),
^((?P<series_name>.+?)[. _-]+)? # Show_Name and separator
s(?P<season_num>\d+)[. _-]* # S01 and optional separator
e(?P<ep_num>\d+) # E02 and separator
(([. _-]*e|-) # linking e/- char
(?P<extra_ep_num>(?!(1080|720|480)[pi])\d+))* # additional E03/etc
[. _-]*((?P<extra_info>.+?) # Source_Quality_Etc-
((?<![. _-])(?<!WEB) # Make sure this is really the release group
-(?P<release_group>[^- ]+))?)?$ # Group
'''
),
('fov',
# Show_Name.1x02.Source_Quality_Etc-Group
@ -71,70 +74,64 @@ normal_regexes = [
# Show_Name.1x02x03x04.Source_Quality_Etc-Group
# Show Name - 1x02-03-04 - My Ep Name
'''
^((?P<series_name>.+?)[\[. _-]+)? # Show_Name and separator
(?P<season_num>\d+)x # 1x
(?P<ep_num>\d+) # 02 and separator
(([. _-]*x|-) # linking x/- char
^((?P<series_name>.+?)[\[. _-]+)? # Show_Name and separator
(?P<season_num>\d+)x # 1x
(?P<ep_num>\d+) # 02 and separator
(([. _-]*x|-) # linking x/- char
(?P<extra_ep_num>
(?!(1080|720|480)[pi])(?!(?<=x)264) # ignore obviously wrong multi-eps
\d+))* # additional x03/etc
[\]. _-]*((?P<extra_info>.+?) # Source_Quality_Etc-
((?<![. _-])(?<!WEB) # Make sure this is really the release group
-(?P<release_group>[^- ]+([. _-]\[.*\])?))?)?$ # Group
'''),
(?!(1080|720|480)[pi])(?!(?<=x)264) # ignore obviously wrong multi-eps
\d+))* # additional x03/etc
[\]. _-]*((?P<extra_info>.+?) # Source_Quality_Etc-
((?<![. _-])(?<!WEB) # Make sure this is really the release group
-(?P<release_group>[^- ]+))?)?$ # Group
'''
),
('scene_date_format',
# Show.Name.2010.11.23.Source.Quality.Etc-Group
# Show Name - 2010-11-23 - Ep Name
'''
^((?P<series_name>.+?)[. _-]+)? # Show_Name and separator
(?P<air_date>(\d+[. _-]\d+[. _-]\d+)|(\d+\w+[. _-]\w+[. _-]\d+))
[. _-]*((?P<extra_info>.+?) # Source_Quality_Etc-
((?<![. _-])(?<!WEB) # Make sure this is really the release group
-(?P<release_group>[^- ]+([. _-]\[.*\])?))?)?$ # Group
'''),
('scene_sports_format',
# Show.Name.100.Event.2010.11.23.Source.Quality.Etc-Group
# Show.Name.2010.11.23.Source.Quality.Etc-Group
# Show Name - 2010-11-23 - Ep Name
^((?P<series_name>.+?)[. _-]+)? # Show_Name and separator
(?P<air_year>\d{4})[. _-]+ # 2010 and separator
(?P<air_month>\d{2})[. _-]+ # 11 and separator
(?P<air_day>\d{2}) # 23 and separator
[. _-]*((?P<extra_info>.+?) # Source_Quality_Etc-
((?<![. _-])(?<!WEB) # Make sure this is really the release group
-(?P<release_group>[^- ]+))?)?$ # Group
'''
^(?P<series_name>.*?(UEFA|MLB|ESPN|WWE|MMA|UFC|TNA|EPL|NASCAR|NBA|NFL|NHL|NRL|PGA|SUPER LEAGUE|FORMULA|FIFA|NETBALL|MOTOGP).*?)[. _-]+
((?P<series_num>\d{1,3})[. _-]+)?
(?P<air_date>(\d+[. _-]\d+[. _-]\d+)|(\d+\w+[. _-]\w+[. _-]\d+))[. _-]+
((?P<extra_info>.+?)((?<![. _-])
(?<!WEB)-(?P<release_group>[^- ]+([. _-]\[.*\])?))?)?$
'''),
),
('stupid',
# tpz-abc102
'''
(?P<release_group>.+?)-\w+?[\. ]? # tpz-abc
(?!264) # don't count x264
(?P<season_num>\d{1,2}) # 1
(?P<ep_num>\d{2})$ # 02
'''),
(?P<release_group>.+?)-\w+?[\. ]? # tpz-abc
(?!264) # don't count x264
(?P<season_num>\d{1,2}) # 1
(?P<ep_num>\d{2})$ # 02
'''
),
('verbose',
# Show Name Season 1 Episode 2 Ep Name
'''
^(?P<series_name>.+?)[. _-]+ # Show Name and separator
season[. _-]+ # season and separator
(?P<season_num>\d+)[. _-]+ # 1
episode[. _-]+ # episode and separator
(?P<ep_num>\d+)[. _-]+ # 02 and separator
(?P<extra_info>.+)$ # Source_Quality_Etc-
'''),
^(?P<series_name>.+?)[. _-]+ # Show Name and separator
season[. _-]+ # season and separator
(?P<season_num>\d+)[. _-]+ # 1
episode[. _-]+ # episode and separator
(?P<ep_num>\d+)[. _-]+ # 02 and separator
(?P<extra_info>.+)$ # Source_Quality_Etc-
'''
),
('season_only',
# Show.Name.S01.Source.Quality.Etc-Group
'''
^((?P<series_name>.+?)[. _-]+)? # Show_Name and separator
s(eason[. _-])? # S01/Season 01
(?P<season_num>\d+)[. _-]* # S01 and optional separator
[. _-]*((?P<extra_info>.+?) # Source_Quality_Etc-
((?<![. _-])(?<!WEB) # Make sure this is really the release group
-(?P<release_group>[^- ]+([. _-]\[.*\])?))?)?$ # Group
^((?P<series_name>.+?)[. _-]+)? # Show_Name and separator
s(eason[. _-])? # S01/Season 01
(?P<season_num>\d+)[. _-]* # S01 and optional separator
[. _-]*((?P<extra_info>.+?) # Source_Quality_Etc-
((?<![. _-])(?<!WEB) # Make sure this is really the release group
-(?P<release_group>[^- ]+))?)?$ # Group
'''
),
@ -142,14 +139,14 @@ normal_regexes = [
# Show.Name.E02-03
# Show.Name.E02.2010
'''
^((?P<series_name>.+?)[. _-]+)? # Show_Name and separator
(e(p(isode)?)?|part|pt)[. _-]? # e, ep, episode, or part
(?P<ep_num>(\d+|[ivx]+)) # first ep num
((([. _-]+(and|&|to)[. _-]+)|-) # and/&/to joiner
^((?P<series_name>.+?)[. _-]+)? # Show_Name and separator
(e(p(isode)?)?|part|pt)[. _-]? # e, ep, episode, or part
(?P<ep_num>(\d+|[ivx]+)) # first ep num
((([. _-]+(and|&|to)[. _-]+)|-) # and/&/to joiner
(?P<extra_ep_num>(?!(1080|720|480)[pi])(\d+|[ivx]+))[. _-]) # second ep num
([. _-]*(?P<extra_info>.+?) # Source_Quality_Etc-
((?<![. _-])(?<!WEB) # Make sure this is really the release group
-(?P<release_group>[^- ]+([. _-]\[.*\])?))?)?$ # Group
([. _-]*(?P<extra_info>.+?) # Source_Quality_Etc-
((?<![. _-])(?<!WEB) # Make sure this is really the release group
-(?P<release_group>[^- ]+))?)?$ # Group
'''
),
@ -158,58 +155,57 @@ normal_regexes = [
# Show.Name.Part.3.Source.Quality.Etc-Group
# Show.Name.Part.1.and.Part.2.Blah-Group
'''
^((?P<series_name>.+?)[. _-]+)? # Show_Name and separator
(e(p(isode)?)?|part|pt)[. _-]? # e, ep, episode, or part
(?P<ep_num>(\d+|([ivx]+(?=[. _-])))) # first ep num
([. _-]+((and|&|to)[. _-]+)? # and/&/to joiner
((e(p(isode)?)?|part|pt)[. _-]?) # e, ep, episode, or part
^((?P<series_name>.+?)[. _-]+)? # Show_Name and separator
(e(p(isode)?)?|part|pt)[. _-]? # e, ep, episode, or part
(?P<ep_num>(\d+|([ivx]+(?=[. _-])))) # first ep num
([. _-]+((and|&|to)[. _-]+)? # and/&/to joiner
((e(p(isode)?)?|part|pt)[. _-]?) # e, ep, episode, or part
(?P<extra_ep_num>(?!(1080|720|480)[pi])
(\d+|([ivx]+(?=[. _-]))))[. _-])* # second ep num
([. _-]*(?P<extra_info>.+?) # Source_Quality_Etc-
((?<![. _-])(?<!WEB) # Make sure this is really the release group
-(?P<release_group>[^- ]+([. _-]\[.*\])?))?)?$ # Group
'''
),
('no_season',
# Show Name - 01 - Ep Name
# 01 - Ep Name
# 01 - Ep Name
'''
^((?P<series_name>.+?)(?:[. _-]{2,}|[. _]))? # Show_Name and separator
(?P<ep_num>\d{1,3}) # 02
(?:-(?P<extra_ep_num>\d{1,3}))* # -03-04-05 etc
\s?of?\s?\d{1,3}? # of joiner (with or without spaces) and series total ep
[. _-]+((?P<extra_info>.+?) # Source_Quality_Etc-
((?<![. _-])(?<!WEB) # Make sure this is really the release group
-(?P<release_group>[^- ]+([. _-]\[.*\])?))?)?$ # Group
(\d+|([ivx]+(?=[. _-]))))[. _-])* # second ep num
([. _-]*(?P<extra_info>.+?) # Source_Quality_Etc-
((?<![. _-])(?<!WEB) # Make sure this is really the release group
-(?P<release_group>[^- ]+))?)?$ # Group
'''
),
('bare',
# Show.Name.102.Source.Quality.Etc-Group
'''
^(?P<series_name>.+?)[. _-]+ # Show_Name and separator
(?P<season_num>\d{1,2}) # 1
(?P<ep_num>\d{2}) # 02 and separator
^(?P<series_name>.+?)[. _-]+ # Show_Name and separator
(?P<season_num>\d{1,2}) # 1
(?P<ep_num>\d{2}) # 02 and separator
([. _-]+(?P<extra_info>(?!\d{3}[. _-]+)[^-]+) # Source_Quality_Etc-
(-(?P<release_group>[^- ]+([. _-]\[.*\])?))?)?$ # Group
'''),
(-(?P<release_group>.+))?)?$ # Group
'''
),
('no_season',
# Show Name - 01 - Ep Name
# 01 - Ep Name
'''
^((?P<series_name>.+?)(?:[. _-]{2,}|[. _]))? # Show_Name and separator
(?P<ep_num>\d{1,2}) # 01
(?:-(?P<extra_ep_num>\d{1,2}))* # 02
[. _-]+((?P<extra_info>.+?) # Source_Quality_Etc-
((?<![. _-])(?<!WEB) # Make sure this is really the release group
-(?P<release_group>[^- ]+))?)?$ # Group
'''
),
]
anime_regexes = [
('anime_ultimate',
"""
'''
^(?:\[(?P<release_group>.+?)\][ ._-]*)
(?P<series_name>.+?)[ ._-]+
(?P<ep_ab_num>((?!(1080|720|480)[pi])|(?![hx].?264))\d{1,3})
(-(?P<extra_ab_ep_num>((?!(1080|720|480)[pi])|(?![hx].?264))\d{1,3}))?[ ._-]+?
(?P<ep_ab_num>\d{1,3})
(-(?P<extra_ab_ep_num>\d{1,3}))?[ ._-]+?
(?:v(?P<version>[0-9]))?
(?:[\w\.]*)
(?:(?:(?:[\[\(])(?P<extra_info>\d{3,4}[xp]?\d{0,4}[\.\w\s-]*)(?:[\]\)]))|(?:\d{3,4}[xp]))
(?:[ ._]?\[(?P<crc>\w+)\])?
.*?
"""
'''
),
('anime_standard',
# [Group Name] Show Name.13-14
@ -219,163 +215,163 @@ anime_regexes = [
# [Group Name] Show Name - 13
# Show Name 13
'''
^(\[(?P<release_group>.+?)\][ ._-]*)? # Release Group and separator
(?P<series_name>.+?)[ ._-]+ # Show_Name and separator
(?P<ep_ab_num>((?!(1080|720|480)[pi])|(?![hx].?264))\d{1,3}) # E01
(-(?P<extra_ab_ep_num>((?!(1080|720|480)[pi])|(?![hx].?264))\d{1,3}))? # E02
(v(?P<version>[0-9]))? # version
[ ._-]+\[(?P<extra_info>\d{3,4}[xp]?\d{0,4}[\.\w\s-]*)\] # Source_Quality_Etc-
(\[(?P<crc>\w{8})\])? # CRC
.*? # Separator and EOL
^(\[(?P<release_group>.+?)\][ ._-]*)? # Release Group and separator
(?P<series_name>.+?)[ ._-]+ # Show_Name and separator
(?P<ep_ab_num>\d{1,3}) # E01
(-(?P<extra_ab_ep_num>\d{1,3}))? # E02
(v(?P<version>[0-9]))? # version
[ ._-]+\[(?P<extra_info>\d{3,4}[xp]?\d{0,4}[\.\w\s-]*)\] # Source_Quality_Etc-
(\[(?P<crc>\w{8})\])? # CRC
.*? # Separator and EOL
'''),
('anime_standard_round',
# TODO examples
# [Stratos-Subs]_Infinite_Stratos_-_12_(1280x720_H.264_AAC)_[379759DB]
# [ShinBunBu-Subs] Bleach - 02-03 (CX 1280x720 x264 AAC)
'''
^(\[(?P<release_group>.+?)\][ ._-]*)? # Release Group and separator
(?P<series_name>.+?)[ ._-]+ # Show_Name and separator
(?P<ep_ab_num>((?!(1080|720|480)[pi])|(?![hx].?264))\d{1,3}) # E01
(-(?P<extra_ab_ep_num>((?!(1080|720|480)[pi])|(?![hx].?264))\d{1,3}))? # E02
(v(?P<version>[0-9]))? # version
[ ._-]+\((?P<extra_info>(CX[ ._-]?)?\d{3,4}[xp]?\d{0,4}[\.\w\s-]*)\) # Source_Quality_Etc-
(\[(?P<crc>\w{8})\])? # CRC
.*? # Separator and EOL
^(\[(?P<release_group>.+?)\][ ._-]*)? # Release Group and separator
(?P<series_name>.+?)[ ._-]+ # Show_Name and separator
(?P<ep_ab_num>\d{1,3}) # E01
(-(?P<extra_ab_ep_num>\d{1,3}))? # E02
(v(?P<version>[0-9]))? # version
[ ._-]+\((?P<extra_info>(CX[ ._-]?)?\d{3,4}[xp]?\d{0,4}[\.\w\s-]*)\) # Source_Quality_Etc-
(\[(?P<crc>\w{8})\])? # CRC
.*? # Separator and EOL
'''),
('anime_slash',
# [SGKK] Bleach 312v1 [720p/MKV]
'''
^(\[(?P<release_group>.+?)\][ ._-]*)? # Release Group and separator
(?P<series_name>.+?)[ ._-]+ # Show_Name and separator
(?P<ep_ab_num>((?!(1080|720|480)[pi])|(?![hx].?264))\d{1,3}) # E01
(-(?P<extra_ab_ep_num>((?!(1080|720|480)[pi])|(?![hx].?264))\d{1,3}))? # E02
(v(?P<version>[0-9]))? # version
[ ._-]+\[(?P<extra_info>\d{3,4}p) # Source_Quality_Etc-
(\[(?P<crc>\w{8})\])? # CRC
.*? # Separator and EOL
^(\[(?P<release_group>.+?)\][ ._-]*)? # Release Group and separator
(?P<series_name>.+?)[ ._-]+ # Show_Name and separator
(?P<ep_ab_num>\d{1,3}) # E01
(-(?P<extra_ab_ep_num>\d{1,3}))? # E02
(v(?P<version>[0-9]))? # version
[ ._-]+\[(?P<extra_info>\d{3,4}p) # Source_Quality_Etc-
(\[(?P<crc>\w{8})\])? # CRC
.*? # Separator and EOL
'''),
('anime_standard_codec',
# [Ayako]_Infinite_Stratos_-_IS_-_07_[H264][720p][EB7838FC]
# [Ayako] Infinite Stratos - IS - 07v2 [H264][720p][44419534]
# [Ayako-Shikkaku] Oniichan no Koto Nanka Zenzen Suki Janain Dakara ne - 10 [LQ][h264][720p] [8853B21C]
'''
^(\[(?P<release_group>.+?)\][ ._-]*)? # Release Group and separator
(?P<series_name>.+?)[ ._]* # Show_Name and separator
([ ._-]+-[ ._-]+[A-Z]+[ ._-]+)?[ ._-]+ # funny stuff, this is sooo nuts ! this will kick me in the butt one day
(?P<ep_ab_num>((?!(1080|720|480)[pi])|(?![hx].?264))\d{1,3}) # E01
(-(?P<extra_ab_ep_num>((?!(1080|720|480)[pi])|(?![hx].?264))\d{1,3}))? # E02
(v(?P<version>[0-9]))? # version
([ ._-](\[\w{1,2}\])?\[[a-z][.]?\w{2,4}\])? #codec
[ ._-]*\[(?P<extra_info>(\d{3,4}[xp]?\d{0,4})?[\.\w\s-]*)\] # Source_Quality_Etc-
(\[(?P<crc>\w{8})\])?
.*? # Separator and EOL
^(\[(?P<release_group>.+?)\][ ._-]*)? # Release Group and separator
(?P<series_name>.+?)[ ._]* # Show_Name and separator
([ ._-]+-[ ._-]+[A-Z]+[ ._-]+)?[ ._-]+ # this will kick me in the butt one day
(?P<ep_ab_num>\d{1,3}) # E01
(-(?P<extra_ab_ep_num>\d{1,3}))? # E02
(v(?P<version>[0-9]))? # version
([ ._-](\[\w{1,2}\])?\[[a-z][.]?\w{2,4}\])? # codec
[ ._-]*\[(?P<extra_info>(\d{3,4}[xp]?\d{0,4})?[\.\w\s-]*)\] # Source_Quality_Etc-
(\[(?P<crc>\w{8})\])? # CRC
.*? # Separator and EOL
'''),
('anime_codec_crc',
'''
^(?:\[(?P<release_group>.*?)\][ ._-]*)?
(?:(?P<series_name>.*?)[ ._-]*)?
(?:(?P<ep_ab_num>(((?!(1080|720|480)[pi])|(?![hx].?264))\d{1,3}))[ ._-]*).+?
(?:\[(?P<codec>.*?)\][ ._-]*)
(?:\[(?P<crc>\w{8})\])?
.*?
'''),
('anime_and_normal',
# Bleach - s16e03-04 - 313-314
# Bleach.s16e03-04.313-314
# Bleach s16e03e04 313-314
'''
^(?P<series_name>.+?)[ ._-]+ # start of string and series name and non optinal separator
[sS](?P<season_num>\d+)[. _-]* # S01 and optional separator
[eE](?P<ep_num>\d+) # epipisode E02
(([. _-]*e|-) # linking e/- char
(?P<extra_ep_num>\d+))* # additional E03/etc
([ ._-]{2,}|[ ._]+) # if "-" is used to separate at least something else has to be there(->{2,}) "s16e03-04-313-314" would make sens any way
((?P<ep_ab_num>((?!(1080|720|480)[pi])|(?![hx].?264))\d{1,3}))? # absolute number
(-(?P<extra_ab_ep_num>((?!(1080|720|480)[pi])|(?![hx].?264))\d{1,3}))? # "-" as separator and anditional absolute number, all optinal
(v(?P<version>[0-9]))? # the version e.g. "v2"
^(?P<series_name>.+?)[ ._-]+ # start of string and series name and non optinal separator
[sS](?P<season_num>\d+)[. _-]* # S01 and optional separator
[eE](?P<ep_num>\d+) # epipisode E02
(([. _-]*e|-) # linking e/- char
(?P<extra_ep_num>\d+))* # additional E03/etc
([ ._-]{2,}|[ ._]+) # if "-" is used to separate at least something else has to be
# there(->{2,}) "s16e03-04-313-314" would make sens any way
(?P<ep_ab_num>\d{1,3}) # absolute number
(-(?P<extra_ab_ep_num>\d{1,3}))* # "-" as separator and anditional absolute number, all optinal
(v(?P<version>[0-9]))? # the version e.g. "v2"
.*?
'''
),
('anime_and_normal_x',
# Bleach - s16e03-04 - 313-314
# Bleach.s16e03-04.313-314
# Bleach s16e03e04 313-314
'''
^(?P<series_name>.+?)[ ._-]+ # start of string and series name and non optinal separator
(?P<season_num>\d+)[. _-]* # S01 and optional separator
[xX](?P<ep_num>\d+) # epipisode E02
(([. _-]*e|-) # linking e/- char
(?P<extra_ep_num>\d+))* # additional E03/etc
([ ._-]{2,}|[ ._]+) # if "-" is used to separate at least something else has to be there(->{2,}) "s16e03-04-313-314" would make sens any way
((?P<ep_ab_num>((?!(1080|720|480)[pi])|(?![hx].?264))\d{1,3}))? # absolute number
(-(?P<extra_ab_ep_num>((?!(1080|720|480)[pi])|(?![hx].?264))\d{1,3}))? # "-" as separator and anditional absolute number, all optinal
(v(?P<version>[0-9]))? # the version e.g. "v2"
^(?P<series_name>.+?)[ ._-]+ # start of string and series name and non optinal separator
(?P<season_num>\d+)[. _-]* # S01 and optional separator
[xX](?P<ep_num>\d+) # epipisode E02
(([. _-]*e|-) # linking e/- char
(?P<extra_ep_num>\d+))* # additional E03/etc
([ ._-]{2,}|[ ._]+) # if "-" is used to separate at least something else has to be
# there(->{2,}) "s16e03-04-313-314" would make sens any way
(?P<ep_ab_num>\d{1,3}) # absolute number
(-(?P<extra_ab_ep_num>\d{1,3}))* # "-" as separator and anditional absolute number, all optinal
(v(?P<version>[0-9]))? # the version e.g. "v2"
.*?
'''
),
('anime_and_normal_reverse',
# Bleach - 313-314 - s16e03-04
'''
^(?P<series_name>.+?)[ ._-]+ # start of string and series name and non optinal separator
(?P<ep_ab_num>((?!(1080|720|480)[pi])|(?![hx].?264))\d{1,3}) # absolute number
(-(?P<extra_ab_ep_num>((?!(1080|720|480)[pi])|(?![hx].?264))\d{1,3}))? # "-" as separator and anditional absolute number, all optinal
(v(?P<version>[0-9]))? # the version e.g. "v2"
([ ._-]{2,}|[ ._]+) # if "-" is used to separate at least something else has to be there(->{2,}) "s16e03-04-313-314" would make sens any way
[sS](?P<season_num>\d+)[. _-]* # S01 and optional separator
[eE](?P<ep_num>\d+) # epipisode E02
(([. _-]*e|-) # linking e/- char
(?P<extra_ep_num>\d+))* # additional E03/etc
^(?P<series_name>.+?)[ ._-]+ # start of string and series name and non optinal separator
(?P<ep_ab_num>\d{1,3}) # absolute number
(-(?P<extra_ab_ep_num>\d{1,3}))* # "-" as separator and anditional absolute number, all optinal
(v(?P<version>[0-9]))? # the version e.g. "v2"
([ ._-]{2,}|[ ._]+) # if "-" is used to separate at least something else has to be
# there(->{2,}) "s16e03-04-313-314" would make sens any way
[sS](?P<season_num>\d+)[. _-]* # S01 and optional separator
[eE](?P<ep_num>\d+) # epipisode E02
(([. _-]*e|-) # linking e/- char
(?P<extra_ep_num>\d+))* # additional E03/etc
.*?
'''
),
('anime_and_normal_front',
# 165.Naruto Shippuuden.s08e014
'''
^(?P<ep_ab_num>((?!(1080|720|480)[pi])|(?![hx].?264))\d{1,3}) # start of string and absolute number
(-(?P<extra_ab_ep_num>((?!(1080|720|480)[pi])|(?![hx].?264))\d{1,3}))? # "-" as separator and anditional absolute number, all optinal
^(?P<ep_ab_num>\d{1,3}) # start of string and absolute number
(-(?P<extra_ab_ep_num>\d{1,3}))* # "-" as separator and anditional absolute number, all optinal
(v(?P<version>[0-9]))?[ ._-]+ # the version e.g. "v2"
(?P<series_name>.+?)[ ._-]+
[sS](?P<season_num>\d+)[. _-]* # S01 and optional separator
[sS](?P<season_num>\d+)[. _-]* # S01 and optional separator
[eE](?P<ep_num>\d+)
(([. _-]*e|-) # linking e/- char
(?P<extra_ep_num>\d+))* # additional E03/etc
(([. _-]*e|-) # linking e/- char
(?P<extra_ep_num>\d+))* # additional E03/etc
.*?
'''
),
('anime_ep_name',
'''
^(?:\[(?P<release_group>.+?)\][ ._-]*)
(?P<series_name>.+?)[ ._-]+
(?P<ep_ab_num>((?!(1080|720|480)[pi])|(?![hx].?264))\d{1,3})
(-(?P<extra_ab_ep_num>((?!(1080|720|480)[pi])|(?![hx].?264))\d{1,3}))?[ ._-]*?
(?:v(?P<version>[0-9])[ ._-]+?)?
(?:.+?[ ._-]+?)?
\[(?P<extra_info>\w+)\][ ._-]?
(?:\[(?P<crc>\w{8})\])?
.*?
^(?:\[(?P<release_group>.+?)\][ ._-]*)
(?P<series_name>.+?)[ ._-]+
(?P<ep_ab_num>\d{1,3})
(-(?P<extra_ab_ep_num>\d{1,3}))*[ ._-]*?
(?:v(?P<version>[0-9])[ ._-]+?)?
(?:.+?[ ._-]+?)?
\[(?P<extra_info>\w+)\][ ._-]?
(?:\[(?P<crc>\w{8})\])?
.*?
'''
),
('anime_bare',
# One Piece - 102
# [ACX]_Wolf's_Spirit_001.mkv
'''
^(\[(?P<release_group>.+?)\][ ._-]*)?
(?P<series_name>.+?)[ ._-]+ # Show_Name and separator
(?P<ep_ab_num>((?!(1080|720|480)[pi])|(?![hx].?264))\d{1,3}) # E01
(-(?P<extra_ab_ep_num>((?!(1080|720|480)[pi])|(?![hx].?264))\d{1,3}))? # E02
(v(?P<version>[0-9]))? # v2
.*? # Separator and EOL
''')
]
(?P<series_name>.+?)[ ._-]+ # Show_Name and separator
(?<!H.)(?P<ep_ab_num>\d{3})(?!0p) # E01, while avoiding H.264 and 1080p from being matched
(-(?P<extra_ab_ep_num>\d{3}))* # E02
(v(?P<version>[0-9]))? # v2
.*? # Separator and EOL
'''),
('standard',
# Show.Name.S01E02.Source.Quality.Etc-Group
# Show Name - S01E02 - My Ep Name
# Show.Name.S01.E03.My.Ep.Name
# Show.Name.S01E02E03.Source.Quality.Etc-Group
# Show Name - S01E02-03 - My Ep Name
# Show.Name.S01.E02.E03
'''
^((?P<series_name>.+?)[. _-]+)? # Show_Name and separator
s(?P<season_num>\d+)[. _-]* # S01 and optional separator
e(?P<ep_num>\d+) # E02 and separator
(([. _-]*e|-) # linking e/- char
(?P<extra_ep_num>(?!(1080|720|480)[pi])\d+))* # additional E03/etc
[. _-]*((?P<extra_info>.+?) # Source_Quality_Etc-
((?<![. _-])(?<!WEB) # Make sure this is really the release group
-(?P<release_group>[^- ]+))?)?$ # Group
'''
),
]

View file

@ -83,23 +83,27 @@ class TraktNotifier:
# Start by getting all episodes in the watchlist
watchlist = TraktCall("user/watchlist/episodes.json/%API%/" + sickbeard.TRAKT_USERNAME, sickbeard.TRAKT_API, sickbeard.TRAKT_USERNAME, sickbeard.TRAKT_PASSWORD)
# Convert watchlist to only contain current show
for show in watchlist:
# Check if tvdb_id exists
if 'tvdb_id' in show:
if unicode(data['shows'][0]['tvdb_id']) == show['tvdb_id']:
data_show = {
'title': show['title'],
'tvdb_id': show['tvdb_id'],
'episodes': []
}
# Add series and episode (number) to the arry
for episodes in show['episodes']:
ep = {'season': episodes['season'], 'episode': episodes['number']}
data_show['episodes'].append(ep)
if data_show is not None:
TraktCall("show/episode/unwatchlist/%API%", sickbeard.TRAKT_API, sickbeard.TRAKT_USERNAME, sickbeard.TRAKT_PASSWORD, data_show)
if watchlist is not None:
# Convert watchlist to only contain current show
for show in watchlist:
# Check if tvdb_id exists
if 'tvdb_id' in show:
if unicode(data['shows'][0]['tvdb_id']) == show['tvdb_id']:
data_show = {
'title': show['title'],
'tvdb_id': show['tvdb_id'],
'episodes': []
}
# Add series and episode (number) to the arry
for episodes in show['episodes']:
ep = {'season': episodes['season'], 'episode': episodes['number']}
data_show['episodes'].append(ep)
if data_show is not None:
TraktCall("show/episode/unwatchlist/%API%", sickbeard.TRAKT_API, sickbeard.TRAKT_USERNAME, sickbeard.TRAKT_PASSWORD, data_show)
else:
logger.log('Failed to get watchlist from trakt. Unable to remove episode from watchlist',
logger.ERROR)
def test_notify(self, api, username, password):
"""

View file

@ -97,7 +97,7 @@ def getNewznabProviderList(data):
providerDict[curDefault.name].needs_auth = curDefault.needs_auth
providerDict[curDefault.name].search_mode = curDefault.search_mode
providerDict[curDefault.name].search_fallback = curDefault.search_fallback
providerDict[curDefault.name].enable_daily = curDefault.enable_daily
providerDict[curDefault.name].enable_recentsearch = curDefault.enable_recentsearch
providerDict[curDefault.name].enable_backlog = curDefault.enable_backlog
return filter(lambda x: x, providerList)
@ -109,13 +109,13 @@ def makeNewznabProvider(configString):
search_mode = 'eponly'
search_fallback = 0
enable_daily = 0
enable_recentsearch = 0
enable_backlog = 0
try:
values = configString.split('|')
if len(values) == 9:
name, url, key, catIDs, enabled, search_mode, search_fallback, enable_daily, enable_backlog = values
name, url, key, catIDs, enabled, search_mode, search_fallback, enable_recentsearch, enable_backlog = values
else:
name = values[0]
url = values[1]
@ -129,7 +129,7 @@ def makeNewznabProvider(configString):
newznab = sys.modules['sickbeard.providers.newznab']
newProvider = newznab.NewznabProvider(name, url, key=key, catIDs=catIDs, search_mode=search_mode,
search_fallback=search_fallback, enable_daily=enable_daily,
search_fallback=search_fallback, enable_recentsearch=enable_recentsearch,
enable_backlog=enable_backlog)
newProvider.enabled = enabled == '1'
@ -157,13 +157,13 @@ def makeTorrentRssProvider(configString):
cookies = None
search_mode = 'eponly'
search_fallback = 0
enable_daily = 0
enable_recentsearch = 0
enable_backlog = 0
try:
values = configString.split('|')
if len(values) == 8:
name, url, cookies, enabled, search_mode, search_fallback, enable_daily, enable_backlog = values
name, url, cookies, enabled, search_mode, search_fallback, enable_recentsearch, enable_backlog = values
else:
name = values[0]
url = values[1]
@ -178,7 +178,7 @@ def makeTorrentRssProvider(configString):
except:
return
newProvider = torrentRss.TorrentRssProvider(name, url, cookies, search_mode, search_fallback, enable_daily,
newProvider = torrentRss.TorrentRssProvider(name, url, cookies, search_mode, search_fallback, enable_recentsearch,
enable_backlog)
newProvider.enabled = enabled == '1'

View file

@ -136,6 +136,8 @@ class BTNProvider(generic.TorrentProvider):
server = jsonrpclib.Server(self.url)
parsedJSON = {}
logger.log(u'Searching with parameters: %s' % params, logger.DEBUG)
try:
parsedJSON = server.getTorrents(apikey, params, int(results_per_page), int(offset))
@ -201,8 +203,6 @@ class BTNProvider(generic.TorrentProvider):
if ep_obj.show.air_by_date or ep_obj.show.sports:
# Search for the year of the air by date show
current_params['name'] = str(ep_obj.airdate).split('-')[0]
elif ep_obj.show.is_anime:
current_params['name'] = "%d" % ep_obj.scene_absolute_number
else:
current_params['name'] = 'Season ' + str(ep_obj.scene_season)
@ -238,8 +238,6 @@ class BTNProvider(generic.TorrentProvider):
# BTN uses dots in dates, we just search for the date since that
# combined with the series identifier should result in just one episode
search_params['name'] = date_str.replace('-', '.')
elif ep_obj.show.anime:
search_params['name'] = "%i" % int(ep_obj.scene_absolute_number)
else:
# Do a general name search for the episode, formatted like SXXEYY
search_params['name'] = "S%02dE%02d" % (ep_obj.scene_season, ep_obj.scene_episode)

View file

@ -56,7 +56,7 @@ class GenericProvider:
self.search_mode = None
self.search_fallback = False
self.enable_daily = False
self.enable_recentsearch = False
self.enable_backlog = False
self.cache = tvcache.TVCache(self)

View file

@ -43,7 +43,7 @@ from lib.bencode import bdecode
class NewznabProvider(generic.NZBProvider):
def __init__(self, name, url, key='', catIDs='5030,5040', search_mode='eponly', search_fallback=False,
enable_daily=False, enable_backlog=False):
enable_recentsearch=False, enable_backlog=False):
generic.NZBProvider.__init__(self, name)
@ -55,7 +55,7 @@ class NewznabProvider(generic.NZBProvider):
self.search_mode = search_mode
self.search_fallback = search_fallback
self.enable_daily = enable_daily
self.enable_recentsearch = enable_recentsearch
self.enable_backlog = enable_backlog
# a 0 in the key spot indicates that no key is needed
@ -77,7 +77,7 @@ class NewznabProvider(generic.NZBProvider):
def configStr(self):
return self.name + '|' + self.url + '|' + self.key + '|' + self.catIDs + '|' + str(
int(self.enabled)) + '|' + self.search_mode + '|' + str(int(self.search_fallback)) + '|' + str(
int(self.enable_daily)) + '|' + str(int(self.enable_backlog))
int(self.enable_recentsearch)) + '|' + str(int(self.enable_backlog))
def imageName(self):
if ek.ek(os.path.isfile,

View file

@ -36,7 +36,7 @@ from lib.bencode import bdecode
class TorrentRssProvider(generic.TorrentProvider):
def __init__(self, name, url, cookies='', search_mode='eponly', search_fallback=False, enable_daily=False,
def __init__(self, name, url, cookies='', search_mode='eponly', search_fallback=False, enable_recentsearch=False,
enable_backlog=False):
generic.TorrentProvider.__init__(self, name)
self.cache = TorrentRssCache(self)
@ -48,7 +48,7 @@ class TorrentRssProvider(generic.TorrentProvider):
self.search_mode = search_mode
self.search_fallback = search_fallback
self.enable_daily = enable_daily
self.enable_recentsearch = enable_recentsearch
self.enable_backlog = enable_backlog
self.cookies = cookies
@ -59,7 +59,7 @@ class TorrentRssProvider(generic.TorrentProvider):
self.enabled,
self.search_mode or '',
self.search_fallback,
self.enable_daily,
self.enable_recentsearch,
self.enable_backlog)
def imageName(self):

View file

@ -61,11 +61,11 @@ class ThePirateBayProvider(generic.TorrentProvider):
self.proxy = ThePirateBayWebproxy()
self.url = 'http://pirateproxy.net/'
self.url = 'http://oldpiratebay.org/'
self.searchurl = self.url + 'search/%s/0/7/200' # order by seed
self.searchurl = self.url + 'search.php?q=%s&Torrent_sort=seeders.desc' # order by seed
self.re_title_url = '/torrent/(?P<id>\d+)/(?P<title>.*?)//1".+?(?P<url>magnet.*?)//1".+?(?P<seeders>\d+)</td>.+?(?P<leechers>\d+)</td>'
self.re_title_url = 'href=["\'](?P<url>magnet:.*?)&.*?/torrent/(?P<id>\d+)/(?P<title>.*?)".+?seeders-row sy">(?P<seeders>\d+)</td>.+?leechers-row ly">(?P<leechers>\d+)</td>'
def isEnabled(self):
return self.enabled

View file

@ -30,7 +30,7 @@ class WombleProvider(generic.NZBProvider):
generic.NZBProvider.__init__(self, "Womble's Index")
self.enabled = False
self.cache = WombleCache(self)
self.url = 'https://newshost.co.za/'
self.url = 'http://newshost.co.za/'
def isEnabled(self):
return self.enabled
@ -53,7 +53,7 @@ class WombleCache(tvcache.TVCache):
return
cl = []
for url in [self.provider.url + 'rss/?sec=tv-sd&fr=false', self.provider.url + 'rss/?sec=tv-hd&fr=false']:
for url in [self.provider.url + 'rss/?sec=tv-x264&fr=false', self.provider.url + 'rss/?sec=tv-sd&fr=false', self.provider.url + 'rss/?sec=tv-hd&fr=false']:
logger.log(u"Womble's Index cache update URL: " + url, logger.DEBUG)
data = self.getRSSFeed(url)
@ -63,12 +63,11 @@ class WombleCache(tvcache.TVCache):
# By now we know we've got data and no auth errors, all we need to do is put it in the database
for item in data.entries:
ci = self._parseItem(item)
title, url = self._get_title_and_url(item)
ci = self._parseItem(title, url)
if ci is not None:
cl.append(ci)
if len(cl) > 0:
myDB = self._getDB()
myDB.mass_action(cl)
@ -81,4 +80,3 @@ class WombleCache(tvcache.TVCache):
return data != 'Invalid Link'
provider = WombleProvider()

View file

@ -19,6 +19,7 @@
import datetime
import locale
import functools
import re
import sickbeard
from sickbeard.network_timezones import sb_timezone
@ -82,12 +83,12 @@ date_presets = ('%Y-%m-%d',
'%A, %b %d, %Y',
'%B %d, %Y',
'%a, %B %d, %Y',
'%A, %B %d, %Y'
)
'%A, %B %d, %Y')
time_presets = ('%I:%M:%S %p',
'%H:%M:%S'
)
'%I:%M:%S %P',
'%H:%M:%S')
# helper class
class static_or_instance(object):
@ -104,142 +105,98 @@ class sbdatetime(datetime.datetime):
@static_or_instance
def convert_to_setting(self, dt=None):
obj = (dt, self)[self is not None]
try:
if sickbeard.TIMEZONE_DISPLAY == 'local':
if self is None:
return dt.astimezone(sb_timezone)
else:
return self.astimezone(sb_timezone)
else:
if self is None:
return dt
else:
return self
if 'local' == sickbeard.TIMEZONE_DISPLAY:
return obj.astimezone(sb_timezone)
except:
if self is None:
return dt
else:
return self
pass
return obj
@static_or_instance
def setlocale(self, setlocale=True, use_has_locale=None, locale_str=''):
if setlocale:
try:
if None is use_has_locale or use_has_locale:
locale.setlocale(locale.LC_TIME, locale_str)
except:
if None is not use_has_locale:
sbdatetime.has_locale = False
pass
# display Time in SickGear Format
@static_or_instance
def sbftime(self, dt=None, show_seconds=False, t_preset=None):
def sbftime(self, dt=None, show_seconds=False, t_preset=None, setlocale=True, markup=False):
try:locale.setlocale(locale.LC_TIME, '')
except:pass
try:
if sbdatetime.has_locale:
locale.setlocale(locale.LC_TIME, 'us_US')
except:
sbdatetime.has_locale = False
sbdatetime.setlocale(setlocale=setlocale, use_has_locale=sbdatetime.has_locale, locale_str='us_US')
strt = ''
try:
if self is None:
if dt is not None:
if t_preset is not None:
strt = dt.strftime(t_preset)
elif show_seconds:
strt = dt.strftime(sickbeard.TIME_PRESET_W_SECONDS)
else:
strt = dt.strftime(sickbeard.TIME_PRESET)
else:
if t_preset is not None:
strt = self.strftime(t_preset)
elif show_seconds:
strt = self.strftime(sickbeard.TIME_PRESET_W_SECONDS)
else:
strt = self.strftime(sickbeard.TIME_PRESET)
finally:
try:
if sbdatetime.has_locale:
locale.setlocale(locale.LC_TIME, '')
except:
sbdatetime.has_locale = False
obj = (dt, self)[self is not None]
if None is not obj:
tmpl = (((sickbeard.TIME_PRESET, sickbeard.TIME_PRESET_W_SECONDS)[show_seconds]),
t_preset)[None is not t_preset]
tmpl = (tmpl.replace(':%S', ''), tmpl)[show_seconds]
strt = obj.strftime(tmpl.replace('%P', '%p'))
if sickbeard.TRIM_ZERO:
strt = re.sub(r'^0(\d:\d\d)', r'\1', strt)
if re.search(r'(?im)%p$', tmpl):
if '%p' in tmpl:
strt = strt.upper()
elif '%P' in tmpl:
strt = strt.lower()
if sickbeard.TRIM_ZERO:
strt = re.sub(r'(?im)^(\d+)(?::00)?(\s?[ap]m)', r'\1\2', strt)
if markup:
match = re.search(r'(?im)(\d{1,2})(?:(.)(\d\d)(?:(.)(\d\d))?)?(?:\s?([ap]m))?$', strt)
if match:
strt = ('%s%s%s%s%s%s' % (
('<span class="time-hr">%s</span>' % match.group(1), '')[None is match.group(1)],
('<span class="time-hr-min">%s</span>' % match.group(2), '')[None is match.group(2)],
('<span class="time-min">%s</span>' % match.group(3), '')[None is match.group(3)],
('<span class="time-min-sec">%s</span>' % match.group(4), '')[None is match.group(4)],
('<span class="time-sec">%s</span>' % match.group(5), '')[None is match.group(5)],
('<span class="time-am-pm">%s</span>' % match.group(6), '')[None is match.group(6)]))
finally:
sbdatetime.setlocale(setlocale=setlocale, use_has_locale=sbdatetime.has_locale)
return strt
# display Date in SickGear Format
@static_or_instance
def sbfdate(self, dt=None, d_preset=None):
def sbfdate(self, dt=None, d_preset=None, setlocale=True):
try:
locale.setlocale(locale.LC_TIME, '')
except:
pass
sbdatetime.setlocale(setlocale=setlocale)
strd = ''
try:
if self is None:
if dt is not None:
if d_preset is not None:
strd = dt.strftime(d_preset)
else:
strd = dt.strftime(sickbeard.DATE_PRESET)
else:
if d_preset is not None:
strd = self.strftime(d_preset)
else:
strd = self.strftime(sickbeard.DATE_PRESET)
obj = (dt, self)[self is not None]
if None is not obj:
strd = obj.strftime((sickbeard.DATE_PRESET, d_preset)[None is not d_preset])
finally:
try:
locale.setlocale(locale.LC_TIME, '')
except:
pass
sbdatetime.setlocale(setlocale=setlocale)
return strd
# display Datetime in SickGear Format
@static_or_instance
def sbfdatetime(self, dt=None, show_seconds=False, d_preset=None, t_preset=None):
def sbfdatetime(self, dt=None, show_seconds=False, d_preset=None, t_preset=None, markup=False):
try:
locale.setlocale(locale.LC_TIME, '')
except:
pass
sbdatetime.setlocale()
strd = ''
obj = (dt, self)[self is not None]
try:
if self is None:
if dt is not None:
if d_preset is not None:
strd = dt.strftime(d_preset)
else:
strd = dt.strftime(sickbeard.DATE_PRESET)
try:
if sbdatetime.has_locale:
locale.setlocale(locale.LC_TIME, 'us_US')
except:
sbdatetime.has_locale = False
if t_preset is not None:
strd += u', ' + dt.strftime(t_preset)
elif show_seconds:
strd += u', ' + dt.strftime(sickbeard.TIME_PRESET_W_SECONDS)
else:
strd += u', ' + dt.strftime(sickbeard.TIME_PRESET)
else:
if d_preset is not None:
strd = self.strftime(d_preset)
else:
strd = self.strftime(sickbeard.DATE_PRESET)
try:
if sbdatetime.has_locale:
locale.setlocale(locale.LC_TIME, 'us_US')
except:
sbdatetime.has_locale = False
if t_preset is not None:
strd += u', ' + self.strftime(t_preset)
elif show_seconds:
strd += u', ' + self.strftime(sickbeard.TIME_PRESET_W_SECONDS)
else:
strd += u', ' + self.strftime(sickbeard.TIME_PRESET)
finally:
try:
if sbdatetime.has_locale:
locale.setlocale(locale.LC_TIME, '')
except:
sbdatetime.has_locale = False
if None is not obj:
strd = u'%s, %s' % (obj.strftime((sickbeard.DATE_PRESET, d_preset)[None is not d_preset]),
sbdatetime.sbftime(dt, show_seconds, t_preset, False, markup))
finally:
sbdatetime.setlocale(use_has_locale=sbdatetime.has_locale)
return strd

View file

@ -375,7 +375,7 @@ def searchForNeededEpisodes():
episodes.extend(wantedEpisodes(curShow, fromDate))
providers = [x for x in sickbeard.providers.sortedProviderList() if x.isActive() and x.enable_daily]
providers = [x for x in sickbeard.providers.sortedProviderList() if x.isActive() and x.enable_recentsearch]
for curProvider in providers:
# spawn separate threads for each provider so we don't need to wait for providers with slow network operation
@ -435,7 +435,7 @@ def searchForNeededEpisodes():
if not didSearch:
logger.log(
u"No NZB/Torrent providers found or enabled in the SickGear config for daily searches. Please check your settings.",
u"No NZB/Torrent providers found or enabled in the SickGear config for recent searches. Please check your settings.",
logger.ERROR)
return foundResults.values()

View file

@ -32,7 +32,7 @@ from sickbeard import network_timezones
from sickbeard.exceptions import ex
class DailySearcher():
class RecentSearcher():
def __init__(self):
self.lock = threading.Lock()
self.amActive = False
@ -97,8 +97,8 @@ class DailySearcher():
myDB = db.DBConnection()
myDB.mass_action(sql_l)
# queue episode for daily search
dailysearch_queue_item = sickbeard.search_queue.DailySearchQueueItem()
sickbeard.searchQueueScheduler.action.add_item(dailysearch_queue_item)
# queue episode for recent search
recentsearch_queue_item = sickbeard.search_queue.RecentSearchQueueItem()
sickbeard.searchQueueScheduler.action.add_item(recentsearch_queue_item)
self.amActive = False

View file

@ -33,7 +33,7 @@ from sickbeard.search import pickBestResult
search_queue_lock = threading.Lock()
BACKLOG_SEARCH = 10
DAILY_SEARCH = 20
RECENT_SEARCH = 20
FAILED_SEARCH = 30
MANUAL_SEARCH = 40
@ -95,17 +95,17 @@ class SearchQueue(generic_queue.GenericQueue):
return True
return False
def is_dailysearch_in_progress(self):
def is_recentsearch_in_progress(self):
for cur_item in self.queue + [self.currentItem]:
if isinstance(cur_item, DailySearchQueueItem):
if isinstance(cur_item, RecentSearchQueueItem):
return True
return False
def queue_length(self):
length = {'backlog': 0, 'daily': 0, 'manual': 0, 'failed': 0}
length = {'backlog': 0, 'recent': 0, 'manual': 0, 'failed': 0}
for cur_item in self.queue:
if isinstance(cur_item, DailySearchQueueItem):
length['daily'] += 1
if isinstance(cur_item, RecentSearchQueueItem):
length['recent'] += 1
elif isinstance(cur_item, BacklogQueueItem):
length['backlog'] += 1
elif isinstance(cur_item, ManualSearchQueueItem):
@ -116,8 +116,8 @@ class SearchQueue(generic_queue.GenericQueue):
def add_item(self, item):
if isinstance(item, DailySearchQueueItem):
# daily searches
if isinstance(item, RecentSearchQueueItem):
# recent searches
generic_queue.GenericQueue.add_item(self, item)
elif isinstance(item, BacklogQueueItem) and not self.is_in_queue(item.show, item.segment):
# backlog searches
@ -128,16 +128,16 @@ class SearchQueue(generic_queue.GenericQueue):
else:
logger.log(u"Not adding item, it's already in the queue", logger.DEBUG)
class DailySearchQueueItem(generic_queue.QueueItem):
class RecentSearchQueueItem(generic_queue.QueueItem):
def __init__(self):
self.success = None
generic_queue.QueueItem.__init__(self, 'Daily Search', DAILY_SEARCH)
generic_queue.QueueItem.__init__(self, 'Recent Search', RECENT_SEARCH)
def run(self):
generic_queue.QueueItem.run(self)
try:
logger.log("Beginning daily search for new episodes")
logger.log("Beginning recent search for new episodes")
foundResults = search.searchForNeededEpisodes()
if not len(foundResults):

View file

@ -97,7 +97,7 @@ class TVCache():
myDB.action('DELETE FROM [' + self.providerID + '] WHERE 1')
def _get_title_and_url(self, item):
# override this in the provider if daily search has a different data layout to backlog searches
# override this in the provider if recent search has a different data layout to backlog searches
return self.provider._get_title_and_url(item)
def _getRSSData(self):
@ -222,7 +222,7 @@ class TVCache():
return True
def shouldClearCache(self):
# if daily search hasn't used our previous results yet then don't clear the cache
# if recent search hasn't used our previous results yet then don't clear the cache
if self.lastUpdate > self.lastSearch:
return False

View file

@ -115,9 +115,15 @@ class CheckVersion():
if self.updater.need_update():
return self.updater.update()
def fetch(self, pull_request):
return self.updater.fetch(pull_request)
def list_remote_branches(self):
return self.updater.list_remote_branches()
def list_remote_pulls(self):
return self.updater.list_remote_pulls()
def get_branch(self):
return self.updater.branch
@ -400,6 +406,17 @@ class GitUpdateManager(UpdateManager):
return re.findall('\S+\Wrefs/heads/(.*)', branches)
return []
def list_remote_pulls(self):
gh = github.GitHub(self.github_repo_user, self.github_repo, self.branch)
return gh.pull_requests()
def fetch(self, pull_request):
output, err, exit_status = self._run_git(self._git_path, 'fetch -f %s %s' % (sickbeard.GIT_REMOTE, pull_request)) # @UnusedVariable
if exit_status == 0:
return True
return False
class SourceUpdateManager(UpdateManager):
def __init__(self):
@ -596,4 +613,8 @@ class SourceUpdateManager(UpdateManager):
def list_remote_branches(self):
gh = github.GitHub(self.github_repo_user, self.github_repo, self.branch)
return [x['name'] for x in gh.branches() if x and 'name' in x]
return [x['name'] for x in gh.branches() if x and 'name' in x]
def list_remote_pulls(self):
# we don't care about testers that don't use git
return []

View file

@ -711,7 +711,7 @@ class CMD_ComingEpisodes(ApiCall):
self.sort, args = self.check_params(args, kwargs, "sort", "date", False, "string", ["date", "show", "network"])
self.type, args = self.check_params(args, kwargs, "type", "today|missed|soon|later", False, "list",
["missed", "later", "today", "soon"])
self.paused, args = self.check_params(args, kwargs, "paused", sickbeard.COMING_EPS_DISPLAY_PAUSED, False, "int",
self.paused, args = self.check_params(args, kwargs, "paused", sickbeard.EPISODE_VIEW_DISPLAY_PAUSED, False, "int",
[0, 1])
# super, missing, help
ApiCall.__init__(self, handler, args, kwargs)
@ -725,7 +725,7 @@ class CMD_ComingEpisodes(ApiCall):
tomorrow = (datetime.date.today() + datetime.timedelta(days=1)).toordinal()
next_week_dt = (datetime.date.today() + datetime.timedelta(days=7))
next_week = (next_week_dt + datetime.timedelta(days=1)).toordinal()
recently = (yesterday_dt - datetime.timedelta(days=sickbeard.COMING_EPS_MISSED_RANGE)).toordinal()
recently = (yesterday_dt - datetime.timedelta(days=sickbeard.EPISODE_VIEW_MISSED_RANGE)).toordinal()
done_show_list = []
qualList = Quality.DOWNLOADED + Quality.SNATCHED + [ARCHIVED, IGNORED]
@ -758,31 +758,27 @@ class CMD_ComingEpisodes(ApiCall):
# multi dimension sort
sorts = {
'date': (lambda a, b: cmp(
(a['parsed_datetime'],
(a['show_name'], remove_article(a['show_name']))[not sickbeard.SORT_ARTICLE],
a['season'], a['episode']),
(b['parsed_datetime'],
(b['show_name'], remove_article(b['show_name']))[not sickbeard.SORT_ARTICLE],
b['season'], b['episode']))),
'show': (lambda a, b: cmp(
((a['show_name'], remove_article(a['show_name']))[not sickbeard.SORT_ARTICLE],
a['parsed_datetime'], a['season'], a['episode']),
((b['show_name'], remove_article(b['show_name']))[not sickbeard.SORT_ARTICLE],
b['parsed_datetime'], b['season'], b['episode']))),
(a['parsed_datetime'], a['data_show_name'], a['season'], a['episode']),
(b['parsed_datetime'], b['data_show_name'], b['season'], b['episode']))),
'network': (lambda a, b: cmp(
(a['network'], a['parsed_datetime'],
(a['show_name'], remove_article(a['show_name']))[not sickbeard.SORT_ARTICLE],
a['season'], a['episode']),
(b['network'], b['parsed_datetime'],
(b['show_name'], remove_article(b['show_name']))[not sickbeard.SORT_ARTICLE],
b['season'], b['episode'])))
(a['data_network'], a['parsed_datetime'], a['data_show_name'], a['season'], a['episode']),
(b['data_network'], b['parsed_datetime'], b['data_show_name'], b['season'], b['episode']))),
'show': (lambda a, b: cmp(
(a['data_show_name'], a['parsed_datetime'], a['season'], a['episode']),
(b['data_show_name'], b['parsed_datetime'], b['season'], b['episode'])))
}
def value_maybe_article(value=''):
return (remove_article(value.lower()), value.lower())[sickbeard.SORT_ARTICLE]
# add parsed_datetime to the dict
for index, item in enumerate(sql_results):
sql_results[index]['parsed_datetime'] = 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[self.sort])
finalEpResults = {}
# add all requested types or all
@ -1507,7 +1503,7 @@ class CMD_SickBeardGetDefaults(ApiCall):
data = {"status": statusStrings[sickbeard.STATUS_DEFAULT].lower(),
"flatten_folders": int(sickbeard.FLATTEN_FOLDERS_DEFAULT), "initial": anyQualities,
"archive": bestQualities, "future_show_paused": int(sickbeard.COMING_EPS_DISPLAY_PAUSED)}
"archive": bestQualities, "future_show_paused": int(sickbeard.EPISODE_VIEW_DISPLAY_PAUSED)}
return _responds(RESULT_SUCCESS, data)
@ -1756,7 +1752,7 @@ class CMD_SickBeardSetDefaults(ApiCall):
sickbeard.FLATTEN_FOLDERS_DEFAULT = int(self.flatten_folders)
if self.future_show_paused != None:
sickbeard.COMING_EPS_DISPLAY_PAUSED = int(self.future_show_paused)
sickbeard.EPISODE_VIEW_DISPLAY_PAUSED = int(self.future_show_paused)
return _responds(RESULT_SUCCESS, msg="Saved defaults")

View file

@ -67,7 +67,7 @@ from lib.dateutil import tz
from lib.unrar2 import RarFile
from lib import subliminal
from trakt import TraktCall
from lib.trakt import TraktCall
try:
import json
@ -300,7 +300,7 @@ class MainHandler(RequestHandler):
def setPosterSortBy(self, sort):
if sort not in ('name', 'date', 'network', 'progress'):
if sort not in ('name', 'time', 'network', 'progress'):
sort = 'name'
sickbeard.POSTER_SORTBY = sort
@ -326,36 +326,40 @@ class MainHandler(RequestHandler):
redirect("/home/displayShow?show=" + show)
def setComingEpsLayout(self, layout):
if layout not in ('poster', 'banner', 'list', 'calendar'):
def setEpisodeViewLayout(self, layout):
if layout not in ('poster', 'banner', 'list', 'daybyday'):
layout = 'banner'
if layout == 'calendar':
sickbeard.COMING_EPS_SORT = 'date'
if 'daybyday' == layout:
sickbeard.EPISODE_VIEW_SORT = 'time'
sickbeard.COMING_EPS_LAYOUT = layout
sickbeard.EPISODE_VIEW_LAYOUT = layout
redirect("/comingEpisodes/")
sickbeard.save_config()
def toggleComingEpsDisplayPaused(self, *args, **kwargs):
redirect("/episodeView/")
sickbeard.COMING_EPS_DISPLAY_PAUSED = not sickbeard.COMING_EPS_DISPLAY_PAUSED
def toggleEpisodeViewDisplayPaused(self, *args, **kwargs):
redirect("/comingEpisodes/")
sickbeard.EPISODE_VIEW_DISPLAY_PAUSED = not sickbeard.EPISODE_VIEW_DISPLAY_PAUSED
def setComingEpsSort(self, sort):
if sort not in ('date', 'network', 'show'):
sort = 'date'
sickbeard.save_config()
if sickbeard.COMING_EPS_LAYOUT == 'calendar':
sort = 'date'
redirect("/episodeView/")
sickbeard.COMING_EPS_SORT = sort
def setEpisodeViewSort(self, sort, redir=1):
if sort not in ('time', 'network', 'show'):
sort = 'time'
redirect("/comingEpisodes/")
sickbeard.EPISODE_VIEW_SORT = sort
def comingEpisodes(self, layout="None"):
""" display the coming episodes """
sickbeard.save_config()
if int(redir):
redirect("/episodeView/")
def episodeView(self, layout="None"):
""" display the episodes """
today_dt = datetime.date.today()
#today = today_dt.toordinal()
yesterday_dt = today_dt - datetime.timedelta(days=1)
@ -363,8 +367,8 @@ class MainHandler(RequestHandler):
tomorrow = (datetime.date.today() + datetime.timedelta(days=1)).toordinal()
next_week_dt = (datetime.date.today() + datetime.timedelta(days=7))
next_week = (next_week_dt + datetime.timedelta(days=1)).toordinal()
if not (layout and layout in ('calendar')) and not (sickbeard.COMING_EPS_LAYOUT and sickbeard.COMING_EPS_LAYOUT in ('calendar')):
recently = (yesterday_dt - datetime.timedelta(days=sickbeard.COMING_EPS_MISSED_RANGE)).toordinal()
if not (layout and layout in 'daybyday') and not (sickbeard.EPISODE_VIEW_LAYOUT and sickbeard.EPISODE_VIEW_LAYOUT in 'daybyday'):
recently = (yesterday_dt - datetime.timedelta(days=sickbeard.EPISODE_VIEW_MISSED_RANGE)).toordinal()
else:
recently = yesterday
@ -379,7 +383,7 @@ class MainHandler(RequestHandler):
for cur_result in sql_results:
done_show_list.append(int(cur_result["showid"]))
if not (layout and layout in ('calendar')) and not (sickbeard.COMING_EPS_LAYOUT and sickbeard.COMING_EPS_LAYOUT in ('calendar')):
if not (layout and layout in 'daybyday') and not (sickbeard.EPISODE_VIEW_LAYOUT and sickbeard.EPISODE_VIEW_LAYOUT in 'daybyday'):
more_sql_results = myDB.select(
"SELECT *, tv_shows.status as show_status FROM tv_episodes outer_eps, tv_shows WHERE season != 0 AND showid NOT IN (" + ','.join(
['?'] * len(
@ -395,49 +399,44 @@ class MainHandler(RequestHandler):
sql_results = list(set(sql_results))
# multi dimension sort
sorts = {
'date': (lambda a, b: cmp(
(a['localtime'],
(a['show_name'], remove_article(a['show_name']))[not sickbeard.SORT_ARTICLE],
a['season'], a['episode']),
(b['localtime'],
(b['show_name'], remove_article(b['show_name']))[not sickbeard.SORT_ARTICLE],
b['season'], b['episode']))),
'show': (lambda a, b: cmp(
((a['show_name'], remove_article(a['show_name']))[not sickbeard.SORT_ARTICLE],
a['localtime'], a['season'], a['episode']),
((b['show_name'], remove_article(b['show_name']))[not sickbeard.SORT_ARTICLE],
b['localtime'], b['season'], b['episode']))),
'network': (lambda a, b: cmp(
(a['network'], a['localtime'],
(a['show_name'], remove_article(a['show_name']))[not sickbeard.SORT_ARTICLE],
a['season'], a['episode']),
(b['network'], b['localtime'],
(b['show_name'], remove_article(b['show_name']))[not sickbeard.SORT_ARTICLE],
b['season'], b['episode'])))
}
# make a dict out of the sql results
sql_results = [dict(row) for row in sql_results]
# multi dimension sort
sorts = {
'network': (lambda a, b: cmp(
(a['data_network'], a['localtime'], a['data_show_name'], a['season'], a['episode']),
(b['data_network'], b['localtime'], b['data_show_name'], b['season'], b['episode']))),
'show': (lambda a, b: cmp(
(a['data_show_name'], a['localtime'], a['season'], a['episode']),
(b['data_show_name'], b['localtime'], b['season'], b['episode']))),
'time': (lambda a, b: cmp(
(a['localtime'], a['data_show_name'], a['season'], a['episode']),
(b['localtime'], b['data_show_name'], b['season'], b['episode'])))
}
def value_maybe_article(value=''):
return (remove_article(value.lower()), value.lower())[sickbeard.SORT_ARTICLE]
# add localtime to the dict
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.COMING_EPS_SORT])
sql_results.sort(sorts[sickbeard.EPISODE_VIEW_SORT])
t = PageTemplate(headers=self.request.headers, file="comingEpisodes.tmpl")
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
# Allow local overriding of layout parameter
if layout and layout in ('poster', 'banner', 'list','calendar'):
if layout and layout in ('banner', 'daybyday', 'list', 'poster'):
t.layout = layout
else:
t.layout = sickbeard.COMING_EPS_LAYOUT
t.layout = sickbeard.EPISODE_VIEW_LAYOUT
return _munge(t)
@ -549,7 +548,7 @@ class PageTemplate(Template):
self.sbPID = str(sickbeard.PID)
self.menu = [
{'title': 'Home', 'key': 'home'},
{'title': 'Coming Episodes', 'key': 'comingEpisodes'},
{'title': 'Episodes', 'key': 'episodeView'},
{'title': 'History', 'key': 'history'},
{'title': 'Manage', 'key': 'manage'},
{'title': 'Config', 'key': 'config'},
@ -630,7 +629,7 @@ class ManageSearches(MainHandler):
# t.backlogPI = sickbeard.backlogSearchScheduler.action.getProgressIndicator()
t.backlogPaused = sickbeard.searchQueueScheduler.action.is_backlog_paused() # @UndefinedVariable
t.backlogRunning = sickbeard.searchQueueScheduler.action.is_backlog_in_progress() # @UndefinedVariable
t.dailySearchStatus = sickbeard.dailySearchScheduler.action.amActive # @UndefinedVariable
t.recentSearchStatus = sickbeard.recentSearchScheduler.action.amActive # @UndefinedVariable
t.findPropersStatus = sickbeard.properFinderScheduler.action.amActive # @UndefinedVariable
t.queueLength = sickbeard.searchQueueScheduler.action.queue_length()
@ -658,10 +657,10 @@ class ManageSearches(MainHandler):
def forceSearch(self, *args, **kwargs):
# force it to run the next time it looks
result = sickbeard.dailySearchScheduler.forceRun()
result = sickbeard.recentSearchScheduler.forceRun()
if result:
logger.log(u"Daily search forced")
ui.notifications.message('Daily search started')
logger.log(u"Recent search forced")
ui.notifications.message('Recent search started')
redirect("/manage/manageSearches/")
@ -1628,10 +1627,10 @@ class ConfigSearch(MainHandler):
def saveSearch(self, use_nzbs=None, use_torrents=None, nzb_dir=None, sab_username=None, sab_password=None,
sab_apikey=None, sab_category=None, sab_host=None, nzbget_username=None, nzbget_password=None,
nzbget_category=None, nzbget_priority=None, nzbget_host=None, nzbget_use_https=None,
backlog_days=None, backlog_frequency=None, dailysearch_frequency=None,
backlog_days=None, backlog_frequency=None, recentsearch_frequency=None,
nzb_method=None, torrent_method=None, usenet_retention=None,
download_propers=None, check_propers_interval=None, allow_high_priority=None,
backlog_startup=None, dailysearch_startup=None,
backlog_startup=None, recentsearch_startup=None,
torrent_dir=None, torrent_username=None, torrent_password=None, torrent_host=None,
torrent_label=None, torrent_path=None, torrent_verify_cert=None,
torrent_seed_time=None, torrent_paused=None, torrent_high_bandwidth=None, ignore_words=None, require_words=None):
@ -1644,7 +1643,7 @@ class ConfigSearch(MainHandler):
if not config.change_TORRENT_DIR(torrent_dir):
results += ["Unable to create directory " + os.path.normpath(torrent_dir) + ", dir not changed."]
config.change_DAILYSEARCH_FREQUENCY(dailysearch_frequency)
config.change_RECENTSEARCH_FREQUENCY(recentsearch_frequency)
config.change_BACKLOG_FREQUENCY(backlog_frequency)
sickbeard.BACKLOG_DAYS = config.to_int(backlog_days, default=7)
@ -1664,7 +1663,7 @@ class ConfigSearch(MainHandler):
sickbeard.ALLOW_HIGH_PRIORITY = config.checkbox_to_value(allow_high_priority)
sickbeard.DAILYSEARCH_STARTUP = config.checkbox_to_value(dailysearch_startup)
sickbeard.RECENTSEARCH_STARTUP = config.checkbox_to_value(recentsearch_startup)
sickbeard.BACKLOG_STARTUP = config.checkbox_to_value(backlog_startup)
sickbeard.SAB_USERNAME = sab_username
@ -2092,10 +2091,10 @@ class ConfigProviders(MainHandler):
newznabProviderDict[cur_id].search_fallback = 0
try:
newznabProviderDict[cur_id].enable_daily = config.checkbox_to_value(
kwargs[cur_id + '_enable_daily'])
newznabProviderDict[cur_id].enable_recentsearch = config.checkbox_to_value(
kwargs[cur_id + '_enable_recentsearch'])
except:
newznabProviderDict[cur_id].enable_daily = 0
newznabProviderDict[cur_id].enable_recentsearch = 0
try:
newznabProviderDict[cur_id].enable_backlog = config.checkbox_to_value(
@ -2258,12 +2257,12 @@ class ConfigProviders(MainHandler):
except:
curTorrentProvider.search_fallback = 0 # these exceptions are catching unselected checkboxes
if hasattr(curTorrentProvider, 'enable_daily'):
if hasattr(curTorrentProvider, 'enable_recentsearch'):
try:
curTorrentProvider.enable_daily = config.checkbox_to_value(
kwargs[curTorrentProvider.getID() + '_enable_daily'])
curTorrentProvider.enable_recentsearch = config.checkbox_to_value(
kwargs[curTorrentProvider.getID() + '_enable_recentsearch'])
except:
curTorrentProvider.enable_daily = 0 # these exceptions are actually catching unselected checkboxes
curTorrentProvider.enable_recentsearch = 0 # these exceptions are actually catching unselected checkboxes
if hasattr(curTorrentProvider, 'enable_backlog'):
try:
@ -2300,12 +2299,12 @@ class ConfigProviders(MainHandler):
except:
curNzbProvider.search_fallback = 0 # these exceptions are actually catching unselected checkboxes
if hasattr(curNzbProvider, 'enable_daily'):
if hasattr(curNzbProvider, 'enable_recentsearch'):
try:
curNzbProvider.enable_daily = config.checkbox_to_value(
kwargs[curNzbProvider.getID() + '_enable_daily'])
curNzbProvider.enable_recentsearch = config.checkbox_to_value(
kwargs[curNzbProvider.getID() + '_enable_recentsearch'])
except:
curNzbProvider.enable_daily = 0 # these exceptions are actually catching unselected checkboxes
curNzbProvider.enable_recentsearch = 0 # these exceptions are actually catching unselected checkboxes
if hasattr(curNzbProvider, 'enable_backlog'):
try:
@ -2980,7 +2979,7 @@ class NewHomeAddShows(MainHandler):
tvdbs = ['tvdb_id', 'tvrage_id']
for index, tvdb in enumerate(tvdbs):
try:
item[u'show_id'] = item[tvdb]
item[u'show_id'] = str(item[tvdb])
tvshow = helpers.findCertainShow(sickbeard.showList, int(item[tvdb]))
except:
continue
@ -3628,6 +3627,17 @@ class Home(MainHandler):
ui.notifications.message('Checking out branch: ', branch)
return self.update(sickbeard.PID)
def pullRequestCheckout(self, branch):
pull_request = branch
branch = branch.split(':')[1]
fetched = sickbeard.versionCheckScheduler.action.fetch(pull_request)
if fetched:
sickbeard.BRANCH = branch
ui.notifications.message('Checking out branch: ', branch)
return self.update(sickbeard.PID)
else:
return redirect('/home/')
def displayShow(self, show=None):
if show is None:

View file

@ -3,163 +3,244 @@ import unittest
import test_lib as test
import sys, os.path
sys.path.append(os.path.abspath('..'))
sys.path.append(os.path.abspath('../lib'))
from sickbeard.name_parser import parser
import sickbeard
sickbeard.SYS_ENCODING = 'UTF-8'
DEBUG = VERBOSE = False
simple_test_cases = {
'standard': {
'Mr.Show.Name.S01E02.Source.Quality.Etc-Group': parser.ParseResult(None, 'Mr Show Name', 1, [2], 'Source.Quality.Etc', 'Group'),
'Show.Name.S01E02': parser.ParseResult(None, 'Show Name', 1, [2]),
'Show Name - S01E02 - My Ep Name': parser.ParseResult(None, 'Show Name', 1, [2], 'My Ep Name'),
'Show.1.0.Name.S01.E03.My.Ep.Name-Group': parser.ParseResult(None, 'Show 1.0 Name', 1, [3], 'My.Ep.Name', 'Group'),
'Show.Name.S01E02E03.Source.Quality.Etc-Group': parser.ParseResult(None, 'Show Name', 1, [2,3], 'Source.Quality.Etc', 'Group'),
'Mr. Show Name - S01E02-03 - My Ep Name': parser.ParseResult(None, 'Mr. Show Name', 1, [2,3], 'My Ep Name'),
'Show.Name.S01.E02.E03': parser.ParseResult(None, 'Show Name', 1, [2,3]),
'Show.Name-0.2010.S01E02.Source.Quality.Etc-Group': parser.ParseResult(None, 'Show Name-0 2010', 1, [2], 'Source.Quality.Etc', 'Group'),
'S01E02 Ep Name': parser.ParseResult(None, None, 1, [2], 'Ep Name'),
'Show Name - S06E01 - 2009-12-20 - Ep Name': parser.ParseResult(None, 'Show Name', 6, [1], '2009-12-20 - Ep Name'),
'Show Name - S06E01 - -30-': parser.ParseResult(None, 'Show Name', 6, [1], '30-' ),
'Show-Name-S06E01-720p': parser.ParseResult(None, 'Show-Name', 6, [1], '720p' ),
'Show-Name-S06E01-1080i': parser.ParseResult(None, 'Show-Name', 6, [1], '1080i' ),
'Show.Name.S06E01.Other.WEB-DL': parser.ParseResult(None, 'Show Name', 6, [1], 'Other.WEB-DL' ),
'Show.Name.S06E01 Some-Stuff Here': parser.ParseResult(None, 'Show Name', 6, [1], 'Some-Stuff Here' ),
},
'fov': {
'Show_Name.1x02.Source_Quality_Etc-Group': parser.ParseResult(None, 'Show Name', 1, [2], 'Source_Quality_Etc', 'Group'),
'Show Name 1x02': parser.ParseResult(None, 'Show Name', 1, [2]),
'Show Name 1x02 x264 Test': parser.ParseResult(None, 'Show Name', 1, [2], 'x264 Test'),
'Show Name - 1x02 - My Ep Name': parser.ParseResult(None, 'Show Name', 1, [2], 'My Ep Name'),
'Show_Name.1x02x03x04.Source_Quality_Etc-Group': parser.ParseResult(None, 'Show Name', 1, [2,3,4], 'Source_Quality_Etc', 'Group'),
'Show Name - 1x02-03-04 - My Ep Name': parser.ParseResult(None, 'Show Name', 1, [2,3,4], 'My Ep Name'),
'1x02 Ep Name': parser.ParseResult(None, None, 1, [2], 'Ep Name'),
'Show-Name-1x02-720p': parser.ParseResult(None, 'Show-Name', 1, [2], '720p'),
'Show-Name-1x02-1080i': parser.ParseResult(None, 'Show-Name', 1, [2], '1080i'),
'Show Name [05x12] Ep Name': parser.ParseResult(None, 'Show Name', 5, [12], 'Ep Name'),
'Show.Name.1x02.WEB-DL': parser.ParseResult(None, 'Show Name', 1, [2], 'WEB-DL'),
},
'standard': {
'Mr.Show.Name.S01E02.Source.Quality.Etc-Group': parser.ParseResult(None, 'Mr Show Name', 1, [2], 'Source.Quality.Etc', 'Group'),
'Show.Name.S01E02': parser.ParseResult(None, 'Show Name', 1, [2]),
'Show Name - S01E02 - My Ep Name': parser.ParseResult(None, 'Show Name', 1, [2], 'My Ep Name'),
'Show.1.0.Name.S01.E03.My.Ep.Name-Group': parser.ParseResult(None, 'Show 1.0 Name', 1, [3], 'My.Ep.Name', 'Group'),
'Show.Name.S01E02E03.Source.Quality.Etc-Group': parser.ParseResult(None, 'Show Name', 1, [2, 3], 'Source.Quality.Etc', 'Group'),
'Mr. Show Name - S01E02-03 - My Ep Name': parser.ParseResult(None, 'Mr. Show Name', 1, [2, 3], 'My Ep Name'),
'Show.Name.S01.E02.E03': parser.ParseResult(None, 'Show Name', 1, [2, 3]),
'Show.Name-0.2010.S01E02.Source.Quality.Etc-Group': parser.ParseResult(None, 'Show Name-0 2010', 1, [2], 'Source.Quality.Etc', 'Group'),
'S01E02 Ep Name': parser.ParseResult(None, None, 1, [2], 'Ep Name'),
'Show Name - S06E01 - 2009-12-20 - Ep Name': parser.ParseResult(None, 'Show Name', 6, [1], '2009-12-20 - Ep Name'),
'Show Name - S06E01 - -30-': parser.ParseResult(None, 'Show Name', 6, [1], '30-'),
'Show-Name-S06E01-720p': parser.ParseResult(None, 'Show-Name', 6, [1], '720p'),
'Show-Name-S06E01-1080i': parser.ParseResult(None, 'Show-Name', 6, [1], '1080i'),
'Show.Name.S06E01.Other.WEB-DL': parser.ParseResult(None, 'Show Name', 6, [1], 'Other.WEB-DL'),
'Show.Name.S06E01 Some-Stuff Here': parser.ParseResult(None, 'Show Name', 6, [1], 'Some-Stuff Here'),
},
'standard_repeat': {
'Show.Name.S01E02.S01E03.Source.Quality.Etc-Group': parser.ParseResult(None, 'Show Name', 1, [2,3], 'Source.Quality.Etc', 'Group'),
'Show.Name.S01E02.S01E03': parser.ParseResult(None, 'Show Name', 1, [2,3]),
'Show Name - S01E02 - S01E03 - S01E04 - Ep Name': parser.ParseResult(None, 'Show Name', 1, [2,3,4], 'Ep Name'),
'Show.Name.S01E02.S01E03.WEB-DL': parser.ParseResult(None, 'Show Name', 1, [2,3], 'WEB-DL'),
},
'fov_repeat': {
'Show.Name.1x02.1x03.Source.Quality.Etc-Group': parser.ParseResult(None, 'Show Name', 1, [2,3], 'Source.Quality.Etc', 'Group'),
'Show.Name.1x02.1x03': parser.ParseResult(None, 'Show Name', 1, [2,3]),
'Show Name - 1x02 - 1x03 - 1x04 - Ep Name': parser.ParseResult(None, 'Show Name', 1, [2,3,4], 'Ep Name'),
'Show.Name.1x02.1x03.WEB-DL': parser.ParseResult(None, 'Show Name', 1, [2,3], 'WEB-DL'),
},
'fov': {
'Show_Name.1x02.Source_Quality_Etc-Group': parser.ParseResult(None, 'Show Name', 1, [2], 'Source_Quality_Etc', 'Group'),
'Show Name 1x02': parser.ParseResult(None, 'Show Name', 1, [2]),
'Show Name 1x02 x264 Test': parser.ParseResult(None, 'Show Name', 1, [2], 'x264 Test'),
'Show Name - 1x02 - My Ep Name': parser.ParseResult(None, 'Show Name', 1, [2], 'My Ep Name'),
'Show_Name.1x02x03x04.Source_Quality_Etc-Group': parser.ParseResult(None, 'Show Name', 1, [2, 3, 4], 'Source_Quality_Etc', 'Group'),
'Show Name - 1x02-03-04 - My Ep Name': parser.ParseResult(None, 'Show Name', 1, [2, 3, 4], 'My Ep Name'),
'1x02 Ep Name': parser.ParseResult(None, None, 1, [2], 'Ep Name'),
'Show-Name-1x02-720p': parser.ParseResult(None, 'Show-Name', 1, [2], '720p'),
'Show-Name-1x02-1080i': parser.ParseResult(None, 'Show-Name', 1, [2], '1080i'),
'Show Name [05x12] Ep Name': parser.ParseResult(None, 'Show Name', 5, [12], 'Ep Name'),
'Show.Name.1x02.WEB-DL': parser.ParseResult(None, 'Show Name', 1, [2], 'WEB-DL'),
},
'bare': {
'Show.Name.102.Source.Quality.Etc-Group': parser.ParseResult(None, 'Show Name', 1, [2], 'Source.Quality.Etc', 'Group'),
'show.name.2010.123.source.quality.etc-group': parser.ParseResult(None, 'show name 2010', 1, [23], 'source.quality.etc', 'group'),
'show.name.2010.222.123.source.quality.etc-group': parser.ParseResult(None, 'show name 2010.222', 1, [23], 'source.quality.etc', 'group'),
'Show.Name.102': parser.ParseResult(None, 'Show Name', 1, [2]),
'the.event.401.hdtv-lol': parser.ParseResult(None, 'the event', 4, [1], 'hdtv', 'lol'),
'show.name.2010.special.hdtv-blah': None,
},
'stupid': {
'tpz-abc102': parser.ParseResult(None, None, 1, [2], None, 'tpz'),
'tpz-abc.102': parser.ParseResult(None, None, 1, [2], None, 'tpz'),
},
'no_season': {
'Show Name - 01 - Ep Name': parser.ParseResult(None, 'Show Name', None, [1], 'Ep Name'),
'01 - Ep Name': parser.ParseResult(None, None, None, [1], 'Ep Name'),
'Show Name - 01 - Ep Name - WEB-DL': parser.ParseResult(None, 'Show Name', None, [1], 'Ep Name - WEB-DL'),
},
'standard_repeat': {
'Show.Name.S01E02.S01E03.Source.Quality.Etc-Group': parser.ParseResult(None, 'Show Name', 1, [2, 3], 'Source.Quality.Etc', 'Group'),
'Show.Name.S01E02.S01E03': parser.ParseResult(None, 'Show Name', 1, [2, 3]),
'Show Name - S01E02 - S01E03 - S01E04 - Ep Name': parser.ParseResult(None, 'Show Name', 1, [2, 3, 4], 'Ep Name'),
'Show.Name.S01E02.S01E03.WEB-DL': parser.ParseResult(None, 'Show Name', 1, [2, 3], 'WEB-DL'),
},
'no_season_general': {
'Show.Name.E23.Source.Quality.Etc-Group': parser.ParseResult(None, 'Show Name', None, [23], 'Source.Quality.Etc', 'Group'),
'Show Name - Episode 01 - Ep Name': parser.ParseResult(None, 'Show Name', None, [1], 'Ep Name'),
'Show.Name.Part.3.Source.Quality.Etc-Group': parser.ParseResult(None, 'Show Name', None, [3], 'Source.Quality.Etc', 'Group'),
'Show.Name.Part.1.and.Part.2.Blah-Group': parser.ParseResult(None, 'Show Name', None, [1,2], 'Blah', 'Group'),
'Show.Name.Part.IV.Source.Quality.Etc-Group': parser.ParseResult(None, 'Show Name', None, [4], 'Source.Quality.Etc', 'Group'),
'Deconstructed.E07.1080i.HDTV.DD5.1.MPEG2-TrollHD': parser.ParseResult(None, 'Deconstructed', None, [7], '1080i.HDTV.DD5.1.MPEG2', 'TrollHD'),
'Show.Name.E23.WEB-DL': parser.ParseResult(None, 'Show Name', None, [23], 'WEB-DL'),
},
'fov_repeat': {
'Show.Name.1x02.1x03.Source.Quality.Etc-Group': parser.ParseResult(None, 'Show Name', 1, [2, 3], 'Source.Quality.Etc', 'Group'),
'Show.Name.1x02.1x03': parser.ParseResult(None, 'Show Name', 1, [2, 3]),
'Show Name - 1x02 - 1x03 - 1x04 - Ep Name': parser.ParseResult(None, 'Show Name', 1, [2, 3, 4], 'Ep Name'),
'Show.Name.1x02.1x03.WEB-DL': parser.ParseResult(None, 'Show Name', 1, [2, 3], 'WEB-DL'),
},
'no_season_multi_ep': {
'Show.Name.E23-24.Source.Quality.Etc-Group': parser.ParseResult(None, 'Show Name', None, [23,24], 'Source.Quality.Etc', 'Group'),
'Show Name - Episode 01-02 - Ep Name': parser.ParseResult(None, 'Show Name', None, [1,2], 'Ep Name'),
'Show.Name.E23-24.WEB-DL': parser.ParseResult(None, 'Show Name', None, [23,24], 'WEB-DL'),
},
'bare': {
'Show.Name.102.Source.Quality.Etc-Group': parser.ParseResult(None, 'Show Name', 1, [2], 'Source.Quality.Etc', 'Group'),
'show.name.2010.123.source.quality.etc-group': parser.ParseResult(None, 'show name 2010', 1, [23], 'source.quality.etc', 'group'),
'show.name.2010.222.123.source.quality.etc-group': parser.ParseResult(None, 'show name 2010.222', 1, [23], 'source.quality.etc', 'group'),
'Show.Name.102': parser.ParseResult(None, 'Show Name', 1, [2]),
'the.event.401.hdtv-lol': parser.ParseResult(None, 'the event', 4, [1], 'hdtv', 'lol'),
# 'show.name.2010.special.hdtv-blah': None,
},
'season_only': {
'Show.Name.S02.Source.Quality.Etc-Group': parser.ParseResult(None, 'Show Name', 2, [], 'Source.Quality.Etc', 'Group'),
'Show Name Season 2': parser.ParseResult(None, 'Show Name', 2),
'Season 02': parser.ParseResult(None, None, 2),
},
'scene_date_format': {
'Show.Name.2010.11.23.Source.Quality.Etc-Group': parser.ParseResult(None, 'Show Name', None, [], 'Source.Quality.Etc', 'Group', datetime.date(2010,11,23)),
'Show Name - 2010.11.23': parser.ParseResult(None, 'Show Name', air_date = datetime.date(2010,11,23)),
'Show.Name.2010.23.11.Source.Quality.Etc-Group': parser.ParseResult(None, 'Show Name', None, [], 'Source.Quality.Etc', 'Group', datetime.date(2010,11,23)),
'Show Name - 2010-11-23 - Ep Name': parser.ParseResult(None, 'Show Name', extra_info = 'Ep Name', air_date = datetime.date(2010,11,23)),
'2010-11-23 - Ep Name': parser.ParseResult(None, extra_info = 'Ep Name', air_date = datetime.date(2010,11,23)),
'Show.Name.2010.11.23.WEB-DL': parser.ParseResult(None, 'Show Name', None, [], 'WEB-DL', None, datetime.date(2010,11,23)),
},
}
'stupid': {
'tpz-abc102': parser.ParseResult(None, None, 1, [2], None, 'tpz'),
'tpz-abc.102': parser.ParseResult(None, None, 1, [2], None, 'tpz'),
},
'no_season': {
'Show Name - 01 - Ep Name': parser.ParseResult(None, 'Show Name', None, [1], 'Ep Name'),
'01 - Ep Name': parser.ParseResult(None, None, None, [1], 'Ep Name'),
'Show Name - 01 - Ep Name - WEB-DL': parser.ParseResult(None, 'Show Name', None, [1], 'Ep Name - WEB-DL'),
},
'no_season_general': {
'Show.Name.E23.Source.Quality.Etc-Group': parser.ParseResult(None, 'Show Name', None, [23], 'Source.Quality.Etc', 'Group'),
'Show Name - Episode 01 - Ep Name': parser.ParseResult(None, 'Show Name', None, [1], 'Ep Name'),
'Show.Name.Part.3.Source.Quality.Etc-Group': parser.ParseResult(None, 'Show Name', None, [3], 'Source.Quality.Etc', 'Group'),
'Show.Name.Part.1.and.Part.2.Blah-Group': parser.ParseResult(None, 'Show Name', None, [1, 2], 'Blah', 'Group'),
'Show.Name.Part.IV.Source.Quality.Etc-Group': parser.ParseResult(None, 'Show Name', None, [4], 'Source.Quality.Etc', 'Group'),
'Deconstructed.E07.1080i.HDTV.DD5.1.MPEG2-TrollHD': parser.ParseResult(None, 'Deconstructed', None, [7], '1080i.HDTV.DD5.1.MPEG2', 'TrollHD'),
'Show.Name.E23.WEB-DL': parser.ParseResult(None, 'Show Name', None, [23], 'WEB-DL'),
},
'no_season_multi_ep': {
'Show.Name.E23-24.Source.Quality.Etc-Group': parser.ParseResult(None, 'Show Name', None, [23, 24], 'Source.Quality.Etc', 'Group'),
'Show Name - Episode 01-02 - Ep Name': parser.ParseResult(None, 'Show Name', None, [1, 2], 'Ep Name'),
'Show.Name.E23-24.WEB-DL': parser.ParseResult(None, 'Show Name', None, [23, 24], 'WEB-DL'),
},
'season_only': {
'Show.Name.S02.Source.Quality.Etc-Group': parser.ParseResult(None, 'Show Name', 2, [], 'Source.Quality.Etc', 'Group'),
'Show Name Season 2': parser.ParseResult(None, 'Show Name', 2),
'Season 02': parser.ParseResult(None, None, 2),
},
'scene_date_format': {
'Show.Name.2010.11.23.Source.Quality.Etc-Group': parser.ParseResult(None, 'Show Name', None, [], 'Source.Quality.Etc', 'Group', datetime.date(2010, 11, 23)),
'Show Name - 2010.11.23': parser.ParseResult(None, 'Show Name', air_date=datetime.date(2010, 11, 23)),
'Show.Name.2010.23.11.Source.Quality.Etc-Group': parser.ParseResult(None, 'Show Name', None, [], 'Source.Quality.Etc', 'Group', datetime.date(2010, 11, 23)),
'Show Name - 2010-11-23 - Ep Name': parser.ParseResult(None, 'Show Name', extra_info='Ep Name', air_date=datetime.date(2010, 11, 23)),
'2010-11-23 - Ep Name': parser.ParseResult(None, extra_info='Ep Name', air_date=datetime.date(2010, 11, 23)),
'Show.Name.2010.11.23.WEB-DL': parser.ParseResult(None, 'Show Name', None, [], 'WEB-DL', None, datetime.date(2010, 11, 23)),
},
'anime_ultimate': {
'[Tsuki] Bleach - 301 [1280x720][61D1D4EE]': parser.ParseResult(None, 'Bleach', None, [], '1280x720', 'Tsuki', None, [301]),
'[Tsuki] Fairy Tail - 70 [1280x720][C4807111]': parser.ParseResult(None, 'Fairy Tail', None, [], '1280x720', 'Tsuki', None, [70]),
'[SGKK] Bleach 312v2 [720p MKV]': parser.ParseResult(None, 'Bleach', None, [], '720p MKV', 'SGKK', None, [312]),
'[BSS-Anon] Tengen Toppa Gurren Lagann - 22-23 [1280x720][h264][6039D9AF]': parser.ParseResult(None, 'Tengen Toppa Gurren Lagann', None, [], '1280x720', 'BSS-Anon', None, [22, 23]),
'[SJSUBS]_Naruto_Shippuden_-_02_[480p AAC]': parser.ParseResult(None, 'Naruto Shippuden', None, [], '480p AAC', 'SJSUBS', None, [2]),
'[SFW-Chihiro] Dance in the Vampire Bund - 12 [1920x1080 Blu-ray FLAC][2F6DBC66].mkv': parser.ParseResult(None, 'Dance in the Vampire Bund', None, [], '1920x1080 Blu-ray FLAC', 'SFW-Chihiro', None, [12]),
'[SHiN-gx] Hanasaku Iroha - 01 [1280x720 h.264 AAC][BDC36683]': parser.ParseResult(None, 'Hanasaku Iroha', None, [], '1280x720 h.264 AAC', 'SHiN-gx', None, [1]),
'[SFW-Chihiro] Dance in the Vampire Bund - 02 [1920x1080 Blu-ray FLAC][C1FA0A09]': parser.ParseResult(None, 'Dance in the Vampire Bund', None, [], '1920x1080 Blu-ray FLAC', 'SFW-Chihiro', None, [2]),
'[HorribleSubs] No. 6 - 11 [720p]': parser.ParseResult(None, 'No. 6', None, [], '720p', 'HorribleSubs', None, [11]),
'[HorribleSubs] D Gray-Man - 312 (480p) [F501C9BE]': parser.ParseResult(None, 'D Gray-Man', None, [], '480p', 'HorribleSubs', None, [312]),
'[SGKK] Tengen Toppa Gurren Lagann - 45-46 (720p h264) [F501C9BE]': parser.ParseResult(None, 'Tengen Toppa Gurren Lagann', None, [], '720p h264', 'SGKK', None, [45, 46]),
'[Stratos-Subs]_Infinite_Stratos_-_12_(1280x720_H.264_AAC)_[379759DB]': parser.ParseResult(None, 'Infinite Stratos', None, [], '1280x720_H.264_AAC', 'Stratos-Subs', None, [12]),
'[ShinBunBu-Subs] Bleach - 02-03 (CX 1280x720 x264 AAC)': parser.ParseResult(None, 'Bleach', None, [], 'CX 1280x720 x264 AAC', 'ShinBunBu-Subs', None, [02, 03]),
'[Doki] Hanasaku Iroha - 03 (848x480 h264 AAC) [CB1AA73B]': parser.ParseResult(None, 'Hanasaku Iroha', None, [], '848x480 h264 AAC', 'Doki', None, [03]),
'[UTW]_Fractal_-_01_[h264-720p][96D3F1BF]': parser.ParseResult(None, 'Fractal', None, [], 'h264-720p', 'UTW', None, [1]),
'[a-s]_inuyasha_-_028_rs2_[BFDDF9F2]': parser.ParseResult(None, 'inuyasha', None, [], 'BFDDF9F2', 'a-s', None, [28]),
'[HorribleSubs] Fairy Tail S2 - 37 [1080p]': parser.ParseResult(None,'Fairy Tail S2', None, [], '1080p', 'HorribleSubs', None, [37]),
'[HorribleSubs] Sword Art Online II - 23 [720p]': parser.ParseResult(None, 'Sword Art Online II', None, [], '720p', 'HorribleSubs', None, [23])
},
'anime_ep_name': {
'[TzaTziki]_One_Piece_279_Chopper_Man_1_[720p][8AE5F25D]': parser.ParseResult(None, 'One Piece', None, [], '720p', 'TzaTziki', None, [279]),
"[ACX]Wolf's_Rain_-_04_-_Scars_in_the_Wasteland_[octavarium]_[82B7E357]": parser.ParseResult(None, "Wolf's Rain", None, [], 'octavarium', 'ACX', None, [4]),
'[ACX]Black Lagoon - 02v2 - Mangrove Heaven [SaintDeath] [7481F875]': parser.ParseResult(None, 'Black Lagoon', None, [], 'SaintDeath', 'ACX', None, [2]),
},
"anime_standard_round": {
'[SGKK] Bleach - 312v2 (1280x720 h264 AAC) [F501C9BE]': parser.ParseResult(None, 'Bleach', None, [], '1280x720 h264 AAC', 'SGKK', None, [312]),
},
'anime_slash': {
'[SGKK] Bleach 312v1 [720p/MKV]': parser.ParseResult(None, 'Bleach', None, [], '720p', 'SGKK', None, [312]),
'[SGKK] Bleach 312 [480p/MKV]': parser.ParseResult(None, 'Bleach', None, [], '480p', 'SGKK', None, [312])
},
'anime_standard_codec': {
'[Ayako]_Infinite_Stratos_-_IS_-_07_[H264][720p][EB7838FC]': parser.ParseResult(None, 'Infinite Stratos', None, [], '720p', 'Ayako', None, [7]),
'[Ayako] Infinite Stratos - IS - 07v2 [H264][720p][44419534]': parser.ParseResult(None, 'Infinite Stratos', None, [], '720p', 'Ayako', None, [7]),
'[Ayako-Shikkaku] Oniichan no Koto Nanka Zenzen Suki Janain Dakara ne - 10 [LQ][h264][720p] [8853B21C]': parser.ParseResult(None, 'Oniichan no Koto Nanka Zenzen Suki Janain Dakara ne', None, [], '720p', 'Ayako-Shikkaku', None, [10]),
'[Tsuki] Fairy Tail - 72 [XviD][C4807111]': parser.ParseResult(None, 'Fairy Tail', None, [], 'C4807111', 'Tsuki', None, [72]),
'Bubblegum Crisis Tokyo 2040 - 25 [aX] [F4E2E558]': parser.ParseResult(None, 'Bubblegum Crisis Tokyo 2040', None, [], "aX", None, None, [25]),
},
'anime_and_normal': {
'Bleach - s02e03 - 012 - Name & Name': parser.ParseResult(None, 'Bleach', 2, [3], None, None, None, [12]),
'Bleach - s02e03e04 - 012-013 - Name & Name': parser.ParseResult(None, 'Bleach', 2, [3, 4], None, None, None, [12, 13]),
'Bleach - s16e03-04 - 313-314': parser.ParseResult(None, 'Bleach', 16, [3, 4], None, None, None, [313, 314]),
'Blue Submarine No. 6 s16e03e04 313-314': parser.ParseResult(None, 'Blue Submarine No. 6', 16, [3, 4], None, None, None, [313, 314]),
'Bleach.s16e03-04.313-314': parser.ParseResult(None, 'Bleach', 16, [3, 4], None, None, None, [313, 314]),
'.hack roots s01e01 001.mkv': parser.ParseResult(None, 'hack roots', 1, [1], None, None, None, [1]),
'.hack sign s01e01 001.mkv': parser.ParseResult(None, 'hack sign', 1, [1], None, None, None, [1])
},
'anime_and_normal_reverse': {
'Bleach - 012 - s02e03 - Name & Name': parser.ParseResult(None, 'Bleach', 2, [3], None, None, None, [12]),
'Blue Submarine No. 6 - 012-013 - s02e03e04 - Name & Name': parser.ParseResult(None, 'Blue Submarine No. 6', 2, [3, 4], None, None, None, [12, 13]),
'07-GHOST - 012-013 - s02e03e04 - Name & Name': parser.ParseResult(None, '07-GHOST', 2, [3, 4], None, None, None, [12, 13]),
'3x3 Eyes - 012-013 - s02e03-04 - Name & Name': parser.ParseResult(None, '3x3 Eyes', 2, [3, 4], None, None, None, [12, 13]),
},
'anime_and_normal_front': {
'165.Naruto Shippuuden.s08e014': parser.ParseResult(None, 'Naruto Shippuuden', 8, [14], None, None, None, [165]),
'165-166.Naruto Shippuuden.s08e014e015': parser.ParseResult(None, 'Naruto Shippuuden', 8, [14, 15], None, None, None, [165, 166]),
'165-166.07-GHOST.s08e014-015': parser.ParseResult(None, '07-GHOST', 8, [14, 15], None, None, None, [165, 166]),
'165-166.3x3 Eyes.S08E014E015': parser.ParseResult(None, '3x3 Eyes', 8, [14, 15], None, None, None, [165, 166]),
},
'anime_bare': {
'One Piece 102': parser.ParseResult(None, 'One Piece', None, [], None, None, None, [102]),
'bleach - 010': parser.ParseResult(None, 'bleach', None, [], None, None, None, [10]),
'Naruto Shippuden - 314v2': parser.ParseResult(None, 'Naruto Shippuden', None, [], None, None, None, [314]),
'Blue Submarine No. 6 104-105': parser.ParseResult(None, 'Blue Submarine No. 6', None, [], None, None, None, [104, 105]),
'Samurai X: Trust & Betrayal (OVA) 001-002': parser.ParseResult(None, 'Samurai X: Trust & Betrayal (OVA)', None, [], None, None, None, [1, 2]),
"[ACX]_Wolf's_Spirit_001.mkv": parser.ParseResult(None, "Wolf's Spirit", None, [], None, 'ACX', None, [1])
}
}
combination_test_cases = [
('/test/path/to/Season 02/03 - Ep Name.avi',
parser.ParseResult(None, None, 2, [3], 'Ep Name'),
['no_season', 'season_only']),
('Show.Name.S02.Source.Quality.Etc-Group/tpz-sn203.avi',
parser.ParseResult(None, 'Show Name', 2, [3], 'Source.Quality.Etc', 'Group'),
['stupid', 'season_only']),
('/test/path/to/Season 02/03 - Ep Name.avi',
parser.ParseResult(None, None, 2, [3], 'Ep Name'),
['no_season', 'season_only']),
('MythBusters.S08E16.720p.HDTV.x264-aAF/aaf-mb.s08e16.720p.mkv',
parser.ParseResult(None, 'MythBusters', 8, [16], '720p.HDTV.x264', 'aAF'),
['standard']),
('/home/drop/storage/TV/Terminator The Sarah Connor Chronicles/Season 2/S02E06 The Tower is Tall, But the Fall is Short.mkv',
parser.ParseResult(None, None, 2, [6], 'The Tower is Tall, But the Fall is Short'),
['standard']),
(r'/Test/TV/Jimmy Fallon/Season 2/Jimmy Fallon - 2010-12-15 - blah.avi',
parser.ParseResult(None, 'Jimmy Fallon', extra_info = 'blah', air_date = datetime.date(2010,12,15)),
['scene_date_format']),
('Show.Name.S02.Source.Quality.Etc-Group/tpz-sn203.avi',
parser.ParseResult(None, 'Show Name', 2, [3], 'Source.Quality.Etc', 'Group'),
['stupid', 'season_only']),
(r'/X/30 Rock/Season 4/30 Rock - 4x22 -.avi',
parser.ParseResult(None, '30 Rock', 4, [22]),
['fov']),
('Season 2\\Show Name - 03-04 - Ep Name.ext',
parser.ParseResult(None, 'Show Name', 2, [3,4], extra_info = 'Ep Name'),
['no_season', 'season_only']),
('Season 02\\03-04-05 - Ep Name.ext',
parser.ParseResult(None, None, 2, [3,4,5], extra_info = 'Ep Name'),
['no_season', 'season_only']),
]
('MythBusters.S08E16.720p.HDTV.x264-aAF/aaf-mb.s08e16.720p.mkv',
parser.ParseResult(None, 'MythBusters', 8, [16], '720p.HDTV.x264', 'aAF'),
['standard']),
('/home/drop/storage/TV/Terminator The Sarah Connor Chronicles/Season 2/S02E06 The Tower is Tall, But the Fall is Short.mkv',
parser.ParseResult(None, None, 2, [6], 'The Tower is Tall, But the Fall is Short'),
['standard']),
(r'/Test/TV/Jimmy Fallon/Season 2/Jimmy Fallon - 2010-12-15 - blah.avi',
parser.ParseResult(None, 'Jimmy Fallon', extra_info='blah', air_date=datetime.date(2010, 12, 15)),
['scene_date_format']),
(r'/X/30 Rock/Season 4/30 Rock - 4x22 -.avi',
parser.ParseResult(None, '30 Rock', 4, [22]),
['fov']),
('Season 2\\Show Name - 03-04 - Ep Name.ext',
parser.ParseResult(None, 'Show Name', 2, [3, 4], extra_info='Ep Name'),
['no_season', 'season_only']),
('Season 02\\03-04-05 - Ep Name.ext',
parser.ParseResult(None, None, 2, [3, 4, 5], extra_info='Ep Name'),
['no_season', 'season_only']),
]
unicode_test_cases = [
(u'The.Big.Bang.Theory.2x07.The.Panty.Pi\xf1ata.Polarization.720p.HDTV.x264.AC3-SHELDON.mkv',
parser.ParseResult(None, 'The.Big.Bang.Theory', 2, [7], '720p.HDTV.x264.AC3', 'SHELDON')
),
('The.Big.Bang.Theory.2x07.The.Panty.Pi\xc3\xb1ata.Polarization.720p.HDTV.x264.AC3-SHELDON.mkv',
parser.ParseResult(None, 'The.Big.Bang.Theory', 2, [7], '720p.HDTV.x264.AC3', 'SHELDON')
),
]
(u'The.Big.Bang.Theory.2x07.The.Panty.Pi\xf1ata.Polarization.720p.HDTV.x264.AC3-SHELDON.mkv',
parser.ParseResult(None, 'The.Big.Bang.Theory', 2, [7], '720p.HDTV.x264.AC3', 'SHELDON')
),
('The.Big.Bang.Theory.2x07.The.Panty.Pi\xc3\xb1ata.Polarization.720p.HDTV.x264.AC3-SHELDON.mkv',
parser.ParseResult(None, 'The.Big.Bang.Theory', 2, [7], '720p.HDTV.x264.AC3', 'SHELDON')
),
]
failure_cases = ['7sins-jfcs01e09-720p-bluray-x264']
class UnicodeTests(test.SickbeardTestDBCase):
def _test_unicode(self, name, result):
np = parser.NameParser(True)
@ -170,47 +251,46 @@ class UnicodeTests(test.SickbeardTestDBCase):
# this shouldn't raise an exception
a = repr(str(parse_result))
def test_unicode(self):
for (name, result) in unicode_test_cases:
self._test_unicode(name, result)
class FailureCaseTests(test.SickbeardTestDBCase):
def _test_name(self, name):
np = parser.NameParser(True)
try:
parse_result = np.parse(name)
except (parser.InvalidNameException, parser.InvalidShowException):
return True
if VERBOSE:
print 'Actual: ', parse_result.which_regex, parse_result
return False
def test_failures(self):
for name in failure_cases:
self.assertTrue(self._test_name(name))
class ComboTests(test.SickbeardTestDBCase):
def _test_combo(self, name, result, which_regexes):
if VERBOSE:
print
print 'Testing', name
print 'Testing', name
np = parser.NameParser(True)
try:
test_result = np.parse(name)
except parser.InvalidShowException:
return False
if DEBUG:
print test_result, test_result.which_regex
print result, which_regexes
self.assertEqual(test_result, result)
for cur_regex in which_regexes:
@ -218,14 +298,14 @@ class ComboTests(test.SickbeardTestDBCase):
self.assertEqual(len(which_regexes), len(test_result.which_regex))
def test_combos(self):
for (name, result, which_regexes) in combination_test_cases:
# Normalise the paths. Converts UNIX-style paths into Windows-style
# paths when test is run on Windows.
self._test_combo(os.path.normpath(name), result, which_regexes)
class BasicTests(test.SickbeardTestDBCase):
class BasicTests(test.SickbeardTestDBCase):
def _test_names(self, np, section, transform=None, verbose=False):
if VERBOSE or verbose:
@ -245,109 +325,150 @@ class BasicTests(test.SickbeardTestDBCase):
return
else:
test_result = np.parse(cur_test)
if DEBUG or verbose:
try:
# self.assertEqual(test_result.which_regex, [section])
self.assertEqual(test_result, result)
except:
print 'air_by_date:', test_result.is_air_by_date, 'air_date:', test_result.air_date
print 'anime:', test_result.is_anime, 'ab_episode_numbers:', test_result.ab_episode_numbers
print test_result
print result
self.assertEqual(test_result.which_regex, [section])
self.assertEqual(test_result, result)
raise
#def test_standard_names(self):
# np = parser.NameParser(False)
# self._test_names(np, 'standard')
#
#def test_standard_repeat_names(self):
# np = parser.NameParser(False)
# self._test_names(np, 'standard_repeat')
#
#def test_fov_names(self):
# np = parser.NameParser(False)
# self._test_names(np, 'fov')
#
#def test_fov_repeat_names(self):
# np = parser.NameParser(False)
# self._test_names(np, 'fov_repeat')
#
#def test_bare_names(self):
# np = parser.NameParser(False)
# self._test_names(np, 'bare')
#
#def test_stupid_names(self):
# np = parser.NameParser(False)
# self._test_names(np, 'stupid')
#
#def test_no_season_names(self):
# np = parser.NameParser(False)
# self._test_names(np, 'no_season')
#
#def test_no_season_general_names(self):
# np = parser.NameParser(False)
# self._test_names(np, 'no_season_general')
#
#def test_no_season_multi_ep_names(self):
# np = parser.NameParser(False)
# self._test_names(np, 'no_season_multi_ep')
#
#def test_season_only_names(self):
# np = parser.NameParser(False)
# self._test_names(np, 'season_only')
#
#def test_scene_date_format_names(self):
# np = parser.NameParser(False)
# self._test_names(np, 'scene_date_format')
#
#def test_standard_file_names(self):
# np = parser.NameParser()
# self._test_names(np, 'standard', lambda x: x + '.avi')
#
#def test_standard_repeat_file_names(self):
# np = parser.NameParser()
# self._test_names(np, 'standard_repeat', lambda x: x + '.avi')
#
#def test_fov_file_names(self):
# np = parser.NameParser()
# self._test_names(np, 'fov', lambda x: x + '.avi')
#
#def test_fov_repeat_file_names(self):
# np = parser.NameParser()
# self._test_names(np, 'fov_repeat', lambda x: x + '.avi')
#
#def test_bare_file_names(self):
# np = parser.NameParser()
# self._test_names(np, 'bare', lambda x: x + '.avi')
#
#def test_stupid_file_names(self):
# np = parser.NameParser()
# self._test_names(np, 'stupid', lambda x: x + '.avi')
#
#def test_no_season_file_names(self):
# np = parser.NameParser()
# self._test_names(np, 'no_season', lambda x: x + '.avi')
#
#def test_no_season_general_file_names(self):
# np = parser.NameParser()
# self._test_names(np, 'no_season_general', lambda x: x + '.avi')
#
#def test_no_season_multi_ep_file_names(self):
# np = parser.NameParser()
# self._test_names(np, 'no_season_multi_ep', lambda x: x + '.avi')
#
#def test_season_only_file_names(self):
# np = parser.NameParser()
# self._test_names(np, 'season_only', lambda x: x + '.avi')
#
#def test_scene_date_format_file_names(self):
# np = parser.NameParser()
# self._test_names(np, 'scene_date_format', lambda x: x + '.avi')
def test_standard_names(self):
np = parser.NameParser(False, testing=True)
self._test_names(np, 'standard')
def test_standard_repeat_names(self):
np = parser.NameParser(False, testing=True)
self._test_names(np, 'standard_repeat')
def test_fov_names(self):
np = parser.NameParser(False, testing=True)
self._test_names(np, 'fov')
def test_fov_repeat_names(self):
np = parser.NameParser(False, testing=True)
self._test_names(np, 'fov_repeat')
def test_bare_names(self):
np = parser.NameParser(False, testing=True)
self._test_names(np, 'bare')
def test_stupid_names(self):
np = parser.NameParser(False, testing=True)
self._test_names(np, 'stupid')
def test_no_season_names(self):
np = parser.NameParser(False, testing=True)
self._test_names(np, 'no_season')
def test_no_season_general_names(self):
np = parser.NameParser(False, testing=True)
self._test_names(np, 'no_season_general')
def test_no_season_multi_ep_names(self):
np = parser.NameParser(False, testing=True)
self._test_names(np, 'no_season_multi_ep')
def test_season_only_names(self):
np = parser.NameParser(False, testing=True)
self._test_names(np, 'season_only')
def test_scene_date_format_names(self):
np = parser.NameParser(False, testing=True)
self._test_names(np, 'scene_date_format')
def test_standard_file_names(self):
np = parser.NameParser(testing=True)
self._test_names(np, 'standard', lambda x: x + '.avi')
def test_standard_repeat_file_names(self):
np = parser.NameParser(testing=True)
self._test_names(np, 'standard_repeat', lambda x: x + '.avi')
def test_fov_file_names(self):
np = parser.NameParser(testing=True)
self._test_names(np, 'fov', lambda x: x + '.avi')
def test_fov_repeat_file_names(self):
np = parser.NameParser(testing=True)
self._test_names(np, 'fov_repeat', lambda x: x + '.avi')
def test_bare_file_names(self):
np = parser.NameParser(testing=True)
self._test_names(np, 'bare', lambda x: x + '.avi')
def test_stupid_file_names(self):
np = parser.NameParser(testing=True)
self._test_names(np, 'stupid', lambda x: x + '.avi')
def test_no_season_file_names(self):
np = parser.NameParser(testing=True)
self._test_names(np, 'no_season', lambda x: x + '.avi')
def test_no_season_general_file_names(self):
np = parser.NameParser(testing=True)
self._test_names(np, 'no_season_general', lambda x: x + '.avi')
def test_no_season_multi_ep_file_names(self):
np = parser.NameParser(testing=True)
self._test_names(np, 'no_season_multi_ep', lambda x: x + '.avi')
def test_season_only_file_names(self):
np = parser.NameParser(testing=True)
self._test_names(np, 'season_only', lambda x: x + '.avi')
def test_scene_date_format_file_names(self):
np = parser.NameParser(testing=True)
self._test_names(np, 'scene_date_format', lambda x: x + '.avi')
def test_combination_names(self):
pass
def test_anime_ultimate(self):
np = parser.NameParser(False, TVShow(is_anime=True), testing=True)
self._test_names(np, 'anime_ultimate')
def test_anime_ep_name(self):
np = parser.NameParser(False, TVShow(is_anime=True), testing=True)
self._test_names(np, 'anime_ep_name')
def test_anime_slash(self):
np = parser.NameParser(False, TVShow(is_anime=True), testing=True)
self._test_names(np, 'anime_slash')
def test_anime_codec(self):
np = parser.NameParser(False, TVShow(is_anime=True), testing=True)
self._test_names(np, 'anime_standard_codec')
def test_anime_and_normal(self):
np = parser.NameParser(False, TVShow(is_anime=True), testing=True)
self._test_names(np, 'anime_and_normal')
def test_anime_and_normal_reverse(self):
np = parser.NameParser(False, TVShow(is_anime=True), testing=True)
self._test_names(np, 'anime_and_normal_reverse')
def test_anime_and_normal_front(self):
np = parser.NameParser(False, TVShow(is_anime=True), testing=True)
self._test_names(np, 'anime_and_normal_front')
def test_anime_bare(self):
np = parser.NameParser(False, TVShow(is_anime=True), testing=True)
self._test_names(np, 'anime_bare')
class TVShow(object):
def __init__(self, is_anime=False):
self.is_anime = is_anime
if __name__ == '__main__':
if len(sys.argv) > 1:
suite = unittest.TestLoader().loadTestsFromName('name_parser_tests.BasicTests.test_'+sys.argv[1])
suite = unittest.TestLoader().loadTestsFromName('name_parser_tests.BasicTests.test_' + sys.argv[1])
else:
suite = unittest.TestLoader().loadTestsFromTestCase(BasicTests)
unittest.TextTestRunner(verbosity=2).run(suite)