From e0138c41647c75376f0e0b62b6be4ec8cac6caf5 Mon Sep 17 00:00:00 2001 From: localhost Date: Fri, 22 May 2026 16:28:41 +0200 Subject: [PATCH] forgot a file --- utils/innertube-pool.ts | 111 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 utils/innertube-pool.ts diff --git a/utils/innertube-pool.ts b/utils/innertube-pool.ts new file mode 100644 index 0000000..1423e1f --- /dev/null +++ b/utils/innertube-pool.ts @@ -0,0 +1,111 @@ +import { Innertube } from 'youtubei.js' + +interface InnertubePoolSlot { + client: Promise + busy?: boolean +} + +export interface InnertubeLease { + innertube: Innertube + refresh: () => Promise + release: () => void +} + +export function createInnertubePool(size = 4) { + const pool = Array.from({ length: Math.max(1, size) }, () => ({ + client: Innertube.create() + })) as InnertubePoolSlot[] + const waiters: Array<(lease: InnertubeLease) => void> = [] + + function getSlot(index: number) { + const slot = pool[index] + + if (!slot) { + throw new Error(`Innertube slot ${index} not found`) + } + + return slot + } + + async function load(index: number) { + const slot = getSlot(index) + + try { + return await slot.client + } catch { + slot.client = Innertube.create() + return await slot.client + } + } + + async function refresh(index: number) { + const slot = getSlot(index) + + slot.client = Innertube.create() + return await slot.client + } + + function release(index: number) { + const next = waiters.shift() + + if (next) { + void load(index).then((innertube) => next(createLease(index, innertube))) + return + } + + delete getSlot(index).busy + } + + function createLease(index: number, innertube: Innertube): InnertubeLease { + const lease: InnertubeLease = { + innertube, + refresh: async () => { + lease.innertube = await refresh(index) + return lease.innertube + }, + release: () => release(index) + } + + return lease + } + + async function acquire() { + const index = pool.findIndex((slot) => !slot.busy) + + if (index === -1) { + return await new Promise((resolve) => { + waiters.push(resolve) + }) + } + + getSlot(index).busy = true + return createLease(index, await load(index)) + } + + async function use(callback: (innertube: Innertube) => Promise, retries = 5) { + const lease = await acquire() + + try { + for (let attempt = 0; attempt < retries; attempt++) { + try { + return await callback(lease.innertube) + } catch (error) { + if (attempt === retries - 1) { + throw error + } + + await lease.refresh() + } + } + + throw new Error('Innertube retries exhausted') + } finally { + lease.release() + } + } + + return { + acquire, + use + } +}