coder-music-cli 0.8.3__tar.gz → 0.8.4__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 (50) hide show
  1. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/PKG-INFO +1 -1
  2. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/coder_music_cli.egg-info/PKG-INFO +1 -1
  3. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/__init__.py +1 -1
  4. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/cli.py +29 -4
  5. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/config.py +57 -1
  6. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/pyproject.toml +1 -1
  7. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/LICENSE +0 -0
  8. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/README.md +0 -0
  9. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/coder_music_cli.egg-info/SOURCES.txt +0 -0
  10. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/coder_music_cli.egg-info/dependency_links.txt +0 -0
  11. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/coder_music_cli.egg-info/entry_points.txt +0 -0
  12. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/coder_music_cli.egg-info/requires.txt +0 -0
  13. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/coder_music_cli.egg-info/top_level.txt +0 -0
  14. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/__main__.py +0 -0
  15. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/ai_tracks.py +0 -0
  16. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/client.py +0 -0
  17. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/context/__init__.py +0 -0
  18. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/context/mood.py +0 -0
  19. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/context/temporal.py +0 -0
  20. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/daemon.py +0 -0
  21. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/hf_cache.py +0 -0
  22. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/history.py +0 -0
  23. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/model_manager.py +0 -0
  24. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/platform/__init__.py +0 -0
  25. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/platform/ipc.py +0 -0
  26. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/platform/paths.py +0 -0
  27. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/platform/player_control.py +0 -0
  28. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/player/__init__.py +0 -0
  29. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/player/base.py +0 -0
  30. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/player/ffplay.py +0 -0
  31. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/sources/__init__.py +0 -0
  32. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/sources/ai_generator.py +0 -0
  33. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/sources/ai_models/__init__.py +0 -0
  34. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/sources/ai_models/audioldm_strategy.py +0 -0
  35. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/sources/ai_models/bark_strategy.py +0 -0
  36. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/sources/ai_models/model_config.py +0 -0
  37. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/sources/ai_models/model_registry.py +0 -0
  38. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/sources/ai_models/model_strategy.py +0 -0
  39. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/sources/ai_models/musicgen_strategy.py +0 -0
  40. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/sources/ai_models/progress_callback.py +0 -0
  41. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/sources/ai_models/strategy_cache.py +0 -0
  42. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/sources/local.py +0 -0
  43. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/sources/radio.py +0 -0
  44. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/sources/youtube.py +0 -0
  45. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/music_cli/youtube_cache.py +0 -0
  46. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/setup.cfg +0 -0
  47. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/tests/test_ai_tracks.py +0 -0
  48. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/tests/test_config.py +0 -0
  49. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/tests/test_context.py +0 -0
  50. {coder_music_cli-0.8.3 → coder_music_cli-0.8.4}/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.8.3
3
+ Version: 0.8.4
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>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coder-music-cli
3
- Version: 0.8.3
3
+ Version: 0.8.4
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>
@@ -1,4 +1,4 @@
1
1
  """music-cli: A command-line music application for coders."""
2
2
 
3
- __version__ = "0.8.3"
3
+ __version__ = "0.8.4"
4
4
  __github_url__ = "https://github.com/luongnv89/music-cli"
@@ -416,7 +416,7 @@ def radios_group(ctx):
416
416
  def radios_list():
417
417
  """List all available radio stations."""
418
418
  config = get_config()
419
- radios = config.get_radios()
419
+ radios = config.get_radios_categorized()
420
420
 
421
421
  if not radios:
422
422
  click.echo(f"No stations configured. Add stations to: {config.radios_file}")
@@ -424,10 +424,35 @@ def radios_list():
424
424
  return
425
425
 
426
426
  click.echo("Available radio stations:\n")
427
- for i, (name, _url) in enumerate(radios, 1):
428
- click.echo(f" {i:2}. {name}")
429
427
 
430
- click.echo(f"\nTotal: {len(radios)} station(s)")
428
+ # Group stations by category
429
+ categories: dict[str, list[dict]] = {}
430
+ for station in radios:
431
+ cat = station["category"]
432
+ if cat not in categories:
433
+ categories[cat] = []
434
+ categories[cat].append(station)
435
+
436
+ # Display each category
437
+ for category, stations in categories.items():
438
+ click.echo(f" [{category}]")
439
+
440
+ # Display in 2 columns
441
+ col_width = 32 # Width for each column
442
+ for i in range(0, len(stations), 2):
443
+ left = stations[i]
444
+ left_str = f" {left['index']:2}. {left['name'][: col_width - 8]}"
445
+
446
+ if i + 1 < len(stations):
447
+ right = stations[i + 1]
448
+ right_str = f"{right['index']:2}. {right['name'][: col_width - 5]}"
449
+ click.echo(f"{left_str:<{col_width}}{right_str}")
450
+ else:
451
+ click.echo(left_str)
452
+
453
+ click.echo() # Empty line after each category
454
+
455
+ click.echo(f"Total: {len(radios)} station(s)")
431
456
  click.echo("Play with: music-cli radios play <number>")
432
457
 
433
458
 
@@ -197,7 +197,7 @@ Top Hits|https://streams.ilovemusic.de/iloveradio1.mp3
197
197
  Rock Radio|https://streams.ilovemusic.de/iloveradio3.mp3
198
198
  Metal [SomaFM]|http://ice1.somafm.com/metal-128-mp3
199
199
 
200
- # Synthwave / Retrowave (Nightride FM)
200
+ # Synthwave/Retrowave
201
201
  Nightride FM|https://stream.nightride.fm/nightride.mp3
202
202
  Chillsynth FM|https://stream.nightride.fm/chillsynth.mp3
203
203
  Darksynth FM|https://stream.nightride.fm/darksynth.mp3
@@ -354,6 +354,62 @@ Radio Capital|https://icecast.unitedradio.it/Capital.mp3
354
354
  radios.append((line, line))
355
355
  return radios
356
356
 
357
+ def get_radios_categorized(self) -> list[dict]:
358
+ """Load radio stations with category information.
359
+
360
+ Returns list of dicts with keys: index, name, url, category
361
+ Category is extracted from comment lines in radios.txt.
362
+ """
363
+ radios: list[dict] = []
364
+ if not self.radios_file.exists():
365
+ return radios
366
+
367
+ current_category = "Other"
368
+ station_index = 0
369
+
370
+ for line in self.radios_file.read_text().splitlines():
371
+ line = line.strip()
372
+ if not line:
373
+ continue
374
+
375
+ if line.startswith("#"):
376
+ # Check for major category (e.g., "# ========== ENGLISH ==========")
377
+ if "==========" in line:
378
+ # Extract category name between equals signs
379
+ category = line.replace("=", "").replace("#", "").strip()
380
+ if category:
381
+ current_category = category
382
+ # Check for subcategory (e.g., "# Chill/Lo-fi")
383
+ elif not line.startswith("# Format:") and not line.startswith("# Add"):
384
+ subcategory = line.lstrip("#").strip()
385
+ if subcategory and not subcategory.startswith("Format"):
386
+ current_category = subcategory
387
+ continue
388
+
389
+ # Parse station
390
+ station_index += 1
391
+ if "|" in line:
392
+ name, url = line.split("|", 1)
393
+ radios.append(
394
+ {
395
+ "index": station_index,
396
+ "name": name.strip(),
397
+ "url": url.strip(),
398
+ "category": current_category,
399
+ }
400
+ )
401
+ else:
402
+ radios.append(
403
+ {
404
+ "index": station_index,
405
+ "name": line,
406
+ "url": line,
407
+ "category": current_category,
408
+ }
409
+ )
410
+
411
+ return radios
412
+
357
413
  def get_radio_by_index(self, index: int) -> tuple[str, str] | None:
358
414
  """Get a radio station by its 1-based index.
359
415
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "coder-music-cli"
7
- version = "0.8.3"
7
+ version = "0.8.4"
8
8
  description = "A command-line music application for coders with daemon support, radio streaming, and AI-generated music"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
File without changes