From 5f5befab29ac632794ee52652094559656f3ac39 Mon Sep 17 00:00:00 2001 From: HERITAGE-XION Date: Tue, 6 Jan 2026 01:57:44 +0100 Subject: [PATCH] updated README --- README.md | 116 +++++++++++++++++++++++-- examples/test_images/test_canon.jpg | Bin 0 -> 7958 bytes examples/test_images/test_fuji.jpg | Bin 0 -> 2241 bytes examples/test_images/test_metadata.png | Bin 0 -> 572 bytes 4 files changed, 111 insertions(+), 5 deletions(-) create mode 100644 examples/test_images/test_canon.jpg create mode 100644 examples/test_images/test_fuji.jpg create mode 100644 examples/test_images/test_metadata.png diff --git a/README.md b/README.md index 21c4e265..0228194f 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,121 @@ -# Metadata Scrubber Tool +# πŸ”’ Metadata Scrubber Tool -A tool for scrubbing metadata from files. +A privacy-focused CLI tool that removes sensitive metadata (EXIF, GPS, author info) from image files. Perfect for protecting your privacy before sharing photos online. -## Installation +## ✨ Features + +- **Multi-format support** - JPEG, PNG (with PDF/Office planned) +- **Concurrent processing** - Process 1000+ files efficiently with ThreadPoolExecutor +- **Dry-run mode** - Preview what would be scrubbed without making changes +- **Smart format detection** - Uses Pillow's format detection, not just file extensions +- **Beautiful CLI** - Rich progress bars and formatted output +- **Privacy-first** - Removes GPS coordinates, camera info, timestamps, author data + +## πŸ“š Educational Value + +This project demonstrates: +- **Factory pattern** for extensible file type handling +- **Abstract base classes** for consistent handler interfaces +- **Concurrent processing** with thread-safe operations +- **CLI development** with Typer and Rich +- **Image metadata handling** with Pillow and piexif + +## πŸ“‹ Prerequisites + +- Python 3.10+ +- [uv](https://github.com/astral-sh/uv) (recommended) or pip + +## πŸš€ Installation ```bash +# Clone the repository +git clone https://github.com/Heritage-XioN/metadata-scrubber-tool.git +cd metadata-scrubber-tool + +# Create virtual environment and install dependencies +uv venv +.venv\Scripts\activate # Windows +# source .venv/bin/activate # Linux/Mac + uv pip install -r requirements.txt ``` -## Usage +## πŸ“– Usage + +### Read Metadata ```bash -python -m src.main +# Single file +python -m src.main read photo.jpg + +# Recursive directory scan +python -m src.main read ./photos/ -r -ext jpg ``` + +### Scrub Metadata + +```bash +# Single file +python -m src.main scrub photo.jpg --output ./cleaned + +# Batch process with 8 workers +python -m src.main scrub ./photos/ -r -ext jpg --output ./cleaned --workers 8 + +# Preview without changes +python -m src.main scrub ./photos/ -r -ext jpg --dry-run +``` + +### CLI Options + +| Command | Options | +|---------|---------| +| `read` | `-r` / `--recursive`, `-ext` / `--extension` | +| `scrub` | `-r`, `-ext`, `-o` / `--output`, `-d` / `--dry-run`, `-w` / `--workers` | +| Global | `-v` / `--verbose` | + +## πŸ—οΈ Architecture + +``` +src/ +β”œβ”€β”€ main.py # CLI entry point (Typer app) +β”œβ”€β”€ commands/ +β”‚ β”œβ”€β”€ read.py # Read metadata command +β”‚ └── scrub.py # Scrub metadata command (batch processing) +β”œβ”€β”€ services/ +β”‚ β”œβ”€β”€ metadata_factory.py # Factory for creating handlers +β”‚ β”œβ”€β”€ metadata_handler.py # Abstract base class +β”‚ β”œβ”€β”€ image_handler.py # JPEG/PNG handler +β”‚ └── batch_processor.py # Concurrent batch processing +β”œβ”€β”€ core/ +β”‚ β”œβ”€β”€ jpeg_metadata.py # JPEG EXIF processor (piexif) +β”‚ └── png_metadata.py # PNG metadata processor (PIL) +└── utils/ + β”œβ”€β”€ display.py # Rich output formatting + β”œβ”€β”€ formatter.py # Value formatting helpers + β”œβ”€β”€ exceptions.py # Custom exceptions + └── logger.py # Logging configuration +``` + +**Data Flow:** +``` +CLI Command β†’ MetadataFactory β†’ Handler (readβ†’wipeβ†’save) β†’ Output + ↓ + Format Detection + ↓ + JpegProcessor / PngProcessor +``` + +## ⚠️ Security Considerations + +- **Always backup files** before scrubbing in production +- **Use `--dry-run`** to preview changes before committing +- **GPS coordinates** are completely stripped for privacy +- **Original files are not modified** - processed copies are created + +## πŸ“„ License + +MIT License - See [LICENSE](LICENSE) for details. + +--- + +Made with ❀️ for privacy diff --git a/examples/test_images/test_canon.jpg b/examples/test_images/test_canon.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6eb33f12d1d9b3e837c151e9913f0a82061028e1 GIT binary patch literal 7958 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 literal 0 HcmV?d00001 diff --git a/examples/test_images/test_fuji.jpg b/examples/test_images/test_fuji.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8cf1e5ca4e12aba21288c9ebcc5749cf7b26ab21 GIT binary patch literal 2241 zcmb7^X;4#F6oBtr0vH2%0a*$(MnFNU@)A)(Sj5OG23$sP8%01gB7(9ERIQ2*XjR0x zv_)lD0y2zC7uSG_0tOe53JRiBQ30ihrL{_JXVUY+BgE;i-Z%H&@11+jx#ym{=-PD8 z5H%+#GzeiBhN9s^x=wOeV1_atA%y}>LI{~59>zyF&?u+{7^PW77s~jaFhkRn(+EQc+>ykue;fsF{Ed7G3QQbQGpmQ1I?5=S`W0Am`2T(a_qSAfdy12HNY%7Z@DYNL78II9mjYQl(3 zjF@Z0yb%Tyra&GE5v?xwXWD*aVhm<)F|B->N+mJ1u&e(zd47UGe zsE77b`9tP^_(byH#~P}sK0UN}8jUC%t`R~OkWGvt^hc9fInFX5B8-?~84`sA6j!fr z1GXYo4v5AX={p&*GcaSfos1@EBe~d!B`h<3kYFet1~DT>mLqbCW#-Z&s}VV7#JCYN zYC)mlf%=OD3@L=dFN6lBWP2Q#36xymC1vg#bGu0z&K~qg_ZZDa)M%ask4ssYTUZJx zD?2xPI~yB2vGe%P-DJ~zeY~bgJ^e$Lhx-Ra1$s*7CC-mp8M9{X8b8Ix%;dO?<*V1k zGEOjorKO#1zx`=-LE|9bs=F&Uc`&no-)g&mR^}wtANsO4Dx_uO)uf05i@-jSPF(wPr^xKN&+eO<#@ICGCN?DH*OU!3E$)}L=}@nY+BR1>xUt|)o9kx_OIIk< z!Yh25#_slbSXXhIS7p96#9m?kS8JDW+%=nMPuH%l^p-I9*5tE(nn7ND-1-c`C0X{n z^Qj5^#}y|J9UujHE=gk(I+px=JfPb7%LH3X z(vB|^zJ67HF00II0&CRgFN%qlvX@5lB#Uc2WauRnf0qOxz_(g)e$Q(Ir2T3Yh;*o&eE>CO9k%O;xCdrNWa zUDieGGR^O4vzIu=Tt)n_uD2mglX~}QUwvWaJjUmkfCUFP~J z-*mM6Xh+t*^FDApOWq^C(%=)_oIi_`n(eUGwVdN}q`)?K+v9Z;T}sCYO^KXMhe*ps$e?iv{MeyztMeShweV@Pn*naQZeq(pmSTqnqv$d*o0PB1< zreH$rSND&wd}x2(brAabeW?U$uI6kt(3UIHm}wBhL4>T$GFszgTAQ0NcY2b$mOKp;K8N^wd5}aFf&nC6N?Fhyy zsXT5*k&oBR)gl3g@{ZYovoj`uvPoC#m{B?y49vlb8L18}mdjELT1Fwxm~K1jYnu=QkI^2VkmJ7cbFRm!@I_Xzgci*r1sukf#~D|` z)sf2N2!ANP=3=CLxv}4sKCZgzM4VbQyAX1@I~QZSMgL=sjQ_k8{-4{S09iPxCx}T$ YL(kNsJLj3RnE(I) literal 0 HcmV?d00001