@bzenky/spoti 0.4.0 → 0.5.0

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.
Files changed (39) hide show
  1. package/README.md +19 -7
  2. package/dist/app.d.ts +6 -2
  3. package/dist/app.js +109 -51
  4. package/dist/app.js.map +1 -1
  5. package/dist/cli.js +2 -1
  6. package/dist/cli.js.map +1 -1
  7. package/dist/services/device.service.d.ts +3 -3
  8. package/dist/services/device.service.js +19 -11
  9. package/dist/services/device.service.js.map +1 -1
  10. package/dist/services/player.service.d.ts +4 -2
  11. package/dist/services/player.service.js +35 -5
  12. package/dist/services/player.service.js.map +1 -1
  13. package/dist/services/queue.service.js +11 -6
  14. package/dist/services/queue.service.js.map +1 -1
  15. package/dist/services/search.service.d.ts +4 -4
  16. package/dist/services/search.service.js +10 -9
  17. package/dist/services/search.service.js.map +1 -1
  18. package/dist/spotify/client.d.ts +2 -0
  19. package/dist/spotify/client.js +170 -46
  20. package/dist/spotify/client.js.map +1 -1
  21. package/dist/spotify/types.d.ts +2 -0
  22. package/dist/ui/completions.js +10 -10
  23. package/dist/ui/interactive-search.d.ts +4 -1
  24. package/dist/ui/interactive-search.js +218 -55
  25. package/dist/ui/interactive-search.js.map +1 -1
  26. package/dist/ui/output.d.ts +27 -11
  27. package/dist/ui/output.js +87 -45
  28. package/dist/ui/output.js.map +1 -1
  29. package/dist/ui/prompts.d.ts +11 -1
  30. package/dist/ui/prompts.js +65 -13
  31. package/dist/ui/prompts.js.map +1 -1
  32. package/dist/ui/watch.js +3 -2
  33. package/dist/ui/watch.js.map +1 -1
  34. package/dist/utils/errors.js +2 -1
  35. package/dist/utils/errors.js.map +1 -1
  36. package/dist/utils/text.d.ts +2 -0
  37. package/dist/utils/text.js +10 -0
  38. package/dist/utils/text.js.map +1 -0
  39. package/package.json +2 -1
@@ -1,6 +1,7 @@
1
1
  import { emitKeypressEvents } from 'node:readline';
2
2
  import { stdin, stdout } from 'node:process';
3
- import { formatAlbum, formatArtist, formatPlaylist, formatTrack } from './output.js';
3
+ import { toError } from '../utils/errors.js';
4
+ import { formatAlbum, formatArtist, formatPlaylist, formatTrack, plainOutputStyles, sanitizeOneLineText, } from './output.js';
4
5
  const CATEGORIES = ['track', 'album', 'artist', 'playlist'];
5
6
  const CATEGORY_LABELS = {
6
7
  track: 'Tracks',
@@ -13,82 +14,185 @@ export async function runInteractiveSearch(options) {
13
14
  const terminal = options.terminal ?? createNodeInteractiveSearchTerminal();
14
15
  if (!terminal.isTTY)
15
16
  return { status: 'not-interactive' };
17
+ const styles = options.styles ?? plainOutputStyles;
18
+ const cache = new Map();
16
19
  let query = '';
17
20
  let category = 'track';
18
21
  let items = null;
19
22
  let selectedIndex = 0;
20
- terminal.setup();
23
+ let submitted = false;
24
+ let errorMessage = null;
25
+ const finish = (result) => {
26
+ terminal.cleanup();
27
+ return result;
28
+ };
21
29
  try {
22
- render(terminal, { query, category, items, selectedIndex });
30
+ terminal.setup();
31
+ render(terminal, { query, category, items, selectedIndex, submitted, errorMessage, styles });
23
32
  while (true) {
24
33
  const key = await terminal.readKey();
25
34
  if (key.type === 'escape')
26
- return { status: 'cancelled' };
27
- if (items === null) {
28
- if (key.type === 'character')
29
- query += key.value;
30
- else if (key.type === 'backspace')
31
- query = query.slice(0, -1);
32
- else if (isCategoryKey(key))
33
- category = moveCategory(category, key.type === 'left' ? -1 : 1);
34
- else if (key.type === 'enter' && query.trim()) {
35
- renderSearching(terminal, query, category);
36
- items = await search(options.search, category, query, options.limit);
37
- selectedIndex = 0;
38
- }
35
+ return finish({ status: 'cancelled' });
36
+ if (key.type === 'character') {
37
+ query += key.value;
38
+ items = null;
39
+ selectedIndex = 0;
40
+ submitted = false;
41
+ errorMessage = null;
42
+ cache.clear();
43
+ }
44
+ else if (key.type === 'backspace') {
45
+ query = removeLastCharacter(query);
46
+ items = null;
47
+ selectedIndex = 0;
48
+ submitted = false;
49
+ errorMessage = null;
50
+ cache.clear();
39
51
  }
40
52
  else if (isCategoryKey(key)) {
41
53
  category = moveCategory(category, key.type === 'left' ? -1 : 1);
42
- renderSearching(terminal, query, category);
43
- items = await search(options.search, category, query, options.limit);
54
+ selectedIndex = 0;
55
+ errorMessage = null;
56
+ if (submitted && query.trim()) {
57
+ const result = await loadResults(terminal, options, cache, query, category);
58
+ if (result.status === 'cancelled')
59
+ return finish({ status: 'cancelled' });
60
+ items = result.items;
61
+ errorMessage = result.errorMessage;
62
+ }
63
+ else {
64
+ items = null;
65
+ }
66
+ }
67
+ else if (key.type === 'enter' && items === null && query.trim()) {
68
+ submitted = true;
69
+ const result = await loadResults(terminal, options, cache, query, category);
70
+ if (result.status === 'cancelled')
71
+ return finish({ status: 'cancelled' });
72
+ items = result.items;
73
+ errorMessage = result.errorMessage;
44
74
  selectedIndex = 0;
45
75
  }
46
- else if (key.type === 'up' && items.length > 0) {
76
+ else if (key.type === 'up' && items && items.length > 0) {
47
77
  selectedIndex = (selectedIndex - 1 + items.length) % items.length;
78
+ errorMessage = null;
48
79
  }
49
- else if (key.type === 'down' && items.length > 0) {
80
+ else if (key.type === 'down' && items && items.length > 0) {
50
81
  selectedIndex = (selectedIndex + 1) % items.length;
82
+ errorMessage = null;
51
83
  }
52
- else if (key.type === 'enter' && items.length > 0) {
84
+ else if (key.type === 'enter' && items && items.length > 0) {
53
85
  const selected = items[selectedIndex];
54
86
  if (!selected)
55
87
  continue;
56
- if (category === 'track')
57
- await options.player.playTrack(selected.uri);
58
- else
59
- await options.player.playContext(selected.uri);
60
- return { status: 'played', category, uri: selected.uri };
88
+ renderStartingPlayback(terminal, selected, category, styles);
89
+ const playback = await runCancellableOperation(terminal, (signal) => category === 'track'
90
+ ? options.player.playTrack(selected.uri, signal)
91
+ : options.player.playContext(selected.uri, signal));
92
+ if (playback.status === 'cancelled')
93
+ return finish({ status: 'cancelled' });
94
+ if (playback.status === 'failed') {
95
+ errorMessage = sanitizeOneLineText(toError(playback.error).message) || 'Playback failed.';
96
+ }
97
+ else {
98
+ return finish({
99
+ status: 'played',
100
+ category,
101
+ uri: selected.uri,
102
+ label: playbackLabel(selected, category),
103
+ });
104
+ }
61
105
  }
62
- render(terminal, { query, category, items, selectedIndex });
106
+ render(terminal, { query, category, items, selectedIndex, submitted, errorMessage, styles });
63
107
  }
64
108
  }
65
- finally {
66
- terminal.cleanup();
109
+ catch (error) {
110
+ try {
111
+ terminal.cleanup();
112
+ }
113
+ catch {
114
+ // Preserve the original setup, rendering, search, or playback error.
115
+ }
116
+ throw error;
67
117
  }
68
118
  }
69
119
  export function createNodeInteractiveSearchTerminal() {
70
120
  let wasRaw = false;
121
+ let rawModeChanged = false;
122
+ let inputResumed = false;
123
+ let screenOpened = false;
124
+ let cleanedUp = true;
71
125
  return {
72
126
  isTTY: Boolean(stdin.isTTY && stdout.isTTY),
73
127
  setup() {
128
+ if (!cleanedUp)
129
+ return;
130
+ cleanedUp = false;
74
131
  wasRaw = Boolean(stdin.isRaw);
75
132
  emitKeypressEvents(stdin);
76
133
  stdin.setRawMode(true);
134
+ rawModeChanged = true;
77
135
  stdin.resume();
136
+ inputResumed = true;
137
+ screenOpened = true;
78
138
  stdout.write('\u001B[?1049h\u001B[?25l');
79
139
  },
80
140
  cleanup() {
81
- if (stdin.isTTY)
82
- stdin.setRawMode(wasRaw);
83
- stdin.pause();
84
- stdout.write('\u001B[?25h\u001B[?1049l');
141
+ if (cleanedUp)
142
+ return;
143
+ cleanedUp = true;
144
+ let firstError;
145
+ if (rawModeChanged && stdin.isTTY) {
146
+ try {
147
+ stdin.setRawMode(wasRaw);
148
+ }
149
+ catch (error) {
150
+ firstError = error;
151
+ }
152
+ }
153
+ rawModeChanged = false;
154
+ if (inputResumed) {
155
+ try {
156
+ stdin.pause();
157
+ }
158
+ catch (error) {
159
+ firstError ??= error;
160
+ }
161
+ }
162
+ inputResumed = false;
163
+ if (screenOpened) {
164
+ try {
165
+ stdout.write('\u001B[?25h\u001B[?1049l');
166
+ }
167
+ catch (error) {
168
+ firstError ??= error;
169
+ }
170
+ }
171
+ screenOpened = false;
172
+ if (firstError !== undefined)
173
+ throw firstError;
85
174
  },
86
- readKey() {
175
+ readKey(signal) {
87
176
  return new Promise((resolve) => {
177
+ let settled = false;
178
+ const finish = (key) => {
179
+ if (settled)
180
+ return;
181
+ settled = true;
182
+ stdin.removeListener('keypress', listener);
183
+ signal?.removeEventListener('abort', abortListener);
184
+ resolve(key);
185
+ };
88
186
  const listener = (character, key = {}) => {
89
- resolve(mapNodeKey(character, key));
187
+ finish(mapNodeKey(character, key));
90
188
  };
189
+ const abortListener = () => finish({ type: 'other' });
190
+ if (signal?.aborted) {
191
+ abortListener();
192
+ return;
193
+ }
91
194
  stdin.once('keypress', listener);
195
+ signal?.addEventListener('abort', abortListener, { once: true });
92
196
  });
93
197
  },
94
198
  write(value) {
@@ -97,47 +201,106 @@ export function createNodeInteractiveSearchTerminal() {
97
201
  };
98
202
  }
99
203
  function render(terminal, state) {
100
- const categoryBar = CATEGORIES.map((entry) => entry === state.category ? `[${CATEGORY_LABELS[entry]}]` : CATEGORY_LABELS[entry]).join(' ');
204
+ const categoryBar = CATEGORIES.map((entry) => entry === state.category
205
+ ? state.styles.heading(`[${CATEGORY_LABELS[entry]}]`)
206
+ : CATEGORY_LABELS[entry]).join(' ');
101
207
  const lines = [
102
- 'Spotify search',
208
+ state.styles.heading('Spotify search'),
103
209
  '',
104
- `${categoryBar}`,
210
+ categoryBar,
105
211
  '',
106
- `Search: ${state.query}${state.items === null ? '▌' : ''}`,
212
+ `Search: ${sanitizeOneLineText(state.query)}${state.items === null ? '▌' : ''}`,
107
213
  '',
108
214
  ];
215
+ if (state.errorMessage) {
216
+ lines.push(`Error: ${state.errorMessage}`, '');
217
+ }
109
218
  if (state.items === null) {
110
- lines.push('Type a query and press Enter. Tab switches category; Esc exits.');
219
+ lines.push(state.submitted
220
+ ? 'Press Enter to retry. Type to edit the query; Tab changes category; Esc exits.'
221
+ : 'Type a query and press Enter. Tab changes category; Esc exits.');
111
222
  }
112
223
  else if (state.items.length === 0) {
113
- lines.push(`No ${CATEGORY_LABELS[state.category].toLowerCase()} found for "${state.query.trim()}".`);
114
- lines.push('', 'Tab switches category; Esc exits.');
224
+ lines.push(`No ${CATEGORY_LABELS[state.category].toLowerCase()} found for "${sanitizeOneLineText(state.query.trim())}".`);
225
+ lines.push('', 'Type to edit the query; Tab changes category; Esc exits.');
115
226
  }
116
227
  else {
117
- lines.push(...state.items.map((item, index) => `${index === state.selectedIndex ? '›' : ' '} ${formatItem(item, state.category)}`), '', '↑/↓ selects; Tab switches category; Enter plays; Esc exits.');
228
+ lines.push(...state.items.map((item, index) => `${index === state.selectedIndex ? '›' : ' '} ${formatItem(item, state.category, state.styles)}`), '', '↑/↓ selects; Enter plays; type to edit; Tab changes category; Esc exits.');
118
229
  }
119
230
  terminal.write(`${CLEAR_SCREEN}${lines.join('\n')}\n`);
120
231
  }
121
- function renderSearching(terminal, query, category) {
122
- terminal.write(`${CLEAR_SCREEN}Searching ${CATEGORY_LABELS[category].toLowerCase()} for "${query.trim()}"…\n`);
232
+ async function loadResults(terminal, options, cache, query, category) {
233
+ const key = `${category}:${query.trim().toLocaleLowerCase()}`;
234
+ const cached = cache.get(key);
235
+ if (cached)
236
+ return { status: 'loaded', items: cached, errorMessage: null };
237
+ renderSearching(terminal, query, category, options.styles ?? plainOutputStyles);
238
+ const result = await runCancellableOperation(terminal, (signal) => search(options.search, category, query, options.limit, signal));
239
+ if (result.status === 'cancelled')
240
+ return result;
241
+ if (result.status === 'failed') {
242
+ return {
243
+ status: 'loaded',
244
+ items: null,
245
+ errorMessage: sanitizeOneLineText(toError(result.error).message) || 'Search failed.',
246
+ };
247
+ }
248
+ cache.set(key, result.value);
249
+ return { status: 'loaded', items: result.value, errorMessage: null };
250
+ }
251
+ function renderSearching(terminal, query, category, styles) {
252
+ terminal.write(`${CLEAR_SCREEN}${styles.heading(`Searching ${CATEGORY_LABELS[category].toLowerCase()}`)} for "${sanitizeOneLineText(query.trim())}"…\n\nEsc cancels and exits.\n`);
253
+ }
254
+ function renderStartingPlayback(terminal, item, category, styles) {
255
+ terminal.write(`${CLEAR_SCREEN}${styles.heading('Starting playback')}\n\n${formatItem(item, category, styles)}\n`);
123
256
  }
124
- async function search(service, category, query, limit) {
257
+ async function search(service, category, query, limit, signal) {
125
258
  if (category === 'track')
126
- return service.searchTracks(query, limit);
259
+ return service.searchTracks(query, limit, signal);
127
260
  if (category === 'album')
128
- return service.searchAlbums(query, limit);
261
+ return service.searchAlbums(query, limit, signal);
129
262
  if (category === 'artist')
130
- return service.searchArtists(query, limit);
131
- return service.searchPlaylists(query, limit);
263
+ return service.searchArtists(query, limit, signal);
264
+ return service.searchPlaylists(query, limit, signal);
132
265
  }
133
- function formatItem(item, category) {
266
+ async function runCancellableOperation(terminal, operation) {
267
+ const operationController = new AbortController();
268
+ const operationResult = operation(operationController.signal).then((value) => ({ status: 'completed', value }), (error) => ({ status: 'failed', error }));
269
+ while (true) {
270
+ const keyController = new AbortController();
271
+ const keyResult = terminal.readKey(keyController.signal).then((key) => ({
272
+ status: 'key',
273
+ key,
274
+ }));
275
+ const result = await Promise.race([operationResult, keyResult]);
276
+ if (result.status !== 'key') {
277
+ keyController.abort();
278
+ return result;
279
+ }
280
+ if (result.key.type === 'escape') {
281
+ operationController.abort();
282
+ return { status: 'cancelled' };
283
+ }
284
+ }
285
+ }
286
+ function formatItem(item, category, styles) {
134
287
  if (category === 'track')
135
- return formatTrack(item);
288
+ return formatTrack(item, undefined, styles);
136
289
  if (category === 'album')
137
- return formatAlbum(item);
290
+ return formatAlbum(item, undefined, styles);
138
291
  if (category === 'artist')
139
- return formatArtist(item);
140
- return formatPlaylist(item);
292
+ return formatArtist(item, undefined, styles);
293
+ return formatPlaylist(item, undefined, styles);
294
+ }
295
+ function playbackLabel(item, category) {
296
+ if (category === 'track') {
297
+ const track = item;
298
+ return `${sanitizeOneLineText(track.name)} — ${track.artists.map(sanitizeOneLineText).join(', ')}`;
299
+ }
300
+ return sanitizeOneLineText(item.name);
301
+ }
302
+ function removeLastCharacter(value) {
303
+ return Array.from(value).slice(0, -1).join('');
141
304
  }
142
305
  function isCategoryKey(key) {
143
306
  return key.type === 'tab' || key.type === 'left' || key.type === 'right';
@@ -1 +1 @@
1
- {"version":3,"file":"interactive-search.js","sourceRoot":"","sources":["../../src/ui/interactive-search.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"interactive-search.js","sourceRoot":"","sources":["../../src/ui/interactive-search.ts"],"names":[],"mappings":"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"}
@@ -1,17 +1,33 @@
1
1
  import type { Album, AlbumDetail, Artist, CurrentPlayback, Playlist, PlaylistDetail, RecentlyPlayedTrack, SavedTrack, Track } from '../services/models.js';
2
+ export { sanitizeOneLineText } from '../utils/text.js';
2
3
  export interface Output {
3
4
  log(message: string): void;
4
5
  error(message: string): void;
5
6
  }
6
7
  export declare const consoleOutput: Output;
7
- export declare function formatPlayback(playback: CurrentPlayback): string;
8
- export declare function formatTrack(track: Track, index?: number): string;
9
- export declare function formatAlbum(album: Album, index?: number): string;
10
- export declare function formatAlbumDetail(album: AlbumDetail): string;
11
- export declare function formatArtist(artist: Artist, index?: number): string;
12
- export declare function formatArtistDetail(artist: Artist): string;
13
- export declare function formatPlaylist(playlist: Playlist, index?: number): string;
14
- export declare function formatPlaylistOverview(playlist: Playlist): string;
15
- export declare function formatPlaylistDetail(playlist: PlaylistDetail): string;
16
- export declare function formatSavedTrack(item: SavedTrack, index?: number): string;
17
- export declare function formatRecentlyPlayed(item: RecentlyPlayedTrack, index?: number): string;
8
+ type OutputStyle = (value: string) => string;
9
+ export interface OutputStyles {
10
+ name: OutputStyle;
11
+ heading: OutputStyle;
12
+ metadata: OutputStyle;
13
+ progress: OutputStyle;
14
+ }
15
+ export declare const plainOutputStyles: OutputStyles;
16
+ export declare const plainStyles: OutputStyles;
17
+ interface OutputStyleOptions {
18
+ isTTY?: boolean | undefined;
19
+ env?: NodeJS.ProcessEnv | undefined;
20
+ }
21
+ export declare function createOutputStyles(isTTY?: boolean, env?: NodeJS.ProcessEnv): OutputStyles;
22
+ export declare function createOutputStyles(options?: OutputStyleOptions): OutputStyles;
23
+ export declare function formatPlayback(playback: CurrentPlayback, styles?: OutputStyles): string;
24
+ export declare function formatTrack(track: Track, index?: number, styles?: OutputStyles): string;
25
+ export declare function formatAlbum(album: Album, index?: number, styles?: OutputStyles): string;
26
+ export declare function formatAlbumDetail(album: AlbumDetail, styles?: OutputStyles): string;
27
+ export declare function formatArtist(artist: Artist, index?: number, styles?: OutputStyles): string;
28
+ export declare function formatArtistDetail(artist: Artist, styles?: OutputStyles): string;
29
+ export declare function formatPlaylist(playlist: Playlist, index?: number, styles?: OutputStyles): string;
30
+ export declare function formatPlaylistOverview(playlist: Playlist, styles?: OutputStyles): string;
31
+ export declare function formatPlaylistDetail(playlist: PlaylistDetail, styles?: OutputStyles): string;
32
+ export declare function formatSavedTrack(item: SavedTrack, index?: number, styles?: OutputStyles): string;
33
+ export declare function formatRecentlyPlayed(item: RecentlyPlayedTrack, index?: number, styles?: OutputStyles): string;
package/dist/ui/output.js CHANGED
@@ -1,81 +1,123 @@
1
+ import { createColors } from 'picocolors';
1
2
  import { createProgressBar, formatDuration } from '../utils/time.js';
3
+ import { sanitizeOneLineText } from '../utils/text.js';
4
+ export { sanitizeOneLineText } from '../utils/text.js';
2
5
  export const consoleOutput = {
3
6
  log: (message) => console.log(message),
4
7
  error: (message) => console.error(message),
5
8
  };
6
- export function formatPlayback(playback) {
9
+ const identity = (value) => value;
10
+ export const plainOutputStyles = Object.freeze({
11
+ name: identity,
12
+ heading: identity,
13
+ metadata: identity,
14
+ progress: identity,
15
+ });
16
+ export const plainStyles = plainOutputStyles;
17
+ export function createOutputStyles(isTTYOrOptions = process.stdout.isTTY, environment = process.env) {
18
+ const isOptions = typeof isTTYOrOptions === 'object';
19
+ const isTTY = isOptions
20
+ ? (isTTYOrOptions.isTTY ?? process.stdout.isTTY)
21
+ : isTTYOrOptions;
22
+ const env = isOptions ? (isTTYOrOptions.env ?? process.env) : environment;
23
+ const colors = createColors(isTTY === true && !('NO_COLOR' in env));
24
+ return {
25
+ name: colors.bold,
26
+ heading: colors.bold,
27
+ metadata: colors.dim,
28
+ progress: colors.green,
29
+ };
30
+ }
31
+ export function formatPlayback(playback, styles = plainOutputStyles) {
7
32
  const indicator = playback.isPlaying ? '▶' : '⏸';
8
- const artists = playback.track.artists.join(', ');
9
- const progress = Math.min(playback.progressMs, playback.track.durationMs);
33
+ const artists = playback.track.artists.map(sanitizeOneLineText).join(', ');
34
+ const duration = Math.max(0, playback.track.durationMs);
35
+ const progress = Math.min(Math.max(0, playback.progressMs), duration);
36
+ const progressLine = `${formatDuration(progress)} ${createProgressBar(progress, duration)} ${formatDuration(duration)}`;
10
37
  return [
11
- `${indicator} ${playback.track.name}`,
12
- `${artists} · ${playback.track.album}`,
38
+ `${indicator} ${styles.name(sanitizeOneLineText(playback.track.name))}`,
39
+ styles.metadata(`${artists} · ${sanitizeOneLineText(playback.track.album)}`),
13
40
  '',
14
- `${formatDuration(progress)} ${createProgressBar(progress, playback.track.durationMs)} ${formatDuration(playback.track.durationMs)}`,
41
+ styles.progress(progressLine),
15
42
  ].join('\n');
16
43
  }
17
- export function formatTrack(track, index) {
44
+ export function formatTrack(track, index, styles = plainOutputStyles) {
18
45
  const prefix = index === undefined ? '' : `${index + 1}. `;
19
- return `${prefix}${track.name} ${track.artists.join(', ')} · ${track.album}`;
46
+ const artists = track.artists.map(sanitizeOneLineText).join(', ');
47
+ return `${prefix}${styles.name(sanitizeOneLineText(track.name))} — ${styles.metadata(artists)} · ${styles.metadata(sanitizeOneLineText(track.album))}`;
20
48
  }
21
- export function formatAlbum(album, index) {
49
+ export function formatAlbum(album, index, styles = plainOutputStyles) {
22
50
  const prefix = index === undefined ? '' : `${index + 1}. `;
23
- const release = album.releaseDate ? ` · ${album.releaseDate}` : '';
24
- return `${prefix}${album.name} ${album.artists.join(', ')} · ${album.totalTracks} tracks${release}`;
51
+ const release = album.releaseDate
52
+ ? ` · ${styles.metadata(sanitizeOneLineText(album.releaseDate))}`
53
+ : '';
54
+ const artists = album.artists.map(sanitizeOneLineText).join(', ');
55
+ return `${prefix}${styles.name(sanitizeOneLineText(album.name))} — ${styles.metadata(artists)} · ${styles.metadata(`${album.totalTracks} tracks`)}${release}`;
25
56
  }
26
- export function formatAlbumDetail(album) {
27
- const lines = [formatAlbum(album), '', 'Tracks:'];
57
+ export function formatAlbumDetail(album, styles = plainOutputStyles) {
58
+ const lines = [formatAlbum(album, undefined, styles), '', styles.heading('Tracks:')];
28
59
  if (album.tracks.length === 0)
29
60
  lines.push('No playable tracks found.');
30
- else
31
- lines.push(...album.tracks.map((track, index) => formatTrack(track, index)));
61
+ else {
62
+ lines.push(...album.tracks.map((track, index) => formatTrack(track, index, styles)));
63
+ }
32
64
  if (album.tracks.length < album.totalTracks) {
33
- lines.push(`Showing ${album.tracks.length} of ${album.totalTracks} tracks.`);
65
+ lines.push(styles.metadata(`Showing ${album.tracks.length} of ${album.totalTracks} tracks.`));
66
+ }
67
+ if (album.externalUrl) {
68
+ lines.push('', styles.metadata(`Spotify: ${sanitizeOneLineText(album.externalUrl)}`));
34
69
  }
35
- if (album.externalUrl)
36
- lines.push('', `Spotify: ${album.externalUrl}`);
37
70
  return lines.join('\n');
38
71
  }
39
- export function formatArtist(artist, index) {
72
+ export function formatArtist(artist, index, styles = plainOutputStyles) {
40
73
  const prefix = index === undefined ? '' : `${index + 1}. `;
41
- return `${prefix}${artist.name}`;
74
+ return `${prefix}${styles.name(sanitizeOneLineText(artist.name))}`;
42
75
  }
43
- export function formatArtistDetail(artist) {
44
- const lines = [formatArtist(artist)];
45
- if (artist.externalUrl)
46
- lines.push(`Spotify: ${artist.externalUrl}`);
76
+ export function formatArtistDetail(artist, styles = plainOutputStyles) {
77
+ const lines = [formatArtist(artist, undefined, styles)];
78
+ if (artist.externalUrl) {
79
+ lines.push(styles.metadata(`Spotify: ${sanitizeOneLineText(artist.externalUrl)}`));
80
+ }
47
81
  return lines.join('\n');
48
82
  }
49
- export function formatPlaylist(playlist, index) {
83
+ export function formatPlaylist(playlist, index, styles = plainOutputStyles) {
50
84
  const prefix = index === undefined ? '' : `${index + 1}. `;
51
85
  const itemLabel = playlist.totalTracks === 1 ? 'item' : 'items';
52
- return `${prefix}${playlist.name} ${playlist.ownerName} · ${playlist.totalTracks} ${itemLabel}`;
86
+ const metadata = `${sanitizeOneLineText(playlist.ownerName)} · ${playlist.totalTracks} ${itemLabel}`;
87
+ return `${prefix}${styles.name(sanitizeOneLineText(playlist.name))} — ${styles.metadata(metadata)}`;
53
88
  }
54
- export function formatPlaylistOverview(playlist) {
55
- const lines = [formatPlaylist(playlist)];
56
- if (playlist.description)
57
- lines.push(playlist.description);
58
- if (playlist.externalUrl)
59
- lines.push(`Spotify: ${playlist.externalUrl}`);
89
+ export function formatPlaylistOverview(playlist, styles = plainOutputStyles) {
90
+ const lines = [formatPlaylist(playlist, undefined, styles)];
91
+ if (playlist.description) {
92
+ lines.push(styles.metadata(sanitizeOneLineText(playlist.description)));
93
+ }
94
+ if (playlist.externalUrl) {
95
+ lines.push(styles.metadata(`Spotify: ${sanitizeOneLineText(playlist.externalUrl)}`));
96
+ }
60
97
  return lines.join('\n');
61
98
  }
62
- export function formatPlaylistDetail(playlist) {
63
- const lines = [formatPlaylist(playlist)];
64
- if (playlist.description)
65
- lines.push(playlist.description);
66
- lines.push('', 'Tracks:');
99
+ export function formatPlaylistDetail(playlist, styles = plainOutputStyles) {
100
+ const lines = [formatPlaylist(playlist, undefined, styles)];
101
+ if (playlist.description) {
102
+ lines.push(styles.metadata(sanitizeOneLineText(playlist.description)));
103
+ }
104
+ lines.push('', styles.heading('Tracks:'));
67
105
  if (playlist.tracks.length === 0)
68
106
  lines.push('No playable tracks found.');
69
- else
70
- lines.push(...playlist.tracks.map((track, index) => formatTrack(track, index)));
71
- if (playlist.externalUrl)
72
- lines.push('', `Spotify: ${playlist.externalUrl}`);
107
+ else {
108
+ lines.push(...playlist.tracks.map((track, index) => formatTrack(track, index, styles)));
109
+ }
110
+ if (playlist.externalUrl) {
111
+ lines.push('', styles.metadata(`Spotify: ${sanitizeOneLineText(playlist.externalUrl)}`));
112
+ }
73
113
  return lines.join('\n');
74
114
  }
75
- export function formatSavedTrack(item, index) {
76
- return `${formatTrack(item.track, index)} · liked ${item.addedAt}`;
115
+ export function formatSavedTrack(item, index, styles = plainOutputStyles) {
116
+ const playedMetadata = styles.metadata(`liked ${sanitizeOneLineText(item.addedAt)}`);
117
+ return `${formatTrack(item.track, index, styles)} · ${playedMetadata}`;
77
118
  }
78
- export function formatRecentlyPlayed(item, index) {
79
- return `${formatTrack(item.track, index)} · played ${item.playedAt}`;
119
+ export function formatRecentlyPlayed(item, index, styles = plainOutputStyles) {
120
+ const playedMetadata = styles.metadata(`played ${sanitizeOneLineText(item.playedAt)}`);
121
+ return `${formatTrack(item.track, index, styles)} · ${playedMetadata}`;
80
122
  }
81
123
  //# sourceMappingURL=output.js.map