feat: add settings (volume, seek step, start queue) with TUI/Qt pages

- settings.py persists to .mts-music/settings.json; CLI 'settings' command
- player gains volume support (ffplay -volume / mpv --volume)
- TUI settings screen and Qt settings page (volume, output device, seek step,
  autoplay, start queue, wave-from-history)
- start queue + autoplay applied at startup; arrows use configured seek step
- shared fetch_liked_tracks helper; cache TUI cover renders and Qt scaled cover
This commit is contained in:
jads
2026-10-23 22:44:55 +03:00
parent b64c2dcd12
commit 637ac23b73
6 changed files with 574 additions and 90 deletions

View File

@@ -411,6 +411,21 @@ def fetch_mix_batch(api: Api, queue: list[str] | None = None) -> dict:
return api.get(f"/rotor/station/{WAVE_STATION}/tracks", **params).get("result") or {}
def fetch_liked_tracks(api: Api, uid: str, limit: int = 300) -> list[dict]:
"""Избранные треки с деталями: /tracks принимает не больше 50 id за раз."""
payload = api.get(f"/users/{uid}/likes/tracks")
refs = ((payload.get("result") or {}).get("library") or {}).get("tracks") or []
ids = [str(ref.get("id")) for ref in refs[:limit]]
tracks: list[dict] = []
for start in range(0, len(ids), 50):
chunk = ids[start : start + 50]
if chunk:
tracks.extend(
api.get("/tracks", trackIds=",".join(chunk)).get("result") or []
)
return tracks
def print_mix_item(index: int, item: dict) -> None:
track = item.get("track") or {}
heart = "♥" if item.get("liked") else " "
@@ -841,6 +856,33 @@ def cmd_qt(args: argparse.Namespace) -> None:
raise SystemExit(qt_main())
def cmd_settings(args: argparse.Namespace) -> None:
# Импорт внутри функции: settings.py сам зависит от mtsmusic.CONFIG_DIR.
from settings import DEFAULTS, load_settings, settings_path, save_settings
saved = False
if args.set:
updates: dict = {}
for pair in args.set:
if "=" not in pair:
die(f"неверный формат: {pair} (нужно ключ=значение)")
key, value = pair.split("=", 1)
key = key.strip()
if key not in DEFAULTS:
die(f"неизвестная настройка: {key}. Доступны: {', '.join(DEFAULTS)}")
updates[key] = value
current = save_settings(updates)
saved = True
else:
current = load_settings()
for key, default in DEFAULTS.items():
print(f"{key:<18} = {current.get(key, default)}")
print(f"\nФайл: {settings_path()}")
if saved:
print("Сохранено.")
def print_tui_check() -> None:
import platform
@@ -936,6 +978,16 @@ def build_parser() -> argparse.ArgumentParser:
qt = sub.add_parser("qt", help="настольное Qt-приложение (PySide6)")
qt.set_defaults(func=cmd_qt)
settings = sub.add_parser("settings", help="показать или изменить настройки")
settings.add_argument(
"-s",
"--set",
nargs="+",
metavar="KEY=VALUE",
help="изменить настройки, например: -s volume=60 seek_step=10",
)
settings.set_defaults(func=cmd_settings)
url = sub.add_parser("url", help="получить прямую ссылку на MP3")
url.add_argument("track_id")
url.set_defaults(func=cmd_url)