Local playlist-file imports¶
Open Playlist Engine can use a local playlist file as the source for the normal match, review, write, and progress pipeline. The application reads playlist metadata only. It never opens referenced audio paths, uploads audio, or sends the playlist file to an external parsing service.
Supported formats¶
| Format | Supported input |
|---|---|
| TXT | Artist - Title per line, or tab-separated title, artist, album, duration, isrc, uri, playlist fields. #PLAYLIST:Name starts or names a playlist. |
| CSV | Canonical schema and common header aliases described below. A playlist column can create multiple playlists in one file. |
| M3U / M3U8 | #EXTM3U, #PLAYLIST, and #EXTINF metadata plus ordered locations. M3U8 must be UTF-8. |
| PLS | Indexed FileN, TitleN, and LengthN entries plus Title and NumberOfEntries. |
| WPL | Windows Media Player SMIL playlists and available media attributes. |
| XSPF | Playlist title, track location, title, creator, album, duration, and identifier metadata. |
| XML | Generic <playlist> / <track> documents using attributes or child fields. |
| JSON | A single playlist object, a playlists array, an array of playlists, or a track array. Nested {"track": ...} items are accepted. |
Content detection takes precedence over a misleading extension and reports the mismatch in the preview.
Canonical CSV¶
Use this header for portable exports:
playlist,title,artist,album,duration_s,isrc,uri
Road Trip,Déjà Vu,Beyoncé,B'Day,239,USSM10603689,spotify:track:abc
Accepted aliases are case-insensitive and ignore spaces, punctuation, and underscores:
| Canonical field | Common aliases |
|---|---|
playlist |
playlist_name, playlistname, list |
title |
track, track_title, tracktitle, song, song_title, name |
artist |
artists, artist_name, track_artist, creator, performer |
album |
album_name, album_title, release |
duration_s |
duration, duration_seconds, length, duration_ms, length_ms |
isrc |
recording_isrc |
uri |
url, location, path, file, provider_uri, track_uri, link |
Durations may be seconds, milliseconds when the header says _ms, MM:SS, or
HH:MM:SS.
Preview and migration¶
- Choose Local playlist file as the source and a connected streaming provider as the target.
- Upload one supported file. The preview shows the detected format, playlist and track counts, duplicates, malformed items, unsupported local paths, and other lossy conversions.
- Select playlists and individual migratable tracks. Duplicates remain in their original order. Unsupported entries remain visible but are not selected.
- Start the migration. The target uses the same match, review, duplicate-write protection, progress, and statistics flow as provider-backed sources.
The raw upload is closed immediately after parsing. The normalized preview is stored in Postgres only until it expires or is consumed by a migration. Successful jobs delete it atomically. Failed or cancelled jobs retain it for a short retry grace, then the worker cleanup job removes it. Unused previews are deleted after the configured retention period.
Limits and configuration¶
| Environment variable | Default | Purpose |
|---|---|---|
OPE_LOCAL_IMPORT_MAX_BYTES |
10485760 |
Maximum request body, 10 MiB. |
OPE_LOCAL_IMPORT_MAX_PLAYLISTS |
100 |
Maximum playlists in one file. |
OPE_LOCAL_IMPORT_MAX_TRACKS |
5000 |
Maximum tracks across the file. |
OPE_LOCAL_IMPORT_MAX_ISSUES |
200 |
Detailed preview findings retained before a summary marker. |
OPE_LOCAL_IMPORT_SPOOL_MEMORY_BYTES |
1048576 |
Bytes kept in memory before the temporary upload stream spills to disk. |
OPE_LOCAL_IMPORT_RETENTION_S |
3600 |
Lifetime of an unused preview. |
OPE_LOCAL_IMPORT_QUEUED_RETENTION_S |
7200 |
Lease while a migration waits for or runs in the worker. |
OPE_LOCAL_IMPORT_FAILED_RETENTION_S |
900 |
Retry grace after failure or cancellation. |
The packaged nginx proxy disables request buffering for the preview endpoint and
uses the same 10 MiB default. If OPE_LOCAL_IMPORT_MAX_BYTES changes, update
client_max_body_size in frontend/nginx.conf to match.
OPE_LOCAL_IMPORT_QUEUED_RETENTION_S must remain greater than
OPE_MIGRATION_WORKER_JOB_TIMEOUT_S; an expired lease marks a stalled job failed
and removes its normalized source data.
API¶
The preview endpoint accepts the file as the raw request body so FastAPI can enforce the limit while streaming instead of buffering a multipart upload first:
curl --data-binary @road-trip.csv \
"http://localhost:8000/api/imports/preview?filename=road-trip.csv"
GET /api/imports/{id} retrieves an owner-scoped, unexpired preview.
DELETE /api/imports/{id} discards an unused preview. Arbitrary server paths are
never accepted.