Change allow Python 3.12.5

Fix person ids fetching
Fix wrong added death dates (by allowing overwriting deathday with None if birthday is on source)
Fix parsing changes to IMDb bio
Update test data
- Change improve efficiency when saving config.ini
Change prevent saving unchanged config.ini
Change add flushing to config.ini file saving (configobj hack)
Change add ConfigEvents queue for saving the config ini more efficiently
Change catch other errors for saving config
- Change improve efficiency when saving viewshow glide
Change don't call '/home/set-display-show-glide' in the first place if there  is no reason (params) to do so
Change add sanity check for set_display_show_glide, only save changed values
This commit is contained in:
Prinz23 2024-06-28 02:57:31 +01:00 committed by JackDandy
parent 80cffd7cef
commit 0c0e25e73c
34 changed files with 126 additions and 16 deletions

View file

@ -1,6 +1,11 @@
### 3.32.4 (2024-06-2x xx:xx:00 UTC) ### 3.32.4 (2024-08-10 11:40:00 UTC)
* Change person cards to not display "eps" when number of eps is unknown * Change person cards to not display "eps" when number of eps is unknown
* Fix wrong added death dates
* Fix parsing changes to IMDb bio
* Change improve efficiency when saving config.ini
* Change improve efficiency when saving viewshow glide
* Change allow Python 3.12.5
### 3.32.3 (2024-06-26 18:10:00 UTC) ### 3.32.3 (2024-06-26 18:10:00 UTC)

View file

@ -4,6 +4,7 @@ Libs with customisations...
/lib/backports/configparser /lib/backports/configparser
/lib/browser_ua /lib/browser_ua
/lib/bs4 /lib/bs4
/lib/configobj/__init__.py
/lib/dateutil/zoneinfo/__init__.py /lib/dateutil/zoneinfo/__init__.py
/lib/dateutil/tz/tz.py /lib/dateutil/tz/tz.py
/lib/enzyme /lib/enzyme

View file

@ -245,7 +245,9 @@ $(document).ready(function() {
if (saveTime){ if (saveTime){
params.slidetime = $.SickGear.config.glideSlideTime; params.slidetime = $.SickGear.config.glideSlideTime;
} }
$.get($.SickGear.Root + '/home/set-display-show-glide', params); if (!$.isEmptyObject(params)){
$.get($.SickGear.Root + '/home/set-display-show-glide', params);
}
} }
} }

View file

@ -219,7 +219,7 @@ class IMDbIndexer(TVInfoBase):
if not bio: if not bio:
return return
with BS4Parser(bio) as bio_item: with BS4Parser(bio) as bio_item:
bv = bio_item.find(string='Mini Bio', recursive=True).find_next('p') bv = bio_item.find('div', attrs={'data-testid': re.compile('mini_bio$')}, recursive=True)
for a in bv.findAll('a'): for a in bv.findAll('a'):
a.replaceWithChildren() a.replaceWithChildren()
for b in bv.findAll('br'): for b in bv.findAll('br'):

View file

@ -2097,6 +2097,7 @@ class ConfigObj(Section):
else: else:
with open(self.filename, 'wb') as h: with open(self.filename, 'wb') as h:
h.write(output_bytes) h.write(output_bytes)
h.flush()
def validate(self, validator, preserve_errors=False, copy=False, def validate(self, validator, preserve_errors=False, copy=False,
section=None): section=None):

View file

@ -38,7 +38,7 @@ warnings.filterwarnings('ignore', message='.*deprecated in cryptography.*')
versions = [((3, 8, 2), (3, 8, 19)), versions = [((3, 8, 2), (3, 8, 19)),
((3, 9, 0), (3, 9, 2)), ((3, 9, 4), (3, 9, 19)), ((3, 9, 0), (3, 9, 2)), ((3, 9, 4), (3, 9, 19)),
((3, 10, 0), (3, 12, 4))] # inclusive version ranges ((3, 10, 0), (3, 12, 5))] # inclusive version ranges
if not any(list(map(lambda v: v[0] <= sys.version_info[:3] <= v[1], versions))) and not int(os.environ.get('PYT', 0)): if not any(list(map(lambda v: v[0] <= sys.version_info[:3] <= v[1], versions))) and not int(os.environ.get('PYT', 0)):
major, minor, micro = sys.version_info[:3] major, minor, micro = sys.version_info[:3]
print('Python %s.%s.%s detected.' % (major, minor, micro)) print('Python %s.%s.%s detected.' % (major, minor, micro))

View file

@ -16,6 +16,7 @@
from collections import OrderedDict from collections import OrderedDict
from threading import Lock from threading import Lock
import copy
import datetime import datetime
import io import io
import os import os
@ -39,6 +40,7 @@ from . import auto_media_process, properFinder # must come after the above impo
from .common import SD, SKIPPED, USER_AGENT from .common import SD, SKIPPED, USER_AGENT
from .config import check_section, check_setting_int, check_setting_str, ConfigMigrator, minimax from .config import check_section, check_setting_int, check_setting_str, ConfigMigrator, minimax
from .databases import cache_db, failed_db, mainDB from .databases import cache_db, failed_db, mainDB
from .event_queue import ConfigEvents
from .indexers.indexer_api import TVInfoAPI from .indexers.indexer_api import TVInfoAPI
from .indexers.indexer_config import TVINFO_IMDB, TVINFO_TVDB, TmdbIndexer from .indexers.indexer_config import TVINFO_IMDB, TVINFO_TVDB, TmdbIndexer
from .providers.generic import GenericProvider from .providers.generic import GenericProvider
@ -73,6 +75,7 @@ ENV = {}
CFG = None # type: ConfigObj CFG = None # type: ConfigObj
CONFIG_FILE = '' CONFIG_FILE = ''
CONFIG_VERSION = None CONFIG_VERSION = None
CONFIG_OLD = None
# Default encryption version (0 for None) # Default encryption version (0 for None)
ENCRYPTION_VERSION = 0 ENCRYPTION_VERSION = 0
@ -86,6 +89,7 @@ DATA_DIR = ''
# system events # system events
# noinspection PyTypeChecker # noinspection PyTypeChecker
events = None # type: Events events = None # type: Events
config_events = None # type: ConfigEvents
show_queue_scheduler = None # type: Optional[scheduler.Scheduler] show_queue_scheduler = None # type: Optional[scheduler.Scheduler]
search_queue_scheduler = None # type: Optional[scheduler.Scheduler] search_queue_scheduler = None # type: Optional[scheduler.Scheduler]
@ -667,7 +671,7 @@ def init_stage_1(console_logging):
WEB_HOST, WEB_ROOT, ACTUAL_CACHE_DIR, CACHE_DIR, ZONEINFO_DIR, ADD_SHOWS_WO_DIR, ADD_SHOWS_METALANG, \ WEB_HOST, WEB_ROOT, ACTUAL_CACHE_DIR, CACHE_DIR, ZONEINFO_DIR, ADD_SHOWS_WO_DIR, ADD_SHOWS_METALANG, \
CREATE_MISSING_SHOW_DIRS, SHOW_DIRS_WITH_DOTS, \ CREATE_MISSING_SHOW_DIRS, SHOW_DIRS_WITH_DOTS, \
RECENTSEARCH_STARTUP, NAMING_FORCE_FOLDERS, SOCKET_TIMEOUT, DEBUG, TVINFO_DEFAULT, \ RECENTSEARCH_STARTUP, NAMING_FORCE_FOLDERS, SOCKET_TIMEOUT, DEBUG, TVINFO_DEFAULT, \
CONFIG_FILE, CONFIG_VERSION, \ CONFIG_FILE, CONFIG_VERSION, CONFIG_OLD, \
REMOVE_FILENAME_CHARS, IMPORT_DEFAULT_CHECKED_SHOWS, WANTEDLIST_CACHE, MODULE_UPDATE_STRING, EXT_UPDATES REMOVE_FILENAME_CHARS, IMPORT_DEFAULT_CHECKED_SHOWS, WANTEDLIST_CACHE, MODULE_UPDATE_STRING, EXT_UPDATES
# Add Show Search # Add Show Search
global RESULTS_SORTBY global RESULTS_SORTBY
@ -1537,7 +1541,7 @@ def init_stage_2():
search_recent_scheduler, search_subtitles_scheduler, \ search_recent_scheduler, search_subtitles_scheduler, \
search_queue_scheduler, show_queue_scheduler, people_queue_scheduler, \ search_queue_scheduler, show_queue_scheduler, people_queue_scheduler, \
watched_state_queue_scheduler, emby_watched_state_scheduler, plex_watched_state_scheduler, \ watched_state_queue_scheduler, emby_watched_state_scheduler, plex_watched_state_scheduler, \
process_media_scheduler, background_mapping_task process_media_scheduler, background_mapping_task, config_events
# Gen Config/Misc # Gen Config/Misc
global SHOW_UPDATE_HOUR, UPDATE_INTERVAL, UPDATE_PACKAGES_INTERVAL global SHOW_UPDATE_HOUR, UPDATE_INTERVAL, UPDATE_PACKAGES_INTERVAL
@ -1728,6 +1732,9 @@ def init_stage_2():
except (BaseException, Exception): except (BaseException, Exception):
pass pass
config_events = ConfigEvents(_save_config)
config_events.start()
__INITIALIZED__ = True __INITIALIZED__ = True
return True return True
@ -1801,7 +1808,7 @@ def sig_handler(signum=None, _=None):
def halt(): def halt():
global __INITIALIZED__, started global __INITIALIZED__, started, config_events
logger.debug('Check INIT_LOCK on halt') logger.debug('Check INIT_LOCK on halt')
with INIT_LOCK: with INIT_LOCK:
@ -1811,6 +1818,11 @@ def halt():
logger.log('Exiting threads') logger.log('Exiting threads')
try:
config_events.stopit()
except (BaseException, Exception):
pass
for p in provider_ping_thread_pool: for p in provider_ping_thread_pool:
provider_ping_thread_pool[p].stop = True provider_ping_thread_pool[p].stop = True
@ -1854,6 +1866,11 @@ def halt():
except (BaseException, Exception) as e: except (BaseException, Exception) as e:
logger.log('Thread %s exception %s' % (thread.name, e)) logger.log('Thread %s exception %s' % (thread.name, e))
try:
config_events.join(10)
except RuntimeError:
pass
__INITIALIZED__ = False __INITIALIZED__ = False
started = False started = False
@ -1869,10 +1886,23 @@ def save_all():
# save config # save config
logger.log('Saving config file to disk') logger.log('Saving config file to disk')
save_config() _save_config(force=True)
def save_config(): def save_config(force=False):
# type: (bool) -> None
"""
add queue request for saving the config.ini
:param force: force save config even if unchanged
"""
global config_events
config_events.put(force)
def _save_config(force=False, **kwargs):
# type: (bool, ...) -> None
global CONFIG_OLD
new_config = ConfigObj() new_config = ConfigObj()
new_config.filename = CONFIG_FILE new_config.filename = CONFIG_FILE
@ -2425,6 +2455,9 @@ def save_config():
new_config['ANIME'] = {} new_config['ANIME'] = {}
new_config['ANIME']['anime_treat_as_hdtv'] = int(ANIME_TREAT_AS_HDTV) new_config['ANIME']['anime_treat_as_hdtv'] = int(ANIME_TREAT_AS_HDTV)
if not force and CONFIG_OLD == new_config and os.path.isfile(new_config.filename):
logger.debug('config.ini not dirty, not saving.')
return
from sg_helpers import copy_file from sg_helpers import copy_file
backup_config = re.sub(r'\.ini$', '.bak', CONFIG_FILE) backup_config = re.sub(r'\.ini$', '.bak', CONFIG_FILE)
from .config import check_valid_config from .config import check_valid_config
@ -2440,6 +2473,7 @@ def save_config():
for _ in range(0, 3): for _ in range(0, 3):
new_config.write() new_config.write()
if check_valid_config(CONFIG_FILE): if check_valid_config(CONFIG_FILE):
CONFIG_OLD = copy.deepcopy(new_config)
return return
logger.warning('saving config file failed, retrying...') logger.warning('saving config file failed, retrying...')
remove_file_perm(CONFIG_FILE) remove_file_perm(CONFIG_FILE)

View file

@ -2,6 +2,67 @@ import queue
import threading import threading
class SetQueue(queue.Queue):
def _init(self, maxsize):
self.queue = set()
def _put(self, item):
self.queue.add(item)
def _get(self):
return self.queue.pop()
class ConfigEvents(threading.Thread):
def __init__(self, callback):
super(ConfigEvents, self).__init__()
self.queue = SetQueue()
self.callback = callback
self.name = 'CONFIG-EVENTS'
self._stopper = threading.Event()
def put(self, etype):
# type: (bool) -> None
"""
put config save event into queue
:param etype: force save config.ini if unchanged
"""
self.queue.put(etype)
def stopit(self):
self._stopper.set()
def run(self):
while not self._stopper.is_set():
try:
# get event type
ev_type = self.queue.get(True, 5)
except queue.Empty:
continue
except(BaseException, Exception):
continue
if ev_type in (True, False, None):
if ev_type is None:
continue
from sickgear import logger
logger.debug(f'Callback {self.callback.__name__}(event type:{ev_type})')
try:
# perform callback if we got an event type
self.callback(ev_type)
# event completed
self.queue.task_done()
except queue.Empty:
pass
except (BaseException, Exception):
pass
# exiting thread
self._stopper.clear()
class Event(object): class Event(object):
def __init__(self, etype): def __init__(self, etype):
self._type = etype self._type = etype

View file

@ -583,7 +583,7 @@ class Person(Referential):
continue continue
if cur_key not in self.__dict__: if cur_key not in self.__dict__:
raise Exception('Person has no property [%s]' % cur_key) raise Exception('Person has no property [%s]' % cur_key)
if None is not cur_value: if None is not cur_value or ('deathday' == cur_key and kwargs.get('birthday')):
if 'akas' == cur_key: if 'akas' == cur_key:
cur_value.update(self.akas) cur_value.update(self.akas)
elif 'nicknames' == cur_key: elif 'nicknames' == cur_key:
@ -768,7 +768,7 @@ class Person(Referential):
self._data_failure = True self._data_failure = True
logger.warning('Error searching extra info for person: %s - %s' % (self.name, ex(e))) logger.warning('Error searching extra info for person: %s - %s' % (self.name, ex(e)))
continue continue
if None is not pd and imdb_confirmed and TVINFO_IMDB == cur_tv_src: if None is not pd and imdb_confirmed and TVINFO_IMDB == cur_tv_info_src:
rp = pd rp = pd
break break
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences

View file

@ -1053,12 +1053,18 @@ class MainHandler(WebHandler):
@staticmethod @staticmethod
def set_display_show_glide(slidetime=None, tvid_prodid=None, start_at=None): def set_display_show_glide(slidetime=None, tvid_prodid=None, start_at=None):
if tvid_prodid and start_at: dirty_config = False
if tvid_prodid and start_at and start_at != sickgear.DISPLAY_SHOW_GLIDE.get(tvid_prodid, {}).get('start_at'):
sickgear.DISPLAY_SHOW_GLIDE.setdefault(tvid_prodid, {}).update({'start_at': start_at}) sickgear.DISPLAY_SHOW_GLIDE.setdefault(tvid_prodid, {}).update({'start_at': start_at})
dirty_config = True
if slidetime: if slidetime and (
sickgear.DISPLAY_SHOW_GLIDE_SLIDETIME = sg_helpers.try_int(slidetime, 3000) int_slidetime := sg_helpers.try_int(slidetime, 3000)) != sickgear.DISPLAY_SHOW_GLIDE_SLIDETIME:
sickgear.save_config() sickgear.DISPLAY_SHOW_GLIDE_SLIDETIME = int_slidetime
dirty_config = True
if dirty_config:
sickgear.save_config()
@staticmethod @staticmethod
def set_poster_sortby(sort): def set_poster_sortby(sort):

View file

@ -1 +1 @@
2024-06-25 2024-06-28