coder-music-cli 0.3.0__tar.gz → 0.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/PKG-INFO +69 -15
  2. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/README.md +67 -14
  3. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/coder_music_cli.egg-info/PKG-INFO +69 -15
  4. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/coder_music_cli.egg-info/SOURCES.txt +6 -0
  5. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/__init__.py +1 -1
  6. coder_music_cli-0.4.1/music_cli/ai_tracks.py +216 -0
  7. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/cli.py +293 -18
  8. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/client.py +68 -13
  9. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/config.py +16 -6
  10. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/daemon.py +254 -30
  11. coder_music_cli-0.4.1/music_cli/platform/__init__.py +140 -0
  12. coder_music_cli-0.4.1/music_cli/platform/ipc.py +315 -0
  13. coder_music_cli-0.4.1/music_cli/platform/paths.py +149 -0
  14. coder_music_cli-0.4.1/music_cli/platform/player_control.py +197 -0
  15. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/player/ffplay.py +34 -13
  16. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/sources/ai_generator.py +14 -2
  17. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/pyproject.toml +2 -1
  18. coder_music_cli-0.4.1/tests/test_ai_tracks.py +267 -0
  19. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/LICENSE +0 -0
  20. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/coder_music_cli.egg-info/dependency_links.txt +0 -0
  21. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/coder_music_cli.egg-info/entry_points.txt +0 -0
  22. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/coder_music_cli.egg-info/requires.txt +0 -0
  23. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/coder_music_cli.egg-info/top_level.txt +0 -0
  24. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/__main__.py +0 -0
  25. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/context/__init__.py +0 -0
  26. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/context/mood.py +0 -0
  27. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/context/temporal.py +0 -0
  28. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/history.py +0 -0
  29. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/player/__init__.py +0 -0
  30. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/player/base.py +0 -0
  31. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/sources/__init__.py +0 -0
  32. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/sources/local.py +0 -0
  33. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/sources/radio.py +0 -0
  34. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/setup.cfg +0 -0
  35. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/tests/test_config.py +0 -0
  36. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/tests/test_context.py +0 -0
  37. {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/tests/test_history.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coder-music-cli
3
- Version: 0.3.0
3
+ Version: 0.4.1
4
4
  Summary: A command-line music application for coders with daemon support, radio streaming, and AI-generated music
5
5
  Author-email: Luong Nguyen <luongnv89@gmail.com>
6
6
  Maintainer-email: Luong Nguyen <luongnv89@gmail.com>
@@ -16,6 +16,7 @@ Classifier: Environment :: Console
16
16
  Classifier: Intended Audience :: Developers
17
17
  Classifier: Operating System :: POSIX :: Linux
18
18
  Classifier: Operating System :: MacOS
19
+ Classifier: Operating System :: Microsoft :: Windows
19
20
  Classifier: Programming Language :: Python :: 3
20
21
  Classifier: Programming Language :: Python :: 3.9
21
22
  Classifier: Programming Language :: Python :: 3.10
@@ -43,11 +44,21 @@ Requires-Dist: bandit>=1.7; extra == "dev"
43
44
  Requires-Dist: pre-commit>=3.0; extra == "dev"
44
45
  Dynamic: license-file
45
46
 
46
- # music-cli
47
+ <p align="center">
48
+ <img src="assets/logo/logo-mark.svg" alt="music-cli logo" width="80" height="80">
49
+ </p>
50
+
51
+ <h1 align="center">music-cli</h1>
47
52
 
48
- [![PyPI version](https://img.shields.io/pypi/v/coder-music-cli.svg)](https://pypi.org/project/coder-music-cli/)
49
- [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
50
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
53
+ <p align="center">
54
+ <a href="https://pypi.org/project/coder-music-cli/"><img src="https://img.shields.io/pypi/v/coder-music-cli.svg" alt="PyPI version"></a>
55
+ <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.9+-blue.svg" alt="Python 3.9+"></a>
56
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
57
+ </p>
58
+
59
+ <p align="center">
60
+ <img src="music-cli-ai.gif" alt="music-cli AI demo" width="600">
61
+ </p>
51
62
 
52
63
  A command-line music player for coders. Background daemon with radio streaming, local MP3s, and AI-generated music.
53
64
 
@@ -68,8 +79,9 @@ pip install coder-music-cli
68
79
  uv pip install coder-music-cli
69
80
 
70
81
  # Install FFmpeg (required)
71
- brew install ffmpeg # macOS
72
- sudo apt install ffmpeg # Ubuntu/Debian
82
+ brew install ffmpeg # macOS
83
+ sudo apt install ffmpeg # Ubuntu/Debian
84
+ choco install ffmpeg # Windows (or: winget install ffmpeg)
73
85
  ```
74
86
 
75
87
  ### Optional: AI Music Generation
@@ -116,6 +128,7 @@ music-cli radios add # Add new station
116
128
  | `next` | Skip track (auto-play mode) |
117
129
  | `volume [0-100]` | Get/set volume |
118
130
  | `radios` | Manage radio stations (list/play/add/remove) |
131
+ | `ai` | Manage AI-generated tracks (list/play/replay/remove) |
119
132
  | `history` | Playback log |
120
133
  | `moods` | Available mood tags |
121
134
  | `config` | Show configuration file locations |
@@ -175,16 +188,35 @@ Generate unique music with Meta's MusicGen model:
175
188
  # Install AI dependencies
176
189
  pip install 'coder-music-cli[ai]'
177
190
 
178
- # Generate music
179
- music-cli play -m ai --mood focus -d 30 # 30-second focus track
180
- music-cli play -m ai --mood energetic # Energetic music
191
+ # Generate and manage AI music
192
+ music-cli ai play # Context-aware generation
193
+ music-cli ai play -p "jazz piano" # Custom prompt
194
+ music-cli ai play --mood focus -d 30 # 30-second focus track
195
+ music-cli ai list # List all generated tracks
196
+ music-cli ai replay 1 # Replay track #1
197
+ music-cli ai remove 2 # Delete track #2
181
198
  ```
182
199
 
200
+ ### AI Command Suite
201
+
202
+ | Command | Description |
203
+ |---------|-------------|
204
+ | `ai list` | Show all AI-generated tracks with prompts |
205
+ | `ai play` | Generate music from current context |
206
+ | `ai play -p "prompt"` | Generate with custom prompt |
207
+ | `ai play --mood focus` | Generate with specific mood |
208
+ | `ai play -d 30` | Generate 30-second track (default: 5s) |
209
+ | `ai replay <num>` | Replay track by number (regenerates if file missing) |
210
+ | `ai remove <num>` | Delete track and audio file |
211
+
183
212
  Features:
213
+ - **Context-aware** - Uses time of day, day of week, and session mood
214
+ - **Custom prompts** - Generate exactly what you want with `-p`
215
+ - **Seamless looping** - All tracks engineered for infinite playback
216
+ - **Track management** - List, replay, and remove generated tracks
217
+ - **Regeneration** - Missing files can be regenerated with original prompt
184
218
  - **Animated feedback** - "composing..." animation while generating
185
- - **Persistent storage** - Generated tracks saved to `~/.config/music-cli/ai_music/`
186
- - **Replay support** - AI tracks appear in history for replay
187
- - **Auto-loop** - Generated tracks loop automatically
219
+ - **Persistent storage** - Tracks saved to `~/.config/music-cli/ai_music/`
188
220
 
189
221
  ## Moods
190
222
 
@@ -192,14 +224,17 @@ Features:
192
224
 
193
225
  ## Configuration
194
226
 
195
- Files in `~/.config/music-cli/`:
227
+ Configuration files location:
228
+ - **Linux/macOS**: `~/.config/music-cli/`
229
+ - **Windows**: `%LOCALAPPDATA%\music-cli\`
196
230
 
197
231
  | File | Purpose |
198
232
  |------|---------|
199
233
  | `config.toml` | Settings (volume, mood mappings, version) |
200
234
  | `radios.txt` | Station URLs (name\|url format) |
201
235
  | `history.jsonl` | Play history |
202
- | `ai_music/` | AI-generated tracks |
236
+ | `ai_tracks.json` | AI track metadata (prompts, durations) |
237
+ | `ai_music/` | AI-generated audio files |
203
238
 
204
239
  ### Version Updates
205
240
 
@@ -255,9 +290,28 @@ GitHub: https://github.com/luongnv89/music-cli
255
290
 
256
291
  - Python 3.9+
257
292
  - FFmpeg
293
+ - **Supported Platforms**: Linux, macOS, Windows 10+
258
294
 
259
295
  ## Changelog
260
296
 
297
+ ### v0.4.1
298
+ - Add Windows 10+ support
299
+ - Platform abstraction layer for cross-platform compatibility
300
+ - TCP localhost IPC on Windows (Unix sockets on Linux/macOS)
301
+ - stdin-based pause/resume on Windows (signals on Linux/macOS)
302
+ - Windows-specific config directory (`%LOCALAPPDATA%\music-cli\`)
303
+ - Add Windows to CI test matrix
304
+
305
+ ### v0.4.0
306
+ - Add `music-cli ai` command suite for AI track management
307
+ - `ai list` - Display all AI tracks with prompts
308
+ - `ai play [-p "prompt"]` - Generate with context or custom prompt
309
+ - `ai replay <num>` - Replay track (regenerates if missing)
310
+ - `ai remove <num>` - Delete track and audio file
311
+ - Add seamless looping via prompt engineering
312
+ - Add context-aware AI generation (time of day, day of week, mood)
313
+ - Default AI duration reduced to 5s for faster generation
314
+
261
315
  ### v0.3.0
262
316
  - Add radio station management (list/play/add/remove by number)
263
317
  - Add 35 curated radio stations (English, French, Spanish, Italian)
@@ -1,8 +1,18 @@
1
- # music-cli
1
+ <p align="center">
2
+ <img src="assets/logo/logo-mark.svg" alt="music-cli logo" width="80" height="80">
3
+ </p>
2
4
 
3
- [![PyPI version](https://img.shields.io/pypi/v/coder-music-cli.svg)](https://pypi.org/project/coder-music-cli/)
4
- [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
5
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ <h1 align="center">music-cli</h1>
6
+
7
+ <p align="center">
8
+ <a href="https://pypi.org/project/coder-music-cli/"><img src="https://img.shields.io/pypi/v/coder-music-cli.svg" alt="PyPI version"></a>
9
+ <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.9+-blue.svg" alt="Python 3.9+"></a>
10
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
11
+ </p>
12
+
13
+ <p align="center">
14
+ <img src="music-cli-ai.gif" alt="music-cli AI demo" width="600">
15
+ </p>
6
16
 
7
17
  A command-line music player for coders. Background daemon with radio streaming, local MP3s, and AI-generated music.
8
18
 
@@ -23,8 +33,9 @@ pip install coder-music-cli
23
33
  uv pip install coder-music-cli
24
34
 
25
35
  # Install FFmpeg (required)
26
- brew install ffmpeg # macOS
27
- sudo apt install ffmpeg # Ubuntu/Debian
36
+ brew install ffmpeg # macOS
37
+ sudo apt install ffmpeg # Ubuntu/Debian
38
+ choco install ffmpeg # Windows (or: winget install ffmpeg)
28
39
  ```
29
40
 
30
41
  ### Optional: AI Music Generation
@@ -71,6 +82,7 @@ music-cli radios add # Add new station
71
82
  | `next` | Skip track (auto-play mode) |
72
83
  | `volume [0-100]` | Get/set volume |
73
84
  | `radios` | Manage radio stations (list/play/add/remove) |
85
+ | `ai` | Manage AI-generated tracks (list/play/replay/remove) |
74
86
  | `history` | Playback log |
75
87
  | `moods` | Available mood tags |
76
88
  | `config` | Show configuration file locations |
@@ -130,16 +142,35 @@ Generate unique music with Meta's MusicGen model:
130
142
  # Install AI dependencies
131
143
  pip install 'coder-music-cli[ai]'
132
144
 
133
- # Generate music
134
- music-cli play -m ai --mood focus -d 30 # 30-second focus track
135
- music-cli play -m ai --mood energetic # Energetic music
145
+ # Generate and manage AI music
146
+ music-cli ai play # Context-aware generation
147
+ music-cli ai play -p "jazz piano" # Custom prompt
148
+ music-cli ai play --mood focus -d 30 # 30-second focus track
149
+ music-cli ai list # List all generated tracks
150
+ music-cli ai replay 1 # Replay track #1
151
+ music-cli ai remove 2 # Delete track #2
136
152
  ```
137
153
 
154
+ ### AI Command Suite
155
+
156
+ | Command | Description |
157
+ |---------|-------------|
158
+ | `ai list` | Show all AI-generated tracks with prompts |
159
+ | `ai play` | Generate music from current context |
160
+ | `ai play -p "prompt"` | Generate with custom prompt |
161
+ | `ai play --mood focus` | Generate with specific mood |
162
+ | `ai play -d 30` | Generate 30-second track (default: 5s) |
163
+ | `ai replay <num>` | Replay track by number (regenerates if file missing) |
164
+ | `ai remove <num>` | Delete track and audio file |
165
+
138
166
  Features:
167
+ - **Context-aware** - Uses time of day, day of week, and session mood
168
+ - **Custom prompts** - Generate exactly what you want with `-p`
169
+ - **Seamless looping** - All tracks engineered for infinite playback
170
+ - **Track management** - List, replay, and remove generated tracks
171
+ - **Regeneration** - Missing files can be regenerated with original prompt
139
172
  - **Animated feedback** - "composing..." animation while generating
140
- - **Persistent storage** - Generated tracks saved to `~/.config/music-cli/ai_music/`
141
- - **Replay support** - AI tracks appear in history for replay
142
- - **Auto-loop** - Generated tracks loop automatically
173
+ - **Persistent storage** - Tracks saved to `~/.config/music-cli/ai_music/`
143
174
 
144
175
  ## Moods
145
176
 
@@ -147,14 +178,17 @@ Features:
147
178
 
148
179
  ## Configuration
149
180
 
150
- Files in `~/.config/music-cli/`:
181
+ Configuration files location:
182
+ - **Linux/macOS**: `~/.config/music-cli/`
183
+ - **Windows**: `%LOCALAPPDATA%\music-cli\`
151
184
 
152
185
  | File | Purpose |
153
186
  |------|---------|
154
187
  | `config.toml` | Settings (volume, mood mappings, version) |
155
188
  | `radios.txt` | Station URLs (name\|url format) |
156
189
  | `history.jsonl` | Play history |
157
- | `ai_music/` | AI-generated tracks |
190
+ | `ai_tracks.json` | AI track metadata (prompts, durations) |
191
+ | `ai_music/` | AI-generated audio files |
158
192
 
159
193
  ### Version Updates
160
194
 
@@ -210,9 +244,28 @@ GitHub: https://github.com/luongnv89/music-cli
210
244
 
211
245
  - Python 3.9+
212
246
  - FFmpeg
247
+ - **Supported Platforms**: Linux, macOS, Windows 10+
213
248
 
214
249
  ## Changelog
215
250
 
251
+ ### v0.4.1
252
+ - Add Windows 10+ support
253
+ - Platform abstraction layer for cross-platform compatibility
254
+ - TCP localhost IPC on Windows (Unix sockets on Linux/macOS)
255
+ - stdin-based pause/resume on Windows (signals on Linux/macOS)
256
+ - Windows-specific config directory (`%LOCALAPPDATA%\music-cli\`)
257
+ - Add Windows to CI test matrix
258
+
259
+ ### v0.4.0
260
+ - Add `music-cli ai` command suite for AI track management
261
+ - `ai list` - Display all AI tracks with prompts
262
+ - `ai play [-p "prompt"]` - Generate with context or custom prompt
263
+ - `ai replay <num>` - Replay track (regenerates if missing)
264
+ - `ai remove <num>` - Delete track and audio file
265
+ - Add seamless looping via prompt engineering
266
+ - Add context-aware AI generation (time of day, day of week, mood)
267
+ - Default AI duration reduced to 5s for faster generation
268
+
216
269
  ### v0.3.0
217
270
  - Add radio station management (list/play/add/remove by number)
218
271
  - Add 35 curated radio stations (English, French, Spanish, Italian)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coder-music-cli
3
- Version: 0.3.0
3
+ Version: 0.4.1
4
4
  Summary: A command-line music application for coders with daemon support, radio streaming, and AI-generated music
5
5
  Author-email: Luong Nguyen <luongnv89@gmail.com>
6
6
  Maintainer-email: Luong Nguyen <luongnv89@gmail.com>
@@ -16,6 +16,7 @@ Classifier: Environment :: Console
16
16
  Classifier: Intended Audience :: Developers
17
17
  Classifier: Operating System :: POSIX :: Linux
18
18
  Classifier: Operating System :: MacOS
19
+ Classifier: Operating System :: Microsoft :: Windows
19
20
  Classifier: Programming Language :: Python :: 3
20
21
  Classifier: Programming Language :: Python :: 3.9
21
22
  Classifier: Programming Language :: Python :: 3.10
@@ -43,11 +44,21 @@ Requires-Dist: bandit>=1.7; extra == "dev"
43
44
  Requires-Dist: pre-commit>=3.0; extra == "dev"
44
45
  Dynamic: license-file
45
46
 
46
- # music-cli
47
+ <p align="center">
48
+ <img src="assets/logo/logo-mark.svg" alt="music-cli logo" width="80" height="80">
49
+ </p>
50
+
51
+ <h1 align="center">music-cli</h1>
47
52
 
48
- [![PyPI version](https://img.shields.io/pypi/v/coder-music-cli.svg)](https://pypi.org/project/coder-music-cli/)
49
- [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
50
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
53
+ <p align="center">
54
+ <a href="https://pypi.org/project/coder-music-cli/"><img src="https://img.shields.io/pypi/v/coder-music-cli.svg" alt="PyPI version"></a>
55
+ <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.9+-blue.svg" alt="Python 3.9+"></a>
56
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
57
+ </p>
58
+
59
+ <p align="center">
60
+ <img src="music-cli-ai.gif" alt="music-cli AI demo" width="600">
61
+ </p>
51
62
 
52
63
  A command-line music player for coders. Background daemon with radio streaming, local MP3s, and AI-generated music.
53
64
 
@@ -68,8 +79,9 @@ pip install coder-music-cli
68
79
  uv pip install coder-music-cli
69
80
 
70
81
  # Install FFmpeg (required)
71
- brew install ffmpeg # macOS
72
- sudo apt install ffmpeg # Ubuntu/Debian
82
+ brew install ffmpeg # macOS
83
+ sudo apt install ffmpeg # Ubuntu/Debian
84
+ choco install ffmpeg # Windows (or: winget install ffmpeg)
73
85
  ```
74
86
 
75
87
  ### Optional: AI Music Generation
@@ -116,6 +128,7 @@ music-cli radios add # Add new station
116
128
  | `next` | Skip track (auto-play mode) |
117
129
  | `volume [0-100]` | Get/set volume |
118
130
  | `radios` | Manage radio stations (list/play/add/remove) |
131
+ | `ai` | Manage AI-generated tracks (list/play/replay/remove) |
119
132
  | `history` | Playback log |
120
133
  | `moods` | Available mood tags |
121
134
  | `config` | Show configuration file locations |
@@ -175,16 +188,35 @@ Generate unique music with Meta's MusicGen model:
175
188
  # Install AI dependencies
176
189
  pip install 'coder-music-cli[ai]'
177
190
 
178
- # Generate music
179
- music-cli play -m ai --mood focus -d 30 # 30-second focus track
180
- music-cli play -m ai --mood energetic # Energetic music
191
+ # Generate and manage AI music
192
+ music-cli ai play # Context-aware generation
193
+ music-cli ai play -p "jazz piano" # Custom prompt
194
+ music-cli ai play --mood focus -d 30 # 30-second focus track
195
+ music-cli ai list # List all generated tracks
196
+ music-cli ai replay 1 # Replay track #1
197
+ music-cli ai remove 2 # Delete track #2
181
198
  ```
182
199
 
200
+ ### AI Command Suite
201
+
202
+ | Command | Description |
203
+ |---------|-------------|
204
+ | `ai list` | Show all AI-generated tracks with prompts |
205
+ | `ai play` | Generate music from current context |
206
+ | `ai play -p "prompt"` | Generate with custom prompt |
207
+ | `ai play --mood focus` | Generate with specific mood |
208
+ | `ai play -d 30` | Generate 30-second track (default: 5s) |
209
+ | `ai replay <num>` | Replay track by number (regenerates if file missing) |
210
+ | `ai remove <num>` | Delete track and audio file |
211
+
183
212
  Features:
213
+ - **Context-aware** - Uses time of day, day of week, and session mood
214
+ - **Custom prompts** - Generate exactly what you want with `-p`
215
+ - **Seamless looping** - All tracks engineered for infinite playback
216
+ - **Track management** - List, replay, and remove generated tracks
217
+ - **Regeneration** - Missing files can be regenerated with original prompt
184
218
  - **Animated feedback** - "composing..." animation while generating
185
- - **Persistent storage** - Generated tracks saved to `~/.config/music-cli/ai_music/`
186
- - **Replay support** - AI tracks appear in history for replay
187
- - **Auto-loop** - Generated tracks loop automatically
219
+ - **Persistent storage** - Tracks saved to `~/.config/music-cli/ai_music/`
188
220
 
189
221
  ## Moods
190
222
 
@@ -192,14 +224,17 @@ Features:
192
224
 
193
225
  ## Configuration
194
226
 
195
- Files in `~/.config/music-cli/`:
227
+ Configuration files location:
228
+ - **Linux/macOS**: `~/.config/music-cli/`
229
+ - **Windows**: `%LOCALAPPDATA%\music-cli\`
196
230
 
197
231
  | File | Purpose |
198
232
  |------|---------|
199
233
  | `config.toml` | Settings (volume, mood mappings, version) |
200
234
  | `radios.txt` | Station URLs (name\|url format) |
201
235
  | `history.jsonl` | Play history |
202
- | `ai_music/` | AI-generated tracks |
236
+ | `ai_tracks.json` | AI track metadata (prompts, durations) |
237
+ | `ai_music/` | AI-generated audio files |
203
238
 
204
239
  ### Version Updates
205
240
 
@@ -255,9 +290,28 @@ GitHub: https://github.com/luongnv89/music-cli
255
290
 
256
291
  - Python 3.9+
257
292
  - FFmpeg
293
+ - **Supported Platforms**: Linux, macOS, Windows 10+
258
294
 
259
295
  ## Changelog
260
296
 
297
+ ### v0.4.1
298
+ - Add Windows 10+ support
299
+ - Platform abstraction layer for cross-platform compatibility
300
+ - TCP localhost IPC on Windows (Unix sockets on Linux/macOS)
301
+ - stdin-based pause/resume on Windows (signals on Linux/macOS)
302
+ - Windows-specific config directory (`%LOCALAPPDATA%\music-cli\`)
303
+ - Add Windows to CI test matrix
304
+
305
+ ### v0.4.0
306
+ - Add `music-cli ai` command suite for AI track management
307
+ - `ai list` - Display all AI tracks with prompts
308
+ - `ai play [-p "prompt"]` - Generate with context or custom prompt
309
+ - `ai replay <num>` - Replay track (regenerates if missing)
310
+ - `ai remove <num>` - Delete track and audio file
311
+ - Add seamless looping via prompt engineering
312
+ - Add context-aware AI generation (time of day, day of week, mood)
313
+ - Default AI duration reduced to 5s for faster generation
314
+
261
315
  ### v0.3.0
262
316
  - Add radio station management (list/play/add/remove by number)
263
317
  - Add 35 curated radio stations (English, French, Spanish, Italian)
@@ -9,6 +9,7 @@ coder_music_cli.egg-info/requires.txt
9
9
  coder_music_cli.egg-info/top_level.txt
10
10
  music_cli/__init__.py
11
11
  music_cli/__main__.py
12
+ music_cli/ai_tracks.py
12
13
  music_cli/cli.py
13
14
  music_cli/client.py
14
15
  music_cli/config.py
@@ -17,6 +18,10 @@ music_cli/history.py
17
18
  music_cli/context/__init__.py
18
19
  music_cli/context/mood.py
19
20
  music_cli/context/temporal.py
21
+ music_cli/platform/__init__.py
22
+ music_cli/platform/ipc.py
23
+ music_cli/platform/paths.py
24
+ music_cli/platform/player_control.py
20
25
  music_cli/player/__init__.py
21
26
  music_cli/player/base.py
22
27
  music_cli/player/ffplay.py
@@ -24,6 +29,7 @@ music_cli/sources/__init__.py
24
29
  music_cli/sources/ai_generator.py
25
30
  music_cli/sources/local.py
26
31
  music_cli/sources/radio.py
32
+ tests/test_ai_tracks.py
27
33
  tests/test_config.py
28
34
  tests/test_context.py
29
35
  tests/test_history.py
@@ -1,4 +1,4 @@
1
1
  """music-cli: A command-line music application for coders."""
2
2
 
3
- __version__ = "0.3.0"
3
+ __version__ = "0.4.1"
4
4
  __github_url__ = "https://github.com/luongnv89/music-cli"
@@ -0,0 +1,216 @@
1
+ """AI track management for music-cli."""
2
+
3
+ import json
4
+ import logging
5
+ from dataclasses import dataclass
6
+ from datetime import datetime
7
+ from pathlib import Path
8
+
9
+ from .config import get_config
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ @dataclass
15
+ class AITrack:
16
+ """A single AI-generated track entry."""
17
+
18
+ prompt: str
19
+ file_path: str
20
+ timestamp: str
21
+ duration: int
22
+ model: str = "musicgen-small"
23
+
24
+ def to_dict(self) -> dict:
25
+ """Convert to dictionary for JSON serialization."""
26
+ return {
27
+ "prompt": self.prompt,
28
+ "file_path": self.file_path,
29
+ "timestamp": self.timestamp,
30
+ "duration": self.duration,
31
+ "model": self.model,
32
+ }
33
+
34
+ @classmethod
35
+ def from_dict(cls, data: dict) -> "AITrack":
36
+ """Create from dictionary."""
37
+ return cls(
38
+ prompt=data.get("prompt", ""),
39
+ file_path=data.get("file_path", ""),
40
+ timestamp=data.get("timestamp", ""),
41
+ duration=data.get("duration", 30),
42
+ model=data.get("model", "musicgen-small"),
43
+ )
44
+
45
+ def file_exists(self) -> bool:
46
+ """Check if the audio file still exists."""
47
+ return Path(self.file_path).exists()
48
+
49
+ def display_prompt(self, max_length: int = 50) -> str:
50
+ """Get a truncated prompt for display."""
51
+ if len(self.prompt) <= max_length:
52
+ return self.prompt
53
+ return self.prompt[: max_length - 3] + "..."
54
+
55
+
56
+ class AITracksManager:
57
+ """Manages AI-generated tracks storage and retrieval."""
58
+
59
+ def __init__(self, tracks_file: Path | None = None):
60
+ """Initialize AI tracks manager.
61
+
62
+ Args:
63
+ tracks_file: Path to the ai_tracks.json file.
64
+ Defaults to config directory.
65
+ """
66
+ if tracks_file is None:
67
+ tracks_file = get_config().ai_tracks_file
68
+ self.tracks_file = tracks_file
69
+ self._ensure_file()
70
+
71
+ def _ensure_file(self) -> None:
72
+ """Create the tracks file if it doesn't exist."""
73
+ if not self.tracks_file.exists():
74
+ self.tracks_file.write_text("[]")
75
+
76
+ def _load_tracks(self) -> list[AITrack]:
77
+ """Load tracks from JSON file."""
78
+ try:
79
+ data = json.loads(self.tracks_file.read_text())
80
+ return [AITrack.from_dict(item) for item in data]
81
+ except (json.JSONDecodeError, OSError) as e:
82
+ logger.warning(f"Failed to load AI tracks: {e}")
83
+ return []
84
+
85
+ def _save_tracks(self, tracks: list[AITrack]) -> None:
86
+ """Save tracks to JSON file."""
87
+ data = [track.to_dict() for track in tracks]
88
+ self.tracks_file.write_text(json.dumps(data, indent=2))
89
+
90
+ def get_all(self) -> list[AITrack]:
91
+ """Get all AI tracks.
92
+
93
+ Returns tracks in reverse chronological order (newest first).
94
+ """
95
+ tracks = self._load_tracks()
96
+ tracks.reverse()
97
+ return tracks
98
+
99
+ def add_track(
100
+ self,
101
+ prompt: str,
102
+ file_path: str,
103
+ duration: int,
104
+ model: str = "musicgen-small",
105
+ ) -> AITrack:
106
+ """Add a new AI track.
107
+
108
+ Args:
109
+ prompt: The prompt used for generation.
110
+ file_path: Path to the generated audio file.
111
+ duration: Duration in seconds.
112
+ model: Model name used for generation.
113
+
114
+ Returns:
115
+ The newly created AITrack.
116
+ """
117
+ track = AITrack(
118
+ prompt=prompt,
119
+ file_path=file_path,
120
+ timestamp=datetime.now().isoformat(),
121
+ duration=duration,
122
+ model=model,
123
+ )
124
+
125
+ tracks = self._load_tracks()
126
+ tracks.append(track)
127
+ self._save_tracks(tracks)
128
+
129
+ return track
130
+
131
+ def get_by_index(self, index: int) -> AITrack | None:
132
+ """Get a track by its 1-based index (newest first).
133
+
134
+ Args:
135
+ index: 1-based index of the track.
136
+
137
+ Returns:
138
+ AITrack if found, None otherwise.
139
+ """
140
+ tracks = self.get_all()
141
+ if 1 <= index <= len(tracks):
142
+ return tracks[index - 1]
143
+ return None
144
+
145
+ def remove_by_index(self, index: int) -> AITrack | None:
146
+ """Remove a track by its 1-based index (newest first).
147
+
148
+ Also deletes the associated audio file.
149
+
150
+ Args:
151
+ index: 1-based index of the track.
152
+
153
+ Returns:
154
+ The removed AITrack if found, None otherwise.
155
+ """
156
+ tracks = self.get_all()
157
+ if not (1 <= index <= len(tracks)):
158
+ return None
159
+
160
+ removed = tracks[index - 1]
161
+
162
+ # Delete the audio file if it exists
163
+ file_path = Path(removed.file_path)
164
+ if file_path.exists():
165
+ try:
166
+ file_path.unlink()
167
+ logger.info(f"Deleted audio file: {file_path}")
168
+ except OSError as e:
169
+ logger.warning(f"Failed to delete audio file: {e}")
170
+
171
+ # Remove from list and save
172
+ # Note: tracks is reversed, so we need to work with original order
173
+ original_tracks = self._load_tracks()
174
+ # Index in original order (0-based from end)
175
+ original_index = len(original_tracks) - index
176
+ del original_tracks[original_index]
177
+ self._save_tracks(original_tracks)
178
+
179
+ return removed
180
+
181
+ def update_file_path(self, index: int, new_file_path: str) -> bool:
182
+ """Update the file path for a track (used after regeneration).
183
+
184
+ Args:
185
+ index: 1-based index of the track.
186
+ new_file_path: New path to the audio file.
187
+
188
+ Returns:
189
+ True if updated, False if track not found.
190
+ """
191
+ tracks = self._load_tracks()
192
+ # Convert to reversed index
193
+ reversed_index = len(tracks) - index
194
+ if not (0 <= reversed_index < len(tracks)):
195
+ return False
196
+
197
+ tracks[reversed_index].file_path = new_file_path
198
+ tracks[reversed_index].timestamp = datetime.now().isoformat()
199
+ self._save_tracks(tracks)
200
+ return True
201
+
202
+ def count(self) -> int:
203
+ """Get the total number of AI tracks."""
204
+ return len(self._load_tracks())
205
+
206
+
207
+ # Global AI tracks manager instance
208
+ _ai_tracks: AITracksManager | None = None
209
+
210
+
211
+ def get_ai_tracks() -> AITracksManager:
212
+ """Get the global AI tracks manager instance."""
213
+ global _ai_tracks
214
+ if _ai_tracks is None:
215
+ _ai_tracks = AITracksManager()
216
+ return _ai_tracks