navidrome-mcp 1.0.1
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/LICENSE +661 -0
- package/README.md +397 -0
- package/dist/capabilities.d.ts +20 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +25 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/client/auth-manager.d.ts +27 -0
- package/dist/client/auth-manager.d.ts.map +1 -0
- package/dist/client/auth-manager.js +54 -0
- package/dist/client/auth-manager.js.map +1 -0
- package/dist/client/navidrome-client.d.ts +28 -0
- package/dist/client/navidrome-client.d.ts.map +1 -0
- package/dist/client/navidrome-client.js +84 -0
- package/dist/client/navidrome-client.js.map +1 -0
- package/dist/config.d.ts +41 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +132 -0
- package/dist/config.js.map +1 -0
- package/dist/constants/defaults.d.ts +29 -0
- package/dist/constants/defaults.d.ts.map +1 -0
- package/dist/constants/defaults.js +37 -0
- package/dist/constants/defaults.js.map +1 -0
- package/dist/constants/timeouts.d.ts +66 -0
- package/dist/constants/timeouts.d.ts.map +1 -0
- package/dist/constants/timeouts.js +66 -0
- package/dist/constants/timeouts.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +70 -0
- package/dist/index.js.map +1 -0
- package/dist/resources/index.d.ts +21 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +83 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/schemas/common.d.ts +68 -0
- package/dist/schemas/common.d.ts.map +1 -0
- package/dist/schemas/common.js +69 -0
- package/dist/schemas/common.js.map +1 -0
- package/dist/schemas/index.d.ts +21 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +24 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/pagination.d.ts +153 -0
- package/dist/schemas/pagination.d.ts.map +1 -0
- package/dist/schemas/pagination.js +93 -0
- package/dist/schemas/pagination.js.map +1 -0
- package/dist/schemas/validation.d.ts +147 -0
- package/dist/schemas/validation.d.ts.map +1 -0
- package/dist/schemas/validation.js +143 -0
- package/dist/schemas/validation.js.map +1 -0
- package/dist/tools/handlers/lastfm-handlers.d.ts +5 -0
- package/dist/tools/handlers/lastfm-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/lastfm-handlers.js +143 -0
- package/dist/tools/handlers/lastfm-handlers.js.map +1 -0
- package/dist/tools/handlers/lyrics-handlers.d.ts +5 -0
- package/dist/tools/handlers/lyrics-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/lyrics-handlers.js +51 -0
- package/dist/tools/handlers/lyrics-handlers.js.map +1 -0
- package/dist/tools/handlers/playlist-handlers.d.ts +5 -0
- package/dist/tools/handlers/playlist-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/playlist-handlers.js +316 -0
- package/dist/tools/handlers/playlist-handlers.js.map +1 -0
- package/dist/tools/handlers/queue-handlers.d.ts +5 -0
- package/dist/tools/handlers/queue-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/queue-handlers.js +135 -0
- package/dist/tools/handlers/queue-handlers.js.map +1 -0
- package/dist/tools/handlers/radio-handlers.d.ts +5 -0
- package/dist/tools/handlers/radio-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/radio-handlers.js +323 -0
- package/dist/tools/handlers/radio-handlers.js.map +1 -0
- package/dist/tools/handlers/registry.d.ts +26 -0
- package/dist/tools/handlers/registry.d.ts.map +1 -0
- package/dist/tools/handlers/registry.js +77 -0
- package/dist/tools/handlers/registry.js.map +1 -0
- package/dist/tools/handlers/search-handlers.d.ts +5 -0
- package/dist/tools/handlers/search-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/search-handlers.js +125 -0
- package/dist/tools/handlers/search-handlers.js.map +1 -0
- package/dist/tools/handlers/tag-handlers.d.ts +5 -0
- package/dist/tools/handlers/tag-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/tag-handlers.js +155 -0
- package/dist/tools/handlers/tag-handlers.js.map +1 -0
- package/dist/tools/handlers/user-preferences-handlers.d.ts +5 -0
- package/dist/tools/handlers/user-preferences-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/user-preferences-handlers.js +155 -0
- package/dist/tools/handlers/user-preferences-handlers.js.map +1 -0
- package/dist/tools/index.d.ts +19 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +20 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/lastfm-discovery.d.ts +107 -0
- package/dist/tools/lastfm-discovery.d.ts.map +1 -0
- package/dist/tools/lastfm-discovery.js +238 -0
- package/dist/tools/lastfm-discovery.js.map +1 -0
- package/dist/tools/library.d.ts +31 -0
- package/dist/tools/library.d.ts.map +1 -0
- package/dist/tools/library.js +272 -0
- package/dist/tools/library.js.map +1 -0
- package/dist/tools/listening-history.d.ts +51 -0
- package/dist/tools/listening-history.d.ts.map +1 -0
- package/dist/tools/listening-history.js +126 -0
- package/dist/tools/listening-history.js.map +1 -0
- package/dist/tools/lyrics.d.ts +35 -0
- package/dist/tools/lyrics.d.ts.map +1 -0
- package/dist/tools/lyrics.js +222 -0
- package/dist/tools/lyrics.js.map +1 -0
- package/dist/tools/media-library.d.ts +46 -0
- package/dist/tools/media-library.d.ts.map +1 -0
- package/dist/tools/media-library.js +179 -0
- package/dist/tools/media-library.js.map +1 -0
- package/dist/tools/playlist-management.d.ts +80 -0
- package/dist/tools/playlist-management.d.ts.map +1 -0
- package/dist/tools/playlist-management.js +384 -0
- package/dist/tools/playlist-management.js.map +1 -0
- package/dist/tools/queue-management.d.ts +48 -0
- package/dist/tools/queue-management.d.ts.map +1 -0
- package/dist/tools/queue-management.js +83 -0
- package/dist/tools/queue-management.js.map +1 -0
- package/dist/tools/radio-discovery.d.ts +95 -0
- package/dist/tools/radio-discovery.d.ts.map +1 -0
- package/dist/tools/radio-discovery.js +352 -0
- package/dist/tools/radio-discovery.js.map +1 -0
- package/dist/tools/radio-validation.d.ts +49 -0
- package/dist/tools/radio-validation.d.ts.map +1 -0
- package/dist/tools/radio-validation.js +463 -0
- package/dist/tools/radio-validation.js.map +1 -0
- package/dist/tools/radio.d.ts +38 -0
- package/dist/tools/radio.d.ts.map +1 -0
- package/dist/tools/radio.js +315 -0
- package/dist/tools/radio.js.map +1 -0
- package/dist/tools/search.d.ts +54 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +165 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/tags.d.ts +63 -0
- package/dist/tools/tags.d.ts.map +1 -0
- package/dist/tools/tags.js +260 -0
- package/dist/tools/tags.js.map +1 -0
- package/dist/tools/test.d.ts +50 -0
- package/dist/tools/test.d.ts.map +1 -0
- package/dist/tools/test.js +121 -0
- package/dist/tools/test.js.map +1 -0
- package/dist/tools/user-preferences.d.ts +73 -0
- package/dist/tools/user-preferences.d.ts.map +1 -0
- package/dist/tools/user-preferences.js +231 -0
- package/dist/tools/user-preferences.js.map +1 -0
- package/dist/transformers/song-transformer.d.ts +122 -0
- package/dist/transformers/song-transformer.d.ts.map +1 -0
- package/dist/transformers/song-transformer.js +232 -0
- package/dist/transformers/song-transformer.js.map +1 -0
- package/dist/types/core.d.ts +147 -0
- package/dist/types/core.d.ts.map +1 -0
- package/dist/types/core.js +19 -0
- package/dist/types/core.js.map +1 -0
- package/dist/types/index.d.ts +23 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +19 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/lyrics.d.ts +58 -0
- package/dist/types/lyrics.d.ts.map +1 -0
- package/dist/types/lyrics.js +19 -0
- package/dist/types/lyrics.js.map +1 -0
- package/dist/types/playlists.d.ts +150 -0
- package/dist/types/playlists.d.ts.map +1 -0
- package/dist/types/playlists.js +19 -0
- package/dist/types/playlists.js.map +1 -0
- package/dist/types/radio.d.ts +192 -0
- package/dist/types/radio.d.ts.map +1 -0
- package/dist/types/radio.js +19 -0
- package/dist/types/radio.js.map +1 -0
- package/dist/types/tags.d.ts +72 -0
- package/dist/types/tags.d.ts.map +1 -0
- package/dist/types/tags.js +19 -0
- package/dist/types/tags.js.map +1 -0
- package/dist/utils/cache.d.ts +32 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +84 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/error-formatter.d.ts +108 -0
- package/dist/utils/error-formatter.d.ts.map +1 -0
- package/dist/utils/error-formatter.js +161 -0
- package/dist/utils/error-formatter.js.map +1 -0
- package/dist/utils/logger.d.ts +33 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +52 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/message-manager.d.ts +74 -0
- package/dist/utils/message-manager.d.ts.map +1 -0
- package/dist/utils/message-manager.js +134 -0
- package/dist/utils/message-manager.js.map +1 -0
- package/dist/utils/version.d.ts +5 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +19 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +78 -0
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navidrome MCP Server - Radio Stream Validation Tool
|
|
3
|
+
* Copyright (C) 2025
|
|
4
|
+
*
|
|
5
|
+
* This program is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU Affero General Public License as published
|
|
7
|
+
* by the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
* (at your option) any later version.
|
|
9
|
+
*
|
|
10
|
+
* This program is distributed in the hope that it will be useful,
|
|
11
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
* GNU Affero General Public License for more details.
|
|
14
|
+
*
|
|
15
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
import { z } from 'zod';
|
|
19
|
+
import { fileTypeFromBuffer } from 'file-type';
|
|
20
|
+
import { SINGLE_VALIDATION_TIMEOUT, MIN_VALIDATION_TIMEOUT, MAX_VALIDATION_TIMEOUT, RADIO_VALIDATION } from '../constants/timeouts.js';
|
|
21
|
+
// Validation parameter schema
|
|
22
|
+
const ValidateStreamSchema = z.object({
|
|
23
|
+
url: z.string().url('URL must be a valid URL'),
|
|
24
|
+
timeout: z.number().min(MIN_VALIDATION_TIMEOUT).max(MAX_VALIDATION_TIMEOUT).optional().default(SINGLE_VALIDATION_TIMEOUT),
|
|
25
|
+
followRedirects: z.boolean().optional().default(true),
|
|
26
|
+
});
|
|
27
|
+
// Valid audio MIME types
|
|
28
|
+
const VALID_AUDIO_MIMES = [
|
|
29
|
+
'audio/mpeg',
|
|
30
|
+
'audio/mp3',
|
|
31
|
+
'audio/aac',
|
|
32
|
+
'audio/aacp',
|
|
33
|
+
'audio/ogg',
|
|
34
|
+
'audio/opus',
|
|
35
|
+
'audio/flac',
|
|
36
|
+
'audio/x-ms-wma',
|
|
37
|
+
'application/ogg',
|
|
38
|
+
'audio/webm',
|
|
39
|
+
'audio/x-mpegurl', // M3U playlist
|
|
40
|
+
'audio/x-scpls', // PLS playlist
|
|
41
|
+
'application/vnd.apple.mpegurl', // HLS
|
|
42
|
+
];
|
|
43
|
+
// Streaming-specific headers to check
|
|
44
|
+
const STREAMING_HEADERS = [
|
|
45
|
+
'icy-name',
|
|
46
|
+
'icy-br',
|
|
47
|
+
'icy-metaint',
|
|
48
|
+
'icy-genre',
|
|
49
|
+
'icy-url',
|
|
50
|
+
'icy-pub',
|
|
51
|
+
'x-audiocast-name',
|
|
52
|
+
'x-audiocast-genre',
|
|
53
|
+
'x-audiocast-bitrate',
|
|
54
|
+
];
|
|
55
|
+
/**
|
|
56
|
+
* Extract streaming headers from response
|
|
57
|
+
*/
|
|
58
|
+
function extractStreamingHeaders(headers) {
|
|
59
|
+
const streamHeaders = {};
|
|
60
|
+
headers.forEach((value, key) => {
|
|
61
|
+
const lowerKey = key.toLowerCase();
|
|
62
|
+
if (STREAMING_HEADERS.includes(lowerKey) || lowerKey.startsWith('icy-')) {
|
|
63
|
+
streamHeaders[lowerKey] = value;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
return streamHeaders;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Check if content type indicates audio
|
|
70
|
+
*/
|
|
71
|
+
function isAudioContentType(contentType) {
|
|
72
|
+
if (!contentType)
|
|
73
|
+
return false;
|
|
74
|
+
const normalized = contentType.toLowerCase();
|
|
75
|
+
return VALID_AUDIO_MIMES.some(mime => normalized.includes(mime));
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Detect audio format from buffer
|
|
79
|
+
*/
|
|
80
|
+
async function detectAudioFormat(buffer) {
|
|
81
|
+
try {
|
|
82
|
+
const fileType = await fileTypeFromBuffer(buffer);
|
|
83
|
+
if (fileType && fileType.mime.startsWith('audio/')) {
|
|
84
|
+
return {
|
|
85
|
+
detected: true,
|
|
86
|
+
format: fileType.ext,
|
|
87
|
+
mime: fileType.mime,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
// Check for common audio signatures manually if file-type doesn't detect
|
|
91
|
+
const signatures = [
|
|
92
|
+
{ bytes: [0xFF, 0xFB], format: 'mp3', mime: 'audio/mpeg' }, // MP3
|
|
93
|
+
{ bytes: [0xFF, 0xF1], format: 'aac', mime: 'audio/aac' }, // AAC
|
|
94
|
+
{ bytes: [0xFF, 0xF9], format: 'aac', mime: 'audio/aac' }, // AAC
|
|
95
|
+
{ bytes: [0x4F, 0x67, 0x67, 0x53], format: 'ogg', mime: 'audio/ogg' }, // OGG
|
|
96
|
+
];
|
|
97
|
+
for (const sig of signatures) {
|
|
98
|
+
let matches = true;
|
|
99
|
+
for (let i = 0; i < sig.bytes.length; i++) {
|
|
100
|
+
if (buffer[i] !== sig.bytes[i]) {
|
|
101
|
+
matches = false;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (matches) {
|
|
106
|
+
return {
|
|
107
|
+
detected: true,
|
|
108
|
+
format: sig.format,
|
|
109
|
+
mime: sig.mime,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return { detected: false };
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return { detected: false };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Perform HEAD request validation
|
|
121
|
+
*/
|
|
122
|
+
async function validateWithHead(context) {
|
|
123
|
+
try {
|
|
124
|
+
const controller = new AbortController();
|
|
125
|
+
const headTimeout = Math.min(RADIO_VALIDATION.FALLBACK_HEAD_TIMEOUT, Math.floor(context.timeout * RADIO_VALIDATION.HEAD_TIMEOUT_RATIO)); // Use 60% of total timeout
|
|
126
|
+
const timeoutId = setTimeout(() => {
|
|
127
|
+
controller.abort();
|
|
128
|
+
}, headTimeout);
|
|
129
|
+
const response = await fetch(context.url, {
|
|
130
|
+
method: 'HEAD',
|
|
131
|
+
redirect: context.followRedirects ? 'follow' : 'manual',
|
|
132
|
+
signal: controller.signal,
|
|
133
|
+
headers: {
|
|
134
|
+
'User-Agent': 'Mozilla/5.0 (compatible; NavidromeBot/1.0)',
|
|
135
|
+
'Accept': 'audio/*',
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
clearTimeout(timeoutId);
|
|
139
|
+
return { response, error: null };
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
if (err instanceof Error) {
|
|
143
|
+
if (err.name === 'AbortError') {
|
|
144
|
+
return { response: null, error: `HEAD request timeout after ${Math.min(RADIO_VALIDATION.FALLBACK_HEAD_TIMEOUT, Math.floor(context.timeout * RADIO_VALIDATION.HEAD_TIMEOUT_RATIO))}ms` };
|
|
145
|
+
}
|
|
146
|
+
return { response: null, error: `HEAD request failed: ${err.message}` };
|
|
147
|
+
}
|
|
148
|
+
return { response: null, error: 'Unknown HEAD request error' };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Sample audio data from stream
|
|
153
|
+
*/
|
|
154
|
+
async function sampleAudioData(url, remainingTimeout) {
|
|
155
|
+
try {
|
|
156
|
+
const controller = new AbortController();
|
|
157
|
+
const sampleTimeout = Math.max(RADIO_VALIDATION.MIN_SAMPLE_TIMEOUT, remainingTimeout); // Ensure at least 2 seconds
|
|
158
|
+
const timeoutId = setTimeout(() => {
|
|
159
|
+
controller.abort();
|
|
160
|
+
}, sampleTimeout);
|
|
161
|
+
const response = await fetch(url, {
|
|
162
|
+
method: 'GET',
|
|
163
|
+
headers: {
|
|
164
|
+
'Range': `bytes=0-${RADIO_VALIDATION.SAMPLE_BUFFER_SIZE - 1}`, // Get first 8KB
|
|
165
|
+
'User-Agent': 'Mozilla/5.0 (compatible; NavidromeBot/1.0)',
|
|
166
|
+
'Accept': 'audio/*',
|
|
167
|
+
},
|
|
168
|
+
signal: controller.signal,
|
|
169
|
+
});
|
|
170
|
+
clearTimeout(timeoutId);
|
|
171
|
+
if (!response.ok && response.status !== 206) {
|
|
172
|
+
return {
|
|
173
|
+
buffer: null,
|
|
174
|
+
headers: response.headers,
|
|
175
|
+
error: `HTTP ${response.status}: ${response.statusText}`
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
// Some servers don't handle Range requests properly and hang on arrayBuffer()
|
|
179
|
+
// Use streaming approach with timeout protection
|
|
180
|
+
try {
|
|
181
|
+
const reader = response.body?.getReader();
|
|
182
|
+
if (!reader) {
|
|
183
|
+
return {
|
|
184
|
+
buffer: null,
|
|
185
|
+
headers: response.headers,
|
|
186
|
+
error: 'No response body reader available'
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
const chunks = [];
|
|
190
|
+
let totalLength = 0;
|
|
191
|
+
const maxBytes = RADIO_VALIDATION.SAMPLE_BUFFER_SIZE; // 8KB limit
|
|
192
|
+
const startTime = Date.now();
|
|
193
|
+
const readTimeout = RADIO_VALIDATION.STREAM_READ_TIMEOUT; // 3 second timeout for reading
|
|
194
|
+
while (true) {
|
|
195
|
+
// Check if we've exceeded our read timeout
|
|
196
|
+
if (Date.now() - startTime > readTimeout) {
|
|
197
|
+
await reader.cancel();
|
|
198
|
+
return {
|
|
199
|
+
buffer: totalLength > 0 ? new Uint8Array(totalLength) : null,
|
|
200
|
+
headers: response.headers,
|
|
201
|
+
error: 'Read timeout - got partial data'
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
const { value, done } = await reader.read();
|
|
205
|
+
if (done)
|
|
206
|
+
break;
|
|
207
|
+
if (value) {
|
|
208
|
+
chunks.push(value);
|
|
209
|
+
totalLength += value.length;
|
|
210
|
+
// Stop if we have enough data
|
|
211
|
+
if (totalLength >= maxBytes) {
|
|
212
|
+
await reader.cancel();
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// Combine chunks
|
|
218
|
+
if (totalLength > 0) {
|
|
219
|
+
const buffer = new Uint8Array(totalLength);
|
|
220
|
+
let offset = 0;
|
|
221
|
+
for (const chunk of chunks) {
|
|
222
|
+
buffer.set(chunk, offset);
|
|
223
|
+
offset += chunk.length;
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
buffer,
|
|
227
|
+
headers: response.headers,
|
|
228
|
+
error: null
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
return {
|
|
233
|
+
buffer: null,
|
|
234
|
+
headers: response.headers,
|
|
235
|
+
error: 'No data received from stream'
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
catch (streamErr) {
|
|
240
|
+
if (streamErr instanceof Error && streamErr.name === 'AbortError') {
|
|
241
|
+
return { buffer: null, headers: response.headers, error: 'Stream reading aborted' };
|
|
242
|
+
}
|
|
243
|
+
throw streamErr;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
catch (err) {
|
|
247
|
+
if (err instanceof Error) {
|
|
248
|
+
if (err.name === 'AbortError') {
|
|
249
|
+
return { buffer: null, headers: null, error: `Audio sampling timeout after ${Math.max(RADIO_VALIDATION.MIN_SAMPLE_TIMEOUT, remainingTimeout)}ms` };
|
|
250
|
+
}
|
|
251
|
+
return { buffer: null, headers: null, error: `Audio sampling failed: ${err.message}` };
|
|
252
|
+
}
|
|
253
|
+
return { buffer: null, headers: null, error: 'Unknown audio sampling error' };
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Generate recommendations based on validation results
|
|
258
|
+
*/
|
|
259
|
+
function generateRecommendations(result) {
|
|
260
|
+
const recommendations = [];
|
|
261
|
+
if (result.status === 'valid') {
|
|
262
|
+
recommendations.push('✅ Stream validated successfully');
|
|
263
|
+
if (result.streamingHeaders?.['icy-name']) {
|
|
264
|
+
recommendations.push(`🎵 Station: ${result.streamingHeaders['icy-name']}`);
|
|
265
|
+
}
|
|
266
|
+
if (result.streamingHeaders?.['icy-br']) {
|
|
267
|
+
recommendations.push(`📊 Bitrate: ${result.streamingHeaders['icy-br']}kbps`);
|
|
268
|
+
}
|
|
269
|
+
if (result.audioFormat?.format) {
|
|
270
|
+
recommendations.push(`🎧 Format: ${result.audioFormat.format.toUpperCase()}`);
|
|
271
|
+
}
|
|
272
|
+
recommendations.push('✨ Ready to add as radio station');
|
|
273
|
+
}
|
|
274
|
+
else if (result.status === 'invalid') {
|
|
275
|
+
recommendations.push('❌ Stream validation failed');
|
|
276
|
+
if (result.httpStatus === 404) {
|
|
277
|
+
recommendations.push('🔍 Stream URL appears to be offline or moved');
|
|
278
|
+
recommendations.push('💡 Check the station\'s official website for updated URLs');
|
|
279
|
+
}
|
|
280
|
+
else if (!result.validation?.hasAudioContentType) {
|
|
281
|
+
recommendations.push('⚠️ URL does not serve audio content');
|
|
282
|
+
recommendations.push('💡 Ensure you\'re using the stream URL, not the website URL');
|
|
283
|
+
}
|
|
284
|
+
else if (!result.validation?.audioDataDetected) {
|
|
285
|
+
recommendations.push('⚠️ Could not detect valid audio data');
|
|
286
|
+
recommendations.push('💡 The stream may be geo-restricted or require authentication');
|
|
287
|
+
}
|
|
288
|
+
recommendations.push('🌐 Try finding alternative streams at radio-browser.info');
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
recommendations.push('⚠️ Stream validation encountered an error');
|
|
292
|
+
recommendations.push('🔄 Try again later or check your network connection');
|
|
293
|
+
}
|
|
294
|
+
return recommendations;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Validate a radio stream URL
|
|
298
|
+
*/
|
|
299
|
+
export async function validateRadioStream(_client, args) {
|
|
300
|
+
const startTime = Date.now();
|
|
301
|
+
const errors = [];
|
|
302
|
+
const warnings = [];
|
|
303
|
+
// Parse and validate input
|
|
304
|
+
let params;
|
|
305
|
+
try {
|
|
306
|
+
params = ValidateStreamSchema.parse(args);
|
|
307
|
+
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
if (error instanceof z.ZodError) {
|
|
310
|
+
return {
|
|
311
|
+
success: false,
|
|
312
|
+
url: String(args),
|
|
313
|
+
status: 'error',
|
|
314
|
+
streamingHeaders: {},
|
|
315
|
+
validation: {
|
|
316
|
+
httpAccessible: false,
|
|
317
|
+
hasAudioContentType: false,
|
|
318
|
+
hasStreamingHeaders: false,
|
|
319
|
+
audioDataDetected: false,
|
|
320
|
+
},
|
|
321
|
+
errors: [`Invalid parameters: ${error.issues.map((e) => e.message).join(', ')}`],
|
|
322
|
+
warnings: [],
|
|
323
|
+
recommendations: ['❌ Please provide a valid URL'],
|
|
324
|
+
testDuration: Date.now() - startTime,
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
throw error;
|
|
328
|
+
}
|
|
329
|
+
const context = {
|
|
330
|
+
url: params.url,
|
|
331
|
+
startTime,
|
|
332
|
+
timeout: params.timeout,
|
|
333
|
+
followRedirects: params.followRedirects,
|
|
334
|
+
};
|
|
335
|
+
// Initialize result
|
|
336
|
+
const result = {
|
|
337
|
+
success: false,
|
|
338
|
+
url: params.url,
|
|
339
|
+
status: 'invalid',
|
|
340
|
+
streamingHeaders: {},
|
|
341
|
+
validation: {
|
|
342
|
+
httpAccessible: false,
|
|
343
|
+
hasAudioContentType: false,
|
|
344
|
+
hasStreamingHeaders: false,
|
|
345
|
+
audioDataDetected: false,
|
|
346
|
+
},
|
|
347
|
+
errors,
|
|
348
|
+
warnings,
|
|
349
|
+
recommendations: [],
|
|
350
|
+
testDuration: 0,
|
|
351
|
+
};
|
|
352
|
+
// Add overall timeout protection
|
|
353
|
+
const overallController = new AbortController();
|
|
354
|
+
const overallTimeoutId = setTimeout(() => {
|
|
355
|
+
overallController.abort();
|
|
356
|
+
errors.push(`Validation timeout after ${params.timeout}ms`);
|
|
357
|
+
}, params.timeout);
|
|
358
|
+
let headResponse = null;
|
|
359
|
+
let headError = null;
|
|
360
|
+
let buffer = null;
|
|
361
|
+
let headers = null;
|
|
362
|
+
let sampleError = null;
|
|
363
|
+
try {
|
|
364
|
+
// Step 1: Try HEAD request first
|
|
365
|
+
const headResult = await validateWithHead(context);
|
|
366
|
+
headResponse = headResult.response;
|
|
367
|
+
headError = headResult.error;
|
|
368
|
+
if (headError) {
|
|
369
|
+
warnings.push(headError);
|
|
370
|
+
}
|
|
371
|
+
// Step 2: Check if HEAD response gives us enough info to determine validity
|
|
372
|
+
let skipAudioSampling = false;
|
|
373
|
+
if (headResponse) {
|
|
374
|
+
const contentType = headResponse.headers.get('content-type');
|
|
375
|
+
const streamHeaders = extractStreamingHeaders(headResponse.headers);
|
|
376
|
+
// If we have clear audio content-type OR streaming headers, we can skip audio sampling
|
|
377
|
+
const hasAudioContentType = contentType && isAudioContentType(contentType);
|
|
378
|
+
const hasStreamingHeaders = Object.keys(streamHeaders).length > 0;
|
|
379
|
+
if (hasAudioContentType || hasStreamingHeaders) {
|
|
380
|
+
skipAudioSampling = true;
|
|
381
|
+
// Create a fake successful result for audio detection based on content-type
|
|
382
|
+
buffer = new Uint8Array([0xFF, 0xFB]); // Minimal buffer to satisfy validation logic
|
|
383
|
+
headers = headResponse.headers;
|
|
384
|
+
sampleError = null;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
// Step 3: Sample audio data only if headers were inconclusive
|
|
388
|
+
if (!skipAudioSampling) {
|
|
389
|
+
const elapsed = Date.now() - startTime;
|
|
390
|
+
const remainingTime = params.timeout - elapsed;
|
|
391
|
+
if (remainingTime > 1000 && !overallController.signal.aborted) {
|
|
392
|
+
const sampleResult = await sampleAudioData(params.url, remainingTime);
|
|
393
|
+
buffer = sampleResult.buffer;
|
|
394
|
+
headers = sampleResult.headers || headResponse?.headers || null;
|
|
395
|
+
sampleError = sampleResult.error;
|
|
396
|
+
}
|
|
397
|
+
else if (remainingTime <= 1000) {
|
|
398
|
+
sampleError = 'Insufficient time remaining for audio sampling';
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
catch (err) {
|
|
403
|
+
if (err instanceof Error && err.name === 'AbortError') {
|
|
404
|
+
errors.push('Validation aborted due to overall timeout');
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
errors.push(`Validation failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
finally {
|
|
411
|
+
clearTimeout(overallTimeoutId);
|
|
412
|
+
}
|
|
413
|
+
if (sampleError && !headResponse) {
|
|
414
|
+
errors.push(sampleError);
|
|
415
|
+
result.status = 'error';
|
|
416
|
+
}
|
|
417
|
+
// Use whichever response we got
|
|
418
|
+
const finalResponse = headResponse || (headers ? { headers, ok: true, status: 200, url: params.url } : null);
|
|
419
|
+
if (finalResponse) {
|
|
420
|
+
result.httpStatus = finalResponse.status || 200;
|
|
421
|
+
result.validation.httpAccessible = true;
|
|
422
|
+
// Check for redirects
|
|
423
|
+
if (finalResponse.url && finalResponse.url !== params.url) {
|
|
424
|
+
result.finalUrl = finalResponse.url;
|
|
425
|
+
}
|
|
426
|
+
// Extract content type
|
|
427
|
+
const contentType = finalResponse.headers.get('content-type');
|
|
428
|
+
if (contentType) {
|
|
429
|
+
result.contentType = contentType;
|
|
430
|
+
result.validation.hasAudioContentType = isAudioContentType(contentType);
|
|
431
|
+
if (!result.validation.hasAudioContentType) {
|
|
432
|
+
errors.push(`Non-audio content type: ${contentType}`);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
// Extract streaming headers
|
|
436
|
+
result.streamingHeaders = extractStreamingHeaders(finalResponse.headers);
|
|
437
|
+
result.validation.hasStreamingHeaders = Object.keys(result.streamingHeaders).length > 0;
|
|
438
|
+
}
|
|
439
|
+
// Step 3: Detect audio format if we got data
|
|
440
|
+
if (buffer && buffer.length > 0) {
|
|
441
|
+
const audioFormat = await detectAudioFormat(buffer);
|
|
442
|
+
result.audioFormat = audioFormat;
|
|
443
|
+
result.validation.audioDataDetected = audioFormat.detected;
|
|
444
|
+
if (!audioFormat.detected && result.validation.hasAudioContentType) {
|
|
445
|
+
warnings.push('Could not detect audio format from data sample');
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
else if (result.validation.httpAccessible) {
|
|
449
|
+
warnings.push('Could not sample audio data from stream');
|
|
450
|
+
}
|
|
451
|
+
// Determine overall success
|
|
452
|
+
result.success = result.validation.httpAccessible &&
|
|
453
|
+
(result.validation.hasAudioContentType ||
|
|
454
|
+
result.validation.audioDataDetected ||
|
|
455
|
+
result.validation.hasStreamingHeaders);
|
|
456
|
+
result.status = result.success ? 'valid' : (errors.length > 0 ? 'error' : 'invalid');
|
|
457
|
+
// Generate recommendations
|
|
458
|
+
result.recommendations = generateRecommendations(result);
|
|
459
|
+
// Set test duration
|
|
460
|
+
result.testDuration = Date.now() - startTime;
|
|
461
|
+
return result;
|
|
462
|
+
}
|
|
463
|
+
//# sourceMappingURL=radio-validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"radio-validation.js","sourceRoot":"","sources":["../../src/tools/radio-validation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Config } from '../config.js';
|
|
2
|
+
import type { RadioStationDTO, CreateRadioStationResponse, DeleteRadioStationResponse, ListRadioStationsResponse, RadioPlaybackInfo } from '../types/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* List all internet radio stations
|
|
5
|
+
*/
|
|
6
|
+
export declare function listRadioStations(config: Config, args: unknown): Promise<ListRadioStationsResponse>;
|
|
7
|
+
/**
|
|
8
|
+
* Create a new radio station
|
|
9
|
+
*/
|
|
10
|
+
export declare function createRadioStation(config: Config, args: unknown): Promise<CreateRadioStationResponse>;
|
|
11
|
+
/**
|
|
12
|
+
* Delete a radio station by ID
|
|
13
|
+
*/
|
|
14
|
+
export declare function deleteRadioStation(config: Config, args: unknown): Promise<DeleteRadioStationResponse>;
|
|
15
|
+
/**
|
|
16
|
+
* Get a specific radio station by ID
|
|
17
|
+
*/
|
|
18
|
+
export declare function getRadioStation(config: Config, args: unknown): Promise<RadioStationDTO>;
|
|
19
|
+
/**
|
|
20
|
+
* Play a radio station (sets it in the queue)
|
|
21
|
+
*/
|
|
22
|
+
export declare function playRadioStation(config: Config, args: unknown): Promise<{
|
|
23
|
+
success: boolean;
|
|
24
|
+
message: string;
|
|
25
|
+
station?: RadioStationDTO;
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* Batch create multiple radio stations
|
|
29
|
+
*/
|
|
30
|
+
export declare function batchCreateRadioStations(config: Config, args: unknown): Promise<{
|
|
31
|
+
results: CreateRadioStationResponse[];
|
|
32
|
+
summary: string;
|
|
33
|
+
}>;
|
|
34
|
+
/**
|
|
35
|
+
* Get current radio playback information
|
|
36
|
+
*/
|
|
37
|
+
export declare function getCurrentRadioInfo(_config: Config, _args: unknown): Promise<RadioPlaybackInfo>;
|
|
38
|
+
//# sourceMappingURL=radio.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"radio.d.ts","sourceRoot":"","sources":["../../src/tools/radio.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EACV,eAAe,EAEf,0BAA0B,EAC1B,0BAA0B,EAC1B,yBAAyB,EACzB,iBAAiB,EAClB,MAAM,mBAAmB,CAAC;AAyC3B;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC,yBAAyB,CAAC,CAsDpC;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC,0BAA0B,CAAC,CAsFrC;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC,0BAA0B,CAAC,CAuCrC;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC,eAAe,CAAC,CAwB1B;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,eAAe,CAAA;CAAE,CAAC,CA+B3E;AAED;;GAEG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC;IAAE,OAAO,EAAE,0BAA0B,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CA4DrE;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,iBAAiB,CAAC,CA2B5B"}
|