piixishstream 1.0.4 → 1.0.5

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.
@@ -82,33 +82,47 @@ export class Streamer {
82
82
  reject("cannot start stream without first joining voice channel");
83
83
  return;
84
84
  }
85
- this.signalStream();
86
- const { guildId: clientGuildId, channelId: clientChannelId, session_id, } = this.voiceConnection;
85
+ if (this.voiceConnection.streamConnection?.webRtcConn?.ready) {
86
+ resolve(this.voiceConnection.streamConnection.webRtcConn);
87
+ return;
88
+ }
89
+ const { guildId: clientGuildId, channelId: clientChannelId, session_id } = this.voiceConnection;
87
90
  const { id: clientUserId } = this.client.user;
88
- if (!session_id)
89
- throw new Error("Session doesn't exist yet");
90
- const streamConn = new StreamConnection(this, clientGuildId, clientUserId, clientChannelId, (conn) => {
91
- resolve(conn);
92
- });
93
- this.voiceConnection.streamConnection = streamConn;
94
- this._gatewayEmitter.on("STREAM_CREATE", (d) => {
91
+ if (!session_id) throw new Error("Session doesn't exist yet");
92
+
93
+ let done = false;
94
+ const onStreamCreate = (d) => {
95
95
  const { channelId, guildId, userId } = parseStreamKey(d.stream_key);
96
- if (clientGuildId !== guildId ||
97
- clientChannelId !== channelId ||
98
- clientUserId !== userId)
99
- return;
96
+ if (clientGuildId !== guildId || clientChannelId !== channelId || clientUserId !== userId) return;
100
97
  streamConn.serverId = d.rtc_server_id;
101
98
  streamConn.streamKey = d.stream_key;
102
99
  streamConn.setSession(session_id);
103
- });
104
- this._gatewayEmitter.on("STREAM_SERVER_UPDATE", (d) => {
100
+ };
101
+ const onStreamServerUpdate = (d) => {
105
102
  const { channelId, guildId, userId } = parseStreamKey(d.stream_key);
106
- if (clientGuildId !== guildId ||
107
- clientChannelId !== channelId ||
108
- clientUserId !== userId)
109
- return;
103
+ if (clientGuildId !== guildId || clientChannelId !== channelId || clientUserId !== userId) return;
110
104
  streamConn.setTokens(d.endpoint, d.token);
105
+ };
106
+ const cleanup = () => {
107
+ if (done) return;
108
+ done = true;
109
+ clearTimeout(timer);
110
+ this._gatewayEmitter.off("STREAM_CREATE", onStreamCreate);
111
+ this._gatewayEmitter.off("STREAM_SERVER_UPDATE", onStreamServerUpdate);
112
+ };
113
+ const timer = setTimeout(() => {
114
+ cleanup();
115
+ reject(new Error("Timeout en attente de la passerelle Discord Go-Live"));
116
+ }, 10000);
117
+
118
+ this.signalStream();
119
+ const streamConn = new StreamConnection(this, clientGuildId, clientUserId, clientChannelId, (conn) => {
120
+ cleanup();
121
+ resolve(conn);
111
122
  });
123
+ this.voiceConnection.streamConnection = streamConn;
124
+ this._gatewayEmitter.on("STREAM_CREATE", onStreamCreate);
125
+ this._gatewayEmitter.on("STREAM_SERVER_UPDATE", onStreamServerUpdate);
112
126
  });
113
127
  }
114
128
  async setStreamPreview(image) {
@@ -60,6 +60,25 @@ export class WebRtcConnWrapper {
60
60
  get webRtcConn() {
61
61
  return this._webRtcConn;
62
62
  }
63
+ waitUntilReady(timeoutMs = 5000) {
64
+ if (this.ready) return Promise.resolve();
65
+ return new Promise((resolve) => {
66
+ let done = false;
67
+ const finish = () => {
68
+ if (!done) {
69
+ done = true;
70
+ clearTimeout(timer);
71
+ resolve();
72
+ }
73
+ };
74
+ const timer = setTimeout(finish, timeoutMs);
75
+ this._webRtcConn?.onStateChange((state) => {
76
+ if (state === "connected") {
77
+ finish();
78
+ }
79
+ });
80
+ });
81
+ }
63
82
  get ready() {
64
83
  return this._webRtcConn?.state() === "connected";
65
84
  }
@@ -274,6 +274,7 @@ export async function playStream(input, streamer, options = {}, cancelSignal) {
274
274
  ? opts.readrateInitialBurst
275
275
  : defaultOptions.readrateInitialBurst,
276
276
  streamPreview: opts.streamPreview ?? defaultOptions.streamPreview,
277
+ keepConnection: opts.keepConnection ?? false,
277
278
  };
278
279
  }
279
280
  const mergedOptions = mergeOptions(options);
@@ -297,12 +298,12 @@ export async function playStream(input, streamer, options = {}, cancelSignal) {
297
298
  let stopStream;
298
299
  if (mergedOptions.type === "go-live") {
299
300
  conn = await streamer.createStream();
300
- stopStream = () => streamer.stopStream();
301
+ stopStream = () => { if (!mergedOptions.keepConnection) streamer.stopStream(); };
301
302
  }
302
303
  else {
303
304
  conn = streamer.voiceConnection.webRtcConn;
304
305
  streamer.signalVideo(true);
305
- stopStream = () => streamer.signalVideo(false);
306
+ stopStream = () => { if (!mergedOptions.keepConnection) streamer.signalVideo(false); };
306
307
  }
307
308
  conn.setPacketizer(videoCodecMap[video.codec]);
308
309
  conn.mediaConnection.setSpeaking(true);
@@ -383,8 +384,10 @@ export async function playStream(input, streamer, options = {}, cancelSignal) {
383
384
  return new Promise((resolve, reject) => {
384
385
  cleanupFuncs.push(() => {
385
386
  stopStream();
386
- conn.mediaConnection.setSpeaking(false);
387
- conn.mediaConnection.setVideoAttributes(false);
387
+ if (!mergedOptions.keepConnection) {
388
+ conn.mediaConnection.setSpeaking(false);
389
+ conn.mediaConnection.setVideoAttributes(false);
390
+ }
388
391
  });
389
392
  let cleanedUp = false;
390
393
  const cleanup = () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "piixishstream",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "Experiment for making video streaming work for discord selfbots",
5
5
  "main": "./dist/index.js",
6
6
  "exports": "./dist/index.js",
@@ -81,3 +81,4 @@
81
81
  ]
82
82
  }
83
83
  }
84
+
@@ -104,63 +104,58 @@ export class Streamer {
104
104
  });
105
105
  }
106
106
 
107
- public createStream(): Promise<WebRtcConnWrapper> {
108
- return new Promise<WebRtcConnWrapper>((resolve, reject) => {
109
- if (!this.client.user) {
110
- reject("Client not logged in");
111
- return;
112
- }
113
- if (!this.voiceConnection) {
114
- reject("cannot start stream without first joining voice channel");
115
- return;
116
- }
117
-
118
- this.signalStream();
119
- const {
120
- guildId: clientGuildId,
121
- channelId: clientChannelId,
122
- session_id,
123
- } = this.voiceConnection;
124
- const { id: clientUserId } = this.client.user;
125
-
126
- if (!session_id) throw new Error("Session doesn't exist yet");
127
- const streamConn = new StreamConnection(
128
- this,
129
- clientGuildId,
130
- clientUserId,
131
- clientChannelId,
132
- (conn) => {
133
- resolve(conn);
134
- },
135
- );
136
- this.voiceConnection.streamConnection = streamConn;
137
- this._gatewayEmitter.on("STREAM_CREATE", (d) => {
138
- const { channelId, guildId, userId } = parseStreamKey(d.stream_key);
139
-
140
- if (
141
- clientGuildId !== guildId ||
142
- clientChannelId !== channelId ||
143
- clientUserId !== userId
144
- )
145
- return;
146
-
147
- streamConn.serverId = d.rtc_server_id;
148
- streamConn.streamKey = d.stream_key;
149
- streamConn.setSession(session_id);
150
- });
151
- this._gatewayEmitter.on("STREAM_SERVER_UPDATE", (d) => {
152
- const { channelId, guildId, userId } = parseStreamKey(d.stream_key);
153
-
154
- if (
155
- clientGuildId !== guildId ||
156
- clientChannelId !== channelId ||
157
- clientUserId !== userId
158
- )
159
- return;
160
-
161
- streamConn.setTokens(d.endpoint, d.token);
162
- });
163
- });
107
+ public createStream(): Promise<WebRtcConnWrapper> {
108
+ return new Promise<WebRtcConnWrapper>((resolve, reject) => {
109
+ if (!this.client.user) {
110
+ reject("Client not logged in");
111
+ return;
112
+ }
113
+ if (!this.voiceConnection) {
114
+ reject("cannot start stream without first joining voice channel");
115
+ return;
116
+ }
117
+ if (this.voiceConnection.streamConnection?.webRtcConn?.ready) {
118
+ resolve(this.voiceConnection.streamConnection.webRtcConn);
119
+ return;
120
+ }
121
+ const { guildId: clientGuildId, channelId: clientChannelId, session_id } = this.voiceConnection;
122
+ const { id: clientUserId } = this.client.user;
123
+ if (!session_id) throw new Error("Session doesn't exist yet");
124
+
125
+ let done = false;
126
+ const onStreamCreate = (d: any) => {
127
+ const { channelId, guildId, userId } = parseStreamKey(d.stream_key);
128
+ if (clientGuildId !== guildId || clientChannelId !== channelId || clientUserId !== userId) return;
129
+ streamConn.serverId = d.rtc_server_id;
130
+ streamConn.streamKey = d.stream_key;
131
+ streamConn.setSession(session_id);
132
+ };
133
+ const onStreamServerUpdate = (d: any) => {
134
+ const { channelId, guildId, userId } = parseStreamKey(d.stream_key);
135
+ if (clientGuildId !== guildId || clientChannelId !== channelId || clientUserId !== userId) return;
136
+ streamConn.setTokens(d.endpoint, d.token);
137
+ };
138
+ const cleanup = () => {
139
+ if (done) return;
140
+ done = true;
141
+ clearTimeout(timer);
142
+ this._gatewayEmitter.off("STREAM_CREATE", onStreamCreate);
143
+ this._gatewayEmitter.off("STREAM_SERVER_UPDATE", onStreamServerUpdate);
144
+ };
145
+ const timer = setTimeout(() => {
146
+ cleanup();
147
+ reject(new Error("Timeout en attente de la passerelle Discord Go-Live"));
148
+ }, 10000);
149
+
150
+ this.signalStream();
151
+ const streamConn = new StreamConnection(this, clientGuildId, clientUserId, clientChannelId, (conn) => {
152
+ cleanup();
153
+ resolve(conn);
154
+ });
155
+ this.voiceConnection.streamConnection = streamConn;
156
+ this._gatewayEmitter.on("STREAM_CREATE", onStreamCreate);
157
+ this._gatewayEmitter.on("STREAM_SERVER_UPDATE", onStreamServerUpdate);
158
+ });
164
159
  }
165
160
 
166
161
  public async setStreamPreview(image: Buffer): Promise<void> {
@@ -93,7 +93,26 @@ export class WebRtcConnWrapper {
93
93
  return this._webRtcConn;
94
94
  }
95
95
 
96
- public get ready() {
96
+ public waitUntilReady(timeoutMs = 5000) {
97
+ if (this.ready) return Promise.resolve();
98
+ return new Promise((resolve) => {
99
+ let done = false;
100
+ const finish = () => {
101
+ if (!done) {
102
+ done = true;
103
+ clearTimeout(timer);
104
+ resolve();
105
+ }
106
+ };
107
+ const timer = setTimeout(finish, timeoutMs);
108
+ this._webRtcConn?.onStateChange((state) => {
109
+ if (state === "connected") {
110
+ finish();
111
+ }
112
+ });
113
+ });
114
+ }
115
+ get ready() {
97
116
  return this._webRtcConn?.state() === "connected";
98
117
  }
99
118
 
@@ -492,7 +492,8 @@ export async function playStream(
492
492
  ? opts.readrateInitialBurst
493
493
  : defaultOptions.readrateInitialBurst,
494
494
 
495
- streamPreview: opts.streamPreview ?? defaultOptions.streamPreview,
495
+ streamPreview: opts.streamPreview ?? defaultOptions.streamPreview,
496
+ keepConnection: opts.keepConnection ?? false,
496
497
  } satisfies PlayStreamOptions;
497
498
  }
498
499
 
@@ -520,11 +521,11 @@ export async function playStream(
520
521
  let stopStream: () => unknown;
521
522
  if (mergedOptions.type === "go-live") {
522
523
  conn = await streamer.createStream();
523
- stopStream = () => streamer.stopStream();
524
+ stopStream = () => { if (!mergedOptions.keepConnection) streamer.stopStream(); };
524
525
  } else {
525
526
  conn = streamer.voiceConnection.webRtcConn;
526
527
  streamer.signalVideo(true);
527
- stopStream = () => streamer.signalVideo(false);
528
+ stopStream = () => { if (!mergedOptions.keepConnection) streamer.signalVideo(false); };
528
529
  }
529
530
  conn.setPacketizer(videoCodecMap[video.codec]);
530
531
  conn.mediaConnection.setSpeaking(true);