@lucaperret/tidal-cli 1.1.2 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,6 +8,7 @@
8
8
 
9
9
  [![npm](https://img.shields.io/npm/v/@lucaperret/tidal-cli)](https://www.npmjs.com/package/@lucaperret/tidal-cli)
10
10
  [![CI](https://img.shields.io/github/actions/workflow/status/lucaperret/tidal-cli/ci.yml?label=tests)](https://github.com/lucaperret/tidal-cli/actions)
11
+ [![smithery badge](https://smithery.ai/badge/lucaperret/tidal)](https://smithery.ai/servers/lucaperret/tidal)
11
12
  [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
12
13
  [![Node](https://img.shields.io/badge/node-%3E%3D20-green.svg)](https://nodejs.org)
13
14
 
@@ -148,9 +149,20 @@ tidal-cli --json playlist list
148
149
  tidal-cli --json artist similar 8992
149
150
  ```
150
151
 
152
+ ## MCP Server (Claude Integration)
153
+
154
+ tidal-cli is available as a remote MCP server for [Claude Desktop](https://claude.ai), [Smithery](https://smithery.ai/servers/lucaperret/tidal), and any MCP-compatible client.
155
+
156
+ **Connect in Claude Desktop:**
157
+ 1. Settings → Connectors → Add custom connector
158
+ 2. Enter: `https://tidal-cli.lucaperret.ch/api/mcp`
159
+ 3. Click "Connect" → log in to Tidal → done
160
+
161
+ 32 tools with OAuth authentication, safety annotations, and 3 prompt templates.
162
+
151
163
  ## Agent Automation
152
164
 
153
- tidal-cli is available as an [OpenClaw](https://openclaw.ai) skill on [ClawHub](https://clawhub.ai/lucaperret/tidal-cli). Install it for your AI agent:
165
+ tidal-cli is also available as an [OpenClaw](https://openclaw.ai) skill on [ClawHub](https://clawhub.ai/lucaperret/tidal-cli). Install it for your AI agent:
154
166
 
155
167
  ```bash
156
168
  clawhub install tidal-cli
package/dist/album.d.ts CHANGED
@@ -1,2 +1,6 @@
1
+ import type { AlbumInfo, AlbumResult } from './types';
2
+ export type { AlbumInfo, AlbumResult };
3
+ export declare function getAlbumInfoData(albumId: string, client: any, countryCode: string): Promise<AlbumInfo>;
1
4
  export declare function getAlbumInfo(albumId: string, json: boolean): Promise<void>;
5
+ export declare function getAlbumByBarcodeData(barcode: string, client: any, countryCode: string): Promise<AlbumResult[]>;
2
6
  export declare function getAlbumByBarcode(barcode: string, json: boolean): Promise<void>;
package/dist/album.js CHANGED
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getAlbumInfoData = getAlbumInfoData;
3
4
  exports.getAlbumInfo = getAlbumInfo;
5
+ exports.getAlbumByBarcodeData = getAlbumByBarcodeData;
4
6
  exports.getAlbumByBarcode = getAlbumByBarcode;
5
7
  const auth_1 = require("./auth");
6
8
  function formatDuration(isoDuration) {
@@ -14,32 +16,29 @@ function formatDuration(isoDuration) {
14
16
  const s = (match[3] ?? '0').padStart(2, '0');
15
17
  return `${h}${m}:${s}`;
16
18
  }
17
- async function getAlbumInfo(albumId, json) {
18
- const client = await (0, auth_1.getApiClient)();
19
+ async function getAlbumInfoData(albumId, client, countryCode) {
19
20
  const { data, error } = await client.GET('/albums/{id}', {
20
21
  params: {
21
22
  path: { id: albumId },
22
23
  query: {
23
- countryCode: await (0, auth_1.getCountryCode)(),
24
+ countryCode,
24
25
  include: ['artists', 'coverArt'],
25
26
  },
26
27
  },
27
28
  });
28
29
  if (error || !data) {
29
- console.error(`Error: Failed to get album info — ${JSON.stringify(error)}`);
30
- process.exit(1);
30
+ throw new Error(`Failed to get album info — ${JSON.stringify(error)}`);
31
31
  }
32
32
  const attrs = data.data?.attributes ?? {};
33
33
  const included = data.included ?? [];
34
34
  const artists = included
35
35
  .filter((item) => item.type === 'artists')
36
36
  .map((item) => item.attributes?.name ?? item.id);
37
- // Get cover art from included artworks
38
37
  const artwork = included.find((item) => item.type === 'artworks');
39
38
  const files = artwork?.attributes?.files ?? [];
40
39
  const preferred = files.find((f) => f.meta?.width === 640) ?? files[0];
41
40
  const coverUrl = preferred?.href;
42
- const result = {
41
+ return {
43
42
  id: albumId,
44
43
  title: attrs.title ?? 'Unknown',
45
44
  artists,
@@ -52,47 +51,56 @@ async function getAlbumInfo(albumId, json) {
52
51
  barcodeId: attrs.barcodeId,
53
52
  coverUrl,
54
53
  };
55
- if (json) {
56
- console.log(JSON.stringify(result, null, 2));
57
- return;
58
- }
59
- console.log(`\nAlbum: [${result.id}] ${result.title}`);
60
- if (result.artists.length > 0)
61
- console.log(` Artists: ${result.artists.join(', ')}`);
62
- if (result.albumType)
63
- console.log(` Type: ${result.albumType}`);
64
- if (result.releaseDate)
65
- console.log(` Release Date: ${result.releaseDate}`);
66
- if (result.numberOfItems !== undefined)
67
- console.log(` Tracks: ${result.numberOfItems}`);
68
- if (result.duration)
69
- console.log(` Duration: ${result.duration}`);
70
- if (result.popularity !== undefined)
71
- console.log(` Popularity: ${result.popularity}`);
72
- if (result.explicit !== undefined)
73
- console.log(` Explicit: ${result.explicit}`);
74
- if (result.barcodeId)
75
- console.log(` Barcode: ${result.barcodeId}`);
76
- if (result.coverUrl)
77
- console.log(` Cover: ${result.coverUrl}`);
78
- console.log();
79
54
  }
80
- async function getAlbumByBarcode(barcode, json) {
55
+ async function getAlbumInfo(albumId, json) {
81
56
  const client = await (0, auth_1.getApiClient)();
57
+ const countryCode = await (0, auth_1.getCountryCode)();
58
+ try {
59
+ const result = await getAlbumInfoData(albumId, client, countryCode);
60
+ if (json) {
61
+ console.log(JSON.stringify(result, null, 2));
62
+ return;
63
+ }
64
+ console.log(`\nAlbum: [${result.id}] ${result.title}`);
65
+ if (result.artists.length > 0)
66
+ console.log(` Artists: ${result.artists.join(', ')}`);
67
+ if (result.albumType)
68
+ console.log(` Type: ${result.albumType}`);
69
+ if (result.releaseDate)
70
+ console.log(` Release Date: ${result.releaseDate}`);
71
+ if (result.numberOfItems !== undefined)
72
+ console.log(` Tracks: ${result.numberOfItems}`);
73
+ if (result.duration)
74
+ console.log(` Duration: ${result.duration}`);
75
+ if (result.popularity !== undefined)
76
+ console.log(` Popularity: ${result.popularity}`);
77
+ if (result.explicit !== undefined)
78
+ console.log(` Explicit: ${result.explicit}`);
79
+ if (result.barcodeId)
80
+ console.log(` Barcode: ${result.barcodeId}`);
81
+ if (result.coverUrl)
82
+ console.log(` Cover: ${result.coverUrl}`);
83
+ console.log();
84
+ }
85
+ catch (err) {
86
+ console.error(`Error: ${err.message}`);
87
+ process.exit(1);
88
+ }
89
+ }
90
+ async function getAlbumByBarcodeData(barcode, client, countryCode) {
82
91
  const { data, error } = await client.GET('/albums', {
83
92
  params: {
84
93
  query: {
85
- countryCode: await (0, auth_1.getCountryCode)(),
94
+ countryCode,
86
95
  'filter[barcodeId]': [barcode],
87
96
  },
88
97
  },
89
98
  });
90
99
  if (error || !data) {
91
- console.error(`Error: Failed to get album by barcode — ${JSON.stringify(error)}`);
92
- process.exit(1);
100
+ throw new Error(`Failed to get album by barcode — ${JSON.stringify(error)}`);
93
101
  }
94
102
  const items = data.data ?? [];
95
- const albums = items.map((item) => {
103
+ return items.map((item) => {
96
104
  const attrs = item.attributes;
97
105
  return {
98
106
  id: item.id,
@@ -104,21 +112,32 @@ async function getAlbumByBarcode(barcode, json) {
104
112
  barcodeId: attrs?.barcodeId,
105
113
  };
106
114
  });
107
- if (json) {
108
- console.log(JSON.stringify(albums, null, 2));
109
- return;
110
- }
111
- if (albums.length === 0) {
112
- console.log(`No albums found for barcode ${barcode}.`);
113
- return;
115
+ }
116
+ async function getAlbumByBarcode(barcode, json) {
117
+ const client = await (0, auth_1.getApiClient)();
118
+ const countryCode = await (0, auth_1.getCountryCode)();
119
+ try {
120
+ const albums = await getAlbumByBarcodeData(barcode, client, countryCode);
121
+ if (json) {
122
+ console.log(JSON.stringify(albums, null, 2));
123
+ return;
124
+ }
125
+ if (albums.length === 0) {
126
+ console.log(`No albums found for barcode ${barcode}.`);
127
+ return;
128
+ }
129
+ console.log(`\nAlbums matching barcode ${barcode}:\n`);
130
+ for (const a of albums) {
131
+ const extras = [a.albumType, a.releaseDate, a.numberOfItems !== undefined ? `${a.numberOfItems} tracks` : undefined]
132
+ .filter(Boolean)
133
+ .join(', ');
134
+ console.log(` [${a.id}] ${a.title}${extras ? ` (${extras})` : ''}`);
135
+ }
136
+ console.log();
114
137
  }
115
- console.log(`\nAlbums matching barcode ${barcode}:\n`);
116
- for (const a of albums) {
117
- const extras = [a.albumType, a.releaseDate, a.numberOfItems !== undefined ? `${a.numberOfItems} tracks` : undefined]
118
- .filter(Boolean)
119
- .join(', ');
120
- console.log(` [${a.id}] ${a.title}${extras ? ` (${extras})` : ''}`);
138
+ catch (err) {
139
+ console.error(`Error: ${err.message}`);
140
+ process.exit(1);
121
141
  }
122
- console.log();
123
142
  }
124
143
  //# sourceMappingURL=album.js.map
package/dist/artist.d.ts CHANGED
@@ -1,5 +1,12 @@
1
+ import type { ArtistInfo, ArtistTrack, ArtistAlbum, SimilarArtist, RadioPlaylist } from './types';
2
+ export type { ArtistInfo, ArtistTrack, ArtistAlbum, SimilarArtist };
3
+ export declare function getArtistInfoData(artistId: string, client: any, countryCode: string): Promise<ArtistInfo>;
1
4
  export declare function getArtistInfo(artistId: string, json: boolean): Promise<void>;
5
+ export declare function getArtistRadioData(artistId: string, client: any, countryCode: string): Promise<RadioPlaylist[]>;
2
6
  export declare function getArtistRadio(artistId: string, json: boolean): Promise<void>;
7
+ export declare function getArtistTracksData(artistId: string, client: any, countryCode: string): Promise<ArtistTrack[]>;
3
8
  export declare function getArtistTracks(artistId: string, json: boolean): Promise<void>;
9
+ export declare function getArtistAlbumsData(artistId: string, client: any, countryCode: string): Promise<ArtistAlbum[]>;
4
10
  export declare function getArtistAlbums(artistId: string, json: boolean): Promise<void>;
11
+ export declare function getSimilarArtistsData(artistId: string, client: any, countryCode: string): Promise<SimilarArtist[]>;
5
12
  export declare function getSimilarArtists(artistId: string, json: boolean): Promise<void>;
package/dist/artist.js CHANGED
@@ -1,9 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getArtistInfoData = getArtistInfoData;
3
4
  exports.getArtistInfo = getArtistInfo;
5
+ exports.getArtistRadioData = getArtistRadioData;
4
6
  exports.getArtistRadio = getArtistRadio;
7
+ exports.getArtistTracksData = getArtistTracksData;
5
8
  exports.getArtistTracks = getArtistTracks;
9
+ exports.getArtistAlbumsData = getArtistAlbumsData;
6
10
  exports.getArtistAlbums = getArtistAlbums;
11
+ exports.getSimilarArtistsData = getSimilarArtistsData;
7
12
  exports.getSimilarArtists = getSimilarArtists;
8
13
  const auth_1 = require("./auth");
9
14
  function formatDuration(isoDuration) {
@@ -17,64 +22,70 @@ function formatDuration(isoDuration) {
17
22
  const s = (match[3] ?? '0').padStart(2, '0');
18
23
  return `${h}${m}:${s}`;
19
24
  }
20
- async function getArtistInfo(artistId, json) {
21
- const client = await (0, auth_1.getApiClient)();
25
+ async function getArtistInfoData(artistId, client, countryCode) {
22
26
  const { data, error } = await client.GET('/artists/{id}', {
23
27
  params: {
24
28
  path: { id: artistId },
25
29
  query: {
26
- countryCode: await (0, auth_1.getCountryCode)(),
30
+ countryCode,
27
31
  include: ['biography'],
28
32
  },
29
33
  },
30
34
  });
31
35
  if (error || !data) {
32
- console.error(`Error: Failed to get artist info — ${JSON.stringify(error)}`);
33
- process.exit(1);
36
+ throw new Error(`Failed to get artist info — ${JSON.stringify(error)}`);
34
37
  }
35
38
  const attrs = data.data?.attributes ?? {};
36
39
  const included = data.included ?? [];
37
40
  const biographyItem = included.find((item) => item.type === 'artistBiographies');
38
41
  const biographyText = biographyItem?.attributes?.text ?? attrs.biography?.text ?? attrs.biography;
39
- const result = {
42
+ return {
40
43
  id: artistId,
41
44
  name: attrs.name ?? 'Unknown',
42
45
  popularity: attrs.popularity,
43
46
  handle: attrs.handle,
44
47
  biography: biographyText,
45
48
  };
46
- if (json) {
47
- console.log(JSON.stringify(result, null, 2));
48
- return;
49
- }
50
- console.log(`\nArtist: [${result.id}] ${result.name}`);
51
- if (result.handle)
52
- console.log(` Handle: ${result.handle}`);
53
- if (result.popularity !== undefined)
54
- console.log(` Popularity: ${result.popularity}`);
55
- if (result.biography)
56
- console.log(` Biography: ${result.biography}`);
57
- console.log();
58
49
  }
59
- async function getArtistRadio(artistId, json) {
50
+ async function getArtistInfo(artistId, json) {
60
51
  const client = await (0, auth_1.getApiClient)();
52
+ const countryCode = await (0, auth_1.getCountryCode)();
53
+ try {
54
+ const result = await getArtistInfoData(artistId, client, countryCode);
55
+ if (json) {
56
+ console.log(JSON.stringify(result, null, 2));
57
+ return;
58
+ }
59
+ console.log(`\nArtist: [${result.id}] ${result.name}`);
60
+ if (result.handle)
61
+ console.log(` Handle: ${result.handle}`);
62
+ if (result.popularity !== undefined)
63
+ console.log(` Popularity: ${result.popularity}`);
64
+ if (result.biography)
65
+ console.log(` Biography: ${result.biography}`);
66
+ console.log();
67
+ }
68
+ catch (err) {
69
+ console.error(`Error: ${err.message}`);
70
+ process.exit(1);
71
+ }
72
+ }
73
+ async function getArtistRadioData(artistId, client, countryCode) {
61
74
  const { data, error } = await client.GET('/artists/{id}/relationships/radio', {
62
75
  params: {
63
76
  path: { id: artistId },
64
77
  query: {
65
- countryCode: await (0, auth_1.getCountryCode)(),
78
+ countryCode,
66
79
  include: ['radio'],
67
80
  },
68
81
  },
69
82
  });
70
83
  if (error || !data) {
71
- console.error(`Error: Failed to get artist radio — ${JSON.stringify(error)}`);
72
- process.exit(1);
84
+ throw new Error(`Failed to get artist radio — ${JSON.stringify(error)}`);
73
85
  }
74
- // Radio returns playlists (mix playlists), not individual tracks
75
86
  const radioData = data.data ?? [];
76
87
  const included = data.included ?? [];
77
- const playlists = radioData.map((item) => {
88
+ return radioData.map((item) => {
78
89
  const incl = included.find((i) => i.id === item.id && i.type === 'playlists');
79
90
  const attrs = incl?.attributes ?? {};
80
91
  return {
@@ -82,83 +93,100 @@ async function getArtistRadio(artistId, json) {
82
93
  type: item.type,
83
94
  name: attrs.name,
84
95
  numberOfItems: attrs.numberOfItems,
85
- description: attrs.description,
86
96
  };
87
97
  });
88
- if (json) {
89
- console.log(JSON.stringify(playlists, null, 2));
90
- return;
91
- }
92
- if (playlists.length === 0) {
93
- console.log(`No radio found for artist ${artistId}.`);
94
- return;
98
+ }
99
+ async function getArtistRadio(artistId, json) {
100
+ const client = await (0, auth_1.getApiClient)();
101
+ const countryCode = await (0, auth_1.getCountryCode)();
102
+ try {
103
+ const playlists = await getArtistRadioData(artistId, client, countryCode);
104
+ if (json) {
105
+ console.log(JSON.stringify(playlists, null, 2));
106
+ return;
107
+ }
108
+ if (playlists.length === 0) {
109
+ console.log(`No radio found for artist ${artistId}.`);
110
+ return;
111
+ }
112
+ console.log(`\nRadio for artist ${artistId}:\n`);
113
+ for (const p of playlists) {
114
+ console.log(` [${p.id}] ${p.name ?? 'Radio Mix'}${p.numberOfItems ? ` (${p.numberOfItems} tracks)` : ''}`);
115
+ }
116
+ console.log();
95
117
  }
96
- console.log(`\nRadio for artist ${artistId}:\n`);
97
- for (const p of playlists) {
98
- console.log(` [${p.id}] ${p.name ?? 'Radio Mix'}${p.numberOfItems ? ` (${p.numberOfItems} tracks)` : ''}`);
118
+ catch (err) {
119
+ console.error(`Error: ${err.message}`);
120
+ process.exit(1);
99
121
  }
100
- console.log();
101
122
  }
102
- async function getArtistTracks(artistId, json) {
103
- const client = await (0, auth_1.getApiClient)();
123
+ async function getArtistTracksData(artistId, client, countryCode) {
104
124
  const { data, error } = await client.GET('/artists/{id}/relationships/tracks', {
105
125
  params: {
106
126
  path: { id: artistId },
107
127
  query: {
108
- countryCode: await (0, auth_1.getCountryCode)(),
128
+ countryCode,
109
129
  'collapseBy': 'FINGERPRINT',
110
130
  include: ['tracks'],
111
131
  },
112
132
  },
113
133
  });
114
134
  if (error || !data) {
115
- console.error(`Error: Failed to get artist tracks — ${JSON.stringify(error)}`);
116
- process.exit(1);
135
+ throw new Error(`Failed to get artist tracks — ${JSON.stringify(error)}`);
117
136
  }
118
137
  const included = data.included ?? [];
119
- const tracks = included
138
+ return included
120
139
  .filter((item) => item.type === 'tracks')
121
140
  .map((item) => {
122
141
  const attrs = item.attributes;
123
142
  return {
124
143
  id: item.id,
125
144
  title: attrs?.title ?? 'Unknown',
126
- duration: attrs?.duration,
145
+ duration: formatDuration(attrs?.duration),
127
146
  isrc: attrs?.isrc,
128
147
  popularity: attrs?.popularity,
129
148
  };
130
149
  });
131
- if (json) {
132
- console.log(JSON.stringify(tracks, null, 2));
133
- return;
134
- }
135
- if (tracks.length === 0) {
136
- console.log(`No tracks found for artist ${artistId}.`);
137
- return;
150
+ }
151
+ async function getArtistTracks(artistId, json) {
152
+ const client = await (0, auth_1.getApiClient)();
153
+ const countryCode = await (0, auth_1.getCountryCode)();
154
+ try {
155
+ const tracks = await getArtistTracksData(artistId, client, countryCode);
156
+ if (json) {
157
+ console.log(JSON.stringify(tracks, null, 2));
158
+ return;
159
+ }
160
+ if (tracks.length === 0) {
161
+ console.log(`No tracks found for artist ${artistId}.`);
162
+ return;
163
+ }
164
+ console.log(`\nTracks for artist ${artistId}:\n`);
165
+ for (const t of tracks) {
166
+ console.log(` [${t.id}] ${t.title}${t.popularity !== undefined ? ` (popularity: ${t.popularity})` : ''}`);
167
+ }
168
+ console.log();
138
169
  }
139
- console.log(`\nTracks for artist ${artistId}:\n`);
140
- for (const t of tracks) {
141
- console.log(` [${t.id}] ${t.title}${t.popularity !== undefined ? ` (popularity: ${t.popularity})` : ''}`);
170
+ catch (err) {
171
+ console.error(`Error: ${err.message}`);
172
+ process.exit(1);
142
173
  }
143
- console.log();
144
174
  }
145
- async function getArtistAlbums(artistId, json) {
146
- const client = await (0, auth_1.getApiClient)();
175
+ async function getArtistAlbumsData(artistId, client, countryCode) {
147
176
  const { data, error } = await client.GET('/artists/{id}/relationships/albums', {
148
177
  params: {
149
178
  path: { id: artistId },
150
179
  query: {
151
- countryCode: await (0, auth_1.getCountryCode)(),
180
+ countryCode,
152
181
  include: ['albums'],
153
182
  },
154
183
  },
155
184
  });
156
185
  if (error || !data) {
157
- console.error(`Error: Failed to get artist albums — ${JSON.stringify(error)}`);
158
- process.exit(1);
186
+ throw new Error(`Failed to get artist albums — ${JSON.stringify(error)}`);
159
187
  }
160
188
  const included = data.included ?? [];
161
- const albums = included
189
+ return included
162
190
  .filter((item) => item.type === 'albums')
163
191
  .map((item) => {
164
192
  const attrs = item.attributes;
@@ -170,40 +198,49 @@ async function getArtistAlbums(artistId, json) {
170
198
  numberOfItems: attrs?.numberOfItems,
171
199
  };
172
200
  });
173
- if (json) {
174
- console.log(JSON.stringify(albums, null, 2));
175
- return;
176
- }
177
- if (albums.length === 0) {
178
- console.log(`No albums found for artist ${artistId}.`);
179
- return;
201
+ }
202
+ async function getArtistAlbums(artistId, json) {
203
+ const client = await (0, auth_1.getApiClient)();
204
+ const countryCode = await (0, auth_1.getCountryCode)();
205
+ try {
206
+ const albums = await getArtistAlbumsData(artistId, client, countryCode);
207
+ if (json) {
208
+ console.log(JSON.stringify(albums, null, 2));
209
+ return;
210
+ }
211
+ if (albums.length === 0) {
212
+ console.log(`No albums found for artist ${artistId}.`);
213
+ return;
214
+ }
215
+ console.log(`\nAlbums for artist ${artistId}:\n`);
216
+ for (const a of albums) {
217
+ const extras = [a.albumType, a.releaseDate, a.numberOfItems !== undefined ? `${a.numberOfItems} tracks` : undefined]
218
+ .filter(Boolean)
219
+ .join(', ');
220
+ console.log(` [${a.id}] ${a.title}${extras ? ` (${extras})` : ''}`);
221
+ }
222
+ console.log();
180
223
  }
181
- console.log(`\nAlbums for artist ${artistId}:\n`);
182
- for (const a of albums) {
183
- const extras = [a.albumType, a.releaseDate, a.numberOfItems !== undefined ? `${a.numberOfItems} tracks` : undefined]
184
- .filter(Boolean)
185
- .join(', ');
186
- console.log(` [${a.id}] ${a.title}${extras ? ` (${extras})` : ''}`);
224
+ catch (err) {
225
+ console.error(`Error: ${err.message}`);
226
+ process.exit(1);
187
227
  }
188
- console.log();
189
228
  }
190
- async function getSimilarArtists(artistId, json) {
191
- const client = await (0, auth_1.getApiClient)();
229
+ async function getSimilarArtistsData(artistId, client, countryCode) {
192
230
  const { data, error } = await client.GET('/artists/{id}/relationships/similarArtists', {
193
231
  params: {
194
232
  path: { id: artistId },
195
233
  query: {
196
- countryCode: await (0, auth_1.getCountryCode)(),
234
+ countryCode,
197
235
  include: ['similarArtists'],
198
236
  },
199
237
  },
200
238
  });
201
239
  if (error || !data) {
202
- console.error(`Error: Failed to get similar artists — ${JSON.stringify(error)}`);
203
- process.exit(1);
240
+ throw new Error(`Failed to get similar artists — ${JSON.stringify(error)}`);
204
241
  }
205
242
  const included = data.included ?? [];
206
- const artists = included
243
+ return included
207
244
  .filter((item) => item.type === 'artists')
208
245
  .map((item) => {
209
246
  const attrs = item.attributes;
@@ -213,18 +250,29 @@ async function getSimilarArtists(artistId, json) {
213
250
  popularity: attrs?.popularity,
214
251
  };
215
252
  });
216
- if (json) {
217
- console.log(JSON.stringify(artists, null, 2));
218
- return;
219
- }
220
- if (artists.length === 0) {
221
- console.log(`No similar artists found for artist ${artistId}.`);
222
- return;
253
+ }
254
+ async function getSimilarArtists(artistId, json) {
255
+ const client = await (0, auth_1.getApiClient)();
256
+ const countryCode = await (0, auth_1.getCountryCode)();
257
+ try {
258
+ const artists = await getSimilarArtistsData(artistId, client, countryCode);
259
+ if (json) {
260
+ console.log(JSON.stringify(artists, null, 2));
261
+ return;
262
+ }
263
+ if (artists.length === 0) {
264
+ console.log(`No similar artists found for artist ${artistId}.`);
265
+ return;
266
+ }
267
+ console.log(`\nSimilar artists to ${artistId}:\n`);
268
+ for (const a of artists) {
269
+ console.log(` [${a.id}] ${a.name}${a.popularity !== undefined ? ` (popularity: ${a.popularity})` : ''}`);
270
+ }
271
+ console.log();
223
272
  }
224
- console.log(`\nSimilar artists to ${artistId}:\n`);
225
- for (const a of artists) {
226
- console.log(` [${a.id}] ${a.name}${a.popularity !== undefined ? ` (popularity: ${a.popularity})` : ''}`);
273
+ catch (err) {
274
+ console.error(`Error: ${err.message}`);
275
+ process.exit(1);
227
276
  }
228
- console.log();
229
277
  }
230
278
  //# sourceMappingURL=artist.js.map
package/dist/history.d.ts CHANGED
@@ -1,3 +1,4 @@
1
- type RecentType = 'tracks' | 'albums' | 'artists';
1
+ import type { RecentItem, RecentType } from './types';
2
+ export type { RecentItem, RecentType };
3
+ export declare function getRecentlyAddedData(type: RecentType, client: any, countryCode: string): Promise<RecentItem[]>;
2
4
  export declare function getRecentlyAdded(type: RecentType, json: boolean): Promise<void>;
3
- export {};
package/dist/history.js CHANGED
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getRecentlyAddedData = getRecentlyAddedData;
3
4
  exports.getRecentlyAdded = getRecentlyAdded;
4
5
  const auth_1 = require("./auth");
5
6
  const endpointMap = {
@@ -12,9 +13,7 @@ const includeTypeMap = {
12
13
  albums: 'albums',
13
14
  artists: 'artists',
14
15
  };
15
- async function getRecentlyAdded(type, json) {
16
- const client = await (0, auth_1.getApiClient)();
17
- const countryCode = await (0, auth_1.getCountryCode)();
16
+ async function getRecentlyAddedData(type, client, countryCode) {
18
17
  const { data, error } = await client.GET(endpointMap[type], {
19
18
  params: {
20
19
  path: { id: 'me' },
@@ -26,8 +25,7 @@ async function getRecentlyAdded(type, json) {
26
25
  },
27
26
  });
28
27
  if (error || !data) {
29
- console.error(`Error: Failed to get recently added ${type} — ${JSON.stringify(error)}`);
30
- process.exit(1);
28
+ throw new Error(`Failed to get recently added ${type} — ${JSON.stringify(error)}`);
31
29
  }
32
30
  const included = data.included ?? [];
33
31
  const items = included
@@ -51,19 +49,31 @@ async function getRecentlyAdded(type, json) {
51
49
  }
52
50
  }
53
51
  }
54
- if (json) {
55
- console.log(JSON.stringify(items, null, 2));
56
- return;
57
- }
58
- if (items.length === 0) {
59
- console.log(`No recently added ${type} found.`);
60
- return;
52
+ return items;
53
+ }
54
+ async function getRecentlyAdded(type, json) {
55
+ const client = await (0, auth_1.getApiClient)();
56
+ const countryCode = await (0, auth_1.getCountryCode)();
57
+ try {
58
+ const items = await getRecentlyAddedData(type, client, countryCode);
59
+ if (json) {
60
+ console.log(JSON.stringify(items, null, 2));
61
+ return;
62
+ }
63
+ if (items.length === 0) {
64
+ console.log(`No recently added ${type} found.`);
65
+ return;
66
+ }
67
+ console.log(`\nRecently added ${type}:\n`);
68
+ for (const item of items) {
69
+ const date = item.addedAt ? ` (added: ${item.addedAt})` : '';
70
+ console.log(` [${item.id}] ${item.name}${date}`);
71
+ }
72
+ console.log();
61
73
  }
62
- console.log(`\nRecently added ${type}:\n`);
63
- for (const item of items) {
64
- const date = item.addedAt ? ` (added: ${item.addedAt})` : '';
65
- console.log(` [${item.id}] ${item.name}${date}`);
74
+ catch (err) {
75
+ console.error(`Error: ${err.message}`);
76
+ process.exit(1);
66
77
  }
67
- console.log();
68
78
  }
69
79
  //# sourceMappingURL=history.js.map