release: Multi-Hoster-Upload 2.1.3
This commit is contained in:
@@ -8,7 +8,7 @@ Multi-Hoster-Upload is a Windows desktop application for sending file batches to
|
||||
|
||||
Download the current Setup or Portable build from the [latest GitHub release](https://github.com/Sucukdeluxe/Multi-Hoster-Upload/releases/latest).
|
||||
|
||||
The latest public release is version 2.1.2. Use the release page for the executables and the full English changelog.
|
||||
The latest public release is version 2.1.3. Use the release page for the executables and the full English changelog.
|
||||
|
||||
## Features
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
function configureStartupRenderer(app) {
|
||||
function configureStartupRenderer(app, env = process.env, platform = process.platform) {
|
||||
const sessionName = String(env && env.SESSIONNAME || '');
|
||||
if (platform === 'win32' && /^RDP-/i.test(sessionName)) app.disableHardwareAcceleration();
|
||||
return app;
|
||||
}
|
||||
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "multi-hoster-uploader",
|
||||
"version": "2.1.2",
|
||||
"version": "2.1.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "multi-hoster-uploader",
|
||||
"version": "2.1.2",
|
||||
"version": "2.1.3",
|
||||
"dependencies": {
|
||||
"chokidar": "^3.6.0",
|
||||
"undici": "^7.29.0",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "multi-hoster-uploader",
|
||||
"version": "2.1.2",
|
||||
"version": "2.1.3",
|
||||
"description": "Upload files to doodstream, voe, vidmoly, byse simultaneously",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
||||
+37
-9
@@ -6545,6 +6545,28 @@ function setupListeners() {
|
||||
}
|
||||
|
||||
// --- Update UI ---
|
||||
function _formatUpdateReleaseNotes(value) {
|
||||
const output = [];
|
||||
let pendingBlank = false;
|
||||
for (const rawLine of String(value || '').replace(/\r\n?/g, '\n').split('\n')) {
|
||||
let line = rawLine.trim();
|
||||
if (!line) {
|
||||
if (output.length > 0) pendingBlank = true;
|
||||
continue;
|
||||
}
|
||||
line = line
|
||||
.replace(/^#{1,6}\s+/, '')
|
||||
.replace(/^[-*+]\s+/, '• ')
|
||||
.replace(/\*\*([^*]+)\*\*/g, '$1')
|
||||
.replace(/`([^`]+)`/g, '$1')
|
||||
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1');
|
||||
if (pendingBlank && output.at(-1) !== '') output.push('');
|
||||
output.push(line);
|
||||
pendingBlank = false;
|
||||
}
|
||||
return output.join('\n');
|
||||
}
|
||||
|
||||
function showUpdateBanner(info) {
|
||||
if (!info) return;
|
||||
_knownUpdateInfo = { ...info, available: true };
|
||||
@@ -6561,9 +6583,12 @@ function showUpdateBanner(info) {
|
||||
const notesBody = document.getElementById('updateReleaseNotesBody');
|
||||
const installButton = document.getElementById('installUpdateBtn');
|
||||
if (title) title.textContent = 'Eine neue Version ist verfügbar';
|
||||
if (message) message.textContent = `Update v${version} verfügbar`;
|
||||
if (message) {
|
||||
message.textContent = `Update v${version} verfügbar`;
|
||||
message.hidden = false;
|
||||
}
|
||||
if (notes && notesBody) {
|
||||
const releaseNotes = String(info.releaseNotes || '').trim();
|
||||
const releaseNotes = _formatUpdateReleaseNotes(info.releaseNotes);
|
||||
notesBody.textContent = releaseNotes.length > 2400 ? `${releaseNotes.slice(0, 2399)}…` : releaseNotes;
|
||||
notes.hidden = !releaseNotes;
|
||||
}
|
||||
@@ -6585,38 +6610,41 @@ function handleUpdateProgress(data) {
|
||||
_updateInstallBusy = true;
|
||||
_setUpdateDialogBusy(true);
|
||||
_setUpdateProgress(0, 'Download 0%');
|
||||
if (message) message.textContent = 'Download wird vorbereitet…';
|
||||
if (message) message.hidden = true;
|
||||
if (button) button.textContent = 'Download 0%';
|
||||
} else if (progress.stage === 'downloading') {
|
||||
const percent = Math.max(0, Math.min(100, Math.round(Number(progress.percent) || 0)));
|
||||
_updateInstallBusy = true;
|
||||
_setUpdateDialogBusy(true);
|
||||
_setUpdateProgress(percent, `Download ${percent}%`);
|
||||
if (message) message.textContent = `Update wird heruntergeladen… ${percent}%`;
|
||||
if (message) message.hidden = true;
|
||||
if (button) button.textContent = `Download ${percent}%`;
|
||||
} else if (progress.stage === 'verifying') {
|
||||
_updateInstallBusy = true;
|
||||
_setUpdateDialogBusy(true);
|
||||
_setUpdateProgress(100, 'Prüfen…');
|
||||
if (message) message.textContent = 'Download wird geprüft…';
|
||||
if (message) message.hidden = true;
|
||||
if (button) button.textContent = 'Prüfen…';
|
||||
} else if (progress.stage === 'prepared') {
|
||||
_updateInstallBusy = true;
|
||||
_setUpdateDialogBusy(true);
|
||||
_setUpdateProgress(100, 'Neustart…');
|
||||
if (message) message.textContent = 'Update ist bereit. Einstellungen werden gespeichert…';
|
||||
if (message) message.hidden = true;
|
||||
if (button) button.textContent = 'Neustart…';
|
||||
} else if (progress.stage === 'launching' || progress.stage === 'done') {
|
||||
_updateInstallBusy = true;
|
||||
_setUpdateDialogBusy(true);
|
||||
_setUpdateProgress(100, 'Neustart…');
|
||||
if (message) message.textContent = progress.stage === 'done' ? 'Update installiert. Die Anwendung startet neu…' : 'Update wird installiert. Die Anwendung startet neu…';
|
||||
if (message) message.hidden = true;
|
||||
if (button) button.textContent = 'Neustart…';
|
||||
} else if (progress.stage === 'error') {
|
||||
_updateInstallBusy = false;
|
||||
_setUpdateDialogBusy(false);
|
||||
_setUpdateProgress(0, 'Update fehlgeschlagen');
|
||||
if (message) message.textContent = `Update fehlgeschlagen: ${String(progress.error || 'Unbekannter Fehler').slice(0, 400)}`;
|
||||
if (message) {
|
||||
message.hidden = false;
|
||||
message.textContent = `Update fehlgeschlagen: ${String(progress.error || 'Unbekannter Fehler').slice(0, 400)}`;
|
||||
}
|
||||
if (button) {
|
||||
button.disabled = false;
|
||||
button.textContent = 'Wiederholen';
|
||||
@@ -6761,7 +6789,7 @@ async function installKnownUpdate() {
|
||||
_setUpdateProgress(0, 'Download 0%');
|
||||
const message = document.getElementById('updateMessage');
|
||||
const button = document.getElementById('installUpdateBtn');
|
||||
if (message) message.textContent = 'Download wird vorbereitet…';
|
||||
if (message) message.hidden = true;
|
||||
if (button) button.textContent = 'Download 0%';
|
||||
try {
|
||||
await persistQueueStateNow();
|
||||
|
||||
@@ -2286,7 +2286,6 @@ select.hs-input { max-width: none; width: auto; min-width: 140px; }
|
||||
color: var(--text-muted);
|
||||
font-size: 11px;
|
||||
line-height: 1.55;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.update-release-notes-title {
|
||||
|
||||
+15
-4
@@ -10,8 +10,9 @@ module.exports = async function afterPack(context) {
|
||||
}
|
||||
|
||||
const productFilename = context.packager?.appInfo?.productFilename;
|
||||
if (!productFilename) {
|
||||
console.warn(" rcedit: skipped - productFilename not available");
|
||||
const version = context.packager?.appInfo?.version;
|
||||
if (!productFilename || !version) {
|
||||
console.warn(" rcedit: skipped - application metadata not available");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -24,8 +25,18 @@ module.exports = async function afterPack(context) {
|
||||
console.warn(" rcedit: skipped - app_icon.ico not found");
|
||||
return;
|
||||
}
|
||||
console.log(` rcedit: patching icon -> ${exePath}`);
|
||||
await rcedit(exePath, { icon: iconPath });
|
||||
console.log(` rcedit: branding executable -> ${exePath}`);
|
||||
await rcedit(exePath, {
|
||||
icon: iconPath,
|
||||
"file-version": version,
|
||||
"product-version": version,
|
||||
"version-string": {
|
||||
FileDescription: "Multi Hoster Uploader",
|
||||
InternalName: productFilename,
|
||||
OriginalFilename: `${productFilename}.exe`,
|
||||
ProductName: "Multi Hoster Uploader"
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn(` rcedit: failed - ${String(error)}`);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const Module = require('node:module');
|
||||
const path = require('node:path');
|
||||
const packageJson = require('../package.json');
|
||||
|
||||
test('packages every Electron preload referenced by the main process', () => {
|
||||
@@ -8,6 +9,39 @@ test('packages every Electron preload referenced by the main process', () => {
|
||||
assert.ok(packageJson.build.files.includes('preload-drop-target.js'));
|
||||
});
|
||||
|
||||
test('afterPack brands the executable metadata shown by Windows', async () => {
|
||||
let editCall = null;
|
||||
const originalLoad = Module._load;
|
||||
const afterPackPath = require.resolve('../scripts/afterPack.cjs');
|
||||
delete require.cache[afterPackPath];
|
||||
Module._load = function (request, parent, isMain) {
|
||||
if (request === 'rcedit') {
|
||||
return async (exePath, options) => { editCall = { exePath, options }; };
|
||||
}
|
||||
return originalLoad.call(this, request, parent, isMain);
|
||||
};
|
||||
try {
|
||||
const afterPack = require(afterPackPath);
|
||||
await afterPack({
|
||||
appOutDir: 'C:\\release',
|
||||
packager: { appInfo: { productFilename: 'Multi-Hoster-Upload', version: '9.8.7' } }
|
||||
});
|
||||
} finally {
|
||||
Module._load = originalLoad;
|
||||
delete require.cache[afterPackPath];
|
||||
}
|
||||
|
||||
assert.equal(editCall.exePath, path.join('C:\\release', 'Multi-Hoster-Upload.exe'));
|
||||
assert.equal(editCall.options['file-version'], '9.8.7');
|
||||
assert.equal(editCall.options['product-version'], '9.8.7');
|
||||
assert.deepEqual(editCall.options['version-string'], {
|
||||
FileDescription: 'Multi Hoster Uploader',
|
||||
InternalName: 'Multi-Hoster-Upload',
|
||||
OriginalFilename: 'Multi-Hoster-Upload.exe',
|
||||
ProductName: 'Multi Hoster Uploader'
|
||||
});
|
||||
});
|
||||
|
||||
test('close readiness is signaled only after the renderer explicitly finishes initialization', () => {
|
||||
const listeners = new Map();
|
||||
const sent = [];
|
||||
|
||||
@@ -29,12 +29,18 @@ class TestBrowserWindow extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
test('configureStartupRenderer leaves hardware acceleration enabled', () => {
|
||||
test('configureStartupRenderer leaves hardware acceleration enabled for a local Windows session', () => {
|
||||
let calls = 0;
|
||||
configureStartupRenderer({ disableHardwareAcceleration() { calls++; } });
|
||||
configureStartupRenderer({ disableHardwareAcceleration() { calls++; } }, { SESSIONNAME: 'Console' }, 'win32');
|
||||
assert.equal(calls, 0);
|
||||
});
|
||||
|
||||
test('configureStartupRenderer disables hardware acceleration for a Windows Remote Desktop session', () => {
|
||||
let calls = 0;
|
||||
configureStartupRenderer({ disableHardwareAcceleration() { calls++; } }, { SESSIONNAME: 'RDP-Tcp#12' }, 'win32');
|
||||
assert.equal(calls, 1);
|
||||
});
|
||||
|
||||
test('createStartupWindow forces the main window to start hidden', () => {
|
||||
const startup = createStartupWindow(TestBrowserWindow, { width: 1100, show: true });
|
||||
|
||||
|
||||
+6
-3
@@ -1423,7 +1423,7 @@ setTimeout(async () => {
|
||||
check('Sidebar indicator stays aligned at the minimum window size', queueProgressVisibility.minimum.sidebarIndicatorAligned);
|
||||
check('Minimum window keeps the settings header compact', compactSettingsHeader <= 58);
|
||||
|
||||
const updateOverlayState = await wc.executeJavaScript('_knownUpdateInfo = { available: true, remoteVersion: "9.9.9" }; _syncHeaderUpdateState(); document.getElementById("headerUpdateBtn").focus(); showUpdateBanner({ remoteVersion: "9.9.9", releaseNotes: "Added live language switching.\\\\nImproved settings layout." }); (() => { const overlay = document.getElementById("updateBanner"); const dialog = overlay?.querySelector(".update-dialog"); const button = document.getElementById("headerUpdateBtn"); return [overlay?.classList.contains("update-overlay"), overlay?.style.display, dialog?.getAttribute("role"), dialog?.getAttribute("aria-modal"), button?.hidden, getComputedStyle(button).display].join("|"); })()');
|
||||
const updateOverlayState = await wc.executeJavaScript('_knownUpdateInfo = { available: true, remoteVersion: "9.9.9" }; _syncHeaderUpdateState(); document.getElementById("headerUpdateBtn").focus(); showUpdateBanner({ remoteVersion: "9.9.9", releaseNotes: "\\\\n\\\\n\\\\n## New in this version\\\\n\\\\n\\\\n### Menus and navigation\\\\n\\\\n- Added live language switching.\\\\n- Improved settings layout.\\\\n\\\\n\\\\n" }); (() => { const overlay = document.getElementById("updateBanner"); const dialog = overlay?.querySelector(".update-dialog"); const button = document.getElementById("headerUpdateBtn"); return [overlay?.classList.contains("update-overlay"), overlay?.style.display, dialog?.getAttribute("role"), dialog?.getAttribute("aria-modal"), button?.hidden, getComputedStyle(button).display].join("|"); })()');
|
||||
check('Available update opens an accessible update dialog', updateOverlayState === 'true|flex|dialog|true|false|flex');
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
@@ -1450,8 +1450,8 @@ setTimeout(async () => {
|
||||
|
||||
const updateDialogActions = await wc.executeJavaScript('[document.getElementById("dismissUpdateBtn")?.textContent?.trim(), document.getElementById("installUpdateBtn")?.textContent?.trim()].join("|")');
|
||||
check('Update dialog offers cancel and install actions', updateDialogActions === 'Abbrechen|Jetzt installieren');
|
||||
const updateDialogChangelog = await wc.executeJavaScript('document.getElementById("updateReleaseNotes")?.hidden + "|" + document.querySelector(".update-release-notes-title")?.textContent?.trim() + "|" + document.getElementById("updateReleaseNotesBody")?.textContent');
|
||||
check('Update dialog shows the GitHub changelog with real line breaks', updateDialogChangelog === 'false|Changelog|Added live language switching.\\nImproved settings layout.');
|
||||
const updateDialogChangelog = await wc.executeJavaScript('(() => { const title = document.querySelector(".update-release-notes-title"); const body = document.getElementById("updateReleaseNotesBody"); const titleRect = title?.getBoundingClientRect(); const bodyRect = body?.getBoundingClientRect(); return { hidden: document.getElementById("updateReleaseNotes")?.hidden, title: title?.textContent?.trim(), body: body?.textContent, gap: bodyRect && titleRect ? bodyRect.top - titleRect.bottom : null }; })()');
|
||||
check('Update dialog renders a compact normalized changelog', updateDialogChangelog.hidden === false && updateDialogChangelog.title === 'Changelog' && updateDialogChangelog.body === 'New in this version\\n\\nMenus and navigation\\n\\n• Added live language switching.\\n• Improved settings layout.' && updateDialogChangelog.gap <= 10);
|
||||
|
||||
const updateHeaderHint = await wc.executeJavaScript('(() => { const button = document.getElementById("headerUpdateBtn"); return [button?.textContent?.trim(), button?.getAttribute("aria-label"), button?.dataset.tooltip].join("|"); })()');
|
||||
check('Available update gives the header action a matching hint', updateHeaderHint === 'Update verfügbar|Update v9.9.9 verfügbar. Klicken zum Installieren.|Update v9.9.9 verfügbar. Klicken zum Installieren.');
|
||||
@@ -1475,12 +1475,15 @@ setTimeout(async () => {
|
||||
closeDisabled: document.getElementById('updateCloseBtn').disabled,
|
||||
dismissDisabled: document.getElementById('dismissUpdateBtn').disabled,
|
||||
headerHidden: header.hidden,
|
||||
messageHidden: document.getElementById('updateMessage').hidden,
|
||||
messageText: document.getElementById('updateMessage').textContent,
|
||||
progressLabel: progress.getAttribute('aria-label'),
|
||||
progressText: progress.getAttribute('aria-valuetext')
|
||||
};
|
||||
})()\`);
|
||||
check('Busy update keeps its progress dialog open', busyUpdateState.display === 'flex' && busyUpdateState.hidden === 'false' && busyUpdateState.closeDisabled === true && busyUpdateState.dismissDisabled === true && busyUpdateState.headerHidden === false);
|
||||
check('Update progress exposes an accessible live value', busyUpdateState.progressLabel === 'Update-Fortschritt' && busyUpdateState.progressText === 'Download 50%');
|
||||
check('Busy update shows progress only below the bar', busyUpdateState.messageHidden === true && busyUpdateState.messageText === 'Update v9.9.9 verfügbar');
|
||||
|
||||
const updateErrorRecovery = await wc.executeJavaScript('handleUpdateProgress({ stage: "error", error: "Netzwerkfehler" }); document.getElementById("dismissUpdateBtn").click(); document.getElementById("updateBanner").style.display + "|" + document.getElementById("updateCloseBtn").disabled + "|" + document.getElementById("dismissUpdateBtn").disabled + "|" + document.getElementById("headerUpdateBtn").hidden');
|
||||
check('Update errors restore all close actions', updateErrorRecovery === 'none|false|false|false');
|
||||
|
||||
Reference in New Issue
Block a user