Radicale plugin: subscribe to ICS feeds with filtering and local edit support
Project description
radicale-ics-sync
Ever wanted to subscribe to an ICS feed (like a university timetable or a shared calendar) in Radicale, but still be able to edit events locally? That's what this plugin is for.
It syncs external ICS feeds into your Radicale calendars, keeps your local changes intact, and lets you filter out events you don't care about.
Installation
pip install radicale-ics-sync
Then configure Radicale to use the plugin (see Configuration).
Configuration
Radicale config
In your Radicale config file, set the storage type to radicale_ics_sync.storage:
[storage]
type = radicale_ics_sync.storage
filesystem_folder = /data/collections
# ics_config is optional; if omitted, defaults to /config/ics_sync.json
ics_config = /config/ics_sync.json
Sync jobs (ics_sync.json)
Create a file /config/ics_sync.json that defines which feeds to sync and where.
[
{
"feed": "https://example.com/calendar.ics",
"collection": "username/calendar-name",
"sync_interval": 3600,
"include_patterns": [],
"exclude_patterns": []
}
]
You can provide multiple pairs of feed + collection. Each sync job supports these fields:
| Field | Required | Default | Description |
|---|---|---|---|
feed |
✅ | — | URL of the ICS feed |
collection |
✅ | — | Radicale collection path |
sync_interval |
3600 |
How often to poll the feed, in seconds | |
include_patterns |
[] |
Can be strings or arbitrary regex patterns | |
exclude_patterns |
[] |
Can be strings or arbitrary regex patterns |
Note: The collection must already exist in Radicale before the plugin can sync to it. Create it through the Radicale web interface or your CalDAV client first.
Filtering
Patterns are matched case-insensitively against the event's SUMMARY (title) field.
{
"feed": "https://university.example.com/timetable.ics",
"collection": "alice/uni",
"exclude_patterns": ["Tutorial", "Exercise"]
}
- If
include_patternsis set: only events matching at least one pattern are synced - If
exclude_patternsis set: events matching any pattern are removed
Docker setup
Here is a minimal docker-compose.yml:
services:
radicale:
build: .
container_name: radicale
restart: always
ports:
- "5232:5232"
volumes:
- ./config:/config/config:ro # Radicale config file (not a directory)
- ./users:/config/users:ro
- ./ics_sync.json:/config/ics_sync.json:ro
- ./data:/data
read_only: true
tmpfs:
- /tmp
And a Dockerfile to install the plugin:
FROM tomsquest/docker-radicale
RUN /venv/bin/pip install radicale-ics-sync --no-cache-dir
Non-Docker setup
Install Radicale and the plugin:
pip install radicale radicale-ics-sync
Create your Radicale config (e.g. ~/.config/radicale/config) and specify the path of your ics_sync.json:
[storage]
type = radicale_ics_sync.storage
filesystem_folder = ~/.local/share/radicale/collections
ics_config = ~/.config/radicale/ics_sync.json
Then create ~/.config/radicale/ics_sync.json and start Radicale:
radicale
State
The plugin stores its sync state in ics_sync_hashes.json, written next to your Radicale data. It tracks the last known content hash per event so unchanged events aren't rewritten. Safe to delete if you want a full re-sync on next startup.
Behavior
The plugin polls ICS feeds at a regular interval. Events are filtered by include/exclude patterns before being written to Radicale. Events that disappear from the upstream feed or are filtered out are deleted from the Radicale collection. Local edits to upstream events are preserved, until the upstream event itself changes, in which case the upstream version wins. Local events are not touched.
Limitations & Roadmap
- Filtering is
SUMMARY-only — other fields (LOCATION,DESCRIPTION) aren't supported yet, but are planned. - Upstream changes fully overwrite local edits — field-level merge (e.g. keeping your local title while accepting upstream time changes) is planned.
Contributing
Issues and pull requests are welcome.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file radicale_ics_sync-0.1.0.tar.gz.
File metadata
- Download URL: radicale_ics_sync-0.1.0.tar.gz
- Upload date:
- Size: 11.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b1f63b6a1a65fcae30e56a1e005bb891608366d8bf88bb252a648d89740e1e0e
|
|
| MD5 |
457b5a1fded2fcf8e5ce98b2aac8d24d
|
|
| BLAKE2b-256 |
7cbfed337d557dfa923a88cea47a19721c904f603c6a578a37f325fdf738136f
|
File details
Details for the file radicale_ics_sync-0.1.0-py3-none-any.whl.
File metadata
- Download URL: radicale_ics_sync-0.1.0-py3-none-any.whl
- Upload date:
- Size: 10.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
924d22e5f1ae2150a9874a47137567e64847018b8269c8fe0a16c768fcbd46e0
|
|
| MD5 |
062277a642182ce7a6b64ab55ed3e350
|
|
| BLAKE2b-256 |
6c16e61c05d8b82911dff9a1ffc85e798b11220a06daf33bf6730db2cd1fb53c
|