diff --git a/gui/slick/js/confirmations.js b/gui/slick/js/confirmations.js index 6e2625edec9f5fa3bcc07ecbbc4fe0d9f29b75a1..577fbfefb8ba9bbaff6034bf8746ce944ab00d9a 100644 --- a/gui/slick/js/confirmations.js +++ b/gui/slick/js/confirmations.js @@ -1,7 +1,7 @@ $(document).ready(function () { - $('a.shutdown').bind("click",function(e) { + $('a.shutdown').bind('click', function(e) { e.preventDefault(); - var target = $( this ).attr('href'); + var target = $(this).attr('href'); $.confirm({ 'title' : 'Shutdown', 'message' : 'Are you sure you want to shutdown SickRage ?', @@ -20,9 +20,9 @@ $(document).ready(function () { }); }); - $('a.restart').bind("click",function(e) { + $('a.restart').bind('click', function(e) { e.preventDefault(); - var target = $( this ).attr('href'); + var target = $(this).attr('href'); $.confirm({ 'title' : 'Restart', 'message' : 'Are you sure you want to restart SickRage ?', @@ -41,9 +41,9 @@ $(document).ready(function () { }); }); - $('a.remove').bind("click",function(e) { + $('a[href^="/home/deleteShow"]').on('click', function(e) { e.preventDefault(); - var target = $( this ).attr('href'); + var target = $(this).attr('href'); var showname = document.getElementById("showtitle").getAttribute('data-showname'); $.confirm({ 'title' : 'Remove Show', @@ -64,9 +64,9 @@ $(document).ready(function () { }); }); - $('a.clearhistory').bind("click",function(e) { + $('a.clearhistory').bind('click', function(e) { e.preventDefault(); - var target = $( this ).attr('href'); + var target = $(this).attr('href'); $.confirm({ 'title' : 'Clear History', 'message' : 'Are you sure you want to clear all download history ?', @@ -84,10 +84,10 @@ $(document).ready(function () { } }); }); - - $('a.trimhistory').bind("click",function(e) { + + $('a.trimhistory').bind('click', function(e) { e.preventDefault(); - var target = $( this ).attr('href'); + var target = $(this).attr('href'); $.confirm({ 'title' : 'Trim History', 'message' : 'Are you sure you want to trim all download history older than 30 days ?', @@ -106,9 +106,9 @@ $(document).ready(function () { }); }); - $('a.submiterrors').bind("click",function(e) { + $('a.submiterrors').on('click', function(e) { e.preventDefault(); - var target = $( this ).attr('href'); + var target = $(this).attr('href'); $.confirm({ 'title' : 'Submit Errors', 'message' : 'Are you sure you want to submit these errors ?<br /><br /><span class="red-text">Make sure SickRage is updated and trigger<br /> this error with debug enabled before submitting</span>', diff --git a/gui/slick/js/lib/jquery.timeago.js b/gui/slick/js/lib/jquery.timeago.js new file mode 100644 index 0000000000000000000000000000000000000000..a8ad067eb39da390b62df845fdf4a36240a86b4f --- /dev/null +++ b/gui/slick/js/lib/jquery.timeago.js @@ -0,0 +1,223 @@ +/** + * Timeago is a jQuery plugin that makes it easy to support automatically + * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago"). + * + * @name timeago + * @version 1.4.2 + * @requires jQuery v1.2.3+ + * @author Ryan McGeary + * @license MIT License - http://www.opensource.org/licenses/mit-license.php + * + * For usage and examples, visit: + * http://timeago.yarp.com/ + * + * Copyright (c) 2008-2015, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org) + */ + +(function (factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['jquery'], factory); + } if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + // Browser globals + factory(jQuery); + } +}(function ($) { + $.timeago = function(timestamp) { + if (timestamp instanceof Date) { + return inWords(timestamp); + } else if (typeof timestamp === "string") { + return inWords($.timeago.parse(timestamp)); + } else if (typeof timestamp === "number") { + return inWords(new Date(timestamp)); + } else { + return inWords($.timeago.datetime(timestamp)); + } + }; + var $t = $.timeago; + + $.extend($.timeago, { + settings: { + refreshMillis: 60000, + allowPast: true, + allowFuture: false, + localeTitle: false, + cutoff: 0, + strings: { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "ago", + suffixFromNow: "from now", + inPast: 'any moment now', + seconds: "less than a minute", + minute: "about a minute", + minutes: "%d minutes", + hour: "about an hour", + hours: "about %d hours", + day: "a day", + days: "%d days", + month: "about a month", + months: "%d months", + year: "about a year", + years: "%d years", + wordSeparator: " ", + numbers: [] + } + }, + + inWords: function(distanceMillis) { + if(!this.settings.allowPast && ! this.settings.allowFuture) { + throw 'timeago allowPast and allowFuture settings can not both be set to false.'; + } + + var $l = this.settings.strings; + var prefix = $l.prefixAgo; + var suffix = $l.suffixAgo; + if (this.settings.allowFuture) { + if (distanceMillis < 0) { + prefix = $l.prefixFromNow; + suffix = $l.suffixFromNow; + } + } + + if(!this.settings.allowPast && distanceMillis >= 0) { + return this.settings.strings.inPast; + } + + var seconds = Math.abs(distanceMillis) / 1000; + var minutes = seconds / 60; + var hours = minutes / 60; + var days = hours / 24; + var years = days / 365; + + function substitute(stringOrFunction, number) { + var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction; + var value = ($l.numbers && $l.numbers[number]) || number; + return string.replace(/%d/i, value); + } + + var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) || + seconds < 90 && substitute($l.minute, 1) || + minutes < 45 && substitute($l.minutes, Math.round(minutes)) || + minutes < 90 && substitute($l.hour, 1) || + hours < 24 && substitute($l.hours, Math.round(hours)) || + hours < 42 && substitute($l.day, 1) || + days < 30 && substitute($l.days, Math.round(days)) || + days < 45 && substitute($l.month, 1) || + days < 365 && substitute($l.months, Math.round(days / 30)) || + years < 1.5 && substitute($l.year, 1) || + substitute($l.years, Math.round(years)); + + var separator = $l.wordSeparator || ""; + if ($l.wordSeparator === undefined) { separator = " "; } + return $.trim([prefix, words, suffix].join(separator)); + }, + + parse: function(iso8601) { + var s = $.trim(iso8601); + s = s.replace(/\.\d+/,""); // remove milliseconds + s = s.replace(/-/,"/").replace(/-/,"/"); + s = s.replace(/T/," ").replace(/Z/," UTC"); + s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400 + s = s.replace(/([\+\-]\d\d)$/," $100"); // +09 -> +0900 + return new Date(s); + }, + datetime: function(elem) { + var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title"); + return $t.parse(iso8601); + }, + isTime: function(elem) { + // jQuery's `is()` doesn't play well with HTML5 in IE + return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time"); + } + }); + + // functions that can be called via $(el).timeago('action') + // init is default when no action is given + // functions are called with context of a single element + var functions = { + init: function(){ + var refresh_el = $.proxy(refresh, this); + refresh_el(); + var $s = $t.settings; + if ($s.refreshMillis > 0) { + this._timeagoInterval = setInterval(refresh_el, $s.refreshMillis); + } + }, + update: function(time){ + var parsedTime = $t.parse(time); + $(this).data('timeago', { datetime: parsedTime }); + if($t.settings.localeTitle) $(this).attr("title", parsedTime.toLocaleString()); + refresh.apply(this); + }, + updateFromDOM: function(){ + $(this).data('timeago', { datetime: $t.parse( $t.isTime(this) ? $(this).attr("datetime") : $(this).attr("title") ) }); + refresh.apply(this); + }, + dispose: function () { + if (this._timeagoInterval) { + window.clearInterval(this._timeagoInterval); + this._timeagoInterval = null; + } + } + }; + + $.fn.timeago = function(action, options) { + var fn = action ? functions[action] : functions.init; + if(!fn){ + throw new Error("Unknown function name '"+ action +"' for timeago"); + } + // each over objects here and call the requested function + this.each(function(){ + fn.call(this, options); + }); + return this; + }; + + function refresh() { + //check if it's still visible + if(!$.contains(document.documentElement,this)){ + //stop if it has been removed + $(this).timeago("dispose"); + return this; + } + + var data = prepareData(this); + var $s = $t.settings; + + if (!isNaN(data.datetime)) { + if ( $s.cutoff == 0 || Math.abs(distance(data.datetime)) < $s.cutoff) { + $(this).text(inWords(data.datetime)); + } + } + return this; + } + + function prepareData(element) { + element = $(element); + if (!element.data("timeago")) { + element.data("timeago", { datetime: $t.datetime(element) }); + var text = $.trim(element.text()); + if ($t.settings.localeTitle) { + element.attr("title", element.data('timeago').datetime.toLocaleString()); + } else if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) { + element.attr("title", text); + } + } + return element.data("timeago"); + } + + function inWords(date) { + return $t.inWords(distance(date)); + } + + function distance(date) { + return (new Date().getTime() - date.getTime()); + } + + // fix for IE6 suckage + document.createElement("abbr"); + document.createElement("time"); +})); diff --git a/gui/slick/js/new/comingEpisodes.js b/gui/slick/js/new/comingEpisodes.js new file mode 100644 index 0000000000000000000000000000000000000000..35d890d7f6f0ff130d0113e0605c90e43bf457ba --- /dev/null +++ b/gui/slick/js/new/comingEpisodes.js @@ -0,0 +1,79 @@ +if($('meta[data-var="sickbeard.COMING_EPS_LAYOUT"]').data('content') == 'list'){ + $.tablesorter.addParser({ + id: 'loadingNames', + is: function(s) { + return false + }, + format: function(s) { + if (0 == s.indexOf('Loading...')){ + return s.replace('Loading...', '000') + } else { + return ($('meta[data-var="sickbeard.SORT_ARTICLE"]').data('content') == 'False' ? (s || '') : (s || '').replace(/^(The|A|An)\s/i,'')); + } + }, + 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(){ + if($('meta[data-var="sickbeard.COMING_EPS_LAYOUT"]').data('content') == 'list'){ + var sortCodes = {'date': 0, 'show': 1, 'network': 4}; + var sort = $('meta[data-var="sickbeard.COMING_EPS_SORT"]').data('content'); + var sortList = (sort in sortCodes) ? [[sortCodes[sort], 0]] : [[0, 0]]; + + $('#showListTable:has(tbody tr)').tablesorter({ + widgets: ['stickyHeaders'], + sortList: sortList, + textExtraction: { + 0: function(node) { return $(node).find('span').text().toLowerCase() }, + 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(); + } + if($('meta[data-var="sickbeard.COMING_EPS_LAYOUT"]').data('content') == 'banner' || $('meta[data-var="sickbeard.COMING_EPS_LAYOUT"]').data('content') == 'poster'){ + $('#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') + }); + }); + }); + } +}); + +window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes diff --git a/gui/slick/js/new/home.js b/gui/slick/js/new/home.js new file mode 100644 index 0000000000000000000000000000000000000000..6a2a00ea34530d9bdd9bfc53cb4907fa0cb55e20 --- /dev/null +++ b/gui/slick/js/new/home.js @@ -0,0 +1,376 @@ +$.tablesorter.addParser({ + id: 'loadingNames', + is: function(s) { + return false; + }, + format: function(s) { + if (s.indexOf('Loading...') == 0) + return s.replace('Loading...','000'); + else + return ($('meta[data-var="sickbeard.SORT_ARTICLE"]').data('content') == 'False' ? (s || '') : (s || '').replace(/^(The|A|An)\s/i,'')); + }, + 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('custom',7); + }, + type: 'numeric' +}); + +$.tablesorter.addParser({ + id: 'eps', + is: function(s) { + return false; + }, + format: function(s) { + match = s.match(/^(.*)/); + + if (match == null || match[1] == "?") + return -10; + + var nums = match[1].split(" / "); + if (nums[0].indexOf("+") != -1) { + var num_parts = nums[0].split("+"); + nums[0] = num_parts[0]; + } + + nums[0] = parseInt(nums[0]) + nums[1] = parseInt(nums[1]) + + if (nums[0] === 0) + return nums[1]; + + var finalNum = parseInt(($('meta[data-var="max_download_count"]').data('content'))*nums[0]/nums[1]); + var pct = Math.round((nums[0]/nums[1])*100) / 1000 + if (finalNum > 0) + finalNum += nums[0]; + + return finalNum + pct; + }, + type: 'numeric' +}); + +$(document).ready(function(){ + // This needs to be refined to work a little faster. + $('.progressbar').each(function(progressbar){ + var showId = $(this).data('show-id'); + var percentage = $(this).data('progress-percentage'); + var classToAdd = percentage == 100 ? 100 : percentage > 80 ? 80 : percentage > 60 ? 60 : percentage > 40 ? 40 : 20; + $(this).progressbar({ value: percentage }); + $(this).data('progress-text') ? $(this).append('<div class="progressbarText" title="' + $(this).data('progress-tip') + '">' + $(this).data('progress-text') + '</div>') : ''; + $(this).find('.ui-progressbar-value').addClass('progress-' + classToAdd); + }); + + $("img#network").on('error', function(){ + $(this).parent().text($(this).attr('alt')); + $(this).remove(); + }); + + $("#showListTableShows:has(tbody tr)").tablesorter({ + sortList: [[7,1],[2,0]], + textExtraction: { + 0: function(node) { return $(node).find("span").text().toLowerCase(); }, + 1: function(node) { return $(node).find("span").text().toLowerCase(); }, + 3: function(node) { return $(node).find("span").prop("title").toLowerCase(); }, + 4: function(node) { return $(node).find("span").text().toLowerCase(); }, + 5: function(node) { return $(node).find("span:first").text(); }, + 6: function(node) { return $(node).find("img").attr("alt"); } + }, + widgets: ['saveSort', 'zebra', 'stickyHeaders', 'filter', 'columnSelector'], + headers: (function(){ + if($('meta[data-var="sickbeard.FILTER_ROW"]').data('content') == 'True'){ + return { + 0: { sorter: 'isoDate' }, + 1: { columnSelector: false }, + 2: { sorter: 'loadingNames' }, + 4: { sorter: 'quality' }, + 5: { sorter: 'eps' }, + 6: { filter : 'parsed' } + } + } else { + return { + 0: { sorter: 'isoDate' }, + 1: { columnSelector: false }, + 2: { sorter: 'loadingNames' }, + 4: { sorter: 'quality' }, + 5: { sorter: 'eps' } + } + } + }()), + widgetOptions: (function(){ + if($('meta[data-var="sickbeard.FILTER_ROW"]').data('content') == 'True'){ + return { + filter_columnFilters: true, + filter_hideFilters : true, + filter_saveFilters : true, + filter_functions : { + 5:function(e, n, f, i, r, c) { + var test = false; + var pct = Math.floor((n % 1) * 1000); + if (f === '') { + test = true; + } else { + var result = f.match(/(<|<=|>=|>)\s(\d+)/i); + if (result) { + if (result[1] === "<") { + if (pct < parseInt(result[2])) { + test = true; + } + } else if (result[1] === "<=") { + if (pct <= parseInt(result[2])) { + test = true; + } + } else if (result[1] === ">=") { + if (pct >= parseInt(result[2])) { + test = true; + } + } else if (result[1] === ">") { + if (pct > parseInt(result[2])) { + test = true; + } + } + } + + var result = f.match(/(\d+)\s(-|to)\s(\d+)/i); + if (result) { + if ((result[2] === "-") || (result[2] === "to")) { + if ((pct >= parseInt(result[1])) && (pct <= parseInt(result[3]))) { + test = true; + } + } + } + + var result = f.match(/(=)?\s?(\d+)\s?(=)?/i); + if (result) { + if ((result[1] === "=") || (result[3] === "=")) { + if (parseInt(result[2]) === pct) { + test = true; + } + } + } + + if (!isNaN(parseFloat(f)) && isFinite(f)) { + if (parseInt(f) === pct) { + test = true; + } + } + } + return test; + } + }, + filter_reset: '.resetshows', + columnSelector_mediaquery: false + } + } else { + return { + filter_columnFilters: false + } + } + }()), + sortStable: true, + sortAppend: [[2,0]] + }); + + $("#showListTableAnime:has(tbody tr)").tablesorter({ + sortList: [[6,1],[2,0]], + textExtraction: { + 0: function(node) { return $(node).find("span").text().toLowerCase(); }, + 1: function(node) { return $(node).find("span").text().toLowerCase(); }, + 3: function(node) { return $(node).find("span").prop("title").toLowerCase(); }, + 4: function(node) { return $(node).find("span").text().toLowerCase(); }, + 5: function(node) { return $(node).find("span:first").text(); }, + 6: function(node) { return $(node).find("img").attr("alt"); } + }, + widgets: ['saveSort', 'zebra', 'stickyHeaders', 'filter', 'columnSelector'], + headers: (function(){ + if($('meta[data-var="sickbeard.FILTER_ROW"]').data('content') == 'True'){ + return { + 0: { sorter: 'isoDate' }, + 1: { columnSelector: false }, + 2: { sorter: 'loadingNames' }, + 4: { sorter: 'quality' }, + 5: { sorter: 'eps' }, + 6: { filter : 'parsed' } + } + } else { + return { + 0: { sorter: 'isoDate' }, + 1: { columnSelector: false }, + 2: { sorter: 'loadingNames' }, + 4: { sorter: 'quality' }, + 5: { sorter: 'eps' } + } + } + }()), + widgetOptions: (function(){ + if($('meta[data-var="sickbeard.FILTER_ROW"]').data('content') == 'True'){ + return { + filter_columnFilters: true, + filter_hideFilters : true, + filter_saveFilters : true, + filter_functions : { + 5:function(e, n, f, i, r, c) { + var test = false; + var pct = Math.floor((n % 1) * 1000); + if (f === '') { + test = true; + } else { + var result = f.match(/(<|<=|>=|>)\s(\d+)/i); + if (result) { + if (result[1] === "<") { + if (pct < parseInt(result[2])) { + test = true; + } + } else if (result[1] === "<=") { + if (pct <= parseInt(result[2])) { + test = true; + } + } else if (result[1] === ">=") { + if (pct >= parseInt(result[2])) { + test = true; + } + } else if (result[1] === ">") { + if (pct > parseInt(result[2])) { + test = true; + } + } + } + + var result = f.match(/(\d+)\s(-|to)\s(\d+)/i); + if (result) { + if ((result[2] === "-") || (result[2] === "to")) { + if ((pct >= parseInt(result[1])) && (pct <= parseInt(result[3]))) { + test = true; + } + } + } + + var result = f.match(/(=)?\s?(\d+)\s?(=)?/i); + if (result) { + if ((result[1] === "=") || (result[3] === "=")) { + if (parseInt(result[2]) === pct) { + test = true; + } + } + } + + if (!isNaN(parseFloat(f)) && isFinite(f)) { + if (parseInt(f) === pct) { + test = true; + } + } + } + return test; + } + }, + filter_reset: '.resetanime', + columnSelector_mediaquery: false + } + } else { + return { + filter_columnFilters: false + } + } + }()), + sortStable: true, + sortAppend: [[2,0]] + }); + + if ($("#showListTableShows").find("tbody").find("tr").size() > 0){ + $.tablesorter.filter.bindSearch( "#showListTableShows", $('.search') ); + } + + if(['True', 1].indexOf($('meta[data-var="sickbeard.ANIME_SPLIT_HOME"]').data('content')) >= 0){ + if($("#showListTableAnime").find("tbody").find("tr").size() > 0){ + $.tablesorter.filter.bindSearch( "#showListTableAnime", $('.search') ); + } + } + + if(['True', 1].indexOf($('meta[data-var="sickbeard.FUZZY_DATING"]').data('content')) >= 0){ + $.timeago.settings.allowFuture = true; + $.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: 'In ', + suffixAgo: "ago", + suffixFromNow: "", + seconds: "less than a minute", + minute: "about a minute", + minutes: "%d minutes", + hour: "about an hour", + hours: "about %d hours", + day: "a day", + days: "%d days", + month: "about a month", + months: "%d months", + year: "about a year", + years: "%d years", + wordSeparator: " ", + numbers: [] + }; + $("[datetime]").timeago(); + } + + var $container = [$('#container'), $('#container-anime')]; + + $.each($container, function (j) { + this.isotope({ + itemSelector: '.show', + sortBy : $('meta[data-var="sickbeard.POSTER_SORTBY"]').data('content'), + sortAscending: $('meta[data-var="sickbeard.POSTER_SORTDIR"]').data('content'), + layoutMode: 'masonry', + masonry: { + columnWidth: 13, + isFitWidth: true + }, + getSortData: { + name: function( itemElem ) { + var name = $( itemElem ).attr('data-name'); + return ($('meta[data-var="sickbeard.SORT_ARTICLE"]').data('content') == 'False' ? (name || '') : (name || '').replace(/^(The|A|An)\s/i,'')); + }, + network: '[data-network]', + date: function( itemElem ) { + var date = $( itemElem ).attr('data-date'); + return date.length && parseInt( date, 10 ) || Number.POSITIVE_INFINITY; + }, + progress: function( itemElem ) { + var progress = $( itemElem ).attr('data-progress'); + return progress.length && parseInt( progress, 10 ) || Number.NEGATIVE_INFINITY; + } + } + }); + }); + + $('#postersort').on( 'change', function() { + var sortValue = this.value; + $('#container').isotope({ sortBy: sortValue }); + $('#container-anime').isotope({ sortBy: sortValue }); + $.get(this.options[this.selectedIndex].getAttribute('data-sort')); + }); + + $('#postersortdirection').on( 'change', function() { + var sortDirection = this.value; + sortDirection = sortDirection == 'true'; + $('#container').isotope({ sortAscending: sortDirection }); + $('#container-anime').isotope({ sortAscending: sortDirection }); + $.get(this.options[this.selectedIndex].getAttribute('data-sort')); + }); + + $('#popover').popover({ + placement: 'bottom', + html: true, // required if content has HTML + content: '<div id="popover-target"></div>' + }).on('shown.bs.popover', function () { // bootstrap popover event triggered when the popover opens + // call this function to copy the column selection code into the popover + $.tablesorter.columnSelector.attachTo( $('#showListTableShows'), '#popover-target'); + if(['True', 1].indexOf($('meta[data-var="sickbeard.ANIME_SPLIT_HOME"]').data('content')) >= 0){ + $.tablesorter.columnSelector.attachTo( $('#showListTableAnime'), '#popover-target'); + } + + }); +}); diff --git a/gui/slick/views/comingEpisodes.mako b/gui/slick/views/comingEpisodes.mako index 62c956b209ff5a98851e94b44321813b1a0599d6..b4f4af2d335e52b81b174d9d737af59501eeec5e 100644 --- a/gui/slick/views/comingEpisodes.mako +++ b/gui/slick/views/comingEpisodes.mako @@ -7,124 +7,14 @@ import time import re %> +<%block name="metas"> +<meta data-var="sickbeard.COMING_EPS_SORT" data-content="${sickbeard.COMING_EPS_SORT}"> +<meta data-var="sickbeard.COMING_EPS_LAYOUT" data-content="${sickbeard.COMING_EPS_LAYOUT}"> +</%block> <%block name="scripts"> -<% fuzzydate = 'airdate' %> -<% sort = sickbeard.COMING_EPS_SORT %> <script type="text/javascript" src="${sbRoot}/js/ajaxEpSearch.js?${sbPID}"></script> -% if 'list' == layout: <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(/^(The|A|An)\s/i, '') -% else: - return (s || '') -% endif - }, - 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(){ -<% sort_codes = {'date': 0, 'show': 1, 'network': 4} %> -% if sort not in sort_codes: - <% sort = 'date' %> -% endif - - sortList = [[${sort_codes[sort]}, 0]]; - - $('#showListTable:has(tbody tr)').tablesorter({ - widgets: ['stickyHeaders'], - sortList: sortList, - textExtraction: { - 0: function(node) { return $(node).find('span').text().toLowerCase() }, - 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(); - - <% fuzzydate = 'airdate' %> - % if sickbeard.FUZZY_DATING: - fuzzyMoment({ - containerClass : '.${fuzzydate}', - dateHasTime : true, - dateFormat : '${sickbeard.DATE_PRESET}', - timeFormat : '${sickbeard.TIME_PRESET}', - trimZero : ${('false', 'true')[bool(sickbeard.TRIM_ZERO)]} - }); - % endif - -}); -</script> -% elif layout in ['banner', 'poster']: -<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') - }); - }); - }); - - % if sickbeard.FUZZY_DATING: - fuzzyMoment({ - dtInline : true, - dtGlue : ' at ', - containerClass : '.${fuzzydate}', - dateHasTime : true, - dateFormat : '${sickbeard.DATE_PRESET}', - timeFormat : '${sickbeard.TIME_PRESET}', - trimZero : ${('false', 'true')[bool(sickbeard.TRIM_ZERO)]} - }); - % endif - -}); -</script> -% endif -<script type="text/javascript" charset="utf-8"> -window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes -</script> +<script type="text/javascript" src="${sbRoot}/js/new/comingEpisodes.js"></script> </%block> <%block name="css"> <style type="text/css"> @@ -202,7 +92,7 @@ window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes <tbody style="text-shadow:none;"> -% for cur_result in sql_results: +% for cur_result in results: <% cur_indexer = int(cur_result['indexer']) run_time = cur_result['runtime'] @@ -225,11 +115,15 @@ window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes show_div = 'listing-default' %> - <!-- 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" nowrap="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> + <% airDate = sbdatetime.sbdatetime.sbfdatetime(cur_result['localtime']).decode(sickbeard.SYS_ENCODING) %> + <% isoDate = sbdatetime.sbdatetime.convert_to_setting(cur_result['localtime']).isoformat('T') %> + <span class="${fuzzydate}"> + <time datetime="${isoDate}" class="date">${airDate}</time> + </span> + <span class="sort_data">${time.mktime(cur_result['localtime'].timetuple())}</span> </td> <td class="tvShow" nowrap="nowrap"><a href="${sbRoot}/home/displayShow?show=${cur_result['showid']}">${cur_result['show_name']}</a> @@ -256,7 +150,7 @@ window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes </td> <td align="center"> - ${renderQualityPill(cur_result['quality'])} + ${renderQualityPill(cur_result['quality'])} </td> <td align="center" style="vertical-align: middle;"> @@ -270,7 +164,6 @@ window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes <a href="${sbRoot}/home/searchEpisode?show=${cur_result['showid']}&season=${cur_result['season']}&episode=${cur_result['episode']}" title="Manual Search" id="forceUpdate-${cur_result['showid']}x${cur_result['season']}x${cur_result['episode']}" 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']} //--> % endfor </tbody> @@ -297,7 +190,7 @@ window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes <br /><br /> % endif -% for cur_result in sql_results: +% for cur_result in results: <% cur_indexer = int(cur_result['indexer']) @@ -430,7 +323,7 @@ window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes <div class="clearfix"> <span class="title">Quality:</span> - ${renderQualityPill(cur_result['quality'])} + ${renderQualityPill(cur_result['quality'])} </div> </td> </tr> @@ -472,7 +365,7 @@ window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes <thead><tr><th>${day.strftime('%A').decode(sickbeard.SYS_ENCODING).capitalize()}</th></tr></thead> <tbody> <% day_has_show = False %> - % for cur_result in sql_results: + % for cur_result in results: % if int(cur_result['paused']) and not sickbeard.COMING_EPS_DISPLAY_PAUSED: <% continue %> % endif diff --git a/gui/slick/views/config_general.mako b/gui/slick/views/config_general.mako index ddba429bc05e201ae75328411fb3def7f7031462..304dc291731a5b94667156ce63da5ce9e5d6ec5c 100644 --- a/gui/slick/views/config_general.mako +++ b/gui/slick/views/config_general.mako @@ -729,7 +729,7 @@ </label> </div> - <div class="field-pair"> + <div class="field-pair" hidden> <label for="git_reset"> <span class="component-title">Git reset</span> <span class="component-desc"> diff --git a/gui/slick/views/home.mako b/gui/slick/views/home.mako index 36fdcbbe0b7f5d97b4b065a542034fdee2543492..35ac5b89d85cdf373bb31cffe7080e1b1a594854 100644 --- a/gui/slick/views/home.mako +++ b/gui/slick/views/home.mako @@ -41,353 +41,20 @@ max_download_count = max_download_count * 100 %> <%block name="scripts"> -<script type="text/javascript" charset="utf-8"> -$.tablesorter.addParser({ - id: 'loadingNames', - is: function(s) { - return false; - }, - format: function(s) { - if (s.indexOf('Loading...') == 0) - return s.replace('Loading...','000'); - else - % if not sickbeard.SORT_ARTICLE: - return (s || '').replace(/^(The|A|An)\s/i,''); - % else: - return (s || ''); - % endif - }, - 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('custom',7); - }, - type: 'numeric' -}); - -$.tablesorter.addParser({ - id: 'eps', - is: function(s) { - return false; - }, - format: function(s) { - match = s.match(/^(.*)/); - - if (match == null || match[1] == "?") - return -10; - - var nums = match[1].split(" / "); - if (nums[0].indexOf("+") != -1) { - var num_parts = nums[0].split("+"); - nums[0] = num_parts[0]; - } - - nums[0] = parseInt(nums[0]) - nums[1] = parseInt(nums[1]) - - if (nums[0] === 0) - return nums[1]; - - var finalNum = parseInt(${max_download_count}*nums[0]/nums[1]); - var pct = Math.round((nums[0]/nums[1])*100) / 1000 - if (finalNum > 0) - finalNum += nums[0]; - - return finalNum + pct; - }, - type: 'numeric' -}); - -$(document).ready(function(){ - // This needs to be refined to work a little faster. - $('.progressbar').each(function(progressbar){ - var showId = $(this).data('show-id'); - var percentage = $(this).data('progress-percentage'); - var classToAdd = percentage == 100 ? 100 : percentage > 80 ? 80 : percentage > 60 ? 60 : percentage > 40 ? 40 : 20; - $(this).progressbar({ value: percentage }); - $(this).data('progress-text') ? $(this).append('<div class="progressbarText" title="' + $(this).data('progress-tip') + '">' + $(this).data('progress-text') + '</div>') : ''; - $(this).find('.ui-progressbar-value').addClass('progress-' + classToAdd); - }); - - $("img#network").on('error', function(){ - $(this).parent().text($(this).attr('alt')); - $(this).remove(); - }); - - $("#showListTableShows:has(tbody tr)").tablesorter({ - sortList: [[7,1],[2,0]], - textExtraction: { - 0: function(node) { return $(node).find("span").text().toLowerCase(); }, - 1: function(node) { return $(node).find("span").text().toLowerCase(); }, - 3: function(node) { return $(node).find("span").prop("title").toLowerCase(); }, - 4: function(node) { return $(node).find("span").text().toLowerCase(); }, - 5: function(node) { return $(node).find("span:first").text(); }, - 6: function(node) { return $(node).data('show-size'); }, - 7: function(node) { return $(node).find("img").attr("alt"); } - }, - widgets: ['saveSort', 'zebra', 'stickyHeaders', 'filter', 'columnSelector'], - headers: { - 0: { sorter: 'isoDate' }, - 1: { columnSelector: false }, - 2: { sorter: 'loadingNames' }, - 4: { sorter: 'quality' }, - 5: { sorter: 'eps' }, - % if sickbeard.FILTER_ROW: - 7: { filter : 'parsed' } - % endif - }, - widgetOptions : { - % if sickbeard.FILTER_ROW: - filter_columnFilters: true, - filter_hideFilters : true, - filter_saveFilters : true, - filter_functions : { - 5:function(e, n, f, i, r, c) { - var test = false; - var pct = Math.floor((n % 1) * 1000); - if (f === '') { - test = true; - } else { - var result = f.match(/(<|<=|>=|>)\s(\d+)/i); - if (result) { - if (result[1] === "<") { - if (pct < parseInt(result[2])) { - test = true; - } - } else if (result[1] === "<=") { - if (pct <= parseInt(result[2])) { - test = true; - } - } else if (result[1] === ">=") { - if (pct >= parseInt(result[2])) { - test = true; - } - } else if (result[1] === ">") { - if (pct > parseInt(result[2])) { - test = true; - } - } - } - - var result = f.match(/(\d+)\s(-|to)\s(\d+)/i); - if (result) { - if ((result[2] === "-") || (result[2] === "to")) { - if ((pct >= parseInt(result[1])) && (pct <= parseInt(result[3]))) { - test = true; - } - } - } - - var result = f.match(/(=)?\s?(\d+)\s?(=)?/i); - if (result) { - if ((result[1] === "=") || (result[3] === "=")) { - if (parseInt(result[2]) === pct) { - test = true; - } - } - } - - if (!isNaN(parseFloat(f)) && isFinite(f)) { - if (parseInt(f) === pct) { - test = true; - } - } - } - return test; - }, - }, - % else: - filter_columnFilters: false, - % endif - filter_reset: '.resetshows', - columnSelector_mediaquery: false, - }, - sortStable: true, - sortAppend: [[2,0]] - }); - - $("#showListTableAnime:has(tbody tr)").tablesorter({ - sortList: [[7,1],[2,0]], - textExtraction: { - 0: function(node) { return $(node).find("span").text().toLowerCase(); }, - 1: function(node) { return $(node).find("span").text().toLowerCase(); }, - 3: function(node) { return $(node).find("span").prop("title").toLowerCase(); }, - 4: function(node) { return $(node).find("span").text().toLowerCase(); }, - 5: function(node) { return $(node).find("span:first").text(); }, - 6: function(node) { return $(node).data('show-size'); }, - 7: function(node) { return $(node).find("img").attr("alt"); } - }, - widgets: ['saveSort', 'zebra', 'stickyHeaders', 'filter', 'columnSelector'], - headers: { - 0: { sorter: 'isoDate' }, - 1: { columnSelector: false }, - 2: { sorter: 'loadingNames' }, - 4: { sorter: 'quality' }, - 5: { sorter: 'eps' }, - % if sickbeard.FILTER_ROW: - 7: { filter : 'parsed' } - % endif - }, - widgetOptions : { - % if sickbeard.FILTER_ROW: - filter_columnFilters: true, - filter_hideFilters : true, - filter_saveFilters : true, - filter_functions : { - 5:function(e, n, f, i, r, c) { - var test = false; - var pct = Math.floor((n % 1) * 1000); - if (f === '') { - test = true; - } else { - var result = f.match(/(<|<=|>=|>)\s(\d+)/i); - if (result) { - if (result[1] === "<") { - if (pct < parseInt(result[2])) { - test = true; - } - } else if (result[1] === "<=") { - if (pct <= parseInt(result[2])) { - test = true; - } - } else if (result[1] === ">=") { - if (pct >= parseInt(result[2])) { - test = true; - } - } else if (result[1] === ">") { - if (pct > parseInt(result[2])) { - test = true; - } - } - } - - var result = f.match(/(\d+)\s(-|to)\s(\d+)/i); - if (result) { - if ((result[2] === "-") || (result[2] === "to")) { - if ((pct >= parseInt(result[1])) && (pct <= parseInt(result[3]))) { - test = true; - } - } - } - - var result = f.match(/(=)?\s?(\d+)\s?(=)?/i); - if (result) { - if ((result[1] === "=") || (result[3] === "=")) { - if (parseInt(result[2]) === pct) { - test = true; - } - } - } - - if (!isNaN(parseFloat(f)) && isFinite(f)) { - if (parseInt(f) === pct) { - test = true; - } - } - } - return test; - }, - }, - % else: - filter_columnFilters: false, - % endif - filter_reset: '.resetanime', - columnSelector_mediaquery: false, - }, - sortStable: true, - sortAppend: [[2,0]] - }); - - if ($("#showListTableShows").find("tbody").find("tr").size() > 0) - $.tablesorter.filter.bindSearch( "#showListTableShows", $('.search') ); - - % if sickbeard.ANIME_SPLIT_HOME: - if ($("#showListTableAnime").find("tbody").find("tr").size() > 0) - $.tablesorter.filter.bindSearch( "#showListTableAnime", $('.search') ); - % endif - - % if sickbeard.FUZZY_DATING: - fuzzyMoment({ - dtInline : ${('true', 'false')[layout == 'poster']}, - containerClass : '.${fuzzydate}', - dateHasTime : false, - dateFormat : '${sickbeard.DATE_PRESET}', - timeFormat : '${sickbeard.TIME_PRESET}', - trimZero : ${('false', 'true')[bool(sickbeard.TRIM_ZERO)]} - }); - % endif - - var $container = [$('#container'), $('#container-anime')]; - - $.each($container, function (j) { - this.isotope({ - itemSelector: '.show', - sortBy : '${sickbeard.POSTER_SORTBY}', - sortAscending: ${sickbeard.POSTER_SORTDIR}, - layoutMode: 'masonry', - masonry: { - columnWidth: 13, - isFitWidth: true - }, - getSortData: { - name: function( itemElem ) { - var name = $( itemElem ).attr('data-name'); - % if not sickbeard.SORT_ARTICLE: - return (name || '').replace(/^(The|A|An)\s/i,''); - % else: - return (name || ''); - % endif - }, - network: '[data-network]', - date: function( itemElem ) { - var date = $( itemElem ).attr('data-date'); - return date.length && parseInt( date, 10 ) || Number.POSITIVE_INFINITY; - }, - progress: function( itemElem ) { - var progress = $( itemElem ).attr('data-progress'); - return progress.length && parseInt( progress, 10 ) || Number.NEGATIVE_INFINITY; - } - } - }); - }); - - $('#postersort').on( 'change', function() { - var sortValue = this.value; - $('#container').isotope({ sortBy: sortValue }); - $('#container-anime').isotope({ sortBy: sortValue }); - $.get(this.options[this.selectedIndex].getAttribute('data-sort')); - }); - - $('#postersortdirection').on( 'change', function() { - var sortDirection = this.value; - sortDirection = sortDirection == 'true'; - $('#container').isotope({ sortAscending: sortDirection }); - $('#container-anime').isotope({ sortAscending: sortDirection }); - $.get(this.options[this.selectedIndex].getAttribute('data-sort')); - }); - - $('#popover') - .popover({ - placement: 'bottom', - html: true, // required if content has HTML - content: '<div id="popover-target"></div>' - }) - // bootstrap popover event triggered when the popover opens - .on('shown.bs.popover', function () { - // call this function to copy the column selection code into the popover - $.tablesorter.columnSelector.attachTo( $('#showListTableShows'), '#popover-target'); - % if sickbeard.ANIME_SPLIT_HOME: - $.tablesorter.columnSelector.attachTo( $('#showListTableAnime'), '#popover-target'); - % endif - }); -}); -</script> +<meta data-var="sickbeard.SORT_ARTICLE" data-content="${sickbeard.SORT_ARTICLE}"> +<meta data-var="sickbeard.FILTER_ROW" data-content="${sickbeard.FILTER_ROW}"> +<meta data-var="sickbeard.ANIME_SPLIT_HOME" data-content="${sickbeard.ANIME_SPLIT_HOME}"> +<meta data-var="sickbeard.POSTER_SORTBY" data-content="${sickbeard.POSTER_SORTBY}"> +<meta data-var="sickbeard.POSTER_SORTDIR" data-content="${sickbeard.POSTER_SORTDIR}"> +<meta data-var="sickbeard.FUZZY_DATING" data-content="${sickbeard.FUZZY_DATING}"> +<meta data-var="sickbeard.DATE_PRESET" data-content="${sickbeard.DATE_PRESET}"> +<meta data-var="sickbeard.TIME_PRESET" data-content="${sickbeard.TIME_PRESET}"> +<meta data-var="sickbeard.TRIM_ZERO" data-content="${sickbeard.TRIM_ZERO}"> +<meta data-var="max_download_count" data-content="${max_download_count}"> +<meta data-var="layout" data-content="${layout}"> +<meta data-var="fuzzydate" data-content="${fuzzydate}"> +<script type="text/javascript" src="${sbRoot}/js/lib/jquery.timeago.js"></script> +<script type="text/javascript" src="${sbRoot}/js/new/home.js"></script> </%block> <%block name="content"> <%namespace file="/inc_defs.mako" import="renderQualityPill"/> @@ -578,7 +245,7 @@ $(document).ready(function(){ </td> <td class="show-table"> - ${renderQualityPill(curShow.quality, overrideClass="show-quality")} + ${renderQualityPill(curShow.quality, overrideClass="show-quality")} </td> </tr> </table> @@ -703,7 +370,9 @@ $(document).ready(function(){ <% temp_sbfdate_next = sbdatetime.sbdatetime.sbfdate(ldatetime) %> <% temp_timegm_next = calendar.timegm(ldatetime.timetuple()) %> <td align="center" class="nowrap"> - <div class="${fuzzydate}">${temp_sbfdate_next}</div> + <div class="${fuzzydate}"> + <time datetime="${ldatetime.isoformat('T')}" class="date">${temp_sbfdate_next}</time> + </div> <span class="sort_data">${temp_timegm_next}</span> </td> % except ValueError: @@ -719,8 +388,10 @@ $(document).ready(function(){ <% temp_sbfdate_prev = sbdatetime.sbdatetime.sbfdate(pdatetime) %> <% temp_timegm_prev = calendar.timegm(pdatetime.timetuple()) %> <td align="center" class="nowrap"> - <div class="${fuzzydate}">${temp_sbfdate_prev}</div> - <span class="sort_data">${temp_timegm_prev}</span> + <div class="${fuzzydate}"> + <time datetime="${pdatetime.isoformat('T')}" class="date">${temp_sbfdate_prev}</time> + </div> + <span class="sort_data">${temp_sbfdate_prev}</span> </td> % except ValueError: <td align="center" class="nowrap"></td> @@ -777,7 +448,6 @@ $(document).ready(function(){ ## <% show_size = sickbeard.helpers.get_size(curShow._location) %> ## <td align="center" data-show-size="${show_size}">${sickbeard.helpers.pretty_filesize(show_size)}</td> - <td align="center" data-show-size="0"></td> <td align="center"> <% paused = int(curShow.paused) == 0 and curShow.status == 'Continuing' %> diff --git a/gui/slick/views/home_addShows.mako b/gui/slick/views/home_addShows.mako index 09b79ae5f2c00b8eadd51676d3e8425aad6e668a..19fce35681ab61eb6bf88f310fed97a9c8cbd5f0 100644 --- a/gui/slick/views/home_addShows.mako +++ b/gui/slick/views/home_addShows.mako @@ -43,8 +43,6 @@ <br/><br/> % endif - - % if sickbeard.USE_IMDB_POPULAR == True: <a href="${sbRoot}/home/addShows/popularShows/" id="btnNewShow" class="btn btn-large"> <div class="button"><div class="icon-addtrendingshow"></div></div> <div class="buttontext"> @@ -54,8 +52,6 @@ </a> <br/><br/> - % endif - <a href="${sbRoot}/home/addShows/existingShows/" id="btnExistingShow" class="btn btn-large"> <div class="button"><div class="icon-addexistingshow"></div></div> diff --git a/gui/slick/views/layouts/main.mako b/gui/slick/views/layouts/main.mako index f57d27cc8c2702c16e38675906c1f0bfd458e125..5052f020324b5504447aa4ca21bcfc1d0cf4abf8 100644 --- a/gui/slick/views/layouts/main.mako +++ b/gui/slick/views/layouts/main.mako @@ -43,6 +43,9 @@ <meta name="msapplication-TileImage" content="${sbRoot}/images/ico/favicon-144.png"> <meta name="msapplication-config" content="${sbRoot}/css/browserconfig.xml"> + <meta data-var="sickbeard.FUZZY_DATING" data-content="${sickbeard.FUZZY_DATING}"> + <%block name="metas" /> + <link rel="shortcut icon" href="${sbRoot}/images/ico/favicon.ico"> <link rel="icon" sizes="16x16 32x32 64x64" href="${sbRoot}/images/ico/favicon.ico"> <link rel="icon" type="image/png" sizes="196x196" href="${sbRoot}/images/ico/favicon-196.png"> diff --git a/gui/slick/views/restart.mako b/gui/slick/views/restart.mako index d072681a4e96dc2ce75081f18ea02f0d57d1324d..d43038c53e48e2110727e0ef27c27c8858fb2d2c 100644 --- a/gui/slick/views/restart.mako +++ b/gui/slick/views/restart.mako @@ -25,6 +25,13 @@ sbDefaultPage = "${sbDefaultPage}"; <script type="text/javascript" src="${sbRoot}/js/lib/jquery-1.11.2.min.js?${sbPID}"></script> <script type="text/javascript" src="${sbRoot}/js/restart.js?${sbPID}&${sbDefaultPage}"></script> </%block> +<%block name="css"> +<style> +.upgrade-notification { + display: none; +} +</style> +</%block> <%block name="content"> <% try: diff --git a/gui/slick/views/status.mako b/gui/slick/views/status.mako index 9ef509c4b0901ad8d6e04b61d32132df9658143b..818f76144004df2ee6b465b4b6ec47ee367f40fe 100644 --- a/gui/slick/views/status.mako +++ b/gui/slick/views/status.mako @@ -3,6 +3,7 @@ import sickbeard from sickbeard import helpers from sickbeard.show_queue import ShowQueueActions + from sickrage.helper.common import dateTimeFormat %> <%block name="scripts"> <script type="text/javascript"> @@ -111,7 +112,7 @@ % else: <td></td> % endif - <td>${service.lastRun.strftime("%Y-%m-%d %H:%M:%S")}</td> + <td>${service.lastRun.strftime(dateTimeFormat)}</td> <td>${service.silent}</td> </tr> <% del service %> @@ -159,7 +160,7 @@ % else: <td>sickbeard.showQueueScheduler.action.currentItem.priority</td> % endif - <td>${sickbeard.showQueueScheduler.action.currentItem.added.strftime("%Y-%m-%d %H:%M:%S")}</td> + <td>${sickbeard.showQueueScheduler.action.currentItem.added.strftime(dateTimeFormat)}</td> <td>${ShowQueueActions.names[sickbeard.showQueueScheduler.action.currentItem.action_id]}</td> </tr> % endif @@ -191,7 +192,7 @@ % else: <td>${item.priority}</td> % endif - <td>${item.added.strftime("%Y-%m-%d %H:%M:%S")}</td> + <td>${item.added.strftime(dateTimeFormat)}</td> <td>${ShowQueueActions.names[item.action_id]}</td> </tr> % endfor diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py index 319de37564c3c3ccf9545d35468a2d8f25962b38..fef229c9a69fe1c0e93435749881ce12174c9e43 100644 --- a/sickbeard/__init__.py +++ b/sickbeard/__init__.py @@ -433,8 +433,6 @@ SYNOLOGYNOTIFIER_NOTIFY_ONSNATCH = False SYNOLOGYNOTIFIER_NOTIFY_ONDOWNLOAD = False SYNOLOGYNOTIFIER_NOTIFY_ONSUBTITLEDOWNLOAD = False -USE_IMDB_POPULAR = True - USE_TRAKT = False TRAKT_USERNAME = None TRAKT_ACCESS_TOKEN = None @@ -566,7 +564,7 @@ def initialize(consoleLogging=True): TORRENT_USERNAME, TORRENT_PASSWORD, TORRENT_HOST, TORRENT_PATH, TORRENT_SEED_TIME, TORRENT_PAUSED, TORRENT_HIGH_BANDWIDTH, TORRENT_LABEL, TORRENT_LABEL_ANIME, TORRENT_VERIFY_CERT, TORRENT_RPCURL, TORRENT_AUTH_TYPE, \ USE_KODI, KODI_ALWAYS_ON, KODI_NOTIFY_ONSNATCH, KODI_NOTIFY_ONDOWNLOAD, KODI_NOTIFY_ONSUBTITLEDOWNLOAD, KODI_UPDATE_FULL, KODI_UPDATE_ONLYFIRST, \ KODI_UPDATE_LIBRARY, KODI_HOST, KODI_USERNAME, KODI_PASSWORD, BACKLOG_FREQUENCY, \ - USE_TRAKT, TRAKT_USERNAME, TRAKT_ACCESS_TOKEN, TRAKT_REFRESH_TOKEN, TRAKT_REMOVE_WATCHLIST, TRAKT_SYNC_WATCHLIST, TRAKT_REMOVE_SHOW_FROM_SICKRAGE, TRAKT_METHOD_ADD, TRAKT_START_PAUSED, traktCheckerScheduler, TRAKT_USE_RECOMMENDED, TRAKT_SYNC, TRAKT_SYNC_REMOVE, TRAKT_DEFAULT_INDEXER, TRAKT_REMOVE_SERIESLIST, TRAKT_TIMEOUT, TRAKT_BLACKLIST_NAME, USE_IMDB_POPULAR, \ + USE_TRAKT, TRAKT_USERNAME, TRAKT_ACCESS_TOKEN, TRAKT_REFRESH_TOKEN, TRAKT_REMOVE_WATCHLIST, TRAKT_SYNC_WATCHLIST, TRAKT_REMOVE_SHOW_FROM_SICKRAGE, TRAKT_METHOD_ADD, TRAKT_START_PAUSED, traktCheckerScheduler, TRAKT_USE_RECOMMENDED, TRAKT_SYNC, TRAKT_SYNC_REMOVE, TRAKT_DEFAULT_INDEXER, TRAKT_REMOVE_SERIESLIST, TRAKT_TIMEOUT, TRAKT_BLACKLIST_NAME, \ USE_PLEX, PLEX_NOTIFY_ONSNATCH, PLEX_NOTIFY_ONDOWNLOAD, PLEX_NOTIFY_ONSUBTITLEDOWNLOAD, PLEX_UPDATE_LIBRARY, USE_PLEX_CLIENT, PLEX_CLIENT_USERNAME, PLEX_CLIENT_PASSWORD, \ PLEX_SERVER_HOST, PLEX_SERVER_TOKEN, PLEX_HOST, PLEX_USERNAME, PLEX_PASSWORD, DEFAULT_BACKLOG_FREQUENCY, MIN_BACKLOG_FREQUENCY, SKIP_REMOVED_FILES, \ USE_EMBY, EMBY_HOST, EMBY_APIKEY, \ @@ -676,7 +674,7 @@ def initialize(consoleLogging=True): # git_remote GIT_REMOTE = check_setting_str(CFG, 'General', 'git_remote', 'origin') GIT_REMOTE_URL = check_setting_str(CFG, 'General', 'git_remote_url', - 'https://github.com/SiCKRAGETV/SickRage.git') + 'https://github.com/%s/%s.git' % (GIT_ORG, GIT_REPO)) # current commit hash CUR_COMMIT_HASH = check_setting_str(CFG, 'General', 'cur_commit_hash', '') @@ -991,7 +989,7 @@ def initialize(consoleLogging=True): check_setting_int(CFG, 'Twitter', 'twitter_notify_onsubtitledownload', 0)) TWITTER_USERNAME = check_setting_str(CFG, 'Twitter', 'twitter_username', '', censor_log=True) TWITTER_PASSWORD = check_setting_str(CFG, 'Twitter', 'twitter_password', '', censor_log=True) - TWITTER_PREFIX = check_setting_str(CFG, 'Twitter', 'twitter_prefix', 'SickRage') + TWITTER_PREFIX = check_setting_str(CFG, 'Twitter', 'twitter_prefix', GIT_REPO) TWITTER_DMTO = check_setting_str(CFG, 'Twitter', 'twitter_dmto', '') TWITTER_USEDM = bool(check_setting_int(CFG, 'Twitter', 'twitter_usedm', 0)) @@ -1058,8 +1056,6 @@ def initialize(consoleLogging=True): TRAKT_TIMEOUT = check_setting_int(CFG, 'Trakt', 'trakt_timeout', 30) TRAKT_BLACKLIST_NAME = check_setting_str(CFG, 'Trakt', 'trakt_blacklist_name', '') - USE_IMDB_POPULAR = bool(check_setting_int(CFG, 'IMDB', 'use_imdb_popular', 1)) - USE_PYTIVO = bool(check_setting_int(CFG, 'pyTivo', 'use_pytivo', 0)) PYTIVO_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'pyTivo', 'pytivo_notify_onsnatch', 0)) PYTIVO_NOTIFY_ONDOWNLOAD = bool(check_setting_int(CFG, 'pyTivo', 'pytivo_notify_ondownload', 0)) @@ -2012,9 +2008,6 @@ def save_config(): new_config['Trakt']['trakt_timeout'] = int(TRAKT_TIMEOUT) new_config['Trakt']['trakt_blacklist_name'] = TRAKT_BLACKLIST_NAME - new_config['IMDB'] = {} - new_config['IMDB']['use_imdb_popular'] = int(USE_IMDB_POPULAR) - new_config['pyTivo'] = {} new_config['pyTivo']['use_pytivo'] = int(USE_PYTIVO) new_config['pyTivo']['pytivo_notify_onsnatch'] = int(PYTIVO_NOTIFY_ONSNATCH) diff --git a/sickbeard/classes.py b/sickbeard/classes.py index 0cc66e639364f15867ec6bd1c733e636153debd5..db93ba1a7c6a298ca9a6ed3f8b9e1bf0c02760eb 100644 --- a/sickbeard/classes.py +++ b/sickbeard/classes.py @@ -25,6 +25,7 @@ import datetime from dateutil import parser from common import USER_AGENT, Quality +from sickrage.helper.common import dateFormat, dateTimeFormat class SickBeardURLopener(urllib.FancyURLopener): @@ -35,7 +36,7 @@ class AuthURLOpener(SickBeardURLopener): """ URLOpener class that supports http auth without needing interactive password entry. If the provided username/password don't work it simply fails. - + user: username to use for HTTP auth pw: password to use for HTTP auth """ @@ -59,11 +60,11 @@ class AuthURLOpener(SickBeardURLopener): # if this is the first try then provide a username/password if self.numTries == 0: self.numTries = 1 - return (self.username, self.password) + return self.username, self.password # if we've tried before then return blank which cancels the request else: - return ('', '') + return '', '' # this is pretty much just a hack for convenience def openit(self, url): @@ -188,9 +189,9 @@ class AllShowsListUI: if searchterm.lower() in name.lower(): if 'firstaired' not in curShow: curShow['firstaired'] = str(datetime.date.fromordinal(1)) - curShow['firstaired'] = re.sub("([-]0{2}){1,}", "", curShow['firstaired']) + curShow['firstaired'] = re.sub("([-]0{2})+", "", curShow['firstaired']) fixDate = parser.parse(curShow['firstaired'], fuzzy=True).date() - curShow['firstaired'] = fixDate.strftime("%Y-%m-%d") + curShow['firstaired'] = fixDate.strftime(dateFormat) if curShow not in searchResults: searchResults += [curShow] @@ -202,7 +203,7 @@ class ShowListUI: """ This class is for tvdb-api. Instead of prompting with a UI to pick the desired result out of a list of shows it tries to be smart about it - based on what shows are in SB. + based on what shows are in SickRage. """ def __init__(self, config, log=None): @@ -245,7 +246,7 @@ class Proper: self.indexerid) + " from " + str(sickbeard.indexerApi(self.indexer).name) -class ErrorViewer(): +class ErrorViewer: """ Keeps a static list of UIErrors to be displayed on the UI and allows the list to be cleared. @@ -269,7 +270,7 @@ class ErrorViewer(): return ErrorViewer.errors -class UIError(): +class UIError: """ Represents an error to be displayed in the web UI. """ @@ -277,4 +278,4 @@ class UIError(): def __init__(self, message): self.title = sys.exc_info()[-2] or message self.message = message - self.time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + self.time = datetime.datetime.now().strftime(dateTimeFormat) diff --git a/sickbeard/databases/mainDB.py b/sickbeard/databases/mainDB.py index 7bf7595897725561131b9fc20351247cc7546264..42ff879c78084ae327849947748ec67df385d707 100644 --- a/sickbeard/databases/mainDB.py +++ b/sickbeard/databases/mainDB.py @@ -25,12 +25,14 @@ from sickbeard import db, common, helpers, logger from sickbeard import encodingKludge as ek from sickbeard.name_parser.parser import NameParser, InvalidNameException, InvalidShowException +from sickrage.helper.common import dateTimeFormat from babelfish import language_converters MIN_DB_VERSION = 9 # oldest db version we support migrating from MAX_DB_VERSION = 42 + class MainSanityCheck(db.DBSanityCheck): def check(self): self.fix_missing_table_indexes() @@ -237,7 +239,7 @@ class MainSanityCheck(db.DBSanityCheck): sqlResults = self.connection.select( "SELECT subtitles, episode_id FROM tv_episodes WHERE subtitles != '' AND subtitles_lastsearch < ?;", - [datetime.datetime(2015, 7, 15, 17, 20, 44, 326380).strftime("%Y-%m-%d %H:%M:%S")]) + [datetime.datetime(2015, 7, 15, 17, 20, 44, 326380).strftime(dateTimeFormat)]) if not sqlResults: return @@ -257,11 +259,12 @@ class MainSanityCheck(db.DBSanityCheck): langs.append(subcode) self.connection.action("UPDATE tv_episodes SET subtitles = ?, subtitles_lastsearch = ? WHERE episode_id = ?;", - [','.join(langs), datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), sqlResult['episode_id']]) + [','.join(langs), datetime.datetime.now().strftime(dateTimeFormat), sqlResult['episode_id']]) def fix_show_nfo_lang(self): self.connection.action("UPDATE tv_shows SET lang = '' WHERE lang = 0 or lang = '0'") + def backupDatabase(version): logger.log(u"Backing up database before upgrade") if not helpers.backupVersionedFile(db.dbFilename(), version): diff --git a/sickbeard/logger.py b/sickbeard/logger.py index a5d1b42bc5c20b311b5a29bd71379cefdd573841..e3161d008fbc71ae86bbab20eb8aff22543e3d17 100644 --- a/sickbeard/logger.py +++ b/sickbeard/logger.py @@ -28,6 +28,7 @@ import locale import sickbeard from sickbeard import classes, encodingKludge as ek +from sickrage.helper.common import dateTimeFormat from github import Github, InputFileContent import codecs @@ -118,16 +119,16 @@ class Logger(object): # rotating log file handler if self.fileLogging: rfh = logging.handlers.RotatingFileHandler(self.logFile, maxBytes=sickbeard.LOG_SIZE, backupCount=sickbeard.LOG_NR, encoding='utf-8') - rfh.setFormatter(CensoredFormatter(u'%(asctime)s %(levelname)-8s %(message)s', '%Y-%m-%d %H:%M:%S')) + rfh.setFormatter(CensoredFormatter(u'%(asctime)s %(levelname)-8s %(message)s', dateTimeFormat)) rfh.setLevel(DEBUG) for logger in self.loggers: logger.addHandler(rfh) - + def shutdown(self): - + logging.shutdown() - + def log(self, msg, level=INFO, *args, **kwargs): meThread = threading.currentThread().getName() message = meThread + u" :: " + msg @@ -155,7 +156,7 @@ class Logger(object): if not (sickbeard.GIT_USERNAME and sickbeard.GIT_PASSWORD and sickbeard.DEBUG and len(classes.ErrorViewer.errors) > 0): self.log('Please set your GitHub username and password in the config and enable debug. Unable to submit issue ticket to GitHub!') return - + try: from versionChecker import CheckVersion checkversion = CheckVersion() @@ -167,7 +168,7 @@ class Logger(object): if commits_behind is None or commits_behind > 0: self.log('Please update SickRage, unable to submit issue ticket to GitHub with an outdated version!') - return + return if self.submitter_running: return 'RUNNING' @@ -186,7 +187,7 @@ class Logger(object): if os.path.isfile(self.logFile): with ek.ek(codecs.open, *[self.logFile, 'r', 'utf-8']) as f: log_data = f.readlines() - + for i in range (1 , int(sickbeard.LOG_NR)): if os.path.isfile(self.logFile + "." + str(i)) and (len(log_data) <= 500): with ek.ek(codecs.open, *[self.logFile + "." + str(i), 'r', 'utf-8']) as f: @@ -230,7 +231,7 @@ class Logger(object): try: message += u"Locale: " + locale.getdefaultlocale()[1] + "\n" except: - message += u"Locale: unknown" + "\n" + message += u"Locale: unknown" + "\n" message += u"Branch: **" + sickbeard.BRANCH + "**\n" message += u"Commit: SiCKRAGETV/SickRage@" + sickbeard.CUR_COMMIT_HASH + "\n" if gist and gist != 'No ERROR found': diff --git a/sickbeard/metadata/kodi_12plus.py b/sickbeard/metadata/kodi_12plus.py index 95e9f1b5ff931311b3e0998c8100a2589704d172..17f8bb1edd9f8ccfdc5f411eb371fd80831f2da8 100644 --- a/sickbeard/metadata/kodi_12plus.py +++ b/sickbeard/metadata/kodi_12plus.py @@ -22,6 +22,7 @@ import sickbeard from sickbeard import logger, exceptions, helpers from sickbeard.exceptions import ex +from sickrage.helper.common import dateFormat import xml.etree.cElementTree as etree @@ -141,7 +142,7 @@ class KODI_12PlusMetadata(generic.GenericMetadata): year = etree.SubElement(tv_node, "year") if getattr(myShow, 'firstaired', None) is not None: try: - year_text = str(datetime.datetime.strptime(myShow["firstaired"], '%Y-%m-%d').year) + year_text = str(datetime.datetime.strptime(myShow["firstaired"], dateFormat).year) if year_text: year.text = year_text except: diff --git a/sickbeard/metadata/mede8er.py b/sickbeard/metadata/mede8er.py index 4db891493c761c5f7d000d188d776f564807e4ab..45e30e9b420d61720b9b9b22d50f5b1f39d61cc7 100644 --- a/sickbeard/metadata/mede8er.py +++ b/sickbeard/metadata/mede8er.py @@ -26,6 +26,7 @@ import mediabrowser from sickbeard import logger, exceptions, helpers from sickbeard.exceptions import ex from sickbeard import encodingKludge as ek +from sickrage.helper.common import dateFormat try: import xml.etree.cElementTree as etree @@ -142,7 +143,7 @@ class Mede8erMetadata(mediabrowser.MediaBrowserMetadata): SeriesName = etree.SubElement(tv_node, "title") SeriesName.text = myShow['seriesname'] - + Genres = etree.SubElement(tv_node, "genres") if getattr(myShow, "genre", None) != None: for genre in myShow['genre'].split('|'): @@ -157,7 +158,7 @@ class Mede8erMetadata(mediabrowser.MediaBrowserMetadata): year = etree.SubElement(tv_node, "year") if getattr(myShow, "firstaired", None) != None: try: - year_text = str(datetime.datetime.strptime(myShow["firstaired"], '%Y-%m-%d').year) + year_text = str(datetime.datetime.strptime(myShow["firstaired"], dateFormat).year) if year_text: year.text = year_text except: @@ -286,7 +287,7 @@ class Mede8erMetadata(mediabrowser.MediaBrowserMetadata): year = etree.SubElement(episode, "year") if getattr(myShow, "firstaired", None) != None: try: - year_text = str(datetime.datetime.strptime(myShow["firstaired"], '%Y-%m-%d').year) + year_text = str(datetime.datetime.strptime(myShow["firstaired"], dateFormat).year) if year_text: year.text = year_text except: @@ -399,7 +400,7 @@ class Mede8erMetadata(mediabrowser.MediaBrowserMetadata): return False return True - + def write_ep_file(self, ep_obj): """ Generates and writes ep_obj's metadata under the given path with the diff --git a/sickbeard/metadata/mediabrowser.py b/sickbeard/metadata/mediabrowser.py index e2d3fcd0ef3895162656e9c4c8a345646f37eca2..7ed50b75add56156809233dea93e4f6947fa8779 100644 --- a/sickbeard/metadata/mediabrowser.py +++ b/sickbeard/metadata/mediabrowser.py @@ -28,6 +28,7 @@ from sickbeard import logger, exceptions, helpers from sickbeard import encodingKludge as ek from sickbeard.exceptions import ex +from sickrage.helper.common import dateFormat import xml.etree.cElementTree as etree @@ -316,7 +317,7 @@ class MediaBrowserMetadata(generic.GenericMetadata): ProductionYear = etree.SubElement(tv_node, "ProductionYear") if getattr(myShow, 'firstaired', None) is not None: try: - year_text = str(datetime.datetime.strptime(myShow['firstaired'], '%Y-%m-%d').year) + year_text = str(datetime.datetime.strptime(myShow['firstaired'], dateFormat).year) if year_text: ProductionYear.text = year_text except: diff --git a/sickbeard/metadata/wdtv.py b/sickbeard/metadata/wdtv.py index 5fba6dea5f1aef1e66d2467aa0917bc72b6efd02..bcc625d47587e918097932b0df1b354932dbc04c 100644 --- a/sickbeard/metadata/wdtv.py +++ b/sickbeard/metadata/wdtv.py @@ -28,6 +28,7 @@ from sickbeard import logger, exceptions, helpers from sickbeard import encodingKludge as ek from sickbeard.exceptions import ex +from sickrage.helper.common import dateFormat import xml.etree.cElementTree as etree @@ -252,7 +253,7 @@ class WDTVMetadata(generic.GenericMetadata): year = etree.SubElement(episode, "year") if getattr(myShow, 'firstaired', None) is not None: try: - year_text = str(datetime.datetime.strptime(myShow["firstaired"], '%Y-%m-%d').year) + year_text = str(datetime.datetime.strptime(myShow["firstaired"], dateFormat).year) if year_text: year.text = year_text except: diff --git a/sickbeard/providers/bitsoup.py b/sickbeard/providers/bitsoup.py index d56d3417c4ea19d7a25aa9ba6fdc1405cb579a54..2bd5d74274a0f0224706850e4db04f317ae81d9a 100644 --- a/sickbeard/providers/bitsoup.py +++ b/sickbeard/providers/bitsoup.py @@ -202,7 +202,7 @@ class BitSoupProvider(generic.TorrentProvider): continue item = title, download_url, id, seeders, leechers - logger.log(u"Found result: " + title.replace(' ','.') + " (" + searchURL + ")", logger.DEBUG) + logger.log(u"Found result: " + title.replace(' ','.') + " (" + search_string + ")", logger.DEBUG) items[mode].append(item) diff --git a/sickbeard/providers/generic.py b/sickbeard/providers/generic.py index 037253b05acbd7a4f0cffd3e47e5a3208c8e05e3..cd0eb802f545fe659d8f243081c0a57c340b9d5f 100644 --- a/sickbeard/providers/generic.py +++ b/sickbeard/providers/generic.py @@ -78,12 +78,12 @@ class GenericProvider: self.headers = {'User-Agent': user_agents[0]} self.btCacheURLS = [ - 'http://torcache.net/torrent/{torrent_hash}.torrent', - 'http://thetorrent.org/torrent/{torrent_hash}.torrent', - 'http://btdig.com/torrent/{torrent_hash}.torrent', - #'http://torrage.com/torrent/{torrent_hash}.torrent', - #'http://itorrents.org/torrent/{torrent_hash}.torrent', - ] + 'http://torcache.net/torrent/{torrent_hash}.torrent', + 'http://thetorrent.org/torrent/{torrent_hash}.torrent', + 'http://btdig.com/torrent/{torrent_hash}.torrent', + # 'http://torrage.com/torrent/{torrent_hash}.torrent', + # 'http://itorrents.org/torrent/{torrent_hash}.torrent', + ] shuffle(self.btCacheURLS) @@ -208,6 +208,10 @@ class GenericProvider: if 'NO_DOWNLOAD_NAME' in url: continue + if not self.proxy.isEnabled() and url.startswith('http'): + # Let's just set a referer for every .torrent/.nzb, should work as a cover-all without side-effects + self.headers.update({'Referer': '/'.join(url.split('/')[:3]) + '/'}) + logger.log(u"Downloading a result from " + self.name + " at " + url) if helpers.download_file(self.proxy._buildURL(url), filename, session=self.session, headers=self.headers): if self._verify_download(filename): diff --git a/sickbeard/subtitles.py b/sickbeard/subtitles.py index 1715f3e15444a5008f7fb4be1fb23ed83b378efd..92bb826d02fda167aa688a446d206b9136e81519 100644 --- a/sickbeard/subtitles.py +++ b/sickbeard/subtitles.py @@ -23,6 +23,7 @@ from sickbeard.exceptions import ex from sickbeard import logger from sickbeard import encodingKludge as ek from sickbeard import db +from sickrage.helper.common import dateTimeFormat import subliminal import babelfish import subprocess @@ -76,7 +77,7 @@ def isValidLanguage(language): return True def getLanguageName(language): - return fromietf(language ).name + return fromietf(language).name # TODO: Filter here for non-languages in sickbeard.SUBTITLES_LANGUAGES def wantedLanguages(sqlLike = False): @@ -89,15 +90,19 @@ def subtitlesLanguages(video_path): """Return a list detected subtitles for the given video file""" resultList = [] + # Serch for embedded subtitles + embedded_languages = subliminal.video.scan_video(video_path, subtitles=False, embedded_subtitles=not sickbeard.EMBEDDED_SUBTITLES_ALL) + + # Search subtitles in the absolute path if sickbeard.SUBTITLES_DIR and ek.ek(os.path.exists, sickbeard.SUBTITLES_DIR): video_path = ek.ek(os.path.join, sickbeard.SUBTITLES_DIR, ek.ek(os.path.basename, video_path)) # Search subtitles in the relative path - if sickbeard.SUBTITLES_DIR: + elif sickbeard.SUBTITLES_DIR: video_path = ek.ek(os.path.join, ek.ek(os.path.dirname, video_path), sickbeard.SUBTITLES_DIR, ek.ek(os.path.basename, video_path)) languages = subliminal.video.scan_subtitle_languages(video_path) - for language in languages: + for language in languages.union(embedded_languages.subtitle_languages): if hasattr(language, 'opensubtitles') and language.opensubtitles: resultList.append(language.opensubtitles) elif hasattr(language, 'alpha3') and language.alpha3: @@ -171,9 +176,9 @@ class SubtitlesFinder(): # Old shows rule throwaway = datetime.datetime.strptime('20110101', '%Y%m%d') - if ((epToSub['airdate_daydiff'] > 7 and epToSub['searchcount'] < 2 and now - datetime.datetime.strptime(epToSub['lastsearch'], '%Y-%m-%d %H:%M:%S') > datetime.timedelta(hours=rules['old'][epToSub['searchcount']])) or + if ((epToSub['airdate_daydiff'] > 7 and epToSub['searchcount'] < 2 and now - datetime.datetime.strptime(epToSub['lastsearch'], dateTimeFormat) > datetime.timedelta(hours=rules['old'][epToSub['searchcount']])) or # Recent shows rule - (epToSub['airdate_daydiff'] <= 7 and epToSub['searchcount'] < 7 and now - datetime.datetime.strptime(epToSub['lastsearch'], '%Y-%m-%d %H:%M:%S') > datetime.timedelta(hours=rules['new'][epToSub['searchcount']]))): + (epToSub['airdate_daydiff'] <= 7 and epToSub['searchcount'] < 7 and now - datetime.datetime.strptime(epToSub['lastsearch'], dateTimeFormat) > datetime.timedelta(hours=rules['new'][epToSub['searchcount']]))): logger.log('Downloading subtitles for episode %dx%d of show %s' % (epToSub['season'], epToSub['episode'], epToSub['show_name']), logger.DEBUG) showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(epToSub['showid'])) diff --git a/sickbeard/tv.py b/sickbeard/tv.py index a9ff54bebc74f468ed121b8dcc0bce02d99f3aee..fb7627b109c0089f073e6ff1626ed5667955eb22 100644 --- a/sickbeard/tv.py +++ b/sickbeard/tv.py @@ -52,6 +52,7 @@ from sickbeard.blackandwhitelist import BlackAndWhiteList from sickbeard import sbdatetime from sickbeard import network_timezones from sickbeard.indexers.indexer_config import INDEXER_TVRAGE +from sickrage.helper.common import dateTimeFormat from dateutil.tz import * from sickbeard import encodingKludge as ek @@ -1457,12 +1458,12 @@ class TVEpisode(object): video = None try: # Never look for subtitles in the same path, as we specify the path later on - video = subliminal.scan_video(vname, subtitles=False, embedded_subtitles=not sickbeard.EMBEDDED_SUBTITLES_ALL or not force) + video = subliminal.scan_video(vname, subtitles=False, embedded_subtitles=False) except Exception: logger.log(u'%s: Exception caught in subliminal.scan_video for S%02dE%02d' % (self.show.indexerid, self.season, self.episode), logger.DEBUG) return - + if not video: return @@ -1506,7 +1507,7 @@ class TVEpisode(object): self.refreshSubtitles() self.subtitles_searchcount += 1 if self.subtitles_searchcount else 1 - self.subtitles_lastsearch = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + self.subtitles_lastsearch = datetime.datetime.now().strftime(dateTimeFormat) self.saveToDB() newSubtitles = frozenset(self.subtitles).difference(previous_subtitles) diff --git a/sickbeard/versionChecker.py b/sickbeard/versionChecker.py index eec39da22707de22924e3adae116e9e41d231544..a106aaeacd6b2a836f14fb77aa19fd91c2f7c8b7 100644 --- a/sickbeard/versionChecker.py +++ b/sickbeard/versionChecker.py @@ -20,7 +20,7 @@ import os import platform import subprocess import re -import urllib + import tarfile import stat import traceback @@ -34,7 +34,6 @@ from sickbeard import logger, helpers from sickbeard.exceptions import ex from sickbeard import encodingKludge as ek import requests -from requests.exceptions import RequestException import shutil import shutil_custom @@ -58,6 +57,8 @@ class CheckVersion(): elif self.install_type == 'source': self.updater = SourceUpdateManager() + self.session=requests.Session() + def run(self, force=False): self.amActive = True @@ -198,9 +199,14 @@ class CheckVersion(): def getDBcompare(self): try: - response = requests.get("http://cdn.rawgit.com/SICKRAGETV/SickRage/" + str(self.updater.get_newest_commit_hash()) +"/sickbeard/databases/mainDB.py") - response.raise_for_status() - match = re.search(r"MAX_DB_VERSION\s=\s(?P<version>\d{2,3})",response.text) + cur_hash = str(self.updater.get_newest_commit_hash()) + assert len(cur_hash) is 40, "Commit hash wrong length: %s hash: %s" % (len(cur_hash), cur_hash) + + check_url = "http://cdn.rawgit.com/%s/%s/%s/sickbeard/databases/mainDB.py" % (sickbeard.GIT_ORG, sickbeard.GIT_REPO, cur_hash) + response = helpers.getURL(check_url, session=self.session) + assert response, "Empty response from %s" % check_url + + match = re.search(r"MAX_DB_VERSION\s=\s(?P<version>\d{2,3})", response) branchDestDBversion = int(match.group('version')) myDB = db.DBConnection() branchCurrDBversion = myDB.checkDBVersion() @@ -210,9 +216,7 @@ class CheckVersion(): return 'equal' else: return 'downgrade' - except RequestException as e: - return 'error' - except Exception as e: + except Exception: return 'error' def find_install_type(self): @@ -624,6 +628,8 @@ class GitUpdateManager(UpdateManager): def update_remote_origin(self): self._run_git(self._git_path, 'config remote.%s.url %s' % (sickbeard.GIT_REMOTE, sickbeard.GIT_REMOTE_URL)) + if sickbeard.GIT_USERNAME: + self._run_git(self._git_path, 'config remote.%s.pushurl %s' % (sickbeard.GIT_REMOTE, sickbeard.GIT_REMOTE_URL.replace(sickbeard.GIT_ORG, sickbeard.GIT_USERNAME))) class SourceUpdateManager(UpdateManager): def __init__(self): @@ -638,6 +644,8 @@ class SourceUpdateManager(UpdateManager): self._newest_commit_hash = None self._num_commits_behind = 0 + self.session=requests.Session() + def _find_installed_branch(self): if sickbeard.CUR_COMMIT_BRANCH == "": return "master" @@ -761,7 +769,7 @@ class SourceUpdateManager(UpdateManager): # retrieve file logger.log(u"Downloading update from " + repr(tar_download_url)) tar_download_path = os.path.join(sr_update_dir, u'sr-update.tar') - urllib.urlretrieve(tar_download_url, tar_download_path) + helpers.download_file(tar_download_url, tar_download_path, session=self.session) if not ek.ek(os.path.isfile, tar_download_path): logger.log(u"Unable to retrieve new version from " + tar_download_url + ", can't update", logger.WARNING) diff --git a/sickbeard/webapi.py b/sickbeard/webapi.py index 48fc149bc868a430214ac3ecdc82ba2eeae34a41..a2eae8507d735210edce81a50f788c0e98e9258c 100644 --- a/sickbeard/webapi.py +++ b/sickbeard/webapi.py @@ -27,10 +27,13 @@ import re import traceback import sickbeard +from sickrage.helper.common import dateFormat, dateTimeFormat, timeFormat +from sickrage.helper.quality import get_quality_string from sickrage.media.ShowFanArt import ShowFanArt from sickrage.media.ShowNetworkLogo import ShowNetworkLogo from sickrage.media.ShowPoster import ShowPoster from sickrage.media.ShowBanner import ShowBanner +from sickrage.show.ComingEpisodes import ComingEpisodes from sickrage.show.History import History from sickrage.system.Restart import Restart from sickrage.system.Shutdown import Shutdown @@ -55,7 +58,6 @@ from sickbeard.common import SNATCHED_PROPER from sickbeard.common import UNAIRED from sickbeard.common import UNKNOWN from sickbeard.common import WANTED -from sickbeard.common import qualityPresetStrings from sickbeard.common import statusStrings import codecs @@ -69,10 +71,6 @@ from tornado.web import RequestHandler indexer_ids = ["indexerid", "tvdbid"] -dateFormat = "%Y-%m-%d" -dateTimeFormat = "%Y-%m-%d %H:%M" -timeFormat = '%A %I:%M %p' - RESULT_SUCCESS = 10 # only use inside the run methods RESULT_FAILURE = 20 # only use inside the run methods RESULT_TIMEOUT = 30 # not used yet :( @@ -517,15 +515,6 @@ def _responds(result_type, data=None, msg=""): "data": data} -def _get_quality_string(q): - qualityString = "Custom" - if q in qualityPresetStrings: - qualityString = qualityPresetStrings[q] - elif q in Quality.qualityStrings: - qualityString = Quality.qualityStrings[q] - return qualityString - - def _get_status_Strings(s): return statusStrings[s] @@ -622,12 +611,15 @@ def _getRootDirs(): class ApiError(Exception): - "Generic API error" + """ + Generic API error + """ class IntParseError(Exception): - "A value could not be parsed into a int. But should be parsable to a int " - + """ + A value could not be parsed into an int, but should be parsable to an int + """ # -------------------------------------------------------------------------------------# @@ -655,126 +647,53 @@ class CMD_Help(ApiCall): class CMD_ComingEpisodes(ApiCall): - _help = {"desc": "display the coming episodes", - "optionalParameters": {"sort": {"desc": "change the sort order"}, - "type": {"desc": "one or more of allowedValues separated by |"}, - "paused": { - "desc": "0 to exclude paused shows, 1 to include them, or omitted to use the SR default"}, - } + _help = { + "desc": "Display the coming episodes", + "optionalParameters": { + "sort": {"desc": "Change the sort order"}, + "type": {"desc": "One or more of allowedValues separated by |"}, + "paused": { + "desc": "0 to exclude paused shows, 1 to include them, or omitted to use SickRage default value" + }, + } } def __init__(self, args, kwargs): # required # optional - 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.sort, args = self.check_params(args, kwargs, "sort", "date", False, "string", ComingEpisodes.sorts.keys()) + self.type, args = self.check_params(args, kwargs, "type", '|'.join(ComingEpisodes.categories), False, "list", + ComingEpisodes.categories) + self.paused, args = self.check_params(args, kwargs, "paused", sickbeard.COMING_EPS_DISPLAY_PAUSED, False, "int", [0, 1]) # super, missing, help ApiCall.__init__(self, args, kwargs) def run(self): - """ display the coming episodes """ - today = datetime.date.today().toordinal() - next_week = (datetime.date.today() + datetime.timedelta(days=7)).toordinal() - recently = (datetime.date.today() - datetime.timedelta(days=sickbeard.COMING_EPS_MISSED_RANGE)).toordinal() - - done_show_list = [] - qualList = Quality.DOWNLOADED + Quality.SNATCHED + Quality.ARCHIVED + [IGNORED] - - myDB = db.DBConnection(row_type="dict") - sql_results = myDB.select( - "SELECT airdate, airs, episode, name AS 'ep_name', description AS 'ep_plot', network, season, showid AS 'indexerid', show_name, tv_shows.quality AS quality, tv_shows.status AS 'show_status', tv_shows.paused AS 'paused' FROM tv_episodes, tv_shows WHERE season != 0 AND airdate >= ? AND airdate < ? AND tv_shows.indexer_id = tv_episodes.showid AND tv_episodes.status NOT IN (" + ','.join( - ['?'] * len(qualList)) + ")", [today, next_week] + qualList) - for cur_result in sql_results: - done_show_list.append(int(cur_result["indexerid"])) - - more_sql_results = myDB.select( - "SELECT airdate, airs, episode, name AS 'ep_name', description AS 'ep_plot', network, season, showid AS 'indexerid', show_name, tv_shows.quality AS quality, tv_shows.status AS 'show_status', tv_shows.paused AS 'paused' FROM tv_episodes outer_eps, tv_shows WHERE season != 0 AND showid NOT IN (" + ','.join( - ['?'] * len( - done_show_list)) + ") AND tv_shows.indexer_id = outer_eps.showid AND airdate = (SELECT airdate FROM tv_episodes inner_eps WHERE inner_eps.season != 0 AND inner_eps.showid = outer_eps.showid AND inner_eps.airdate >= ? ORDER BY inner_eps.airdate ASC LIMIT 1) AND outer_eps.status NOT IN (" + ','.join( - ['?'] * len(Quality.DOWNLOADED + Quality.SNATCHED)) + ")", - done_show_list + [next_week] + Quality.DOWNLOADED + Quality.SNATCHED) - sql_results += more_sql_results - - more_sql_results = myDB.select( - "SELECT airdate, airs, episode, name AS 'ep_name', description AS 'ep_plot', network, season, showid AS 'indexerid', show_name, tv_shows.quality AS quality, tv_shows.status AS 'show_status', tv_shows.paused AS 'paused' FROM tv_episodes, tv_shows WHERE season != 0 AND tv_shows.indexer_id = tv_episodes.showid AND airdate < ? AND airdate >= ? AND tv_episodes.status = ? AND tv_episodes.status NOT IN (" + ','.join( - ['?'] * len(qualList)) + ")", [today, recently, WANTED] + qualList) - sql_results += more_sql_results - - # sort by air date - sorts = { - 'date': (lambda x, y: cmp(int(x["airdate"]), int(y["airdate"]))), - 'show': (lambda a, b: cmp(a["show_name"], b["show_name"])), - 'network': (lambda a, b: cmp(a["network"], b["network"])), - } - - sql_results.sort(sorts[self.sort]) - finalEpResults = {} - - # add all requested types or all - for curType in self.type: - finalEpResults[curType] = [] - - # Safety Measure to convert rows in sql_results to dict. - # This should not be required as the DB connections should only be returning dict results not sqlite3.row_type - dict_results = [dict(row) for row in sql_results] - - for ep in dict_results: - """ - Missed: yesterday... (less than 1week) - Today: today - Soon: tomorrow till next week - Later: later than next week - """ - - if ep["paused"] and not self.paused: - continue - - ep['airs'] = str(ep['airs']).replace('am', ' AM').replace('pm', ' PM').replace(' ', ' ') - dtEpisodeAirs = sbdatetime.sbdatetime.convert_to_setting( - network_timezones.parse_date_time(int(ep['airdate']), ep['airs'], ep['network'])) - ep['airdate'] = dtEpisodeAirs.toordinal() - - status = "soon" - if ep["airdate"] < today: - status = "missed" - elif ep["airdate"] >= next_week: - status = "later" - elif ep["airdate"] >= today and ep["airdate"] < next_week: - if ep["airdate"] == today: - status = "today" - else: - status = "soon" - - # skip unwanted - if self.type is not None and not status in self.type: - continue - - if not ep["network"]: - ep["network"] = "" - - ep["quality"] = _get_quality_string(ep["quality"]) - # clean up tvdb horrible airs field - ep['airs'] = sbdatetime.sbdatetime.sbftime(dtEpisodeAirs, t_preset=timeFormat).lstrip('0').replace(' 0', - ' ') - # start day of the week on 1 (monday) - ep['weekday'] = 1 + datetime.date.fromordinal(dtEpisodeAirs.toordinal()).weekday() - # Add tvdbid for backward compability - ep["tvdbid"] = ep['indexerid'] - ep['airdate'] = sbdatetime.sbdatetime.sbfdate(dtEpisodeAirs, d_preset=dateFormat) - - # TODO: check if this obsolete - if not status in finalEpResults: - finalEpResults[status] = [] - - finalEpResults[status].append(ep) + """ Display the coming episodes """ + grouped_coming_episodes = ComingEpisodes.get_coming_episodes(self.type, self.sort, True, self.paused) + data = {section: [] for section in grouped_coming_episodes.keys()} + + for section, coming_episodes in grouped_coming_episodes.iteritems(): + for coming_episode in coming_episodes: + data[section].append({ + 'airdate': coming_episode['airdate'], + 'airs': coming_episode['airs'], + 'ep_name': coming_episode['name'], + 'ep_plot': coming_episode['description'], + 'episode': coming_episode['episode'], + 'indexerid': coming_episode['indexer_id'], + 'network': coming_episode['network'], + 'paused': coming_episode['paused'], + 'quality': coming_episode['quality'], + 'season': coming_episode['season'], + 'show_name': coming_episode['show_name'], + 'show_status': coming_episode['status'], + 'tvdbid': coming_episode['tvdbid'], + 'weekday': coming_episode['weekday'] + }) - return _responds(RESULT_SUCCESS, finalEpResults) + return _responds(RESULT_SUCCESS, data) class CMD_Episode(ApiCall): @@ -836,7 +755,7 @@ class CMD_Episode(ApiCall): d_preset=dateFormat) status, quality = Quality.splitCompositeStatus(int(episode["status"])) episode["status"] = _get_status_Strings(status) - episode["quality"] = _get_quality_string(quality) + episode["quality"] = get_quality_string(quality) episode["file_size_human"] = _sizeof_fmt(episode["file_size"]) return _responds(RESULT_SUCCESS, episode) @@ -886,8 +805,8 @@ class CMD_EpisodeSearch(ApiCall): if ep_queue_item.success: status, quality = Quality.splitCompositeStatus(epObj.status) # @UnusedVariable # TODO: split quality and status? - return _responds(RESULT_SUCCESS, {"quality": _get_quality_string(quality)}, - "Snatched (" + _get_quality_string(quality) + ")") + return _responds(RESULT_SUCCESS, {"quality": get_quality_string(quality)}, + "Snatched (" + get_quality_string(quality) + ")") return _responds(RESULT_FAILURE, msg='Unable to find episode') @@ -1143,7 +1062,7 @@ class CMD_History(ApiCall): continue row["status"] = status - row["quality"] = _get_quality_string(quality) + row["quality"] = get_quality_string(quality) row["date"] = _historyDate_to_dateTimeForm(str(row["date"])) del row["action"] @@ -1917,7 +1836,7 @@ class CMD_Show(ApiCall): genreList.append(genre) showDict["genre"] = genreList - showDict["quality"] = _get_quality_string(showObj.quality) + showDict["quality"] = get_quality_string(showObj.quality) anyQualities, bestQualities = _mapQuality(showObj.quality) showDict["quality_details"] = {"initial": anyQualities, "archive": bestQualities} @@ -2583,7 +2502,7 @@ class CMD_ShowSeasons(ApiCall): for row in sqlResults: status, quality = Quality.splitCompositeStatus(int(row["status"])) row["status"] = _get_status_Strings(status) - row["quality"] = _get_quality_string(quality) + row["quality"] = get_quality_string(quality) dtEpisodeAirs = sbdatetime.sbdatetime.convert_to_setting( network_timezones.parse_date_time(row['airdate'], showObj.airs, showObj.network)) row['airdate'] = sbdatetime.sbdatetime.sbfdate(dtEpisodeAirs, d_preset=dateFormat) @@ -2607,7 +2526,7 @@ class CMD_ShowSeasons(ApiCall): del row["episode"] status, quality = Quality.splitCompositeStatus(int(row["status"])) row["status"] = _get_status_Strings(status) - row["quality"] = _get_quality_string(quality) + row["quality"] = get_quality_string(quality) dtEpisodeAirs = sbdatetime.sbdatetime.convert_to_setting( network_timezones.parse_date_time(row['airdate'], showObj.airs, showObj.network)) row['airdate'] = sbdatetime.sbdatetime.sbfdate(dtEpisodeAirs, d_preset=dateFormat) @@ -2683,7 +2602,7 @@ class CMD_ShowSetQuality(ApiCall): showObj.quality = newQuality return _responds(RESULT_SUCCESS, - msg=showObj.name + " quality has been changed to " + _get_quality_string(showObj.quality)) + msg=showObj.name + " quality has been changed to " + get_quality_string(showObj.quality)) class CMD_ShowStats(ApiCall): @@ -2853,7 +2772,7 @@ class CMD_Shows(ApiCall): showDict = { "paused": curShow.paused, - "quality": _get_quality_string(curShow.quality), + "quality": get_quality_string(curShow.quality), "language": curShow.lang, "air_by_date": curShow.air_by_date, "sports": curShow.sports, diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index 09f270e579a5c8b0d2291d5eee8db3e52a364113..ae7e2868070d6ecbb9c313b1925b2c813d5ad08a 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -35,7 +35,6 @@ from sickbeard import search_queue from sickbeard import naming from sickbeard import subtitles from sickbeard import network_timezones -from sickbeard import sbdatetime from sickbeard.providers import newznab, rsstorrent from sickbeard.common import Quality, Overview, statusStrings, qualityPresetStrings, cpu_presets from sickbeard.common import SNATCHED, UNAIRED, IGNORED, ARCHIVED, WANTED, FAILED, SKIPPED @@ -58,6 +57,7 @@ from sickrage.media.ShowBanner import ShowBanner from sickrage.media.ShowFanArt import ShowFanArt from sickrage.media.ShowNetworkLogo import ShowNetworkLogo from sickrage.media.ShowPoster import ShowPoster +from sickrage.show.ComingEpisodes import ComingEpisodes from sickrage.show.History import History as HistoryTool from sickrage.system.Restart import Restart from sickrage.system.Shutdown import Shutdown @@ -466,88 +466,49 @@ class WebRoot(WebHandler): return self.redirect("/comingEpisodes/") - def comingEpisodes(self, layout="None"): - - today1 = datetime.date.today() - today = today1.toordinal() - next_week1 = (datetime.date.today() + datetime.timedelta(days=7)) - next_week = next_week1.toordinal() - recently = (datetime.date.today() - datetime.timedelta(days=sickbeard.COMING_EPS_MISSED_RANGE)).toordinal() - - done_show_list = [] - qualList = Quality.DOWNLOADED + Quality.SNATCHED + [ARCHIVED, IGNORED] - - myDB = db.DBConnection() - sql_results = myDB.select( - "SELECT *, tv_shows.status AS show_status FROM tv_episodes, tv_shows WHERE season != 0 AND airdate >= ? AND airdate < ? AND tv_shows.indexer_id = tv_episodes.showid AND tv_episodes.status NOT IN (" + ','.join( - ['?'] * len(qualList)) + ")", [today, next_week] + qualList) - - for cur_result in sql_results: - done_show_list.append(int(cur_result["showid"])) - - 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( - done_show_list)) + ") AND tv_shows.indexer_id = outer_eps.showid AND airdate = (SELECT airdate FROM tv_episodes inner_eps WHERE inner_eps.season != 0 AND inner_eps.showid = outer_eps.showid AND inner_eps.airdate >= ? ORDER BY inner_eps.airdate ASC LIMIT 1) AND outer_eps.status NOT IN (" + ','.join( - ['?'] * len(Quality.DOWNLOADED + Quality.SNATCHED)) + ")", - done_show_list + [next_week] + Quality.DOWNLOADED + Quality.SNATCHED) - sql_results += more_sql_results - - more_sql_results = myDB.select( - "SELECT *, tv_shows.status AS show_status FROM tv_episodes, tv_shows WHERE season != 0 AND tv_shows.indexer_id = tv_episodes.showid AND airdate < ? AND airdate >= ? AND tv_episodes.status = ? AND tv_episodes.status NOT IN (" + ','.join( - ['?'] * len(qualList)) + ")", [today, recently, WANTED] + qualList) - sql_results += more_sql_results - - # sort by localtime - sorts = { - 'date': (lambda x, y: cmp(x["localtime"], y["localtime"])), - 'show': (lambda a, b: cmp((a["show_name"], a["localtime"]), (b["show_name"], b["localtime"]))), - 'network': (lambda a, b: cmp((a["network"], a["localtime"]), (b["network"], b["localtime"]))), - } - - # make a dict out of the sql results - sql_results = [dict(row) for row in sql_results] - - # 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.sort(sorts[sickbeard.COMING_EPS_SORT]) + def comingEpisodes(self, layout=None): + next_week = datetime.date.today() + datetime.timedelta(days=7) + next_week1 = datetime.datetime.combine(next_week, datetime.time(tzinfo=network_timezones.sb_timezone)) + results = ComingEpisodes.get_coming_episodes(ComingEpisodes.categories, sickbeard.COMING_EPS_SORT, False) + today = datetime.datetime.now().replace(tzinfo=network_timezones.sb_timezone) - t = PageTemplate(rh=self, file="comingEpisodes.mako") - # paused_item = { 'title': '', 'path': 'toggleComingEpsDisplayPaused' } - # paused_item['title'] = 'Hide Paused' if sickbeard.COMING_EPS_DISPLAY_PAUSED else 'Show Paused' - paused_item = {'title': 'View Paused:', 'path': {'': ''}} - paused_item['path'] = {'Hide': 'toggleComingEpsDisplayPaused'} if sickbeard.COMING_EPS_DISPLAY_PAUSED else { - 'Show': 'toggleComingEpsDisplayPaused'} submenu = [ - {'title': 'Sort by:', 'path': {'Date': 'setComingEpsSort/?sort=date', - 'Show': 'setComingEpsSort/?sort=show', - 'Network': 'setComingEpsSort/?sort=network', - }}, - - {'title': 'Layout:', 'path': {'Banner': 'setComingEpsLayout/?layout=banner', - 'Poster': 'setComingEpsLayout/?layout=poster', - 'List': 'setComingEpsLayout/?layout=list', - 'Calendar': 'setComingEpsLayout/?layout=calendar', - }}, - paused_item, + { + 'title': 'Sort by:', + 'path': { + 'Date': 'setComingEpsSort/?sort=date', + 'Show': 'setComingEpsSort/?sort=show', + 'Network': 'setComingEpsSort/?sort=network', + } + }, + { + 'title': 'Layout:', + 'path': { + 'Banner': 'setComingEpsLayout/?layout=banner', + 'Poster': 'setComingEpsLayout/?layout=poster', + 'List': 'setComingEpsLayout/?layout=list', + 'Calendar': 'setComingEpsLayout/?layout=calendar', + } + }, + { + 'title': 'View Paused:', + 'path': { + 'Hide': 'toggleComingEpsDisplayPaused' + } if sickbeard.COMING_EPS_DISPLAY_PAUSED else { + 'Show': 'toggleComingEpsDisplayPaused' + } + }, ] - next_week = datetime.datetime.combine(next_week1, datetime.time(tzinfo=network_timezones.sb_timezone)) - today = datetime.datetime.now().replace(tzinfo=network_timezones.sb_timezone) - sql_results = sql_results - # Allow local overriding of layout parameter if layout and layout in ('poster', 'banner', 'list', 'calendar'): layout = layout else: layout = sickbeard.COMING_EPS_LAYOUT - return t.render(submenu=submenu, next_week=next_week, today=today, sql_results=sql_results, layout=layout, - title='Coming Episodes', header='Coming Episodes', topmenu='comingEpisodes') + t = PageTemplate(rh=self, file='comingEpisodes.mako') + return t.render(submenu=submenu, next_week=next_week1, today=today, results=results, layout=layout, + title='Coming Episodes', header='Coming Episodes', topmenu='comingEpisodes') class CalendarHandler(BaseHandler): @@ -4666,7 +4627,7 @@ class ConfigNotifications(Config): trakt_remove_watchlist=None, trakt_sync_watchlist=None, trakt_remove_show_from_sickrage=None, trakt_method_add=None, trakt_start_paused=None, trakt_use_recommended=None, trakt_sync=None, trakt_sync_remove=None, trakt_default_indexer=None, trakt_remove_serieslist=None, trakt_timeout=None, trakt_blacklist_name=None, - use_synologynotifier=None, synologynotifier_notify_onsnatch=None, use_imdb_popular=None, + use_synologynotifier=None, synologynotifier_notify_onsnatch=None, synologynotifier_notify_ondownload=None, synologynotifier_notify_onsubtitledownload=None, use_pytivo=None, pytivo_notify_onsnatch=None, pytivo_notify_ondownload=None, pytivo_notify_onsubtitledownload=None, pytivo_update_library=None, @@ -4802,8 +4763,6 @@ class ConfigNotifications(Config): sickbeard.TRAKT_TIMEOUT = int(trakt_timeout) sickbeard.TRAKT_BLACKLIST_NAME = trakt_blacklist_name - sickbeard.USE_IMDB_POPULAR = config.checkbox_to_value(use_imdb_popular) - sickbeard.USE_EMAIL = config.checkbox_to_value(use_email) sickbeard.EMAIL_NOTIFY_ONSNATCH = config.checkbox_to_value(email_notify_onsnatch) sickbeard.EMAIL_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(email_notify_ondownload) diff --git a/sickrage/helper/__init__.py b/sickrage/helper/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/sickrage/helper/common.py b/sickrage/helper/common.py new file mode 100644 index 0000000000000000000000000000000000000000..bf8bd35317afb2303a703b864d3ffdbab69a7149 --- /dev/null +++ b/sickrage/helper/common.py @@ -0,0 +1,3 @@ +dateFormat = '%Y-%m-%d' +dateTimeFormat = '%Y-%m-%d %H:%M:%S' +timeFormat = '%A %I:%M %p' diff --git a/sickrage/helper/quality.py b/sickrage/helper/quality.py new file mode 100644 index 0000000000000000000000000000000000000000..ebdc1c48719ce151803d9b7345e6959a08b7bda9 --- /dev/null +++ b/sickrage/helper/quality.py @@ -0,0 +1,16 @@ +from sickbeard.common import Quality, qualityPresetStrings + + +def get_quality_string(quality): + """ + :param quality: The quality to convert into a string + :return: The string representation of the provided quality + """ + + if quality in qualityPresetStrings: + return qualityPresetStrings[quality] + + if quality in Quality.qualityStrings: + return Quality.qualityStrings[quality] + + return 'Custom' diff --git a/sickrage/show/ComingEpisodes.py b/sickrage/show/ComingEpisodes.py new file mode 100644 index 0000000000000000000000000000000000000000..2026a9cf9ccdabd678a3bbe4a4b105bf3cccc05a --- /dev/null +++ b/sickrage/show/ComingEpisodes.py @@ -0,0 +1,140 @@ +import sickbeard + +from datetime import date, timedelta +from sickbeard.common import IGNORED, Quality, WANTED +from sickbeard.db import DBConnection +from sickbeard.network_timezones import parse_date_time +from sickbeard.sbdatetime import sbdatetime +from sickrage.helper.common import dateFormat, timeFormat +from sickrage.helper.quality import get_quality_string + + +class ComingEpisodes: + """ + Missed: yesterday...(less than 1 week) + Today: today + Soon: tomorrow till next week + Later: later than next week + """ + categories = ['later', 'missed', 'soon', 'today'] + sorts = { + 'date': (lambda a, b: cmp(a['localtime'], b['localtime'])), + 'network': (lambda a, b: cmp((a['network'], a['localtime']), (b['network'], b['localtime']))), + 'show': (lambda a, b: cmp((a['show_name'], a['localtime']), (b['show_name'], b['localtime']))), + } + + def __init__(self): + pass + + @staticmethod + def get_coming_episodes(categories, sort, group, paused=sickbeard.COMING_EPS_DISPLAY_PAUSED): + """ + :param categories: The categories of coming episodes. See ``ComingEpisodes.categories`` + :param sort: The sort to apply to the coming episodes. See ``ComingEpisodes.sorts`` + :param group: ``True`` to group the coming episodes by category, ``False`` otherwise + :param paused: ``True`` to include paused shows, ``False`` otherwise + :return: The list of coming episodes + """ + + if not isinstance(categories, list): + categories = categories.split('|') + + if sort not in ComingEpisodes.sorts.keys(): + sort = 'date' + + today = date.today().toordinal() + next_week = (date.today() + timedelta(days=7)).toordinal() + recently = (date.today() - timedelta(days=sickbeard.COMING_EPS_MISSED_RANGE)).toordinal() + qualities_list = Quality.DOWNLOADED + Quality.SNATCHED + Quality.ARCHIVED + [IGNORED] + + fields_to_select = ', '.join( + ['airdate', 'airs', 'description', 'episode', 'imdb_id', 'e.indexer', 'indexer_id', 'name', 'network', + 'paused', 'quality', 'runtime', 'season', 'show_name', 'showid', 's.status'] + ) + db = DBConnection(row_type='dict') + results = db.select( + 'SELECT %s ' % fields_to_select + + 'FROM tv_episodes e, tv_shows s ' + 'WHERE season != 0 ' + 'AND airdate >= ? ' + 'AND airdate < ? ' + 'AND s.indexer_id = e.showid ' + 'AND e.status NOT IN (' + ','.join(['?'] * len(qualities_list)) + ')', + [today, next_week] + qualities_list + ) + + done_shows_list = [int(result['showid']) for result in results] + placeholder = ','.join(['?'] * len(done_shows_list)) + placeholder2 = ','.join(['?'] * len(Quality.DOWNLOADED + Quality.SNATCHED)) + + results += db.select( + 'SELECT %s ' % fields_to_select + + 'FROM tv_episodes e, tv_shows s ' + 'WHERE season != 0 ' + 'AND showid NOT IN (' + placeholder + ') ' + 'AND s.indexer_id = e.showid ' + 'AND airdate = (SELECT airdate ' + 'FROM tv_episodes inner_e ' + 'WHERE inner_e.season != 0 ' + 'AND inner_e.showid = e.showid ' + 'AND inner_e.airdate >= ? ' + 'ORDER BY inner_e.airdate ASC LIMIT 1) ' + 'AND e.status NOT IN (' + placeholder2 + ')', + done_shows_list + [next_week] + Quality.DOWNLOADED + Quality.SNATCHED + ) + + results += db.select( + 'SELECT %s ' % fields_to_select + + 'FROM tv_episodes e, tv_shows s ' + 'WHERE season != 0 ' + 'AND s.indexer_id = e.showid ' + 'AND airdate < ? ' + 'AND airdate >= ? ' + 'AND e.status = ? ' + 'AND e.status NOT IN (' + ','.join(['?'] * len(qualities_list)) + ')', + [today, recently, WANTED] + qualities_list + ) + + for index, item in enumerate(results): + results[index]['localtime'] = sbdatetime.convert_to_setting( + parse_date_time(item['airdate'], item['airs'], item['network'])) + + results.sort(ComingEpisodes.sorts[sort]) + + if not group: + return results + + grouped_results = {category: [] for category in categories} + + for result in results: + if result['paused'] and not paused: + continue + + result['airs'] = str(result['airs']).replace('am', ' AM').replace('pm', ' PM').replace(' ', ' ') + result['airdate'] = result['localtime'].toordinal() + + if result['airdate'] < today: + category = 'missed' + elif result['airdate'] >= next_week: + category = 'later' + elif result['airdate'] == today: + category = 'today' + else: + category = 'soon' + + if len(categories) > 0 and category not in categories: + continue + + if not result['network']: + result['network'] = '' + + result['quality'] = get_quality_string(result['quality']) + result['airs'] = sbdatetime.sbftime(result['localtime'], t_preset=timeFormat).lstrip('0').replace(' 0', ' ') + result['weekday'] = 1 + date.fromordinal(result['airdate']).weekday() + result['tvdbid'] = result['indexer_id'] + result['airdate'] = sbdatetime.sbfdate(result['localtime'], d_preset=dateFormat) + result['localtime'] = result['localtime'].toordinal() + + grouped_results[category].append(result) + + return grouped_results diff --git a/sickrage/show/History.py b/sickrage/show/History.py index e6aa2b54a80137c7504681e845ccbce0a98c4598..d1b69555550b60f0fe5fad6dbaab9f569653fc64 100644 --- a/sickrage/show/History.py +++ b/sickrage/show/History.py @@ -38,7 +38,7 @@ class History: else: actions = [] - common_sql = 'SELECT h.*, show_name ' \ + common_sql = 'SELECT action, date, episode, provider, h.quality, resource, season, show_name, showid ' \ 'FROM history h, tv_shows s ' \ 'WHERE h.showid = s.indexer_id ' filter_sql = 'AND action in (' + ','.join(['?'] * len(actions)) + ') ' diff --git a/sickrage/show/__init__.py b/sickrage/show/__init__.py index 38638bc46e7f39d9a00ec01e39f859e08a76df83..6a2173ff8b45d1709c6ca6d68d5be027c582602d 100644 --- a/sickrage/show/__init__.py +++ b/sickrage/show/__init__.py @@ -1 +1 @@ -__all__ = ['History'] +__all__ = ['ComingEpisodes', 'History']