diff --git a/typescript-version/package-lock.json b/typescript-version/package-lock.json
index d5c4242..7db1956 100644
--- a/typescript-version/package-lock.json
+++ b/typescript-version/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "twitch-vod-manager",
- "version": "3.8.0",
+ "version": "3.8.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "twitch-vod-manager",
- "version": "3.8.0",
+ "version": "3.8.1",
"license": "MIT",
"dependencies": {
"axios": "^1.6.0",
diff --git a/typescript-version/package.json b/typescript-version/package.json
index 0b5304d..e7a1757 100644
--- a/typescript-version/package.json
+++ b/typescript-version/package.json
@@ -1,6 +1,6 @@
{
"name": "twitch-vod-manager",
- "version": "3.8.0",
+ "version": "3.8.1",
"description": "Twitch VOD Manager - Download Twitch VODs easily",
"main": "dist/main.js",
"author": "xRangerDE",
diff --git a/typescript-version/src/index.html b/typescript-version/src/index.html
index 2d01a93..250dc9b 100644
--- a/typescript-version/src/index.html
+++ b/typescript-version/src/index.html
@@ -335,7 +335,7 @@
Updates
-
Version: v3.8.0
+
Version: v3.8.1
@@ -346,7 +346,7 @@
Nicht verbunden
- v3.8.0
+ v3.8.1
diff --git a/typescript-version/src/main.ts b/typescript-version/src/main.ts
index d41c133..a662bfb 100644
--- a/typescript-version/src/main.ts
+++ b/typescript-version/src/main.ts
@@ -1,14 +1,14 @@
import { app, BrowserWindow, ipcMain, dialog, shell } from 'electron';
import * as path from 'path';
import * as fs from 'fs';
-import { spawn, ChildProcess, execSync, exec, execFileSync, spawnSync } from 'child_process';
+import { spawn, ChildProcess, execSync, exec, spawnSync } from 'child_process';
import axios from 'axios';
import { autoUpdater } from 'electron-updater';
// ==========================================
// CONFIG & CONSTANTS
// ==========================================
-const APP_VERSION = '3.8.0';
+const APP_VERSION = '3.8.1';
const UPDATE_CHECK_URL = 'http://24-music.de/version.json';
// Paths
@@ -273,15 +273,36 @@ async function downloadFile(url: string, destinationPath: string): Promise {
try {
fs.mkdirSync(destinationDir, { recursive: true });
- execFileSync('powershell', [
- '-NoProfile',
- '-ExecutionPolicy', 'Bypass',
- '-Command',
- `Expand-Archive -Path '${zipPath.replace(/'/g, "''")}' -DestinationPath '${destinationDir.replace(/'/g, "''")}' -Force`
- ], { windowsHide: true, stdio: 'ignore' });
+
+ const command = `Expand-Archive -Path '${zipPath.replace(/'/g, "''")}' -DestinationPath '${destinationDir.replace(/'/g, "''")}' -Force`;
+
+ await new Promise((resolve, reject) => {
+ const proc = spawn('powershell', [
+ '-NoProfile',
+ '-ExecutionPolicy', 'Bypass',
+ '-Command',
+ command
+ ], { windowsHide: true });
+
+ let stderr = '';
+ proc.stderr?.on('data', (data) => {
+ stderr += data.toString();
+ });
+
+ proc.on('close', (code) => {
+ if (code === 0) {
+ resolve();
+ } else {
+ reject(new Error(`Expand-Archive exit code ${code}: ${stderr.trim()}`));
+ }
+ });
+
+ proc.on('error', (err) => reject(err));
+ });
+
return true;
} catch (e) {
appendDebugLog('extract-zip-failed', { zipPath, destinationDir, error: String(e) });
@@ -327,7 +348,7 @@ async function ensureStreamlinkInstalled(): Promise {
fs.rmSync(TOOLS_STREAMLINK_DIR, { recursive: true, force: true });
fs.mkdirSync(TOOLS_STREAMLINK_DIR, { recursive: true });
- const extractOk = extractZip(zipPath, TOOLS_STREAMLINK_DIR);
+ const extractOk = await extractZip(zipPath, TOOLS_STREAMLINK_DIR);
try { fs.unlinkSync(zipPath); } catch { }
if (!extractOk) return false;
@@ -368,7 +389,7 @@ async function ensureFfmpegInstalled(): Promise {
fs.rmSync(TOOLS_FFMPEG_DIR, { recursive: true, force: true });
fs.mkdirSync(TOOLS_FFMPEG_DIR, { recursive: true });
- const extractOk = extractZip(zipPath, TOOLS_FFMPEG_DIR);
+ const extractOk = await extractZip(zipPath, TOOLS_FFMPEG_DIR);
try { fs.unlinkSync(zipPath); } catch { }
if (!extractOk) return false;
@@ -1653,12 +1674,7 @@ ipcMain.handle('save-video-dialog', async (_, defaultName: string) => {
app.whenReady().then(() => {
refreshBundledToolPaths();
createWindow();
-
- void (async () => {
- const streamlinkOk = await ensureStreamlinkInstalled();
- const ffmpegOk = await ensureFfmpegInstalled();
- appendDebugLog('startup-tools-check', { streamlinkOk, ffmpegOk });
- })();
+ appendDebugLog('startup-tools-check-skipped', 'Deferred to first use');
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
diff --git a/typescript-version/src/renderer.ts b/typescript-version/src/renderer.ts
index e33e9fe..a80b095 100644
--- a/typescript-version/src/renderer.ts
+++ b/typescript-version/src/renderer.ts
@@ -21,7 +21,7 @@ async function init(): Promise {
updateDownloadButtonState();
window.api.onQueueUpdated((q: QueueItem[]) => {
- queue = Array.isArray(q) ? q : [];
+ queue = mergeQueueState(Array.isArray(q) ? q : []);
renderQueue();
});
@@ -82,6 +82,33 @@ async function init(): Promise {
}, 2000);
}
+function mergeQueueState(nextQueue: QueueItem[]): QueueItem[] {
+ const prevById = new Map(queue.map((item) => [item.id, item]));
+
+ return nextQueue.map((item) => {
+ const prev = prevById.get(item.id);
+ if (!prev) {
+ return item;
+ }
+
+ if (item.status !== 'downloading') {
+ return item;
+ }
+
+ return {
+ ...item,
+ progress: item.progress > 0 ? item.progress : prev.progress,
+ speed: item.speed || prev.speed,
+ eta: item.eta || prev.eta,
+ currentPart: item.currentPart || prev.currentPart,
+ totalParts: item.totalParts || prev.totalParts,
+ downloadedBytes: item.downloadedBytes || prev.downloadedBytes,
+ totalBytes: item.totalBytes || prev.totalBytes,
+ progressStatus: item.progressStatus || prev.progressStatus
+ };
+ });
+}
+
function updateDownloadButtonState(): void {
const btn = byId('btnStart');
btn.textContent = downloading ? 'Stoppen' : 'Start';
@@ -90,7 +117,7 @@ function updateDownloadButtonState(): void {
async function syncQueueAndDownloadState(): Promise {
const latestQueue = await window.api.getQueue();
- queue = Array.isArray(latestQueue) ? latestQueue : [];
+ queue = mergeQueueState(Array.isArray(latestQueue) ? latestQueue : []);
renderQueue();
const backendDownloading = await window.api.isDownloading();