release: Multi-Hoster-Upload 2.1.10

This commit is contained in:
Sucukdeluxe
2026-08-11 18:32:05 +02:00
parent e8eea5b5de
commit 191fe92d44
6 changed files with 76 additions and 38 deletions
+1 -1
View File
@@ -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.9. Use the release page for the executables and the full English changelog.
The latest public release is version 2.1.10. Use the release page for the executables and the full English changelog.
## Features
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "multi-hoster-uploader",
"version": "2.1.9",
"version": "2.1.10",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "multi-hoster-uploader",
"version": "2.1.9",
"version": "2.1.10",
"dependencies": {
"chokidar": "^3.6.0",
"undici": "^7.29.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "multi-hoster-uploader",
"version": "2.1.9",
"version": "2.1.10",
"description": "Upload files to doodstream, voe, vidmoly, byse simultaneously",
"main": "main.js",
"scripts": {
+6 -9
View File
@@ -1563,17 +1563,9 @@ function suppressPreviewKeysStillSelected(keys) {
// Build preview jobs from selected files x selected hosters (before upload starts)
function buildQueuePreview() {
const hosters = getSelectedHosters();
if (hosters.length === 0) {
queueJobs = queueJobs.filter(j => j.status !== 'preview');
rebuildJobIndex();
renderQueueTable();
persistQueueStateSoon();
return;
}
// Remove old preview jobs
queueJobs = queueJobs.filter(j => j.status !== 'preview');
// Build a Set for fast existence checks
if (hosters.length > 0) {
const existingKeys = new Set();
for (const j of queueJobs) {
existingKeys.add(`${j.file}|${j.hoster}`);
@@ -1595,8 +1587,13 @@ function buildQueuePreview() {
}
}
}
}
rebuildJobIndex();
_queueStatsCache = null;
renderQueueTable();
updateStatusBar();
updateStatsPanel();
persistQueueStateSoon();
}
+14 -6
View File
@@ -2518,13 +2518,18 @@ select.hs-input { max-width: none; width: auto; min-width: 140px; }
}
.view-sidebar-badge {
height: 22px;
min-width: 22px;
padding: 2px 6px;
display: inline-grid;
place-items: center;
padding: 0 7px;
border-radius: 999px;
background: rgba(255, 255, 255, .055);
color: var(--text-dim);
font-size: 10px;
background: var(--accent);
color: var(--accent-ink);
font-size: 12px;
font-weight: 700;
font-variant-numeric: tabular-nums;
line-height: 1;
text-align: center;
}
@@ -3768,11 +3773,14 @@ input[type="checkbox"] {
display: grid;
}
#upload-view .view-sidebar-copy,
#upload-view .view-sidebar-badge {
#upload-view .view-sidebar-copy {
display: block;
}
#upload-view .view-sidebar-badge {
display: grid;
}
#upload-view .upload-sidebar-lower {
min-width: 0;
gap: 10px;
+35 -2
View File
@@ -313,7 +313,7 @@ setTimeout(async () => {
await wc.debugger.sendCommand('Input.dispatchDragEvent', { type: 'drop', ...populatedDropPoint, data: dragData });
await waitUntil(() => wc.executeJavaScript('document.getElementById("hosterModal")?.style.display === "flex"'));
populatedDropState = await wc.executeJavaScript('(() => ({ modal: document.getElementById("hosterModal")?.style.display, paths: _pendingFiles.map(file => file.path) }))()');
await wc.executeJavaScript('cancelHosterModal(); selectedFiles = []; queueJobs = []; rebuildJobIndex(); updateUploadView(); renderQueueTable();');
await wc.executeJavaScript('cancelHosterModal(); selectedFiles = []; queueJobs = []; rebuildJobIndex(); _queueStatsCache = null; updateUploadView(); renderQueueTable(); updateStatusBar();');
} finally {
if (wc.debugger.isAttached()) wc.debugger.detach();
try { fs.unlinkSync(populatedDropFixture); } catch {}
@@ -337,7 +337,7 @@ setTimeout(async () => {
await wc.debugger.sendCommand('Input.dispatchDragEvent', { type: 'drop', ...duplicateDropPoint, data: dragData });
await new Promise(resolve => setTimeout(resolve, 100));
duplicateDropState = await wc.executeJavaScript('(() => ({ modal: document.getElementById("hosterModal")?.style.display, pending: _pendingFiles.length, toast: document.getElementById("copyToast")?.textContent, shown: document.getElementById("copyToast")?.classList.contains("show") }))()');
await wc.executeJavaScript('selectedFiles = []; queueJobs = []; rebuildJobIndex(); updateUploadView(); renderQueueTable();');
await wc.executeJavaScript('selectedFiles = []; queueJobs = []; rebuildJobIndex(); _queueStatsCache = null; updateUploadView(); renderQueueTable(); updateStatusBar();');
} finally {
if (wc.debugger.isAttached()) wc.debugger.detach();
try { fs.unlinkSync(duplicateDropFixture); } catch {}
@@ -374,6 +374,39 @@ setTimeout(async () => {
const initialTelemetryValues = await wc.executeJavaScript('[...document.querySelectorAll("#uploadTelemetry .upload-telemetry-value")].map(el => el.getAttribute("aria-label") || el.textContent.trim()).join("|")');
check('Upload telemetry starts with stable empty values', initialTelemetryValues === '0|0|0|0|0|0|0 B/s|--:--');
const previewQueueCounts = await wc.executeJavaScript(\`(() => {
queueJobs = [{ id: 'existing-done', file: 'C:/ui/existing-done.bin', fileName: 'existing-done.bin', hoster: 'doodstream.com', status: 'done', bytesUploaded: 1024, bytesTotal: 1024, speedKbs: 0, elapsed: 1, remaining: 0, progress: 1 }];
selectedFiles = [];
selectedUploadHosters = ['doodstream.com'];
_sessionDoneCount = 1;
_sessionErrorCount = 0;
_queueStatsCache = null;
updateStatusBar();
selectedFiles = [1, 2, 3].map(index => ({ path: 'C:/ui/new-' + index + '.bin', name: 'new-' + index + '.bin', size: 2048 }));
buildQueuePreview();
const result = {
queue: queueJobs.map(job => job.status).join('|'),
sidebar: ['All', 'Active', 'Waiting', 'Done', 'Error'].map(key => document.getElementById('uploadSidebar' + key + 'Count')?.textContent).join('|'),
telemetry: ['Total', 'Remaining', 'Running'].map(key => document.getElementById('uploadTelemetry' + key)?.getAttribute('aria-label')).join('|')
};
queueJobs = [];
selectedFiles = [];
selectedUploadHosters = [];
_sessionDoneCount = 0;
_queueStatsCache = null;
updateStatusBar();
return result;
})()\`);
check('Adding preview files immediately refreshes upload counts before the batch starts', previewQueueCounts.queue === 'done|preview|preview|preview' && previewQueueCounts.sidebar === '4|0|3|1|0' && previewQueueCounts.telemetry === '4|3|0');
const sidebarBadgeStyle = await wc.executeJavaScript(\`(() => {
const badge = document.getElementById('uploadSidebarAllCount');
const style = getComputedStyle(badge);
const rect = badge.getBoundingClientRect();
return { background: style.backgroundColor, color: style.color, fontSize: parseFloat(style.fontSize), fontWeight: parseInt(style.fontWeight, 10), width: rect.width, height: rect.height };
})()\`);
check('Sidebar count badges use a larger light-blue high-contrast pill', sidebarBadgeStyle.background === 'rgb(186, 208, 252)' && sidebarBadgeStyle.color === 'rgb(16, 23, 35)' && sidebarBadgeStyle.fontSize >= 12 && sidebarBadgeStyle.fontWeight >= 600 && sidebarBadgeStyle.width >= 22 && sidebarBadgeStyle.height >= 22);
const speedSparklineState = await wc.executeJavaScript('(() => { const widget = document.getElementById("uploadSpeedSparkline"); const canvas = document.getElementById("uploadSpeedCanvas"); const rect = canvas?.getBoundingClientRect(); return [Boolean(widget), widget?.classList.contains("is-hidden"), rect?.width > 0, rect?.height > 0, document.getElementById("uploadSpeedValue")?.textContent].join("|"); })()');
check('Upload header exposes the visible speed sparkline', speedSparklineState === 'true|false|true|true|0 B/s');