Commit 986e796a authored by echel0n's avatar echel0n

Merge branch 'release/9.2.101'

parents cda782dc aa7292f1
# Changelog
- * 30539e5 - 2018-01-31: Release v9.2.100
- * 10479f1 - 2018-02-08: Release v9.2.101
- * e740a8e - 2018-02-08: Fixed unicode issues with srDateTime class when using locales
- * c94ad9f - 2018-02-07: Fixed issues with backlog and proper searches
- * f50e104 - 2018-02-07: Fixed issue with next airdate and main show page
- * c912546 - 2018-02-07: Fixed issue with next airdate and main show page
- * e9494a7 - 2018-02-07: Fixed issue with next airdate and main show page
- * 563c4a0 - 2018-02-07: Added search delay feature to edit show options, allows setting a delay in days to delay daily searches for specific shows that may not release same day on provider sites
- * ca75c7e - 2018-02-06: refactored srDateTime class added locale support for displaying date and time
- * 2c89486 - 2018-02-06: Fixed issue #175 - autocomplete "Enter the folder containing the episode " field when manually post-processing using Post-Processing Dir from settings
- * b608667 - 2018-02-04: Fixed issue #176 - autocomplete colors when using dark theme hard to see
- * e73b9af - 2018-02-04: Fixed issue #176 - autocomplete colors when using dark theme hard to see
- * e77ea77 - 2018-02-04: Fixed issue #176 - autocomplete colors when using dark theme hard to see
- * 372b59e - 2018-02-04: Fixed issue #177 - case-insensitive sorting for drop-down show list
- * c5bee33 - 2018-01-31: Release v9.2.100
- * c91e143 - 2018-01-28: Fixed issue #170 - double popup on hover over network in show list
- * 2155ce3 - 2018-01-26: Updated usenet-crawler url to new API url
- * d1b497f - 2018-01-25: Fixed Error loading IMDb info: 'NoneType' object is not iterable
......
......@@ -2434,49 +2434,6 @@ input sizing (for config pages)
margin-top: -4px;
}
/* =======================================================================
browser.css overrides
========================================================================== */
#fileBrowserDialog {
overflow-y: auto;
}
#fileBrowserDialog ul li a:hover {
color: #00f;
background: none;
}
#fileBrowserDialog h2 {
font-size: 20px;
}
.ui-autocomplete {
max-height: 180px;
overflow-x: hidden;
overflow-y: auto;
}
/* IE6 hack since it doesn't support max-height */
* html .ui-autocomplete {
height: 180px;
padding-right: 20px;
}
.ui-autocomplete .ui-menu-item .ui-state-focus {
color: #fff;
background: none;
background-color: #0a246a;
}
/* restore 1.8.x resize handle on dialog button pane */
.ui-dialog .ui-resizable-se {
width: 14px;
height: 14px;
right: 3px;
bottom: 3px;
background-position: -80px -224px;
}
/* =======================================================================
jquery-steps.css
========================================================================== */
......@@ -2938,34 +2895,13 @@ File Browser
float: left;
}
/* jQuery-UI autocomplete overrides to make it look more like the old autocomplete */
.ui-autocomplete {
max-height: 180px;
overflow-y: auto;
/* prevent horizontal scrollbar */
overflow-x: hidden;
/* add padding to account for vertical scrollbar */
padding-right: 20px;
}
* html .ui-autocomplete {
height: 180px;
}
.ui-menu .ui-menu-item {
background-color: #eeeeee;
}
.ui-menu .ui-menu-item-alternate {
background-color: #ffffff;
}
.ui-menu a.ui-state-hover {
background: none;
background-color: #0A246A;
color: #ffffff;
}
/* =======================================================================
Country Flags
========================================================================== */
......
......@@ -400,7 +400,7 @@ jQuery(document).ready(function ($) {
});
});
if (SICKRAGE.metaToBool('sickrage.VIEW_CHANGELOG')){
if (SICKRAGE.metaToBool('sickrage.VIEW_CHANGELOG')) {
$("#changelog").click();
}
......@@ -837,7 +837,7 @@ jQuery(document).ready(function ($) {
SICKRAGE.browser.fileBrowserDialog.dialog('option', 'buttons', [
{
text: gt("Ok"),
"class": "btn",
class: "btn",
click: function () {
// store the browsed path to the associated text field
callback(SICKRAGE.browser.currentBrowserPath, options);
......@@ -846,7 +846,7 @@ jQuery(document).ready(function ($) {
},
{
text: gt("Cancel"),
"class": "btn",
class: "btn",
click: function () {
$(this).dialog("close");
}
......@@ -892,10 +892,8 @@ jQuery(document).ready(function ($) {
},
open: function () {
$(".ui-autocomplete li.ui-menu-item a").removeClass("ui-corner-all");
$(".ui-autocomplete li.ui-menu-item:odd a").addClass("ui-menu-item-alternate");
}
})
.data("ui-autocomplete")._renderItem = function (ul, item) {
}).data("ui-autocomplete")._renderItem = function (ul, item) {
//highlight the matched search term from the item -- note that this is global and will match anywhere
var result_item = item.label;
var x = new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + query + ")(?![^<>]*>)(?![^&;]+;)", "gi");
......
......@@ -23,7 +23,7 @@ import os
import sickrage
from sickrage.core.databases import srDatabase
from sickrage.core.databases.main.index import MainTVShowsIndex, MainTVEpisodesIndex, MainIMDBInfoIndex, \
MainXEMRefreshIndex, MainSceneNumberingIndex, MainIndexerMappingIndex, MainHistoryIndex, MainInfoIndex, \
MainXEMRefreshIndex, MainSceneNumberingIndex, MainIndexerMappingIndex, MainHistoryIndex, \
MainBlacklistIndex, MainWhitelistIndex
......@@ -35,7 +35,6 @@ class MainDB(srDatabase):
'xem_refresh': MainXEMRefreshIndex,
'scene_numbering': MainSceneNumberingIndex,
'indexer_mapping': MainIndexerMappingIndex,
'info': MainInfoIndex,
'blacklist': MainBlacklistIndex,
'whitelist': MainWhitelistIndex,
'history': MainHistoryIndex,
......@@ -54,7 +53,6 @@ class MainDB(srDatabase):
'history': ['action', 'date', 'showid', 'season', 'episode', 'quality', 'resource', 'provider', 'version'],
'imdb_info': ['indexer_id', 'imdb_id', 'title', 'year', 'akas', 'runtimes', 'genres', 'countries',
'country_codes', 'certificates', 'rating', 'votes', 'last_update'],
'info': ['last_backlog', 'last_indexer', 'last_proper_search'],
'scene_numbering': ['indexer', 'indexer_id', 'season', 'episode', 'scene_season', 'scene_episode',
'absolute_number', 'scene_absolute_number'],
'blacklist': ['show_id', 'range', 'keyword'],
......
......@@ -126,21 +126,6 @@ class MainHistoryIndex(HashIndex):
return data.get('showid'), None
class MainInfoIndex(HashIndex):
_version = 1
def __init__(self, *args, **kwargs):
kwargs['key_format'] = 'I'
super(MainInfoIndex, self).__init__(*args, **kwargs)
def make_key(self, key):
return key
def make_key_value(self, data):
if data.get('_t') == 'info' and data.get('last_indexer'):
return data.get('last_indexer'), None
class MainBlacklistIndex(HashIndex):
_version = 1
......
......@@ -19,11 +19,10 @@
from __future__ import unicode_literals
import functools
import locale
from datetime import datetime
import sickrage
from sickrage.core.helpers import encoding
date_presets = (
'%Y-%m-%d',
......@@ -91,191 +90,142 @@ date_presets = (
time_presets = ('%I:%M:%S %p', '%H:%M:%S')
# helper class
class static_or_instance(object):
def __init__(self, func):
self.func = func
class srDateTime(object):
def __init__(self, dt, convert=False):
self.dt = dt
if convert and sickrage.app.config.timezone_display == 'local':
self.dt = dt.astimezone(sickrage.app.tz)
def __get__(self, instance, owner):
return functools.partial(self.func, instance)
# subclass datetime to add function to display custom date and time formats
class srDateTime(datetime):
has_locale = True
en_US_norm = locale.normalize('en_US.utf-8')
@static_or_instance
def convert_to_setting(self, dt=None):
try:
if sickrage.app.config.timezone_display == 'local':
return dt.astimezone(sickrage.app.tz) if self is None else self.astimezone(sickrage.app.tz)
else:
return dt if self is None else self
except Exception:
return dt if self is None else self
self.has_locale = True
self.en_US_norm = locale.normalize('en_US.utf-8')
# display Time in SickRage Format
@static_or_instance
def srftime(self, dt=None, show_seconds=False, t_preset=None):
def srftime(self, show_seconds=False, t_preset=None):
"""
Display time in SR format
TODO: Rename this to srftime
:param dt: datetime object
:param show_seconds: Boolean, show seconds
:param t_preset: Preset time format
:return: time string
"""
strt = ''
try:
locale.setlocale(locale.LC_TIME, '')
except Exception:
pass
try:
if srDateTime.has_locale:
locale.setlocale(locale.LC_TIME, 'en_US')
if self.has_locale:
locale.setlocale(locale.LC_TIME, locale.normalize(sickrage.app.config.gui_lang))
except Exception:
try:
if srDateTime.has_locale:
locale.setlocale(locale.LC_TIME, srDateTime.en_US_norm)
if self.has_locale:
locale.setlocale(locale.LC_TIME, self.en_US_norm)
except Exception:
srDateTime.has_locale = False
self.has_locale = False
strt = ''
try:
if self is None:
if dt is not None:
if t_preset is not None:
strt = dt.strftime(t_preset)
elif show_seconds:
strt = dt.strftime(sickrage.app.config.time_preset_w_seconds)
else:
strt = dt.strftime(sickrage.app.config.time_preset)
if t_preset is not None:
strt = self.dt.strftime(t_preset)
elif show_seconds:
strt = self.dt.strftime(sickrage.app.config.time_preset_w_seconds)
else:
if t_preset is not None:
strt = self.strftime(t_preset)
elif show_seconds:
strt = self.strftime(sickrage.app.config.time_preset_w_seconds)
else:
strt = self.strftime(sickrage.app.config.time_preset)
strt = self.dt.strftime(sickrage.app.config.time_preset)
finally:
try:
if srDateTime.has_locale:
if self.has_locale:
locale.setlocale(locale.LC_TIME, '')
except Exception:
srDateTime.has_locale = False
self.has_locale = False
return strt
return encoding.to_unicode(strt)
# display Date in SickRage Format
@static_or_instance
def srfdate(self, dt=None, d_preset=None):
def srfdate(self, d_preset=None):
"""
Display date in SR format
TODO: Rename this to srfdate
:param dt: datetime object
:param d_preset: Preset date format
:return: date string
"""
strd = ''
try:
locale.setlocale(locale.LC_TIME, '')
except Exception:
pass
strd = ''
try:
if self is None:
if dt is not None:
if d_preset is not None:
strd = dt.strftime(d_preset)
else:
strd = dt.strftime(sickrage.app.config.date_preset)
if self.has_locale:
locale.setlocale(locale.LC_TIME, locale.normalize(sickrage.app.config.gui_lang))
except Exception:
try:
if self.has_locale:
locale.setlocale(locale.LC_TIME, self.en_US_norm)
except Exception:
self.has_locale = False
try:
if d_preset is not None:
strd = self.dt.strftime(d_preset)
else:
if d_preset is not None:
strd = self.strftime(d_preset)
else:
strd = self.strftime(sickrage.app.config.date_preset)
strd = self.dt.strftime(sickrage.app.config.date_preset)
finally:
try:
locale.setlocale(locale.LC_TIME, '')
except Exception:
pass
return strd
return encoding.to_unicode(strd)
# display Datetime in SickRage Format
@static_or_instance
def srfdatetime(self, dt=None, show_seconds=False, d_preset=None, t_preset=None):
def srfdatetime(self, show_seconds=False, d_preset=None, t_preset=None):
"""
Show datetime in SR format
TODO: Rename this to srfdatetime
:param dt: datetime object
:param show_seconds: Boolean, show seconds as well
:param d_preset: Preset date format
:param t_preset: Preset time format
:return: datetime string
"""
strd = ''
try:
locale.setlocale(locale.LC_TIME, '')
except Exception:
pass
strd = ''
try:
if self is None:
if dt is not None:
if d_preset is not None:
strd = dt.strftime(d_preset)
else:
strd = dt.strftime(sickrage.app.config.date_preset)
try:
if srDateTime.has_locale:
locale.setlocale(locale.LC_TIME, 'en_US')
except Exception:
try:
if srDateTime.has_locale:
locale.setlocale(locale.LC_TIME, srDateTime.en_US_norm)
except Exception:
srDateTime.has_locale = False
if t_preset is not None:
strd += ', {}'.format(dt.strftime(t_preset))
elif show_seconds:
strd += ', {}'.format(dt.strftime(sickrage.app.config.time_preset_w_seconds))
else:
strd += ', {}'.format(dt.strftime(sickrage.app.config.time_preset))
if d_preset is not None:
strd = self.dt.strftime(d_preset)
else:
if d_preset is not None:
strd = self.strftime(d_preset)
else:
strd = self.strftime(sickrage.app.config.date_preset)
strd = self.dt.strftime(sickrage.app.config.date_preset)
try:
if self.has_locale:
locale.setlocale(locale.LC_TIME, locale.normalize(sickrage.app.config.gui_lang))
except Exception:
try:
if srDateTime.has_locale:
locale.setlocale(locale.LC_TIME, 'en_US')
if self.has_locale:
locale.setlocale(locale.LC_TIME, self.en_US_norm)
except Exception:
try:
if srDateTime.has_locale:
locale.setlocale(locale.LC_TIME, srDateTime.en_US_norm)
except Exception:
srDateTime.has_locale = False
if t_preset is not None:
strd += ', {}'.format(dt.strftime(t_preset))
elif show_seconds:
strd += ', {}'.format(dt.strftime(sickrage.app.config.time_preset_w_seconds))
else:
strd += ', {}'.format(dt.strftime(sickrage.app.config.time_preset))
self.has_locale = False
if t_preset is not None:
strd += ', {}'.format(self.dt.strftime(t_preset))
elif show_seconds:
strd += ', {}'.format(self.dt.strftime(sickrage.app.config.time_preset_w_seconds))
else:
strd += ', {}'.format(self.dt.strftime(sickrage.app.config.time_preset))
finally:
try:
if srDateTime.has_locale:
if self.has_locale:
locale.setlocale(locale.LC_TIME, '')
except Exception:
srDateTime.has_locale = False
self.has_locale = False
return strd
return encoding.to_unicode(strd)
......@@ -31,11 +31,12 @@ class BacklogSearcher(object):
def __init__(self, *args, **kwargs):
self.name = "BACKLOG"
self.lock = threading.Lock()
self._lastBacklog = None
self._last_backlog_search = None
self.cycleTime = 21 / 60 / 24
self.amActive = False
self.amPaused = False
self.amWaiting = False
self.forced = False
self._resetPI()
def run(self, force=False):
......@@ -49,19 +50,16 @@ class BacklogSearcher(object):
self.cycleTime = sickrage.app.config.backlog_searcher_freq / 60 / 24
try:
self.forced = force
self.searchBacklog()
finally:
self.amActive = False
def forceSearch(self):
self._set_lastBacklog(1)
self.lastRun = datetime.datetime.fromordinal(1)
def nextRun(self):
if self._lastBacklog <= 1:
if self._last_backlog_search <= 1:
return datetime.date.today()
else:
return datetime.date.fromordinal(self._lastBacklog + self.cycleTime)
return datetime.date.fromordinal(self._last_backlog_search + self.cycleTime)
def _resetPI(self):
self.percentDone = 0
......@@ -78,7 +76,6 @@ class BacklogSearcher(object):
return (not self.amWaiting) and self.amActive
def searchBacklog(self, which_shows=None):
if self.amActive:
sickrage.app.log.debug("Backlog is still running, not starting it again")
return
......@@ -90,61 +87,43 @@ class BacklogSearcher(object):
if which_shows:
show_list = which_shows
self._lastBacklog = self._get_lastBacklog()
curDate = datetime.date.today().toordinal()
fromDate = datetime.date.fromordinal(1)
if not which_shows and not ((curDate - self._lastBacklog) >= self.cycleTime):
sickrage.app.log.info(
"Running limited backlog on missed episodes " + str(
sickrage.app.config.backlog_days) + " day(s) and older only")
fromDate = datetime.date.today() - datetime.timedelta(days=sickrage.app.config.backlog_days)
# go through non air-by-date shows and see if they need any episodes
for curShow in show_list:
if curShow.paused:
sickrage.app.log.debug("Skipping backlog for {} because the show is paused".format(curShow.name))
continue
self._last_backlog_search = self._get_lastBacklogSearch(curShow.indexerid)
if not which_shows and self.forced:
sickrage.app.log.info("Running limited backlog on missed episodes " + str(
sickrage.app.config.backlog_days) + " day(s) and older only")
fromDate = datetime.date.today() - datetime.timedelta(days=sickrage.app.config.backlog_days)
else:
sickrage.app.log.info('Running full backlog search on missed episodes for selected shows')
segments = self._get_segments(curShow, fromDate)
for season, segment in segments.items():
self.currentSearchInfo = {'title': curShow.name + " Season " + str(season)}
sickrage.app.search_queue.put(BacklogQueueItem(curShow, segment)) # @UndefinedVariable
else:
sickrage.app.search_queue.put(BacklogQueueItem(curShow, segment))
if not segments:
sickrage.app.log.debug(
"Nothing needs to be downloaded for {show_name}, skipping".format(show_name=curShow.name))
"Nothing needs to be downloaded for {}, skipping".format(curShow.name))
# don't consider this an actual backlog search if we only did recent eps
# or if we only did certain shows
if fromDate == datetime.date.fromordinal(1) and not which_shows:
self._set_lastBacklog(curDate)
# don't consider this an actual backlog search if we only did recent eps
# or if we only did certain shows
if fromDate == datetime.date.fromordinal(1) and not which_shows:
self._set_lastBacklogSearch(curShow.indexerid, curDate)
self.amActive = False
self._resetPI()
def _get_lastBacklog(self):
sickrage.app.log.debug("Retrieving the last check time from the DB")
dbData = [x for x in sickrage.app.main_db.all('info')]
if len(dbData) == 0:
lastBacklog = 1
elif dbData[0]["last_backlog"] is None or dbData[0]["last_backlog"] == "":
lastBacklog = 1
else:
lastBacklog = int(dbData[0]["last_backlog"])
if lastBacklog > datetime.date.today().toordinal():
lastBacklog = 1
return lastBacklog
def _get_segments(self, show, fromDate):
if show.paused:
sickrage.app.log.debug(
"Skipping backlog for {show_name} because the show is paused".format(show_name=show.name))
return {}
anyQualities, bestQualities = Quality.splitQuality(show.quality)
sickrage.app.log.debug("Seeing if we need anything from {}".format(show.name))
......@@ -177,20 +156,21 @@ class BacklogSearcher(object):
return wanted