@ziplayer/plugin 0.1.50 → 0.1.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/TTSPlugin.d.ts.map +1 -1
- package/dist/TTSPlugin.js +4 -1
- package/dist/TTSPlugin.js.map +1 -1
- package/package.json +4 -4
- package/src/AttachmentsPlugin.ts +523 -523
- package/src/TTSPlugin.ts +4 -1
- package/dist/utils/progress-bar.d.ts +0 -8
- package/dist/utils/progress-bar.d.ts.map +0 -1
- package/dist/utils/progress-bar.js +0 -24
- package/dist/utils/progress-bar.js.map +0 -1
package/dist/TTSPlugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TTSPlugin.d.ts","sourceRoot":"","sources":["../src/TTSPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAKlC;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sCAAsC;IACtC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;;;;;;;;;;OAYG;IACH,YAAY,CAAC,EAAE,CACd,IAAI,EAAE,MAAM,EACZ,GAAG,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE,KAElD,OAAO,CAAC,QAAQ,GAAG,MAAM,GAAG,GAAG,GAAG,MAAM,GAAG,UAAU,GAAG,WAAW,CAAC,GACpE,QAAQ,GACR,MAAM,GACN,GAAG,GACH,MAAM,GACN,UAAU,GACV,WAAW,CAAC;CACf;AAcD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,qBAAa,SAAU,SAAQ,UAAU;IACxC,IAAI,SAAS;IACb,OAAO,SAAW;IAClB,OAAO,CAAC,IAAI,CAA0F;IAEtG;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;gBACS,IAAI,CAAC,EAAE,gBAAgB;IASnC;;;;;;;;;;OAUG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAMjC;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAqCvE;;;;;;;OAOG;YACW,kBAAkB;IAgChC;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAaxB;;;;;;;;;;;;;;;;OAgBG;IACG,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"TTSPlugin.d.ts","sourceRoot":"","sources":["../src/TTSPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAKlC;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sCAAsC;IACtC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;;;;;;;;;;OAYG;IACH,YAAY,CAAC,EAAE,CACd,IAAI,EAAE,MAAM,EACZ,GAAG,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE,KAElD,OAAO,CAAC,QAAQ,GAAG,MAAM,GAAG,GAAG,GAAG,MAAM,GAAG,UAAU,GAAG,WAAW,CAAC,GACpE,QAAQ,GACR,MAAM,GACN,GAAG,GACH,MAAM,GACN,UAAU,GACV,WAAW,CAAC;CACf;AAcD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,qBAAa,SAAU,SAAQ,UAAU;IACxC,IAAI,SAAS;IACb,OAAO,SAAW;IAClB,OAAO,CAAC,IAAI,CAA0F;IAEtG;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;gBACS,IAAI,CAAC,EAAE,gBAAgB;IASnC;;;;;;;;;;OAUG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAMjC;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAqCvE;;;;;;;OAOG;YACW,kBAAkB;IAgChC;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAaxB;;;;;;;;;;;;;;;;OAgBG;IACG,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC;YAsEpC,UAAU;IAgBxB,OAAO,CAAC,UAAU;IAiDlB,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,aAAa;CAYrB"}
|
package/dist/TTSPlugin.js
CHANGED
|
@@ -262,7 +262,10 @@ class TTSPlugin extends ziplayer_1.BasePlugin {
|
|
|
262
262
|
const urlStr = o.url.toString();
|
|
263
263
|
try {
|
|
264
264
|
type =
|
|
265
|
-
normType(o.type) ||
|
|
265
|
+
normType(o.type) ||
|
|
266
|
+
(urlStr.endsWith(".webm") ? "webm/opus"
|
|
267
|
+
: urlStr.endsWith(".ogg") ? "ogg/opus"
|
|
268
|
+
: undefined);
|
|
266
269
|
const res = await axios_1.default.get(urlStr, { responseType: "stream" });
|
|
267
270
|
stream = res.data;
|
|
268
271
|
metadata = o.metadata;
|
package/dist/TTSPlugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TTSPlugin.js","sourceRoot":"","sources":["../src/TTSPlugin.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TTSPlugin.js","sourceRoot":"","sources":["../src/TTSPlugin.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ziplayer/plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.52",
|
|
4
4
|
"description": "A modular Discord voice player with plugin system",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ZiPlayer",
|
|
@@ -28,15 +28,15 @@
|
|
|
28
28
|
"prepare": "npm run build"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@zibot/scdl": "^0.0.
|
|
31
|
+
"@zibot/scdl": "^0.0.6",
|
|
32
32
|
"@zibot/zitts": "^0.0.3",
|
|
33
|
-
"axios": "^1.
|
|
33
|
+
"axios": "^1.13.2",
|
|
34
34
|
"cli-progress": "^3.12.0",
|
|
35
35
|
"googlevideo": "^1.0.0",
|
|
36
36
|
"music-metadata": "^11.9.0",
|
|
37
37
|
"youtube-sr": "^4.3.4",
|
|
38
38
|
"youtubei.js": "^16.0.1",
|
|
39
|
-
"ziplayer": "^0.1
|
|
39
|
+
"ziplayer": "^0.2.1"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@types/node": "^20.0.0",
|
package/src/AttachmentsPlugin.ts
CHANGED
|
@@ -1,523 +1,523 @@
|
|
|
1
|
-
import { BasePlugin, Track, SearchResult, StreamInfo } from "ziplayer";
|
|
2
|
-
import { Readable } from "stream";
|
|
3
|
-
import axios from "axios";
|
|
4
|
-
import { parseBuffer } from "music-metadata";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Configuration options for the AttachmentsPlugin.
|
|
8
|
-
*/
|
|
9
|
-
export interface AttachmentsPluginOptions {
|
|
10
|
-
/** Maximum file size in bytes (default: 25MB) */
|
|
11
|
-
maxFileSize?: number;
|
|
12
|
-
/** Allowed audio file extensions */
|
|
13
|
-
allowedExtensions?: string[];
|
|
14
|
-
/** Whether to enable debug logging */
|
|
15
|
-
debug?: boolean;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* A plugin for handling Discord attachment URLs and local audio files.
|
|
20
|
-
*
|
|
21
|
-
* This plugin provides support for:
|
|
22
|
-
* - Discord attachment URLs (cdn.discordapp.com, media.discordapp.net)
|
|
23
|
-
* - Direct audio file URLs
|
|
24
|
-
* - Local file paths (if accessible)
|
|
25
|
-
* - Various audio formats (mp3, wav, ogg, m4a, flac, etc.)
|
|
26
|
-
* - File size validation
|
|
27
|
-
* - Audio metadata analysis (duration, title, artist, album, etc.)
|
|
28
|
-
* - Stream extraction from URLs
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* const attachmentsPlugin = new AttachmentsPlugin({
|
|
32
|
-
* maxFileSize: 25 * 1024 * 1024, // 25MB
|
|
33
|
-
* allowedExtensions: ['mp3', 'wav', 'ogg', 'm4a', 'flac']
|
|
34
|
-
* });
|
|
35
|
-
*
|
|
36
|
-
* // Add to PlayerManager
|
|
37
|
-
* const manager = new PlayerManager({
|
|
38
|
-
* plugins: [attachmentsPlugin]
|
|
39
|
-
* });
|
|
40
|
-
*
|
|
41
|
-
* // Search for attachment content
|
|
42
|
-
* const result = await attachmentsPlugin.search(
|
|
43
|
-
* "https://cdn.discordapp.com/attachments/123/456/audio.mp3",
|
|
44
|
-
* "user123"
|
|
45
|
-
* );
|
|
46
|
-
* const stream = await attachmentsPlugin.getStream(result.tracks[0]);
|
|
47
|
-
*
|
|
48
|
-
* @since 1.0.0
|
|
49
|
-
*/
|
|
50
|
-
export class AttachmentsPlugin extends BasePlugin {
|
|
51
|
-
name = "attachments";
|
|
52
|
-
version = "1.0.0";
|
|
53
|
-
|
|
54
|
-
private opts: AttachmentsPluginOptions;
|
|
55
|
-
private readonly defaultAllowedExtensions = ["mp3", "wav", "ogg", "m4a", "flac", "aac", "wma", "opus", "webm"];
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Creates a new AttachmentsPlugin instance.
|
|
59
|
-
*
|
|
60
|
-
* @param opts - Configuration options for the attachments plugin
|
|
61
|
-
* @param opts.maxFileSize - Maximum file size in bytes (default: 25MB)
|
|
62
|
-
* @param opts.allowedExtensions - Allowed audio file extensions (default: common audio formats)
|
|
63
|
-
* @param opts.debug - Whether to enable debug logging (default: false)
|
|
64
|
-
*
|
|
65
|
-
* @example
|
|
66
|
-
* // Basic attachments plugin
|
|
67
|
-
* const attachmentsPlugin = new AttachmentsPlugin();
|
|
68
|
-
*
|
|
69
|
-
* // Custom configuration
|
|
70
|
-
* const customPlugin = new AttachmentsPlugin({
|
|
71
|
-
* maxFileSize: 50 * 1024 * 1024, // 50MB
|
|
72
|
-
* allowedExtensions: ['mp3', 'wav', 'ogg'],
|
|
73
|
-
* debug: true
|
|
74
|
-
* });
|
|
75
|
-
*/
|
|
76
|
-
constructor(opts?: AttachmentsPluginOptions) {
|
|
77
|
-
super();
|
|
78
|
-
this.opts = {
|
|
79
|
-
maxFileSize: opts?.maxFileSize || 25 * 1024 * 1024, // 25MB default
|
|
80
|
-
allowedExtensions: opts?.allowedExtensions || this.defaultAllowedExtensions,
|
|
81
|
-
debug: opts?.debug || false,
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Determines if this plugin can handle the given query.
|
|
87
|
-
*
|
|
88
|
-
* @param query - The URL or file path to check
|
|
89
|
-
* @returns `true` if the query is a Discord attachment URL or audio file URL, `false` otherwise
|
|
90
|
-
*
|
|
91
|
-
* @example
|
|
92
|
-
* plugin.canHandle("https://cdn.discordapp.com/attachments/123/456/audio.mp3"); // true
|
|
93
|
-
* plugin.canHandle("https://example.com/song.wav"); // true
|
|
94
|
-
* plugin.canHandle("youtube.com/watch?v=123"); // false
|
|
95
|
-
*/
|
|
96
|
-
canHandle(query: string): boolean {
|
|
97
|
-
if (!query) return false;
|
|
98
|
-
|
|
99
|
-
const q = query.trim();
|
|
100
|
-
|
|
101
|
-
// Check if it's a URL
|
|
102
|
-
if (q.startsWith("http://") || q.startsWith("https://")) {
|
|
103
|
-
try {
|
|
104
|
-
const url = new URL(q);
|
|
105
|
-
|
|
106
|
-
// Discord attachment URLs
|
|
107
|
-
if (url.hostname === "cdn.discordapp.com" || url.hostname === "media.discordapp.net") {
|
|
108
|
-
return this.isAudioFile(q);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Any other URL - check if it's an audio file
|
|
112
|
-
return this.isAudioFile(q);
|
|
113
|
-
} catch {
|
|
114
|
-
return false;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Check if it's a local file path (basic check)
|
|
119
|
-
if (q.includes("/") || q.includes("\\")) {
|
|
120
|
-
return this.isAudioFile(q);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Validates if a URL is a valid Discord attachment URL or audio file URL.
|
|
128
|
-
*
|
|
129
|
-
* @param url - The URL to validate
|
|
130
|
-
* @returns `true` if the URL is valid and points to an audio file, `false` otherwise
|
|
131
|
-
*
|
|
132
|
-
* @example
|
|
133
|
-
* plugin.validate("https://cdn.discordapp.com/attachments/123/456/audio.mp3"); // true
|
|
134
|
-
* plugin.validate("https://example.com/song.wav"); // true
|
|
135
|
-
* plugin.validate("https://example.com/image.jpg"); // false
|
|
136
|
-
*/
|
|
137
|
-
validate(url: string): boolean {
|
|
138
|
-
return this.canHandle(url) && this.isAudioFile(url);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Creates a track from an attachment URL or file path.
|
|
143
|
-
*
|
|
144
|
-
* This method handles both Discord attachment URLs and direct audio file URLs.
|
|
145
|
-
* It extracts metadata from the URL and creates a track that can be played.
|
|
146
|
-
*
|
|
147
|
-
* @param query - The attachment URL or file path
|
|
148
|
-
* @param requestedBy - The user ID who requested the track
|
|
149
|
-
* @returns A SearchResult containing a single track
|
|
150
|
-
*
|
|
151
|
-
* @example
|
|
152
|
-
* // Discord attachment
|
|
153
|
-
* const result = await plugin.search(
|
|
154
|
-
* "https://cdn.discordapp.com/attachments/123/456/audio.mp3",
|
|
155
|
-
* "user123"
|
|
156
|
-
* );
|
|
157
|
-
*
|
|
158
|
-
* // Direct audio file URL
|
|
159
|
-
* const result2 = await plugin.search(
|
|
160
|
-
* "https://example.com/song.wav",
|
|
161
|
-
* "user123"
|
|
162
|
-
* );
|
|
163
|
-
*/
|
|
164
|
-
async search(query: string, requestedBy: string): Promise<SearchResult> {
|
|
165
|
-
if (!this.canHandle(query)) {
|
|
166
|
-
return { tracks: [] };
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
try {
|
|
170
|
-
const filename = this.extractFilename(query);
|
|
171
|
-
const fileExtension = this.getFileExtension(filename);
|
|
172
|
-
let title = filename || `Audio File (${fileExtension})`;
|
|
173
|
-
|
|
174
|
-
// Get file size if it's a URL
|
|
175
|
-
let fileSize = 0;
|
|
176
|
-
let duration = 0;
|
|
177
|
-
let audioMetadata: any = {};
|
|
178
|
-
|
|
179
|
-
if (query.startsWith("http://") || query.startsWith("https://")) {
|
|
180
|
-
try {
|
|
181
|
-
const headResponse = await axios.head(query, { timeout: 5000 });
|
|
182
|
-
const contentLength = headResponse.headers["content-length"];
|
|
183
|
-
if (contentLength) {
|
|
184
|
-
fileSize = parseInt(contentLength, 10);
|
|
185
|
-
|
|
186
|
-
// Check file size limit
|
|
187
|
-
if (fileSize > this.opts.maxFileSize!) {
|
|
188
|
-
throw new Error(`File too large: ${this.formatBytes(fileSize)} (max: ${this.formatBytes(this.opts.maxFileSize!)})`);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
} catch (error) {
|
|
192
|
-
this.debug("Could not get file size:", error);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Analyze audio metadata to get duration and other info
|
|
196
|
-
try {
|
|
197
|
-
const analysisResult = await this.analyzeAudioMetadata(query);
|
|
198
|
-
duration = analysisResult.duration;
|
|
199
|
-
audioMetadata = analysisResult.metadata || {};
|
|
200
|
-
|
|
201
|
-
// Use metadata title if available
|
|
202
|
-
if (audioMetadata.title && audioMetadata.title.trim()) {
|
|
203
|
-
const artist = audioMetadata.artist ? ` - ${audioMetadata.artist}` : "";
|
|
204
|
-
const album = audioMetadata.album ? ` (${audioMetadata.album})` : "";
|
|
205
|
-
const finalTitle = `${audioMetadata.title}${artist}${album}`;
|
|
206
|
-
if (finalTitle.trim()) {
|
|
207
|
-
title = finalTitle;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
} catch (error) {
|
|
211
|
-
this.debug("Could not analyze audio metadata:", error);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const track: Track = {
|
|
216
|
-
id: this.generateTrackId(query),
|
|
217
|
-
title,
|
|
218
|
-
url: query,
|
|
219
|
-
duration,
|
|
220
|
-
requestedBy,
|
|
221
|
-
source: this.name,
|
|
222
|
-
metadata: {
|
|
223
|
-
filename,
|
|
224
|
-
extension: fileExtension,
|
|
225
|
-
fileSize,
|
|
226
|
-
isDiscordAttachment: this.isDiscordAttachment(query),
|
|
227
|
-
...audioMetadata, // Include all audio metadata
|
|
228
|
-
},
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
return { tracks: [track] };
|
|
232
|
-
} catch (error) {
|
|
233
|
-
this.debug("Error creating track:", error);
|
|
234
|
-
return { tracks: [] };
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Retrieves the audio stream from an attachment URL or file path.
|
|
240
|
-
*
|
|
241
|
-
* This method downloads the audio file from the URL and returns it as a stream.
|
|
242
|
-
* It handles various audio formats and provides proper error handling.
|
|
243
|
-
*
|
|
244
|
-
* @param track - The Track object to get the stream for
|
|
245
|
-
* @returns A StreamInfo object containing the audio stream
|
|
246
|
-
* @throws {Error} If the URL is invalid, file is too large, or download fails
|
|
247
|
-
*
|
|
248
|
-
* @example
|
|
249
|
-
* const track = {
|
|
250
|
-
* id: "attachment-123",
|
|
251
|
-
* title: "audio.mp3",
|
|
252
|
-
* url: "https://cdn.discordapp.com/attachments/123/456/audio.mp3",
|
|
253
|
-
* ...
|
|
254
|
-
* };
|
|
255
|
-
* const streamInfo = await plugin.getStream(track);
|
|
256
|
-
* console.log(streamInfo.type); // "arbitrary"
|
|
257
|
-
* console.log(streamInfo.stream); // Readable stream
|
|
258
|
-
*/
|
|
259
|
-
async getStream(track: Track): Promise<StreamInfo> {
|
|
260
|
-
if (track.source !== this.name) {
|
|
261
|
-
throw new Error("Track is not from AttachmentsPlugin");
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const url = track.url;
|
|
265
|
-
if (!url) {
|
|
266
|
-
throw new Error("No URL provided for track");
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
try {
|
|
270
|
-
this.debug("Downloading audio from:", url);
|
|
271
|
-
|
|
272
|
-
// Download the file
|
|
273
|
-
const response = await axios.get(url, {
|
|
274
|
-
responseType: "stream",
|
|
275
|
-
timeout: 30000, // 30 second timeout
|
|
276
|
-
maxContentLength: this.opts.maxFileSize,
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
const stream = response.data as Readable;
|
|
280
|
-
const contentType = response.headers["content-type"] || "";
|
|
281
|
-
const contentLength = response.headers["content-length"];
|
|
282
|
-
|
|
283
|
-
this.debug("Download successful:", {
|
|
284
|
-
contentType,
|
|
285
|
-
contentLength: contentLength ? parseInt(contentLength, 10) : "unknown",
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
// Determine stream type based on content type or file extension
|
|
289
|
-
const streamType = this.getStreamType(contentType, track.metadata?.extension);
|
|
290
|
-
|
|
291
|
-
return {
|
|
292
|
-
stream,
|
|
293
|
-
type: streamType,
|
|
294
|
-
metadata: {
|
|
295
|
-
...track.metadata,
|
|
296
|
-
contentType,
|
|
297
|
-
contentLength: contentLength ? parseInt(contentLength, 10) : undefined,
|
|
298
|
-
},
|
|
299
|
-
};
|
|
300
|
-
} catch (error: any) {
|
|
301
|
-
if (error.code === "ECONNABORTED") {
|
|
302
|
-
throw new Error(`Download timeout for ${url}`);
|
|
303
|
-
}
|
|
304
|
-
if (error.response?.status === 404) {
|
|
305
|
-
throw new Error(`File not found: ${url}`);
|
|
306
|
-
}
|
|
307
|
-
if (error.response?.status === 403) {
|
|
308
|
-
throw new Error(`Access denied: ${url}`);
|
|
309
|
-
}
|
|
310
|
-
if (error.message?.includes("maxContentLength")) {
|
|
311
|
-
throw new Error(`File too large: ${url}`);
|
|
312
|
-
}
|
|
313
|
-
throw new Error(`Failed to download audio: ${error.message || error}`);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Provides a fallback by attempting to re-download the file.
|
|
319
|
-
*
|
|
320
|
-
* @param track - The Track object to get a fallback stream for
|
|
321
|
-
* @returns A StreamInfo object containing the fallback audio stream
|
|
322
|
-
* @throws {Error} If fallback download fails
|
|
323
|
-
*/
|
|
324
|
-
async getFallback(track: Track): Promise<StreamInfo> {
|
|
325
|
-
this.debug("Attempting fallback for track:", track.title);
|
|
326
|
-
return await this.getStream(track);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* Checks if a file path or URL is an audio file based on extension.
|
|
331
|
-
*/
|
|
332
|
-
private isAudioFile(path: string): boolean {
|
|
333
|
-
const extension = this.getFileExtension(path);
|
|
334
|
-
return this.opts.allowedExtensions!.includes(extension.toLowerCase());
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Extracts the file extension from a path or URL.
|
|
339
|
-
*/
|
|
340
|
-
private getFileExtension(path: string): string {
|
|
341
|
-
const lastDot = path.lastIndexOf(".");
|
|
342
|
-
if (lastDot === -1) return "";
|
|
343
|
-
|
|
344
|
-
const extension = path.slice(lastDot + 1);
|
|
345
|
-
// Remove query parameters if present
|
|
346
|
-
const questionMark = extension.indexOf("?");
|
|
347
|
-
return questionMark === -1 ? extension : extension.slice(0, questionMark);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
/**
|
|
351
|
-
* Extracts filename from a URL or path.
|
|
352
|
-
*/
|
|
353
|
-
private extractFilename(path: string): string {
|
|
354
|
-
try {
|
|
355
|
-
if (path.startsWith("http://") || path.startsWith("https://")) {
|
|
356
|
-
const url = new URL(path);
|
|
357
|
-
const pathname = url.pathname;
|
|
358
|
-
const lastSlash = pathname.lastIndexOf("/");
|
|
359
|
-
return lastSlash === -1 ? pathname : pathname.slice(lastSlash + 1);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// Local file path
|
|
363
|
-
const lastSlash = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
|
|
364
|
-
return lastSlash === -1 ? path : path.slice(lastSlash + 1);
|
|
365
|
-
} catch {
|
|
366
|
-
return "Unknown File";
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
/**
|
|
371
|
-
* Checks if a URL is a Discord attachment URL.
|
|
372
|
-
*/
|
|
373
|
-
private isDiscordAttachment(url: string): boolean {
|
|
374
|
-
try {
|
|
375
|
-
const urlObj = new URL(url);
|
|
376
|
-
return urlObj.hostname === "cdn.discordapp.com" || urlObj.hostname === "media.discordapp.net";
|
|
377
|
-
} catch {
|
|
378
|
-
return false;
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Generates a unique track ID for a given URL.
|
|
384
|
-
*/
|
|
385
|
-
private generateTrackId(url: string): string {
|
|
386
|
-
// Create a hash-like ID from the URL
|
|
387
|
-
const hash = this.simpleHash(url);
|
|
388
|
-
return `attachment-${hash}-${Date.now()}`;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
/**
|
|
392
|
-
* Simple hash function for generating IDs.
|
|
393
|
-
*/
|
|
394
|
-
private simpleHash(str: string): string {
|
|
395
|
-
let hash = 0;
|
|
396
|
-
for (let i = 0; i < str.length; i++) {
|
|
397
|
-
const char = str.charCodeAt(i);
|
|
398
|
-
hash = (hash << 5) - hash + char;
|
|
399
|
-
hash = hash & hash; // Convert to 32-bit integer
|
|
400
|
-
}
|
|
401
|
-
return Math.abs(hash).toString(36);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
* Determines the appropriate stream type based on content type and file extension.
|
|
406
|
-
*/
|
|
407
|
-
private getStreamType(contentType: string, extension?: string): StreamInfo["type"] {
|
|
408
|
-
const type = contentType.toLowerCase();
|
|
409
|
-
const ext = extension?.toLowerCase() || "";
|
|
410
|
-
|
|
411
|
-
// Check content type first
|
|
412
|
-
if (type.includes("webm") && type.includes("opus")) return "webm/opus";
|
|
413
|
-
if (type.includes("ogg") && type.includes("opus")) return "ogg/opus";
|
|
414
|
-
|
|
415
|
-
// Fallback to extension
|
|
416
|
-
if (ext === "webm") return "webm/opus";
|
|
417
|
-
if (ext === "ogg") return "ogg/opus";
|
|
418
|
-
|
|
419
|
-
// Default to arbitrary for all other types (mp3, wav, flac, mp4, etc.)
|
|
420
|
-
return "arbitrary";
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
/**
|
|
424
|
-
* Formats bytes into a human-readable string.
|
|
425
|
-
*/
|
|
426
|
-
private formatBytes(bytes: number): string {
|
|
427
|
-
if (bytes === 0) return "0 Bytes";
|
|
428
|
-
const k = 1024;
|
|
429
|
-
const sizes = ["Bytes", "KB", "MB", "GB"];
|
|
430
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
431
|
-
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
/**
|
|
435
|
-
* Analyzes audio metadata to extract duration and other information.
|
|
436
|
-
*
|
|
437
|
-
* @param url - The URL to analyze
|
|
438
|
-
* @returns Promise containing duration in seconds and metadata
|
|
439
|
-
*/
|
|
440
|
-
private async analyzeAudioMetadata(url: string): Promise<{ duration: number; metadata?: any }> {
|
|
441
|
-
try {
|
|
442
|
-
this.debug("Analyzing audio metadata for:", url);
|
|
443
|
-
|
|
444
|
-
// Download a small portion of the file to analyze metadata
|
|
445
|
-
const response = await axios.get(url, {
|
|
446
|
-
responseType: "arraybuffer",
|
|
447
|
-
timeout: 10000, // 10 second timeout for metadata analysis
|
|
448
|
-
maxContentLength: 1024 * 1024, // Only download first 1MB for metadata
|
|
449
|
-
headers: {
|
|
450
|
-
Range: "bytes=0-1048575", // Request first 1MB
|
|
451
|
-
},
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
const buffer = Buffer.from(response.data);
|
|
455
|
-
const metadata = await parseBuffer(buffer);
|
|
456
|
-
|
|
457
|
-
const duration = metadata.format.duration || 0;
|
|
458
|
-
this.debug("Audio metadata analysis result:", {
|
|
459
|
-
duration: `${duration}s`,
|
|
460
|
-
format: metadata.format.container,
|
|
461
|
-
codec: metadata.format.codec,
|
|
462
|
-
bitrate: metadata.format.bitrate,
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
return {
|
|
466
|
-
duration: Math.round(duration),
|
|
467
|
-
metadata: {
|
|
468
|
-
format: metadata.format.container,
|
|
469
|
-
codec: metadata.format.codec,
|
|
470
|
-
bitrate: metadata.format.bitrate,
|
|
471
|
-
sampleRate: metadata.format.sampleRate,
|
|
472
|
-
channels: metadata.format.numberOfChannels,
|
|
473
|
-
title: metadata.common.title,
|
|
474
|
-
artist: metadata.common.artist,
|
|
475
|
-
album: metadata.common.album,
|
|
476
|
-
year: metadata.common.year,
|
|
477
|
-
genre: metadata.common.genre,
|
|
478
|
-
},
|
|
479
|
-
};
|
|
480
|
-
} catch (error: any) {
|
|
481
|
-
this.debug("Failed to analyze audio metadata:", error.message);
|
|
482
|
-
|
|
483
|
-
// Fallback: try without Range header for some servers that don't support it
|
|
484
|
-
try {
|
|
485
|
-
const response = await axios.get(url, {
|
|
486
|
-
responseType: "arraybuffer",
|
|
487
|
-
timeout: 10000,
|
|
488
|
-
maxContentLength: 1024 * 1024, // 1MB limit
|
|
489
|
-
});
|
|
490
|
-
|
|
491
|
-
const buffer = Buffer.from(response.data);
|
|
492
|
-
const metadata = await parseBuffer(buffer);
|
|
493
|
-
|
|
494
|
-
const duration = metadata.format.duration || 0;
|
|
495
|
-
this.debug("Audio metadata analysis (fallback) result:", {
|
|
496
|
-
duration: `${duration}s`,
|
|
497
|
-
format: metadata.format.container,
|
|
498
|
-
});
|
|
499
|
-
|
|
500
|
-
return {
|
|
501
|
-
duration: Math.round(duration),
|
|
502
|
-
metadata: {
|
|
503
|
-
format: metadata.format.container,
|
|
504
|
-
codec: metadata.format.codec,
|
|
505
|
-
bitrate: metadata.format.bitrate,
|
|
506
|
-
},
|
|
507
|
-
};
|
|
508
|
-
} catch (fallbackError: any) {
|
|
509
|
-
this.debug("Fallback metadata analysis also failed:", fallbackError.message);
|
|
510
|
-
return { duration: 0 };
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
/**
|
|
516
|
-
* Debug logging helper.
|
|
517
|
-
*/
|
|
518
|
-
private debug(message: string, ...args: any[]): void {
|
|
519
|
-
if (this.opts.debug) {
|
|
520
|
-
console.log(`[AttachmentsPlugin] ${message}`, ...args);
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
}
|
|
1
|
+
import { BasePlugin, Track, SearchResult, StreamInfo } from "ziplayer";
|
|
2
|
+
import { Readable } from "stream";
|
|
3
|
+
import axios from "axios";
|
|
4
|
+
import { parseBuffer } from "music-metadata";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Configuration options for the AttachmentsPlugin.
|
|
8
|
+
*/
|
|
9
|
+
export interface AttachmentsPluginOptions {
|
|
10
|
+
/** Maximum file size in bytes (default: 25MB) */
|
|
11
|
+
maxFileSize?: number;
|
|
12
|
+
/** Allowed audio file extensions */
|
|
13
|
+
allowedExtensions?: string[];
|
|
14
|
+
/** Whether to enable debug logging */
|
|
15
|
+
debug?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A plugin for handling Discord attachment URLs and local audio files.
|
|
20
|
+
*
|
|
21
|
+
* This plugin provides support for:
|
|
22
|
+
* - Discord attachment URLs (cdn.discordapp.com, media.discordapp.net)
|
|
23
|
+
* - Direct audio file URLs
|
|
24
|
+
* - Local file paths (if accessible)
|
|
25
|
+
* - Various audio formats (mp3, wav, ogg, m4a, flac, etc.)
|
|
26
|
+
* - File size validation
|
|
27
|
+
* - Audio metadata analysis (duration, title, artist, album, etc.)
|
|
28
|
+
* - Stream extraction from URLs
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* const attachmentsPlugin = new AttachmentsPlugin({
|
|
32
|
+
* maxFileSize: 25 * 1024 * 1024, // 25MB
|
|
33
|
+
* allowedExtensions: ['mp3', 'wav', 'ogg', 'm4a', 'flac']
|
|
34
|
+
* });
|
|
35
|
+
*
|
|
36
|
+
* // Add to PlayerManager
|
|
37
|
+
* const manager = new PlayerManager({
|
|
38
|
+
* plugins: [attachmentsPlugin]
|
|
39
|
+
* });
|
|
40
|
+
*
|
|
41
|
+
* // Search for attachment content
|
|
42
|
+
* const result = await attachmentsPlugin.search(
|
|
43
|
+
* "https://cdn.discordapp.com/attachments/123/456/audio.mp3",
|
|
44
|
+
* "user123"
|
|
45
|
+
* );
|
|
46
|
+
* const stream = await attachmentsPlugin.getStream(result.tracks[0]);
|
|
47
|
+
*
|
|
48
|
+
* @since 1.0.0
|
|
49
|
+
*/
|
|
50
|
+
export class AttachmentsPlugin extends BasePlugin {
|
|
51
|
+
name = "attachments";
|
|
52
|
+
version = "1.0.0";
|
|
53
|
+
|
|
54
|
+
private opts: AttachmentsPluginOptions;
|
|
55
|
+
private readonly defaultAllowedExtensions = ["mp3", "wav", "ogg", "m4a", "flac", "aac", "wma", "opus", "webm"];
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Creates a new AttachmentsPlugin instance.
|
|
59
|
+
*
|
|
60
|
+
* @param opts - Configuration options for the attachments plugin
|
|
61
|
+
* @param opts.maxFileSize - Maximum file size in bytes (default: 25MB)
|
|
62
|
+
* @param opts.allowedExtensions - Allowed audio file extensions (default: common audio formats)
|
|
63
|
+
* @param opts.debug - Whether to enable debug logging (default: false)
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* // Basic attachments plugin
|
|
67
|
+
* const attachmentsPlugin = new AttachmentsPlugin();
|
|
68
|
+
*
|
|
69
|
+
* // Custom configuration
|
|
70
|
+
* const customPlugin = new AttachmentsPlugin({
|
|
71
|
+
* maxFileSize: 50 * 1024 * 1024, // 50MB
|
|
72
|
+
* allowedExtensions: ['mp3', 'wav', 'ogg'],
|
|
73
|
+
* debug: true
|
|
74
|
+
* });
|
|
75
|
+
*/
|
|
76
|
+
constructor(opts?: AttachmentsPluginOptions) {
|
|
77
|
+
super();
|
|
78
|
+
this.opts = {
|
|
79
|
+
maxFileSize: opts?.maxFileSize || 25 * 1024 * 1024, // 25MB default
|
|
80
|
+
allowedExtensions: opts?.allowedExtensions || this.defaultAllowedExtensions,
|
|
81
|
+
debug: opts?.debug || false,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Determines if this plugin can handle the given query.
|
|
87
|
+
*
|
|
88
|
+
* @param query - The URL or file path to check
|
|
89
|
+
* @returns `true` if the query is a Discord attachment URL or audio file URL, `false` otherwise
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* plugin.canHandle("https://cdn.discordapp.com/attachments/123/456/audio.mp3"); // true
|
|
93
|
+
* plugin.canHandle("https://example.com/song.wav"); // true
|
|
94
|
+
* plugin.canHandle("youtube.com/watch?v=123"); // false
|
|
95
|
+
*/
|
|
96
|
+
canHandle(query: string): boolean {
|
|
97
|
+
if (!query) return false;
|
|
98
|
+
|
|
99
|
+
const q = query.trim();
|
|
100
|
+
|
|
101
|
+
// Check if it's a URL
|
|
102
|
+
if (q.startsWith("http://") || q.startsWith("https://")) {
|
|
103
|
+
try {
|
|
104
|
+
const url = new URL(q);
|
|
105
|
+
|
|
106
|
+
// Discord attachment URLs
|
|
107
|
+
if (url.hostname === "cdn.discordapp.com" || url.hostname === "media.discordapp.net") {
|
|
108
|
+
return this.isAudioFile(q);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Any other URL - check if it's an audio file
|
|
112
|
+
return this.isAudioFile(q);
|
|
113
|
+
} catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Check if it's a local file path (basic check)
|
|
119
|
+
if (q.includes("/") || q.includes("\\")) {
|
|
120
|
+
return this.isAudioFile(q);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Validates if a URL is a valid Discord attachment URL or audio file URL.
|
|
128
|
+
*
|
|
129
|
+
* @param url - The URL to validate
|
|
130
|
+
* @returns `true` if the URL is valid and points to an audio file, `false` otherwise
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* plugin.validate("https://cdn.discordapp.com/attachments/123/456/audio.mp3"); // true
|
|
134
|
+
* plugin.validate("https://example.com/song.wav"); // true
|
|
135
|
+
* plugin.validate("https://example.com/image.jpg"); // false
|
|
136
|
+
*/
|
|
137
|
+
validate(url: string): boolean {
|
|
138
|
+
return this.canHandle(url) && this.isAudioFile(url);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Creates a track from an attachment URL or file path.
|
|
143
|
+
*
|
|
144
|
+
* This method handles both Discord attachment URLs and direct audio file URLs.
|
|
145
|
+
* It extracts metadata from the URL and creates a track that can be played.
|
|
146
|
+
*
|
|
147
|
+
* @param query - The attachment URL or file path
|
|
148
|
+
* @param requestedBy - The user ID who requested the track
|
|
149
|
+
* @returns A SearchResult containing a single track
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* // Discord attachment
|
|
153
|
+
* const result = await plugin.search(
|
|
154
|
+
* "https://cdn.discordapp.com/attachments/123/456/audio.mp3",
|
|
155
|
+
* "user123"
|
|
156
|
+
* );
|
|
157
|
+
*
|
|
158
|
+
* // Direct audio file URL
|
|
159
|
+
* const result2 = await plugin.search(
|
|
160
|
+
* "https://example.com/song.wav",
|
|
161
|
+
* "user123"
|
|
162
|
+
* );
|
|
163
|
+
*/
|
|
164
|
+
async search(query: string, requestedBy: string): Promise<SearchResult> {
|
|
165
|
+
if (!this.canHandle(query)) {
|
|
166
|
+
return { tracks: [] };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
const filename = this.extractFilename(query);
|
|
171
|
+
const fileExtension = this.getFileExtension(filename);
|
|
172
|
+
let title = filename || `Audio File (${fileExtension})`;
|
|
173
|
+
|
|
174
|
+
// Get file size if it's a URL
|
|
175
|
+
let fileSize = 0;
|
|
176
|
+
let duration = 0;
|
|
177
|
+
let audioMetadata: any = {};
|
|
178
|
+
|
|
179
|
+
if (query.startsWith("http://") || query.startsWith("https://")) {
|
|
180
|
+
try {
|
|
181
|
+
const headResponse = await axios.head(query, { timeout: 5000 });
|
|
182
|
+
const contentLength = headResponse.headers["content-length"];
|
|
183
|
+
if (contentLength) {
|
|
184
|
+
fileSize = parseInt(contentLength, 10);
|
|
185
|
+
|
|
186
|
+
// Check file size limit
|
|
187
|
+
if (fileSize > this.opts.maxFileSize!) {
|
|
188
|
+
throw new Error(`File too large: ${this.formatBytes(fileSize)} (max: ${this.formatBytes(this.opts.maxFileSize!)})`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
} catch (error) {
|
|
192
|
+
this.debug("Could not get file size:", error);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Analyze audio metadata to get duration and other info
|
|
196
|
+
try {
|
|
197
|
+
const analysisResult = await this.analyzeAudioMetadata(query);
|
|
198
|
+
duration = analysisResult.duration;
|
|
199
|
+
audioMetadata = analysisResult.metadata || {};
|
|
200
|
+
|
|
201
|
+
// Use metadata title if available
|
|
202
|
+
if (audioMetadata.title && audioMetadata.title.trim()) {
|
|
203
|
+
const artist = audioMetadata.artist ? ` - ${audioMetadata.artist}` : "";
|
|
204
|
+
const album = audioMetadata.album ? ` (${audioMetadata.album})` : "";
|
|
205
|
+
const finalTitle = `${audioMetadata.title}${artist}${album}`;
|
|
206
|
+
if (finalTitle.trim()) {
|
|
207
|
+
title = finalTitle;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
} catch (error) {
|
|
211
|
+
this.debug("Could not analyze audio metadata:", error);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const track: Track = {
|
|
216
|
+
id: this.generateTrackId(query),
|
|
217
|
+
title,
|
|
218
|
+
url: query,
|
|
219
|
+
duration,
|
|
220
|
+
requestedBy,
|
|
221
|
+
source: this.name,
|
|
222
|
+
metadata: {
|
|
223
|
+
filename,
|
|
224
|
+
extension: fileExtension,
|
|
225
|
+
fileSize,
|
|
226
|
+
isDiscordAttachment: this.isDiscordAttachment(query),
|
|
227
|
+
...audioMetadata, // Include all audio metadata
|
|
228
|
+
},
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
return { tracks: [track] };
|
|
232
|
+
} catch (error) {
|
|
233
|
+
this.debug("Error creating track:", error);
|
|
234
|
+
return { tracks: [] };
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Retrieves the audio stream from an attachment URL or file path.
|
|
240
|
+
*
|
|
241
|
+
* This method downloads the audio file from the URL and returns it as a stream.
|
|
242
|
+
* It handles various audio formats and provides proper error handling.
|
|
243
|
+
*
|
|
244
|
+
* @param track - The Track object to get the stream for
|
|
245
|
+
* @returns A StreamInfo object containing the audio stream
|
|
246
|
+
* @throws {Error} If the URL is invalid, file is too large, or download fails
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* const track = {
|
|
250
|
+
* id: "attachment-123",
|
|
251
|
+
* title: "audio.mp3",
|
|
252
|
+
* url: "https://cdn.discordapp.com/attachments/123/456/audio.mp3",
|
|
253
|
+
* ...
|
|
254
|
+
* };
|
|
255
|
+
* const streamInfo = await plugin.getStream(track);
|
|
256
|
+
* console.log(streamInfo.type); // "arbitrary"
|
|
257
|
+
* console.log(streamInfo.stream); // Readable stream
|
|
258
|
+
*/
|
|
259
|
+
async getStream(track: Track): Promise<StreamInfo> {
|
|
260
|
+
if (track.source !== this.name) {
|
|
261
|
+
throw new Error("Track is not from AttachmentsPlugin");
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const url = track.url;
|
|
265
|
+
if (!url) {
|
|
266
|
+
throw new Error("No URL provided for track");
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
try {
|
|
270
|
+
this.debug("Downloading audio from:", url);
|
|
271
|
+
|
|
272
|
+
// Download the file
|
|
273
|
+
const response = await axios.get(url, {
|
|
274
|
+
responseType: "stream",
|
|
275
|
+
timeout: 30000, // 30 second timeout
|
|
276
|
+
maxContentLength: this.opts.maxFileSize,
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
const stream = response.data as Readable;
|
|
280
|
+
const contentType = response.headers["content-type"] || "";
|
|
281
|
+
const contentLength = response.headers["content-length"];
|
|
282
|
+
|
|
283
|
+
this.debug("Download successful:", {
|
|
284
|
+
contentType,
|
|
285
|
+
contentLength: contentLength ? parseInt(contentLength, 10) : "unknown",
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// Determine stream type based on content type or file extension
|
|
289
|
+
const streamType = this.getStreamType(contentType, track.metadata?.extension);
|
|
290
|
+
|
|
291
|
+
return {
|
|
292
|
+
stream,
|
|
293
|
+
type: streamType,
|
|
294
|
+
metadata: {
|
|
295
|
+
...track.metadata,
|
|
296
|
+
contentType,
|
|
297
|
+
contentLength: contentLength ? parseInt(contentLength, 10) : undefined,
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
} catch (error: any) {
|
|
301
|
+
if (error.code === "ECONNABORTED") {
|
|
302
|
+
throw new Error(`Download timeout for ${url}`);
|
|
303
|
+
}
|
|
304
|
+
if (error.response?.status === 404) {
|
|
305
|
+
throw new Error(`File not found: ${url}`);
|
|
306
|
+
}
|
|
307
|
+
if (error.response?.status === 403) {
|
|
308
|
+
throw new Error(`Access denied: ${url}`);
|
|
309
|
+
}
|
|
310
|
+
if (error.message?.includes("maxContentLength")) {
|
|
311
|
+
throw new Error(`File too large: ${url}`);
|
|
312
|
+
}
|
|
313
|
+
throw new Error(`Failed to download audio: ${error.message || error}`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Provides a fallback by attempting to re-download the file.
|
|
319
|
+
*
|
|
320
|
+
* @param track - The Track object to get a fallback stream for
|
|
321
|
+
* @returns A StreamInfo object containing the fallback audio stream
|
|
322
|
+
* @throws {Error} If fallback download fails
|
|
323
|
+
*/
|
|
324
|
+
async getFallback(track: Track): Promise<StreamInfo> {
|
|
325
|
+
this.debug("Attempting fallback for track:", track.title);
|
|
326
|
+
return await this.getStream(track);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Checks if a file path or URL is an audio file based on extension.
|
|
331
|
+
*/
|
|
332
|
+
private isAudioFile(path: string): boolean {
|
|
333
|
+
const extension = this.getFileExtension(path);
|
|
334
|
+
return this.opts.allowedExtensions!.includes(extension.toLowerCase());
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Extracts the file extension from a path or URL.
|
|
339
|
+
*/
|
|
340
|
+
private getFileExtension(path: string): string {
|
|
341
|
+
const lastDot = path.lastIndexOf(".");
|
|
342
|
+
if (lastDot === -1) return "";
|
|
343
|
+
|
|
344
|
+
const extension = path.slice(lastDot + 1);
|
|
345
|
+
// Remove query parameters if present
|
|
346
|
+
const questionMark = extension.indexOf("?");
|
|
347
|
+
return questionMark === -1 ? extension : extension.slice(0, questionMark);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Extracts filename from a URL or path.
|
|
352
|
+
*/
|
|
353
|
+
private extractFilename(path: string): string {
|
|
354
|
+
try {
|
|
355
|
+
if (path.startsWith("http://") || path.startsWith("https://")) {
|
|
356
|
+
const url = new URL(path);
|
|
357
|
+
const pathname = url.pathname;
|
|
358
|
+
const lastSlash = pathname.lastIndexOf("/");
|
|
359
|
+
return lastSlash === -1 ? pathname : pathname.slice(lastSlash + 1);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Local file path
|
|
363
|
+
const lastSlash = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
|
|
364
|
+
return lastSlash === -1 ? path : path.slice(lastSlash + 1);
|
|
365
|
+
} catch {
|
|
366
|
+
return "Unknown File";
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Checks if a URL is a Discord attachment URL.
|
|
372
|
+
*/
|
|
373
|
+
private isDiscordAttachment(url: string): boolean {
|
|
374
|
+
try {
|
|
375
|
+
const urlObj = new URL(url);
|
|
376
|
+
return urlObj.hostname === "cdn.discordapp.com" || urlObj.hostname === "media.discordapp.net";
|
|
377
|
+
} catch {
|
|
378
|
+
return false;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Generates a unique track ID for a given URL.
|
|
384
|
+
*/
|
|
385
|
+
private generateTrackId(url: string): string {
|
|
386
|
+
// Create a hash-like ID from the URL
|
|
387
|
+
const hash = this.simpleHash(url);
|
|
388
|
+
return `attachment-${hash}-${Date.now()}`;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Simple hash function for generating IDs.
|
|
393
|
+
*/
|
|
394
|
+
private simpleHash(str: string): string {
|
|
395
|
+
let hash = 0;
|
|
396
|
+
for (let i = 0; i < str.length; i++) {
|
|
397
|
+
const char = str.charCodeAt(i);
|
|
398
|
+
hash = (hash << 5) - hash + char;
|
|
399
|
+
hash = hash & hash; // Convert to 32-bit integer
|
|
400
|
+
}
|
|
401
|
+
return Math.abs(hash).toString(36);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Determines the appropriate stream type based on content type and file extension.
|
|
406
|
+
*/
|
|
407
|
+
private getStreamType(contentType: string, extension?: string): StreamInfo["type"] {
|
|
408
|
+
const type = contentType.toLowerCase();
|
|
409
|
+
const ext = extension?.toLowerCase() || "";
|
|
410
|
+
|
|
411
|
+
// Check content type first
|
|
412
|
+
if (type.includes("webm") && type.includes("opus")) return "webm/opus";
|
|
413
|
+
if (type.includes("ogg") && type.includes("opus")) return "ogg/opus";
|
|
414
|
+
|
|
415
|
+
// Fallback to extension
|
|
416
|
+
if (ext === "webm") return "webm/opus";
|
|
417
|
+
if (ext === "ogg") return "ogg/opus";
|
|
418
|
+
|
|
419
|
+
// Default to arbitrary for all other types (mp3, wav, flac, mp4, etc.)
|
|
420
|
+
return "arbitrary";
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Formats bytes into a human-readable string.
|
|
425
|
+
*/
|
|
426
|
+
private formatBytes(bytes: number): string {
|
|
427
|
+
if (bytes === 0) return "0 Bytes";
|
|
428
|
+
const k = 1024;
|
|
429
|
+
const sizes = ["Bytes", "KB", "MB", "GB"];
|
|
430
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
431
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Analyzes audio metadata to extract duration and other information.
|
|
436
|
+
*
|
|
437
|
+
* @param url - The URL to analyze
|
|
438
|
+
* @returns Promise containing duration in seconds and metadata
|
|
439
|
+
*/
|
|
440
|
+
private async analyzeAudioMetadata(url: string): Promise<{ duration: number; metadata?: any }> {
|
|
441
|
+
try {
|
|
442
|
+
this.debug("Analyzing audio metadata for:", url);
|
|
443
|
+
|
|
444
|
+
// Download a small portion of the file to analyze metadata
|
|
445
|
+
const response = await axios.get(url, {
|
|
446
|
+
responseType: "arraybuffer",
|
|
447
|
+
timeout: 10000, // 10 second timeout for metadata analysis
|
|
448
|
+
maxContentLength: 1024 * 1024, // Only download first 1MB for metadata
|
|
449
|
+
headers: {
|
|
450
|
+
Range: "bytes=0-1048575", // Request first 1MB
|
|
451
|
+
},
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
const buffer = Buffer.from(response.data);
|
|
455
|
+
const metadata = await parseBuffer(buffer);
|
|
456
|
+
|
|
457
|
+
const duration = metadata.format.duration || 0;
|
|
458
|
+
this.debug("Audio metadata analysis result:", {
|
|
459
|
+
duration: `${duration}s`,
|
|
460
|
+
format: metadata.format.container,
|
|
461
|
+
codec: metadata.format.codec,
|
|
462
|
+
bitrate: metadata.format.bitrate,
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
return {
|
|
466
|
+
duration: Math.round(duration),
|
|
467
|
+
metadata: {
|
|
468
|
+
format: metadata.format.container,
|
|
469
|
+
codec: metadata.format.codec,
|
|
470
|
+
bitrate: metadata.format.bitrate,
|
|
471
|
+
sampleRate: metadata.format.sampleRate,
|
|
472
|
+
channels: metadata.format.numberOfChannels,
|
|
473
|
+
title: metadata.common.title,
|
|
474
|
+
artist: metadata.common.artist,
|
|
475
|
+
album: metadata.common.album,
|
|
476
|
+
year: metadata.common.year,
|
|
477
|
+
genre: metadata.common.genre,
|
|
478
|
+
},
|
|
479
|
+
};
|
|
480
|
+
} catch (error: any) {
|
|
481
|
+
this.debug("Failed to analyze audio metadata:", error.message);
|
|
482
|
+
|
|
483
|
+
// Fallback: try without Range header for some servers that don't support it
|
|
484
|
+
try {
|
|
485
|
+
const response = await axios.get(url, {
|
|
486
|
+
responseType: "arraybuffer",
|
|
487
|
+
timeout: 10000,
|
|
488
|
+
maxContentLength: 1024 * 1024, // 1MB limit
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
const buffer = Buffer.from(response.data);
|
|
492
|
+
const metadata = await parseBuffer(buffer);
|
|
493
|
+
|
|
494
|
+
const duration = metadata.format.duration || 0;
|
|
495
|
+
this.debug("Audio metadata analysis (fallback) result:", {
|
|
496
|
+
duration: `${duration}s`,
|
|
497
|
+
format: metadata.format.container,
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
return {
|
|
501
|
+
duration: Math.round(duration),
|
|
502
|
+
metadata: {
|
|
503
|
+
format: metadata.format.container,
|
|
504
|
+
codec: metadata.format.codec,
|
|
505
|
+
bitrate: metadata.format.bitrate,
|
|
506
|
+
},
|
|
507
|
+
};
|
|
508
|
+
} catch (fallbackError: any) {
|
|
509
|
+
this.debug("Fallback metadata analysis also failed:", fallbackError.message);
|
|
510
|
+
return { duration: 0 };
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Debug logging helper.
|
|
517
|
+
*/
|
|
518
|
+
private debug(message: string, ...args: any[]): void {
|
|
519
|
+
if (this.opts.debug) {
|
|
520
|
+
console.log(`[AttachmentsPlugin] ${message}`, ...args);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
package/src/TTSPlugin.ts
CHANGED
|
@@ -318,7 +318,10 @@ export class TTSPlugin extends BasePlugin {
|
|
|
318
318
|
const urlStr = o.url.toString();
|
|
319
319
|
try {
|
|
320
320
|
type =
|
|
321
|
-
normType(o.type) ||
|
|
321
|
+
normType(o.type) ||
|
|
322
|
+
(urlStr.endsWith(".webm") ? "webm/opus"
|
|
323
|
+
: urlStr.endsWith(".ogg") ? "ogg/opus"
|
|
324
|
+
: undefined);
|
|
322
325
|
const res = await axios.get(urlStr, { responseType: "stream" });
|
|
323
326
|
stream = res.data as unknown as Readable;
|
|
324
327
|
metadata = o.metadata;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
interface MultiBar {
|
|
2
|
-
create(total: number, startValue: number, options: any): any;
|
|
3
|
-
stop(): void;
|
|
4
|
-
}
|
|
5
|
-
export declare function createMultiProgressBar(): MultiBar;
|
|
6
|
-
export declare function setupProgressBar(multiBar: MultiBar, filename: string, total: number): any;
|
|
7
|
-
export {};
|
|
8
|
-
//# sourceMappingURL=progress-bar.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"progress-bar.d.ts","sourceRoot":"","sources":["../../src/utils/progress-bar.ts"],"names":[],"mappings":"AACA,UAAU,QAAQ;IACjB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,GAAG,CAAC;IAC7D,IAAI,IAAI,IAAI,CAAC;CACb;AAOD,wBAAgB,sBAAsB,IAAI,QAAQ,CAcjD;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,OAEnF"}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createMultiProgressBar = createMultiProgressBar;
|
|
4
|
-
exports.setupProgressBar = setupProgressBar;
|
|
5
|
-
// Mock implementation for now - will be replaced with actual cli-progress when available
|
|
6
|
-
function createMultiProgressBar() {
|
|
7
|
-
return {
|
|
8
|
-
create(total, startValue, options) {
|
|
9
|
-
return {
|
|
10
|
-
increment(value) {
|
|
11
|
-
// Mock progress bar implementation
|
|
12
|
-
console.log(`Progress: ${value}/${total}`);
|
|
13
|
-
},
|
|
14
|
-
};
|
|
15
|
-
},
|
|
16
|
-
stop() {
|
|
17
|
-
console.log("Progress completed");
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
function setupProgressBar(multiBar, filename, total) {
|
|
22
|
-
return multiBar.create(total, 0, { filename });
|
|
23
|
-
}
|
|
24
|
-
//# sourceMappingURL=progress-bar.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"progress-bar.js","sourceRoot":"","sources":["../../src/utils/progress-bar.ts"],"names":[],"mappings":";;AAWA,wDAcC;AAED,4CAEC;AAnBD,yFAAyF;AACzF,SAAgB,sBAAsB;IACrC,OAAO;QACN,MAAM,CAAC,KAAa,EAAE,UAAkB,EAAE,OAAY;YACrD,OAAO;gBACN,SAAS,CAAC,KAAa;oBACtB,mCAAmC;oBACnC,OAAO,CAAC,GAAG,CAAC,aAAa,KAAK,IAAI,KAAK,EAAE,CAAC,CAAC;gBAC5C,CAAC;aACD,CAAC;QACH,CAAC;QACD,IAAI;YACH,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;QACnC,CAAC;KACD,CAAC;AACH,CAAC;AAED,SAAgB,gBAAgB,CAAC,QAAkB,EAAE,QAAgB,EAAE,KAAa;IACnF,OAAO,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;AAChD,CAAC"}
|