AdBlock Plus filter syntax to Apple Safari Content Blocker JSON converter
Find a file
2026-05-07 03:03:57 -04:00
examples Initial MVP commit 2026-05-07 03:03:57 -04:00
src/abp2cb Initial MVP commit 2026-05-07 03:03:57 -04:00
tests Initial MVP commit 2026-05-07 03:03:57 -04:00
.gitignore Initial MVP commit 2026-05-07 03:03:57 -04:00
LICENSE Initial MVP commit 2026-05-07 03:03:57 -04:00
pyproject.toml Initial MVP commit 2026-05-07 03:03:57 -04:00
README.md Initial MVP commit 2026-05-07 03:03:57 -04:00

abp2cb

AdBlock Plus filter syntax → Apple Safari Content Blocker JSON. A clean, dependency-free Python CLI for converting EasyList / EasyPrivacy / uBlock filters into the declarative JSON format Safari Content Blocker extensions consume.

Why this project

Last night (May 7) the user pitched "iBlock" — uBlock Origin reimagined as a native iPhone app. The brainstorm flagged the single biggest technical hurdle: converting Adblock Plus syntax to Safari's declarative Content Blocker JSON. Cosmetic filters like ##.ad-banner translate cleanly, network rules with options (||example.com^$third-party) translate with care, and procedural / scriptlet filters (:has-text(), +js(...)) don't translate at all and must be dropped.

abp2cb is the foundation: a tested, scriptable pipeline you can run in CI to produce the daily JSON push that an iOS Content Blocker (or a Cloudflare Worker DoH endpoint) would consume.

This is MVP day-one scope — single file in, single JSON out, with a sensible filter-by-filter rewriter and full reporting on dropped rules.

Install

cd ~/Code/daily-mvps/abp2cb-2026-05-07
python3 -m pip install -e .

No runtime dependencies — pure stdlib.

Use

# Convert a list, write JSON to stdout, stats to stderr
abp2cb convert examples/sample.txt -o blockerList.json

# Show stats only (don't write JSON)
abp2cb stats examples/sample.txt

# Fetch + convert EasyList from upstream (network)
abp2cb fetch-easylist -o easylist.json

# Library use
python3 -c "from abp2cb import convert_text; print(convert_text(open('examples/sample.txt').read())[:1])"

Apple caps a single Content Blocker extension at 150,000 rules; abp2cb warns when the output crosses that threshold and suggests --split N to shard into multiple JSON files (one per stacked extension, the technique AdGuard / 1Blocker / Wipr use).

What converts, what doesn't

ABP construct Status Notes
` domain.com^` network block
` domain.com^$third-party`
` domain.com^$image,script`
`@@ domain.com^` exception
##.ad-banner cosmetic css-display-none
domain.com##.ad scoped cosmetic if-domain trigger
/regex/ regex rule passed through as url-filter
##+js(...) scriptlet dropped Not expressible declaratively
##^script:has-text(...) dropped Procedural, JS-only
$redirect=... dropped Requires JS execution
! comment / blank Skipped

Dropped rules are summarized at the end of convert / stats so you can track parity over time.

Roadmap (next session)

  • Cloudflare Worker DoH companion that uses the network-rule subset for resolver-level blocking (instant updates, no App Store review).
  • --split sharding for >150k rule lists.
  • Round-trip JSON → ABP for diffing against upstream.

License

MIT