ReelSpace turns your media library into clear, actionable storage insights. See which movies, shows, libraries, resolutions, codecs, and duplicates consume the most space—then make smarter decisions before your server fills up.
ReelSpace does not delete, move, rename, or transcode files in the initial release.
The project is not affiliated with, endorsed by, or sponsored by Plex, Unraid, or Lime Technology. Plex is a trademark of Plex, Inc.; Unraid is a trademark of Lime Technology, Inc.
- Streaming, cancellable full and quick scans with live root, path, and file progress
- Read-only scan-root folder browser for directories mounted under
/data - Safe controls for clearing scan history or removing indexed roots without deleting media
- Cached directory aggregates, historical snapshots, growth, changes, and forecasts
- Explorer with hierarchical browsing, filtering, sorting, pagination, and exports
ffprobemetadata for resolution, codec, HDR, container, audio, and bitrate- Staged duplicate detection with optional full SHA-256 verification
- Device/inode hardlink grouping excluded from reclaimable-space calculations
- Logical, allocated, physical-unique, and sparse-file-aware size reporting
- Optional Plex HTTP API enrichment and path mappings
- Webhook, Discord, Slack, ntfy, and Gotify test delivery; adapter extension points
- Responsive light/dark/system UI, OpenAPI docs, diagnostics, and health endpoints
- Non-privileged Docker packaging and a safe Unraid Community Applications template
Screenshots will be added from the first clean Unraid 7.2.x release-candidate build:
docs/screenshots/overview.png— dashboard and growthdocs/screenshots/explorer.png— hierarchical storage explorerdocs/screenshots/duplicates.png— duplicates and hardlinks
These are deliberately marked placeholders; no image is presented as a tested Unraid installation screenshot yet.
The React/TypeScript interface and FastAPI service ship in one container. SQLAlchemy
stores the current index, cached aggregates, compact snapshots, and per-scan changes
in SQLite under /config. The scanner streams filesystem entries, never follows
symlinks, and purges stale index entries only after a successful scan. Plex,
notifications, and future automation integrations are adapters around the independent
filesystem core. See the implementation plan.
The latest automated and Docker smoke results are recorded in
the validation report.
The current release is
v0.1.3, published as
ghcr.io/bgriffin63/reelspace:latest for linux/amd64 and linux/arm64.
ReelSpace is a Docker application, not an Unraid plugin. Do not paste its URL into
Plugins → Install Plugin; that screen accepts .plg files, while ReelSpace ships
as a container.
Until ReelSpace is listed in Community Applications, install its Docker template:
-
Open Unraid → Terminal and run:
mkdir -p /boot/config/plugins/dockerMan/templates-user curl -fsSL \ https://raw.githubusercontent.com/BGriffin63/ReelSpace/main/templates/reelspace.xml \ -o /boot/config/plugins/dockerMan/templates-user/my-ReelSpace.xml
-
Refresh the Docker page, select Add Container, and choose ReelSpace from the Template list.
-
Review the required values:
Setting Host value Container value Access Application data /mnt/user/appdata/reelspace/configRead/write Media /mnt/user/Media/data/mediaRead-only Web UI 80808080TCP PUID / PGID 99/100PUID/PGIDUnraid defaults -
Change
/mnt/user/Mediaif your share uses a different spelling or location. Unraid paths are case-sensitive. -
Click Create, wait for the image download to finish, then open
http://YOUR-UNRAID-IP:8080. -
In Settings → Scan roots, select Browse, choose
/data/mediaor one of its subfolders, enter a display name and category, and select Add scan root. -
Open Scans and start a quick or full scan.
ReelSpace's folder browser only displays directories mounted under /data. To expose
another share, edit the container and add another read-only path mapping:
/mnt/user/downloads -> /data/downloads:ro
/mnt/user/domains -> /data/vms:ro
/mnt/user/Media/TV -> /data/tv:ro
You can optionally map /mnt/user to /data/shares:ro to browse all user shares,
although mapping only the shares ReelSpace needs is safer. Never map ReelSpace's own
/config directory beneath /data, and enter container paths such as /data/media
inside ReelSpace—not Unraid host paths such as /mnt/user/Media.
To update on Unraid, open Docker, select Check for Updates, and update
ReelSpace. Unraid recreates the container automatically; the /config mapping
preserves the database and settings, and startup applies database migrations.
Create a compose.yaml containing:
services:
reelspace:
image: ghcr.io/bgriffin63/reelspace:latest
container_name: reelspace
ports:
- "8080:8080"
environment:
TZ: America/Chicago
PUID: 1000
PGID: 1000
UMASK: "022"
volumes:
- ./reelspace-config:/config
- /path/to/media:/data/media:ro
restart: unless-stopped
security_opt:
- no-new-privileges:trueReplace /path/to/media, then install or update:
docker compose pull
docker compose up -dOpen http://localhost:8080, add /data/media under Settings → Scan roots, and
start a scan. State persists in ./reelspace-config.
docker run -d \
--name reelspace \
--restart unless-stopped \
--security-opt no-new-privileges:true \
-p 8080:8080 \
-e TZ=America/Chicago \
-e PUID=1000 \
-e PGID=1000 \
-e UMASK=022 \
-v reelspace-config:/config \
-v /path/to/media:/data/media:ro \
ghcr.io/bgriffin63/reelspace:latestThe API documentation is available at http://localhost:8080/docs.
A full scan re-stats and reanalyzes every included file. A quick scan reuses
cached hashes and media probes when size and modification time are unchanged, while
still traversing the root to detect removals. Defaults are intentionally conservative:
two filesystem workers, one ffprobe worker, small pauses between batches, and no
symlink following.
Logical size is the sum of path-visible apparent sizes. Allocated size uses filesystem blocks where available. Physical unique size counts a device/inode identity once. A sparse VM image can report a large logical size but much lower allocated size.
Hardlinks are multiple directory entries for the same (device, inode). Sonarr and
Radarr frequently use them so downloads and imported media share blocks. They are
shown as hardlink groups and are never treated as independently reclaimable duplicate
copies. True duplicates have distinct physical identities and matching content hashes.
Settings includes a lightweight daily scheduler for all enabled roots. The recommended default is a quick scan during an off-peak hour, such as 03:00. The scheduler sleeps between checks and consumes negligible CPU while idle. If a root is already scanning at its scheduled time, ReelSpace retries it later rather than overlapping the scan. Scheduled quick scans still traverse the root to detect additions and removals, but unchanged files reuse cached hashes and media metadata.
Media probing runs only for recognized extensions, is bounded by timeout and file-size
settings, and is cached by size and nanosecond modification time. HDR classification is
conservative; missing evidence produces Unknown. “REMUX-like” means high bitrate and
is an estimate, not a Plex edition classification.
To connect Plex, get a token using Plex's documented account process, enter the server
URL and token under Settings → Plex connection, and run the connection test. The
token is encrypted with an installation-local key under /config, returned to the UI
only as “configured,” and never logged. Path mappings translate a Plex-visible prefix
(for example /movies) to the scanner container prefix (/data/media/movies). The
core scanner works fully without Plex.
Use Notifications to test generic webhooks, Discord, Slack, ntfy, or Gotify. Provider adapters are isolated so SMTP, Pushover, quiet hours, event rules, cooldowns, and native Unraid notification delivery can evolve without coupling the scanner to a provider. Secrets must be supplied only through application configuration, never in public issue reports.
Stop the container and copy the complete /config directory, including reelspace.db and
.secret-key. Restore both together; the key is required to decrypt stored secrets.
For an integrity check:
docker exec reelspace python -c \
"import sqlite3; print(sqlite3.connect('/config/reelspace.db').execute('pragma integrity_check').fetchone()[0])"To back up SQLite safely while running:
docker exec reelspace python -c \
"import sqlite3; s=sqlite3.connect('/config/reelspace.db'); d=sqlite3.connect('/config/reelspace-backup.db'); s.backup(d)"Before upgrading, back up /config, pull the new image, and recreate the container.
Docker Compose users can run docker compose pull && docker compose up -d; Unraid
users can select Docker → Check for Updates, then update ReelSpace. Alembic
migrations run automatically at startup, and release notes identify any exceptional
step.
- Path inaccessible: confirm it is mapped into the container and the host path
exists. Do not enter
/mnt/user/...in the UI unless that exact path was mounted. - Permission denied: align PUID/PGID with a user/group allowed to read the share.
Unraid defaults are
99:100. Leave analyzed mounts read-only. - No media metadata: verify
ffprobecan read the file and inspect the scan warning. Corrupt files and timeouts do not abort a scan. - Unexpected double count: remove overlapping roots or explicitly acknowledge the overlap only when intentional.
- Network mount disappeared: restore it and scan again. Failed scans do not purge the last known-good index.
- Slow scan: disable metadata/duplicate analysis for non-media roots, reduce probing, use quick scans, and schedule full scans away from playback hours.
For one million files, use SSD-backed appdata, retain WAL mode, keep root scopes purposeful, avoid overlaps, paginate the explorer, and rely on cached aggregates rather than repeated full scans. The synthetic index utility is:
python -m app.benchmark --files 1000000There is no telemetry, cloud account, destructive filesystem operation, privileged
mode, Docker socket, or required host networking. All intelligence remains in
/config. Expose the UI only to a trusted LAN or place it behind an authenticated TLS
reverse proxy. See SECURITY.md for the threat model.
Backend:
python -m venv .venv
. .venv/bin/activate
pip install -e "./backend[dev]"
REELSPACE_CONFIG_DIR=./local-config uvicorn app.main:app --reload --port 8080
pytest
ruff check backend tests
mypy backend/appFrontend:
cd frontend
pnpm install
pnpm dev
pnpm test
pnpm buildContainer:
docker build --build-arg VERSION=0.1.3 -t reelspace:local .
docker run --rm -p 8080:8080 -v reelspace-config:/config \
-v /path/to/media:/data/media:ro reelspace:local- Installation and container lifecycle are validated on Unraid 7.2.0. Large-library scans and FUSE inode accounting still need release-candidate validation against representative media shares.
- Plex connection and library discovery are implemented; the deeper library sync and watched/unmatched reports are extension points for the next milestone.
- Complete event-rule persistence, SMTP/Pushover delivery, and optional Unraid host metrics are not yet complete.
- No cleanup workflow exists by design.
Future adapters are planned for Sonarr, Radarr, Lidarr, Readarr, Bazarr, Overseerr, Jellyseerr, SABnzbd, NZBGet, qBittorrent, Transmission, Tautulli, Jellyfin, Emby, the Unraid API, native notifications, and an explicitly reviewed cleanup workflow.
A Community Applications submission is planned but is not yet listed. Until it is approved, use the manual Unraid Docker-template process above. Maintainers should follow the Unraid submission checklist.
