Add streamlink command fallback and improve ENOENT errors (v3.7.9)
Resolve streamlink execution more robustly by trying direct binary and Python module launchers, then return actionable error messages when streamlink is missing on server environments.
This commit is contained in:
parent
78378b9812
commit
a3c3c6d225
4
typescript-version/package-lock.json
generated
4
typescript-version/package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "twitch-vod-manager",
|
"name": "twitch-vod-manager",
|
||||||
"version": "3.7.8",
|
"version": "3.7.9",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "twitch-vod-manager",
|
"name": "twitch-vod-manager",
|
||||||
"version": "3.7.8",
|
"version": "3.7.9",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.6.0",
|
"axios": "^1.6.0",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "twitch-vod-manager",
|
"name": "twitch-vod-manager",
|
||||||
"version": "3.7.8",
|
"version": "3.7.9",
|
||||||
"description": "Twitch VOD Manager - Download Twitch VODs easily",
|
"description": "Twitch VOD Manager - Download Twitch VODs easily",
|
||||||
"main": "dist/main.js",
|
"main": "dist/main.js",
|
||||||
"author": "xRangerDE",
|
"author": "xRangerDE",
|
||||||
|
|||||||
@ -335,7 +335,7 @@
|
|||||||
|
|
||||||
<div class="settings-card">
|
<div class="settings-card">
|
||||||
<h3>Updates</h3>
|
<h3>Updates</h3>
|
||||||
<p id="versionInfo" style="margin-bottom: 10px; color: var(--text-secondary);">Version: v3.7.8</p>
|
<p id="versionInfo" style="margin-bottom: 10px; color: var(--text-secondary);">Version: v3.7.9</p>
|
||||||
<button class="btn-secondary" onclick="checkUpdate()">Nach Updates suchen</button>
|
<button class="btn-secondary" onclick="checkUpdate()">Nach Updates suchen</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -346,7 +346,7 @@
|
|||||||
<div class="status-dot" id="statusDot"></div>
|
<div class="status-dot" id="statusDot"></div>
|
||||||
<span id="statusText">Nicht verbunden</span>
|
<span id="statusText">Nicht verbunden</span>
|
||||||
</div>
|
</div>
|
||||||
<span id="versionText">v3.7.8</span>
|
<span id="versionText">v3.7.9</span>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import { autoUpdater } from 'electron-updater';
|
|||||||
// ==========================================
|
// ==========================================
|
||||||
// CONFIG & CONSTANTS
|
// CONFIG & CONSTANTS
|
||||||
// ==========================================
|
// ==========================================
|
||||||
const APP_VERSION = '3.7.8';
|
const APP_VERSION = '3.7.9';
|
||||||
const UPDATE_CHECK_URL = 'http://24-music.de/version.json';
|
const UPDATE_CHECK_URL = 'http://24-music.de/version.json';
|
||||||
|
|
||||||
// Paths
|
// Paths
|
||||||
@ -172,6 +172,7 @@ let currentDownloadCancelled = false;
|
|||||||
let downloadStartTime = 0;
|
let downloadStartTime = 0;
|
||||||
let downloadedBytes = 0;
|
let downloadedBytes = 0;
|
||||||
const userIdLoginCache = new Map<string, string>();
|
const userIdLoginCache = new Map<string, string>();
|
||||||
|
let streamlinkCommandCache: { command: string; prefixArgs: string[] } | null = null;
|
||||||
|
|
||||||
// ==========================================
|
// ==========================================
|
||||||
// TOOL PATHS
|
// TOOL PATHS
|
||||||
@ -201,6 +202,52 @@ function getStreamlinkPath(): string {
|
|||||||
return 'streamlink';
|
return 'streamlink';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canExecute(cmd: string): boolean {
|
||||||
|
try {
|
||||||
|
execSync(cmd, { stdio: 'ignore', windowsHide: true });
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStreamlinkCommand(): { command: string; prefixArgs: string[] } {
|
||||||
|
if (streamlinkCommandCache) {
|
||||||
|
return streamlinkCommandCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
const directPath = getStreamlinkPath();
|
||||||
|
if (directPath !== 'streamlink' || canExecute('streamlink --version')) {
|
||||||
|
streamlinkCommandCache = { command: directPath, prefixArgs: [] };
|
||||||
|
return streamlinkCommandCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
if (canExecute('py -3 -m streamlink --version')) {
|
||||||
|
streamlinkCommandCache = { command: 'py', prefixArgs: ['-3', '-m', 'streamlink'] };
|
||||||
|
return streamlinkCommandCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canExecute('python -m streamlink --version')) {
|
||||||
|
streamlinkCommandCache = { command: 'python', prefixArgs: ['-m', 'streamlink'] };
|
||||||
|
return streamlinkCommandCache;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (canExecute('python3 -m streamlink --version')) {
|
||||||
|
streamlinkCommandCache = { command: 'python3', prefixArgs: ['-m', 'streamlink'] };
|
||||||
|
return streamlinkCommandCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canExecute('python -m streamlink --version')) {
|
||||||
|
streamlinkCommandCache = { command: 'python', prefixArgs: ['-m', 'streamlink'] };
|
||||||
|
return streamlinkCommandCache;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
streamlinkCommandCache = { command: directPath, prefixArgs: [] };
|
||||||
|
return streamlinkCommandCache;
|
||||||
|
}
|
||||||
|
|
||||||
function getFFmpegPath(): string {
|
function getFFmpegPath(): string {
|
||||||
try {
|
try {
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
@ -777,8 +824,8 @@ function downloadVODPart(
|
|||||||
totalParts: number
|
totalParts: number
|
||||||
): Promise<DownloadResult> {
|
): Promise<DownloadResult> {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const streamlinkPath = getStreamlinkPath();
|
const streamlinkCmd = getStreamlinkCommand();
|
||||||
const args = [url, 'best', '-o', filename, '--force'];
|
const args = [...streamlinkCmd.prefixArgs, url, 'best', '-o', filename, '--force'];
|
||||||
let lastErrorLine = '';
|
let lastErrorLine = '';
|
||||||
|
|
||||||
if (startTime) {
|
if (startTime) {
|
||||||
@ -788,10 +835,10 @@ function downloadVODPart(
|
|||||||
args.push('--hls-duration', endTime);
|
args.push('--hls-duration', endTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Starting download:', streamlinkPath, args);
|
console.log('Starting download:', streamlinkCmd.command, args);
|
||||||
appendDebugLog('download-part-start', { itemId, streamlinkPath, filename, args });
|
appendDebugLog('download-part-start', { itemId, command: streamlinkCmd.command, filename, args });
|
||||||
|
|
||||||
const proc = spawn(streamlinkPath, args, { windowsHide: true });
|
const proc = spawn(streamlinkCmd.command, args, { windowsHide: true });
|
||||||
currentProcess = proc;
|
currentProcess = proc;
|
||||||
|
|
||||||
downloadStartTime = Date.now();
|
downloadStartTime = Date.now();
|
||||||
@ -890,8 +937,12 @@ function downloadVODPart(
|
|||||||
clearInterval(progressInterval);
|
clearInterval(progressInterval);
|
||||||
console.error('Process error:', err);
|
console.error('Process error:', err);
|
||||||
currentProcess = null;
|
currentProcess = null;
|
||||||
appendDebugLog('download-part-process-error', { itemId, error: String(err) });
|
const rawError = String(err);
|
||||||
resolve({ success: false, error: String(err) });
|
const errorMessage = rawError.includes('ENOENT')
|
||||||
|
? 'Streamlink nicht gefunden. Installiere Streamlink oder Python+streamlink (py -3 -m pip install streamlink).'
|
||||||
|
: rawError;
|
||||||
|
appendDebugLog('download-part-process-error', { itemId, error: errorMessage, rawError });
|
||||||
|
resolve({ success: false, error: errorMessage });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user