@ziplayer/plugin 0.1.52 → 0.2.1-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/dist/YouTubePlugin.d.ts +5 -1
- package/dist/YouTubePlugin.d.ts.map +1 -1
- package/dist/YouTubePlugin.js +72 -107
- package/dist/YouTubePlugin.js.map +1 -1
- package/dist/index.d.ts +0 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -16
- package/dist/index.js.map +1 -1
- package/dist/utils/sabr-stream-factory.d.ts +29 -9
- package/dist/utils/sabr-stream-factory.d.ts.map +1 -1
- package/dist/utils/sabr-stream-factory.js +170 -31
- package/dist/utils/sabr-stream-factory.js.map +1 -1
- package/dist/utils/stream-converter.d.ts +8 -5
- package/dist/utils/stream-converter.d.ts.map +1 -1
- package/dist/utils/stream-converter.js +105 -41
- package/dist/utils/stream-converter.js.map +1 -1
- package/package.json +46 -45
- package/src/YouTubePlugin.ts +591 -620
- package/src/index.ts +0 -17
- package/src/utils/sabr-stream-factory.ts +284 -96
- package/src/utils/stream-converter.ts +113 -45
- package/tsconfig.json +6 -2
- package/src/YTSRPlugin.ts +0 -596
- package/src/types/googlevideo.d.ts +0 -45
|
@@ -3,24 +3,169 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.DEFAULT_SABR_OPTIONS = void 0;
|
|
4
4
|
exports.createSabrStream = createSabrStream;
|
|
5
5
|
exports.createOutputStream = createOutputStream;
|
|
6
|
-
exports.
|
|
6
|
+
exports.sanitizeFileName = sanitizeFileName;
|
|
7
|
+
exports.bytesToMB = bytesToMB;
|
|
7
8
|
const fs_1 = require("fs");
|
|
8
9
|
const path_1 = require("path");
|
|
9
10
|
const os_1 = require("os");
|
|
11
|
+
const youtubei_js_1 = require("youtubei.js");
|
|
12
|
+
const sabr_stream_1 = require("googlevideo/sabr-stream");
|
|
13
|
+
const utils_1 = require("googlevideo/utils");
|
|
14
|
+
const bgutils_js_1 = require("bgutils-js");
|
|
15
|
+
const jsdom_1 = require("jsdom");
|
|
16
|
+
const stream_converter_1 = require("./stream-converter");
|
|
10
17
|
/**
|
|
11
|
-
*
|
|
18
|
+
* Generates a web PoToken for YouTube authentication
|
|
19
|
+
* This is required for accessing restricted video content
|
|
12
20
|
*/
|
|
13
|
-
async function
|
|
21
|
+
async function generateWebPoToken(contentBinding) {
|
|
14
22
|
try {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
23
|
+
const requestKey = "O43z0dpjhgX20SCx4KAo";
|
|
24
|
+
if (!contentBinding)
|
|
25
|
+
throw new Error("Could not get visitor data");
|
|
26
|
+
const dom = new jsdom_1.JSDOM();
|
|
27
|
+
Object.assign(globalThis, {
|
|
28
|
+
window: dom.window,
|
|
29
|
+
document: dom.window.document,
|
|
30
|
+
});
|
|
31
|
+
const bgConfig = {
|
|
32
|
+
fetch: (input, init) => fetch(input, init),
|
|
33
|
+
globalObj: globalThis,
|
|
34
|
+
identifier: contentBinding,
|
|
35
|
+
requestKey,
|
|
36
|
+
};
|
|
37
|
+
const bgChallenge = await bgutils_js_1.BG.Challenge.create(bgConfig);
|
|
38
|
+
if (!bgChallenge)
|
|
39
|
+
throw new Error("Could not get challenge");
|
|
40
|
+
const interpreterJavascript = bgChallenge.interpreterJavascript.privateDoNotAccessOrElseSafeScriptWrappedValue;
|
|
41
|
+
if (interpreterJavascript) {
|
|
42
|
+
new Function(interpreterJavascript)();
|
|
43
|
+
}
|
|
44
|
+
else
|
|
45
|
+
throw new Error("Could not load VM");
|
|
46
|
+
const poTokenResult = await bgutils_js_1.BG.PoToken.generate({
|
|
47
|
+
program: bgChallenge.program,
|
|
48
|
+
globalName: bgChallenge.globalName,
|
|
49
|
+
bgConfig,
|
|
50
|
+
});
|
|
51
|
+
const placeholderPoToken = bgutils_js_1.BG.PoToken.generatePlaceholder(contentBinding);
|
|
52
|
+
return {
|
|
53
|
+
visitorData: contentBinding,
|
|
54
|
+
placeholderPoToken,
|
|
55
|
+
poToken: poTokenResult.poToken,
|
|
56
|
+
};
|
|
20
57
|
}
|
|
21
58
|
catch (error) {
|
|
22
|
-
|
|
23
|
-
|
|
59
|
+
console.warn("PoToken generation failed, continuing without it:", error);
|
|
60
|
+
return {
|
|
61
|
+
visitorData: contentBinding,
|
|
62
|
+
placeholderPoToken: "",
|
|
63
|
+
poToken: "",
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Makes a proper player request to YouTube API
|
|
69
|
+
*/
|
|
70
|
+
async function makePlayerRequest(innertube, videoId, reloadPlaybackContext) {
|
|
71
|
+
const watchEndpoint = new youtubei_js_1.YTNodes.NavigationEndpoint({
|
|
72
|
+
watchEndpoint: { videoId },
|
|
73
|
+
});
|
|
74
|
+
const extraArgs = {
|
|
75
|
+
playbackContext: {
|
|
76
|
+
adPlaybackContext: { pyv: true },
|
|
77
|
+
contentPlaybackContext: {
|
|
78
|
+
vis: 0,
|
|
79
|
+
splay: false,
|
|
80
|
+
lactMilliseconds: "-1",
|
|
81
|
+
signatureTimestamp: innertube.session.player?.signature_timestamp,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
contentCheckOk: true,
|
|
85
|
+
racyCheckOk: true,
|
|
86
|
+
};
|
|
87
|
+
if (reloadPlaybackContext) {
|
|
88
|
+
extraArgs.playbackContext.reloadPlaybackContext = reloadPlaybackContext;
|
|
89
|
+
}
|
|
90
|
+
return watchEndpoint.call(innertube.actions, {
|
|
91
|
+
...extraArgs,
|
|
92
|
+
parse: true,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* YouTube VM shim
|
|
97
|
+
* This allows the SABR stream to execute YouTube's custom JavaScript for deciphering signatures and generating tokens
|
|
98
|
+
*/
|
|
99
|
+
youtubei_js_1.Platform.shim.eval = async (data, env) => {
|
|
100
|
+
const properties = [];
|
|
101
|
+
if (env.n)
|
|
102
|
+
properties.push(`n: exportedVars.nFunction("${env.n}")`);
|
|
103
|
+
if (env.sig)
|
|
104
|
+
properties.push(`sig: exportedVars.sigFunction("${env.sig}")`);
|
|
105
|
+
const code = `${data.output}\nreturn { ${properties.join(", ")} }`;
|
|
106
|
+
return new Function(code)();
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Creates a SABR audio stream for YouTube video download
|
|
110
|
+
* This provides better quality and more reliable streaming than standard methods
|
|
111
|
+
*/
|
|
112
|
+
async function createSabrStream(videoId, innertube, options) {
|
|
113
|
+
try {
|
|
114
|
+
// Generate PoToken for authentication
|
|
115
|
+
const webPo = await generateWebPoToken(videoId);
|
|
116
|
+
// Make initial player request
|
|
117
|
+
const player = await makePlayerRequest(innertube, videoId);
|
|
118
|
+
const title = player.video_details?.title || "unknown";
|
|
119
|
+
const serverAbrStreamingUrl = await innertube.session.player?.decipher(player.streaming_data?.server_abr_streaming_url);
|
|
120
|
+
const ustreamerConfig = player.player_config?.media_common_config.media_ustreamer_request_config?.video_playback_ustreamer_config;
|
|
121
|
+
if (!serverAbrStreamingUrl || !ustreamerConfig) {
|
|
122
|
+
throw new Error("Missing SABR streaming config");
|
|
123
|
+
}
|
|
124
|
+
const sabrFormats = player.streaming_data?.adaptive_formats.map((f) => (0, utils_1.buildSabrFormat)(f)) || [];
|
|
125
|
+
const sabr = new sabr_stream_1.SabrStream({
|
|
126
|
+
formats: sabrFormats,
|
|
127
|
+
serverAbrStreamingUrl,
|
|
128
|
+
videoPlaybackUstreamerConfig: ustreamerConfig,
|
|
129
|
+
poToken: webPo.poToken,
|
|
130
|
+
clientInfo: {
|
|
131
|
+
clientName: parseInt(youtubei_js_1.Constants.CLIENT_NAME_IDS[innertube.session.context.client.clientName]),
|
|
132
|
+
clientVersion: innertube.session.context.client.clientVersion,
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
// Handle player response reload events
|
|
136
|
+
sabr.on("reloadPlayerResponse", async (ctx) => {
|
|
137
|
+
try {
|
|
138
|
+
const pr = await makePlayerRequest(innertube, videoId, ctx);
|
|
139
|
+
const url = await innertube.session.player?.decipher(pr.streaming_data?.server_abr_streaming_url);
|
|
140
|
+
const config = pr.player_config?.media_common_config.media_ustreamer_request_config?.video_playback_ustreamer_config;
|
|
141
|
+
if (url && config) {
|
|
142
|
+
sabr.setStreamingURL(url);
|
|
143
|
+
sabr.setUstreamerConfig(config);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
console.error("Failed to reload player response:", error);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
// Start the stream with audio preference
|
|
151
|
+
const mergedOptions = { ...exports.DEFAULT_SABR_OPTIONS, ...options };
|
|
152
|
+
const { audioStream, selectedFormats } = await sabr.start({
|
|
153
|
+
audioQuality: mergedOptions?.audioQuality || "medium",
|
|
154
|
+
});
|
|
155
|
+
// Convert Web Stream to Node.js Readable stream with optimized buffer
|
|
156
|
+
const nodeStream = (0, stream_converter_1.webStreamToNodeStream)(audioStream, 32 * 1024); // 32KB buffer for YouTube streams
|
|
157
|
+
return {
|
|
158
|
+
title,
|
|
159
|
+
stream: nodeStream,
|
|
160
|
+
format: {
|
|
161
|
+
mimeType: selectedFormats.audioFormat.mimeType || "audio/webm",
|
|
162
|
+
itag: selectedFormats.audioFormat.itag || 0,
|
|
163
|
+
contentLength: selectedFormats.audioFormat.contentLength || 0,
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
throw new Error(`SABR stream creation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
24
169
|
}
|
|
25
170
|
}
|
|
26
171
|
/**
|
|
@@ -38,23 +183,16 @@ function createOutputStream(videoTitle, mimeType) {
|
|
|
38
183
|
};
|
|
39
184
|
}
|
|
40
185
|
/**
|
|
41
|
-
*
|
|
186
|
+
* Sanitizes a filename by removing invalid characters
|
|
42
187
|
*/
|
|
43
|
-
function
|
|
44
|
-
return
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
progressBar.increment(chunk.length);
|
|
52
|
-
}
|
|
53
|
-
},
|
|
54
|
-
close() {
|
|
55
|
-
outputStream.end();
|
|
56
|
-
},
|
|
57
|
-
});
|
|
188
|
+
function sanitizeFileName(name) {
|
|
189
|
+
return name.replace(/[^\w\d]+/g, "_").slice(0, 128);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Converts bytes to megabytes
|
|
193
|
+
*/
|
|
194
|
+
function bytesToMB(bytes) {
|
|
195
|
+
return (bytes / 1024 / 1024).toFixed(2);
|
|
58
196
|
}
|
|
59
197
|
/**
|
|
60
198
|
* Gets file extension from MIME type
|
|
@@ -71,13 +209,14 @@ function getExtensionFromMimeType(mimeType) {
|
|
|
71
209
|
return mimeMap[mimeType] || "bin";
|
|
72
210
|
}
|
|
73
211
|
/**
|
|
74
|
-
* Default sabr playback options
|
|
212
|
+
* Default sabr playback options - optimized for memory usage
|
|
213
|
+
* Using MEDIUM quality and WebM/Opus reduces bandwidth by ~30-40%
|
|
75
214
|
*/
|
|
76
215
|
exports.DEFAULT_SABR_OPTIONS = {
|
|
77
|
-
preferWebM: true,
|
|
78
|
-
preferOpus: true,
|
|
79
|
-
videoQuality: "
|
|
80
|
-
audioQuality: "
|
|
216
|
+
preferWebM: true, // WebM with Opus is more memory-efficient
|
|
217
|
+
preferOpus: true, // Opus codec = smaller bitrate vs AAC
|
|
218
|
+
videoQuality: "360p", // Lower resolution = less processing
|
|
219
|
+
audioQuality: "medium", // Medium quality balances quality vs bandwidth (~96-128kbps vs 256kbps)
|
|
81
220
|
enabledTrackTypes: "VIDEO_AND_AUDIO",
|
|
82
221
|
};
|
|
83
222
|
//# sourceMappingURL=sabr-stream-factory.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sabr-stream-factory.js","sourceRoot":"","sources":["../../src/utils/sabr-stream-factory.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"sabr-stream-factory.js","sourceRoot":"","sources":["../../src/utils/sabr-stream-factory.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { Readable } from "stream";
|
|
2
2
|
/**
|
|
3
3
|
* Converts a Web ReadableStream to a Node.js Readable stream
|
|
4
|
+
* with proper backpressure handling to prevent memory bloat
|
|
5
|
+
*
|
|
6
|
+
* Optimization:
|
|
7
|
+
* - Respects highWaterMark (64KB buffer)
|
|
8
|
+
* - Handles backpressure by pausing reads when buffer is full
|
|
9
|
+
* - Batches small chunks to reduce overhead
|
|
10
|
+
* - Lazy evaluation - only reads when consumer is ready
|
|
4
11
|
*/
|
|
5
|
-
export declare function webStreamToNodeStream(webStream: ReadableStream): Readable;
|
|
6
|
-
/**
|
|
7
|
-
* Converts a Web ReadableStream to a Node.js Readable stream with progress tracking
|
|
8
|
-
*/
|
|
9
|
-
export declare function webStreamToNodeStreamWithProgress(webStream: ReadableStream, progressCallback?: (bytesRead: number) => void): Readable;
|
|
12
|
+
export declare function webStreamToNodeStream(webStream: ReadableStream, highWaterMark?: number): Readable;
|
|
10
13
|
//# sourceMappingURL=stream-converter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream-converter.d.ts","sourceRoot":"","sources":["../../src/utils/stream-converter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAElC
|
|
1
|
+
{"version":3,"file":"stream-converter.d.ts","sourceRoot":"","sources":["../../src/utils/stream-converter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAElC;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,cAAc,EAAE,aAAa,GAAE,MAAkB,GAAG,QAAQ,CAsI5G"}
|
|
@@ -1,71 +1,135 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.webStreamToNodeStream = webStreamToNodeStream;
|
|
4
|
-
exports.webStreamToNodeStreamWithProgress = webStreamToNodeStreamWithProgress;
|
|
5
4
|
const stream_1 = require("stream");
|
|
6
5
|
/**
|
|
7
6
|
* Converts a Web ReadableStream to a Node.js Readable stream
|
|
7
|
+
* with proper backpressure handling to prevent memory bloat
|
|
8
|
+
*
|
|
9
|
+
* Optimization:
|
|
10
|
+
* - Respects highWaterMark (64KB buffer)
|
|
11
|
+
* - Handles backpressure by pausing reads when buffer is full
|
|
12
|
+
* - Batches small chunks to reduce overhead
|
|
13
|
+
* - Lazy evaluation - only reads when consumer is ready
|
|
8
14
|
*/
|
|
9
|
-
function webStreamToNodeStream(webStream) {
|
|
15
|
+
function webStreamToNodeStream(webStream, highWaterMark = 64 * 1024) {
|
|
16
|
+
let reader = null;
|
|
17
|
+
let pumpActive = true;
|
|
18
|
+
let abortController = null;
|
|
19
|
+
let isReading = false; // Prevent concurrent reads
|
|
10
20
|
const nodeStream = new stream_1.Readable({
|
|
11
|
-
|
|
12
|
-
|
|
21
|
+
// Set buffer size to 64KB - balance between memory and throughput
|
|
22
|
+
highWaterMark: highWaterMark,
|
|
23
|
+
read(size) {
|
|
24
|
+
// Resume reading when stream is ready for more data
|
|
25
|
+
if (!isReading && pumpActive && reader) {
|
|
26
|
+
isReading = true;
|
|
27
|
+
pump().catch((error) => {
|
|
28
|
+
if (pumpActive) {
|
|
29
|
+
console.error("[stream-converter] Pump error:", error);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
destroy(error, callback) {
|
|
35
|
+
// Gracefully stop the pump when stream is destroyed
|
|
36
|
+
pumpActive = false;
|
|
37
|
+
// Cancel reader if active
|
|
38
|
+
if (reader) {
|
|
39
|
+
try {
|
|
40
|
+
reader.cancel().catch(() => {
|
|
41
|
+
// Ignore cancel errors
|
|
42
|
+
});
|
|
43
|
+
reader = null;
|
|
44
|
+
}
|
|
45
|
+
catch { }
|
|
46
|
+
}
|
|
47
|
+
// Abort any pending operations
|
|
48
|
+
if (abortController) {
|
|
49
|
+
try {
|
|
50
|
+
abortController.abort();
|
|
51
|
+
}
|
|
52
|
+
catch { }
|
|
53
|
+
abortController = null;
|
|
54
|
+
}
|
|
55
|
+
callback(error);
|
|
13
56
|
},
|
|
14
57
|
});
|
|
58
|
+
// Create abort controller for graceful shutdown
|
|
59
|
+
abortController = new AbortController();
|
|
15
60
|
// Create a reader from the Web Stream
|
|
16
|
-
|
|
17
|
-
// Read chunks
|
|
61
|
+
reader = webStream.getReader();
|
|
62
|
+
// Read chunks with backpressure support
|
|
18
63
|
const pump = async () => {
|
|
19
64
|
try {
|
|
20
|
-
while (
|
|
65
|
+
while (pumpActive && reader) {
|
|
21
66
|
const { done, value } = await reader.read();
|
|
67
|
+
// Check if pump was stopped during read
|
|
68
|
+
if (!pumpActive || !reader) {
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
22
71
|
if (done) {
|
|
23
72
|
nodeStream.push(null); // End the stream
|
|
24
73
|
break;
|
|
25
74
|
}
|
|
26
|
-
|
|
75
|
+
if (value && pumpActive) {
|
|
76
|
+
// Convert to Buffer and push
|
|
77
|
+
const buffer = Buffer.from(value);
|
|
78
|
+
// Check backpressure: push() returns false if internal buffer is full
|
|
79
|
+
// This means we should pause and let the consumer catch up
|
|
80
|
+
const shouldContinue = nodeStream.push(buffer);
|
|
81
|
+
if (!shouldContinue) {
|
|
82
|
+
// Internal buffer is full, pause reading
|
|
83
|
+
isReading = false;
|
|
84
|
+
break; // Exit pump, will resume when consumer calls read()
|
|
85
|
+
}
|
|
86
|
+
}
|
|
27
87
|
}
|
|
28
88
|
}
|
|
29
89
|
catch (error) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
function webStreamToNodeStreamWithProgress(webStream, progressCallback) {
|
|
41
|
-
const nodeStream = new stream_1.Readable({
|
|
42
|
-
read() {
|
|
43
|
-
// This will be handled by the Web Stream reader
|
|
44
|
-
},
|
|
45
|
-
});
|
|
46
|
-
let bytesRead = 0;
|
|
47
|
-
const reader = webStream.getReader();
|
|
48
|
-
const pump = async () => {
|
|
49
|
-
try {
|
|
50
|
-
while (true) {
|
|
51
|
-
const { done, value } = await reader.read();
|
|
52
|
-
if (done) {
|
|
53
|
-
nodeStream.push(null); // End the stream
|
|
54
|
-
break;
|
|
90
|
+
// Only destroy if pump is still active and stream exists
|
|
91
|
+
if (pumpActive) {
|
|
92
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
93
|
+
// Ignore "Controller is already closed" and stream cancelled errors
|
|
94
|
+
if (errorMsg.includes("Controller is already closed") ||
|
|
95
|
+
errorMsg.includes("already been cancelled") ||
|
|
96
|
+
errorMsg.includes("stream closed") ||
|
|
97
|
+
errorMsg.includes("aborted")) {
|
|
98
|
+
// Stream was destroyed externally, just end cleanly
|
|
99
|
+
nodeStream.push(null);
|
|
55
100
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (progressCallback) {
|
|
60
|
-
progressCallback(bytesRead);
|
|
101
|
+
else {
|
|
102
|
+
// Real error, report it
|
|
103
|
+
nodeStream.destroy(error);
|
|
61
104
|
}
|
|
62
105
|
}
|
|
63
106
|
}
|
|
64
|
-
|
|
65
|
-
|
|
107
|
+
finally {
|
|
108
|
+
// Mark as not reading
|
|
109
|
+
isReading = false;
|
|
110
|
+
// Cleanup reader when pump ends
|
|
111
|
+
try {
|
|
112
|
+
if (reader) {
|
|
113
|
+
await reader.cancel();
|
|
114
|
+
reader = null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch { }
|
|
118
|
+
pumpActive = false;
|
|
66
119
|
}
|
|
67
120
|
};
|
|
68
|
-
pump
|
|
121
|
+
// Start initial pump when stream is requested
|
|
122
|
+
// Note: pump will be called by read() callback for backpressure compliance
|
|
123
|
+
setImmediate(() => {
|
|
124
|
+
if (pumpActive && reader && !isReading) {
|
|
125
|
+
isReading = true;
|
|
126
|
+
pump().catch((error) => {
|
|
127
|
+
if (pumpActive) {
|
|
128
|
+
console.error("[stream-converter] Initial pump error:", error);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
});
|
|
69
133
|
return nodeStream;
|
|
70
134
|
}
|
|
71
135
|
//# sourceMappingURL=stream-converter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream-converter.js","sourceRoot":"","sources":["../../src/utils/stream-converter.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"stream-converter.js","sourceRoot":"","sources":["../../src/utils/stream-converter.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,45 +1,46 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@ziplayer/plugin",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "A modular Discord voice player with plugin system",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"ZiPlayer",
|
|
7
|
-
"@ziplayer/plugin",
|
|
8
|
-
"discord",
|
|
9
|
-
"music",
|
|
10
|
-
"player",
|
|
11
|
-
"voice"
|
|
12
|
-
],
|
|
13
|
-
"homepage": "https://player.ziji.world",
|
|
14
|
-
"bugs": {
|
|
15
|
-
"url": "https://github.com/ZiProject/ZiPlayer/issues"
|
|
16
|
-
},
|
|
17
|
-
"repository": {
|
|
18
|
-
"type": "git",
|
|
19
|
-
"url": "git+https://github.com/ZiProject/ZiPlayer.git"
|
|
20
|
-
},
|
|
21
|
-
"license": "MIT",
|
|
22
|
-
"author": "Ziji",
|
|
23
|
-
"main": "dist/index.js",
|
|
24
|
-
"types": "dist/index.d.ts",
|
|
25
|
-
"scripts": {
|
|
26
|
-
"build": "tsc",
|
|
27
|
-
"dev": "tsc --watch",
|
|
28
|
-
"prepare": "npm run build"
|
|
29
|
-
},
|
|
30
|
-
"dependencies": {
|
|
31
|
-
"@zibot/scdl": "^0.0.6",
|
|
32
|
-
"@zibot/zitts": "^0.0.3",
|
|
33
|
-
"axios": "^1.13.2",
|
|
34
|
-
"
|
|
35
|
-
"googlevideo": "^
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"youtubei.js": "^16.0.1",
|
|
39
|
-
"ziplayer": "^0.2.
|
|
40
|
-
},
|
|
41
|
-
"devDependencies": {
|
|
42
|
-
"@types/
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@ziplayer/plugin",
|
|
3
|
+
"version": "0.2.1-dev-2",
|
|
4
|
+
"description": "A modular Discord voice player with plugin system",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ZiPlayer",
|
|
7
|
+
"@ziplayer/plugin",
|
|
8
|
+
"discord",
|
|
9
|
+
"music",
|
|
10
|
+
"player",
|
|
11
|
+
"voice"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://player.ziji.world",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/ZiProject/ZiPlayer/issues"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/ZiProject/ZiPlayer.git"
|
|
20
|
+
},
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"author": "Ziji",
|
|
23
|
+
"main": "dist/index.js",
|
|
24
|
+
"types": "dist/index.d.ts",
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsc",
|
|
27
|
+
"dev": "tsc --watch",
|
|
28
|
+
"prepare": "npm run build"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@zibot/scdl": "^0.0.6",
|
|
32
|
+
"@zibot/zitts": "^0.0.3",
|
|
33
|
+
"axios": "^1.13.2",
|
|
34
|
+
"bgutils-js": "^3.2.0",
|
|
35
|
+
"googlevideo": "^4.0.4",
|
|
36
|
+
"jsdom": "^28.1.0",
|
|
37
|
+
"music-metadata": "^11.9.0",
|
|
38
|
+
"youtubei.js": "^16.0.1",
|
|
39
|
+
"ziplayer": "^0.2.3"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/jsdom": "^27.0.0",
|
|
43
|
+
"@types/node": "^20.0.0",
|
|
44
|
+
"typescript": "^5.0.0"
|
|
45
|
+
}
|
|
46
|
+
}
|