@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/playback.js CHANGED
@@ -33,7 +33,9 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.playbackInfoData = playbackInfoData;
36
37
  exports.playbackInfo = playbackInfo;
38
+ exports.playbackUrlData = playbackUrlData;
37
39
  exports.playbackUrl = playbackUrl;
38
40
  exports.playbackPlay = playbackPlay;
39
41
  const auth_1 = require("./auth");
@@ -62,8 +64,7 @@ function parseDataUri(dataUri) {
62
64
  throw new Error('Invalid data URI from trackManifests');
63
65
  return { mimeType: match[1], data: match[2] };
64
66
  }
65
- async function fetchTrackManifest(trackId, quality) {
66
- const client = await (0, auth_1.getApiClient)();
67
+ async function fetchTrackManifestData(trackId, quality, client) {
67
68
  const formats = qualityToFormats[quality] ?? qualityToFormats.HIGH;
68
69
  const { data, error } = await client.GET('/trackManifests/{id}', {
69
70
  params: {
@@ -78,13 +79,11 @@ async function fetchTrackManifest(trackId, quality) {
78
79
  },
79
80
  });
80
81
  if (error || !data) {
81
- console.error(`Error: Failed to get track manifest — ${JSON.stringify(error)}`);
82
- process.exit(1);
82
+ throw new Error(`Failed to get track manifest — ${JSON.stringify(error)}`);
83
83
  }
84
84
  const attrs = data.data?.attributes;
85
85
  if (!attrs?.uri) {
86
- console.error('Error: No manifest URI in response.');
87
- process.exit(1);
86
+ throw new Error('No manifest URI in response.');
88
87
  }
89
88
  const { mimeType, data: manifestBase64 } = parseDataUri(attrs.uri);
90
89
  return {
@@ -101,6 +100,11 @@ async function fetchTrackManifest(trackId, quality) {
101
100
  albumPeakAmplitude: attrs.albumAudioNormalizationData?.peakAmplitude ?? 0,
102
101
  };
103
102
  }
103
+ // Keep backward-compatible internal helper
104
+ async function fetchTrackManifest(trackId, quality) {
105
+ const client = await (0, auth_1.getApiClient)();
106
+ return fetchTrackManifestData(trackId, quality, client);
107
+ }
104
108
  function decodeManifest(base64Manifest, mimeType) {
105
109
  const decoded = Buffer.from(base64Manifest, 'base64').toString('utf-8');
106
110
  // JSON manifest (BTS): has urls array
@@ -118,7 +122,6 @@ function decodeManifest(base64Manifest, mimeType) {
118
122
  const initMatch = decoded.match(/initialization="([^"]+)"/);
119
123
  const mediaMatch = decoded.match(/media="([^"]+)"/);
120
124
  const codecsMatch = decoded.match(/codecs="([^"]+)"/);
121
- // Parse segment timeline: <S d="176128" r="6"/> means 7 segments, <S d="89088"/> means 1
122
125
  const segmentDurations = [];
123
126
  const sMatches = decoded.matchAll(/<S d="(\d+)"(?:\s+r="(\d+)")?\/>/g);
124
127
  let segNum = 1;
@@ -139,7 +142,6 @@ function decodeManifest(base64Manifest, mimeType) {
139
142
  codecs: codecsMatch?.[1],
140
143
  };
141
144
  }
142
- // Fallback: try BaseURL
143
145
  const baseUrlMatch = decoded.match(/<BaseURL>([^<]+)<\/BaseURL>/);
144
146
  if (baseUrlMatch) {
145
147
  return { type: 'direct', url: baseUrlMatch[1], codecs: codecsMatch?.[1] };
@@ -148,12 +150,10 @@ function decodeManifest(base64Manifest, mimeType) {
148
150
  throw new Error('Unable to parse manifest');
149
151
  }
150
152
  async function downloadDashStream(dash) {
151
- // Download init segment
152
153
  const initRes = await fetch(dash.initUrl);
153
154
  if (!initRes.ok)
154
155
  throw new Error(`Failed to download init segment (${initRes.status})`);
155
156
  const initBuf = Buffer.from(await initRes.arrayBuffer());
156
- // Download media segments
157
157
  const segBuffers = [initBuf];
158
158
  for (const segNum of dash.segments) {
159
159
  const segUrl = dash.mediaTemplate.replace('$Number$', String(segNum));
@@ -164,9 +164,9 @@ async function downloadDashStream(dash) {
164
164
  }
165
165
  return Buffer.concat(segBuffers);
166
166
  }
167
- async function playbackInfo(trackId, quality, json) {
168
- const info = await fetchTrackManifest(trackId, quality);
169
- const result = {
167
+ async function playbackInfoData(trackId, quality, client) {
168
+ const info = await fetchTrackManifestData(trackId, quality, client);
169
+ return {
170
170
  trackId: info.trackId,
171
171
  presentation: info.trackPresentation,
172
172
  previewReason: info.previewReason,
@@ -178,57 +178,73 @@ async function playbackInfo(trackId, quality, json) {
178
178
  albumReplayGain: info.albumReplayGain,
179
179
  albumPeakAmplitude: info.albumPeakAmplitude,
180
180
  };
181
- if (json) {
182
- console.log(JSON.stringify(result, null, 2));
183
- return;
181
+ }
182
+ async function playbackInfo(trackId, quality, json) {
183
+ const client = await (0, auth_1.getApiClient)();
184
+ try {
185
+ const result = await playbackInfoData(trackId, quality, client);
186
+ if (json) {
187
+ console.log(JSON.stringify(result, null, 2));
188
+ return;
189
+ }
190
+ console.log(`\nPlayback info for track ${trackId}:\n`);
191
+ console.log(` Quality: ${result.audioQuality}`);
192
+ console.log(` Formats: ${result.formats?.join(', ')}`);
193
+ console.log(` Presentation: ${result.presentation}`);
194
+ if (result.previewReason) {
195
+ console.log(` Preview reason: ${result.previewReason}`);
196
+ }
197
+ console.log(` Manifest type: ${result.manifestMimeType}`);
198
+ console.log(` Track gain: ${result.trackReplayGain} dB`);
199
+ console.log(` Album gain: ${result.albumReplayGain} dB`);
200
+ console.log();
184
201
  }
185
- console.log(`\nPlayback info for track ${trackId}:\n`);
186
- console.log(` Quality: ${result.audioQuality}`);
187
- console.log(` Formats: ${result.formats.join(', ')}`);
188
- console.log(` Presentation: ${result.presentation}`);
189
- if (result.previewReason) {
190
- console.log(` Preview reason: ${result.previewReason}`);
202
+ catch (err) {
203
+ console.error(`Error: ${err.message}`);
204
+ process.exit(1);
191
205
  }
192
- console.log(` Manifest type: ${result.manifestMimeType}`);
193
- console.log(` Track gain: ${result.trackReplayGain} dB`);
194
- console.log(` Album gain: ${result.albumReplayGain} dB`);
195
- console.log();
196
206
  }
197
- async function playbackUrl(trackId, quality, json) {
198
- const info = await fetchTrackManifest(trackId, quality);
207
+ async function playbackUrlData(trackId, quality, client) {
208
+ const info = await fetchTrackManifestData(trackId, quality, client);
199
209
  const stream = decodeManifest(info.manifest, info.manifestMimeType);
200
210
  if (stream.type === 'direct') {
201
- if (json) {
202
- console.log(JSON.stringify({ trackId: info.trackId, url: stream.url, audioQuality: info.audioQuality }, null, 2));
203
- }
204
- else {
205
- console.log(stream.url);
206
- }
211
+ return { trackId: info.trackId, url: stream.url, audioQuality: info.audioQuality, type: 'direct' };
207
212
  }
208
213
  else if (stream.dash) {
214
+ return {
215
+ trackId: info.trackId,
216
+ type: 'dash',
217
+ initUrl: stream.dash.initUrl,
218
+ segmentCount: stream.dash.segments.length,
219
+ audioQuality: info.audioQuality,
220
+ };
221
+ }
222
+ throw new Error('No stream URL available for this track.');
223
+ }
224
+ async function playbackUrl(trackId, quality, json) {
225
+ const client = await (0, auth_1.getApiClient)();
226
+ try {
227
+ const result = await playbackUrlData(trackId, quality, client);
209
228
  if (json) {
210
- console.log(JSON.stringify({
211
- trackId: info.trackId,
212
- type: 'dash',
213
- initUrl: stream.dash.initUrl,
214
- segmentCount: stream.dash.segments.length,
215
- audioQuality: info.audioQuality,
216
- }, null, 2));
229
+ console.log(JSON.stringify(result, null, 2));
230
+ return;
231
+ }
232
+ if (result.type === 'direct') {
233
+ console.log(result.url);
217
234
  }
218
235
  else {
219
- console.log(`DASH stream (${stream.dash.segments.length} segments)`);
220
- console.log(` Init: ${stream.dash.initUrl}`);
236
+ console.log(`DASH stream (${result.segmentCount} segments)`);
237
+ console.log(` Init: ${result.initUrl}`);
221
238
  }
222
239
  }
223
- else {
224
- console.error('Error: No stream URL available for this track.');
240
+ catch (err) {
241
+ console.error(`Error: ${err.message}`);
225
242
  process.exit(1);
226
243
  }
227
244
  }
228
245
  async function playbackPlay(trackId, quality) {
229
246
  const info = await fetchTrackManifest(trackId, quality);
230
247
  const stream = decodeManifest(info.manifest, info.manifestMimeType);
231
- // Determine file extension from format
232
248
  const isFlac = info.formats.includes('FLAC') || info.formats.includes('FLAC_HIRES');
233
249
  const ext = isFlac ? '.flac' : '.mp4';
234
250
  const tmpFile = path.join(os.tmpdir(), `tidal-${trackId}${ext}`);
@@ -1,9 +1,52 @@
1
+ import type { PlaylistInfo } from './types';
2
+ export type { PlaylistInfo };
3
+ export declare function listPlaylistsData(client: any, countryCode: string): Promise<PlaylistInfo[]>;
1
4
  export declare function listPlaylists(json: boolean): Promise<void>;
5
+ export declare function createPlaylistData(name: string, description: string, client: any): Promise<{
6
+ id: string;
7
+ name: string;
8
+ description: string;
9
+ }>;
2
10
  export declare function createPlaylist(name: string, description: string, json: boolean): Promise<void>;
11
+ export declare function renamePlaylistData(playlistId: string, name: string, client: any): Promise<{
12
+ id: string;
13
+ name: string;
14
+ success: boolean;
15
+ }>;
3
16
  export declare function renamePlaylist(playlistId: string, name: string, json: boolean): Promise<void>;
17
+ export declare function deletePlaylistData(playlistId: string, client: any): Promise<{
18
+ id: string;
19
+ deleted: boolean;
20
+ }>;
4
21
  export declare function deletePlaylist(playlistId: string, json: boolean): Promise<void>;
22
+ export declare function addTrackToPlaylistData(playlistId: string, trackId: string, client: any): Promise<{
23
+ playlistId: string;
24
+ trackId: string;
25
+ added: boolean;
26
+ }>;
5
27
  export declare function addTrackToPlaylist(playlistId: string, trackId: string, json: boolean): Promise<void>;
28
+ export declare function removeTrackFromPlaylistData(playlistId: string, trackId: string, client: any): Promise<{
29
+ playlistId: string;
30
+ trackId: string;
31
+ removed: boolean;
32
+ }>;
6
33
  export declare function removeTrackFromPlaylist(playlistId: string, trackId: string, json: boolean): Promise<void>;
34
+ export declare function addAlbumToPlaylistData(playlistId: string, albumId: string, client: any, countryCode: string): Promise<{
35
+ playlistId: string;
36
+ albumId: string;
37
+ tracksAdded: number;
38
+ }>;
7
39
  export declare function addAlbumToPlaylist(playlistId: string, albumId: string, json: boolean): Promise<void>;
40
+ export declare function moveTrackInPlaylistData(playlistId: string, trackId: string, positionBefore: string, client: any): Promise<{
41
+ playlistId: string;
42
+ trackId: string;
43
+ positionBefore: string;
44
+ moved: boolean;
45
+ }>;
8
46
  export declare function moveTrackInPlaylist(playlistId: string, trackId: string, positionBefore: string, json: boolean): Promise<void>;
47
+ export declare function updatePlaylistDescriptionData(playlistId: string, description: string, client: any): Promise<{
48
+ id: string;
49
+ description: string;
50
+ success: boolean;
51
+ }>;
9
52
  export declare function updatePlaylistDescription(playlistId: string, description: string, json: boolean): Promise<void>;
package/dist/playlist.js CHANGED
@@ -1,30 +1,37 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listPlaylistsData = listPlaylistsData;
3
4
  exports.listPlaylists = listPlaylists;
5
+ exports.createPlaylistData = createPlaylistData;
4
6
  exports.createPlaylist = createPlaylist;
7
+ exports.renamePlaylistData = renamePlaylistData;
5
8
  exports.renamePlaylist = renamePlaylist;
9
+ exports.deletePlaylistData = deletePlaylistData;
6
10
  exports.deletePlaylist = deletePlaylist;
11
+ exports.addTrackToPlaylistData = addTrackToPlaylistData;
7
12
  exports.addTrackToPlaylist = addTrackToPlaylist;
13
+ exports.removeTrackFromPlaylistData = removeTrackFromPlaylistData;
8
14
  exports.removeTrackFromPlaylist = removeTrackFromPlaylist;
15
+ exports.addAlbumToPlaylistData = addAlbumToPlaylistData;
9
16
  exports.addAlbumToPlaylist = addAlbumToPlaylist;
17
+ exports.moveTrackInPlaylistData = moveTrackInPlaylistData;
10
18
  exports.moveTrackInPlaylist = moveTrackInPlaylist;
19
+ exports.updatePlaylistDescriptionData = updatePlaylistDescriptionData;
11
20
  exports.updatePlaylistDescription = updatePlaylistDescription;
12
21
  const auth_1 = require("./auth");
13
- async function listPlaylists(json) {
14
- const client = await (0, auth_1.getApiClient)();
22
+ async function listPlaylistsData(client, countryCode) {
15
23
  const { data, error } = await client.GET('/playlists', {
16
24
  params: {
17
25
  query: {
18
26
  'filter[owners.id]': ['me'],
19
- countryCode: await (0, auth_1.getCountryCode)(),
27
+ countryCode,
20
28
  },
21
29
  },
22
30
  });
23
31
  if (error || !data) {
24
- console.error(`Error: Failed to list playlists — ${JSON.stringify(error)}`);
25
- process.exit(1);
32
+ throw new Error(`Failed to list playlists — ${JSON.stringify(error)}`);
26
33
  }
27
- const playlists = (data.data ?? []).map((p) => ({
34
+ return (data.data ?? []).map((p) => ({
28
35
  id: p.id,
29
36
  name: p.attributes?.name ?? 'Untitled',
30
37
  description: p.attributes?.description,
@@ -32,24 +39,34 @@ async function listPlaylists(json) {
32
39
  createdAt: p.attributes?.createdAt,
33
40
  lastModifiedAt: p.attributes?.lastModifiedAt,
34
41
  }));
35
- if (json) {
36
- console.log(JSON.stringify(playlists, null, 2));
37
- return;
38
- }
39
- if (playlists.length === 0) {
40
- console.log('No playlists found.');
41
- return;
42
+ }
43
+ async function listPlaylists(json) {
44
+ const client = await (0, auth_1.getApiClient)();
45
+ const countryCode = await (0, auth_1.getCountryCode)();
46
+ try {
47
+ const playlists = await listPlaylistsData(client, countryCode);
48
+ if (json) {
49
+ console.log(JSON.stringify(playlists, null, 2));
50
+ return;
51
+ }
52
+ if (playlists.length === 0) {
53
+ console.log('No playlists found.');
54
+ return;
55
+ }
56
+ console.log('\nYour playlists:\n');
57
+ for (const p of playlists) {
58
+ console.log(` [${p.id}] ${p.name} (${p.numberOfItems ?? 0} tracks)`);
59
+ if (p.description)
60
+ console.log(` ${p.description}`);
61
+ }
62
+ console.log();
42
63
  }
43
- console.log('\nYour playlists:\n');
44
- for (const p of playlists) {
45
- console.log(` [${p.id}] ${p.name} (${p.numberOfItems ?? 0} tracks)`);
46
- if (p.description)
47
- console.log(` ${p.description}`);
64
+ catch (err) {
65
+ console.error(`Error: ${err.message}`);
66
+ process.exit(1);
48
67
  }
49
- console.log();
50
68
  }
51
- async function createPlaylist(name, description, json) {
52
- const client = await (0, auth_1.getApiClient)();
69
+ async function createPlaylistData(name, description, client) {
53
70
  const { data, error } = await client.POST('/playlists', {
54
71
  body: {
55
72
  data: {
@@ -63,23 +80,31 @@ async function createPlaylist(name, description, json) {
63
80
  },
64
81
  });
65
82
  if (error || !data) {
66
- console.error(`Error: Failed to create playlist — ${JSON.stringify(error)}`);
67
- process.exit(1);
83
+ throw new Error(`Failed to create playlist — ${JSON.stringify(error)}`);
68
84
  }
69
85
  const created = data.data;
70
- const result = {
86
+ return {
71
87
  id: created.id,
72
88
  name: created.attributes?.name ?? name,
73
89
  description: created.attributes?.description ?? description,
74
90
  };
75
- if (json) {
76
- console.log(JSON.stringify(result, null, 2));
77
- return;
78
- }
79
- console.log(`\nPlaylist created: [${result.id}] ${result.name}`);
80
91
  }
81
- async function renamePlaylist(playlistId, name, json) {
92
+ async function createPlaylist(name, description, json) {
82
93
  const client = await (0, auth_1.getApiClient)();
94
+ try {
95
+ const result = await createPlaylistData(name, description, client);
96
+ if (json) {
97
+ console.log(JSON.stringify(result, null, 2));
98
+ return;
99
+ }
100
+ console.log(`\nPlaylist created: [${result.id}] ${result.name}`);
101
+ }
102
+ catch (err) {
103
+ console.error(`Error: ${err.message}`);
104
+ process.exit(1);
105
+ }
106
+ }
107
+ async function renamePlaylistData(playlistId, name, client) {
83
108
  const { error } = await client.PATCH('/playlists/{id}', {
84
109
  params: { path: { id: playlistId } },
85
110
  body: {
@@ -91,32 +116,50 @@ async function renamePlaylist(playlistId, name, json) {
91
116
  },
92
117
  });
93
118
  if (error) {
94
- console.error(`Error: Failed to rename playlist — ${JSON.stringify(error)}`);
95
- process.exit(1);
96
- }
97
- if (json) {
98
- console.log(JSON.stringify({ id: playlistId, name, success: true }, null, 2));
99
- return;
119
+ throw new Error(`Failed to rename playlist — ${JSON.stringify(error)}`);
100
120
  }
101
- console.log(`\nPlaylist ${playlistId} renamed to "${name}".`);
121
+ return { id: playlistId, name, success: true };
102
122
  }
103
- async function deletePlaylist(playlistId, json) {
123
+ async function renamePlaylist(playlistId, name, json) {
104
124
  const client = await (0, auth_1.getApiClient)();
125
+ try {
126
+ const result = await renamePlaylistData(playlistId, name, client);
127
+ if (json) {
128
+ console.log(JSON.stringify(result, null, 2));
129
+ return;
130
+ }
131
+ console.log(`\nPlaylist ${playlistId} renamed to "${name}".`);
132
+ }
133
+ catch (err) {
134
+ console.error(`Error: ${err.message}`);
135
+ process.exit(1);
136
+ }
137
+ }
138
+ async function deletePlaylistData(playlistId, client) {
105
139
  const { error } = await client.DELETE('/playlists/{id}', {
106
140
  params: { path: { id: playlistId } },
107
141
  });
108
142
  if (error) {
109
- console.error(`Error: Failed to delete playlist — ${JSON.stringify(error)}`);
110
- process.exit(1);
111
- }
112
- if (json) {
113
- console.log(JSON.stringify({ id: playlistId, deleted: true }, null, 2));
114
- return;
143
+ throw new Error(`Failed to delete playlist — ${JSON.stringify(error)}`);
115
144
  }
116
- console.log(`\nPlaylist ${playlistId} deleted.`);
145
+ return { id: playlistId, deleted: true };
117
146
  }
118
- async function addTrackToPlaylist(playlistId, trackId, json) {
147
+ async function deletePlaylist(playlistId, json) {
119
148
  const client = await (0, auth_1.getApiClient)();
149
+ try {
150
+ const result = await deletePlaylistData(playlistId, client);
151
+ if (json) {
152
+ console.log(JSON.stringify(result, null, 2));
153
+ return;
154
+ }
155
+ console.log(`\nPlaylist ${playlistId} deleted.`);
156
+ }
157
+ catch (err) {
158
+ console.error(`Error: ${err.message}`);
159
+ process.exit(1);
160
+ }
161
+ }
162
+ async function addTrackToPlaylistData(playlistId, trackId, client) {
120
163
  const { error } = await client.POST('/playlists/{id}/relationships/items', {
121
164
  params: { path: { id: playlistId } },
122
165
  body: {
@@ -124,30 +167,36 @@ async function addTrackToPlaylist(playlistId, trackId, json) {
124
167
  },
125
168
  });
126
169
  if (error) {
127
- console.error(`Error: Failed to add track — ${JSON.stringify(error)}`);
128
- process.exit(1);
170
+ throw new Error(`Failed to add track — ${JSON.stringify(error)}`);
129
171
  }
130
- if (json) {
131
- console.log(JSON.stringify({ playlistId, trackId, added: true }, null, 2));
132
- return;
133
- }
134
- console.log(`\nTrack ${trackId} added to playlist ${playlistId}.`);
172
+ return { playlistId, trackId, added: true };
135
173
  }
136
- async function removeTrackFromPlaylist(playlistId, trackId, json) {
174
+ async function addTrackToPlaylist(playlistId, trackId, json) {
137
175
  const client = await (0, auth_1.getApiClient)();
138
- // Get playlist items to find the item index (required by the API)
176
+ try {
177
+ const result = await addTrackToPlaylistData(playlistId, trackId, client);
178
+ if (json) {
179
+ console.log(JSON.stringify(result, null, 2));
180
+ return;
181
+ }
182
+ console.log(`\nTrack ${trackId} added to playlist ${playlistId}.`);
183
+ }
184
+ catch (err) {
185
+ console.error(`Error: ${err.message}`);
186
+ process.exit(1);
187
+ }
188
+ }
189
+ async function removeTrackFromPlaylistData(playlistId, trackId, client) {
139
190
  const { data: itemsData, error: itemsError } = await client.GET('/playlists/{id}/relationships/items', {
140
191
  params: { path: { id: playlistId } },
141
192
  });
142
193
  if (itemsError || !itemsData) {
143
- console.error(`Error: Failed to get playlist items — ${JSON.stringify(itemsError)}`);
144
- process.exit(1);
194
+ throw new Error(`Failed to get playlist items — ${JSON.stringify(itemsError)}`);
145
195
  }
146
196
  const items = itemsData.data ?? [];
147
197
  const item = items.find((i) => i.id === trackId);
148
198
  if (!item) {
149
- console.error(`Error: Track ${trackId} not found in playlist ${playlistId}.`);
150
- process.exit(1);
199
+ throw new Error(`Track ${trackId} not found in playlist ${playlistId}.`);
151
200
  }
152
201
  const { error } = await client.DELETE('/playlists/{id}/relationships/items', {
153
202
  params: { path: { id: playlistId } },
@@ -156,71 +205,82 @@ async function removeTrackFromPlaylist(playlistId, trackId, json) {
156
205
  },
157
206
  });
158
207
  if (error) {
159
- console.error(`Error: Failed to remove track — ${JSON.stringify(error)}`);
160
- process.exit(1);
208
+ throw new Error(`Failed to remove track — ${JSON.stringify(error)}`);
161
209
  }
162
- if (json) {
163
- console.log(JSON.stringify({ playlistId, trackId, removed: true }, null, 2));
164
- return;
165
- }
166
- console.log(`\nTrack ${trackId} removed from playlist ${playlistId}.`);
210
+ return { playlistId, trackId, removed: true };
167
211
  }
168
- async function addAlbumToPlaylist(playlistId, albumId, json) {
212
+ async function removeTrackFromPlaylist(playlistId, trackId, json) {
169
213
  const client = await (0, auth_1.getApiClient)();
170
- // First get album tracks
214
+ try {
215
+ const result = await removeTrackFromPlaylistData(playlistId, trackId, client);
216
+ if (json) {
217
+ console.log(JSON.stringify(result, null, 2));
218
+ return;
219
+ }
220
+ console.log(`\nTrack ${trackId} removed from playlist ${playlistId}.`);
221
+ }
222
+ catch (err) {
223
+ console.error(`Error: ${err.message}`);
224
+ process.exit(1);
225
+ }
226
+ }
227
+ async function addAlbumToPlaylistData(playlistId, albumId, client, countryCode) {
171
228
  const { data: albumData, error: albumError } = await client.GET('/albums/{id}', {
172
229
  params: {
173
230
  path: { id: albumId },
174
- query: { countryCode: await (0, auth_1.getCountryCode)(), include: ['items'] },
231
+ query: { countryCode, include: ['items'] },
175
232
  },
176
233
  });
177
234
  if (albumError || !albumData) {
178
- console.error(`Error: Failed to get album — ${JSON.stringify(albumError)}`);
179
- process.exit(1);
235
+ throw new Error(`Failed to get album — ${JSON.stringify(albumError)}`);
180
236
  }
181
- // Extract track IDs from included items
182
237
  const included = albumData.included ?? [];
183
238
  const trackIds = included
184
239
  .filter((item) => item.type === 'tracks')
185
240
  .map((item) => ({ id: item.id, type: 'tracks' }));
186
241
  if (trackIds.length === 0) {
187
- console.error('Error: No tracks found in album.');
188
- process.exit(1);
242
+ throw new Error('No tracks found in album.');
189
243
  }
190
244
  const { error } = await client.POST('/playlists/{id}/relationships/items', {
191
245
  params: { path: { id: playlistId } },
192
246
  body: { data: trackIds },
193
247
  });
194
248
  if (error) {
195
- console.error(`Error: Failed to add album tracks — ${JSON.stringify(error)}`);
196
- process.exit(1);
249
+ throw new Error(`Failed to add album tracks — ${JSON.stringify(error)}`);
197
250
  }
198
- if (json) {
199
- console.log(JSON.stringify({ playlistId, albumId, tracksAdded: trackIds.length }, null, 2));
200
- return;
201
- }
202
- console.log(`\n${trackIds.length} tracks from album ${albumId} added to playlist ${playlistId}.`);
251
+ return { playlistId, albumId, tracksAdded: trackIds.length };
203
252
  }
204
- async function moveTrackInPlaylist(playlistId, trackId, positionBefore, json) {
253
+ async function addAlbumToPlaylist(playlistId, albumId, json) {
205
254
  const client = await (0, auth_1.getApiClient)();
206
- // Get playlist items to find the itemId for the track
255
+ const countryCode = await (0, auth_1.getCountryCode)();
256
+ try {
257
+ const result = await addAlbumToPlaylistData(playlistId, albumId, client, countryCode);
258
+ if (json) {
259
+ console.log(JSON.stringify(result, null, 2));
260
+ return;
261
+ }
262
+ console.log(`\n${result.tracksAdded} tracks from album ${albumId} added to playlist ${playlistId}.`);
263
+ }
264
+ catch (err) {
265
+ console.error(`Error: ${err.message}`);
266
+ process.exit(1);
267
+ }
268
+ }
269
+ async function moveTrackInPlaylistData(playlistId, trackId, positionBefore, client) {
207
270
  const { data: itemsData, error: itemsError } = await client.GET('/playlists/{id}/relationships/items', {
208
271
  params: { path: { id: playlistId } },
209
272
  });
210
273
  if (itemsError || !itemsData) {
211
- console.error(`Error: Failed to get playlist items — ${JSON.stringify(itemsError)}`);
212
- process.exit(1);
274
+ throw new Error(`Failed to get playlist items — ${JSON.stringify(itemsError)}`);
213
275
  }
214
276
  const items = itemsData.data ?? [];
215
277
  const item = items.find((i) => i.id === trackId);
216
278
  if (!item) {
217
- console.error(`Error: Track ${trackId} not found in playlist ${playlistId}.`);
218
- process.exit(1);
279
+ throw new Error(`Track ${trackId} not found in playlist ${playlistId}.`);
219
280
  }
220
281
  const itemId = item.meta?.itemId;
221
282
  if (!itemId) {
222
- console.error(`Error: Could not find itemId for track ${trackId}.`);
223
- process.exit(1);
283
+ throw new Error(`Could not find itemId for track ${trackId}.`);
224
284
  }
225
285
  const meta = {};
226
286
  if (positionBefore !== 'end') {
@@ -234,18 +294,27 @@ async function moveTrackInPlaylist(playlistId, trackId, positionBefore, json) {
234
294
  },
235
295
  });
236
296
  if (error) {
237
- console.error(`Error: Failed to move track — ${JSON.stringify(error)}`);
238
- process.exit(1);
239
- }
240
- if (json) {
241
- console.log(JSON.stringify({ playlistId, trackId, positionBefore, moved: true }, null, 2));
242
- return;
297
+ throw new Error(`Failed to move track — ${JSON.stringify(error)}`);
243
298
  }
244
- const posDesc = positionBefore === 'end' ? 'to end of playlist' : `before item ${positionBefore}`;
245
- console.log(`\nTrack ${trackId} moved ${posDesc} in playlist ${playlistId}.`);
299
+ return { playlistId, trackId, positionBefore, moved: true };
246
300
  }
247
- async function updatePlaylistDescription(playlistId, description, json) {
301
+ async function moveTrackInPlaylist(playlistId, trackId, positionBefore, json) {
248
302
  const client = await (0, auth_1.getApiClient)();
303
+ try {
304
+ const result = await moveTrackInPlaylistData(playlistId, trackId, positionBefore, client);
305
+ if (json) {
306
+ console.log(JSON.stringify(result, null, 2));
307
+ return;
308
+ }
309
+ const posDesc = positionBefore === 'end' ? 'to end of playlist' : `before item ${positionBefore}`;
310
+ console.log(`\nTrack ${trackId} moved ${posDesc} in playlist ${playlistId}.`);
311
+ }
312
+ catch (err) {
313
+ console.error(`Error: ${err.message}`);
314
+ process.exit(1);
315
+ }
316
+ }
317
+ async function updatePlaylistDescriptionData(playlistId, description, client) {
249
318
  const { error } = await client.PATCH('/playlists/{id}', {
250
319
  params: { path: { id: playlistId } },
251
320
  body: {
@@ -257,13 +326,23 @@ async function updatePlaylistDescription(playlistId, description, json) {
257
326
  },
258
327
  });
259
328
  if (error) {
260
- console.error(`Error: Failed to update playlist description — ${JSON.stringify(error)}`);
261
- process.exit(1);
329
+ throw new Error(`Failed to update playlist description — ${JSON.stringify(error)}`);
262
330
  }
263
- if (json) {
264
- console.log(JSON.stringify({ id: playlistId, description, success: true }, null, 2));
265
- return;
331
+ return { id: playlistId, description, success: true };
332
+ }
333
+ async function updatePlaylistDescription(playlistId, description, json) {
334
+ const client = await (0, auth_1.getApiClient)();
335
+ try {
336
+ const result = await updatePlaylistDescriptionData(playlistId, description, client);
337
+ if (json) {
338
+ console.log(JSON.stringify(result, null, 2));
339
+ return;
340
+ }
341
+ console.log(`\nPlaylist ${playlistId} description updated.`);
342
+ }
343
+ catch (err) {
344
+ console.error(`Error: ${err.message}`);
345
+ process.exit(1);
266
346
  }
267
- console.log(`\nPlaylist ${playlistId} description updated.`);
268
347
  }
269
348
  //# sourceMappingURL=playlist.js.map