ignore .DS_Store and other OS junk files when detecting empty folders

This commit is contained in:
Justin Oro
2026-08-26 11:20:13 -07:00
parent f0af397544
commit 1a3df93a11
+47 -8
View File
@@ -45,6 +45,34 @@ VIDEO_EXTENSIONS = {
SUBTITLE_EXTENSIONS = {"srt", "sub"} SUBTITLE_EXTENSIONS = {"srt", "sub"}
SUBTITLE_FOLDER_NAMES = {"subs", "subtitles"} SUBTITLE_FOLDER_NAMES = {"subs", "subtitles"}
IGNORABLE_JUNK_FILENAMES = {".ds_store", "thumbs.db", "desktop.ini", ".localized"}
def is_ignorable_junk_file(path):
name = path.name.lower()
return name in IGNORABLE_JUNK_FILENAMES or name.startswith("._")
def purge_ignorable_junk(folder):
try:
entries = list(folder.iterdir())
except OSError:
return
for entry in entries:
if entry.is_file() and is_ignorable_junk_file(entry):
try:
entry.unlink()
except OSError:
pass
def is_effectively_empty(folder):
try:
entries = list(folder.iterdir())
except OSError:
return False
return all(entry.is_file() and is_ignorable_junk_file(entry) for entry in entries)
SEASON_EP_PATTERNS = [ SEASON_EP_PATTERNS = [
(re.compile(r'[Ss](\d{1,2})[Ee](\d{1,2})'), 'E'), (re.compile(r'[Ss](\d{1,2})[Ee](\d{1,2})'), 'E'),
(re.compile(r'[Ss](\d{1,2})[Xx](\d{1,2})'), 'X'), (re.compile(r'[Ss](\d{1,2})[Xx](\d{1,2})'), 'X'),
@@ -1595,7 +1623,8 @@ def rename_consolidated_subtitles(subtitles, new_video_path, log):
for source_dir in source_dirs: for source_dir in source_dirs:
try: try:
if source_dir.exists() and not any(source_dir.iterdir()): if source_dir.exists() and is_effectively_empty(source_dir):
purge_ignorable_junk(source_dir)
source_dir.rmdir() source_dir.rmdir()
print(f"Removed empty folder: {source_dir}") print(f"Removed empty folder: {source_dir}")
except OSError: except OSError:
@@ -1881,15 +1910,18 @@ def merge_orphaned_subtitle_packs(container_folder, target_folder, season, log):
try: try:
for leftover in sorted(pack.rglob("*"), reverse=True): for leftover in sorted(pack.rglob("*"), reverse=True):
if leftover.is_dir() and not any(leftover.iterdir()): if leftover.is_dir() and is_effectively_empty(leftover):
purge_ignorable_junk(leftover)
leftover.rmdir() leftover.rmdir()
if pack.exists() and not any(pack.iterdir()): if pack.exists() and is_effectively_empty(pack):
purge_ignorable_junk(pack)
pack.rmdir() pack.rmdir()
except OSError: except OSError:
pass pass
try: try:
if subs_folder.exists() and not any(subs_folder.iterdir()): if subs_folder.exists() and is_effectively_empty(subs_folder):
purge_ignorable_junk(subs_folder)
subs_folder.rmdir() subs_folder.rmdir()
print(f"Removed empty folder: {subs_folder}") print(f"Removed empty folder: {subs_folder}")
except OSError: except OSError:
@@ -1968,7 +2000,8 @@ def merge_duplicate_show_folder(source_folder, target_folder, raw_name, final_na
moved += merge_orphaned_subtitle_packs(sub, target_folder, season, log) moved += merge_orphaned_subtitle_packs(sub, target_folder, season, log)
try: try:
if sub.exists() and not any(sub.iterdir()): if sub.exists() and is_effectively_empty(sub):
purge_ignorable_junk(sub)
sub.rmdir() sub.rmdir()
except OSError: except OSError:
pass pass
@@ -2014,7 +2047,8 @@ def merge_duplicate_show_folder(source_folder, target_folder, raw_name, final_na
moved += merge_orphaned_subtitle_packs(source_folder, target_folder, folder_season, log) moved += merge_orphaned_subtitle_packs(source_folder, target_folder, folder_season, log)
try: try:
if source_folder.exists() and not any(source_folder.iterdir()): if source_folder.exists() and is_effectively_empty(source_folder):
purge_ignorable_junk(source_folder)
source_folder.rmdir() source_folder.rmdir()
print(f"Removed empty folder: {source_folder}") print(f"Removed empty folder: {source_folder}")
except OSError: except OSError:
@@ -2543,7 +2577,10 @@ def remove_empty_folders(share, confirm_all, dry_run=False):
if root_path.resolve() == share_path: if root_path.resolve() == share_path:
continue continue
try: try:
remaining = [c for c in root_path.iterdir() if c not in removed_paths] remaining = [
c for c in root_path.iterdir()
if c not in removed_paths and not (c.is_file() and is_ignorable_junk_file(c))
]
except OSError as e: except OSError as e:
vprint(f" Could not read {root_path}: {e}") vprint(f" Could not read {root_path}: {e}")
continue continue
@@ -2566,6 +2603,7 @@ def remove_empty_folders(share, confirm_all, dry_run=False):
continue continue
try: try:
purge_ignorable_junk(root_path)
root_path.rmdir() root_path.rmdir()
print(f"Removed empty folder: {root_path}") print(f"Removed empty folder: {root_path}")
removed_paths.add(root_path) removed_paths.add(root_path)
@@ -3383,7 +3421,8 @@ def run_restore(log_arg):
parent = src.parent parent = src.parent
try: try:
if parent.exists() and not any(parent.iterdir()): if parent.exists() and is_effectively_empty(parent):
purge_ignorable_junk(parent)
parent.rmdir() parent.rmdir()
print(f"Removed empty folder: {parent}") print(f"Removed empty folder: {parent}")
except OSError: except OSError: