Files
trackpull/docs/file-management.md

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:

  1. The requested relative path is joined with the user's base directory.
  2. .resolve() canonicalizes the result (expands .., symlinks, etc.).
  3. The resolved path is checked to ensure it starts with the resolved user directory.
  4. 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:

  1. Flatten nested directories — removes redundant intermediate folders.
  2. Rename from metadata — reads embedded ID3/FLAC tags and renames files to Title - Artist.ext (see rename_from_metadata() in utils.py).
  3. Wrap single tracks — if a download produces only one file with no folder, it is moved into a named subfolder.
  4. 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