mirror of
https://github.com/rommapp/romm.git
synced 2026-06-28 06:46:00 +00:00
Support compound suffix exclusions like "hash.txt" for multi-dot filenames
Agent-Logs-Url: https://github.com/rommapp/romm/sessions/d1c69638-bfa0-480e-8050-d565b234ea44 Co-authored-by: gantoine <3247106+gantoine@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
21de7e21f8
commit
55cd0cfc4f
@@ -205,6 +205,19 @@ class FSHandler:
|
||||
match = EXTENSION_REGEX.search(file_name)
|
||||
return match.group(1) if match else ""
|
||||
|
||||
def iter_file_extensions(self, file_name: str) -> list[str]:
|
||||
"""Return all right-anchored sub-extensions for a filename.
|
||||
|
||||
For "game.nds.enc.hash.txt" this yields:
|
||||
["nds.enc.hash.txt", "enc.hash.txt", "hash.txt", "txt"]
|
||||
This allows exclusion rules like "hash.txt" to match multi-dot filenames.
|
||||
"""
|
||||
ext = self.parse_file_extension(file_name)
|
||||
if not ext:
|
||||
return []
|
||||
parts = ext.split(".")
|
||||
return [".".join(parts[i:]) for i in range(len(parts))]
|
||||
|
||||
def extract_uuid_v4_from_filename(self, file_name: str) -> str:
|
||||
match = UUID_V4_REGEX.search(file_name)
|
||||
return match.group(0) if match else ""
|
||||
@@ -215,16 +228,11 @@ class FSHandler:
|
||||
excluded_files: list[str] = []
|
||||
|
||||
for file_name in files:
|
||||
# Get the compound extension (e.g. "nds.hash.txt" for "game.nds.hash.txt")
|
||||
# and the last single extension (e.g. "txt") for multi-dot filenames.
|
||||
ext = self.parse_file_extension(file_name)
|
||||
suffix = Path(file_name).suffix.lstrip(".")
|
||||
|
||||
# Exclude the file if the compound extension or the last single extension
|
||||
# is in the excluded list. Checking both handles files with multiple dots
|
||||
# (e.g. "game.nds.hash.txt" should be excluded when "txt" is excluded).
|
||||
if (ext and ext.lower() in excluded_extensions) or (
|
||||
suffix and suffix.lower() in excluded_extensions
|
||||
# Check all right-anchored sub-extensions so that rules like "hash.txt"
|
||||
# match multi-dot filenames such as "game.nds.enc.hash.txt".
|
||||
if any(
|
||||
e.lower() in excluded_extensions
|
||||
for e in self.iter_file_extensions(file_name)
|
||||
):
|
||||
excluded_files.append(file_name)
|
||||
|
||||
|
||||
@@ -456,12 +456,11 @@ class FSRomsHandler(FSHandler):
|
||||
f"{abs_fs_path}/{rom.fs_name}", recursive=True
|
||||
):
|
||||
# Check if file is excluded by extension.
|
||||
# Also check the last single extension for multi-dot filenames
|
||||
# (e.g. "game.nds.hash.txt" should be excluded when "txt" is excluded).
|
||||
ext = self.parse_file_extension(file_name)
|
||||
suffix = Path(file_name).suffix.lstrip(".")
|
||||
if (ext and ext.lower() in excluded_file_exts) or (
|
||||
suffix and suffix.lower() in excluded_file_exts
|
||||
# Check all right-anchored sub-extensions so that rules like "hash.txt"
|
||||
# match multi-dot filenames such as "game.nds.enc.hash.txt".
|
||||
if any(
|
||||
e.lower() in excluded_file_exts
|
||||
for e in self.iter_file_extensions(file_name)
|
||||
):
|
||||
continue
|
||||
|
||||
|
||||
@@ -145,6 +145,22 @@ class TestFSHandler:
|
||||
assert handler.parse_file_extension("no_extension") == ""
|
||||
assert handler.parse_file_extension("file.with.dots.txt") == "with.dots.txt"
|
||||
|
||||
def test_iter_file_extensions(self, handler: FSHandler):
|
||||
"""Test that all right-anchored sub-extensions are returned"""
|
||||
assert handler.iter_file_extensions("game.nds") == ["nds"]
|
||||
assert handler.iter_file_extensions("game.nds.hash.txt") == [
|
||||
"nds.hash.txt",
|
||||
"hash.txt",
|
||||
"txt",
|
||||
]
|
||||
assert handler.iter_file_extensions("game.nds.enc.hash.txt") == [
|
||||
"nds.enc.hash.txt",
|
||||
"enc.hash.txt",
|
||||
"hash.txt",
|
||||
"txt",
|
||||
]
|
||||
assert handler.iter_file_extensions("no_extension") == []
|
||||
|
||||
def test_exclude_single_files(self, handler: FSHandler):
|
||||
"""Test file exclusion functionality"""
|
||||
files = ["test.txt", "game.rom", "excluded.tmp", "data.json"]
|
||||
@@ -162,7 +178,7 @@ class TestFSHandler:
|
||||
assert "data.json" in result
|
||||
|
||||
def test_exclude_single_files_multi_dot(self, handler: FSHandler):
|
||||
"""Test that files with multiple dots are excluded based on their last extension"""
|
||||
"""Test that files with multiple dots are excluded by last or compound extension"""
|
||||
files = [
|
||||
"game.nds",
|
||||
"game.nds.hash.txt",
|
||||
@@ -172,17 +188,29 @@ class TestFSHandler:
|
||||
]
|
||||
|
||||
with patch("handler.filesystem.base_handler.cm.get_config") as mock_config:
|
||||
# Exclude by last single extension
|
||||
mock_config.return_value.EXCLUDED_SINGLE_EXT = ["txt"]
|
||||
mock_config.return_value.EXCLUDED_SINGLE_FILES = []
|
||||
|
||||
result = handler.exclude_single_files(files)
|
||||
|
||||
# Files with .txt as the last extension should be excluded regardless of
|
||||
# how many dots are in the filename
|
||||
assert "game.nds.hash.txt" not in result
|
||||
assert "game.nds.enc.hash.txt" not in result
|
||||
assert "readme.txt" not in result
|
||||
# Non-txt files should not be excluded
|
||||
assert "game.nds" in result
|
||||
assert "game.rom" in result
|
||||
|
||||
with patch("handler.filesystem.base_handler.cm.get_config") as mock_config:
|
||||
# Exclude by compound sub-extension "hash.txt"
|
||||
mock_config.return_value.EXCLUDED_SINGLE_EXT = ["hash.txt"]
|
||||
mock_config.return_value.EXCLUDED_SINGLE_FILES = []
|
||||
|
||||
result = handler.exclude_single_files(files)
|
||||
|
||||
assert "game.nds.hash.txt" not in result
|
||||
assert "game.nds.enc.hash.txt" not in result
|
||||
# "readme.txt" does NOT end in "hash.txt" — should remain
|
||||
assert "readme.txt" in result
|
||||
assert "game.nds" in result
|
||||
assert "game.rom" in result
|
||||
|
||||
|
||||
Reference in New Issue
Block a user