Fix VOD loading by reconnecting Twitch auth on demand

Refresh Twitch authentication automatically when selecting streamers and retry Helix calls after 401 responses so valid streamers no longer show empty VOD states due to stale connection state.
This commit is contained in:
xRangerDE 2026-02-13 09:31:08 +01:00
parent fb29ee77d3
commit f3bfed9b56
2 changed files with 67 additions and 14 deletions

View File

@ -1628,9 +1628,12 @@
document.getElementById('pageTitle').textContent = name; document.getElementById('pageTitle').textContent = name;
if (!isConnected) { if (!isConnected) {
updateStatus('Nicht verbunden', false); await connect();
if (!isConnected) {
document.getElementById('vodGrid').innerHTML = '<div class="empty-state"><h3>Nicht verbunden</h3><p>Bitte Twitch API Daten in den Einstellungen prufen.</p></div>';
return; return;
} }
}
document.getElementById('vodGrid').innerHTML = '<div class="empty-state"><p>Lade VODs...</p></div>'; document.getElementById('vodGrid').innerHTML = '<div class="empty-state"><p>Lade VODs...</p></div>';
@ -1887,8 +1890,8 @@
// Settings // Settings
async function saveSettings() { async function saveSettings() {
const clientId = document.getElementById('clientId').value; const clientId = document.getElementById('clientId').value.trim();
const clientSecret = document.getElementById('clientSecret').value; const clientSecret = document.getElementById('clientSecret').value.trim();
const downloadPath = document.getElementById('downloadPath').value; const downloadPath = document.getElementById('downloadPath').value;
const downloadMode = document.getElementById('downloadMode').value; const downloadMode = document.getElementById('downloadMode').value;
const partMinutes = parseInt(document.getElementById('partMinutes').value); const partMinutes = parseInt(document.getElementById('partMinutes').value);

View File

@ -291,11 +291,19 @@ async function twitchLogin(): Promise<boolean> {
} }
} }
async function getUserId(username: string): Promise<string | null> { async function ensureTwitchAuth(forceRefresh = false): Promise<boolean> {
if (!accessToken) return null; if (!forceRefresh && accessToken) {
return true;
}
try { return await twitchLogin();
const response = await axios.get('https://api.twitch.tv/helix/users', { }
async function getUserId(username: string): Promise<string | null> {
if (!(await ensureTwitchAuth())) return null;
const fetchUser = async () => {
return await axios.get('https://api.twitch.tv/helix/users', {
params: { login: username }, params: { login: username },
headers: { headers: {
'Client-ID': config.client_id, 'Client-ID': config.client_id,
@ -303,18 +311,32 @@ async function getUserId(username: string): Promise<string | null> {
}, },
timeout: API_TIMEOUT timeout: API_TIMEOUT
}); });
};
try {
const response = await fetchUser();
return response.data.data[0]?.id || null; return response.data.data[0]?.id || null;
} catch (e) { } catch (e) {
if (axios.isAxiosError(e) && e.response?.status === 401 && (await ensureTwitchAuth(true))) {
try {
const retryResponse = await fetchUser();
return retryResponse.data.data[0]?.id || null;
} catch (retryError) {
console.error('Error getting user after relogin:', retryError);
return null;
}
}
console.error('Error getting user:', e); console.error('Error getting user:', e);
return null; return null;
} }
} }
async function getVODs(userId: string): Promise<VOD[]> { async function getVODs(userId: string): Promise<VOD[]> {
if (!accessToken) return []; if (!(await ensureTwitchAuth())) return [];
try { const fetchVods = async () => {
const response = await axios.get('https://api.twitch.tv/helix/videos', { return await axios.get('https://api.twitch.tv/helix/videos', {
params: { params: {
user_id: userId, user_id: userId,
type: 'archive', type: 'archive',
@ -326,18 +348,32 @@ async function getVODs(userId: string): Promise<VOD[]> {
}, },
timeout: API_TIMEOUT timeout: API_TIMEOUT
}); });
};
try {
const response = await fetchVods();
return response.data.data; return response.data.data;
} catch (e) { } catch (e) {
if (axios.isAxiosError(e) && e.response?.status === 401 && (await ensureTwitchAuth(true))) {
try {
const retryResponse = await fetchVods();
return retryResponse.data.data;
} catch (retryError) {
console.error('Error getting VODs after relogin:', retryError);
return [];
}
}
console.error('Error getting VODs:', e); console.error('Error getting VODs:', e);
return []; return [];
} }
} }
async function getClipInfo(clipId: string): Promise<any | null> { async function getClipInfo(clipId: string): Promise<any | null> {
if (!accessToken) return null; if (!(await ensureTwitchAuth())) return null;
try { const fetchClip = async () => {
const response = await axios.get('https://api.twitch.tv/helix/clips', { return await axios.get('https://api.twitch.tv/helix/clips', {
params: { id: clipId }, params: { id: clipId },
headers: { headers: {
'Client-ID': config.client_id, 'Client-ID': config.client_id,
@ -345,8 +381,22 @@ async function getClipInfo(clipId: string): Promise<any | null> {
}, },
timeout: API_TIMEOUT timeout: API_TIMEOUT
}); });
};
try {
const response = await fetchClip();
return response.data.data[0] || null; return response.data.data[0] || null;
} catch (e) { } catch (e) {
if (axios.isAxiosError(e) && e.response?.status === 401 && (await ensureTwitchAuth(true))) {
try {
const retryResponse = await fetchClip();
return retryResponse.data.data[0] || null;
} catch (retryError) {
console.error('Error getting clip after relogin:', retryError);
return null;
}
}
console.error('Error getting clip:', e); console.error('Error getting clip:', e);
return null; return null;
} }