This works great! So, so nice to just be able to directly work with the database.
Here is the key new script, that dumps out all the playlists to plain m3us (just relative paths) in the same directory.
I am also very happy to have this script as it means there is now little risk in using Strawberry - I can make my playlists there and if needbe, dump them back out to the plain m3us I use with ncmpcpp.
#!/usr/bin/env python
# Tested with
# Strawberry 1.0.17
# macOS 12.2 (Monterey)
# Python 3.11.2
# Inspired by code from @TheBlackKoala posted at
# https://github.com/clementine-player/Clementine/issues/6214
import os
import sqlite3
import urllib.parse
# Update with the path to your database (or a copy of it)
DATABASE_PATH = os.path.join(os.environ["HOME"], "Library",
"Application Support", "Strawberry", "Strawberry",
"strawberry.db")
# Update with a root to strip from absolute paths
LIBRARY_ROOT = os.path.join(os.sep, "Volumes", "BigDrive", "music_library")
def _name_for_playlist(c, playlist_id):
c.execute("SELECT name FROM playlists WHERE rowid=?", playlist_id)
return c.fetchone()[0]
def _url_to_path(url):
return urllib.parse.unquote(
url.removeprefix("file://")).removeprefix(LIBRARY_ROOT).removeprefix(
os.sep)
def _paths_for_playlist(c, playlist_id):
c.execute(
"SELECT s.url FROM playlist_items AS p JOIN songs AS s ON p.collection_id = s.rowid WHERE playlist = ?",
playlist_id)
return [_url_to_path(item[0]) for item in c.fetchall()]
def _write_plain_m3u_from_paths(name, paths):
with open(f"{name}.m3u", "w") as m3u_file:
for path in paths:
m3u_file.write(f"{path}\n")
def main():
connection = sqlite3.connect(DATABASE_PATH)
cursor = connection.cursor()
cursor.execute("SELECT DISTINCT playlist FROM playlist_items")
for playlist_id in cursor.fetchall():
name = _name_for_playlist(cursor, playlist_id)
paths = _paths_for_playlist(cursor, playlist_id)
_write_plain_m3u_from_paths(name, paths)
if __name__ == "__main__":
main()