YouTube Music (Free) — A Custom Music Assistant Provider with Optional Library Sync

Ah... No fallback or selection if there is Nightly installed. And if you only have nightly it won't work.

Oh wow... Should have read that before manually modifying the fallback ID xD

# Usage:
#   sh install_provider.sh [--force] [--ref REF] [--ma-id ID]
#                          [--python-version VER] [--config-dir DIR]
#                          [--no-restart] [--no-stage]

hi @sproft. I've replied to the issue

Can i take it that it works for you now?

@RKor, thanks for the ping. The pieces that should resolve this are now on main (PRs #7 and #9). To close issue #6 we just need one verification run from a real MA instance with the latest code. Here is what to do:

1. Pull the latest provider into your MA container

curl -fsSL https://raw.githubusercontent.com/sproft/music-assistant-ytmusic/main/scripts/install_provider.sh | sh

This grabs main, stages it under /config/custom_components/mass/providers/, copies it into the live MA container, and restarts MA. Re-run the watcher add-on rebuild afterwards if you use one, so future HA restarts pick up the new code too.

2. Recapture your cookie correctly

Open music.youtube.com while logged in. Open DevTools, go to the Network tab, and click on a youtubei/v1/... request (not the page itself, not a static asset). Copy the full Cookie request header. Confirm the captured string contains all of these names:

  • __Secure-3PAPISID
  • __Secure-1PSID
  • __Secure-3PSID
  • SAPISID
  • HSID
  • SSID

If any of those are missing, you grabbed a scoped request. Try a different youtubei/v1/... call. The prettyPrint=false request you used last time is fine as long as the full set above is there.

Paste it into the provider's Cookie header field and save.

3. Watch the logs for two things

ha addons logs d5369777_music_assistant | grep -iE "ytmusic_free|youtube music" | tail -40

What we expect to see now:

  1. At setup: the line that reports cookie authentication and library sync enabled, with no recommended cookies missing warning right next to it. If that warning appears, your cookie is still partial. Go back to step 2.
  2. When the library disappears later (the symptom you originally hit): a single WARNING from music_assistant.ytmusic_free telling you to refresh the cookie. Without that warning we cannot distinguish "YouTube invalidated the session" from "MA cache desync."

4. Paste the result into issue #6

Two log snippets are enough:

  1. The ytmusic_free lines from provider setup. This confirms no warning fires with a full cookie.
  2. The ytmusic_free lines from the moment the library goes empty. This confirms the auth-lapse warning fires and points at the cookie.

If you still see the library disappearing without any WARNING line in the logs, paste that too. That points at a different problem (likely MA-side cache invalidation rather than the cookie).

The "owned playlists not appearing in MA's default Playlists section" symptom is unrelated to the cookie issue and will be tracked separately once #6 is closed.

As a general tip for anyone having problems. Make sure to grab the cookie form a fresh login from a new incognito window.

I'm using musicassistant on docker and had problems when the container was put down. If someone else is having the same problem and doesn't want to re-run the script manually. Probably not the best way but it works.

Create a systemd service file

[Unit]
Description=YTMusic Docker Watcher
After=docker.service
Requires=docker.service

[Service]
ExecStart=/bin/bash path-to-script/music-assistant-watcher.sh
Restart=always
User=user

[Install]
WantedBy=multi-user.target

Create a watchdog that run install_provider after music assistant container started

# Run Youtube Music(free) when music assistant is started

MIN_INTERVAL=10 #minutes

TIMESTAMP_FILE="path-to-timestamp-file/ytmusic_last_run"

docker events --filter container=musicassistant --filter event=start | while read event; do
    echo "Container started"

    now=$(date +%s)
    
    # Don't continue with script if interval is less than MIN_INTERVAL
    # avoid loop because install_provider.sh restart container
    if [ -f "$TIMESTAMP_FILE" ]; then
        last_run=$(cat "$TIMESTAMP_FILE")
        diff=$(( (now - last_run) / 60 ))

        if [ "$diff" -lt "$MIN_INTERVAL" ]; then
            echo "Skipping: ran $diff minutes ago"
            continue
        fi
    fi

    echo "$now" > "$TIMESTAMP_FILE"

    echo "Waiting 90s for container to start..."
    sleep 90

    echo "Running Youtube Music(free) installer"
    
    curl -fsSL https://raw.githubusercontent.com/sproft/music-assistant-ytmusic/main/scripts/install_provider.sh | 
        sh -s -- --force --ma-id musicassistant

done

Heads-up: Music Assistant moved to Python 3.14 — re-run the installer if the provider broke

A recent Music Assistant update changed its internal Python from 3.13 to 3.14. This provider is installed into MA's Python folder (/app/venv/lib/python3.XX/site-packages/...), so after the update the old path no longer matched and the provider could disappear, or the Watcher add-on could fail to re-install it.

There was also a second, related snag on Home Assistant OS 18+: the Supervisor renamed its addons folder to apps, which stopped the Watcher installer from finding the local add-ons directory.

Both are now fixed in the install scripts on main. If you were affected, re-running the installer is all you need.

Are you affected?

You might see one of these:

  • The provider missing from Settings → Apps after an MA or HA update.
  • The Watcher add-on log showing:
    Could not find the file /app/venv/lib/python3.13/site-packages/music_assistant ... ERROR: cp failed
  • The installer aborting with:
    could not find local add-ons directory. Pass --addons-dir explicitly.

How to fix it

1. Update the provider (run from a shell with Docker access, e.g. the Advanced SSH & Web Terminal add-on with Protection mode off, or a host shell):

curl -fsSL https://raw.githubusercontent.com/sproft/music-assistant-ytmusic/main/scripts/install_provider.sh | sh -s -- --force

The script now auto-detects the Python version, so you no longer pass it by hand.

2. If you use the Watcher add-on (keeps the provider installed across HA restarts), re-run its installer too:

curl -fsSL https://raw.githubusercontent.com/sproft/music-assistant-ytmusic/main/scripts/install_watcher_addon.sh | sh -s -- --force

It now auto-detects the add-ons folder (HAOS 18+ apps/local, the legacy addons/local, the /addons mapping inside the SSH/Samba add-on, and Supervised paths) and stamps a fresh version on every run so Home Assistant rebuilds the add-on.

3. Rebuild the Watcher add-on so the new files take effect:
Settings → Add-ons → MA Provider Watcher → three-dot menu → Rebuild, then Start.

4. Verify. Open the Watcher log and look for Copied OK / MA restarted. To confirm the Python version MA uses:

docker exec addon_d5369777_music_assistant ls /app/venv/lib/

If auto-detection still cannot find your add-ons folder

Pass it explicitly. From the HAOS host console:

curl -fsSL https://raw.githubusercontent.com/sproft/music-assistant-ytmusic/main/scripts/install_watcher_addon.sh | sh -s -- --force --addons-dir /mnt/data/supervisor/apps/local

Inside the SSH/Samba add-on use --addons-dir /addons.

Notes

TLDR, but a simple question: Will this be a problem the next time a Python, SAMBA, and/or OS Upgrade comes along as well? They will...

Does your upgrade routine burst into a happy music solo to announce when it is successful? Could it?

I like the Idea, but i really would like this as a separate docker or docker compose.

Good question, and a fair one. Short version: a Python minor-version bump (like 3.13 to 3.14) will break it again if nothing is watching, because the provider is installed into Music Assistant's own venv and that path is pinned to the Python version (.../python3.13/... becomes .../python3.14/...). The files are still there, just in the old folder MA no longer looks at.

That is the whole reason the Watcher add-on exists. It notices when MA updates or restarts and re-injects the provider into the new path automatically, so you should not have to touch anything. The recent fix for the HAOS 18+ folder rename (the old addons folder is now apps) was about keeping that Watcher reliable after an OS upgrade too.

SAMBA or general OS upgrades on their own should not matter, since they do not move MA's Python folder. The thing to watch for is specifically MA bumping its bundled Python.

So the recommendation is: install the Watcher add-on and leave it running. That turns "re-run the installer after every MA update" into "it just comes back on its own."

And yes, I love the happy-music-solo idea. Will note it down. But not very high an my priority list.

I understand the appeal, a separate container would feel a lot cleaner than installing into MA's venv. The catch is what this project actually is: it is a Music Assistant provider, not a standalone service. It registers itself through MA's in-process provider API, so it has to live inside the same Python process as Music Assistant. A separate Docker container would have no way to show up as a music provider in MA, it would just be a process with nothing to plug into.

That is why the installer drops it into MA's site-packages rather than shipping its own image. If you run Music Assistant itself in Docker, the provider still has to sit inside that MA container's venv. The Watcher add-on is what keeps that workable: when the MA container is updated or recreated, the Watcher re-injects the provider so you do not have to rebuild anything by hand.

If MA ever gains first-class support for external or out-of-tree providers, that would be the clean path to something container-shaped, and I would happily move to it. For now, in-process is the only way a provider can hook in. It will never be a part of the official Music Assistant providers as they have clearly stated they will only support Youtube Premium Accounts.

i used the Web Terminal...
How do i properly add the --ma-id command in the fsSL command? Can't you Auto Detect Beta/Nightly Versions? It's always "music_assistant" anyway.

➜  ~ curl -fsSL https://raw.githubusercontent.com/sproft/music-assistant-ytmusic/main/scripts/install_provider.sh | sh
[2026-07-01 21:18:48] Preflight checks...
[2026-07-01 21:18:48] WARN: could not auto-detect MA container; using fallback 'addon_d5369777_music_assistant'.
[2026-07-01 21:18:48]       Verify with: docker ps | grep music   then re-run with --ma-id ID
ERROR: MA container 'addon_d5369777_music_assistant' not found. Pass --ma-id ID with the right name.
➜  ~ curl -fsSL https://raw.githubusercontent.com/sproft/music-assistant-ytmusic/main/scripts/install_provider.sh | sh --ma-id addon_d5369777_music_assistant_beta
sh: bad option '--ma-id'
curl: (23) Failure writing output to destination, passed 1369 returned 0
➜  ~ docker ps | grep music
7f0b7388b9e3   ghcr.io/music-assistant/server:2.10.0b2                    "/usr/local/bin/entr…"   13 minutes ago   Up 13 minutes                                                                                                      addon_d5369777_music_assistant_beta
➜  ~ curl -fsSL https://raw.githubusercontent.com/sproft/music-assistant-ytmusic/main/scripts/install_provider.sh --ma-id addon_d5369777_music_assistant_beta | sh 
curl: option --ma-id: is unknown
curl: try 'curl --help' or 'curl --manual' for more information
➜  ~ 
➜  ~ 
➜  ~ curl -fsSL https://raw.githubusercontent.com/sproft/music-assistant-ytmusic/main/scripts/install_watcher_addon.sh | sh
[2026-07-01 21:23:59] Preflight checks...
[2026-07-01 21:23:59] Detected local add-ons path: /addons
[2026-07-01 21:23:59] WARN: could not auto-detect MA container; using fallback 'addon_d5369777_music_assistant'.
[2026-07-01 21:23:59]       Verify with: docker ps | grep music   then re-run with --ma-id ID
Error response from daemon: No such container: addon_d5369777_music_assistant
[2026-07-01 21:23:59] WARN: could not auto-detect Python version; using fallback 'python3.13'.
[2026-07-01 21:23:59] Downloading https://codeload.github.com/sproft/music-assistant-ytmusic/tar.gz/refs/heads/main
[2026-07-01 21:23:59] Extracting...
[2026-07-01 21:23:59] Creating /addons/ma_provider_watcher
[2026-07-01 21:23:59] Writing config.yaml
[2026-07-01 21:23:59] Writing build.yaml
[2026-07-01 21:23:59] Writing Dockerfile
[2026-07-01 21:23:59] Writing run.sh (MA=addon_d5369777_music_assistant, python3.13)
[2026-07-01 21:23:59] Install complete: /addons/ma_provider_watcher

Next steps:
  1. In Home Assistant: Settings -> Add-ons -> Add-on Store
     (three-dot menu) -> Check for updates.
  2. Open "MA Provider Watcher" under Local add-ons.
       First install:  click Install.
       Re-installing:  click Rebuild (three-dot menu) so the new run.sh and
                       provider files are baked into the image. A running
                       add-on keeps its old cached image until you rebuild.
  3. On the Info tab, turn Protection mode OFF (required for Docker socket access).
  4. Start the add-on and check the logs for "Copied OK" / "MA restarted".

This installer stamped version 1.0.20260701212359 so Home Assistant detects the
change. If you re-ran to fix the MA container ID or Python version and the
add-on still uses the old value, Rebuild it (step 2) -- "Check for updates"
alone does not rebuild a cached local add-on image.

If MA container ID or Python version was wrong, re-run with:
  sh install_watcher_addon.sh --force --ma-id <ID> --python-version <pythonX.Y>

Yeah... i think Youtube changed something...
OR i'm just incompetent...

2026-07-01 21:48:04.735 INFO (MainThread) [music_assistant.YouTube Music (Free)] Config updated, reloading provider ytmusic_free (instance_id=ytmusic_free)
2026-07-01 21:48:05.924 WARNING (MainThread) [music_assistant.ytmusic_free] Cookie authentication failed (Cookie must contain __Secure-3PAPISID), falling back to anonymous mode. You may need to refresh your cookie.
2026-07-01 21:48:05.925 INFO (MainThread) [music_assistant.ytmusic_free] YouTube Music (Free) initialized — anonymous mode
2026-07-01 21:48:05.925 INFO (MainThread) [music_assistant] Loaded music provider YouTube Music (Free)

So i went to the storage Tab. And found the Cookies myself:


But can't add them.

2026-07-01 22:00:03.775 INFO (MainThread) [music_assistant.YouTube Music (Free)] Config updated, reloading provider ytmusic_free (instance_id=ytmusic_free)
2026-07-01 22:00:04.952 WARNING (MainThread) [music_assistant.ytmusic_free] Cookie is missing recommended values: __Secure-1PSID, __Secure-3PSID, SAPISID. The provider may validate at init and then fail library calls a few minutes later. Recapture the Cookie header from a `youtubei/v1/...` request on music.youtube.com (see issue #6 for the full set to include).
2026-07-01 22:00:05.513 WARNING (MainThread) [music_assistant.ytmusic_free] Cookie authentication failed (Server returned HTTP 401: Unauthorized.
Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.), falling back to anonymous mode. You may need to refresh your cookie.
2026-07-01 22:00:05.514 INFO (MainThread) [music_assistant.ytmusic_free] YouTube Music (Free) initialized — anonymous mode
2026-07-01 22:00:05.514 INFO (MainThread) [music_assistant] Loaded music provider YouTube Music (Free)

Found Issue 6, but how do i format them in one string to include?

Edit: Lol would you look at that?
Did the same thing in Chrome instead of Firefox! and it worked...

Nothing you can do about the low Bitrate. Is there?

Low Quality label: That "LQ" indicator in MA is based on what MA reports back from the stream metadata, not the actual bitrate. The android_music client we use returns 128–256 kbps AAC/Opus, which MA categorizes as "low quality" regardless of the toggle. The toggle controls yt-dlp's format selector (m4a/bestaudio/best vs worstaudio/worst), with it on, you're getting the best available audio. The label is just MA being strict about what counts as "high quality" (typically lossless / hi-res).

It's the best we can get

how is this different then for instance this one: giantorth/music-assistant-yt-dlp

The other one replaces the full Music Assistant addon. While this one here is just a Plugin for the official Music Assistant.

@sproft i don’t care about the LQ Logo, i was just astonished about the 47Kbits Bitrate. You think that value is correct? you said 128kbit AAC which is what i assumed as well, therefore wondered why it only showed 47.

Edit:
Must have been that one Video which only had low Quality.

If there are music-assistant updates my add-on will be fine most of the time and you can just update music-assistant to your hearts content. The full replacement needs to be rebuild anytime a music assistant update comes out. Or you are missing out on features.

I am glad it is working fine. Could you send me the link of the video you used?