91 lines
2.6 KiB
JavaScript
91 lines
2.6 KiB
JavaScript
/**
|
|
* Twitch provider using Helix API
|
|
*/
|
|
const handler = {
|
|
id: 'tw',
|
|
label: 'Twitch',
|
|
/**
|
|
* @param {string} q
|
|
* @param {{ limit: number, page?: number }} opts
|
|
* @returns {Promise<Array<any>>}
|
|
*/
|
|
async search(q, opts) {
|
|
const { limit = 10, page = 1 } = opts || {};
|
|
try {
|
|
// First, get OAuth token
|
|
const authResponse = await fetch('https://id.twitch.tv/oauth2/token', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
},
|
|
body: new URLSearchParams({
|
|
client_id: process.env.TWITCH_CLIENT_ID,
|
|
client_secret: process.env.TWITCH_CLIENT_SECRET,
|
|
grant_type: 'client_credentials'
|
|
})
|
|
});
|
|
|
|
if (!authResponse.ok) {
|
|
throw new Error(`Twitch auth error: ${authResponse.status}`);
|
|
}
|
|
|
|
const { access_token } = await authResponse.json();
|
|
|
|
// Then search channels with cursor-based pagination
|
|
const perPage = Math.min(Math.max(1, Number(limit || 10)), 100);
|
|
const targetPage = Math.max(1, Number(page || 1));
|
|
let after = '';
|
|
let currentPage = 1;
|
|
let lastData = [];
|
|
|
|
while (currentPage <= targetPage) {
|
|
const params = new URLSearchParams({
|
|
query: q,
|
|
first: String(perPage)
|
|
});
|
|
if (after) params.set('after', after);
|
|
|
|
const response = await fetch(
|
|
`https://api.twitch.tv/helix/search/channels?` + params.toString(),
|
|
{
|
|
headers: {
|
|
'Client-ID': process.env.TWITCH_CLIENT_ID,
|
|
'Authorization': `Bearer ${access_token}`
|
|
}
|
|
}
|
|
);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`Twitch API error: ${response.status}`);
|
|
}
|
|
const data = await response.json();
|
|
|
|
if (currentPage === targetPage) {
|
|
lastData = Array.isArray(data.data) ? data.data : [];
|
|
break;
|
|
}
|
|
const nextCursor = data?.pagination?.cursor;
|
|
if (!nextCursor) {
|
|
lastData = [];
|
|
break;
|
|
}
|
|
after = String(nextCursor);
|
|
currentPage++;
|
|
}
|
|
|
|
return lastData.map(item => ({
|
|
title: item.title || item.display_name,
|
|
id: item.id,
|
|
url: `https://www.twitch.tv/${item.broadcaster_login}`,
|
|
thumbnail: item.thumbnail_url?.replace('{width}x{height}', '440x248'),
|
|
uploaderName: item.display_name,
|
|
type: 'stream',
|
|
isLive: item.is_live || item.started_at
|
|
}));
|
|
} catch (error) {
|
|
console.error('Twitch search error:', error);
|
|
return [];
|
|
}
|
|
}
|
|
};
|
|
export default handler; |