Merge branch 'feature/ChangeRenameEpisodes' into dev

This commit is contained in:
JackDandy 2023-05-03 00:38:25 +01:00
commit 7acddd88b2
9 changed files with 129 additions and 57 deletions

View file

@ -16,6 +16,7 @@
* Change remove redundant py2 import futures
* Change add jobs to centralise scheduler activities
* Change refactor scene_exceptions
* Add to config/media-process/File Handling, "Rename TBA" and "Rename any"
* Add config to change media process log message if there is no media to process
* Change view-show text "invalid timeformat" to "time unknown"

View file

@ -242,10 +242,30 @@
<div class="field-pair">
<label for="rename_episodes">
<span class="component-title">Rename episodes</span>
<span class="component-title">Rename episode files</span>
<span class="component-desc">
<input type="checkbox" name="rename_episodes" id="rename_episodes" #if $sickgear.RENAME_EPISODES == True then $checked else ''#>
<p>rename episodes using the Episode Naming settings</p>
<p>using the Episode Naming settings</p>
</span>
</label>
</div>
<div class="field-pair">
<label for="rename_tba_episodes">
<span class="component-title">Rename "TBA" named files</span>
<span class="component-desc">
<input type="checkbox" name="rename_tba_episodes" id="rename_tba_episodes" #if $sickgear.RENAME_TBA_EPISODES == True then $checked else ''#>
<p>if the TV info episode name changes</p>
</span>
</label>
</div>
<div class="field-pair">
<label for="rename_name_changed_episodes">
<span class="component-title">Rename any named file</span>
<span class="component-desc">
<input type="checkbox" name="rename_name_changed_episodes" id="rename_name_changed_episodes" #if $sickgear.RENAME_NAME_CHANGED_EPISODES == True then $checked else ''#>
<p>if the TV info episode name changes</p>
</span>
</label>
</div>

View file

@ -284,6 +284,8 @@ ADD_SHOWS_METALANG = 'en'
CREATE_MISSING_SHOW_DIRS = False
SHOW_DIRS_WITH_DOTS = False
RENAME_EPISODES = False
RENAME_TBA_EPISODES = True
RENAME_NAME_CHANGED_EPISODES = False
AIRDATE_EPISODES = False
PROCESS_AUTOMATICALLY = False
KEEP_PROCESSED_DIR = False
@ -717,8 +719,9 @@ def init_stage_1(console_logging):
global TV_DOWNLOAD_DIR, PROCESS_METHOD, PROCESS_AUTOMATICALLY, MEDIAPROCESS_INTERVAL, \
POSTPONE_IF_SYNC_FILES, PROCESS_POSITIVE_LOG, EXTRA_SCRIPTS, SG_EXTRA_SCRIPTS, \
DEFAULT_MEDIAPROCESS_INTERVAL, MIN_MEDIAPROCESS_INTERVAL, \
UNPACK, SKIP_REMOVED_FILES, MOVE_ASSOCIATED_FILES, NFO_RENAME, RENAME_EPISODES, AIRDATE_EPISODES, \
USE_FAILED_DOWNLOADS, DELETE_FAILED
UNPACK, SKIP_REMOVED_FILES, MOVE_ASSOCIATED_FILES, NFO_RENAME, \
RENAME_EPISODES, RENAME_TBA_EPISODES, RENAME_NAME_CHANGED_EPISODES, \
AIRDATE_EPISODES, USE_FAILED_DOWNLOADS, DELETE_FAILED
# Media Process/Episode Naming
global NAMING_PATTERN, NAMING_MULTI_EP, NAMING_STRIP_YEAR, NAMING_CUSTOM_ABD, NAMING_ABD_PATTERN, \
NAMING_CUSTOM_SPORTS, NAMING_SPORTS_PATTERN, \
@ -1013,6 +1016,8 @@ def init_stage_1(console_logging):
PROCESS_AUTOMATICALLY = bool(check_setting_int(CFG, 'General', 'process_automatically', 0))
UNPACK = bool(check_setting_int(CFG, 'General', 'unpack', 0))
RENAME_EPISODES = bool(check_setting_int(CFG, 'General', 'rename_episodes', 1))
RENAME_TBA_EPISODES = bool(check_setting_int(CFG, 'General', 'rename_tba_episodes', 1))
RENAME_NAME_CHANGED_EPISODES = bool(check_setting_int(CFG, 'General', 'rename_name_changed_episodes', 0))
AIRDATE_EPISODES = bool(check_setting_int(CFG, 'General', 'airdate_episodes', 0))
KEEP_PROCESSED_DIR = bool(check_setting_int(CFG, 'General', 'keep_processed_dir', 1))
PROCESS_METHOD = check_setting_str(CFG, 'General', 'process_method', 'copy' if KEEP_PROCESSED_DIR else 'move')
@ -1994,6 +1999,8 @@ def save_config():
new_config['General']['process_automatically'] = int(PROCESS_AUTOMATICALLY)
new_config['General']['unpack'] = int(UNPACK)
new_config['General']['rename_episodes'] = int(RENAME_EPISODES)
new_config['General']['rename_tba_episodes'] = int(RENAME_TBA_EPISODES)
new_config['General']['rename_name_changed_episodes'] = int(RENAME_NAME_CHANGED_EPISODES)
new_config['General']['airdate_episodes'] = int(AIRDATE_EPISODES)
new_config['General']['create_missing_show_dirs'] = int(CREATE_MISSING_SHOW_DIRS)
new_config['General']['show_dirs_with_dots'] = int(SHOW_DIRS_WITH_DOTS)

View file

@ -422,7 +422,7 @@ def move_and_symlink_file(src_file, dest_file):
copy_file(src_file, dest_file)
def rename_ep_file(cur_path, new_path, old_path_length=0):
def rename_ep_file(cur_path, new_path, old_path_length=0, use_rename=False):
"""
Creates all folders needed to move a file to its new location, renames it, then cleans up any folders
left that are now empty.
@ -433,6 +433,7 @@ def rename_ep_file(cur_path, new_path, old_path_length=0):
:type new_path: AnyStr
:param old_path_length: The length of media file path (old name) WITHOUT THE EXTENSION
:type old_path_length: int or long
:param use_rename: use rename instead of shutil.move
:return: success
:rtype: bool
"""
@ -466,8 +467,11 @@ def rename_ep_file(cur_path, new_path, old_path_length=0):
# move the file
try:
logger.log(f'Renaming file from {cur_path} to {new_path}')
shutil.move(cur_path, new_path)
except (OSError, IOError) as e:
if use_rename:
os.rename(cur_path, new_path)
else:
shutil.move(cur_path, new_path)
except (OSError, IOError, IsADirectoryError, NotADirectoryError, FileExistsError) as e:
logger.error(f'Failed renaming {cur_path} to {new_path}: {ex(e)}')
return False

View file

@ -132,7 +132,7 @@ def notify_git_update(new_version=''):
n.notify_git_update(new_version)
def notify_update_library(ep_obj, flush_q=False):
def notify_update_library(ep_obj, flush_q=False, include_online=True):
if not flush_q or sickgear.QUEUE_UPDATE_LIBRARY:
@ -160,7 +160,8 @@ def notify_update_library(ep_obj, flush_q=False):
elif not flush_q:
n.update_library(show_obj=ep_obj.show_obj, show_name=ep_obj.show_obj.name, ep_obj=ep_obj)
n.update_library(show_obj=ep_obj.show_obj, show_name=ep_obj.show_obj.name, ep_obj=ep_obj,
include_online=include_online)
if flush_q:
sickgear.QUEUE_UPDATE_LIBRARY = []

View file

@ -91,9 +91,13 @@ class BaseNotifier(object):
def notify_git_update(self, *args, **kwargs):
pass
def update_library(self, **kwargs):
def update_library(self, include_online=True, **kwargs):
"""
note: nmj_notifier fires its library update when the notify_download is issued (inside notifiers)
:param include_online: Set False if to exclude derived notifiers from library updates.
For example, Trakt doesn't need to be updated when renaming the episode name part of a filename,
therefore, this is set False for that specific notification use case update.
"""
pass

View file

@ -41,9 +41,10 @@ class TraktNotifier(BaseNotifier):
return True
return False
def update_library(self, ep_obj=None, **kwargs):
def update_library(self, ep_obj=None, include_online=True, **kwargs):
self._update_collection(ep_obj)
if include_online:
self._update_collection(ep_obj)
def _update_collection(self, ep_obj):
"""

View file

@ -78,6 +78,10 @@ if coreid_warnings:
tz_p = du_parser()
invalid_date_limit = datetime.date(1900, 1, 1)
tba_tvinfo_name = re.compile(r'^(episode \d+|tb[ad])$', flags=re.I)
tba_file_name = re.compile(r'\b(episode.\d+|tb[ad])\b', flags=re.I)
pattern_ep_name = re.compile(r'%E[._]?N', flags=re.I)
# status codes for switching tv show source
TVSWITCH_DUPLICATE_SHOW = 0
TVSWITCH_ID_CONFLICT = 1
@ -4301,6 +4305,8 @@ class TVEpisode(TVEpisodeBase):
if self._name != self.dict_prevent_nonetype(ep_info, 'episodename'):
switch_list.append(self.show_obj.switch_ep_change_sql(old_tvid, old_prodid, episode, season,
TVSWITCH_EP_RENAMED))
old_name = self._name or ''
self.name = self.dict_prevent_nonetype(ep_info, 'episodename')
self.season = season
self.episode = episode
@ -4462,6 +4468,22 @@ class TVEpisode(TVEpisodeBase):
self.status = Quality.status_from_name_or_file(self._location, anime=self._show_obj.is_anime)
logger.debug('%s%s' % (msg, statusStrings[self._status]))
if sickgear.RENAME_EPISODES and self.with_ep_name() \
and os.path.splitext(ep_filename := os.path.basename(self._location or ''))[0] != \
os.path.basename(self.proper_path()) \
and (sickgear.RENAME_NAME_CHANGED_EPISODES
or (sickgear.RENAME_TBA_EPISODES
and (bool(tba_tvinfo_name.search(old_name))
or (not bool(tba_tvinfo_name.search(self._name or ''))
and bool(tba_file_name.search(ep_filename or ''))))
)) \
and os.path.isfile(self._location):
# noinspection PySimplifyBooleanCheck
if re_res := self.rename():
notifiers.notify_update_library(self, include_online=False)
elif False == re_res:
logger.debug('Failed to rename files to TV info episode name')
# shouldn't get here probably
else:
msg = '(2) Status changes from %s to ' % statusStrings[self._status]
@ -5121,17 +5143,8 @@ class TVEpisode(TVEpisodeBase):
"""
Just the folder name of the episode
"""
if None is pattern:
# we only use ABD if it's enabled, this is an ABD show, AND this is not a multi-ep
if self._show_obj.air_by_date and sickgear.NAMING_CUSTOM_ABD and not self.related_ep_obj:
pattern = sickgear.NAMING_ABD_PATTERN
elif self._show_obj.sports and sickgear.NAMING_CUSTOM_SPORTS and not self.related_ep_obj:
pattern = sickgear.NAMING_SPORTS_PATTERN
elif self._show_obj.anime and sickgear.NAMING_CUSTOM_ANIME:
pattern = sickgear.NAMING_ANIME_PATTERN
else:
pattern = sickgear.NAMING_PATTERN
pattern = self.naming_pattern()
# split off the dirs only, if they exist
name_groups = re.split(r'[\\/]', pattern)
@ -5145,24 +5158,35 @@ class TVEpisode(TVEpisodeBase):
"""
Just the filename of the episode, formatted based on the naming settings
"""
if None is pattern:
# we only use ABD if it's enabled, this is an ABD show, AND this is not a multi-ep
if self._show_obj.air_by_date and sickgear.NAMING_CUSTOM_ABD and not self.related_ep_obj:
pattern = sickgear.NAMING_ABD_PATTERN
elif self._show_obj.sports and sickgear.NAMING_CUSTOM_SPORTS and not self.related_ep_obj:
pattern = sickgear.NAMING_SPORTS_PATTERN
elif self._show_obj.anime and sickgear.NAMING_CUSTOM_ANIME:
pattern = sickgear.NAMING_ANIME_PATTERN
else:
pattern = sickgear.NAMING_PATTERN
# split off the dirs only, if they exist
name_groups = re.split(r'[\\/]', pattern)
name_groups = re.split(r'[\\/]', pattern or self.naming_pattern())
return self._format_pattern(name_groups[-1], multi, anime_type)
def with_ep_name(self):
# type: (...) -> bool
"""
returns if the episode naming contain the episode name
"""
return bool(pattern_ep_name.search(self.naming_pattern()))
def naming_pattern(self):
# type: (...) -> AnyStr
"""
return a naming pattern for this show
"""
# we only use ABD if it's enabled, this is an ABD show, AND this is not a multi-ep
if self._show_obj.air_by_date and sickgear.NAMING_CUSTOM_ABD and not self.related_ep_obj:
return sickgear.NAMING_ABD_PATTERN
if self._show_obj.sports and sickgear.NAMING_CUSTOM_SPORTS and not self.related_ep_obj:
return sickgear.NAMING_SPORTS_PATTERN
if self._show_obj.anime and sickgear.NAMING_CUSTOM_ANIME:
return sickgear.NAMING_ANIME_PATTERN
return sickgear.NAMING_PATTERN
def rename(self):
# type: (...) -> Optional[bool]
"""
Renames an episode file and all related files to the location and filename as specified
in the naming settings.
@ -5202,25 +5226,31 @@ class TVEpisode(TVEpisodeBase):
logger.debug('Files associated to %s: %s' % (self.location, related_files))
# move the ep file
result = helpers.rename_ep_file(self.location, absolute_proper_path, absolute_current_path_no_ext_length)
result = helpers.rename_ep_file(self.location, absolute_proper_path, absolute_current_path_no_ext_length,
use_rename=True)
any_renamed = all_renamed = result
# move related files
for cur_related_file in related_files:
renamed = helpers.rename_ep_file(cur_related_file, absolute_proper_path,
absolute_current_path_no_ext_length)
absolute_current_path_no_ext_length, use_rename=True)
any_renamed |= renamed
all_renamed &= renamed
if not renamed:
logger.error('%s: Unable to rename file %s' % (self._epid, cur_related_file))
for cur_related_sub in related_subs:
absolute_proper_subs_path = os.path.join(sickgear.SUBTITLES_DIR, self.formatted_filename())
renamed = helpers.rename_ep_file(cur_related_sub, absolute_proper_subs_path,
absolute_current_path_no_ext_length)
absolute_current_path_no_ext_length, use_rename=True)
any_renamed |= renamed
all_renamed &= renamed
if not renamed:
logger.error('%s: Unable to rename file %s' % (self._epid, cur_related_sub))
# save the ep
with self.lock:
if result:
if any_renamed:
with self.lock:
self.location = absolute_proper_path + file_ext
for ep_obj in self.related_ep_obj:
ep_obj.location = absolute_proper_path + file_ext
@ -5233,14 +5263,16 @@ class TVEpisode(TVEpisodeBase):
sql_l = []
with self.lock:
for ep_obj in [self] + self.related_ep_obj:
result = ep_obj.get_sql()
if None is not result:
sql_l.append(result)
ep_sql = ep_obj.get_sql()
if None is not ep_sql:
sql_l.append(ep_sql)
if 0 < len(sql_l):
my_db = db.DBConnection()
my_db.mass_action(sql_l)
return all_renamed
def airdate_modify_stamp(self):
"""
Make modify date and time of a file reflect the show air date and time.

View file

@ -8498,20 +8498,20 @@ class ConfigMediaProcess(Config):
t.submenu = self.config_menu('Processing')
return t.respond()
def save_post_processing(self, tv_download_dir=None, process_automatically=None, mediaprocess_interval=None,
unpack=None, keep_processed_dir=None, process_method=None,
extra_scripts='', sg_extra_scripts='',
rename_episodes=None, airdate_episodes=None,
move_associated_files=None, postpone_if_sync_files=None, process_positive_log=None,
naming_custom_abd=None, naming_custom_sports=None, naming_custom_anime=None,
naming_strip_year=None, use_failed_downloads=None, delete_failed=None,
skip_removed_files=None, nfo_rename=None,
xbmc_data=None, xbmc_12plus_data=None, mediabrowser_data=None, sony_ps3_data=None,
wdtv_data=None, tivo_data=None, mede8er_data=None, kodi_data=None,
naming_pattern=None, naming_multi_ep=None,
naming_anime=None, naming_anime_pattern=None, naming_anime_multi_ep=None,
naming_abd_pattern=None, naming_sports_pattern=None,
**kwargs): # kwargs picks up deprecated vars sent from legacy UIs
def save_post_processing(
self, tv_download_dir=None, process_method=None, process_automatically=None, mediaprocess_interval=None,
postpone_if_sync_files=None, process_positive_log=None, extra_scripts='', sg_extra_scripts='',
unpack=None, skip_removed_files=None, move_associated_files=None, nfo_rename=None,
rename_episodes=None, rename_tba_episodes=None, rename_name_changed_episodes=None,
airdate_episodes=None, use_failed_downloads=None, delete_failed=None,
naming_pattern=None, naming_multi_ep=None, naming_strip_year=None,
naming_custom_abd=None, naming_abd_pattern=None,
naming_custom_sports=None, naming_sports_pattern=None,
naming_custom_anime=None, naming_anime_pattern=None,naming_anime_multi_ep=None, naming_anime=None,
kodi_data=None, mede8er_data=None, xbmc_data=None, mediabrowser_data=None,
sony_ps3_data=None, tivo_data=None, wdtv_data=None, xbmc_12plus_data=None,
keep_processed_dir=None,
**kwargs): # kwargs picks up deprecated vars sent from legacy UIs
results = []
@ -8536,6 +8536,8 @@ class ConfigMediaProcess(Config):
sickgear.EXTRA_SCRIPTS = [x.strip() for x in extra_scripts.split('|') if x.strip()]
sickgear.SG_EXTRA_SCRIPTS = [x.strip() for x in sg_extra_scripts.split('|') if x.strip()]
sickgear.RENAME_EPISODES = config.checkbox_to_value(rename_episodes)
sickgear.RENAME_TBA_EPISODES = config.checkbox_to_value(rename_tba_episodes)
sickgear.RENAME_NAME_CHANGED_EPISODES = config.checkbox_to_value(rename_name_changed_episodes)
sickgear.AIRDATE_EPISODES = config.checkbox_to_value(airdate_episodes)
sickgear.MOVE_ASSOCIATED_FILES = config.checkbox_to_value(move_associated_files)
sickgear.POSTPONE_IF_SYNC_FILES = config.checkbox_to_value(postpone_if_sync_files)