Skip to main content

HTTP server exposing local filesystem as a wcpan.drive feed API

Project description

wcpan.drive.feed

An HTTP server that watches local filesystem paths with inotify, computes file metadata lazily, and exposes a token-based change log API — similar to Google Drive's changes API. This enables clients to efficiently poll for incremental changes without full directory scans.

Features

  • Watches directories recursively via inotify or fanotify (Linux only)
  • Computes MD5 hash, MIME type, and image/video dimensions per file
  • Token-based change log API: clients request only changes since their last cursor
  • Merges intermediate events (e.g. multiple modifies → single update; create+delete → skip)
  • No authentication (designed for intranet use)

Requirements

  • Linux (inotify or fanotify)
  • Python 3.13+
  • libmagic1 and mediainfo system packages

Installation

# Install system dependencies
apt-get install libmagic1 mediainfo

# Install Python package with your chosen watcher backend
pip install "wcpan-drive-feed[inotify]"   # inotify backend
pip install "wcpan-drive-feed[fanotify]"  # fanotify backend

Configuration

Create a YAML config file:

host: "0.0.0.0"
port: 8080
database_url: "/data/db/server.db"
watches:
  media: /mnt/media
  photos: /mnt/photos
watcher:
  backend: inotify  # or "fanotify"
# Optional:
# exclude:
#   - "*.tmp"
# log_path: /data/logs/server.log

The watcher.backend field is required and must be either "inotify" or "fanotify".

Running

wcpan.drive.feed --config=/path/to/server.yaml

The --config flag defaults to /data/server.yaml.

API

GET /api/v1/cursor

Returns the current change log cursor (the latest change ID).

{ "cursor": 42 }

GET /api/v1/changes?cursor=<int>

Returns all changes since the given cursor. Missing cursor defaults to 0. Invalid cursor returns HTTP 400.

{
  "cursor": 57,
  "changes": [
    {
      "removed": false,
      "node": {
        "id": "...",
        "parent_id": "...",
        "name": "image.jpg",
        "is_directory": false,
        "ctime": "2026-03-04T12:00:00+00:00",
        "mtime": "2026-03-04T12:00:00+00:00",
        "mime_type": "image/jpeg",
        "hash": "d41d8cd98f00b204e9800998ecf8427e",
        "size": 204800,
        "is_image": true,
        "is_video": false,
        "width": 1920,
        "height": 1080,
        "ms_duration": 0
      }
    },
    {
      "removed": true,
      "node_id": "..."
    }
  ]
}

Nodes appear in two phases: first with hash: "" immediately after detection, then again with the full hash/MIME/dimensions after metadata computation completes. Clients should treat a second update for the same node as the final state.

GET /api/v1/root

Returns the virtual super-root node (parent of all watch-root directories).

{
  "id": "00000000-0000-0000-0000-000000000000",
  "parent_id": null,
  "name": "",
  "is_directory": true,
  "ctime": "2026-03-04T12:00:00+00:00",
  "mtime": "2026-03-04T12:00:00+00:00",
  "mime_type": "",
  "hash": "",
  "size": 0,
  "is_image": false,
  "is_video": false,
  "width": 0,
  "height": 0,
  "ms_duration": 0
}

GET /api/v1/nodes/{id}/path

Returns the absolute filesystem path for a node.

{ "path": "/mnt/media/photos/image.jpg" }

Returns HTTP 404 if the node does not exist, HTTP 400 if the node is the super-root.

Development

# Run tests
make test

# Format code
make format

# Lint
make lint

How It Works

  1. On startup, the server initializes the SQLite database (auto-creates schema; aborts on version mismatch), then scans all configured watch paths and inserts/updates nodes.
  2. The configured watcher backend (asyncinotify or a custom fanotify backend) monitors each path for filesystem events (create, modify, delete, move).
  3. File metadata (MD5, MIME type, image/video dimensions) is computed in a ThreadPoolExecutor to avoid blocking the event loop.
  4. Every change is appended to a changes table. The /changes endpoint merges overlapping events per node — the last event wins — so clients always see the effective state.
  5. The server returns HTTP 503 while the startup scan and queue drain are in progress.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

wcpan_drive_feed-1.6.0.tar.gz (89.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

wcpan_drive_feed-1.6.0-py3-none-any.whl (30.0 kB view details)

Uploaded Python 3

File details

Details for the file wcpan_drive_feed-1.6.0.tar.gz.

File metadata

  • Download URL: wcpan_drive_feed-1.6.0.tar.gz
  • Upload date:
  • Size: 89.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for wcpan_drive_feed-1.6.0.tar.gz
Algorithm Hash digest
SHA256 5267fe0cf5dea58a75d42da72246597afbb6af07bbd30f45191c4c1a46a7cdac
MD5 e36926d31279ac7c2cac131c6b17b980
BLAKE2b-256 2f120822395a04b15cfdb9e69218c23487c94f56f21f39c3c08b819a54d73222

See more details on using hashes here.

File details

Details for the file wcpan_drive_feed-1.6.0-py3-none-any.whl.

File metadata

  • Download URL: wcpan_drive_feed-1.6.0-py3-none-any.whl
  • Upload date:
  • Size: 30.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for wcpan_drive_feed-1.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ba30bbb8cb82457beb923102581d219bd56f6f039813ec64bf78054790ecc71c
MD5 adf8db7f6055574b6388db6f44d25678
BLAKE2b-256 c6ffbdc51313da1b3501677c797f06fb0607f5f7f2e62a29f4af694dc5f06dda

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page