115 lines
3.7 KiB
TypeScript
115 lines
3.7 KiB
TypeScript
import { AudioPlayer, AudioPlayerStatus, AudioResource, createAudioPlayer, VoiceConnection } from "@discordjs/voice";
|
|
import { Voice } from "../db/generated/prisma/enums";
|
|
import TTSTypecastModel from "../tts/typecast";
|
|
import TTSPapagoModel from "../tts/papago";
|
|
import { Guild } from "discord.js";
|
|
import { getOrCreateVoiceConnection } from "./util";
|
|
import TTSModelBase from "../tts";
|
|
import { DiscordUserProfile } from "../db/generated/prisma/client";
|
|
import { nyaize } from "../utils/nyaize";
|
|
import { OutputHandler } from "../utils/outputHandler";
|
|
|
|
export async function createVoiceBuffer(voice: Voice, text: string): Promise<Buffer> {
|
|
if (voice == "TypeCast") {
|
|
const content = TTSTypecastModel.instance.ttsify(text);
|
|
|
|
if (!content.length)
|
|
throw new Error("Empty content");
|
|
|
|
return await TTSTypecastModel.instance.getMemcachedVoice(
|
|
TTSTypecastModel.instance.createRequestId(content)
|
|
);
|
|
} else if (voice == "Papago") {
|
|
const content = TTSTypecastModel.instance.ttsify(text);
|
|
if (!content.length)
|
|
throw new Error("Empty content");
|
|
|
|
return await TTSPapagoModel.instance.getMemcachedVoice(
|
|
TTSPapagoModel.instance.createRequestId(content)
|
|
);
|
|
} else {
|
|
throw new Error(`Unknown voice type: ${voice}`);
|
|
}
|
|
}
|
|
|
|
class VoiceQueue {
|
|
private connection: VoiceConnection;
|
|
private list: AudioResource[];
|
|
private currentPlayer?: AudioPlayer;
|
|
constructor(connection: VoiceConnection) {
|
|
this.connection = connection;
|
|
this.list = [];
|
|
}
|
|
public static fromConnection(connection: VoiceConnection): VoiceQueue {
|
|
return (connection as any).queue ??= new VoiceQueue(connection);
|
|
}
|
|
private play() {
|
|
if (!this.list[0]) return;
|
|
const player = this.currentPlayer = createAudioPlayer();
|
|
this.connection.subscribe(player);
|
|
player.once(AudioPlayerStatus.Idle, this.next.bind(this));
|
|
player.play(this.list[0]);
|
|
}
|
|
public enqueue(resource: AudioResource) {
|
|
this.list.push(resource);
|
|
if (this.list.length == 1) {
|
|
this.play();
|
|
return;
|
|
}
|
|
}
|
|
public next() {
|
|
this.currentPlayer?.removeAllListeners(AudioPlayerStatus.Idle);
|
|
this.list.shift();
|
|
this.play();
|
|
}
|
|
public hasNext(): boolean {
|
|
return !!this.list[0];
|
|
}
|
|
}
|
|
|
|
export async function playVoice(
|
|
guild: Guild,
|
|
profile: DiscordUserProfile,
|
|
voice: Voice,
|
|
text: string
|
|
) {
|
|
if (profile.nya)
|
|
text = nyaize(text);
|
|
|
|
try {
|
|
let connection = await getOrCreateVoiceConnection(guild);
|
|
if (!connection)
|
|
throw new Error("Yaeju is not joined VoiceChat");
|
|
|
|
let voiceBuffer: Buffer;
|
|
if (voice == "TypeCast") {
|
|
if (profile.canTypecast) {
|
|
voiceBuffer = await createVoiceBuffer(voice, text);
|
|
} else {
|
|
throw new Error(`the user ${profile.userId} is can't use typecast voice`);
|
|
}
|
|
} else {
|
|
voiceBuffer = await createVoiceBuffer(voice, text);
|
|
}
|
|
|
|
VoiceQueue.fromConnection(connection).enqueue(
|
|
TTSModelBase.bufferToAudioResource(voiceBuffer)
|
|
);
|
|
} catch(err) {
|
|
OutputHandler.errorLog("[PlayVoice Error]", err);
|
|
throw new Error(err as any);
|
|
}
|
|
}
|
|
|
|
export async function skipCurrentVoice(guild: Guild): Promise<boolean> {
|
|
let connection = await getOrCreateVoiceConnection(guild);
|
|
if (!connection)
|
|
throw new Error("Yaeju is not joined VoiceChat");
|
|
|
|
const vqueue = VoiceQueue.fromConnection(connection);
|
|
if (vqueue.hasNext()) {
|
|
vqueue.next();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|