Rockbox open source high quality audio player as a Music Player Daemon
mpris
rockbox
mpd
libadwaita
audio
rust
zig
deno
1"""Library: albums, artists, tracks, search, likes, scan."""
2
3from __future__ import annotations
4
5from ..transport import HttpTransport
6from ..types import Album, Artist, SearchResults, Track
7from ._fragments import ALBUM_FIELDS, ARTIST_FIELDS, TRACK_FIELDS
8
9
10class LibraryApi:
11 def __init__(self, http: HttpTransport) -> None:
12 self._http = http
13
14 # --- albums ---------------------------------------------------------
15
16 async def albums(self) -> list[Album]:
17 data = await self._http.execute(
18 f"{ALBUM_FIELDS} query Albums "
19 "{ albums { ...AlbumFields tracks { id title path length albumArt } } }"
20 )
21 return [Album.model_validate(a) for a in data.get("albums", [])]
22
23 async def album(self, id: str) -> Album | None:
24 data = await self._http.execute(
25 f"{TRACK_FIELDS} {ALBUM_FIELDS} "
26 "query Album($id: String!) "
27 "{ album(id: $id) { ...AlbumFields tracks { ...TrackFields } } }",
28 {"id": id},
29 )
30 raw = data.get("album")
31 return Album.model_validate(raw) if raw is not None else None
32
33 async def liked_albums(self) -> list[Album]:
34 data = await self._http.execute(
35 f"{ALBUM_FIELDS} query LikedAlbums {{ likedAlbums {{ ...AlbumFields }} }}"
36 )
37 return [Album.model_validate(a) for a in data.get("likedAlbums", [])]
38
39 async def like_album(self, id: str) -> None:
40 await self._http.execute(
41 "mutation LikeAlbum($id: String!) { likeAlbum(id: $id) }", {"id": id}
42 )
43
44 async def unlike_album(self, id: str) -> None:
45 await self._http.execute(
46 "mutation UnlikeAlbum($id: String!) { unlikeAlbum(id: $id) }", {"id": id}
47 )
48
49 # --- artists --------------------------------------------------------
50
51 async def artists(self) -> list[Artist]:
52 data = await self._http.execute(
53 f"{ARTIST_FIELDS} query Artists "
54 "{ artists { ...ArtistFields albums { id title albumArt year } } }"
55 )
56 return [Artist.model_validate(a) for a in data.get("artists", [])]
57
58 async def artist(self, id: str) -> Artist | None:
59 data = await self._http.execute(
60 f"{ARTIST_FIELDS} {TRACK_FIELDS} "
61 "query Artist($id: String!) { artist(id: $id) { "
62 "...ArtistFields "
63 "albums { id title albumArt year yearString md5 artistId "
64 "tracks { id title path length } } "
65 "tracks { ...TrackFields } } }",
66 {"id": id},
67 )
68 raw = data.get("artist")
69 return Artist.model_validate(raw) if raw is not None else None
70
71 # --- tracks ---------------------------------------------------------
72
73 async def tracks(self) -> list[Track]:
74 data = await self._http.execute(
75 f"{TRACK_FIELDS} query Tracks {{ tracks {{ ...TrackFields }} }}"
76 )
77 return [Track.model_validate(t) for t in data.get("tracks", [])]
78
79 async def track(self, id: str) -> Track | None:
80 data = await self._http.execute(
81 f"{TRACK_FIELDS} query Track($id: String!) "
82 "{ track(id: $id) { ...TrackFields } }",
83 {"id": id},
84 )
85 raw = data.get("track")
86 return Track.model_validate(raw) if raw is not None else None
87
88 async def liked_tracks(self) -> list[Track]:
89 data = await self._http.execute(
90 f"{TRACK_FIELDS} query LikedTracks {{ likedTracks {{ ...TrackFields }} }}"
91 )
92 return [Track.model_validate(t) for t in data.get("likedTracks", [])]
93
94 async def like_track(self, id: str) -> None:
95 await self._http.execute(
96 "mutation LikeTrack($id: String!) { likeTrack(id: $id) }", {"id": id}
97 )
98
99 async def unlike_track(self, id: str) -> None:
100 await self._http.execute(
101 "mutation UnlikeTrack($id: String!) { unlikeTrack(id: $id) }", {"id": id}
102 )
103
104 # --- search ---------------------------------------------------------
105
106 async def search(self, term: str) -> SearchResults:
107 data = await self._http.execute(
108 f"{TRACK_FIELDS} {ALBUM_FIELDS} {ARTIST_FIELDS} "
109 "query Search($term: String!) { search(term: $term) { "
110 "artists { ...ArtistFields } "
111 "albums { ...AlbumFields } "
112 "tracks { ...TrackFields } "
113 "likedTracks { ...TrackFields } "
114 "likedAlbums { ...AlbumFields } } }",
115 {"term": term},
116 )
117 return SearchResults.model_validate(data.get("search") or {})
118
119 # --- maintenance ----------------------------------------------------
120
121 async def scan(self) -> None:
122 """Trigger a library rescan."""
123 await self._http.execute("mutation ScanLibrary { scanLibrary }")