import sqlite3 import os from dataclasses import dataclass, field from typing import List, Optional @dataclass class Track: id: int title: str path: str favorite: bool rating: Optional[int] play_count: int last_played: Optional[str] playlists: List[str] = field(default_factory=list) @dataclass class Album: id: int title: str tracks: List[Track] = field(default_factory=list) @dataclass class Artist: id: str name: str albums: List[Album] = field(default_factory=list) class NavidromeDB: def __init__(self, db_path: str): if not os.path.isfile(db_path): raise FileNotFoundError(f"Navidrome DB not found: {db_path}") self.conn = sqlite3.connect(db_path) self.conn.row_factory = sqlite3.Row def close(self): self.conn.close() def get_artists(self) -> List[Artist]: artists = [] c = self.conn.cursor() c.execute("SELECT id, name FROM artist ORDER BY name") for row in c.fetchall(): artist = Artist(id=row["id"], name=row["name"]) artist.albums = self.get_albums(artist.id) artists.append(artist) return artists def get_albums(self, artist_id: str) -> List[Album]: albums = [] c = self.conn.cursor() c.execute( "SELECT id, name FROM album WHERE album_artist_id=? ORDER BY name", (artist_id,) ) for row in c.fetchall(): album = Album(id=row["id"], title=row["name"]) album.tracks = self.get_tracks(album.id) albums.append(album) return albums def get_tracks(self, album_id: int) -> List[Track]: tracks = [] c = self.conn.cursor() c.execute( "SELECT t.id, t.name AS Title, t.path, ut.favorite, ut.rating, ut.play_count AS PlayCount, ut.last_played AS LastPlayed " "FROM track t " "LEFT JOIN usertrack ut ON ut.track_id = t.id " "WHERE t.album_id=? ORDER BY t.track_number", (album_id,) ) for row in c.fetchall(): track = Track( id=row["id"], title=row["Title"], path=row["path"], favorite=bool(row["favorite"]), rating=row["rating"], play_count=row["PlayCount"] or 0, last_played=row["LastPlayed"], playlists=self.get_playlists_for_track(row["id"]) ) tracks.append(track) return tracks def get_playlists_for_track(self, track_id: int) -> List[str]: c = self.conn.cursor() c.execute( "SELECT p.name FROM playlist p " "JOIN playlisttrack pt ON pt.playlist_id=p.id " "WHERE pt.track_id=?", (track_id,) ) return [row["name"] for row in c.fetchall()] # Example usage if __name__ == "__main__": db_path = "/mnt/main/data/music/navidrome.db" navidb = NavidromeDB(db_path) try: artists = navidb.get_artists() for artist in artists: print(f"Artist: {artist.name}") for album in artist.albums: print(f" Album: {album.title}") for track in album.tracks: print(f" Track: {track.title} | Path: {track.path} | Fav: {track.favorite} | Plays: {track.play_count}") finally: navidb.close()