Remove dead code identified in docs/SANITIZATION-REVIEW.md:
- app/blueprints/content_old.py and app/blueprints/playlist.py
- app/models/group.py, app/utils/nginx_config_reader.py
- orphaned templates (content_list, edit_content, upload_content,
player_page) and the related group/Template references
Result: 6 blueprints, 82 routes, no dead modules or orphan templates.
Add files that deploy.sh and docker-entrypoint.sh already require but
which were never tracked:
- https_manager.py (referenced by deploy.sh, migrate_network.sh,
docker-entrypoint.sh)
- Caddyfile.example (seeded by deploy.sh; its absence aborts deploy)
Relocate generated Graphify artifacts from graphify-out/ to
docs/graphify-out/ (110 files, no content change) and archive the
superseded docs under docs/.
Ignore hygiene:
- ignore ad-hoc .env backups (.env.bak*) — they contain live secrets
- keep the pre-sanitization snapshots (docs/legacy code/,
docs/old_code_documentation/) on disk but out of the repo
Fix .env.example: drop a duplicated config block, genericize the
hardcoded host IP, and document HOSTNAME_INTERNAL.
10 KiB
DigiServer v2 — Code Sanitization Review
Generated: 2026-09-10
Snapshot: docs/legacy code/ (1.7 MB, 171 files, 49 Python files) — full restore point.
Analysed 42 Python files / 240 functions / 104 routes / 32 templates under app/ and migrations/.
Review each section below and reply with the IDs you want deleted (e.g.
A1, A2, B1). Nothing is deleted until you confirm. Everything is recoverable fromdocs/legacy code/.
✅ Status — Applied 2026-09-10
Removed (A1, A2, D1, D2):
| ID | Removed | Notes |
|---|---|---|
| A1 | app/blueprints/content_old.py |
+ 3 templates orphaned by its removal |
| A2 | app/utils/nginx_config_reader.py |
|
| D1 | 5 group functions + their __init__.py exports |
get_player_status_info kept (live) |
| D2 | app/models/group.py, Content.groups, Content.group_count |
Extra cleanup triggered by A1/D2:
- Deleted orphaned templates:
upload_content.html(278),edit_content.html(11) - Removed the dead
groupskey from/api/system-infoandgroup_countfrom/api/content - Removed the
'group_id': getattr(player, 'group_id', None)compat shim from/api/player-status - Updated 8 documentation files
Also removed (B1, B2) — applied in a second pass:
| ID | Removed | Notes |
|---|---|---|
| B1 | app/blueprints/playlist.py (310 LOC) + its registration in app.py |
Whole legacy blueprint; its only real route redirected to content.manage_playlist_content |
| B2 | 3 routes in players.py: reorder_content, reorder_playlist, remove_from_playlist (~103 LOC) |
Two queried nonexistent columns (Content.player_id, Content.position, Player.playlist_version) → guaranteed 500s |
Extra cleanup triggered by B1:
- Deleted
content_list.html(202 lines) — its only remaining reference wasurl_for('playlist.manage_playlist'). It was already orphaned (no Python file rendered it) aftercontent_old.pywas deleted, so it has been removed for real this time. - Deleted
players/player_page.html(227 lines) — it was never rendered by any view (theplayers.player_pageroute redirects tomanage_player), so it was dead UI. Corrects the earlier "false positive" note below.
⚠️ Functional note:
players.regenerate_auth_code(POST /players/<id>/regenerate-auth) is now referenced by no template —player_page.htmlwas its only caller. The endpoint still works if invoked directly. The equivalent control lives inmanage_player.htmlvia the quickconnect flow. Restoreplayer_page.htmlfromdocs/legacy code/if you want that button back.
Result (A + B + D combined):
42 → 38 Python modules 9,311 → 8,296 LOC
104 → 82 routes 32 → 28 templates
7 → 6 blueprints dead modules: 2 → 0, orphan templates: 0
Verified: all files compile; smoke test passes on a fresh DB (every API endpoint + key UI route returns
< 500); db.metadata no longer registers group/group_content; app boots against a migrated copy of the
real dashboard.db with all data intact; app.blueprints no longer contains playlist.
⚠️ Still open: the real
data/instance/dashboard.dbpredates theoriginal_filenamemigration. On startup the entrypoint now applies it automatically. To fix locally, run:DATABASE_URL=sqlite:///$PWD/data/instance/dashboard.db ./.venv/bin/python migrations/add_original_filename_to_content.py
Section A — Dead modules (zero importers)
| ID | Target | LOC | Evidence | Risk |
|---|---|---|---|---|
| A1 | app/blueprints/content_old.py |
500 | app.py imports content.py; this file's content_bp is never registered. Superseded "old" content workflow. |
Low |
| A2 | app/utils/nginx_config_reader.py |
120 | Never imported anywhere. Stack migrated nginx → Caddy, so the reader is obsolete. | Low |
graph LR
app_py["app.py<br/>register_blueprints()"] --> content["content.py<br/>content_bp ✅ ACTIVE"]
content_old["content_old.py<br/>content_bp ❌ DEAD"] -.->|never imported| x1[" "]
nginx["nginx_config_reader.py<br/>❌ DEAD"] -.->|never imported| x2[" "]
caddy["caddy_manager.py<br/>✅ ACTIVE"] --> app_py
style content_old fill:#7f1d1d,color:#fff
style nginx fill:#7f1d1d,color:#fff
Section B — Legacy duplicate route surface — ✅ REMOVED
Two blueprints exposed parallel implementations of the same operations. Only the content.*
versions were wired to the UI; the legacy ones had no template references.
| ID | Target | LOC | Evidence | Risk |
|---|---|---|---|---|
| B1 ✅ | app/blueprints/playlist.py (whole file + registration) |
310 | Entire file was legacy per-player playlist. Its manage_playlist route did nothing but redirect to the modern content.manage_playlist_content. The other 6 routes had no template reference. |
Low–Med |
| B2 ✅ | 3 routes in players.py: reorder_content, reorder_playlist, remove_from_playlist |
~103 | Superseded by content.*. Two queried nonexistent columns (Content.player_id, .position, Player.playlist_version) → guaranteed HTTP 500 if called. |
Low |
Duplicate operation matrix
| Operation | Modern (LIVE) | Legacy (DEAD) |
|---|---|---|
| Add content | content.add_content_to_playlist |
playlist.add_to_playlist |
| Remove content | content.remove_content_from_playlist |
playlist.remove_from_playlist, players.remove_from_playlist ⚠️broken |
| Reorder | content.reorder_playlist_content |
playlist.reorder_playlist, players.reorder_content ⚠️broken, players.reorder_playlist ⚠️broken |
| Set duration | content.update_playlist_content_duration |
playlist.update_duration |
| Mute audio | content.update_playlist_content_muted |
playlist.update_muted |
| Toggle edit | content.update_playlist_content_edit_enabled |
— |
| Clear | — | playlist.clear_playlist |
Section C — Broken code: references to columns that do not exist
Confirmed against the live DB schema. These raised AttributeError/OperationalError at runtime.
All resolved by deleting A1 + B2.
| ID | Location | Broken reference | Reachable? |
|---|---|---|---|
| C1 ✅ | players.py:768,775 (remove_from_playlist) |
player.playlist_version |
Via route only (no UI link) |
| C2 ✅ | players.py:~700 (reorder_playlist) |
Content.player_id, Content.position |
Via route only (no UI link) |
| C3 | content_old.py:189-190 |
player.playlist_version |
No (dead module) |
| C4 | content_old.py:50,57 |
content.player_id, player.group |
No (dead module) |
✅ Resolved by deleting A1 + B2. If you keep those files, they must be rewritten.
Not a bug (verified by hand): Content._playlist_duration /
._playlist_position / ._playlist_muted in api.py are set dynamically by
Playlist.get_content_ordered(). These are intentional and work correctly — the
static analyzer flags them because it only sees model class attributes.
Section D — Legacy groups subsystem
Groups are fully deprecated (0 rows, /api/groups already commented out), but the code lingers.
| ID | Target | LOC | Evidence | Risk |
|---|---|---|---|---|
| D1 | 5 group functions in app/utils/group_player_management.py: get_group_statistics, assign_player_to_group, bulk_assign_players_to_group, get_online_players_count, get_players_by_status + their __init__.py exports |
~130 | Zero callers outside the module. All three group functions reference player.group_id, which does not exist → broken. |
Low |
| D2 | app/models/group.py (Group model + group_content table) |
71 | Retained only because Content.groups FK relationship and Content.group_count reference it. Requires touching the Content model. |
Medium |
⚠️ Keep:
get_player_status_info()(top of the same file) is live — used atplayers.py:28andplayers.py:432. Only the group functions should go.
Section E — Legacy redirect stubs
Thin compatibility shims that only redirect to the modern UI. They're harmless but keep dead URL surface alive.
| ID | Target | Evidence |
|---|---|---|
| E1 | players.player_page (/players/<id>) |
Body is a single redirect(url_for('players.manage_player')). Still url_for-referenced by 2 templates, so keeping it is fine. |
| E2 | playlist.manage_playlist (/playlist/<id>) |
Part of B1 — already covered by deleting that file. |
Section F — Orphan templates / assets
| ID | Target | LOC | Evidence |
|---|---|---|---|
| — | (none) | — | After the B1/B2 pass the codebase has 0 orphan templates. |
Recommended batches — ✅ ALL APPLIED
| Batch | Contents | Total removed | Status |
|---|---|---|---|
| Batch 1 — Safe clean | A1, A2 | ~620 LOC | ✅ done |
| Batch 2 — Legacy playlist | B1, B2 | ~413 LOC | ✅ done |
| Batch 3 — Groups | D1 | ~130 LOC | ✅ done |
| Batch 4 — Group model | D2 | ~71 LOC | ✅ done |
Every batch was followed by: compile-all + app-factory boot + route smoke test, so hidden dependencies were caught before moving on.
How I verified this (so you can trust it)
| Check | Method |
|---|---|
| Module reachability | AST import extraction + register_blueprint cross-reference |
| Route usage | url_for('endpoint') and literal path matching against all templates/JS |
| Broken columns | Compared every var.attr access against live SQLite PRAGMA table_info |
| Dynamic attributes | Manually inspected get_content_ordered() to rule out false positives |
| Duplicate bodies | ast.dump body hashing across all functions (result: 0 exact duplicates) |
Reproduce anytime:
./.venv/bin/python docs/tools/sanitize_report.py # dead code + broken refs
./.venv/bin/python docs/tools/sanitize_audit.py # full function inventory
./.venv/bin/python docs/tools/sanitize_templates.py # orphan templates
./.venv/bin/python docs/tools/smoke_test.py # post-change smoke test
⚠️ Rollback / hygiene notes
docs/legacy code/is excluded from the Docker image (vialegacy code/and**/legacy code/in.dockerignore) so it never ships to production or bloats the build.- It is not git-ignored, so it will appear in
git status. Decide:- commit it as a recovery point, or
- add
legacy code/to.gitignoreif you'd rather rely on git history.
- Deleting files listed here is not recoverable from git unless committed first — the
docs/legacy code/copy is your safety net.