@discord-player/plugin-host 8.0.0-dev.1 → 8.0.0-dev.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -28
- package/dist/compat/base-extractor.d.ts +6 -0
- package/dist/compat/base-extractor.js.map +1 -1
- package/dist/compat/extractor-execution-context.js.map +1 -1
- package/dist/compat/extractor-plugin.js +107 -75
- package/dist/compat/extractor-plugin.js.map +1 -1
- package/dist/compat/utils.js.map +1 -1
- package/dist/errors.d.ts +0 -2
- package/dist/errors.js +0 -2
- package/dist/errors.js.map +1 -1
- package/dist/host.js +81 -20
- package/dist/host.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/legacy-player.d.ts +1 -1
- package/dist/lyrics.d.ts +56 -0
- package/dist/lyrics.js +110 -0
- package/dist/lyrics.js.map +1 -0
- package/dist/media.js.map +1 -1
- package/dist/plugins/execution-options.d.ts +2 -0
- package/dist/plugins/operation-dispatch.js +10 -2
- package/dist/plugins/operation-dispatch.js.map +1 -1
- package/dist/plugins/plugin-error.js.map +1 -1
- package/dist/plugins/registry-internals.d.ts +2 -2
- package/dist/plugins/registry.d.ts +45 -5
- package/dist/plugins/registry.js +149 -15
- package/dist/plugins/registry.js.map +1 -1
- package/dist/plugins/stream-resolver.js +5 -2
- package/dist/plugins/stream-resolver.js.map +1 -1
- package/dist/plugins/types.d.ts +26 -23
- package/dist/plugins/types.js.map +1 -1
- package/dist/protocol.d.ts +35 -0
- package/dist/protocol.js.map +1 -1
- package/dist/providers/lrclib.d.ts +120 -0
- package/dist/providers/lrclib.js +183 -0
- package/dist/providers/lrclib.js.map +1 -0
- package/dist/remote.d.ts +29 -0
- package/dist/remote.js +142 -0
- package/dist/remote.js.map +1 -0
- package/dist/runtime.d.ts +0 -8
- package/dist/runtime.js +41 -43
- package/dist/runtime.js.map +1 -1
- package/dist/structures/search-result.js.map +1 -1
- package/dist/structures/track-types.d.ts +7 -3
- package/dist/structures/track.d.ts +4 -11
- package/dist/structures/track.js +8 -10
- package/dist/structures/track.js.map +1 -1
- package/dist/types.d.ts +2 -4
- package/dist/utils/legacy-serialization-schemas.d.ts +10 -0
- package/dist/utils/serialization-schemas.d.ts +15 -0
- package/dist/utils/serialization-schemas.js +1 -0
- package/dist/utils/serialization-schemas.js.map +1 -1
- package/dist/utils/serialization.js.map +1 -1
- package/docs/remedia-contract.md +9 -8
- package/package.json +3 -3
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/** Search parameters supported by the LRCLIB API. */
|
|
2
|
+
export interface LrcSearchParams {
|
|
3
|
+
/** Free-form query. Either `q` or `trackName` is required. */
|
|
4
|
+
q?: string;
|
|
5
|
+
/** Track title. Either `q` or `trackName` is required. */
|
|
6
|
+
trackName?: string;
|
|
7
|
+
/** Artist name filter. */
|
|
8
|
+
artistName?: string;
|
|
9
|
+
/** Album name filter. */
|
|
10
|
+
albumName?: string;
|
|
11
|
+
}
|
|
12
|
+
/** Parameters accepted by LRCLIB's exact lookup endpoints. */
|
|
13
|
+
export interface LrcGetParams extends LrcSearchParams {
|
|
14
|
+
/** Track duration in seconds. */
|
|
15
|
+
duration?: number;
|
|
16
|
+
}
|
|
17
|
+
/** Lyrics record returned by LRCLIB search. */
|
|
18
|
+
export interface LrcSearchResult {
|
|
19
|
+
/** LRCLIB record ID. */
|
|
20
|
+
id: number;
|
|
21
|
+
/** Display name supplied by LRCLIB. */
|
|
22
|
+
name: string;
|
|
23
|
+
/** Track title. */
|
|
24
|
+
trackName: string;
|
|
25
|
+
/** Artist name. */
|
|
26
|
+
artistName: string;
|
|
27
|
+
/** Album name. */
|
|
28
|
+
albumName: string;
|
|
29
|
+
/** Track duration in seconds. */
|
|
30
|
+
duration: number;
|
|
31
|
+
/** Whether the recording is instrumental. */
|
|
32
|
+
instrumental: boolean;
|
|
33
|
+
/** Plain lyric text, when available. */
|
|
34
|
+
plainLyrics: string;
|
|
35
|
+
/** Synchronized LRC text, when supplied by the service. */
|
|
36
|
+
syncedLyrics?: string;
|
|
37
|
+
}
|
|
38
|
+
/** Lyrics record returned by exact LRCLIB lookups. */
|
|
39
|
+
/** Lyrics record returned by the LRCLIB exact lookup endpoints. */
|
|
40
|
+
export type LrcGetResult = Omit<LrcSearchResult, 'name'>;
|
|
41
|
+
/** LRCLIB API client with serialized requests and bounded retries. */
|
|
42
|
+
export declare class LrcLib {
|
|
43
|
+
#private;
|
|
44
|
+
/** Player used to identify requests and publish debug messages. */
|
|
45
|
+
readonly player: {
|
|
46
|
+
version: string;
|
|
47
|
+
debug(message: string): void;
|
|
48
|
+
};
|
|
49
|
+
/** Base API URL. */
|
|
50
|
+
api: string;
|
|
51
|
+
/** Request timeout in milliseconds. */
|
|
52
|
+
timeout: number;
|
|
53
|
+
/** Maximum retry count for rate limits and server errors. */
|
|
54
|
+
retryLimit: number;
|
|
55
|
+
/**
|
|
56
|
+
* Create an LRCLIB client owned by one Player.
|
|
57
|
+
* @param player Player used to identify requests and publish debug messages.
|
|
58
|
+
*/
|
|
59
|
+
constructor(
|
|
60
|
+
/** Player used to identify requests and publish debug messages. */
|
|
61
|
+
player: {
|
|
62
|
+
version: string;
|
|
63
|
+
debug(message: string): void;
|
|
64
|
+
});
|
|
65
|
+
/**
|
|
66
|
+
* Set the request timeout.
|
|
67
|
+
* @param timeout Positive request timeout in milliseconds.
|
|
68
|
+
*/
|
|
69
|
+
setRequestTimeout(
|
|
70
|
+
/** Timeout in milliseconds. */
|
|
71
|
+
timeout: number): void;
|
|
72
|
+
/**
|
|
73
|
+
* Set the maximum retry count.
|
|
74
|
+
* @param limit Non-negative retry count for rate limits and server errors.
|
|
75
|
+
*/
|
|
76
|
+
setRetryLimit(
|
|
77
|
+
/** Non-negative retry count. */
|
|
78
|
+
limit: number): void;
|
|
79
|
+
/**
|
|
80
|
+
* Get lyrics by exact track metadata.
|
|
81
|
+
* @param params Track title, artist, album, and optional duration.
|
|
82
|
+
*/
|
|
83
|
+
get(
|
|
84
|
+
/** Exact lookup metadata. */
|
|
85
|
+
params: LrcGetParams): Promise<LrcGetResult>;
|
|
86
|
+
/**
|
|
87
|
+
* Get lyrics by LRCLIB record ID.
|
|
88
|
+
* @param id Numeric LRCLIB record ID.
|
|
89
|
+
*/
|
|
90
|
+
getById(
|
|
91
|
+
/** Numeric LRCLIB record ID. */
|
|
92
|
+
id: `${number}` | number): Promise<LrcGetResult>;
|
|
93
|
+
/**
|
|
94
|
+
* Get cached lyrics by exact track metadata.
|
|
95
|
+
* @param params Track title, artist, album, and optional duration.
|
|
96
|
+
*/
|
|
97
|
+
getCached(
|
|
98
|
+
/** Exact lookup metadata. */
|
|
99
|
+
params: LrcGetParams): Promise<LrcGetResult>;
|
|
100
|
+
/**
|
|
101
|
+
* Search LRCLIB for matching lyric records.
|
|
102
|
+
* @param params Free-form query or track and artist metadata.
|
|
103
|
+
*/
|
|
104
|
+
search(
|
|
105
|
+
/** Search query or track and artist metadata. */
|
|
106
|
+
params: LrcSearchParams): Promise<LrcSearchResult[]>;
|
|
107
|
+
/**
|
|
108
|
+
* Send one serialized request to LRCLIB.
|
|
109
|
+
* @param path API path, with or without a leading slash.
|
|
110
|
+
* @param options Additional fetch options merged with the default GET request.
|
|
111
|
+
* @returns Parsed JSON response.
|
|
112
|
+
*/
|
|
113
|
+
request<Result>(
|
|
114
|
+
/** API path, with or without a leading slash. */
|
|
115
|
+
path: string,
|
|
116
|
+
/** Additional fetch options merged with the default GET request. */
|
|
117
|
+
options?: RequestInit): Promise<Result>;
|
|
118
|
+
}
|
|
119
|
+
/** Optional LRCLIB provider. No provider is installed by constructing a Player. */
|
|
120
|
+
export declare const lrclibPlugin: (options?: Record<string, never> | undefined) => import("../plugins/types.js").PluginRegistration<Record<string, never>>;
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { definePlugin } from '../plugins/types.js';
|
|
2
|
+
import { parseLyrics } from '../lyrics.js';
|
|
3
|
+
function toSnakeCase(params) {
|
|
4
|
+
const result = new URLSearchParams();
|
|
5
|
+
for (const [key, value] of Object.entries(params)) {
|
|
6
|
+
if (value === undefined || value === null || value === '')
|
|
7
|
+
continue;
|
|
8
|
+
const snake = key.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
9
|
+
result.set(snake, String(value));
|
|
10
|
+
}
|
|
11
|
+
return result;
|
|
12
|
+
}
|
|
13
|
+
function delayWait(ms, signal) {
|
|
14
|
+
signal?.throwIfAborted();
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
const abort = () => {
|
|
17
|
+
clearTimeout(timer);
|
|
18
|
+
reject(signal?.reason);
|
|
19
|
+
};
|
|
20
|
+
const timer = setTimeout(() => {
|
|
21
|
+
signal?.removeEventListener('abort', abort);
|
|
22
|
+
resolve();
|
|
23
|
+
}, ms);
|
|
24
|
+
signal?.addEventListener('abort', abort, { once: true });
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/** LRCLIB API client with serialized requests and bounded retries. */
|
|
28
|
+
export class LrcLib {
|
|
29
|
+
player;
|
|
30
|
+
/** Base API URL. */
|
|
31
|
+
api = 'https://lrclib.net/api';
|
|
32
|
+
/** Request timeout in milliseconds. */
|
|
33
|
+
timeout = 15_000;
|
|
34
|
+
/** Maximum retry count for rate limits and server errors. */
|
|
35
|
+
retryLimit = 5;
|
|
36
|
+
#tail = Promise.resolve();
|
|
37
|
+
/**
|
|
38
|
+
* Create an LRCLIB client owned by one Player.
|
|
39
|
+
* @param player Player used to identify requests and publish debug messages.
|
|
40
|
+
*/
|
|
41
|
+
constructor(
|
|
42
|
+
/** Player used to identify requests and publish debug messages. */
|
|
43
|
+
player) {
|
|
44
|
+
this.player = player;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Set the request timeout.
|
|
48
|
+
* @param timeout Positive request timeout in milliseconds.
|
|
49
|
+
*/
|
|
50
|
+
setRequestTimeout(
|
|
51
|
+
/** Timeout in milliseconds. */
|
|
52
|
+
timeout) {
|
|
53
|
+
if (!Number.isFinite(timeout) || timeout <= 0)
|
|
54
|
+
throw new RangeError('Request timeout must be a positive finite number.');
|
|
55
|
+
this.timeout = timeout;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Set the maximum retry count.
|
|
59
|
+
* @param limit Non-negative retry count for rate limits and server errors.
|
|
60
|
+
*/
|
|
61
|
+
setRetryLimit(
|
|
62
|
+
/** Non-negative retry count. */
|
|
63
|
+
limit) {
|
|
64
|
+
if (!Number.isInteger(limit) || limit < 0)
|
|
65
|
+
throw new RangeError('Retry limit must be a non-negative integer.');
|
|
66
|
+
this.retryLimit = limit;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get lyrics by exact track metadata.
|
|
70
|
+
* @param params Track title, artist, album, and optional duration.
|
|
71
|
+
*/
|
|
72
|
+
get(
|
|
73
|
+
/** Exact lookup metadata. */
|
|
74
|
+
params) {
|
|
75
|
+
return this.request(`get?${toSnakeCase(params)}`);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Get lyrics by LRCLIB record ID.
|
|
79
|
+
* @param id Numeric LRCLIB record ID.
|
|
80
|
+
*/
|
|
81
|
+
getById(
|
|
82
|
+
/** Numeric LRCLIB record ID. */
|
|
83
|
+
id) {
|
|
84
|
+
return this.request(`get/${id}`);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Get cached lyrics by exact track metadata.
|
|
88
|
+
* @param params Track title, artist, album, and optional duration.
|
|
89
|
+
*/
|
|
90
|
+
getCached(
|
|
91
|
+
/** Exact lookup metadata. */
|
|
92
|
+
params) {
|
|
93
|
+
return this.request(`get-cached?${toSnakeCase(params)}`);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Search LRCLIB for matching lyric records.
|
|
97
|
+
* @param params Free-form query or track and artist metadata.
|
|
98
|
+
*/
|
|
99
|
+
search(
|
|
100
|
+
/** Search query or track and artist metadata. */
|
|
101
|
+
params) {
|
|
102
|
+
if (!params.q && !params.trackName)
|
|
103
|
+
throw new TypeError('Provide either q or trackName when searching lyrics.');
|
|
104
|
+
return this.request(`search?${toSnakeCase(params)}`);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Send one serialized request to LRCLIB.
|
|
108
|
+
* @param path API path, with or without a leading slash.
|
|
109
|
+
* @param options Additional fetch options merged with the default GET request.
|
|
110
|
+
* @returns Parsed JSON response.
|
|
111
|
+
*/
|
|
112
|
+
async request(
|
|
113
|
+
/** API path, with or without a leading slash. */
|
|
114
|
+
path,
|
|
115
|
+
/** Additional fetch options merged with the default GET request. */
|
|
116
|
+
options = {}) {
|
|
117
|
+
let release;
|
|
118
|
+
const turn = new Promise((resolve) => {
|
|
119
|
+
release = resolve;
|
|
120
|
+
});
|
|
121
|
+
const previous = this.#tail;
|
|
122
|
+
this.#tail = previous.then(() => turn);
|
|
123
|
+
await previous;
|
|
124
|
+
try {
|
|
125
|
+
const url = `${this.api}${path.startsWith('/') ? path : `/${path}`}`;
|
|
126
|
+
this.player.debug(`[LrcLib] Requesting ${path}`);
|
|
127
|
+
for (let attempt = 0;; attempt++) {
|
|
128
|
+
const response = await fetch(url, {
|
|
129
|
+
method: 'GET',
|
|
130
|
+
redirect: 'follow',
|
|
131
|
+
...options,
|
|
132
|
+
signal: options.signal ?? AbortSignal.timeout(this.timeout),
|
|
133
|
+
headers: {
|
|
134
|
+
'User-Agent': `Discord-Player/${this.player.version}`,
|
|
135
|
+
'Content-Type': 'application/json',
|
|
136
|
+
...options.headers,
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
if (response.ok)
|
|
140
|
+
return (await response.json());
|
|
141
|
+
if ((response.status === 429 || response.status >= 500) && attempt < this.retryLimit) {
|
|
142
|
+
const retryAfter = Number(response.headers.get('retry-after'));
|
|
143
|
+
const delay = Number.isFinite(retryAfter) && retryAfter > 0 ? retryAfter * 1000 : Math.min(250 * 2 ** attempt, 4000);
|
|
144
|
+
await delayWait(delay, options.signal);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
throw new Error(`LRCLIB request failed with HTTP ${response.status} ${response.statusText}.`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
finally {
|
|
151
|
+
release();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/** Optional LRCLIB provider. No provider is installed by constructing a Player. */
|
|
156
|
+
export const lrclibPlugin = definePlugin({
|
|
157
|
+
id: 'org.lrclib',
|
|
158
|
+
setup(ctx) {
|
|
159
|
+
const client = new LrcLib({ version: 'plugin-host', debug: (message) => ctx.player.debug(message) });
|
|
160
|
+
ctx.on('lyrics.search', async ({ input }, { signal }) => {
|
|
161
|
+
const params = typeof input === 'string' ? { q: input } : { trackName: input.title, artistName: input.author };
|
|
162
|
+
const records = await client.request(`search?${toSnakeCase(params)}`, {
|
|
163
|
+
signal: AbortSignal.any([signal, AbortSignal.timeout(client.timeout)]),
|
|
164
|
+
});
|
|
165
|
+
return records.map((record) => ({
|
|
166
|
+
plain: record.plainLyrics
|
|
167
|
+
? { available: true, text: record.plainLyrics }
|
|
168
|
+
: { available: false },
|
|
169
|
+
synced: record.syncedLyrics
|
|
170
|
+
? { available: true, lines: parseLyrics(record.syncedLyrics) }
|
|
171
|
+
: { available: false },
|
|
172
|
+
metadata: {
|
|
173
|
+
id: record.id,
|
|
174
|
+
trackName: record.trackName,
|
|
175
|
+
artistName: record.artistName,
|
|
176
|
+
albumName: record.albumName,
|
|
177
|
+
instrumental: record.instrumental,
|
|
178
|
+
},
|
|
179
|
+
}));
|
|
180
|
+
});
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
//# sourceMappingURL=lrclib.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lrclib.js","sourceRoot":"","sources":["../../src/providers/lrclib.ts"],"names":[],"mappings":"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"}
|
package/dist/remote.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { OperationName } from './plugins/types.js';
|
|
2
|
+
/** Existing standalone HTTP deployment. Authentication stays outside Track metadata. */
|
|
3
|
+
export interface RemotePluginHostOptions {
|
|
4
|
+
/** Stable deployment identity matching the audio node's configured host. */
|
|
5
|
+
hostId: string;
|
|
6
|
+
/** HTTP(S) origin without embedded credentials. */
|
|
7
|
+
url: string;
|
|
8
|
+
/** Optional bearer credential. */
|
|
9
|
+
token?: string;
|
|
10
|
+
/** Bounded discovery and execution deadline, default 15000ms. */
|
|
11
|
+
timeoutMs?: number;
|
|
12
|
+
}
|
|
13
|
+
/** Advertised provider capabilities from a standalone host. */
|
|
14
|
+
export interface RemoteProvider {
|
|
15
|
+
/** Actual provider identity, separate from deployment identity. */
|
|
16
|
+
id: string;
|
|
17
|
+
/** Implemented operation names. */
|
|
18
|
+
operations: OperationName[];
|
|
19
|
+
/** Registered source protocols. */
|
|
20
|
+
protocols: string[];
|
|
21
|
+
/** Advertised operation priorities, applied unless the client descriptor overrides them. */
|
|
22
|
+
priorities?: Partial<Record<OperationName, number>>;
|
|
23
|
+
}
|
|
24
|
+
/** Validate live advertised capabilities; no plugin is installed on the remote server.
|
|
25
|
+
* @param host Existing standalone deployment.
|
|
26
|
+
* @param signal Registration lifetime.
|
|
27
|
+
* @returns Actual remote provider declarations.
|
|
28
|
+
*/
|
|
29
|
+
export declare function discoverRemote(host: RemotePluginHostOptions, signal: AbortSignal): Promise<RemoteProvider[]>;
|
package/dist/remote.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { lyricsResultSchema } from './lyrics.js';
|
|
3
|
+
import { deserializeSourceResult, serializeSourceTrack, } from './protocol.js';
|
|
4
|
+
import { Track } from './structures/track.js';
|
|
5
|
+
/** Fetch one authenticated metadata response through the current HTTP boundary.
|
|
6
|
+
* @internal
|
|
7
|
+
* @param host Existing deployment.
|
|
8
|
+
* @param path Discovery or execution path.
|
|
9
|
+
* @param signal Request cancellation.
|
|
10
|
+
* @param body Optional serialized execution request.
|
|
11
|
+
* @returns Parsed metadata; operational failures reject.
|
|
12
|
+
*/
|
|
13
|
+
export async function remoteMetadata(host, path, signal, body) {
|
|
14
|
+
const url = new URL(host.url);
|
|
15
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/.test(host.hostId) ||
|
|
16
|
+
!['http:', 'https:'].includes(url.protocol) ||
|
|
17
|
+
url.username ||
|
|
18
|
+
url.password ||
|
|
19
|
+
url.pathname !== '/' ||
|
|
20
|
+
url.search ||
|
|
21
|
+
url.hash)
|
|
22
|
+
throw new TypeError('Source host requires a stable ID and an HTTP(S) origin without credentials');
|
|
23
|
+
if (!Number.isSafeInteger(host.timeoutMs ?? 15000) || (host.timeoutMs ?? 15000) < 1)
|
|
24
|
+
throw new RangeError('Source host timeout must be a positive safe integer');
|
|
25
|
+
const response = await fetch(`${url.origin}${path}`, {
|
|
26
|
+
method: body === undefined ? 'GET' : 'POST',
|
|
27
|
+
redirect: 'error',
|
|
28
|
+
signal: AbortSignal.any([signal, AbortSignal.timeout(host.timeoutMs ?? 15000)]),
|
|
29
|
+
headers: { 'content-type': 'application/json', ...(host.token ? { authorization: `Bearer ${host.token}` } : {}) },
|
|
30
|
+
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
31
|
+
});
|
|
32
|
+
if (!response.ok)
|
|
33
|
+
throw new Error(`Source host ${host.hostId}: metadata request failed (HTTP ${response.status})`);
|
|
34
|
+
return response.json();
|
|
35
|
+
}
|
|
36
|
+
/** Validate live advertised capabilities; no plugin is installed on the remote server.
|
|
37
|
+
* @param host Existing standalone deployment.
|
|
38
|
+
* @param signal Registration lifetime.
|
|
39
|
+
* @returns Actual remote provider declarations.
|
|
40
|
+
*/
|
|
41
|
+
export async function discoverRemote(host, signal) {
|
|
42
|
+
const manifest = await remoteMetadata(host, '/.well-known/remedia', signal);
|
|
43
|
+
if (!manifest ||
|
|
44
|
+
typeof manifest !== 'object' ||
|
|
45
|
+
!('protocolVersion' in manifest) ||
|
|
46
|
+
manifest.protocolVersion !== 1 ||
|
|
47
|
+
!('providers' in manifest) ||
|
|
48
|
+
!Array.isArray(manifest.providers))
|
|
49
|
+
throw new Error(`Source host ${host.hostId}: incompatible metadata manifest`);
|
|
50
|
+
if (!('hostId' in manifest) || manifest.hostId !== host.hostId)
|
|
51
|
+
throw new Error(`Source host ${host.hostId}: deployment identity does not match advertised host`);
|
|
52
|
+
return manifest.providers.map((provider) => {
|
|
53
|
+
if (!provider ||
|
|
54
|
+
typeof provider.id !== 'string' ||
|
|
55
|
+
!Array.isArray(provider.operations) ||
|
|
56
|
+
!provider.operations.every((name) => typeof name === 'string') ||
|
|
57
|
+
!Array.isArray(provider.protocols) ||
|
|
58
|
+
!provider.protocols.every((name) => typeof name === 'string'))
|
|
59
|
+
throw new TypeError('Invalid remote provider manifest');
|
|
60
|
+
if (provider.priorities !== undefined)
|
|
61
|
+
z.record(z.string(), z.number()).parse(provider.priorities);
|
|
62
|
+
return provider;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/** Register remote metadata operations into the ordinary capability registry.
|
|
66
|
+
* @internal
|
|
67
|
+
* @param ctx Registration owner.
|
|
68
|
+
* @param providerId Actual remote provider ID.
|
|
69
|
+
* @param host Existing deployment configuration.
|
|
70
|
+
* @param advertised Already validated discovery for a bulk host registration.
|
|
71
|
+
*/
|
|
72
|
+
export async function registerRemote(ctx, providerId, host, advertised) {
|
|
73
|
+
const provider = (advertised ?? (await discoverRemote(host, ctx.signal))).find((item) => item.id === providerId);
|
|
74
|
+
if (!provider)
|
|
75
|
+
throw new Error(`Source host ${host.hostId}: provider ${providerId} is not advertised`);
|
|
76
|
+
const execute = async (request, signal) => {
|
|
77
|
+
const envelope = await remoteMetadata(host, '/remedia/v1/execute', signal, {
|
|
78
|
+
pluginId: 'discord-player.source',
|
|
79
|
+
operation: 'metadata.execute',
|
|
80
|
+
input: request,
|
|
81
|
+
});
|
|
82
|
+
if (!envelope ||
|
|
83
|
+
typeof envelope !== 'object' ||
|
|
84
|
+
!('handled' in envelope) ||
|
|
85
|
+
envelope.handled !== true ||
|
|
86
|
+
!('result' in envelope))
|
|
87
|
+
throw new Error(`Source host ${host.hostId}: invalid metadata response`);
|
|
88
|
+
return envelope.result;
|
|
89
|
+
};
|
|
90
|
+
const portableQuery = (input) => {
|
|
91
|
+
const original = input.options ?? {};
|
|
92
|
+
return {
|
|
93
|
+
...input,
|
|
94
|
+
options: {
|
|
95
|
+
limit: original.limit,
|
|
96
|
+
blockExtractors: original.blockExtractors,
|
|
97
|
+
requestedBy: typeof original.requestedBy === 'object' ? original.requestedBy?.id : original.requestedBy,
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
if (provider.operations.includes('search.query.match')) {
|
|
102
|
+
ctx.on('search.query.match', async (input, { signal }) => {
|
|
103
|
+
const result = await execute({ providerId, operation: 'search.query.match', input: portableQuery(input) }, signal);
|
|
104
|
+
const matches = z
|
|
105
|
+
.array(z.object({ score: z.number(), pluginId: z.string().optional() }).nullable())
|
|
106
|
+
.parse(result);
|
|
107
|
+
return matches.find((match) => match !== null && match.score >= 0) ?? null;
|
|
108
|
+
}, { protocols: provider.protocols, priority: provider.priorities?.['search.query.match'] });
|
|
109
|
+
}
|
|
110
|
+
if (provider.operations.includes('search.query.resolve')) {
|
|
111
|
+
ctx.on('search.query.resolve', async (input, { signal }) => {
|
|
112
|
+
const result = deserializeSourceResult((await execute({ providerId, operation: 'search.query.resolve', input: portableQuery(input) }, signal)));
|
|
113
|
+
for (const track of result.tracks)
|
|
114
|
+
if (track.sourceReference?.pluginId !== providerId || track.sourceReference.hostId !== host.hostId)
|
|
115
|
+
throw new Error('Remote Track provider or deployment identity does not match its registration');
|
|
116
|
+
return result;
|
|
117
|
+
}, { protocols: provider.protocols, priority: provider.priorities?.['search.query.resolve'] });
|
|
118
|
+
}
|
|
119
|
+
if (provider.operations.includes('track.related')) {
|
|
120
|
+
ctx.on('track.related', async ({ track }, { signal }) => {
|
|
121
|
+
const result = await execute({ providerId, operation: 'track.related', input: { track: serializeSourceTrack(track) } }, signal);
|
|
122
|
+
return deserializeSourceResult(result);
|
|
123
|
+
}, { priority: provider.priorities?.['track.related'] });
|
|
124
|
+
}
|
|
125
|
+
if (provider.operations.includes('lyrics.search')) {
|
|
126
|
+
ctx.on('lyrics.search', async ({ input }, { signal }) => {
|
|
127
|
+
const result = await execute({
|
|
128
|
+
providerId,
|
|
129
|
+
operation: 'lyrics.search',
|
|
130
|
+
input: { input: input instanceof Track ? input.serialize() : input },
|
|
131
|
+
}, signal);
|
|
132
|
+
const groups = z.array(z.array(lyricsResultSchema).nullable()).parse(result);
|
|
133
|
+
return groups
|
|
134
|
+
.flatMap((group) => group ?? [])
|
|
135
|
+
.map((record) => ({
|
|
136
|
+
...record,
|
|
137
|
+
provider: { pluginId: providerId, hostId: host.hostId, data: record.provider?.data ?? null },
|
|
138
|
+
}));
|
|
139
|
+
}, { priority: provider.priorities?.['lyrics.search'] });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=remote.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote.js","sourceRoot":"","sources":["../src/remote.ts"],"names":[],"mappings":"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"}
|
package/dist/runtime.d.ts
CHANGED
|
@@ -30,20 +30,12 @@ export declare class SourcePluginRuntime extends SafeEventEmitter<SourceRuntimeE
|
|
|
30
30
|
* @param options Source settings and optional embedding context.
|
|
31
31
|
*/
|
|
32
32
|
constructor(options?: SourcePluginRuntimeOptions);
|
|
33
|
-
/** Queue orchestration requires an actual DiscordPlayer. */
|
|
34
|
-
get queues(): never;
|
|
35
|
-
/** Voice nodes require an actual DiscordPlayer. */
|
|
36
|
-
get nodes(): never;
|
|
37
|
-
/** Audio clients are unavailable in standalone source execution. */
|
|
38
|
-
get remedia(): never;
|
|
39
33
|
/** Required audio access is unavailable in standalone execution. */
|
|
40
34
|
getRemedia(): RemediaClient;
|
|
41
35
|
/** Optional audio access returns null.
|
|
42
36
|
* @param throwOnMissing Optional lookup flag.
|
|
43
37
|
*/
|
|
44
38
|
getRemedia(throwOnMissing: false): RemediaClient | null;
|
|
45
|
-
/** Playback is outside the source compatibility API. */
|
|
46
|
-
play(): never;
|
|
47
39
|
/** Emit a source diagnostic.
|
|
48
40
|
* @param message Diagnostic text.
|
|
49
41
|
*/
|
package/dist/runtime.js
CHANGED
|
@@ -24,27 +24,11 @@ export class SourcePluginRuntime extends SafeEventEmitter {
|
|
|
24
24
|
this.plugins = new PluginRegistry(this.player, () => this.options.blockExtractors ?? [], options.hostId);
|
|
25
25
|
this.extractors = new ExtractorExecutionContext(this.player);
|
|
26
26
|
}
|
|
27
|
-
/** Queue orchestration requires an actual DiscordPlayer. */
|
|
28
|
-
get queues() {
|
|
29
|
-
throw new Error('SourcePluginRuntime does not provide queues; use DiscordPlayer for playback');
|
|
30
|
-
}
|
|
31
|
-
/** Voice nodes require an actual DiscordPlayer. */
|
|
32
|
-
get nodes() {
|
|
33
|
-
throw new Error('SourcePluginRuntime does not provide voice nodes; use DiscordPlayer for playback');
|
|
34
|
-
}
|
|
35
|
-
/** Audio clients are unavailable in standalone source execution. */
|
|
36
|
-
get remedia() {
|
|
37
|
-
throw new Error('SourcePluginRuntime does not initialize a Remedia audio client');
|
|
38
|
-
}
|
|
39
27
|
getRemedia(throwOnMissing = true) {
|
|
40
28
|
if (throwOnMissing)
|
|
41
29
|
throw new Error('SourcePluginRuntime does not initialize an audio client; use DiscordPlayer');
|
|
42
30
|
return null;
|
|
43
31
|
}
|
|
44
|
-
/** Playback is outside the source compatibility API. */
|
|
45
|
-
play() {
|
|
46
|
-
throw new Error('SourcePluginRuntime cannot play audio; use DiscordPlayer');
|
|
47
|
-
}
|
|
48
32
|
/** Emit a source diagnostic.
|
|
49
33
|
* @param message Diagnostic text.
|
|
50
34
|
*/
|
|
@@ -74,45 +58,59 @@ export class SourcePluginRuntime extends SafeEventEmitter {
|
|
|
74
58
|
return new SearchResult({ tracks: query.tracks, playlist: query });
|
|
75
59
|
}
|
|
76
60
|
const blocked = [...(this.options.blockExtractors ?? []), ...(options.blockExtractors ?? [])];
|
|
77
|
-
const target = options.pluginId ?? (options.searchEngine?.startsWith('ext:') ? options.searchEngine.slice(4) : undefined);
|
|
78
61
|
await this.plugins.ready();
|
|
79
|
-
const
|
|
62
|
+
const engine = options.searchEngine;
|
|
63
|
+
const legacy = engine?.startsWith('ext:') ? engine.slice(4) : undefined;
|
|
64
|
+
if (legacy !== undefined && !this.extractors.get(legacy))
|
|
65
|
+
throw new Error(`Legacy extractor ${legacy} is not registered`);
|
|
66
|
+
const target = legacy ?? (engine && this.plugins.has(engine) ? engine : undefined);
|
|
67
|
+
const prefix = options.resolveCustomProtocols !== false && !/^(?:https?|file):/i.test(query) && !/^[a-z]:/i.test(query)
|
|
68
|
+
? /^([a-z][a-z0-9+.-]*):(?:\/\/)?(.*)$/is.exec(query)
|
|
69
|
+
: null;
|
|
80
70
|
const protocol = prefix && this.plugins.supportsProtocol(prefix[1], { pluginId: target, blocked }) ? prefix[1] : undefined;
|
|
71
|
+
const providerHint = target ? undefined : engine;
|
|
81
72
|
const input = {
|
|
82
73
|
query: protocol ? prefix[2] : query,
|
|
83
74
|
protocol,
|
|
75
|
+
providerHint,
|
|
84
76
|
options: {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
77
|
+
requestedBy: options.requestedBy,
|
|
78
|
+
requestOptions: options.requestOptions,
|
|
79
|
+
limit: options.limit,
|
|
80
|
+
blockExtractors: options.blockExtractors,
|
|
89
81
|
},
|
|
90
82
|
};
|
|
91
|
-
const execution = { signal: options.signal, blocked, pluginId: target };
|
|
83
|
+
const execution = { signal: options.signal, blocked, pluginId: target, hostId: options.hostId };
|
|
92
84
|
const matches = target
|
|
93
|
-
? [{ pluginId: target, score: 0 }]
|
|
94
|
-
:
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
});
|
|
103
|
-
if (!result) {
|
|
104
|
-
return SearchResult.empty();
|
|
105
|
-
}
|
|
106
|
-
for (const track of result.tracks) {
|
|
107
|
-
track.sourceReference ??= {
|
|
85
|
+
? [{ pluginId: target, score: 0, hostId: options.hostId }]
|
|
86
|
+
: protocol
|
|
87
|
+
? this.plugins.protocolMatches(protocol, execution)
|
|
88
|
+
: await this.plugins.execute('search.query.match', input, execution);
|
|
89
|
+
for (const match of matches) {
|
|
90
|
+
if (!match?.pluginId || match.score < 0)
|
|
91
|
+
continue;
|
|
92
|
+
const result = await this.plugins.execute('search.query.resolve', input, {
|
|
93
|
+
...execution,
|
|
108
94
|
pluginId: match.pluginId,
|
|
109
|
-
|
|
110
|
-
};
|
|
111
|
-
if (
|
|
112
|
-
|
|
95
|
+
hostId: match.hostId ?? (target ? options.hostId : null),
|
|
96
|
+
});
|
|
97
|
+
if (!result?.tracks.length) {
|
|
98
|
+
if (target)
|
|
99
|
+
break;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
for (const track of result.tracks) {
|
|
103
|
+
track.sourceReference ??= {
|
|
104
|
+
pluginId: match.pluginId,
|
|
105
|
+
...(match.hostId ? { hostId: match.hostId } : {}),
|
|
106
|
+
data: { url: track.url },
|
|
107
|
+
};
|
|
108
|
+
if (options.requestedBy !== undefined)
|
|
109
|
+
track.requestedBy = options.requestedBy;
|
|
113
110
|
}
|
|
111
|
+
return new SearchResult(result);
|
|
114
112
|
}
|
|
115
|
-
return
|
|
113
|
+
return SearchResult.empty();
|
|
116
114
|
}
|
|
117
115
|
}
|
|
118
116
|
//# sourceMappingURL=runtime.js.map
|