@lucaperret/tidal-cli 1.1.2 → 1.2.3

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/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
package/dist/index.js CHANGED
@@ -18,8 +18,22 @@ const artist_1 = require("./artist");
18
18
  const track_1 = require("./track");
19
19
  const album_1 = require("./album");
20
20
  const recommend_1 = require("./recommend");
21
+ const mix_1 = require("./mix");
21
22
  const user_1 = require("./user");
22
23
  const history_1 = require("./history");
24
+ const search_history_1 = require("./search-history");
25
+ const saved_1 = require("./saved");
26
+ const share_1 = require("./share");
27
+ const MIX_CATEGORIES = ['daily', 'discovery', 'new-release', 'offline'];
28
+ function parseMixCategory(value) {
29
+ if (!value)
30
+ return undefined;
31
+ if (!MIX_CATEGORIES.includes(value)) {
32
+ console.error(`Error: invalid mix category "${value}". Use one of: ${MIX_CATEGORIES.join(', ')}`);
33
+ process.exit(2);
34
+ }
35
+ return value;
36
+ }
23
37
  const program = new commander_1.Command();
24
38
  program
25
39
  .name('tidal-cli')
@@ -85,6 +99,24 @@ searchCmd
85
99
  .action(wrapAction(async (genre) => {
86
100
  await (0, search_1.search)('playlist', genre || 'top hits', getJson());
87
101
  }));
102
+ searchCmd
103
+ .command('history')
104
+ .description('List your search history')
105
+ .action(wrapAction(async () => {
106
+ await (0, search_history_1.listSearchHistory)(getJson());
107
+ }));
108
+ searchCmd
109
+ .command('history-delete <entry-id>')
110
+ .description('Delete a single search history entry')
111
+ .action(wrapAction(async (entryId) => {
112
+ await (0, search_history_1.deleteSearchHistoryEntry)(entryId, getJson());
113
+ }));
114
+ searchCmd
115
+ .command('history-clear')
116
+ .description('Clear all search history entries')
117
+ .action(wrapAction(async () => {
118
+ await (0, search_history_1.clearSearchHistory)(getJson());
119
+ }));
88
120
  // Artist
89
121
  const artistCmd = program
90
122
  .command('artist')
@@ -167,8 +199,71 @@ albumCmd
167
199
  program
168
200
  .command('recommend')
169
201
  .description('Get personalized recommendations')
202
+ .option('--type <category>', `Mix category: ${MIX_CATEGORIES.join(', ')}`)
203
+ .action(wrapAction(async (opts) => {
204
+ await (0, recommend_1.getRecommendations)(parseMixCategory(opts.type), getJson());
205
+ }));
206
+ // Mix
207
+ const mixCmd = program
208
+ .command('mix')
209
+ .description('Browse personalized mix contents');
210
+ mixCmd
211
+ .command('items <mix-id>')
212
+ .description('Get items inside a specific mix')
213
+ .requiredOption('--type <category>', `Mix category: ${MIX_CATEGORIES.join(', ')}`)
214
+ .action(wrapAction(async (mixId, opts) => {
215
+ const cat = parseMixCategory(opts.type);
216
+ if (!cat)
217
+ return;
218
+ await (0, mix_1.getMixItems)(cat, mixId, getJson());
219
+ }));
220
+ // Share
221
+ program
222
+ .command('share <type> <id>')
223
+ .description('Create a share link for a track or album (type: track | album)')
224
+ .action(wrapAction(async (type, id) => {
225
+ const normalized = type === 'track' ? 'tracks' : type === 'album' ? 'albums' : null;
226
+ if (!normalized) {
227
+ console.error('Error: type must be "track" or "album"');
228
+ process.exit(2);
229
+ }
230
+ await (0, share_1.createShare)(normalized, id, getJson());
231
+ }));
232
+ // Saved (save for later)
233
+ const savedCmd = program
234
+ .command('saved')
235
+ .description('Manage your save-for-later collection');
236
+ savedCmd
237
+ .command('list')
238
+ .description('List items saved for later')
170
239
  .action(wrapAction(async () => {
171
- await (0, recommend_1.getRecommendations)(getJson());
240
+ await (0, saved_1.listSavedItems)(getJson());
241
+ }));
242
+ savedCmd
243
+ .command('add')
244
+ .description('Save an item for later')
245
+ .requiredOption('--type <type>', 'Item type: tracks, albums, artists, playlists, videos')
246
+ .requiredOption('--id <id>', 'Item ID')
247
+ .action(wrapAction(async (opts) => {
248
+ const allowed = ['tracks', 'albums', 'artists', 'playlists', 'videos'];
249
+ if (!allowed.includes(opts.type)) {
250
+ console.error(`Error: invalid type. Use one of: ${allowed.join(', ')}`);
251
+ process.exit(2);
252
+ }
253
+ await (0, saved_1.addSavedItem)(opts.type, opts.id, getJson());
254
+ }));
255
+ savedCmd
256
+ .command('remove')
257
+ .description('Remove an item from saved')
258
+ .requiredOption('--type <type>', 'Item type')
259
+ .requiredOption('--id <id>', 'Item ID')
260
+ .action(wrapAction(async (opts) => {
261
+ const allowed = ['tracks', 'albums', 'artists', 'playlists', 'videos'];
262
+ if (!allowed.includes(opts.type)) {
263
+ console.error(`Error: invalid type. Use one of: ${allowed.join(', ')}`);
264
+ process.exit(2);
265
+ }
266
+ await (0, saved_1.removeSavedItem)(opts.type, opts.id, getJson());
172
267
  }));
173
268
  // User
174
269
  const userCmd = program
package/dist/library.d.ts CHANGED
@@ -1,7 +1,30 @@
1
- type LibraryResourceType = 'artist' | 'album' | 'track' | 'video';
1
+ import type { LibraryResourceType } from './types';
2
+ export type { LibraryResourceType };
3
+ export declare function addToLibraryData(resourceType: LibraryResourceType, resourceId: string, client: any): Promise<{
4
+ resourceType: string;
5
+ resourceId: string;
6
+ added: boolean;
7
+ }>;
2
8
  export declare function addToLibrary(resourceType: LibraryResourceType, resourceId: string, json: boolean): Promise<void>;
9
+ export declare function removeFromLibraryData(resourceType: LibraryResourceType, resourceId: string, client: any): Promise<{
10
+ resourceType: string;
11
+ resourceId: string;
12
+ removed: boolean;
13
+ }>;
3
14
  export declare function removeFromLibrary(resourceType: LibraryResourceType, resourceId: string, json: boolean): Promise<void>;
15
+ export declare function listFavoritedPlaylistsData(client: any): Promise<Array<{
16
+ id: string;
17
+ name: string;
18
+ numberOfItems?: number;
19
+ }>>;
4
20
  export declare function listFavoritedPlaylists(json: boolean): Promise<void>;
21
+ export declare function addPlaylistToFavoritesData(playlistId: string, client: any): Promise<{
22
+ playlistId: string;
23
+ added: boolean;
24
+ }>;
5
25
  export declare function addPlaylistToFavorites(playlistId: string, json: boolean): Promise<void>;
26
+ export declare function removePlaylistFromFavoritesData(playlistId: string, client: any): Promise<{
27
+ playlistId: string;
28
+ removed: boolean;
29
+ }>;
6
30
  export declare function removePlaylistFromFavorites(playlistId: string, json: boolean): Promise<void>;
7
- export {};
package/dist/library.js CHANGED
@@ -1,9 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.addToLibraryData = addToLibraryData;
3
4
  exports.addToLibrary = addToLibrary;
5
+ exports.removeFromLibraryData = removeFromLibraryData;
4
6
  exports.removeFromLibrary = removeFromLibrary;
7
+ exports.listFavoritedPlaylistsData = listFavoritedPlaylistsData;
5
8
  exports.listFavoritedPlaylists = listFavoritedPlaylists;
9
+ exports.addPlaylistToFavoritesData = addPlaylistToFavoritesData;
6
10
  exports.addPlaylistToFavorites = addPlaylistToFavorites;
11
+ exports.removePlaylistFromFavoritesData = removePlaylistFromFavoritesData;
7
12
  exports.removePlaylistFromFavorites = removePlaylistFromFavorites;
8
13
  const auth_1 = require("./auth");
9
14
  const collectionEndpoints = {
@@ -12,8 +17,7 @@ const collectionEndpoints = {
12
17
  track: { path: '/userCollectionTracks/{id}/relationships/items', type: 'tracks' },
13
18
  video: { path: '/userCollectionVideos/{id}/relationships/items', type: 'videos' },
14
19
  };
15
- async function addToLibrary(resourceType, resourceId, json) {
16
- const client = await (0, auth_1.getApiClient)();
20
+ async function addToLibraryData(resourceType, resourceId, client) {
17
21
  const endpoint = collectionEndpoints[resourceType];
18
22
  const { error } = await client.POST(endpoint.path, {
19
23
  params: { path: { id: 'me' } },
@@ -22,17 +26,26 @@ async function addToLibrary(resourceType, resourceId, json) {
22
26
  },
23
27
  });
24
28
  if (error) {
25
- console.error(`Error: Failed to add ${resourceType} to library — ${JSON.stringify(error)}`);
26
- process.exit(1);
29
+ throw new Error(`Failed to add ${resourceType} to library — ${JSON.stringify(error)}`);
27
30
  }
28
- if (json) {
29
- console.log(JSON.stringify({ resourceType, resourceId, added: true }, null, 2));
30
- return;
31
- }
32
- console.log(`\n${capitalize(resourceType)} ${resourceId} added to your library.`);
31
+ return { resourceType, resourceId, added: true };
33
32
  }
34
- async function removeFromLibrary(resourceType, resourceId, json) {
33
+ async function addToLibrary(resourceType, resourceId, json) {
35
34
  const client = await (0, auth_1.getApiClient)();
35
+ try {
36
+ const result = await addToLibraryData(resourceType, resourceId, client);
37
+ if (json) {
38
+ console.log(JSON.stringify(result, null, 2));
39
+ return;
40
+ }
41
+ console.log(`\n${capitalize(resourceType)} ${resourceId} added to your library.`);
42
+ }
43
+ catch (err) {
44
+ console.error(`Error: ${err.message}`);
45
+ process.exit(1);
46
+ }
47
+ }
48
+ async function removeFromLibraryData(resourceType, resourceId, client) {
36
49
  const endpoint = collectionEndpoints[resourceType];
37
50
  const { error } = await client.DELETE(endpoint.path, {
38
51
  params: { path: { id: 'me' } },
@@ -41,17 +54,26 @@ async function removeFromLibrary(resourceType, resourceId, json) {
41
54
  },
42
55
  });
43
56
  if (error) {
44
- console.error(`Error: Failed to remove ${resourceType} from library — ${JSON.stringify(error)}`);
45
- process.exit(1);
46
- }
47
- if (json) {
48
- console.log(JSON.stringify({ resourceType, resourceId, removed: true }, null, 2));
49
- return;
57
+ throw new Error(`Failed to remove ${resourceType} from library — ${JSON.stringify(error)}`);
50
58
  }
51
- console.log(`\n${capitalize(resourceType)} ${resourceId} removed from your library.`);
59
+ return { resourceType, resourceId, removed: true };
52
60
  }
53
- async function listFavoritedPlaylists(json) {
61
+ async function removeFromLibrary(resourceType, resourceId, json) {
54
62
  const client = await (0, auth_1.getApiClient)();
63
+ try {
64
+ const result = await removeFromLibraryData(resourceType, resourceId, client);
65
+ if (json) {
66
+ console.log(JSON.stringify(result, null, 2));
67
+ return;
68
+ }
69
+ console.log(`\n${capitalize(resourceType)} ${resourceId} removed from your library.`);
70
+ }
71
+ catch (err) {
72
+ console.error(`Error: ${err.message}`);
73
+ process.exit(1);
74
+ }
75
+ }
76
+ async function listFavoritedPlaylistsData(client) {
55
77
  const { data, error } = await client.GET('/userCollectionPlaylists/{id}/relationships/items', {
56
78
  params: {
57
79
  path: { id: 'me' },
@@ -61,11 +83,10 @@ async function listFavoritedPlaylists(json) {
61
83
  },
62
84
  });
63
85
  if (error || !data) {
64
- console.error(`Error: Failed to list favorited playlists — ${JSON.stringify(error)}`);
65
- process.exit(1);
86
+ throw new Error(`Failed to list favorited playlists — ${JSON.stringify(error)}`);
66
87
  }
67
88
  const included = data.included ?? [];
68
- const playlists = included
89
+ return included
69
90
  .filter((item) => item.type === 'playlists')
70
91
  .map((item) => {
71
92
  const attrs = item.attributes;
@@ -75,22 +96,31 @@ async function listFavoritedPlaylists(json) {
75
96
  numberOfItems: attrs?.numberOfItems,
76
97
  };
77
98
  });
78
- if (json) {
79
- console.log(JSON.stringify(playlists, null, 2));
80
- return;
81
- }
82
- if (playlists.length === 0) {
83
- console.log('No favorited playlists found.');
84
- return;
99
+ }
100
+ async function listFavoritedPlaylists(json) {
101
+ const client = await (0, auth_1.getApiClient)();
102
+ try {
103
+ const playlists = await listFavoritedPlaylistsData(client);
104
+ if (json) {
105
+ console.log(JSON.stringify(playlists, null, 2));
106
+ return;
107
+ }
108
+ if (playlists.length === 0) {
109
+ console.log('No favorited playlists found.');
110
+ return;
111
+ }
112
+ console.log('\nFavorited playlists:\n');
113
+ for (const p of playlists) {
114
+ console.log(` [${p.id}] ${p.name} (${p.numberOfItems ?? 0} items)`);
115
+ }
116
+ console.log();
85
117
  }
86
- console.log('\nFavorited playlists:\n');
87
- for (const p of playlists) {
88
- console.log(` [${p.id}] ${p.name} (${p.numberOfItems ?? 0} items)`);
118
+ catch (err) {
119
+ console.error(`Error: ${err.message}`);
120
+ process.exit(1);
89
121
  }
90
- console.log();
91
122
  }
92
- async function addPlaylistToFavorites(playlistId, json) {
93
- const client = await (0, auth_1.getApiClient)();
123
+ async function addPlaylistToFavoritesData(playlistId, client) {
94
124
  const { error } = await client.POST('/userCollectionPlaylists/{id}/relationships/items', {
95
125
  params: { path: { id: 'me' } },
96
126
  body: {
@@ -98,17 +128,26 @@ async function addPlaylistToFavorites(playlistId, json) {
98
128
  },
99
129
  });
100
130
  if (error) {
101
- console.error(`Error: Failed to add playlist to favorites — ${JSON.stringify(error)}`);
102
- process.exit(1);
103
- }
104
- if (json) {
105
- console.log(JSON.stringify({ playlistId, added: true }, null, 2));
106
- return;
131
+ throw new Error(`Failed to add playlist to favorites — ${JSON.stringify(error)}`);
107
132
  }
108
- console.log(`\nPlaylist ${playlistId} added to favorites.`);
133
+ return { playlistId, added: true };
109
134
  }
110
- async function removePlaylistFromFavorites(playlistId, json) {
135
+ async function addPlaylistToFavorites(playlistId, json) {
111
136
  const client = await (0, auth_1.getApiClient)();
137
+ try {
138
+ const result = await addPlaylistToFavoritesData(playlistId, client);
139
+ if (json) {
140
+ console.log(JSON.stringify(result, null, 2));
141
+ return;
142
+ }
143
+ console.log(`\nPlaylist ${playlistId} added to favorites.`);
144
+ }
145
+ catch (err) {
146
+ console.error(`Error: ${err.message}`);
147
+ process.exit(1);
148
+ }
149
+ }
150
+ async function removePlaylistFromFavoritesData(playlistId, client) {
112
151
  const { error } = await client.DELETE('/userCollectionPlaylists/{id}/relationships/items', {
113
152
  params: { path: { id: 'me' } },
114
153
  body: {
@@ -116,14 +155,24 @@ async function removePlaylistFromFavorites(playlistId, json) {
116
155
  },
117
156
  });
118
157
  if (error) {
119
- console.error(`Error: Failed to remove playlist from favorites — ${JSON.stringify(error)}`);
120
- process.exit(1);
158
+ throw new Error(`Failed to remove playlist from favorites — ${JSON.stringify(error)}`);
121
159
  }
122
- if (json) {
123
- console.log(JSON.stringify({ playlistId, removed: true }, null, 2));
124
- return;
160
+ return { playlistId, removed: true };
161
+ }
162
+ async function removePlaylistFromFavorites(playlistId, json) {
163
+ const client = await (0, auth_1.getApiClient)();
164
+ try {
165
+ const result = await removePlaylistFromFavoritesData(playlistId, client);
166
+ if (json) {
167
+ console.log(JSON.stringify(result, null, 2));
168
+ return;
169
+ }
170
+ console.log(`\nPlaylist ${playlistId} removed from favorites.`);
171
+ }
172
+ catch (err) {
173
+ console.error(`Error: ${err.message}`);
174
+ process.exit(1);
125
175
  }
126
- console.log(`\nPlaylist ${playlistId} removed from favorites.`);
127
176
  }
128
177
  function capitalize(s) {
129
178
  return s.charAt(0).toUpperCase() + s.slice(1);
package/dist/mix.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type { MixCategory, MixItem } from './types';
2
+ export type { MixItem };
3
+ export declare function getMixItemsData(category: MixCategory, mixId: string, client: any, countryCode: string): Promise<MixItem[]>;
4
+ export declare function getMixItems(category: MixCategory, mixId: string, json: boolean): Promise<void>;
package/dist/mix.js ADDED
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getMixItemsData = getMixItemsData;
4
+ exports.getMixItems = getMixItems;
5
+ const auth_1 = require("./auth");
6
+ const categoryEndpoints = {
7
+ daily: '/userDailyMixes/{id}/relationships/items',
8
+ discovery: '/userDiscoveryMixes/{id}/relationships/items',
9
+ 'new-release': '/userNewReleaseMixes/{id}/relationships/items',
10
+ offline: '/userOfflineMixes/{id}/relationships/items',
11
+ };
12
+ async function getMixItemsData(category, mixId, client, countryCode) {
13
+ const { data, error } = await client.GET(categoryEndpoints[category], {
14
+ params: {
15
+ path: { id: mixId },
16
+ query: {
17
+ countryCode,
18
+ include: ['items'],
19
+ },
20
+ },
21
+ });
22
+ if (error || !data) {
23
+ throw new Error(`Failed to get mix items — ${JSON.stringify(error)}`);
24
+ }
25
+ const included = data.included ?? [];
26
+ return included.map((item) => {
27
+ const attrs = item.attributes ?? {};
28
+ return {
29
+ id: item.id,
30
+ type: item.type,
31
+ name: attrs.title ?? attrs.name ?? 'Untitled',
32
+ };
33
+ });
34
+ }
35
+ async function getMixItems(category, mixId, json) {
36
+ const client = await (0, auth_1.getApiClient)();
37
+ const countryCode = await (0, auth_1.getCountryCode)();
38
+ try {
39
+ const items = await getMixItemsData(category, mixId, client, countryCode);
40
+ if (json) {
41
+ console.log(JSON.stringify(items, null, 2));
42
+ return;
43
+ }
44
+ if (items.length === 0) {
45
+ console.log('No items found in this mix.');
46
+ return;
47
+ }
48
+ console.log(`\n${category} mix [${mixId}] items:\n`);
49
+ for (const item of items) {
50
+ console.log(` [${item.id}] (${item.type}) ${item.name}`);
51
+ }
52
+ console.log();
53
+ }
54
+ catch (err) {
55
+ console.error(`Error: ${err.message}`);
56
+ process.exit(1);
57
+ }
58
+ }
59
+ //# sourceMappingURL=mix.js.map
@@ -1,3 +1,7 @@
1
+ import type { PlaybackInfo, PlaybackUrl } from './types';
2
+ export type { PlaybackInfo, PlaybackUrl };
3
+ export declare function playbackInfoData(trackId: string, quality: string, client: any): Promise<PlaybackInfo>;
1
4
  export declare function playbackInfo(trackId: string, quality: string, json: boolean): Promise<void>;
5
+ export declare function playbackUrlData(trackId: string, quality: string, client: any): Promise<PlaybackUrl>;
2
6
  export declare function playbackUrl(trackId: string, quality: string, json: boolean): Promise<void>;
3
7
  export declare function playbackPlay(trackId: string, quality: string): Promise<void>;