Files
BRETT/server/utils/twitch.ts
T
2026-10-03 20:07:49 +02:00

54 lines
1.9 KiB
TypeScript

import type { TwitchTarget } from '../../../shared/utils/twitch'
// Öffentliche Client-ID der Twitch-Web-App (gql.twitch.tv wird von twitch.tv selbst genutzt)
const TWITCH_CLIENT_ID = 'kimne78kx3ncx6brgo4mv6wki5h1ko'
function gqlStr(value: string): string {
return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
}
interface TwitchPreview {
image: string | null
title: string | null
}
// Echtes Twitch-Thumbnail/-Titel via öffentlicher GQL-API — og:image liefert
// bei Twitch nur das generische Logo
export async function fetchTwitchPreview(target: TwitchTarget): Promise<TwitchPreview> {
try {
const query = target.kind === 'video'
? `{ video(id: "${gqlStr(target.id)}") { previewThumbnailURL(height: 360, width: 640) title } }`
: target.kind === 'clip'
? `{ clip(slug: "${gqlStr(target.id)}") { thumbnailURL title } }`
: `{ user(login: "${gqlStr(target.id.toLowerCase())}") { displayName broadcastSettings { title } profileImageURL(width: 300) stream { previewImageURL(width: 640, height: 360) } } }`
const res = await fetch('https://gql.twitch.tv/gql', {
method: 'POST',
signal: AbortSignal.timeout(5000),
headers: {
'content-type': 'application/json',
'client-id': TWITCH_CLIENT_ID,
},
body: JSON.stringify({ query }),
})
if (!res.ok) return { image: null, title: null }
const data = (await res.json())?.data
if (!data) return { image: null, title: null }
if (target.kind === 'video') {
return { image: data.video?.previewThumbnailURL ?? null, title: data.video?.title ?? null }
}
if (target.kind === 'clip') {
return { image: data.clip?.thumbnailURL ?? null, title: data.clip?.title ?? null }
}
const u = data.user
return {
image: u?.stream?.previewImageURL ?? u?.profileImageURL ?? null,
title: u?.broadcastSettings?.title ?? u?.displayName ?? null,
}
}
catch {
return { image: null, title: null }
}
}