djs-selfbot-v13 3.7.34 → 3.7.35
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/package.json
CHANGED
|
@@ -53,14 +53,44 @@ class WebRtcStreamSession extends EventEmitter {
|
|
|
53
53
|
this._abort = new AbortController();
|
|
54
54
|
this._playPromise = null;
|
|
55
55
|
this._command = null;
|
|
56
|
+
this._ffmpegProc = null;
|
|
56
57
|
this._started = false;
|
|
58
|
+
this._running = false;
|
|
59
|
+
this._paused = false;
|
|
60
|
+
this._stopped = false;
|
|
61
|
+
this._positionMs = 0;
|
|
62
|
+
this._runStartWall = 0;
|
|
63
|
+
this._pausedAt = 0;
|
|
64
|
+
this._pausedTotalMs = 0;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
_getPositionMs() {
|
|
68
|
+
if (!this._running) return this._positionMs;
|
|
69
|
+
if (this._paused) return this._positionMs;
|
|
70
|
+
return this._positionMs + (Date.now() - this._runStartWall - this._pausedTotalMs);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
_resetRunClock() {
|
|
74
|
+
this._runStartWall = Date.now();
|
|
75
|
+
this._pausedAt = 0;
|
|
76
|
+
this._pausedTotalMs = 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
_signalFfmpeg(signal) {
|
|
80
|
+
const pid = this._ffmpegProc?.pid;
|
|
81
|
+
if (!pid) return;
|
|
82
|
+
try {
|
|
83
|
+
process.kill(pid, signal);
|
|
84
|
+
} catch {
|
|
85
|
+
// process already exited
|
|
86
|
+
}
|
|
57
87
|
}
|
|
58
88
|
|
|
59
89
|
/**
|
|
60
90
|
* Construit la commande ffmpeg : transcode en H264 + Opus, sortie nut.
|
|
61
91
|
* Pas de filtre azmq (compatibilité ffmpeg sans libzmq).
|
|
62
92
|
*/
|
|
63
|
-
_buildFfmpeg(url) {
|
|
93
|
+
_buildFfmpeg(url, seekSec = 0) {
|
|
64
94
|
const {
|
|
65
95
|
fps,
|
|
66
96
|
height = 720,
|
|
@@ -78,6 +108,8 @@ class WebRtcStreamSession extends EventEmitter {
|
|
|
78
108
|
const output = new PassThrough();
|
|
79
109
|
const command = ffmpeg(url);
|
|
80
110
|
|
|
111
|
+
if (seekSec > 0) command.seekInput(seekSec);
|
|
112
|
+
|
|
81
113
|
const isHttp = typeof url === 'string' && /^https?:\/\//i.test(url);
|
|
82
114
|
if (isHttp) {
|
|
83
115
|
command.inputOptions([
|
|
@@ -123,13 +155,21 @@ class WebRtcStreamSession extends EventEmitter {
|
|
|
123
155
|
return { command, output };
|
|
124
156
|
}
|
|
125
157
|
|
|
126
|
-
async _run(url, signal) {
|
|
158
|
+
async _run(url, signal, seekSec = 0) {
|
|
127
159
|
await ensureFfmpegPath();
|
|
128
160
|
|
|
129
|
-
const { command, output } = this._buildFfmpeg(url);
|
|
161
|
+
const { command, output } = this._buildFfmpeg(url, seekSec);
|
|
130
162
|
this._command = command;
|
|
163
|
+
this._ffmpegProc = null;
|
|
164
|
+
this._running = true;
|
|
165
|
+
this._paused = false;
|
|
166
|
+
this._stopped = false;
|
|
167
|
+
this._resetRunClock();
|
|
131
168
|
|
|
132
|
-
command.on('start', cmd =>
|
|
169
|
+
command.on('start', cmd => {
|
|
170
|
+
this._ffmpegProc = command.ffmpegProc ?? null;
|
|
171
|
+
this.emit('debug', `[cmd] ${cmd}`);
|
|
172
|
+
});
|
|
133
173
|
command.on('stderr', line => this.emit('debug', String(line)));
|
|
134
174
|
|
|
135
175
|
command.on('error', (err, _stdout, stderr) => {
|
|
@@ -139,7 +179,12 @@ class WebRtcStreamSession extends EventEmitter {
|
|
|
139
179
|
this.emit('error', new Error(stderr ? `${msg}\n${stderr}` : msg));
|
|
140
180
|
});
|
|
141
181
|
|
|
142
|
-
|
|
182
|
+
const onAbort = () => {
|
|
183
|
+
this._running = false;
|
|
184
|
+
this._ffmpegProc = null;
|
|
185
|
+
command.kill('SIGTERM');
|
|
186
|
+
};
|
|
187
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
143
188
|
command.run();
|
|
144
189
|
|
|
145
190
|
const { livestream } = this.options;
|
|
@@ -149,8 +194,17 @@ class WebRtcStreamSession extends EventEmitter {
|
|
|
149
194
|
this._playPromise = playStream(output, this.streamer, playOptions, signal);
|
|
150
195
|
|
|
151
196
|
this._playPromise
|
|
152
|
-
.then(() =>
|
|
197
|
+
.then(() => {
|
|
198
|
+
this._running = false;
|
|
199
|
+
this._ffmpegProc = null;
|
|
200
|
+
if (!signal.aborted) {
|
|
201
|
+
this._positionMs = 0;
|
|
202
|
+
this.emit('finish');
|
|
203
|
+
}
|
|
204
|
+
})
|
|
153
205
|
.catch(err => {
|
|
206
|
+
this._running = false;
|
|
207
|
+
this._ffmpegProc = null;
|
|
154
208
|
if (signal.aborted || err?.name === 'AbortError') return;
|
|
155
209
|
this.emit('error', err);
|
|
156
210
|
});
|
|
@@ -171,14 +225,56 @@ class WebRtcStreamSession extends EventEmitter {
|
|
|
171
225
|
void this._run(url, this._abort.signal);
|
|
172
226
|
}
|
|
173
227
|
|
|
228
|
+
/**
|
|
229
|
+
* Met en pause la lecture (ffmpeg suspendu, connexion vocale conservée).
|
|
230
|
+
*/
|
|
231
|
+
pause() {
|
|
232
|
+
if (!this._running || this._paused || this._stopped) return;
|
|
233
|
+
this._positionMs = this._getPositionMs();
|
|
234
|
+
this._pausedAt = Date.now();
|
|
235
|
+
this._signalFfmpeg('SIGSTOP');
|
|
236
|
+
this._paused = true;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Reprend la lecture (après pause ou stop).
|
|
241
|
+
*/
|
|
242
|
+
async resume() {
|
|
243
|
+
if (this._stopped) {
|
|
244
|
+
const seekSec = this._positionMs / 1000;
|
|
245
|
+
this._stopped = false;
|
|
246
|
+
await this._run(this.options.url, this._abort.signal, seekSec);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (!this._running || !this._paused) return;
|
|
250
|
+
this._pausedTotalMs += Date.now() - this._pausedAt;
|
|
251
|
+
this._pausedAt = 0;
|
|
252
|
+
this._signalFfmpeg('SIGCONT');
|
|
253
|
+
this._paused = false;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Arrête la lecture en conservant la position et la connexion vocale.
|
|
258
|
+
*/
|
|
174
259
|
stop() {
|
|
260
|
+
if (!this._running) return;
|
|
261
|
+
this._positionMs = this._getPositionMs();
|
|
262
|
+
this._stopped = true;
|
|
263
|
+
this._paused = false;
|
|
264
|
+
this._running = false;
|
|
265
|
+
this._ffmpegProc = null;
|
|
175
266
|
this._abort.abort();
|
|
267
|
+
this._abort = new AbortController();
|
|
176
268
|
}
|
|
177
269
|
|
|
270
|
+
/**
|
|
271
|
+
* Relance depuis la dernière position enregistrée.
|
|
272
|
+
*/
|
|
178
273
|
async replay() {
|
|
179
|
-
this.
|
|
180
|
-
this.
|
|
181
|
-
|
|
274
|
+
const seekSec = (this._running ? this._getPositionMs() : this._positionMs) / 1000;
|
|
275
|
+
if (this._running) this.stop();
|
|
276
|
+
this._stopped = false;
|
|
277
|
+
await this._run(this.options.url, this._abort.signal, seekSec);
|
|
182
278
|
}
|
|
183
279
|
|
|
184
280
|
disconnect() {
|
package/typings/index.d.ts
CHANGED
|
@@ -918,7 +918,7 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
|
|
|
918
918
|
}
|
|
919
919
|
>
|
|
920
920
|
>;
|
|
921
|
-
public startStream(options: StartStreamOptions): Promise<
|
|
921
|
+
public startStream(options: StartStreamOptions): Promise<WebRtcStreamSession>;
|
|
922
922
|
|
|
923
923
|
public on<K extends keyof ClientEvents>(event: K, listener: (...args: ClientEvents[K]) => Awaitable<void>): this;
|
|
924
924
|
public on<S extends string | symbol>(
|
|
@@ -1208,10 +1208,30 @@ export interface StartStreamOptions {
|
|
|
1208
1208
|
downloadHttp?: boolean;
|
|
1209
1209
|
/** Max video bitrate in kbps. Default: bitrate * 1.5 */
|
|
1210
1210
|
bitrateMax?: number;
|
|
1211
|
+
/** x264 tune (ex. film). */
|
|
1212
|
+
tune?: string;
|
|
1213
|
+
/** readrateInitialBurst pour sources live. */
|
|
1214
|
+
livestream?: boolean;
|
|
1211
1215
|
/** Low-latency encoding and playback. Default true. */
|
|
1212
1216
|
lowLatency?: boolean;
|
|
1213
1217
|
}
|
|
1214
1218
|
|
|
1219
|
+
/** Session Go Live WebRTC retournée par {@link Client#startStream}. */
|
|
1220
|
+
export class WebRtcStreamSession extends EventEmitter {
|
|
1221
|
+
public readonly client: Client;
|
|
1222
|
+
public pause(): void;
|
|
1223
|
+
public resume(): Promise<void>;
|
|
1224
|
+
public stop(): void;
|
|
1225
|
+
public replay(): Promise<void>;
|
|
1226
|
+
public disconnect(): void;
|
|
1227
|
+
public on(event: 'finish', listener: () => void): this;
|
|
1228
|
+
public on(event: 'debug', listener: (message: string) => void): this;
|
|
1229
|
+
public on(event: 'error', listener: (error: Error) => void): this;
|
|
1230
|
+
public once(event: 'finish', listener: () => void): this;
|
|
1231
|
+
public once(event: 'debug', listener: (message: string) => void): this;
|
|
1232
|
+
public once(event: 'error', listener: (error: Error) => void): this;
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1215
1235
|
export class StreamSession extends EventEmitter {
|
|
1216
1236
|
public readonly client: Client;
|
|
1217
1237
|
public readonly voiceConnection: VoiceConnection;
|