Skip to content

Create Services for Playlist Export / Import with options#3387

Draft
chrisuthe wants to merge 17 commits intomusic-assistant:devfrom
chrisuthe:task/m3u-playlist-export-import
Draft

Create Services for Playlist Export / Import with options#3387
chrisuthe wants to merge 17 commits intomusic-assistant:devfrom
chrisuthe:task/m3u-playlist-export-import

Conversation

@chrisuthe
Copy link
Member

@chrisuthe chrisuthe commented Mar 13, 2026

Allows import/export of MA playlists and radio stations, separately. Export includes an option to export basic data (for URI matching) or EXTINF metadata for "Library Matching" which is an option on import. Library matching uses the EXTINF to attempt to search & match items somewhere in your library. Great for re-adding your library or transferring from one MA user to another. Also added is the ability replace a playlist track with a single call for an eventual UX of "Manual match" scenario.

I asked AI to create a quick page to test this functionality with since I don't think I'll be the person to implement the UI for it, it's available here: https://gist.github.com/chrisuthe/387180c78b8b12ae12b6659c814fa7d2 just save it locally and pop it open for testing.

New API Commands

music/playlists/export_playlist

  • Parameters: db_playlist_id, include_track_metadata (optional, default false)
  • Exports a builtin playlist as M3U8 string. When include_track_metadata=true, emits #EXTINF (artist, title, duration) and #EXTMA (ISRC, MusicBrainz Recording ID, album, podcast, authors, media type, provider domain) lines.

music/playlists/import_playlist

  • Parameters: m3u_data, library_matching (optional, default false)
  • Imports an M3U/M3U8 string as a new builtin playlist. When library_matching=true, searches all providers to resolve unmatched URIs using a tiered matching strategy:
    1. ISRC match (score 10) — exact, cross-provider
    2. MusicBrainz Recording ID match (score 10) — exact, cross-provider
    3. Fuzzy fallback — title (required) + artist (required if present) + album bonus + duration bonus + media type gate + podcast/author matching

music/playlists/replace_playlist_track

  • Parameters: db_playlist_id, position (1-based), new_uri
  • Replaces a single track in a builtin playlist by position, preserving playlist order. Enables manual remapping of unresolved tracks after import.

music/radio/export_radios

  • No parameters
  • Exports all builtin radio stations as a single M3U8 string with #EXTINF and #EXTMA metadata.

music/radio/import_radios

  • Parameters: m3u_data
  • Imports radio stations from an M3U/M3U8 string, skipping duplicates by stream URL.

M3U Format Extension

Uses a custom #EXTMA: comment line (ignored by standard M3U players) to carry structured metadata:

#EXTMA:isrc=USRC17607839,mbid=a1b2c3d4-...,album=OK Computer,media_type=track,provider=spotify
#EXTINF:240,Radiohead - Everything In Its Right Place
spotify://track/abc123

Supported fields: isrc, mbid, album, media_type, provider, podcast, authors, narrators

How Matching Works

When library_matching=true, each track whose URI can't resolve (provider not available) is searched across all available providers. Each search result is scored, and the highest-scoring result across all providers wins. If an ISRC or MusicBrainz ID matches (score 10), it returns immediately without searching further providers.

A score of 0 means "not a match" — the candidate is rejected. Any score > 0 is a valid match candidate. The highest score wins. If no candidates score > 0, the original (unresolvable) URI is kept in the playlist and counted as "unmatched" in the import log.

Factor Score Behavior
ISRC match 10 Instant win, stops searching all providers
MusicBrainz Recording ID 10 Instant win, stops searching all providers
Title match +1 Required — 0 if title doesn't match
Artist match +2 Required if present — 0 if artist doesn't match
Album match +1 Bonus, helps pick the right version of a song
Duration within 2s +2 Near-exact bonus
Duration within 5s +1 Close enough bonus (M3U truncates to int)
Podcast name match +2 Like artist, but for podcast episodes
Authors match +2 Like artist, but for audiobooks
Media type mismatch 0 Hard reject — won't match a podcast against a track

Allow callers to opt-in to resolving track metadata (artist, title,
duration) as EXTINF lines in M3U export. Defaults to False for fast
bare-URI exports; radios always include EXTINF regardless.
When importing an M3U playlist with library_matching=True, tracks whose
provider is unavailable are searched across all providers using EXTINF
metadata (artist, title, duration). Adds parse_extinf_title helper,
_match_track_by_metadata/_score_track_match to builtin provider, and
passes the optional parameter through the controller API.
Add replace_playlist_track to builtin provider and playlist controller
for in-place remapping of unresolvable tracks after import. Add scoring
integration tests for the library matching logic.
@chrisuthe chrisuthe added this to the 2.9.0 milestone Mar 13, 2026
@chrisuthe chrisuthe self-assigned this Mar 13, 2026
@chrisuthe chrisuthe marked this pull request as draft March 13, 2026 16:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant