morille 0.1.0 → 0.4.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.
- package/README.md +15 -3
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +2 -0
- package/dist/app.js.map +1 -0
- package/dist/components/animated-subtitle.d.ts +19 -0
- package/dist/components/animated-subtitle.d.ts.map +1 -0
- package/dist/components/animated-subtitle.js +109 -0
- package/dist/components/animated-subtitle.js.map +1 -0
- package/dist/components/animated-title.d.ts +16 -0
- package/dist/components/animated-title.d.ts.map +1 -0
- package/dist/components/animated-title.js +47 -0
- package/dist/components/animated-title.js.map +1 -0
- package/dist/components/browse-detail-view.d.ts.map +1 -0
- package/dist/components/browse-detail-view.js +2 -1
- package/dist/components/browse-detail-view.js.map +1 -0
- package/dist/components/device-indicator.d.ts +18 -0
- package/dist/components/device-indicator.d.ts.map +1 -0
- package/dist/components/device-indicator.js +53 -0
- package/dist/components/device-indicator.js.map +1 -0
- package/dist/components/device-picker-view.d.ts +10 -0
- package/dist/components/device-picker-view.d.ts.map +1 -0
- package/dist/components/device-picker-view.js +65 -0
- package/dist/components/device-picker-view.js.map +1 -0
- package/dist/components/lyrics-panel.d.ts.map +1 -0
- package/dist/components/lyrics-view.d.ts.map +1 -0
- package/dist/components/lyrics-view.js +2 -1
- package/dist/components/lyrics-view.js.map +1 -0
- package/dist/components/panel-content.d.ts.map +1 -0
- package/dist/components/panel-content.js +4 -0
- package/dist/components/panel-content.js.map +1 -0
- package/dist/components/playback-status.d.ts.map +1 -0
- package/dist/components/player.d.ts.map +1 -1
- package/dist/components/player.js +46 -10
- package/dist/components/player.js.map +1 -1
- package/dist/components/progress-bar.d.ts.map +1 -0
- package/dist/components/progress-bar.js +2 -1
- package/dist/components/progress-bar.js.map +1 -0
- package/dist/components/queue-view.d.ts.map +1 -0
- package/dist/components/queue-view.js +2 -1
- package/dist/components/queue-view.js.map +1 -0
- package/dist/components/search-panel.d.ts.map +1 -0
- package/dist/components/search-panel.js +3 -2
- package/dist/components/search-panel.js.map +1 -0
- package/dist/components/shimmer.d.ts.map +1 -0
- package/dist/components/shimmer.js +2 -2
- package/dist/components/shimmer.js.map +1 -0
- package/dist/components/track-facts.d.ts +13 -0
- package/dist/components/track-facts.d.ts.map +1 -0
- package/dist/components/track-facts.js +52 -0
- package/dist/components/track-facts.js.map +1 -0
- package/dist/components/track-info-skeleton.d.ts.map +1 -0
- package/dist/components/track-info.d.ts.map +1 -0
- package/dist/components/track-info.js +2 -1
- package/dist/components/track-info.js.map +1 -0
- package/dist/config.d.ts +3 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -4
- package/dist/config.js.map +1 -1
- package/dist/contexts/devices-context.d.ts +28 -0
- package/dist/contexts/devices-context.d.ts.map +1 -0
- package/dist/contexts/devices-context.js +33 -0
- package/dist/contexts/devices-context.js.map +1 -0
- package/dist/contexts/lyrics-context.d.ts.map +1 -0
- package/dist/contexts/panel-mode-context.d.ts +2 -1
- package/dist/contexts/panel-mode-context.d.ts.map +1 -0
- package/dist/contexts/panel-mode-context.js +3 -0
- package/dist/contexts/panel-mode-context.js.map +1 -0
- package/dist/contexts/queue-context.d.ts.map +1 -0
- package/dist/contexts/search-context.d.ts.map +1 -0
- package/dist/hooks/use-album-art.d.ts.map +1 -0
- package/dist/hooks/use-album-art.js +3 -1
- package/dist/hooks/use-album-art.js.map +1 -0
- package/dist/hooks/use-browse.d.ts.map +1 -0
- package/dist/hooks/use-browse.js +7 -3
- package/dist/hooks/use-browse.js.map +1 -0
- package/dist/hooks/use-devices.d.ts +24 -0
- package/dist/hooks/use-devices.d.ts.map +1 -0
- package/dist/hooks/use-devices.js +106 -0
- package/dist/hooks/use-devices.js.map +1 -0
- package/dist/hooks/use-lyrics.d.ts.map +1 -0
- package/dist/hooks/use-playback.d.ts +7 -0
- package/dist/hooks/use-playback.d.ts.map +1 -0
- package/dist/hooks/use-playback.js +59 -18
- package/dist/hooks/use-playback.js.map +1 -0
- package/dist/hooks/use-player-input.d.ts.map +1 -0
- package/dist/hooks/use-player-input.js +31 -2
- package/dist/hooks/use-player-input.js.map +1 -0
- package/dist/hooks/use-queue.d.ts.map +1 -0
- package/dist/hooks/use-queue.js +8 -3
- package/dist/hooks/use-queue.js.map +1 -0
- package/dist/hooks/use-search.d.ts.map +1 -0
- package/dist/hooks/use-search.js +2 -1
- package/dist/hooks/use-search.js.map +1 -0
- package/dist/icons.d.ts +29 -0
- package/dist/icons.d.ts.map +1 -0
- package/dist/icons.js +43 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +9 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +15 -0
- package/dist/logger.js.map +1 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/spotify/auth.d.ts.map +1 -0
- package/dist/spotify/auth.js +3 -1
- package/dist/spotify/auth.js.map +1 -0
- package/dist/spotify/client.d.ts.map +1 -0
- package/dist/spotify/client.js +3 -1
- package/dist/spotify/client.js.map +1 -0
- package/dist/spotify/devices.d.ts +36 -0
- package/dist/spotify/devices.d.ts.map +1 -0
- package/dist/spotify/devices.js +72 -0
- package/dist/spotify/devices.js.map +1 -0
- package/dist/spotify/fetch-with-retry.d.ts.map +1 -0
- package/dist/spotify/fetch-with-retry.js +5 -4
- package/dist/spotify/fetch-with-retry.js.map +1 -0
- package/dist/spotify/lyrics.d.ts.map +1 -0
- package/dist/spotify/lyrics.js +5 -2
- package/dist/spotify/lyrics.js.map +1 -0
- package/dist/spotify/playback.d.ts +9 -0
- package/dist/spotify/playback.d.ts.map +1 -0
- package/dist/spotify/playback.js +40 -8
- package/dist/spotify/playback.js.map +1 -0
- package/dist/spotify/search.d.ts.map +1 -0
- package/dist/spotify/search.js +4 -4
- package/dist/spotify/search.js.map +1 -0
- package/dist/spotify/track-facts.d.ts +9 -0
- package/dist/spotify/track-facts.d.ts.map +1 -0
- package/dist/spotify/track-facts.js +95 -0
- package/dist/spotify/track-facts.js.map +1 -0
- package/package.json +21 -1
package/dist/hooks/use-search.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import { SEARCH_DEBOUNCE_MS } from '../config.js';
|
|
3
|
+
import { logger } from '../logger.js';
|
|
3
4
|
import { searchSpotify } from '../spotify/search.js';
|
|
4
5
|
/**
|
|
5
6
|
* Manages search state with debounced API calls and deduplication.
|
|
@@ -26,7 +27,7 @@ export function useSearch(client, enabled) {
|
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
29
|
catch (error) {
|
|
29
|
-
|
|
30
|
+
logger.error(`Search failed for query "${q}":`, error);
|
|
30
31
|
}
|
|
31
32
|
finally {
|
|
32
33
|
if (inflightRef.current === q) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-search.js","sourceRoot":"","sources":["../../src/hooks/use-search.ts"],"names":[],"mappings":"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"}
|
package/dist/icons.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralised UI characters used across the player.
|
|
3
|
+
*
|
|
4
|
+
* Grouping all glyphs in one place makes it easy to swap them out for
|
|
5
|
+
* a different aesthetic, switch between ASCII and Unicode flavours,
|
|
6
|
+
* and audit which characters the app actually relies on (e.g. when
|
|
7
|
+
* checking which fonts/terminals will render correctly).
|
|
8
|
+
*
|
|
9
|
+
* Convention: trailing space is part of the icon when the icon is followed
|
|
10
|
+
* by a label on the same row, so consumers can do `{ICON}{label}` directly.
|
|
11
|
+
*/
|
|
12
|
+
export declare const ICON_PLAYER_PLAY = "> ";
|
|
13
|
+
export declare const ICON_PLAYER_PAUSE = "|| ";
|
|
14
|
+
export declare const ICON_LIST_PLAYING = "\u25B6 ";
|
|
15
|
+
export declare const ICON_LIST_SELECTED = "> ";
|
|
16
|
+
export declare const ICON_LIST_GUTTER = " ";
|
|
17
|
+
export declare const ICON_INSTRUMENTAL = "\u266A";
|
|
18
|
+
export declare const ICON_BAR_FILLED = "\u2588";
|
|
19
|
+
export declare const ICON_BAR_EMPTY = "\u2591";
|
|
20
|
+
export declare const ICON_SHIMMER = "\u2591";
|
|
21
|
+
export declare const ICON_ARROW_VERTICAL = "\u2191/\u2193";
|
|
22
|
+
export declare const ICON_ARROW_HORIZONTAL = "\u2190/\u2192";
|
|
23
|
+
export declare const ICON_DEVICE_COMPUTER = "\u25AF";
|
|
24
|
+
export declare const ICON_DEVICE_PHONE = "\u25A2";
|
|
25
|
+
export declare const ICON_DEVICE_SPEAKER = "\u25C9";
|
|
26
|
+
export declare const ICON_DEVICE_TV = "\u25AD";
|
|
27
|
+
export declare const ICON_DEVICE_CAR = "\u25A3";
|
|
28
|
+
export declare const ICON_DEVICE_GAME = "\u25C6";
|
|
29
|
+
export declare const ICON_DEVICE_UNKNOWN = "\u00B7";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../src/icons.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,eAAO,MAAM,gBAAgB,OAAO,CAAC;AACrC,eAAO,MAAM,iBAAiB,QAAQ,CAAC;AAIvC,eAAO,MAAM,iBAAiB,YAAY,CAAC;AAE3C,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAIrC,eAAO,MAAM,iBAAiB,WAAW,CAAC;AAG1C,eAAO,MAAM,eAAe,WAAW,CAAC;AACxC,eAAO,MAAM,cAAc,WAAW,CAAC;AAKvC,eAAO,MAAM,YAAY,WAAW,CAAC;AAGrC,eAAO,MAAM,mBAAmB,kBAAkB,CAAC;AACnD,eAAO,MAAM,qBAAqB,kBAAkB,CAAC;AAGrD,eAAO,MAAM,oBAAoB,WAAW,CAAC;AAC7C,eAAO,MAAM,iBAAiB,WAAW,CAAC;AAC1C,eAAO,MAAM,mBAAmB,WAAW,CAAC;AAC5C,eAAO,MAAM,cAAc,WAAW,CAAC;AACvC,eAAO,MAAM,eAAe,WAAW,CAAC;AACxC,eAAO,MAAM,gBAAgB,WAAW,CAAC;AACzC,eAAO,MAAM,mBAAmB,WAAW,CAAC"}
|
package/dist/icons.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralised UI characters used across the player.
|
|
3
|
+
*
|
|
4
|
+
* Grouping all glyphs in one place makes it easy to swap them out for
|
|
5
|
+
* a different aesthetic, switch between ASCII and Unicode flavours,
|
|
6
|
+
* and audit which characters the app actually relies on (e.g. when
|
|
7
|
+
* checking which fonts/terminals will render correctly).
|
|
8
|
+
*
|
|
9
|
+
* Convention: trailing space is part of the icon when the icon is followed
|
|
10
|
+
* by a label on the same row, so consumers can do `{ICON}{label}` directly.
|
|
11
|
+
*/
|
|
12
|
+
// ----- Player status (track-info header — global play/pause indicator) -----
|
|
13
|
+
// Kept ASCII so it renders cleanly even in terminals/fonts without media glyphs.
|
|
14
|
+
export const ICON_PLAYER_PLAY = '> ';
|
|
15
|
+
export const ICON_PLAYER_PAUSE = '|| ';
|
|
16
|
+
// ----- List item markers (queue, search results, browse drill-downs) -----
|
|
17
|
+
// Marker shown next to a track/album/playlist that matches current playback.
|
|
18
|
+
export const ICON_LIST_PLAYING = '\u25B6 '; // ▶
|
|
19
|
+
// Cursor shown next to the highlighted (selected) item in a navigable list.
|
|
20
|
+
export const ICON_LIST_SELECTED = '> ';
|
|
21
|
+
// Empty gutter for items that are neither selected nor playing — keeps columns aligned.
|
|
22
|
+
export const ICON_LIST_GUTTER = ' ';
|
|
23
|
+
// ----- Lyrics -----
|
|
24
|
+
// Placeholder shown for empty/instrumental lines in the synced view.
|
|
25
|
+
export const ICON_INSTRUMENTAL = '\u266A'; // ♪
|
|
26
|
+
// ----- Progress bar -----
|
|
27
|
+
export const ICON_BAR_FILLED = '\u2588'; // █
|
|
28
|
+
export const ICON_BAR_EMPTY = '\u2591'; // ░
|
|
29
|
+
// ----- Shimmer / skeleton placeholder -----
|
|
30
|
+
// Same glyph as ICON_BAR_EMPTY but kept as a separate constant so the two can
|
|
31
|
+
// diverge later without touching every consumer.
|
|
32
|
+
export const ICON_SHIMMER = '\u2591'; // ░
|
|
33
|
+
// ----- Keyboard hint labels -----
|
|
34
|
+
export const ICON_ARROW_VERTICAL = '\u2191/\u2193'; // ↑/↓
|
|
35
|
+
export const ICON_ARROW_HORIZONTAL = '\u2190/\u2192'; // ←/→
|
|
36
|
+
// ----- Device types (device picker + header indicator) -----
|
|
37
|
+
export const ICON_DEVICE_COMPUTER = '\u25AF'; // ▯
|
|
38
|
+
export const ICON_DEVICE_PHONE = '\u25A2'; // ▢
|
|
39
|
+
export const ICON_DEVICE_SPEAKER = '\u25C9'; // ◉
|
|
40
|
+
export const ICON_DEVICE_TV = '\u25AD'; // ▭
|
|
41
|
+
export const ICON_DEVICE_CAR = '\u25A3'; // ▣
|
|
42
|
+
export const ICON_DEVICE_GAME = '\u25C6'; // ◆
|
|
43
|
+
export const ICON_DEVICE_UNKNOWN = '\u00B7'; // ·
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"icons.js","sourceRoot":"","sources":["../src/icons.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,8EAA8E;AAC9E,iFAAiF;AACjF,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC;AACrC,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAC;AAEvC,4EAA4E;AAC5E,6EAA6E;AAC7E,MAAM,CAAC,MAAM,iBAAiB,GAAG,SAAS,CAAC,CAAC,IAAI;AAChD,4EAA4E;AAC5E,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC;AACvC,wFAAwF;AACxF,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAErC,qBAAqB;AACrB,qEAAqE;AACrE,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC,CAAC,IAAI;AAE/C,2BAA2B;AAC3B,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC,CAAC,IAAI;AAC7C,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAC,CAAC,IAAI;AAE5C,6CAA6C;AAC7C,8EAA8E;AAC9E,iDAAiD;AACjD,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC,CAAC,IAAI;AAE1C,mCAAmC;AACnC,MAAM,CAAC,MAAM,mBAAmB,GAAG,eAAe,CAAC,CAAC,MAAM;AAC1D,MAAM,CAAC,MAAM,qBAAqB,GAAG,eAAe,CAAC,CAAC,MAAM;AAE5D,8DAA8D;AAC9D,MAAM,CAAC,MAAM,oBAAoB,GAAG,QAAQ,CAAC,CAAC,IAAI;AAClD,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC,CAAC,IAAI;AAC/C,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAQ,CAAC,CAAC,IAAI;AACjD,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAC,CAAC,IAAI;AAC5C,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC,CAAC,IAAI;AAC7C,MAAM,CAAC,MAAM,gBAAgB,GAAG,QAAQ,CAAC,CAAC,IAAI;AAC9C,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAQ,CAAC,CAAC,IAAI"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import './logger.js';
|
|
2
3
|
export type { RepeatMode, TrackInfo } from './spotify/playback.js';
|
|
3
4
|
export { getCurrentTrackInfo, getPlaybackState, pause, play, playInContext, playUri, seek, setRepeat, setShuffle, setVolume, skipNext, skipPrevious, togglePlayback, } from './spotify/playback.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAMA,OAAO,aAAa,CAAC;AASrB,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,KAAK,EACL,IAAI,EACJ,aAAa,EACb,OAAO,EACP,IAAI,EACJ,SAAS,EACT,UAAU,EACV,SAAS,EACT,QAAQ,EACR,YAAY,EACZ,cAAc,GACf,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
import { existsSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { loadEnvFile } from 'node:process';
|
|
5
|
+
// Side-effect import: captures the `--debug` flag from `process.argv` at
|
|
6
|
+
// module load before anything else observes argv. Must come first.
|
|
7
|
+
import './logger.js';
|
|
8
|
+
process.argv = process.argv.filter((arg) => arg !== '--debug');
|
|
5
9
|
const envPath = join(import.meta.dirname, '../.env');
|
|
6
10
|
if (existsSync(envPath)) {
|
|
7
11
|
loadEnvFile(envPath);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,yEAAyE;AACzE,mEAAmE;AACnE,OAAO,aAAa,CAAC;AAErB,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC;AAE/D,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;AACrD,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;IACxB,WAAW,CAAC,OAAO,CAAC,CAAC;AACvB,CAAC;AAGD,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,KAAK,EACL,IAAI,EACJ,aAAa,EACb,OAAO,EACP,IAAI,EACJ,SAAS,EACT,UAAU,EACV,SAAS,EACT,QAAQ,EACR,YAAY,EACZ,cAAc,GACf,MAAM,uBAAuB,CAAC;AAE/B,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC"}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single sanctioned sink for stderr output in morille. All `console.error`
|
|
3
|
+
* calls across the codebase go through this object. When neither the
|
|
4
|
+
* `--debug` CLI flag nor the `DEBUG=1` env var is set, `logger.error` is a
|
|
5
|
+
* no-op so the ink TUI stays silent.
|
|
6
|
+
*/
|
|
7
|
+
export declare const logger: {
|
|
8
|
+
error: (...args: unknown[]) => void;
|
|
9
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,eAAO,MAAM,MAAM;IACjB,KAAK,YAAY,OAAO,EAAE,KAAG,IAAI;CAIlC,CAAC"}
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// biome-ignore lint/complexity/useLiteralKeys: TS noPropertyAccessFromIndexSignature requires bracket notation
|
|
2
|
+
const debugEnabled = process.argv.includes('--debug') || process.env['DEBUG'] === '1';
|
|
3
|
+
/**
|
|
4
|
+
* Single sanctioned sink for stderr output in morille. All `console.error`
|
|
5
|
+
* calls across the codebase go through this object. When neither the
|
|
6
|
+
* `--debug` CLI flag nor the `DEBUG=1` env var is set, `logger.error` is a
|
|
7
|
+
* no-op so the ink TUI stays silent.
|
|
8
|
+
*/
|
|
9
|
+
export const logger = {
|
|
10
|
+
error: (...args) => {
|
|
11
|
+
if (!debugEnabled)
|
|
12
|
+
return;
|
|
13
|
+
console.error(...args);
|
|
14
|
+
},
|
|
15
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,+GAA+G;AAC/G,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,GAAG,CAAC;AAEtF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,KAAK,EAAE,CAAC,GAAG,IAAe,EAAQ,EAAE;QAClC,IAAI,CAAC,YAAY;YAAE,OAAO;QAC1B,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;IACzB,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/spotify/auth.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAgH3D,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;CAC9D,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,WAAW,CAAC,CAoBxG;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,WAAW,GAAG,IAAI,CAYpD;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAKnD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAK1D;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CA6B7F"}
|
package/dist/spotify/auth.js
CHANGED
|
@@ -3,6 +3,7 @@ import { createHash, randomBytes } from 'node:crypto';
|
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
4
4
|
import { createServer } from 'node:http';
|
|
5
5
|
import { AUTH_REDIRECT_PORT, AUTH_REDIRECT_URI, AUTH_SCOPES, CONFIG_DIR, TOKEN_PATH } from '../config.js';
|
|
6
|
+
import { logger } from '../logger.js';
|
|
6
7
|
function generateCodeVerifier() {
|
|
7
8
|
return randomBytes(64).toString('base64url');
|
|
8
9
|
}
|
|
@@ -124,7 +125,8 @@ export function loadStoredToken() {
|
|
|
124
125
|
const data = readFileSync(TOKEN_PATH, 'utf-8');
|
|
125
126
|
return JSON.parse(data);
|
|
126
127
|
}
|
|
127
|
-
catch {
|
|
128
|
+
catch (err) {
|
|
129
|
+
logger.error(`Failed to read stored token at ${TOKEN_PATH}:`, err);
|
|
128
130
|
return null;
|
|
129
131
|
}
|
|
130
132
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/spotify/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/spotify/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAGrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAIrD;;;;;;GAMG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAapC;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,CAAC,CAoB9G"}
|
package/dist/spotify/client.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SpotifyApi } from '@spotify/web-api-ts-sdk';
|
|
2
2
|
import { DEFAULT_SPOTIFY_CLIENT_ID } from '../config.js';
|
|
3
|
+
import { logger } from '../logger.js';
|
|
3
4
|
import { authenticate, isTokenExpired, loadStoredToken, refreshToken, storeToken } from './auth.js';
|
|
4
5
|
import { fetchWithRetry } from './fetch-with-retry.js';
|
|
5
6
|
/**
|
|
@@ -34,7 +35,8 @@ export async function createSpotifyClient(clientId, options) {
|
|
|
34
35
|
try {
|
|
35
36
|
token = await refreshToken(clientId, token);
|
|
36
37
|
}
|
|
37
|
-
catch {
|
|
38
|
+
catch (err) {
|
|
39
|
+
logger.error('Token refresh failed, falling back to full re-auth:', err);
|
|
38
40
|
token = null;
|
|
39
41
|
}
|
|
40
42
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/spotify/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACpG,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD;;;;;;GAMG;AACH,MAAM,UAAU,WAAW;IACzB,+GAA+G;IAC/G,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;IACjD,MAAM,QAAQ,GAAG,OAAO,EAAE,IAAI,EAAE,IAAI,yBAAyB,CAAC;IAC9D,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACb,mCAAmC;YACjC,oEAAoE;YACpE,wEAAwE;YACxE,mDAAmD,CACtD,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,QAAgB,EAAE,OAA6B;IACvF,IAAI,KAAK,GAAG,eAAe,EAAE,CAAC;IAE9B,IAAI,KAAK,IAAI,cAAc,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,aAAa,EAAE,CAAC;QAC1D,IAAI,CAAC;YACH,KAAK,GAAG,MAAM,YAAY,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC9C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,CAAC,qDAAqD,EAAE,GAAG,CAAC,CAAC;YACzE,KAAK,GAAG,IAAI,CAAC;QACf,CAAC;IACH,CAAC;IAED,IAAI,CAAC,KAAK,IAAI,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;QACpC,KAAK,GAAG,MAAM,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC9C,UAAU,CAAC,KAAK,CAAC,CAAC;IACpB,CAAC;IAED,OAAO,UAAU,CAAC,eAAe,CAAC,QAAQ,EAAE,KAAK,EAAE;QACjD,KAAK,EAAE,cAAc;KACtB,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { SpotifyApi } from '@spotify/web-api-ts-sdk';
|
|
2
|
+
export type DeviceType = 'Computer' | 'Smartphone' | 'Speaker' | 'TV' | 'AVR' | 'STB' | 'AudioDongle' | 'GameConsole' | 'CastVideo' | 'CastAudio' | 'Automobile' | 'Unknown';
|
|
3
|
+
export type SpotifyDevice = {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
type: DeviceType;
|
|
7
|
+
isActive: boolean;
|
|
8
|
+
isRestricted: boolean;
|
|
9
|
+
volumePercent: number | null;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Narrows the SDK's `string` device type to the `DeviceType` union,
|
|
13
|
+
* falling back to `'Unknown'` for any value Spotify adds later that we do
|
|
14
|
+
* not recognise yet.
|
|
15
|
+
*/
|
|
16
|
+
export declare function normalizeDeviceType(value: string | null | undefined): DeviceType;
|
|
17
|
+
/**
|
|
18
|
+
* Lists every Spotify Connect device visible to the current user. Ghost
|
|
19
|
+
* entries with `id === null` (briefly returned during state transitions)
|
|
20
|
+
* are filtered out so callers never have to handle them.
|
|
21
|
+
* @param client - Authenticated Spotify API client
|
|
22
|
+
* @returns Array of devices ready to display and target
|
|
23
|
+
*/
|
|
24
|
+
export declare function fetchDevices(client: SpotifyApi): Promise<SpotifyDevice[]>;
|
|
25
|
+
/**
|
|
26
|
+
* Transfers playback to a target device via the Spotify Connect API.
|
|
27
|
+
* The SDK's deserializer calls JSON.parse on any non-204 response body, and
|
|
28
|
+
* this endpoint sometimes returns 200 with a non-JSON body — the transfer
|
|
29
|
+
* still succeeds server-side, so the resulting SyntaxError is swallowed
|
|
30
|
+
* (same behavior as `playback.ts#ignoreSdkParseError`).
|
|
31
|
+
* @param client - Authenticated Spotify API client
|
|
32
|
+
* @param deviceId - Target device ID
|
|
33
|
+
* @param resume - When true, playback resumes on the new device; when false,
|
|
34
|
+
* the session is transferred but the playing state is preserved
|
|
35
|
+
*/
|
|
36
|
+
export declare function transferPlayback(client: SpotifyApi, deviceId: string, resume: boolean): Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devices.d.ts","sourceRoot":"","sources":["../../src/spotify/devices.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE1D,MAAM,MAAM,UAAU,GAClB,UAAU,GACV,YAAY,GACZ,SAAS,GACT,IAAI,GACJ,KAAK,GACL,KAAK,GACL,aAAa,GACb,aAAa,GACb,WAAW,GACX,WAAW,GACX,YAAY,GACZ,SAAS,CAAC;AAEd,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,CAAC;AAiBF;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,UAAU,CAKhF;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAe/E;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAY3G"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const KNOWN_DEVICE_TYPES = new Set([
|
|
2
|
+
'Computer',
|
|
3
|
+
'Smartphone',
|
|
4
|
+
'Speaker',
|
|
5
|
+
'TV',
|
|
6
|
+
'AVR',
|
|
7
|
+
'STB',
|
|
8
|
+
'AudioDongle',
|
|
9
|
+
'GameConsole',
|
|
10
|
+
'CastVideo',
|
|
11
|
+
'CastAudio',
|
|
12
|
+
'Automobile',
|
|
13
|
+
'Unknown',
|
|
14
|
+
]);
|
|
15
|
+
/**
|
|
16
|
+
* Narrows the SDK's `string` device type to the `DeviceType` union,
|
|
17
|
+
* falling back to `'Unknown'` for any value Spotify adds later that we do
|
|
18
|
+
* not recognise yet.
|
|
19
|
+
*/
|
|
20
|
+
export function normalizeDeviceType(value) {
|
|
21
|
+
if (value && KNOWN_DEVICE_TYPES.has(value)) {
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
return 'Unknown';
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Lists every Spotify Connect device visible to the current user. Ghost
|
|
28
|
+
* entries with `id === null` (briefly returned during state transitions)
|
|
29
|
+
* are filtered out so callers never have to handle them.
|
|
30
|
+
* @param client - Authenticated Spotify API client
|
|
31
|
+
* @returns Array of devices ready to display and target
|
|
32
|
+
*/
|
|
33
|
+
export async function fetchDevices(client) {
|
|
34
|
+
const response = await client.player.getAvailableDevices();
|
|
35
|
+
const devices = [];
|
|
36
|
+
for (const raw of response.devices) {
|
|
37
|
+
if (!raw.id)
|
|
38
|
+
continue;
|
|
39
|
+
devices.push({
|
|
40
|
+
id: raw.id,
|
|
41
|
+
name: raw.name,
|
|
42
|
+
type: normalizeDeviceType(raw.type),
|
|
43
|
+
isActive: raw.is_active,
|
|
44
|
+
isRestricted: raw.is_restricted,
|
|
45
|
+
volumePercent: raw.volume_percent,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return devices;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Transfers playback to a target device via the Spotify Connect API.
|
|
52
|
+
* The SDK's deserializer calls JSON.parse on any non-204 response body, and
|
|
53
|
+
* this endpoint sometimes returns 200 with a non-JSON body — the transfer
|
|
54
|
+
* still succeeds server-side, so the resulting SyntaxError is swallowed
|
|
55
|
+
* (same behavior as `playback.ts#ignoreSdkParseError`).
|
|
56
|
+
* @param client - Authenticated Spotify API client
|
|
57
|
+
* @param deviceId - Target device ID
|
|
58
|
+
* @param resume - When true, playback resumes on the new device; when false,
|
|
59
|
+
* the session is transferred but the playing state is preserved
|
|
60
|
+
*/
|
|
61
|
+
export async function transferPlayback(client, deviceId, resume) {
|
|
62
|
+
try {
|
|
63
|
+
await client.player.transferPlayback([
|
|
64
|
+
deviceId,
|
|
65
|
+
], resume);
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
if (err instanceof SyntaxError)
|
|
69
|
+
return;
|
|
70
|
+
throw err;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devices.js","sourceRoot":"","sources":["../../src/spotify/devices.ts"],"names":[],"mappings":"AAyBA,MAAM,kBAAkB,GAA4B,IAAI,GAAG,CAAa;IACtE,UAAU;IACV,YAAY;IACZ,SAAS;IACT,IAAI;IACJ,KAAK;IACL,KAAK;IACL,aAAa;IACb,aAAa;IACb,WAAW;IACX,WAAW;IACX,YAAY;IACZ,SAAS;CACV,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAgC;IAClE,IAAI,KAAK,IAAK,kBAA0C,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACpE,OAAO,KAAmB,CAAC;IAC7B,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAkB;IACnD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,mBAAmB,EAAE,CAAC;IAC3D,MAAM,OAAO,GAAoB,EAAE,CAAC;IACpC,KAAK,MAAM,GAAG,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;QACnC,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,SAAS;QACtB,OAAO,CAAC,IAAI,CAAC;YACX,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,IAAI,EAAE,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC;YACnC,QAAQ,EAAE,GAAG,CAAC,SAAS;YACvB,YAAY,EAAE,GAAG,CAAC,aAAa;YAC/B,aAAa,EAAE,GAAG,CAAC,cAAc;SAClC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,MAAkB,EAAE,QAAgB,EAAE,MAAe;IAC1F,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAClC;YACE,QAAQ;SACT,EACD,MAAM,CACP,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,WAAW;YAAE,OAAO;QACvC,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-with-retry.d.ts","sourceRoot":"","sources":["../../src/spotify/fetch-with-retry.ts"],"names":[],"mappings":"AA8BA;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAwB9F"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { USER_AGENT } from '../config.js';
|
|
2
|
+
import { logger } from '../logger.js';
|
|
2
3
|
const MAX_RETRIES = 3;
|
|
3
4
|
const DEFAULT_RETRY_AFTER_S = 1;
|
|
4
5
|
const MAX_RETRY_AFTER_S = 5;
|
|
@@ -30,11 +31,11 @@ function withUserAgent(init) {
|
|
|
30
31
|
export function fetchWithRetry(input, init) {
|
|
31
32
|
const initWithUa = withUserAgent(init);
|
|
32
33
|
const key = getCacheKey(input, initWithUa);
|
|
33
|
-
|
|
34
|
+
logger.error(`fetchWithRetry: ${key}`);
|
|
34
35
|
// Only deduplicate GET requests - mutations must always go through
|
|
35
36
|
const method = initWithUa.method ?? 'GET';
|
|
36
37
|
if (method !== 'GET') {
|
|
37
|
-
|
|
38
|
+
logger.error(`fetchWithRetry: Body`, initWithUa.body);
|
|
38
39
|
return attempt(input, initWithUa, MAX_RETRIES);
|
|
39
40
|
}
|
|
40
41
|
const pending = inflight.get(key);
|
|
@@ -55,7 +56,7 @@ async function attempt(input, init, retryCount) {
|
|
|
55
56
|
// Calling `.text()` on the original response would leave the body unusable
|
|
56
57
|
// and trigger "Body is unusable: Body has already been read" downstream.
|
|
57
58
|
const bodyText = await response.clone().text();
|
|
58
|
-
|
|
59
|
+
logger.error(`Request failed with status ${response.status} for ${input.toString()}`, bodyText);
|
|
59
60
|
}
|
|
60
61
|
if (response.status !== 429 || retryCount >= MAX_RETRIES) {
|
|
61
62
|
return response;
|
|
@@ -64,7 +65,7 @@ async function attempt(input, init, retryCount) {
|
|
|
64
65
|
const retryAfterS = retryAfterHeader ? Number.parseInt(retryAfterHeader, 10) : Number.NaN;
|
|
65
66
|
const clampedS = Number.isNaN(retryAfterS) ? DEFAULT_RETRY_AFTER_S : Math.min(retryAfterS, MAX_RETRY_AFTER_S);
|
|
66
67
|
const delayMs = clampedS * 1000;
|
|
67
|
-
|
|
68
|
+
logger.error(`Received 429 for ${input.toString()}. Retrying after ${clampedS} seconds (retry ${retryCount + 1}/${MAX_RETRIES})`);
|
|
68
69
|
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
69
70
|
return attempt(input, init, retryCount + 1);
|
|
70
71
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-with-retry.js","sourceRoot":"","sources":["../../src/spotify/fetch-with-retry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,MAAM,WAAW,GAAG,CAAC,CAAC;AACtB,MAAM,qBAAqB,GAAG,CAAC,CAAC;AAChC,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAE5B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA6B,CAAC;AAEtD,SAAS,WAAW,CAAC,KAAwB,EAAE,IAAkB;IAC/D,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;IAC7B,MAAM,MAAM,GAAG,IAAI,EAAE,MAAM,IAAI,KAAK,CAAC;IACrC,OAAO,GAAG,MAAM,IAAI,GAAG,EAAE,CAAC;AAC5B,CAAC;AAED;;;GAGG;AACH,SAAS,aAAa,CAAC,IAAkB;IACvC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC3C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IACxC,CAAC;IACD,OAAO;QACL,GAAG,IAAI;QACP,OAAO;KACR,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,KAAwB,EAAE,IAAkB;IACzE,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,GAAG,GAAG,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IAE3C,MAAM,CAAC,KAAK,CAAC,mBAAmB,GAAG,EAAE,CAAC,CAAC;IAEvC,mEAAmE;IACnE,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,IAAI,KAAK,CAAC;IAC1C,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;QACrB,MAAM,CAAC,KAAK,CAAC,sBAAsB,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC;QACtD,OAAO,OAAO,CAAC,KAAK,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;IACjD,CAAC;IAED,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE;QACzD,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC3B,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,KAAK,UAAU,OAAO,CAAC,KAAwB,EAAE,IAA6B,EAAE,UAAkB;IAChG,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAE1C,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,yEAAyE;QACzE,8EAA8E;QAC9E,2EAA2E;QAC3E,yEAAyE;QACzE,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC;QAC/C,MAAM,CAAC,KAAK,CAAC,8BAA8B,QAAQ,CAAC,MAAM,QAAQ,KAAK,CAAC,QAAQ,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC;IAClG,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,UAAU,IAAI,WAAW,EAAE,CAAC;QACzD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,gBAAgB,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAC7D,MAAM,WAAW,GAAG,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;IAC1F,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC;IAC9G,MAAM,OAAO,GAAG,QAAQ,GAAG,IAAI,CAAC;IAEhC,MAAM,CAAC,KAAK,CACV,oBAAoB,KAAK,CAAC,QAAQ,EAAE,oBAAoB,QAAQ,mBAAmB,UAAU,GAAG,CAAC,IAAI,WAAW,GAAG,CACpH,CAAC;IAEF,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7D,OAAO,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,UAAU,GAAG,CAAC,CAAC,CAAC;AAC9C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lyrics.d.ts","sourceRoot":"","sources":["../../src/spotify/lyrics.ts"],"names":[],"mappings":"AAgCA,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IACnB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,CAAC;AA8CF;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAc7G;AAiCD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,SAAI,GAAG,MAAM,CAUhG"}
|
package/dist/spotify/lyrics.js
CHANGED
|
@@ -2,6 +2,7 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { CONFIG_DIR } from '../config.js';
|
|
5
|
+
import { logger } from '../logger.js';
|
|
5
6
|
import { fetchWithRetry } from './fetch-with-retry.js';
|
|
6
7
|
const LYRICS_CACHE_DIR = join(CONFIG_DIR, 'lyrics-cache');
|
|
7
8
|
function getCachePath(trackName, artistName) {
|
|
@@ -15,7 +16,8 @@ function readDiskCache(trackName, artistName) {
|
|
|
15
16
|
try {
|
|
16
17
|
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
17
18
|
}
|
|
18
|
-
catch {
|
|
19
|
+
catch (err) {
|
|
20
|
+
logger.error(`Failed to read lyrics cache at ${path}:`, err);
|
|
19
21
|
return null;
|
|
20
22
|
}
|
|
21
23
|
}
|
|
@@ -106,7 +108,8 @@ async function doFetch(trackName, artistName, durationMs) {
|
|
|
106
108
|
writeDiskCache(trackName, artistName, lyrics);
|
|
107
109
|
return lyrics;
|
|
108
110
|
}
|
|
109
|
-
catch {
|
|
111
|
+
catch (err) {
|
|
112
|
+
logger.error(`Failed to fetch lyrics for "${trackName}" by "${artistName}":`, err);
|
|
110
113
|
return null;
|
|
111
114
|
}
|
|
112
115
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lyrics.js","sourceRoot":"","sources":["../../src/spotify/lyrics.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PlaybackState, SpotifyApi } from '@spotify/web-api-ts-sdk';
|
|
2
|
+
import type { DeviceType } from './devices.js';
|
|
2
3
|
export type RepeatMode = 'off' | 'context' | 'track';
|
|
3
4
|
export type PlaybackContext = {
|
|
4
5
|
type: string;
|
|
@@ -12,12 +13,20 @@ export type TrackInfo = {
|
|
|
12
13
|
progressMs: number;
|
|
13
14
|
durationMs: number;
|
|
14
15
|
deviceId: string | null;
|
|
16
|
+
deviceName: string | null;
|
|
17
|
+
deviceType: DeviceType | null;
|
|
15
18
|
volume: number | null;
|
|
16
19
|
shuffle: boolean;
|
|
17
20
|
repeat: RepeatMode;
|
|
18
21
|
albumImageUrl: string | null;
|
|
19
22
|
context: PlaybackContext | null;
|
|
20
23
|
uri: string;
|
|
24
|
+
releaseYear: number | null;
|
|
25
|
+
popularity: number | null;
|
|
26
|
+
explicit: boolean;
|
|
27
|
+
artistCount: number;
|
|
28
|
+
albumTotalTracks: number | null;
|
|
29
|
+
isrcCountry: string | null;
|
|
21
30
|
};
|
|
22
31
|
/**
|
|
23
32
|
* Fetches the current playback state from Spotify.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playback.d.ts","sourceRoot":"","sources":["../../src/spotify/playback.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAEzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/C,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,SAAS,GAAG,OAAO,CAAC;AAErD,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,EAAE,eAAe,GAAG,IAAI,CAAC;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAaxF;AAED;;;;GAIG;AACH,wBAAsB,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE9E;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAMf;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEzG;AAED;;;;;GAKG;AACH,wBAAsB,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM9F;AAED;;;;GAIG;AACH,wBAAsB,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/E;AAED;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAElF;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtF;AAED;;;;;GAKG;AACH,wBAAsB,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEnG;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3G;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAErG;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvG;AAyBD;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAkBzE;AA0CD;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAqCvF"}
|
package/dist/spotify/playback.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { logger } from '../logger.js';
|
|
2
|
+
import { normalizeDeviceType } from './devices.js';
|
|
1
3
|
/**
|
|
2
4
|
* Fetches the current playback state from Spotify.
|
|
3
5
|
* @param client - Authenticated Spotify API client
|
|
@@ -135,10 +137,10 @@ async function ignoreSdkParseError(fn) {
|
|
|
135
137
|
}
|
|
136
138
|
// Translate the SDK's misleading 403 message for player restriction errors
|
|
137
139
|
if (err instanceof Error && err.message.includes('Restriction violated')) {
|
|
138
|
-
|
|
140
|
+
logger.error('Action failed due to Spotify playback restrictions:', err);
|
|
139
141
|
throw new Error('Action not available for the current playback context');
|
|
140
142
|
}
|
|
141
|
-
|
|
143
|
+
logger.error('Unexpected error during playback control:', err);
|
|
142
144
|
throw err;
|
|
143
145
|
}
|
|
144
146
|
}
|
|
@@ -164,6 +166,30 @@ export async function togglePlayback(client) {
|
|
|
164
166
|
await play(client, deviceId);
|
|
165
167
|
return true;
|
|
166
168
|
}
|
|
169
|
+
function extractTrackMetadata(item) {
|
|
170
|
+
const artist = 'artists' in item ? item.artists.map((a) => a.name).join(', ') : 'Unknown';
|
|
171
|
+
const album = 'album' in item ? item.album.name : '';
|
|
172
|
+
const albumImageUrl = 'album' in item && item.album.images.length > 0 ? item.album.images[item.album.images.length - 1].url : null;
|
|
173
|
+
const parsedYear = 'album' in item ? Number.parseInt(item.album.release_date.slice(0, 4), 10) : Number.NaN;
|
|
174
|
+
const releaseYear = Number.isFinite(parsedYear) ? parsedYear : null;
|
|
175
|
+
const popularity = 'popularity' in item ? item.popularity : null;
|
|
176
|
+
const explicit = 'explicit' in item ? item.explicit : false;
|
|
177
|
+
const artistCount = 'artists' in item ? item.artists.length : 0;
|
|
178
|
+
const albumTotalTracks = 'album' in item ? item.album.total_tracks : null;
|
|
179
|
+
const isrcRaw = 'external_ids' in item ? item.external_ids?.isrc : undefined;
|
|
180
|
+
const isrcCountry = isrcRaw && isrcRaw.length >= 2 ? isrcRaw.slice(0, 2).toUpperCase() : null;
|
|
181
|
+
return {
|
|
182
|
+
artist,
|
|
183
|
+
album,
|
|
184
|
+
albumImageUrl,
|
|
185
|
+
releaseYear,
|
|
186
|
+
popularity,
|
|
187
|
+
explicit,
|
|
188
|
+
artistCount,
|
|
189
|
+
albumTotalTracks,
|
|
190
|
+
isrcCountry,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
167
193
|
/**
|
|
168
194
|
* Fetches simplified info about the currently playing track.
|
|
169
195
|
* @param client - Authenticated Spotify API client
|
|
@@ -175,21 +201,21 @@ export async function getCurrentTrackInfo(client) {
|
|
|
175
201
|
return null;
|
|
176
202
|
}
|
|
177
203
|
const { item } = state;
|
|
178
|
-
const
|
|
179
|
-
const album = 'album' in item ? item.album.name : '';
|
|
180
|
-
const albumImageUrl = 'album' in item && item.album.images.length > 0 ? item.album.images[item.album.images.length - 1].url : null;
|
|
204
|
+
const meta = extractTrackMetadata(item);
|
|
181
205
|
return {
|
|
182
206
|
name: item.name,
|
|
183
|
-
artist,
|
|
184
|
-
album,
|
|
207
|
+
artist: meta.artist,
|
|
208
|
+
album: meta.album,
|
|
185
209
|
isPlaying: state.is_playing,
|
|
186
210
|
progressMs: state.progress_ms,
|
|
187
211
|
durationMs: item.duration_ms,
|
|
188
212
|
deviceId: state.device.id,
|
|
213
|
+
deviceName: state.device.name ?? null,
|
|
214
|
+
deviceType: state.device.type ? normalizeDeviceType(state.device.type) : null,
|
|
189
215
|
volume: state.device.volume_percent,
|
|
190
216
|
shuffle: state.shuffle_state,
|
|
191
217
|
repeat: state.repeat_state,
|
|
192
|
-
albumImageUrl,
|
|
218
|
+
albumImageUrl: meta.albumImageUrl,
|
|
193
219
|
context: state.context
|
|
194
220
|
? {
|
|
195
221
|
type: state.context.type,
|
|
@@ -197,5 +223,11 @@ export async function getCurrentTrackInfo(client) {
|
|
|
197
223
|
}
|
|
198
224
|
: null,
|
|
199
225
|
uri: item.uri,
|
|
226
|
+
releaseYear: meta.releaseYear,
|
|
227
|
+
popularity: meta.popularity,
|
|
228
|
+
explicit: meta.explicit,
|
|
229
|
+
artistCount: meta.artistCount,
|
|
230
|
+
albumTotalTracks: meta.albumTotalTracks,
|
|
231
|
+
isrcCountry: meta.isrcCountry,
|
|
200
232
|
};
|
|
201
233
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playback.js","sourceRoot":"","sources":["../../src/spotify/playback.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/spotify/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAG1D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,SAAS,EAAE,oBAAoB,EAAE,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB,CAAC;AAaF,wBAAsB,aAAa,CACjC,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,MAA6B,GACnC,OAAO,CAAC,aAAa,CAAC,CA0ExB;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAchG;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAmBzG;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,UAAU,EAClB,KAAK,GAAE,MAA6B,EACpC,MAAM,GAAE,MAAU,GACjB,OAAO,CAAC;IACT,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CAiBD"}
|
package/dist/spotify/search.js
CHANGED
|
@@ -6,11 +6,11 @@ import { SEARCH_RESULTS_LIMIT, USER_PLAYLISTS_LIMIT } from '../config.js';
|
|
|
6
6
|
* @param limit - Max results per category (default: SEARCH_RESULTS_LIMIT)
|
|
7
7
|
* @returns Categorized search results
|
|
8
8
|
*/
|
|
9
|
-
//
|
|
10
|
-
// even though
|
|
11
|
-
const
|
|
9
|
+
// Spotify's live API rejects values above 10 with `400 Invalid limit`,
|
|
10
|
+
// even though older docs claim `max=50`.
|
|
11
|
+
const SEARCH_MAX_LIMIT = 10;
|
|
12
12
|
export async function searchSpotify(client, query, limit = SEARCH_RESULTS_LIMIT) {
|
|
13
|
-
const safeLimit = Math.max(1, Math.min(limit,
|
|
13
|
+
const safeLimit = Math.max(1, Math.min(limit, SEARCH_MAX_LIMIT));
|
|
14
14
|
const data = await client.search(query, [
|
|
15
15
|
'track',
|
|
16
16
|
'album',
|