From 7005cd0681fdf82ac35123792091c551b866fa85 Mon Sep 17 00:00:00 2001 From: Roland Date: Wed, 1 Jul 2026 19:00:56 -0600 Subject: [PATCH] Hiding .DS_Store files in the File Browser. --- wrolpi/files/lib.py | 8 ++++++++ wrolpi/files/test/test_api.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/wrolpi/files/lib.py b/wrolpi/files/lib.py index 0ce8610b..c461eb6a 100644 --- a/wrolpi/files/lib.py +++ b/wrolpi/files/lib.py @@ -175,6 +175,9 @@ def _get_recursive_directory_dict(session: Session, directory: pathlib.Path, dir if directory in directories: children = dict() for path in directory.iterdir(): + if not path.is_dir() and path.name in HIDDEN_FILES: + # Never show ignored files. + continue if path.is_dir() and path in directories: children[f'{path.name}/'] = _get_recursive_directory_dict(session, path, directories) elif path.is_dir(): @@ -186,6 +189,8 @@ def _get_recursive_directory_dict(session: Session, directory: pathlib.Path, dir HIDDEN_DIRECTORIES = ('lost+found',) +# macOS metadata files which should never be shown in the FileBrowser. +HIDDEN_FILES = ('.DS_Store', '._.DS_Store') def list_directories_contents(session: Session, directories_: List[str]) -> Dict: @@ -208,6 +213,9 @@ def list_directories_contents(session: Session, directories_: List[str]) -> Dict if path.is_dir() and path.name in HIDDEN_DIRECTORIES: # Never show ignored directories. continue + if not path.is_dir() and path.name in HIDDEN_FILES: + # Never show ignored files. + continue if path.is_dir(): paths[f'{path.name}/'] = _get_recursive_directory_dict(session, path, directories) else: diff --git a/wrolpi/files/test/test_api.py b/wrolpi/files/test/test_api.py index 9ece3aa5..eb7b10bf 100644 --- a/wrolpi/files/test/test_api.py +++ b/wrolpi/files/test/test_api.py @@ -117,6 +117,36 @@ async def test_list_files_api(test_session, async_client, make_files_structure, await check_get_files(['videos', 'archives'], expected) +@pytest.mark.asyncio +async def test_list_files_api_hides_ds_store(test_session, async_client, make_files_structure, test_directory): + """macOS metadata files (.DS_Store / ._.DS_Store) are never shown in the FileBrowser.""" + make_files_structure([ + '.DS_Store', # Top-level metadata file should be hidden. + '._.DS_Store', # Top-level AppleDouble metadata file should be hidden. + 'archives/foo.txt', + 'archives/.DS_Store', # Nested metadata file should be hidden. + 'archives/._.DS_Store', # Nested AppleDouble metadata file should be hidden. + ]) + + async def get_files(directories): + request, response = await async_client.post('/api/files', content=json.dumps({'directories': directories})) + assert response.status_code == HTTPStatus.OK + return response.json['files'] + + # Top-level listing hides the metadata files. + top_level = await get_files([]) + assert '.DS_Store' not in top_level + assert '._.DS_Store' not in top_level + assert 'archives/' in top_level + + # Nested listing hides the metadata files, but still shows real files. + nested = await get_files(['archives']) + children = nested['archives/']['children'] + assert '.DS_Store' not in children + assert '._.DS_Store' not in children + assert 'foo.txt' in children + + @pytest.mark.asyncio async def test_delete_file(test_session, async_client, make_files_structure, test_directory): files = ['bar.txt', 'baz/', 'foo']