mirror of
https://github.com/SickGear/SickGear.git
synced 2024-11-15 01:15:05 +00:00
Merge branch 'feature/ChangeRenameEpisodes' into dev
This commit is contained in:
commit
7acddd88b2
9 changed files with 129 additions and 57 deletions
|
@ -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"
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 = []
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue