release: Multi-Hoster-Upload 2.1.10
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).
|
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
|
## Features
|
||||||
|
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "multi-hoster-uploader",
|
"name": "multi-hoster-uploader",
|
||||||
"version": "2.1.9",
|
"version": "2.1.10",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "multi-hoster-uploader",
|
"name": "multi-hoster-uploader",
|
||||||
"version": "2.1.9",
|
"version": "2.1.10",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": "^3.6.0",
|
"chokidar": "^3.6.0",
|
||||||
"undici": "^7.29.0",
|
"undici": "^7.29.0",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "multi-hoster-uploader",
|
"name": "multi-hoster-uploader",
|
||||||
"version": "2.1.9",
|
"version": "2.1.10",
|
||||||
"description": "Upload files to doodstream, voe, vidmoly, byse simultaneously",
|
"description": "Upload files to doodstream, voe, vidmoly, byse simultaneously",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+6
-9
@@ -1563,17 +1563,9 @@ function suppressPreviewKeysStillSelected(keys) {
|
|||||||
// Build preview jobs from selected files x selected hosters (before upload starts)
|
// Build preview jobs from selected files x selected hosters (before upload starts)
|
||||||
function buildQueuePreview() {
|
function buildQueuePreview() {
|
||||||
const hosters = getSelectedHosters();
|
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');
|
queueJobs = queueJobs.filter(j => j.status !== 'preview');
|
||||||
|
|
||||||
// Build a Set for fast existence checks
|
if (hosters.length > 0) {
|
||||||
const existingKeys = new Set();
|
const existingKeys = new Set();
|
||||||
for (const j of queueJobs) {
|
for (const j of queueJobs) {
|
||||||
existingKeys.add(`${j.file}|${j.hoster}`);
|
existingKeys.add(`${j.file}|${j.hoster}`);
|
||||||
@@ -1595,8 +1587,13 @@ function buildQueuePreview() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rebuildJobIndex();
|
rebuildJobIndex();
|
||||||
|
_queueStatsCache = null;
|
||||||
renderQueueTable();
|
renderQueueTable();
|
||||||
|
updateStatusBar();
|
||||||
|
updateStatsPanel();
|
||||||
persistQueueStateSoon();
|
persistQueueStateSoon();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-6
@@ -2518,13 +2518,18 @@ select.hs-input { max-width: none; width: auto; min-width: 140px; }
|
|||||||
}
|
}
|
||||||
|
|
||||||
.view-sidebar-badge {
|
.view-sidebar-badge {
|
||||||
|
height: 22px;
|
||||||
min-width: 22px;
|
min-width: 22px;
|
||||||
padding: 2px 6px;
|
display: inline-grid;
|
||||||
|
place-items: center;
|
||||||
|
padding: 0 7px;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
background: rgba(255, 255, 255, .055);
|
background: var(--accent);
|
||||||
color: var(--text-dim);
|
color: var(--accent-ink);
|
||||||
font-size: 10px;
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
|
line-height: 1;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3768,11 +3773,14 @@ input[type="checkbox"] {
|
|||||||
display: grid;
|
display: grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
#upload-view .view-sidebar-copy,
|
#upload-view .view-sidebar-copy {
|
||||||
#upload-view .view-sidebar-badge {
|
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#upload-view .view-sidebar-badge {
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
|
||||||
#upload-view .upload-sidebar-lower {
|
#upload-view .upload-sidebar-lower {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
|||||||
+35
-2
@@ -313,7 +313,7 @@ setTimeout(async () => {
|
|||||||
await wc.debugger.sendCommand('Input.dispatchDragEvent', { type: 'drop', ...populatedDropPoint, data: dragData });
|
await wc.debugger.sendCommand('Input.dispatchDragEvent', { type: 'drop', ...populatedDropPoint, data: dragData });
|
||||||
await waitUntil(() => wc.executeJavaScript('document.getElementById("hosterModal")?.style.display === "flex"'));
|
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) }))()');
|
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 {
|
} finally {
|
||||||
if (wc.debugger.isAttached()) wc.debugger.detach();
|
if (wc.debugger.isAttached()) wc.debugger.detach();
|
||||||
try { fs.unlinkSync(populatedDropFixture); } catch {}
|
try { fs.unlinkSync(populatedDropFixture); } catch {}
|
||||||
@@ -337,7 +337,7 @@ setTimeout(async () => {
|
|||||||
await wc.debugger.sendCommand('Input.dispatchDragEvent', { type: 'drop', ...duplicateDropPoint, data: dragData });
|
await wc.debugger.sendCommand('Input.dispatchDragEvent', { type: 'drop', ...duplicateDropPoint, data: dragData });
|
||||||
await new Promise(resolve => setTimeout(resolve, 100));
|
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") }))()');
|
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 {
|
} finally {
|
||||||
if (wc.debugger.isAttached()) wc.debugger.detach();
|
if (wc.debugger.isAttached()) wc.debugger.detach();
|
||||||
try { fs.unlinkSync(duplicateDropFixture); } catch {}
|
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("|")');
|
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|--:--');
|
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("|"); })()');
|
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');
|
check('Upload header exposes the visible speed sparkline', speedSparklineState === 'true|false|true|true|0 B/s');
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user