No description
  • Python 95.7%
  • JavaScript 2.9%
  • Dockerfile 1.4%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-24 05:17:16 +00:00
.forgejo/workflows Replace the Octave provider with a provider-independent Library Archiver plugin 2026-10-08 21:46:36 +02:00
music_assistant_archiver Bump Music Assistant base 2.10.7 -> 2.10.8 2026-10-24 05:17:16 +00:00
tests share: confirm with the frontend's toast instead of alert() 2026-10-23 15:57:51 +02:00
tools Replace the Octave provider with a provider-independent Library Archiver plugin 2026-10-08 21:46:36 +02:00
.gitignore Keep the ISRC on memoized tracks; drop a stray debug file 2026-08-18 10:01:16 +02:00
README.md share: confirm with the frontend's toast instead of alert() 2026-10-23 15:57:51 +02:00
repository.json Replace the Octave provider with a provider-independent Library Archiver plugin 2026-10-08 21:46:36 +02:00

Library Archiver → Music Assistant

Vlastní build Music Assistant serveru s přibaleným pluginem Library Archiver: cokoli, co má v MA srdíčko (tracky, celá alba, diskografie umělců, tracky oblíbených playlistů), se zrcadlí jako otagované audio soubory do složky Filesystem providera (typicky SMB share). Nezávisle na tom, odkud se hraje — plugin jde přes běžnou MA stream cestu providera.

Repo je zároveň Home Assistant add-on repository — release se propíše do HA jako běžný update add-onu. (Repo si drží původní jméno music-assistant-octave a add-on původní slug music_assistant_octave, protože změna URL i slugu by v HA znamenala nový add-on s prázdným /data.)

Struktura

repository.json                  # HA add-on repo marker
music_assistant_archiver/        # HA add-on (wrapper nad oficiálním MA image)
├── config.yaml                  # verze add-onu — bump = release
├── build.yaml                   # pin MA base image (ghcr.io/music-assistant/server:X.Y.Z)
├── Dockerfile                   # kopíruje plugin do site-packages v image
├── patches/                     # build-time patche: bug importu playlistů, „Share link“ v menu
└── archiver/                    # samotný plugin
    ├── manifest.json
    ├── __init__.py              # PluginProvider: konfigurace, eventy, debounce
    └── archive.py               # výběr „co chci“ z knihovny, stažení, tagování, trash
└── ytdlp/                       # music provider: anonymní YouTube přes yt-dlp
    ├── manifest.json
    ├── __init__.py              # search / get_track / get_artist / get_stream_details
    └── helpers.py               # čisté funkce (parsování názvu, výběr formátu)
└── share/                       # plugin: veřejné odkazy na lokální tracky
    ├── __init__.py              # /share/<token>.<ext> = audio soubor, API share/link
    └── helpers.py               # token (HMAC), Content-Disposition
tests/test_ytdlp.py              # self-check helpers.py
tests/test_share.py              # self-check tokenu, hlaviček a patche frontendu
tests/test_archive.py            # self-check čistých částí (bez MA serveru)
tools/ma_import_convert.py       # Spotify/YT Music CSV → M3U8 pro import do MA

MA načítá providery jen z package adresáře (music_assistant/providers/), žádný custom-provider dir neexistuje (ověřeno na 2.10.5) — proto injektáž přes vlastní add-on postavený FROM oficiálního server image.

Instalace v Home Assistantu

  1. Settings → Add-ons → Add-on Store → ⋮ → Repositories → přidej URL tohoto Forgejo repa.
  2. Nainstaluj Music Assistant (Archiver). Build proběhne lokálně na HA hostu.
  3. Zastav oficiální MA add-on (stejné porty, host_network) — nesmí běžet oba.
  4. V MA UI přidej plugin Library Archiver a v jeho nastavení vyber cílovou Filesystem instanci.

Release flow (Forgejo)

  1. Uprav plugin v music_assistant_archiver/archiver/.
  2. Bump version v config.yaml (schéma <MA verze>.<build>, např. 2.10.5.1).
  3. Commit + push.
  4. V HA: Add-on Store → ⋮ → Check for updates → u add-onu se objeví Update.

Upgrade MA verze: automaticky — auto-bump.yml denně porovná upstream stable verzi MA add-onu a patch release sám bumpne + pushne; HA add-on má auto_update. Minor/major skok workflow schválně failne — ověř plugin proti novému music_assistant/models/plugin.py a bumpni ručně.

Plugin — konfigurace

Volba Default Poznámka
archive_provider — instance Filesystem providera (SMB/local/NFS); zrcadlí, co má v MA srdíčko
archive_path — advanced alternativa: libovolná absolutní cesta místo Filesystem instance
trash_retention_days 90 advanced: purge .trash/ souborů starších než X dní (od unlike). 0 = navždy

Provider YouTube (yt-dlp)

Oficiální YouTube Music provider v MA odmítne účet bez YT Music Premium (User does not have Youtube Music Premium). Tenhle provider žádný účet nemá: hledá přes ytsearch a streamuje nejlepší audio-only formát (m4a, jinak opus), URL si tahá yt-dlp v okamžiku přehrání. Vyžaduje add-on YT Music PO Token Generator (bgutil) – bez PO tokenu YouTube stream nevydá; adresa serveru je v nastavení providera (default http://127.0.0.1:4416, host_network).

  • Každé video je track, kanál je umělec. Název Umělec - Skladba se dělí, (Official Video)-šum z názvu padá; jinak umělec = jméno kanálu bez - Topic.
  • Žádná knihovna na straně YouTube (bez účtu není co synchronizovat); srdíčko v MA ho uloží do MA knihovny a Archiver ho stáhne na share jako cokoliv jiného.
  • Když YouTube odpoví Sign in to confirm you're not a bot (blok anonymních streamů z dané IP, hledání jede dál), vlož do nastavení providera cookie: celou hlavičku Cookie z prohlížeče přihlášeného na youtube.com (jakýkoli Google účet, Premium netřeba). Google ji občas otočí, pak vložit znovu.
  • yt-dlp + bgutil-ytdlp-pot-provider se instalují při startu providera (stejně jako v oficiálním), proto přežijí i stárnutí MA image.

Odkaz přímo na audio soubor lokálního songu: prohlížeč ho rovnou přehraje (a jde uložit jako „Umělec - Skladba.flac“), komukoli, bez účtu a bez HTML.

  • Zdroj pravdy je playlist Sdílené: každý track v něm má odkaz <External URL>/share/<token>.<přípona>, odebrání z playlistu odkaz zruší. Seznam odkazů je v nastavení pluginu, „Revoke all links“ (advanced) vymění tajný klíč a zneplatní všechny najednou.
  • „Sdílet“ / „Share link“ v kontextovém menu tracku (patch frontendu, viz níž) track do playlistu přidá a odkaz zkopíruje do schránky; potvrdí to toast frontendu (vue-sonner, patch ho najde v bundlu). Když schránka nejde (plain http, prošlý klik), toast nabídne tlačítko „Kopírovat“.
  • Sdílet jde jen track z knihovny, který má soubor na Filesystem provideru. Soubor se posílá tak, jak je (FLAC zůstane FLAC), s podporou Range, takže přehrávač umí přetáčet. Přípona v odkazu je jen pro chat aplikace, které podle ní poznají audio; token platí i bez ní.
  • Cache: soubor jde s Cache-Control: public, max-age=3600, takže opakovaná stažení obslouží Cloudflare a zrušený odkaz (odebrání z playlistu, Revoke all links) hraje z cache nejvýš hodinu. 404 je no-store, aby cachovaná chyba nepřekryla nový odkaz.
  • Token = HMAC-SHA256(klíč, id tracku v knihovně), klíč leží v /data/share.secret (zálohuje se s add-onem).
  • Ven vystav jen /share/: v npmplus proxy host na MA :8095 s location /share/ a vším ostatním zakázaným; v MA Settings → Core → Webserver → External URL nastav tu veřejnou adresu. Bez External URL vedou odkazy na LAN adresu MA.
  • Patch frontendu (patches/frontend_share_menu.py): frontend nemá pro pluginy žádný rozšiřovací bod, takže patch vloží share_menu.js do index.html a v minifikovaném bundlu nechá showContextMenuForMediaItem poslat menu přes hook. Build nikdy neshodí; když se bundle změní a vzor nesedí, položka v menu prostě chybí a odkazy zůstanou v nastavení pluginu. Text položky je česky nebo anglicky podle jazyka MA (bez nastavení podle prohlížeče); frontend překlady pluginů neumí, proto je slovník přímo v JS.

Implementační poznámky

  • Zdroj pravdy je MA knihovna: favorite tracky + tracky favorited alb, umělců (celá diskografie přes provider mapping umělce) a favorited playlistů. Custom playlisty bez srdíčka se nestahují. Triggery: event bus (MEDIA_ITEM_ADDED/UPDATED — like i editace playlistu tím projdou), start add-onu; vše debounced 10 s, při selhání retry za 15 min.
  • Klíč záznamu v manifestu .archive.json je id library položky (jeden soubor na nahrávku bez ohledu na providera). Track, který v knihovně ještě není (diskografie umělce), se klíčuje <instance>:<id>, dokud ho index nezařadí.
  • Stažení = provider.get_stream_details() → ffmpeg. HTTP/HLS/encrypted URL si ffmpeg tahá sám (vč. -decryption_key), CUSTOM stream jde do ffmpeg přes stdin. Známé kontejnery se kopírují bit po bitu (-c:a copy: flac/mp3/m4a/ogg/opus/wav/…), PCM a neznámé formáty se kódují do FLAC. Preferuje se mapping deklarovaný jako lossless; zdrojem nikdy není cílový Filesystem provider sám.
  • Adopce: track, který už má mapping na cílový Filesystem provider a soubor existuje, se jen zapíše do manifestu (file = item_id = relativní cesta). Díky tomu archiv z Octave éry pokračuje bez nového stahování.
  • Nikdy nepřepisuj existující soubor: MA váže položku knihovny na soubor, výměna při dalším indexu položku zruší i se srdíčkem (ověřeno 2026-08-20). Stahuje se jen to, co na disku chybí.
  • Tagy z MA Track: title, artist (všichni), album_artist (album → jinak první interpret; Jellyfin podle něj seskupuje), album, track, disc, date, ISRC (na něm stojí párování). cover.jpg vedle souborů přes mass.metadata.get_image_data_for_item.
  • Unlike = přesun do .trash/ (mtime = čas unlike), nikdy hard delete; ochrana: víc než polovina archivu „nechtěná“ v jednom běhu = přeskočit úklid (nejspíš neúplné čtení knihovny). Max 25 stažení na běh, 2 s pauza.
  • Po zápisu se spustí sync cílové Filesystem instance — MA pak při přehrávání sám preferuje lokální kopii.
  • Patch upstream bugu importu (patches/builtin_resolve_path_uri.py, aplikuje se při buildu): matcher importovaných playlistů zapisuje spárované URI jen do cesty M3U položky, resolver staví dostupnost jen z #EXTPROV: řádků → spárované tracky zůstávaly šedé. Patch build nikdy neshodí; když upstream blok změní, tiše se přeskočí.
  • AppArmor: add-on přibaluje apparmor.txt (kopie z oficiálního add-onu). Bez něj Supervisor nasadí docker-default profil, který tiše zařízne mount(2) — SMB provider pak hlásí mount error(13).

Jednorázový import ze Spotify / YT Music

  1. Spotify: exportify.app → export playlistu / Liked Songs jako CSV. YT Music: Google Takeout → YouTube Music → CSV.
  2. python tools/ma_import_convert.py export.csv --name "Spotify Liked" → vznikne .m3u8.
  3. V MA: Playlists → Import playlist → nahrát .m3u8, zapnout track matching.
  4. Playlist ❤️ → archiver ho celý postupně stáhne na share.