From e48c790d891090a4452de6c4e5c5ad98c8cdf416 Mon Sep 17 00:00:00 2001 From: Nathan Date: Mon, 29 Dec 2025 11:19:34 -0700 Subject: [PATCH] zipseq ignore Thumbs.db --- ...12-29_17-30Z-zipseq-thumbs-db-exclusion.md | 253 ++++++++++++++++++ zip_sequences.py | 16 +- 2 files changed, 267 insertions(+), 2 deletions(-) create mode 100644 .specstory/history/2025-12-29_17-30Z-zipseq-thumbs-db-exclusion.md diff --git a/.specstory/history/2025-12-29_17-30Z-zipseq-thumbs-db-exclusion.md b/.specstory/history/2025-12-29_17-30Z-zipseq-thumbs-db-exclusion.md new file mode 100644 index 0000000..bc25f15 --- /dev/null +++ b/.specstory/history/2025-12-29_17-30Z-zipseq-thumbs-db-exclusion.md @@ -0,0 +1,253 @@ + + + + +# Zipseq Thumbs.db exclusion (2025-12-29 17:30Z) + +_**User (2025-12-29 17:37Z)**_ + +zipseq needs to completely ignore Thumbs.db. It should be removed from any existing manifests. If the manifest is changed, but only Thumbs.db is changed (removed) it should not be rezipped. + +--- + +_**Agent (model default, mode Agent)**_ + + + +--- + + +Examining `zip_sequences.py` to understand how it handles manifests and files. + + +--- + + +
Tool use: **read_file** • Read file: zip_sequences.py + +
+
+ +--- + + + +--- + + +Updating `zip_sequences.py` to ignore `Thumbs.db` files. Modifying file iteration, state loading, and state comparison. + + +--- + + +
Tool use: **read_file** • Read file: zip_sequences.py + +
+
+ +--- + + +
Tool use: **read_file** • Read file: zip_sequences.py + +
+
+ +--- + + +
Tool use: **read_file** • Read file: zip_sequences.py + +
+
+ +--- + + +
Tool use: **read_file** • Read file: zip_sequences.py + +
+
+ +--- + + + +--- + + +Updating the code to ignore `Thumbs.db` files: + + +--- + + + +
Tool use: **search_replace** • Edit file: zip_sequences.py + +**Chunk 1** +Lines added: 3, lines removed: undefined + +```diff +@@ -522,4 +522,7 @@ + continue + for filename in filenames: ++ # Ignore Thumbs.db files completely ++ if filename.lower() == "thumbs.db": ++ continue + yield path / filename + +``` + +
+ +
+ +--- + + + +
Tool use: **search_replace** • Edit file: zip_sequences.py + +**Chunk 1** +Lines added: 6, lines removed: undefined + +```diff +@@ -567,4 +567,10 @@ + try: + state = json.loads(state_path.read_text()) ++ # Remove Thumbs.db entries from loaded state ++ if "files" in state: ++ state["files"] = [ ++ entry for entry in state.get("files", []) ++ if Path(entry.get("path", "")).name.lower() != "thumbs.db" ++ ] + # Normalize timestamps in loaded state to ensure consistency + # This handles state files created before normalization was added +``` + +
+ +
+ +--- + + + +
Tool use: **search_replace** • Edit file: zip_sequences.py + +**Chunk 1** +Lines added: 4, lines removed: 1 + +```diff +@@ -597,7 +597,10 @@ + + def normalize_state(state: dict) -> dict: +- """Normalize timestamps in state to filesystem precision.""" ++ """Normalize timestamps in state to filesystem precision and filter out Thumbs.db.""" + normalized = {"files": []} + for entry in state.get("files", []): ++ # Ignore Thumbs.db files in state comparison ++ if Path(entry.get("path", "")).name.lower() == "thumbs.db": ++ continue + mtime_ns = entry["mtime_ns"] + if is_windows: +``` + +**Chunk 2** +Lines added: 1, lines removed: 1 + +```diff +@@ -610,5 +613,5 @@ + return normalized + +- # Compare normalized states ++ # Compare normalized states (Thumbs.db already filtered out) + normalized_seq = normalize_state(seq_state) + normalized_stored = normalize_state(stored_state) +``` + +
+ +
+ +--- + + + +--- + + +Checking for lint errors and verifying that `zip_sequence` uses the filtered file list: + + +--- + + +
+ Tool use: **read_lints** • Read lints for 1 file + +Lint paths: + +- `zip_sequences.py` + +Lint results + + +**No lint errors found** +
+
+ +--- + + +
Tool use: **read_file** • Read file: zip_sequences.py + +
+
+ +--- + + + +--- + + +Verifying that `zip_sequence` uses the filtered iterator: + + +--- + + +
Tool use: **read_file** • Read file: zip_sequences.py + +
+
+ +--- + + + +--- + + +## Summary of Changes + +1. **`iter_sequence_files()`** — Skips `Thumbs.db` files during iteration, so they're never included in new states or archives. + +2. **`load_state()`** — Removes any `Thumbs.db` entries from existing manifests when loading state files. + +3. **`state_changed()`** — Filters out `Thumbs.db` entries from both states before comparison, so if the only change is removing `Thumbs.db`, it won't trigger a rezip. + +Results: +- `Thumbs.db` files are ignored during scanning +- Existing manifests have `Thumbs.db` entries removed when loaded +- If a manifest change is only due to `Thumbs.db` removal, it won't trigger recompression + +The `zip_sequence()` function automatically benefits from these changes since it uses `iter_sequence_files()`, which now filters out `Thumbs.db`. + +No linting errors were found. The changes are ready to use. + +--- + diff --git a/zip_sequences.py b/zip_sequences.py index d6474e2..7437630 100644 --- a/zip_sequences.py +++ b/zip_sequences.py @@ -521,6 +521,9 @@ def iter_sequence_files(seq_dir: Path) -> Iterator[Path]: if is_archive_path(path): continue for filename in filenames: + # Ignore Thumbs.db files completely + if filename.lower() == "thumbs.db": + continue yield path / filename @@ -563,6 +566,12 @@ def load_state(state_path: Path) -> dict | None: return None try: state = json.loads(state_path.read_text()) + # Remove Thumbs.db entries from loaded state + if "files" in state: + state["files"] = [ + entry for entry in state.get("files", []) + if Path(entry.get("path", "")).name.lower() != "thumbs.db" + ] # Normalize timestamps in loaded state to ensure consistency # This handles state files created before normalization was added is_windows = platform.system() == "Windows" @@ -587,9 +596,12 @@ def state_changed(seq_state: dict, stored_state: dict | None, *, verbose: bool = is_windows = platform.system() == "Windows" def normalize_state(state: dict) -> dict: - """Normalize timestamps in state to filesystem precision.""" + """Normalize timestamps in state to filesystem precision and filter out Thumbs.db.""" normalized = {"files": []} for entry in state.get("files", []): + # Ignore Thumbs.db files in state comparison + if Path(entry.get("path", "")).name.lower() == "thumbs.db": + continue mtime_ns = entry["mtime_ns"] if is_windows: mtime_ns = (mtime_ns // 100) * 100 @@ -600,7 +612,7 @@ def state_changed(seq_state: dict, stored_state: dict | None, *, verbose: bool = }) return normalized - # Compare normalized states + # Compare normalized states (Thumbs.db already filtered out) normalized_seq = normalize_state(seq_state) normalized_stored = normalize_state(stored_state)