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.
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/PKG-INFO +69 -15
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/README.md +67 -14
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/coder_music_cli.egg-info/PKG-INFO +69 -15
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/coder_music_cli.egg-info/SOURCES.txt +6 -0
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/__init__.py +1 -1
- coder_music_cli-0.4.1/music_cli/ai_tracks.py +216 -0
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/cli.py +293 -18
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/client.py +68 -13
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/config.py +16 -6
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/daemon.py +254 -30
- coder_music_cli-0.4.1/music_cli/platform/__init__.py +140 -0
- coder_music_cli-0.4.1/music_cli/platform/ipc.py +315 -0
- coder_music_cli-0.4.1/music_cli/platform/paths.py +149 -0
- coder_music_cli-0.4.1/music_cli/platform/player_control.py +197 -0
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/player/ffplay.py +34 -13
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/sources/ai_generator.py +14 -2
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/pyproject.toml +2 -1
- coder_music_cli-0.4.1/tests/test_ai_tracks.py +267 -0
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/LICENSE +0 -0
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/coder_music_cli.egg-info/dependency_links.txt +0 -0
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/coder_music_cli.egg-info/entry_points.txt +0 -0
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/coder_music_cli.egg-info/requires.txt +0 -0
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/coder_music_cli.egg-info/top_level.txt +0 -0
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/__main__.py +0 -0
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/context/__init__.py +0 -0
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/context/mood.py +0 -0
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/context/temporal.py +0 -0
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/history.py +0 -0
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/player/__init__.py +0 -0
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/player/base.py +0 -0
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/sources/__init__.py +0 -0
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/sources/local.py +0 -0
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/music_cli/sources/radio.py +0 -0
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/setup.cfg +0 -0
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/tests/test_config.py +0 -0
- {coder_music_cli-0.3.0 → coder_music_cli-0.4.1}/tests/test_context.py +0 -0
- {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
|
+
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
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
|
72
|
-
sudo apt install ffmpeg
|
|
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
|
|
180
|
-
music-cli play -
|
|
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** -
|
|
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
|
-
|
|
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
|
-
| `
|
|
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
|
-
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
|
27
|
-
sudo apt install ffmpeg
|
|
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
|
|
135
|
-
music-cli play -
|
|
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** -
|
|
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
|
-
|
|
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
|
-
| `
|
|
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
|
+
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
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
|
72
|
-
sudo apt install ffmpeg
|
|
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
|
|
180
|
-
music-cli play -
|
|
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** -
|
|
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
|
-
|
|
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
|
-
| `
|
|
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
|
|
@@ -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
|