feat(tag-file): apply macOS Finder color tags

This commit is contained in:
2026-06-25 09:02:43 +03:00
parent a172b55df7
commit 83edb4a950
Executable
+69
View File
@@ -0,0 +1,69 @@
#!/usr/bin/env python3
import argparse
import plistlib
import struct
import subprocess
from pathlib import Path
COLORS = {
"gray": 1,
"green": 2,
"purple": 3,
"blue": 4,
"yellow": 5,
"red": 6,
"orange": 7,
}
def tag_file(path: Path, color: str) -> None:
idx = COLORS[color]
new_tag = f"{color.capitalize()}\n{idx}"
# Read existing tags, then append if not already present
result = subprocess.run(["xattr", "-px", "com.apple.metadata:_kMDItemUserTags", path], capture_output=True, text=True)
if result.returncode == 0:
raw = bytes.fromhex(result.stdout.replace("\n", "").replace(" ", ""))
existing = plistlib.loads(raw)
else:
existing = []
if new_tag not in existing:
existing.append(new_tag)
plist_data = plistlib.dumps(existing, fmt=plistlib.FMT_BINARY)
subprocess.run(["xattr", "-wx", "com.apple.metadata:_kMDItemUserTags", plist_data.hex(), path], check=True)
# FinderInfo: color in bits 1-3 of fdFlags (offset 8, big-endian uint16)
result = subprocess.run(["xattr", "-px", "com.apple.FinderInfo", path], capture_output=True, text=True)
if result.returncode == 0:
raw = bytes.fromhex(result.stdout.replace("\n", "").replace(" ", ""))
fi = bytearray(raw) if len(raw) >= 32 else bytearray(32)
else:
fi = bytearray(32)
flags = struct.unpack_from(">H", fi, 8)[0]
flags = (flags & ~0x000E) | (idx << 1)
struct.pack_into(">H", fi, 8, flags)
subprocess.run(["xattr", "-wx", "com.apple.FinderInfo", bytes(fi).hex(), path], check=True)
def main() -> None:
parser = argparse.ArgumentParser(description="Tag files with Finder color labels")
parser.add_argument(
"--tag", required=True, choices=list(COLORS),
metavar="COLOR", help=f"One of: {', '.join(COLORS)}",
)
parser.add_argument("files", nargs="+", type=Path)
args = parser.parse_args()
for path in args.files:
if not path.exists():
print(f"skipping {path}: not found")
continue
tag_file(path, args.tag)
print(f"tagged {path}{args.tag}")
if __name__ == "__main__":
main()