feat: add ElevenLabs Firebase token emulation
This commit is contained in:
+1
-1
@@ -8,7 +8,7 @@ services:
|
|||||||
DISCORD_TOKEN:
|
DISCORD_TOKEN:
|
||||||
TTS_TIKTOK_SESSIONID:
|
TTS_TIKTOK_SESSIONID:
|
||||||
TTS_ELEVENLABS_KEY:
|
TTS_ELEVENLABS_KEY:
|
||||||
TTS_ELEVENLABS_TOKEN:
|
TTS_ELEVENLABS_REFRESHTOKEN:
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- ./db.sqlite:/app/db.sqlite
|
- ./db.sqlite:/app/db.sqlite
|
||||||
@@ -5,6 +5,10 @@ import * as https from 'https';
|
|||||||
|
|
||||||
const ELEVENLABS_API_ENDPOINT = 'api.elevenlabs.io';
|
const ELEVENLABS_API_ENDPOINT = 'api.elevenlabs.io';
|
||||||
|
|
||||||
|
const FIREBASE_API_KEY = "AIzaSyBSsRE_1Os04-bxpd5JTLIniy3UK4OqKys";
|
||||||
|
const FIREBASE_URL = `https://securetoken.googleapis.com/v1/token?key=${FIREBASE_API_KEY}`;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TO-DO: Implement previous text
|
TO-DO: Implement previous text
|
||||||
*/
|
*/
|
||||||
@@ -39,6 +43,12 @@ interface ElevenLabsStreamRequest {
|
|||||||
voice_settings: ElevenLabsVoiceSettings;
|
voice_settings: ElevenLabsVoiceSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface FirebaseSession {
|
||||||
|
idToken: string;
|
||||||
|
refreshToken: string;
|
||||||
|
expiresAt: number;
|
||||||
|
}
|
||||||
|
|
||||||
export class ElevenLabsTTS implements TTSModule {
|
export class ElevenLabsTTS implements TTSModule {
|
||||||
private voices: Array<ElevenLabsVoice> | undefined = undefined;
|
private voices: Array<ElevenLabsVoice> | undefined = undefined;
|
||||||
private models: Array<ElevenLabsModel> | undefined = undefined;
|
private models: Array<ElevenLabsModel> | undefined = undefined;
|
||||||
@@ -48,29 +58,38 @@ export class ElevenLabsTTS implements TTSModule {
|
|||||||
public settings: ElevenLabsVoiceSettings;
|
public settings: ElevenLabsVoiceSettings;
|
||||||
public modelId: string;
|
public modelId: string;
|
||||||
|
|
||||||
|
private session: FirebaseSession | undefined = undefined;
|
||||||
|
|
||||||
|
private initializationPromise: Promise<void> | undefined = undefined;
|
||||||
|
|
||||||
public static readonly DEFAULT_SETTINGS: ElevenLabsVoiceSettings = {
|
public static readonly DEFAULT_SETTINGS: ElevenLabsVoiceSettings = {
|
||||||
stability: 0.5,
|
stability: 0.0,
|
||||||
similarity_boost: 0.5,
|
similarity_boost: 0.5,
|
||||||
style: 0.0,
|
style: 1.0,
|
||||||
speed: 1.0,
|
speed: 1.0,
|
||||||
user_speaker_boost: true
|
user_speaker_boost: true
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
if (this.canBeUsed()) {
|
|
||||||
this.fetchVoices();
|
|
||||||
this.fetchModels();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.settings = ElevenLabsTTS.DEFAULT_SETTINGS;
|
this.settings = ElevenLabsTTS.DEFAULT_SETTINGS;
|
||||||
|
|
||||||
this.modelId = 'eleven_flash_v2_5';
|
this.modelId = 'eleven_flash_v2_5';
|
||||||
|
|
||||||
|
if (this.canBeUsed())
|
||||||
|
this.initializationPromise = this.init();
|
||||||
|
|
||||||
this.setSettings = this.setSettings.bind(this);
|
this.setSettings = this.setSettings.bind(this);
|
||||||
this.setModel = this.setModel.bind(this);
|
this.setModel = this.setModel.bind(this);
|
||||||
this.getModels = this.getModels.bind(this);
|
this.getModels = this.getModels.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async init(): Promise<void> {
|
||||||
|
await this.ensureSession();
|
||||||
|
await Promise.all([
|
||||||
|
this.fetchVoices(),
|
||||||
|
this.fetchModels()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TTSModule methods
|
TTSModule methods
|
||||||
*/
|
*/
|
||||||
@@ -79,7 +98,11 @@ export class ElevenLabsTTS implements TTSModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async generate(voice: string, text: string): Promise<TTSResponse> {
|
async generate(voice: string, text: string): Promise<TTSResponse> {
|
||||||
if (!this.voices) return {};
|
await this.initializationPromise;
|
||||||
|
await this.ensureSession();
|
||||||
|
|
||||||
|
if (!this.voices) return { error: 'no voices' };
|
||||||
|
if (!this.session) return { error: 'no session' };
|
||||||
|
|
||||||
const voiceData = this.voices.find((entry) => entry.name === voice);
|
const voiceData = this.voices.find((entry) => entry.name === voice);
|
||||||
if (!voiceData) return { error: 'Invalid voice' };
|
if (!voiceData) return { error: 'Invalid voice' };
|
||||||
@@ -100,10 +123,8 @@ export class ElevenLabsTTS implements TTSModule {
|
|||||||
'Sec-Fetch-Site': 'same-site',
|
'Sec-Fetch-Site': 'same-site',
|
||||||
'Sec-Fetch-Mode': 'cors',
|
'Sec-Fetch-Mode': 'cors',
|
||||||
'Sec-Fetch-Dest': 'empty',
|
'Sec-Fetch-Dest': 'empty',
|
||||||
host: 'api.us.elevenlabs.io',
|
host: 'api.elevenlabs.io',
|
||||||
...(config.tts_elevenlabs_token
|
Authorization: `Bearer ${this.session.idToken}`
|
||||||
? { Authorization: `Bearer ${config.tts_elevenlabs_token}` }
|
|
||||||
: { 'xi-api-key': config.tts_elevenlabs_key })
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -132,7 +153,7 @@ export class ElevenLabsTTS implements TTSModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
canBeUsed(): boolean {
|
canBeUsed(): boolean {
|
||||||
return config.tts_elevenlabs_key != undefined;
|
return config.tts_elevenlabs_refreshtoken != undefined && config.tts_elevenlabs_key != undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -221,6 +242,35 @@ export class ElevenLabsTTS implements TTSModule {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async ensureSession(): Promise<void> {
|
||||||
|
if (this.session && Date.now() < this.session.expiresAt - 300000)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const refreshToken = this.session?.refreshToken || config.tts_elevenlabs_refreshtoken;
|
||||||
|
if (!refreshToken) throw new Error("No refresh token available");
|
||||||
|
|
||||||
|
const response = await fetch(FIREBASE_URL, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
'Referer': 'https://elevenlabs.io/',
|
||||||
|
'Origin': 'https://elevenlabs.io'
|
||||||
|
},
|
||||||
|
body: new URLSearchParams({
|
||||||
|
grant_type: 'refresh_token',
|
||||||
|
refresh_token: refreshToken,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) throw new Error(`Auth Refresh Failed: ${await response.text()}`);
|
||||||
|
const data = await response.json();
|
||||||
|
this.session = {
|
||||||
|
idToken: data.id_token,
|
||||||
|
refreshToken: data.refresh_token,
|
||||||
|
expiresAt: Date.now() + (parseInt(data.expires_in) * 1000)
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new ElevenLabsTTS();
|
export default new ElevenLabsTTS();
|
||||||
|
|||||||
+2
-2
@@ -7,7 +7,7 @@ export interface Config {
|
|||||||
tts_default_voice: string | undefined;
|
tts_default_voice: string | undefined;
|
||||||
|
|
||||||
tts_elevenlabs_key: string | undefined;
|
tts_elevenlabs_key: string | undefined;
|
||||||
tts_elevenlabs_token: string | undefined;
|
tts_elevenlabs_refreshtoken: string | undefined;
|
||||||
tts_tiktok_sessionid: string | undefined;
|
tts_tiktok_sessionid: string | undefined;
|
||||||
|
|
||||||
steam_webapi_key: string | undefined;
|
steam_webapi_key: string | undefined;
|
||||||
@@ -31,7 +31,7 @@ function loadConfig(): Config {
|
|||||||
tts_default_mode: process.env.DEFAULT_TTS_MODE,
|
tts_default_mode: process.env.DEFAULT_TTS_MODE,
|
||||||
tts_default_voice: process.env.DEFAULT_TTS_VOICE,
|
tts_default_voice: process.env.DEFAULT_TTS_VOICE,
|
||||||
tts_elevenlabs_key: process.env.TTS_ELEVENLABS_KEY,
|
tts_elevenlabs_key: process.env.TTS_ELEVENLABS_KEY,
|
||||||
tts_elevenlabs_token: process.env.TTS_ELEVENLABS_TOKEN,
|
tts_elevenlabs_refreshtoken: process.env.TTS_ELEVENLABS_REFRESHTOKEN,
|
||||||
steam_webapi_key: process.env.STEAM_WEBAPI_KEY,
|
steam_webapi_key: process.env.STEAM_WEBAPI_KEY,
|
||||||
aws_access_id: process.env.AWS_ACCESS_ID,
|
aws_access_id: process.env.AWS_ACCESS_ID,
|
||||||
aws_access_key: process.env.AWS_ACCESS_KEY,
|
aws_access_key: process.env.AWS_ACCESS_KEY,
|
||||||
|
|||||||
Reference in New Issue
Block a user