Harden queue lifecycle and auth/update check concurrency (v4.1.8)
This commit is contained in:
parent
8b508177b5
commit
4ade0a46ac
4
typescript-version/package-lock.json
generated
4
typescript-version/package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "twitch-vod-manager",
|
||||
"version": "4.1.7",
|
||||
"version": "4.1.8",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "twitch-vod-manager",
|
||||
"version": "4.1.7",
|
||||
"version": "4.1.8",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^1.6.0",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "twitch-vod-manager",
|
||||
"version": "4.1.7",
|
||||
"version": "4.1.8",
|
||||
"description": "Twitch VOD Manager - Download Twitch VODs easily",
|
||||
"main": "dist/main.js",
|
||||
"author": "xRangerDE",
|
||||
|
||||
@ -457,7 +457,7 @@
|
||||
|
||||
<div class="settings-card">
|
||||
<h3 id="updateTitle">Updates</h3>
|
||||
<p id="versionInfo" style="margin-bottom: 10px; color: var(--text-secondary);">Version: v4.1.7</p>
|
||||
<p id="versionInfo" style="margin-bottom: 10px; color: var(--text-secondary);">Version: v4.1.8</p>
|
||||
<button class="btn-secondary" id="checkUpdateBtn" onclick="checkUpdate()">Nach Updates suchen</button>
|
||||
</div>
|
||||
|
||||
@ -502,7 +502,7 @@
|
||||
<div class="status-dot" id="statusDot"></div>
|
||||
<span id="statusText">Nicht verbunden</span>
|
||||
</div>
|
||||
<span id="versionText">v4.1.7</span>
|
||||
<span id="versionText">v4.1.8</span>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@ -8,7 +8,7 @@ import { autoUpdater } from 'electron-updater';
|
||||
// ==========================================
|
||||
// CONFIG & CONSTANTS
|
||||
// ==========================================
|
||||
const APP_VERSION = '4.1.7';
|
||||
const APP_VERSION = '4.1.8';
|
||||
const UPDATE_CHECK_URL = 'http://24-music.de/version.json';
|
||||
|
||||
// Paths
|
||||
@ -396,6 +396,7 @@ let autoUpdateStartupTimer: NodeJS.Timeout | null = null;
|
||||
let autoUpdateCheckInProgress = false;
|
||||
let autoUpdateReadyToInstall = false;
|
||||
let lastAutoUpdateCheckAt = 0;
|
||||
let twitchLoginInFlight: Promise<boolean> | null = null;
|
||||
|
||||
// ==========================================
|
||||
// TOOL PATHS
|
||||
@ -1447,6 +1448,10 @@ function emitQueueUpdated(force = false): void {
|
||||
mainWindow?.webContents.send('queue-updated', downloadQueue);
|
||||
}
|
||||
|
||||
function hasQueueItemId(id: string): boolean {
|
||||
return downloadQueue.some((item) => item.id === id);
|
||||
}
|
||||
|
||||
function getRuntimeMetricsSnapshot(): RuntimeMetricsSnapshot {
|
||||
return {
|
||||
...runtimeMetrics,
|
||||
@ -1644,6 +1649,22 @@ async function twitchLogin(): Promise<boolean> {
|
||||
}
|
||||
}
|
||||
|
||||
function requestTwitchLogin(): Promise<boolean> {
|
||||
if (twitchLoginInFlight) {
|
||||
return twitchLoginInFlight;
|
||||
}
|
||||
|
||||
let loginPromise: Promise<boolean>;
|
||||
loginPromise = twitchLogin().finally(() => {
|
||||
if (twitchLoginInFlight === loginPromise) {
|
||||
twitchLoginInFlight = null;
|
||||
}
|
||||
});
|
||||
|
||||
twitchLoginInFlight = loginPromise;
|
||||
return loginPromise;
|
||||
}
|
||||
|
||||
async function ensureTwitchAuth(forceRefresh = false): Promise<boolean> {
|
||||
if (!config.client_id || !config.client_secret) {
|
||||
accessToken = null;
|
||||
@ -1654,7 +1675,7 @@ async function ensureTwitchAuth(forceRefresh = false): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
return await twitchLogin();
|
||||
return await requestTwitchLogin();
|
||||
}
|
||||
|
||||
function normalizeLogin(input: string): string {
|
||||
@ -2749,6 +2770,14 @@ async function processQueue(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasQueueItemId(item.id)) {
|
||||
appendDebugLog('queue-item-finished-removed', { itemId: item.id });
|
||||
runtimeMetrics.activeItemId = null;
|
||||
runtimeMetrics.activeItemTitle = null;
|
||||
activeQueueItemId = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
const wasPaused = pauseRequested || (finalResult.error || '').includes('pausiert');
|
||||
item.status = finalResult.success ? 'completed' : (wasPaused ? 'paused' : 'error');
|
||||
item.progress = finalResult.success ? 100 : item.progress;
|
||||
@ -2813,6 +2842,13 @@ function createWindow(): void {
|
||||
|
||||
mainWindow.loadFile(path.join(__dirname, '../src/index.html'));
|
||||
|
||||
mainWindow.webContents.on('did-finish-load', () => {
|
||||
emitQueueUpdated(true);
|
||||
if (isDownloading) {
|
||||
mainWindow?.webContents.send('download-started');
|
||||
}
|
||||
});
|
||||
|
||||
mainWindow.on('closed', () => {
|
||||
mainWindow = null;
|
||||
});
|
||||
@ -2961,6 +2997,7 @@ ipcMain.handle('save-config', (_, newConfig: Partial<Config>) => {
|
||||
|
||||
if (config.client_id !== previousClientId || config.client_secret !== previousClientSecret) {
|
||||
accessToken = null;
|
||||
twitchLoginInFlight = null;
|
||||
}
|
||||
|
||||
if (config.metadata_cache_minutes !== previousCacheMinutes) {
|
||||
@ -3021,6 +3058,9 @@ ipcMain.handle('remove-from-queue', (_, id: string) => {
|
||||
if (currentProcess) {
|
||||
currentProcess.kill();
|
||||
}
|
||||
activeQueueItemId = null;
|
||||
runtimeMetrics.activeItemId = null;
|
||||
runtimeMetrics.activeItemTitle = null;
|
||||
appendDebugLog('queue-item-removed-active-cancelled', { id });
|
||||
}
|
||||
|
||||
|
||||
2
typescript-version/src/renderer-globals.d.ts
vendored
2
typescript-version/src/renderer-globals.d.ts
vendored
@ -178,7 +178,7 @@ interface ApiBridge {
|
||||
cutVideo(inputFile: string, startTime: number, endTime: number): Promise<{ success: boolean; outputFile: string | null }>;
|
||||
mergeVideos(inputFiles: string[], outputFile: string): Promise<{ success: boolean; outputFile: string | null }>;
|
||||
getVersion(): Promise<string>;
|
||||
checkUpdate(): Promise<{ checking?: boolean; error?: boolean }>;
|
||||
checkUpdate(): Promise<{ checking?: boolean; error?: boolean; skipped?: 'ready-to-install' | 'in-progress' | 'throttled' | 'error' | string }>;
|
||||
downloadUpdate(): Promise<{ downloading?: boolean; error?: boolean }>;
|
||||
installUpdate(): Promise<void>;
|
||||
openExternal(url: string): Promise<void>;
|
||||
|
||||
@ -3,7 +3,16 @@ async function checkUpdateSilent(): Promise<void> {
|
||||
}
|
||||
|
||||
async function checkUpdate(): Promise<void> {
|
||||
await window.api.checkUpdate();
|
||||
const result = await window.api.checkUpdate();
|
||||
|
||||
if (result?.error) {
|
||||
return;
|
||||
}
|
||||
|
||||
const skippedReason = result?.skipped;
|
||||
if (skippedReason === 'in-progress' || skippedReason === 'ready-to-install' || skippedReason === 'throttled') {
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if (byId('updateBanner').style.display !== 'flex') {
|
||||
|
||||
@ -10,6 +10,8 @@ async function init(): Promise<void> {
|
||||
config.language = language;
|
||||
const initialQueue = await window.api.getQueue();
|
||||
queue = Array.isArray(initialQueue) ? initialQueue : [];
|
||||
downloading = await window.api.isDownloading();
|
||||
markQueueActivity();
|
||||
const version = await window.api.getVersion();
|
||||
|
||||
byId('versionText').textContent = `v${version}`;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user