3.1 KiB
File Management
Overview
Each user has an isolated directory under /downloads/{user_id}/. The frontend provides a browser-style file tree with support for downloading individual files, downloading folders as ZIP archives, and deleting files or folders.
Directory Structure
/downloads/
└── {user_id}/
└── {collection or album name}/
├── Track 1.flac
├── Track 2.flac
└── cover.jpg
Single-track downloads are automatically wrapped in a folder named after the track.
Security
All file access goes through a path traversal check:
- The requested relative path is joined with the user's base directory.
.resolve()canonicalizes the result (expands.., symlinks, etc.).- The resolved path is checked to ensure it starts with the resolved user directory.
- Any path that escapes the user directory returns
400 Bad Request.
Admins can browse any user's directory through dedicated admin endpoints that accept a user_id parameter.
API Endpoints
User endpoints
| Endpoint | Method | Description |
|---|---|---|
/api/files |
GET | List directory contents at ?path= (relative to user root) |
/api/files/download |
GET | Download a single file at ?path= |
/api/files/download-folder |
GET | Download a directory as a ZIP at ?path= |
/api/files/delete |
DELETE | Delete a file or directory at ?path= |
Admin endpoints
| Endpoint | Method | Description |
|---|---|---|
/api/admin/files |
GET | List a specific user's directory; requires ?user_id= and optional ?path= |
/api/admin/files/download |
GET | Download a file from any user's directory |
/api/admin/files/download-folder |
GET | Download a folder as ZIP from any user's directory |
/api/admin/files/delete |
DELETE | Delete from any user's directory |
Directory Listing Response
[
{ "name": "Album Name", "path": "Album Name", "is_dir": true },
{ "name": "Track.flac", "path": "Album Name/Track.flac", "is_dir": false, "size": 24601234 }
]
Directories always appear before files. Paths are relative to the user's root.
ZIP Downloads
Folder downloads are streamed directly as a ZIP file using Python's zipfile module. Files are added with paths relative to the requested folder, so the ZIP extracts cleanly into a single directory.
Post-Processing (after download)
After a Votify or Monochrome download completes, several cleanup steps run automatically:
- Flatten nested directories — removes redundant intermediate folders.
- Rename from metadata — reads embedded ID3/FLAC tags and renames files to
Title - Artist.ext(seerename_from_metadata()inutils.py). - Wrap single tracks — if a download produces only one file with no folder, it is moved into a named subfolder.
- Cleanup empty dirs — removes any directories left empty after the above steps.
Key Files
| File | Relevance |
|---|---|
| app.py | All file route handlers |
| utils.py | sanitize_filename, rename_from_metadata, cleanup_empty_dirs |