54 lines
2.7 KiB
Markdown
54 lines
2.7 KiB
Markdown
# Lifecycle & Removal Specification
|
|
|
|
This document defines the "Dislike $\rightarrow$ Delayed Deletion" lifecycle. This state machine ensures user intent is respected while preventing accidental permanent loss of music.
|
|
|
|
## 1. The Dislike State Machine
|
|
|
|
A track follows this state transition to ensure a "Grace Period" before physical deletion.
|
|
|
|
| Current State | Action | Next State | Side Effects |
|
|
| :--- | :--- | :--- | :--- |
|
|
| **LIBRARY** | User Dislikes | **PENDING_REMOVAL** | `dislikes` row created; Track becomes `HIDDEN`. |
|
|
| **PENDING_REMOVAL** | User Restores | **LIBRARY** | `dislikes` row deleted; Track becomes `VISIBLE`. |
|
|
| **PENDING_REMOVAL** | Grace Period Ends | **WARNING_SENT** | `ntfy` notification sent; `warned_at` timestamp set. |
|
|
| **WARNING_SENT** | 24h Passes | **DELETED** | File deleted from FS; Track record removed from DB. |
|
|
|
|
## 2. Detailed Transitions
|
|
|
|
### **Phase 1: The Dislike (Immediate)**
|
|
When a user triggers a dislike:
|
|
1. **DB Transaction:**
|
|
* Update `tracks.state = 'HIDDEN'`.
|
|
* Insert into `dislikes` table `{track_id, disliked_at: now}`.
|
|
* Log `feedback(action='disliked')`.
|
|
2. **UI Update:** The track immediately disappears from all "active" views (Library, Playlists, Vibe Queue, Search).
|
|
|
|
### **Phase 2: The Grace Period (The "Safety Net")**
|
|
* **Duration ($X$):** Configurable (default: 48 hours).
|
|
* **Behavior:** The track remains on disk and in the database, but is filtered out of all user-facing discovery and playback.
|
|
|
|
### **Phase 3: The Warning (The "Nudge")**
|
|
When `now > disliked_at + X`:
|
|
1. **Worker Action:** The `Cleanup/Sweep Worker` identifies the track.
|
|
2. **Notification:** Sends a message via `ntfy` (e.g., *"Are you sure? '<Track Name>' is scheduled for deletion in 24h"*).
|
|
3. **DB Update:** Set `dislikes.warned_at = now`.
|
|
|
|
### **Phase 4: The Finality (The "Cleanup")**
|
|
When `now > warned_at + 24h`:
|
|
1. **FS Action:** Delete the physical file at `tracks.path`.
|
|
2. **DB Action:**
|
|
* Cascade delete all related records (history, play_counts, etc.).
|
|
* Remove the `tracks` record.
|
|
3. **Logging:** Log `feedback(action='deleted_permanent')`.
|
|
|
|
## 2. Safety Invariants
|
|
|
|
- **No Immediate Deletion:** No user action (other than a "Hard Delete" admin command) can trigger immediate file deletion.
|
|
- **State Consistency:** A track cannot be in `PENDING_REMOVAL` and `LIBRARY` simultaneously.
|
|
- **Atomic Deletion:** The file deletion and the database removal must be treated as a single logical unit of work to prevent "Orphaned Files" (files on disk with no DB record) or "Ghost Records" (DB records with no file).
|
|
|
|
## 3. User Recovery
|
|
The "Restore" action is a simple reversal:
|
|
- `DELETE FROM dislikes WHERE track_id = X;`
|
|
- `UPDATE tracks SET state = 'LIBRARY' WHERE id = X;`
|