update discordeno-audio-plugin and finally switch to yt_download, update discordeno

This commit is contained in:
Lexie Love 2023-02-10 11:11:01 -06:00
parent 320d6d1782
commit 891b081791
11 changed files with 119 additions and 106 deletions

24
deps.ts
View File

@ -1,4 +1,4 @@
export { ApplicationCommandOptionTypes, createBot, Intents, startBot } from "https://deno.land/x/discordeno@17.0.1/mod.ts";
export { ApplicationCommandOptionTypes, createBot, Intents, startBot } from "https://deno.land/x/discordeno@18.0.1/mod.ts";
export {
createGlobalApplicationCommand,
editGlobalApplicationCommand,
@ -9,15 +9,15 @@ export {
sendFollowupMessage,
sendMessage,
upsertGlobalApplicationCommands
} from "https://deno.land/x/discordeno@17.0.1/helpers/mod.ts";
export { type BigString, type CreateApplicationCommand, type CreateSlashApplicationCommand, type InteractionCallbackData } from "https://deno.land/x/discordeno@17.0.1/types/mod.ts";
export { type CreateMessage } from "https://deno.land/x/discordeno@17.0.1/helpers/messages/mod.ts";
export { type InteractionResponse } from "https://deno.land/x/discordeno@17.0.1/types/discordeno.ts";
export { editOriginalInteractionResponse, sendInteractionResponse } from "https://deno.land/x/discordeno@17.0.1/helpers/interactions/mod.ts";
export { sendPrivateInteractionResponse } from "https://deno.land/x/discordeno@17.0.1/plugins/mod.ts";
export { type Channel } from "https://deno.land/x/discordeno@17.0.1/transformers/channel.ts";
export { type Bot } from "https://deno.land/x/discordeno@17.0.1/bot.ts";
export { type Interaction } from "https://deno.land/x/discordeno@17.0.1/transformers/interaction.ts";
export { type ApplicationCommandOption, type ApplicationCommandOptionChoice, type Embed } from "https://deno.land/x/discordeno@17.0.1/transformers/mod.ts";
export { leaveVoiceChannel } from "https://deno.land/x/discordeno@17.0.1/helpers/guilds/mod.ts";
} from "https://deno.land/x/discordeno@18.0.1/helpers/mod.ts";
export { type BigString, type CreateApplicationCommand, type CreateSlashApplicationCommand, type InteractionCallbackData } from "https://deno.land/x/discordeno@18.0.1/types/mod.ts";
export { type CreateMessage } from "https://deno.land/x/discordeno@18.0.1/helpers/messages/mod.ts";
export { type InteractionResponse } from "https://deno.land/x/discordeno@18.0.1/types/discordeno.ts";
export { editOriginalInteractionResponse, sendInteractionResponse } from "https://deno.land/x/discordeno@18.0.1/helpers/interactions/mod.ts";
export { sendPrivateInteractionResponse } from "https://deno.land/x/discordeno@18.0.1/plugins/mod.ts";
export { type Channel } from "https://deno.land/x/discordeno@18.0.1/transformers/channel.ts";
export { type Bot } from "https://deno.land/x/discordeno@18.0.1/bot.ts";
export { type Interaction } from "https://deno.land/x/discordeno@18.0.1/transformers/interaction.ts";
export { type ApplicationCommandOption, type ApplicationCommandOptionChoice, type Embed } from "https://deno.land/x/discordeno@18.0.1/transformers/mod.ts";
export { leaveVoiceChannel } from "https://deno.land/x/discordeno@18.0.1/helpers/guilds/mod.ts";
export { getConnectionData } from "./discordeno-audio-plugin/src/connection-data.ts";

View File

@ -1,8 +1,6 @@
export * from "https://deno.land/x/discordeno@17.1.0/mod.ts";
export * from "https://deno.land/x/discordeno@17.1.0/plugins/cache/mod.ts";
export * from "https://deno.land/x/discordeno@18.0.1/mod.ts";
export * from "https://deno.land/x/discordeno@18.0.1/plugins/cache/mod.ts";
export * as opus from "https://unpkg.com/@evan/wasm@0.0.95/target/opus/deno.js";
export * from "https://unpkg.com/@evan/wasm@0.0.95/target/nacl/deno.js";
export { ytDownload } from "https://deno.land/x/yt_download@1.2/mod.ts";
export type { VideoFormat } from "https://deno.land/x/ytdl_core@v0.1.2/src/types.ts";
export { downloadFromInfo, getInfo, ytdl } from "https://deno.land/x/ytdl_core@v0.1.2/mod.ts";
export { ytDownload } from "https://deno.land/x/yt_download@1.3/mod.ts";
export { default as YouTube } from "https://deno.land/x/youtube_sr@v4.1.17/mod.ts";

View File

@ -1,20 +1,8 @@
import { YouTube, ytdl } from "../../deps.ts";
import { YouTube, ytDownload } from "../../deps.ts";
import { bufferIter } from "../../utils/mod.ts";
import { demux } from "../demux/mod.ts";
import { createAudioSource, empty } from "./audio-source.ts";
function supportedFormatFilter(format: {
codecs: string;
container: string;
audioSampleRate?: string;
}) {
return (
format.codecs === "opus" &&
format.container === "webm" &&
format.audioSampleRate === "48000"
);
}
export async function getYoutubeSources(added_by?: string, ...queries: string[]) {
const sources = queries.map((query) => getYoutubeSource(query, added_by));
const awaitedSources = await Promise.all(sources);
@ -30,11 +18,12 @@ export async function getYoutubeSource(query: string, added_by?: string) {
const { id, title } = results[0];
return createAudioSource(title!, async () => {
try {
const stream = await ytdl(id!, {
filter: supportedFormatFilter
const stream = await ytDownload(id!, {
mimeType: `audio/webm; codecs="opus"`,
});
return bufferIter(demux(stream));
} catch {
console.error("error");
console.log(`Failed to play ${title}\n Returning empty stream`);
return empty();
}

View File

@ -26,6 +26,9 @@ export type ConnectionData = {
speaking: boolean;
sequence: number;
timestamp: number;
missedHeart: number;
lastHeart?: number;
reconnect: number;
};
connectInfo: {
endpoint?: string;
@ -106,6 +109,8 @@ export function getConnectionData(botId: bigint, guildId: bigint) {
speaking: false,
sequence: randomNBit(16),
timestamp: randomNBit(32),
missedHeart: 0,
reconnect: 0,
},
connectInfo: {},
audio: new EventSource<[Uint8Array]>(),

View File

@ -76,16 +76,6 @@ export function createOnAudio(
}
})
.filter((data) => data !== undefined)
.map((data) => data!)
.filter((data) => !silence(...data.decoded));
.map((data) => data!);
};
}
function silence(...values: number[]) {
for (const value of values) {
if (value !== 0) {
return false;
}
}
return true;
}

View File

@ -3,42 +3,23 @@ import { ConnectionData, setUserSSRC } from "../connection-data.ts";
import { discoverIP } from "../udp/discover.ts";
import { setSpeaking } from "../udp/speaking.ts";
import { sendHeart } from "./heartbeat.ts";
export enum ServerVoiceOpcodes {
/** Complete the websocket handshake. */
Ready = VoiceOpcodes.Ready,
/** Describe the session. */
SessionDescription = VoiceOpcodes.SessionDescription,
/** Indicate which users are speaking. */
Speaking = VoiceOpcodes.Speaking,
/** Sent to acknowledge a received client heartbeat. */
HeartbeatACK = VoiceOpcodes.HeartbeatACK,
/** Time to wait between sending heartbeats in milliseconds. */
Hello = VoiceOpcodes.Hello,
/** Acknowledge a successful session resume. */
Resumed = VoiceOpcodes.Resumed,
/** A client has disconnected from the voice channel */
ClientDisconnect = VoiceOpcodes.ClientDisconnect,
/** Weird OP code containing video and audio codecs */
Undocumented = 14,
}
import { ReceiveVoiceOpcodes } from "./opcodes.ts";
export const socketHandlers: Record<
ServerVoiceOpcodes,
ReceiveVoiceOpcodes,
(connectionData: ConnectionData, d: any) => void
> = {
[ServerVoiceOpcodes.Ready]: ready,
[ServerVoiceOpcodes.SessionDescription]: sessionDescription,
[ServerVoiceOpcodes.Speaking]: speaking,
[ServerVoiceOpcodes.HeartbeatACK]: heartbeatACK,
[ServerVoiceOpcodes.Hello]: hello,
[ServerVoiceOpcodes.Resumed]: resumed,
[ServerVoiceOpcodes.ClientDisconnect]: clientDisconnect,
[ServerVoiceOpcodes.Undocumented]: undocumented,
[ReceiveVoiceOpcodes.Ready]: ready,
[ReceiveVoiceOpcodes.SessionDescription]: sessionDescription,
[ReceiveVoiceOpcodes.Speaking]: speaking,
[ReceiveVoiceOpcodes.HeartbeatACK]: heartbeatACK,
[ReceiveVoiceOpcodes.Hello]: hello,
[ReceiveVoiceOpcodes.Resumed]: resumed,
[ReceiveVoiceOpcodes.ClientDisconnect]: clientDisconnect,
};
function hello(conn: ConnectionData, d: { heartbeat_interval: number }) {
conn.stopHeart = sendHeart(conn.ws!, d.heartbeat_interval);
conn.stopHeart = sendHeart(conn, d.heartbeat_interval);
}
function ready(conn: ConnectionData, d: any) {
@ -65,8 +46,9 @@ function ready(conn: ConnectionData, d: any) {
}
function resumed(conn: ConnectionData) {
console.log("Resumed success");
conn.context.ready = true;
conn.context.reconnect = 0;
setSpeaking(conn, true);
}
function sessionDescription(conn: ConnectionData, d: any) {
@ -75,6 +57,7 @@ function sessionDescription(conn: ConnectionData, d: any) {
conn.secret = new Uint8Array(secret);
conn.mode = mode;
conn.context.ready = true;
conn.context.reconnect = 0;
setSpeaking(conn, true);
}
@ -83,8 +66,12 @@ function speaking(conn: ConnectionData, d: any) {
const ssrc = Number(d.ssrc);
setUserSSRC(conn, user_id, ssrc);
}
function heartbeatACK() {}
function heartbeatACK(conn: ConnectionData, d: number) {
if (conn.context.lastHeart === d) {
conn.context.missedHeart = 0;
conn.context.lastHeart = undefined;
}
}
function clientDisconnect() {}
function undocumented() {}

View File

@ -1,29 +1,37 @@
import { VoiceOpcodes } from "../../deps.ts";
import { setDriftlessTimeout } from "npm:driftless";
import { ConnectionData } from "../mod.ts";
function createHeartBeat(time: number) {
return JSON.stringify({
op: VoiceOpcodes.Heartbeat,
d: time,
});
function sendHeartBeat(conn: ConnectionData) {
if (conn.context.lastHeart !== undefined) {
conn.context.missedHeart++;
}
conn.context.lastHeart = Date.now();
conn.ws?.send(
JSON.stringify({
op: VoiceOpcodes.Heartbeat,
d: conn.context.lastHeart,
})
);
}
export function sendHeart(ws: WebSocket, interval: number) {
export function sendHeart(conn: ConnectionData, interval: number) {
let last = Date.now();
let timestamp = 0;
const heartbeat = createHeartBeat(timestamp);
if (ws.readyState === WebSocket.OPEN) {
ws.send(heartbeat);
if (conn.ws?.readyState === WebSocket.OPEN) {
sendHeartBeat(conn);
}
let done = false;
const repeatBeat = () => {
if (done || ws.readyState !== WebSocket.OPEN) {
if (done || conn.ws?.readyState !== WebSocket.OPEN) {
return;
}
if (conn.context.missedHeart >= 3) {
console.log("Missed too many heartbeats, attempting reconnect");
conn.ws?.close();
return;
}
timestamp += interval;
const heartbeat = createHeartBeat(timestamp);
last = Date.now();
ws.send(heartbeat);
sendHeartBeat(conn);
setDriftlessTimeout(repeatBeat, interval + (last - Date.now()));
};
setDriftlessTimeout(repeatBeat, interval + (last - Date.now()));

View File

@ -1,6 +1,7 @@
import { VoiceOpcodes } from "../../deps.ts";
import { VoiceCloseEventCodes } from "../../deps.ts";
import { ConnectionData } from "../connection-data.ts";
import { ServerVoiceOpcodes, socketHandlers } from "./handlers.ts";
import { socketHandlers } from "./handlers.ts";
import { SendVoiceOpcodes } from "./opcodes.ts";
export function connectWebSocket(
conn: ConnectionData,
@ -19,7 +20,7 @@ export function connectWebSocket(
const ws = new WebSocket(`wss://${endpoint}?v=4`);
conn.ws = ws;
const identifyRequest = JSON.stringify({
op: VoiceOpcodes.Identify,
op: SendVoiceOpcodes.Identify,
d: {
server_id: guildId.toString(),
user_id: userId.toString(),
@ -28,7 +29,7 @@ export function connectWebSocket(
},
});
const resumeRequest = JSON.stringify({
op: VoiceOpcodes.Resume,
op: SendVoiceOpcodes.Resume,
d: {
server_id: guildId.toString(),
session_id: sessionId,
@ -69,7 +70,7 @@ function handleOpen(
function handleMessage(conn: ConnectionData, ev: MessageEvent<any>) {
const data = JSON.parse(ev.data);
socketHandlers[data.op as ServerVoiceOpcodes](conn, data.d);
socketHandlers[data.op]?.(conn, data.d);
}
function handleClose(
@ -80,15 +81,19 @@ function handleClose(
) {
conn.stopHeart();
conn.context.ready = false;
if (event.code < 4000) {
console.log("The socket lost its connection for some reason");
conn.resume = true;
connectWebSocket(conn, userId, guildId);
} else if (event.code === 4014) {
conn.context.speaking = false;
} else if (event.code === 4006) {
conn.context.speaking = false;
conn.context.speaking = false;
conn.context.lastHeart = undefined;
conn.context.missedHeart = 0;
if (VoiceCloseEventCodes.Disconnect === event.code) {
console.log("Couldn't reconnect :(");
return;
}
if (conn.context.reconnect >= 3) {
return;
}
conn.context.reconnect++;
conn.resume = true;
connectWebSocket(conn, userId, guildId);
}
/**

View File

@ -0,0 +1,31 @@
import { VoiceOpcodes } from "../../deps.ts";
export enum ReceiveVoiceOpcodes {
/** Complete the websocket handshake. */
Ready = VoiceOpcodes.Ready,
/** Describe the session. */
SessionDescription = VoiceOpcodes.SessionDescription,
/** Indicate which users are speaking. */
Speaking = VoiceOpcodes.Speaking,
/** Sent to acknowledge a received client heartbeat. */
HeartbeatACK = VoiceOpcodes.HeartbeatACK,
/** Time to wait between sending heartbeats in milliseconds. */
Hello = VoiceOpcodes.Hello,
/** Acknowledge a successful session resume. */
Resumed = VoiceOpcodes.Resumed,
/** A client has disconnected from the voice channel */
ClientDisconnect = VoiceOpcodes.ClientDisconnect,
}
export enum SendVoiceOpcodes {
/** Begin a voice websocket connection. */
Identify = VoiceOpcodes.Identify,
/** Select the voice protocol. */
SelectProtocol = VoiceOpcodes.SelectProtocol,
/** Keep the websocket connection alive. */
Heartbeat = VoiceOpcodes.Heartbeat,
/** Indicate which users are speaking. */
Speaking = VoiceOpcodes.Speaking,
/** Resume a connection. */
Resume = VoiceOpcodes.Resume,
}

View File

@ -1,4 +1,4 @@
export async function* streamAsyncIterator<T>(stream: ReadableStream<T>) {
export async function* streamAsyncIterator(stream: ReadableStream) {
// Get a lock on the stream
const reader = stream.getReader();

View File

@ -1,6 +1,6 @@
import { ytdl } from "https://deno.land/x/ytdl_core@v0.1.1/mod.ts";
import { Bot } from "https://deno.land/x/discordeno@17.0.1/bot.ts";
import { connectToVoiceChannel } from "https://deno.land/x/discordeno@17.0.1/helpers/guilds/mod.ts";
import { Bot } from "https://deno.land/x/discordeno@18.0.1/bot.ts";
import { connectToVoiceChannel } from "https://deno.land/x/discordeno@18.0.1/helpers/guilds/mod.ts";
import { configs } from "./configs.ts";
import { getChannel, getChannels, getGuild, type BigString, type Embed, type InteractionCallbackData, type InteractionResponse } from "./deps.ts";