Initial design for MediaShelf
Software design for a Plex library analytics and reclaim-reporting tool. v1 is report-only: no deletion, no filesystem access, Plex API as the sole data source. Movies at item level, TV rolled up to season level. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GVbG48GAXfCZatcmX123Ra
This commit is contained in:
commit
fdcddc8149
4 changed files with 1105 additions and 0 deletions
30
.env.example
Normal file
30
.env.example
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# MediaShelf configuration — copy to .env and fill in.
|
||||
# NEVER commit .env. PLEX_TOKEN is a secret.
|
||||
|
||||
MEDIASHELF_SECRET_KEY=change-me
|
||||
LOG_LEVEL=INFO
|
||||
TZ=America/Regina
|
||||
|
||||
# --- Plex connection ---
|
||||
PLEX_BASE_URL=http://192.168.1.10:32400
|
||||
PLEX_TOKEN=your-plex-token-here
|
||||
PLEX_VERIFY_SSL=true
|
||||
PLEX_TIMEOUT_S=30
|
||||
PLEX_PAGE_SIZE=500
|
||||
PLEX_REQUEST_DELAY_MS=0
|
||||
|
||||
# --- Storage ---
|
||||
DATABASE_PATH=/data/mediashelf.db
|
||||
|
||||
# --- Scanning ---
|
||||
SCAN_SCHEDULE_CRON=0 4 * * *
|
||||
SCAN_FULL_SWEEP_CRON=0 3 * * 0
|
||||
SCAN_ON_STARTUP=false
|
||||
SCAN_LOCK_TIMEOUT_S=7200
|
||||
|
||||
# --- Reclaim score tuning ---
|
||||
SCORE_STALE_HORIZON_DAYS=730
|
||||
SCORE_AGE_HORIZON_DAYS=1095
|
||||
SCORE_POPULAR_AT=3
|
||||
SCORE_GRACE_DAYS=30
|
||||
SCORE_RECENT_DAYS=90
|
||||
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
.env
|
||||
*.db
|
||||
*.db-wal
|
||||
*.db-shm
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
.venv/
|
||||
venv/
|
||||
.pytest_cache/
|
||||
.coverage
|
||||
htmlcov/
|
||||
dist/
|
||||
build/
|
||||
*.egg-info/
|
||||
data/
|
||||
.DS_Store
|
||||
47
README.md
Normal file
47
README.md
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# MediaShelf
|
||||
|
||||
A self-hosted web app for figuring out which of the thousands of files in a Plex library
|
||||
are actually worth keeping.
|
||||
|
||||
MediaShelf scans a Plex Media Server over its HTTP API, builds a local snapshot of every
|
||||
movie and TV season, and joins together the facts Plex already knows but never shows side
|
||||
by side — date added, size on disk, file path, owning library, watch count, and last
|
||||
watched — into a sortable, filterable, chartable grid with a tunable **reclaim score**
|
||||
that ranks deletion candidates.
|
||||
|
||||
## Status
|
||||
|
||||
**v1 is report-only.** MediaShelf does not delete, move, or modify anything. It produces
|
||||
a ranked list, saved rule sets, and CSV export. Deletion is designed for in the roadmap
|
||||
but deliberately not built, so the scanner and the scoring model can be trusted before
|
||||
anything destructive is wired up.
|
||||
|
||||
Nothing is implemented yet — this repository currently holds the design.
|
||||
|
||||
## What it does
|
||||
|
||||
- Full-library ingest from Plex, no agent on the Plex host, no filesystem mounts
|
||||
- Movies at item level, TV rolled up to **season** level
|
||||
- Watch data pulled from the server-wide playback history, so plays by *every* Plex
|
||||
account are counted — not just the token owner's
|
||||
- Sort and filter on every metric; charts for size by library, additions over time,
|
||||
watched vs. unwatched by size, and size vs. last-watched
|
||||
- A weighted reclaim score with live sliders, and grace rules so it never recommends
|
||||
something you added last week
|
||||
- Named, re-runnable saved views — *"unwatched, older than 2 years, over 10 GB"*
|
||||
- CSV export of any view
|
||||
|
||||
## Planned stack
|
||||
|
||||
Python + Flask, SQLite (WAL), vanilla JS front-end, single container deployed as a
|
||||
Portainer stack behind Nginx Proxy Manager.
|
||||
|
||||
## Roadmap
|
||||
|
||||
- **v2** — two-stage quarantine-then-purge deletion, with authentication, a path
|
||||
allowlist, and an audit log
|
||||
- **v3** — Emby and Jellyfin support behind the existing `MediaProvider` abstraction
|
||||
|
||||
## Documentation
|
||||
|
||||
- [`docs/design.md`](docs/design.md) — the full software design
|
||||
1012
docs/design.md
Normal file
1012
docs/design.md
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue