This commit is contained in:
1 parent
2b90b2775f
commit
ac20d12b13
3 files changed
+42
-1
No files matched your search
@@ -114,6 +114,7 @@ type AudioManagerOptions = {
|
|||||||
};
|
};
|
||||||
source?: { type: 'url'; url: string } | { type: 'file'; path: string };
|
source?: { type: 'url'; url: string } | { type: 'file'; path: string };
|
||||||
renewIntervalMs?: number | false;
|
renewIntervalMs?: number | false;
|
||||||
|
reconnectAttempts?: number;
|
||||||
connectTimeoutMs?: number;
|
connectTimeoutMs?: number;
|
||||||
onError?: (error: Error) => void;
|
onError?: (error: Error) => void;
|
||||||
volume?: {
|
volume?: {
|
||||||
@@ -124,15 +125,17 @@ type AudioManagerOptions = {
|
|||||||
```
|
```
|
||||||
|
|
||||||
`connectTimeoutMs` and numeric `renewIntervalMs` values must be whole milliseconds from `1` through `2_147_483_647`.
|
`connectTimeoutMs` and numeric `renewIntervalMs` values must be whole milliseconds from `1` through `2_147_483_647`.
|
||||||
|
`reconnectAttempts` must be a non-negative safe integer. URL sources must use `http:` or `https:`.
|
||||||
`volume.initialPercent` must be a finite number from `0` through `100` and requires `volume.enabled: true`.
|
`volume.initialPercent` must be a finite number from `0` through `100` and requires `volume.enabled: true`.
|
||||||
|
|
||||||
### Defaults
|
### Defaults
|
||||||
|
|
||||||
| Option | Default |
|
| Option | Default |
|
||||||
| ------------------ | ---------- |
|
| ------------------- | ---------- |
|
||||||
| `ffmpeg.mode` | `'native'` |
|
| `ffmpeg.mode` | `'native'` |
|
||||||
| `connectTimeoutMs` | `20_000` |
|
| `connectTimeoutMs` | `20_000` |
|
||||||
| `renewIntervalMs` | `false` |
|
| `renewIntervalMs` | `false` |
|
||||||
|
| `reconnectAttempts` | `0` |
|
||||||
| `volume.enabled` | `false` |
|
| `volume.enabled` | `false` |
|
||||||
|
|
||||||
### Methods
|
### Methods
|
||||||
@@ -188,6 +191,9 @@ Connection renewal is disabled by default because `@discordjs/voice` handles rec
|
|||||||
`renewIntervalMs` only when an application has a measured need for periodic restarts. `stop()` and `dispose()` always
|
`renewIntervalMs` only when an application has a measured need for periodic restarts. `stop()` and `dispose()` always
|
||||||
clear the renewal timer.
|
clear the renewal timer.
|
||||||
|
|
||||||
|
After an unrecoverable disconnect, playback stops by default. Set `reconnectAttempts` to retry joining the configured
|
||||||
|
voice channel and restarting the current source.
|
||||||
|
|
||||||
## Errors
|
## Errors
|
||||||
|
|
||||||
The package exports these error classes:
|
The package exports these error classes:
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ export default class AudioManager implements Disposable {
|
|||||||
private connectAttempt: AbortController | undefined;
|
private connectAttempt: AbortController | undefined;
|
||||||
private renewTimer: NodeJS.Timeout | undefined;
|
private renewTimer: NodeJS.Timeout | undefined;
|
||||||
private playbackState: PlaybackState = 'idle';
|
private playbackState: PlaybackState = 'idle';
|
||||||
|
private reconnectGeneration = 0;
|
||||||
private connectionOptions: VoiceConnectionOptions | undefined;
|
private connectionOptions: VoiceConnectionOptions | undefined;
|
||||||
private audioSource: AudioSource | undefined;
|
private audioSource: AudioSource | undefined;
|
||||||
private readonly options: Required<Pick<AudioManagerOptions, 'connectTimeoutMs'>> &
|
private readonly options: Required<Pick<AudioManagerOptions, 'connectTimeoutMs'>> &
|
||||||
@@ -61,6 +62,13 @@ export default class AudioManager implements Disposable {
|
|||||||
assertValidTimerDelay(options.renewIntervalMs, 'renewIntervalMs');
|
assertValidTimerDelay(options.renewIntervalMs, 'renewIntervalMs');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
options.reconnectAttempts !== undefined &&
|
||||||
|
(!Number.isSafeInteger(options.reconnectAttempts) || options.reconnectAttempts < 0)
|
||||||
|
) {
|
||||||
|
throw new AudioManagerConfigError('reconnectAttempts must be a non-negative safe integer.');
|
||||||
|
}
|
||||||
|
|
||||||
if (options.volume?.initialPercent !== undefined) {
|
if (options.volume?.initialPercent !== undefined) {
|
||||||
if (options.volume.enabled !== true) {
|
if (options.volume.enabled !== true) {
|
||||||
throw new AudioManagerConfigError('volume.initialPercent requires volume.enabled to be true.');
|
throw new AudioManagerConfigError('volume.initialPercent requires volume.enabled to be true.');
|
||||||
@@ -254,6 +262,7 @@ export default class AudioManager implements Disposable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.reconnectGeneration += 1;
|
||||||
this.cancelConnectAttempt();
|
this.cancelConnectAttempt();
|
||||||
this.clearRenewTimer();
|
this.clearRenewTimer();
|
||||||
this.stopCurrentPlayback();
|
this.stopCurrentPlayback();
|
||||||
@@ -285,6 +294,7 @@ export default class AudioManager implements Disposable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.reconnectGeneration += 1;
|
||||||
this.cancelConnectAttempt();
|
this.cancelConnectAttempt();
|
||||||
this.clearRenewTimer();
|
this.clearRenewTimer();
|
||||||
this.stopCurrentPlayback();
|
this.stopCurrentPlayback();
|
||||||
@@ -366,6 +376,9 @@ export default class AudioManager implements Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async handleDisconnectedConnection(connection: VoiceConnection): Promise<void> {
|
private async handleDisconnectedConnection(connection: VoiceConnection): Promise<void> {
|
||||||
|
const shouldReconnect = this.resource !== undefined && (this.options.reconnectAttempts ?? 0) > 0;
|
||||||
|
const reconnectGeneration = this.reconnectGeneration;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Promise.race([
|
await Promise.race([
|
||||||
entersState(connection, VoiceConnectionStatus.Signalling, DISCONNECT_RECOVERY_TIMEOUT_MS),
|
entersState(connection, VoiceConnectionStatus.Signalling, DISCONNECT_RECOVERY_TIMEOUT_MS),
|
||||||
@@ -376,6 +389,22 @@ export default class AudioManager implements Disposable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shouldReconnect) {
|
||||||
|
this.clearRenewTimer();
|
||||||
|
for (let attempt = 0; attempt < (this.options.reconnectAttempts ?? 0); attempt += 1) {
|
||||||
|
try {
|
||||||
|
await this.start();
|
||||||
|
return;
|
||||||
|
} catch (reconnectError) {
|
||||||
|
error = reconnectError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.reconnectGeneration !== reconnectGeneration || this.playbackState === 'disposed') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.clearRenewTimer();
|
this.clearRenewTimer();
|
||||||
this.stopCurrentPlayback();
|
this.stopCurrentPlayback();
|
||||||
this.audioPlayer.stop(true);
|
this.audioPlayer.stop(true);
|
||||||
|
|||||||
@@ -150,6 +150,12 @@ export type AudioManagerOptions = {
|
|||||||
*/
|
*/
|
||||||
renewIntervalMs?: number | false;
|
renewIntervalMs?: number | false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of full reconnect and playback restart attempts after an unrecoverable disconnect.
|
||||||
|
* Disabled by default. Must be a non-negative safe integer.
|
||||||
|
*/
|
||||||
|
reconnectAttempts?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum milliseconds to wait for the Discord voice connection to become ready.
|
* Maximum milliseconds to wait for the Discord voice connection to become ready.
|
||||||
* Must be an integer from `1` through `2_147_483_647`.
|
* Must be an integer from `1` through `2_147_483_647`.
|
||||||
|
|||||||
Reference in new issue
Block a user