| examples | ||
| src/abp2cb | ||
| tests | ||
| .gitignore | ||
| LICENSE | ||
| pyproject.toml | ||
| README.md | ||
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).
--splitsharding for >150k rule lists.- Round-trip JSON → ABP for diffing against upstream.
License
MIT