# greenwoodc/reaper architecture

> Architecture diagram of the GitHub repository greenwoodc/reaper, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-11-12). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/greenwoodc/reaper
- Repository: https://github.com/greenwoodc/reaper

## Overview

Reaper is a web-driven music pipeline: users upload playlist CSVs, scan Navidrome for tracks, download and import missing music, then rescan and create or update a Navidrome playlist. The graph includes the wizard and dashboard, backend job/API control, core matching and playlist stages, and settings. Download/import execution is documented, but its shell scripts are absent from the supplied tree; those details are therefore represented at the backend job layer. Some backend route and job wiring was not excerpted, so uncertain implementation links are omitted.

Main entry points: [`backend/routes.py`](https://github.com/greenwoodc/reaper/blob/main/backend/routes.py) (Flask API), [`frontend/src/lib/api.ts`](https://github.com/greenwoodc/reaper/blob/main/frontend/src/lib/api.ts) (Frontend API client), [`core/scan.py`](https://github.com/greenwoodc/reaper/blob/main/core/scan.py) (Initial library scan), [`core/rescan.py`](https://github.com/greenwoodc/reaper/blob/main/core/rescan.py) (Library rescan)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_ui["Web interface"]
  node_wizard["Pipeline wizard<br/>[Wizard.tsx]"]
  node_upload["Upload and scan UI<br/>[UploadStep.tsx]"]
  node_download_ui["Download UI<br/>[DownloadStep.tsx]"]
  node_finalize_ui["Finalize UI<br/>[FinalizeStep.tsx]"]
  node_dashboard["Playlist dashboard"]
  node_settings_ui["Settings form<br/>[SettingsForm.tsx]"]
end

subgraph group_control["API and jobs"]
  node_api_client["Frontend API client<br/>[api.ts]"]
  node_backend["Flask API<br/>[routes.py]"]
  node_job_runtime["Job runtime<br/>[jobs.py]"]
end

subgraph group_pipeline["Playlist pipeline"]
  node_config["Pipeline configuration<br/>[config.py]"]
  node_scan["Initial library scan<br/>[scan.py]"]
  node_matching["Track matching<br/>[matching.py]"]
  node_cleanup["Search cleanup<br/>[cleanup.py]"]
  node_rescan["Library rescan<br/>[rescan.py]"]
  node_playlist["Playlist creation<br/>[create_playlist.py]"]
  node_settings["Settings persistence<br/>[settings.py]"]
end

subgraph group_services["State and integrations"]
  node_playlist_files["Playlist working files"]
  node_navidrome["Navidrome server"]
  node_download_import["Soulseek and beets"]
end

node_user(("Playlist user"))

node_user -->|"operates"| node_wizard
node_wizard -->|"presents"| node_upload
node_wizard -->|"presents"| node_download_ui
node_wizard -->|"presents"| node_finalize_ui
node_user -->|"resumes playlists"| node_dashboard
node_user -->|"configures"| node_settings_ui
node_upload -->|"uploads CSV"| node_api_client
node_download_ui -->|"starts jobs"| node_api_client
node_finalize_ui -->|"starts finalize"| node_api_client
node_dashboard -->|"manages playlists"| node_api_client
node_settings_ui -->|"updates settings"| node_api_client
node_api_client -->|"calls HTTP API"| node_backend
node_backend -->|"runs scan"| node_scan
node_backend -->|"previews cleanup"| node_cleanup
node_backend -->|"runs rescan"| node_rescan
node_backend -->|"creates playlist"| node_playlist
node_backend -->|"reads and writes"| node_settings
node_backend -->|"manages jobs"| node_job_runtime
node_scan -->|"matches tracks"| node_matching
node_rescan -->|"matches tracks"| node_matching
node_scan -->|"searches library"| node_navidrome
node_rescan -->|"rescans and searches"| node_navidrome
node_playlist -->|"creates or updates"| node_navidrome
node_backend -->|"drives download and import"| node_download_import
node_download_import -->|"uses playlist staging"| node_playlist_files
node_scan -->|"writes scan results"| node_playlist_files
node_rescan -->|"updates results"| node_playlist_files
node_playlist -->|"reads matched tracks"| node_playlist_files
node_settings -->|"loads configuration"| node_config
node_scan -->|"uses settings"| node_config
node_rescan -->|"uses settings"| node_config
node_playlist -->|"uses ordering"| node_config
node_cleanup -->|"prepares search terms"| node_download_import

click node_wizard "https://github.com/greenwoodc/reaper/blob/main/frontend/src/components/Wizard.tsx"
click node_upload "https://github.com/greenwoodc/reaper/blob/main/frontend/src/components/UploadStep.tsx"
click node_download_ui "https://github.com/greenwoodc/reaper/blob/main/frontend/src/components/DownloadStep.tsx"
click node_finalize_ui "https://github.com/greenwoodc/reaper/blob/main/frontend/src/components/FinalizeStep.tsx"
click node_dashboard "https://github.com/greenwoodc/reaper/blob/main/frontend/src/components/PlaylistDashboard.tsx"
click node_settings_ui "https://github.com/greenwoodc/reaper/blob/main/frontend/src/components/SettingsForm.tsx"
click node_api_client "https://github.com/greenwoodc/reaper/blob/main/frontend/src/lib/api.ts"
click node_backend "https://github.com/greenwoodc/reaper/blob/main/backend/routes.py"
click node_job_runtime "https://github.com/greenwoodc/reaper/blob/main/backend/jobs.py"
click node_config "https://github.com/greenwoodc/reaper/blob/main/core/config.py"
click node_scan "https://github.com/greenwoodc/reaper/blob/main/core/scan.py"
click node_matching "https://github.com/greenwoodc/reaper/blob/main/core/matching.py"
click node_cleanup "https://github.com/greenwoodc/reaper/blob/main/core/cleanup.py"
click node_rescan "https://github.com/greenwoodc/reaper/blob/main/core/rescan.py"
click node_playlist "https://github.com/greenwoodc/reaper/blob/main/core/create_playlist.py"
click node_settings "https://github.com/greenwoodc/reaper/blob/main/core/settings.py"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_wizard,node_upload,node_download_ui,node_finalize_ui,node_dashboard,node_settings_ui,node_user toneBlue
class node_api_client,node_backend,node_job_runtime toneAmber
class node_config,node_scan,node_matching,node_cleanup,node_rescan,node_playlist,node_settings toneMint
class node_playlist_files,node_navidrome,node_download_import toneRose
```

## Components

### Web interface

- [Pipeline wizard](https://github.com/greenwoodc/reaper/blob/main/frontend/src/components/Wizard.tsx): `frontend/src/components/Wizard.tsx`
- [Upload and scan UI](https://github.com/greenwoodc/reaper/blob/main/frontend/src/components/UploadStep.tsx): `frontend/src/components/UploadStep.tsx`
- [Download UI](https://github.com/greenwoodc/reaper/blob/main/frontend/src/components/DownloadStep.tsx): `frontend/src/components/DownloadStep.tsx`
- [Finalize UI](https://github.com/greenwoodc/reaper/blob/main/frontend/src/components/FinalizeStep.tsx): `frontend/src/components/FinalizeStep.tsx`
- [Playlist dashboard](https://github.com/greenwoodc/reaper/blob/main/frontend/src/components/PlaylistDashboard.tsx): `frontend/src/components/PlaylistDashboard.tsx`
- [Settings form](https://github.com/greenwoodc/reaper/blob/main/frontend/src/components/SettingsForm.tsx): `frontend/src/components/SettingsForm.tsx`

### API and jobs

- [Frontend API client](https://github.com/greenwoodc/reaper/blob/main/frontend/src/lib/api.ts): `frontend/src/lib/api.ts`
- [Flask API](https://github.com/greenwoodc/reaper/blob/main/backend/routes.py): `backend/routes.py`
- [Job runtime](https://github.com/greenwoodc/reaper/blob/main/backend/jobs.py): `backend/jobs.py`

### Playlist pipeline

- [Pipeline configuration](https://github.com/greenwoodc/reaper/blob/main/core/config.py): `core/config.py`
- [Initial library scan](https://github.com/greenwoodc/reaper/blob/main/core/scan.py): `core/scan.py`
- [Track matching](https://github.com/greenwoodc/reaper/blob/main/core/matching.py): `core/matching.py`
- [Search cleanup](https://github.com/greenwoodc/reaper/blob/main/core/cleanup.py): `core/cleanup.py`
- [Library rescan](https://github.com/greenwoodc/reaper/blob/main/core/rescan.py): `core/rescan.py`
- [Playlist creation](https://github.com/greenwoodc/reaper/blob/main/core/create_playlist.py): `core/create_playlist.py`
- [Settings persistence](https://github.com/greenwoodc/reaper/blob/main/core/settings.py): `core/settings.py`

### State and integrations

- **Playlist working files**
- **Navidrome server**
- **Soulseek and beets**

### Other components

- **Playlist user**

## Connections

- Playlist user → Pipeline wizard: operates
- Pipeline wizard → Upload and scan UI: presents (evidence: `frontend/src/components/Wizard.tsx`)
- Pipeline wizard → Download UI: presents (evidence: `frontend/src/components/Wizard.tsx`)
- Pipeline wizard → Finalize UI: presents (evidence: `frontend/src/components/Wizard.tsx`)
- Playlist user → Playlist dashboard: resumes playlists
- Playlist user → Settings form: configures
- Upload and scan UI → Frontend API client: uploads CSV (evidence: `frontend/src/lib/api.ts`)
- Download UI → Frontend API client: starts jobs (evidence: `frontend/src/components/DownloadStep.tsx`)
- Finalize UI → Frontend API client: starts finalize (evidence: `frontend/src/lib/api.ts`)
- Playlist dashboard → Frontend API client: manages playlists (evidence: `frontend/src/lib/api.ts`)
- Settings form → Frontend API client: updates settings (evidence: `frontend/src/lib/api.ts`)
- Frontend API client → Flask API: calls HTTP API (evidence: `frontend/src/lib/api.ts`)
- Flask API → Initial library scan: runs scan (evidence: `backend/routes.py`)
- Flask API → Search cleanup: previews cleanup (evidence: `backend/routes.py`)
- Flask API → Library rescan: runs rescan (evidence: `backend/routes.py`)
- Flask API → Playlist creation: creates playlist (evidence: `backend/routes.py`)
- Flask API → Settings persistence: reads and writes (evidence: `backend/routes.py`)
- Flask API → Job runtime: manages jobs (evidence: `backend/routes.py`)
- Initial library scan → Track matching: matches tracks (evidence: `core/scan.py`)
- Library rescan → Track matching: matches tracks (evidence: `core/rescan.py`)
- Initial library scan → Navidrome server: searches library
- Library rescan → Navidrome server: rescans and searches
- Playlist creation → Navidrome server: creates or updates
- Flask API → Soulseek and beets: drives download and import
- Soulseek and beets → Playlist working files: uses playlist staging
- Initial library scan → Playlist working files: writes scan results
- Library rescan → Playlist working files: updates results
- Playlist creation → Playlist working files: reads matched tracks
- Settings persistence → Pipeline configuration: loads configuration (evidence: `core/settings.py`)
- Initial library scan → Pipeline configuration: uses settings (evidence: `core/scan.py`)
- Library rescan → Pipeline configuration: uses settings (evidence: `core/rescan.py`)
- Playlist creation → Pipeline configuration: uses ordering (evidence: `core/create_playlist.py`)
- Search cleanup → Soulseek and beets: prepares search terms
