diff --git a/lib/config-store.js b/lib/config-store.js index 5f9b277..a11d5e7 100644 --- a/lib/config-store.js +++ b/lib/config-store.js @@ -32,6 +32,7 @@ const DEFAULTS = { parallelUploadCount: 0, // 0 = use per-hoster limits only scaleParallelUploads: false, removeFromQueueOnDone: false, + showDropTarget: false, globalMaxSpeedKbs: 0, // 0 = unlimited global speed pendingQueue: null, scramble: { diff --git a/main.js b/main.js index 4271337..ce6151c 100644 --- a/main.js +++ b/main.js @@ -13,6 +13,7 @@ const backupCrypto = require('./lib/backup-crypto'); const FolderMonitor = require('./lib/folder-monitor'); let mainWindow; +let dropTargetWindow = null; let tray = null; const configStore = new ConfigStore(app); let uploadManager = null; @@ -497,6 +498,14 @@ app.whenReady().then(() => { debugLog(`folder-monitor auto-start failed: ${err.message}`); } + // Auto-show drop target if enabled + try { + const dtConfig = configStore.load(); + if (dtConfig.globalSettings && dtConfig.globalSettings.showDropTarget) { + createDropTargetWindow(); + } + } catch {} + // Auto-check for updates after 3 seconds setTimeout(async () => { try { @@ -518,6 +527,7 @@ app.on('window-all-closed', () => { app.on('before-quit', () => { try { folderMonitor.stop(); } catch {} + destroyDropTargetWindow(); }); // --- IPC Handlers --- @@ -893,6 +903,58 @@ ipcMain.handle('get-always-on-top', () => { return false; }); +// --- Drop Target Window --- +function createDropTargetWindow() { + if (dropTargetWindow && !dropTargetWindow.isDestroyed()) return; + const { screen } = require('electron'); + const display = screen.getPrimaryDisplay(); + const { width, height } = display.workAreaSize; + dropTargetWindow = new BrowserWindow({ + width: 120, + height: 120, + x: width - 140, + y: height - 140, + frame: false, + transparent: true, + alwaysOnTop: true, + skipTaskbar: true, + resizable: false, + minimizable: false, + maximizable: false, + focusable: false, + webPreferences: { + contextIsolation: true, + nodeIntegration: false, + preload: path.join(__dirname, 'preload-drop-target.js') + } + }); + dropTargetWindow.loadFile('renderer/drop-target.html'); + dropTargetWindow.on('closed', () => { dropTargetWindow = null; }); +} + +function destroyDropTargetWindow() { + if (dropTargetWindow && !dropTargetWindow.isDestroyed()) { + dropTargetWindow.close(); + dropTargetWindow = null; + } +} + +ipcMain.handle('show-drop-target', () => { + createDropTargetWindow(); + return true; +}); + +ipcMain.handle('hide-drop-target', () => { + destroyDropTargetWindow(); + return true; +}); + +ipcMain.on('drop-target:files', (_event, paths) => { + if (mainWindow && !mainWindow.isDestroyed()) { + mainWindow.webContents.send('drop-target:files', paths); + } +}); + // --- Shutdown after finish --- let shutdownMode = 'nothing'; let shutdownTimer = null; diff --git a/package.json b/package.json index bfa3837..599d90d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "multi-hoster-uploader", - "version": "1.9.6", + "version": "1.9.7", "description": "Upload files to doodstream, voe, vidmoly, byse simultaneously", "main": "main.js", "scripts": { diff --git a/preload-drop-target.js b/preload-drop-target.js new file mode 100644 index 0000000..3d11d02 --- /dev/null +++ b/preload-drop-target.js @@ -0,0 +1,5 @@ +const { contextBridge, ipcRenderer } = require('electron'); + +contextBridge.exposeInMainWorld('dropTargetApi', { + sendFiles: (paths) => ipcRenderer.send('drop-target:files', paths) +}); diff --git a/preload.js b/preload.js index 2d67ab6..5c827fd 100644 --- a/preload.js +++ b/preload.js @@ -64,6 +64,13 @@ contextBridge.exposeInMainWorld('api', { ipcRenderer.on('folder-monitor:new-files', (_event, data) => callback(data)); }, + // Drop Target + showDropTarget: () => ipcRenderer.invoke('show-drop-target'), + hideDropTarget: () => ipcRenderer.invoke('hide-drop-target'), + onDropTargetFiles: (callback) => { + ipcRenderer.on('drop-target:files', (_event, paths) => callback(paths)); + }, + // Debug debugTestUpload: () => ipcRenderer.invoke('debug-test-upload'), debugLog: (msg) => ipcRenderer.invoke('debug-log', msg), @@ -91,5 +98,6 @@ contextBridge.exposeInMainWorld('api', { ipcRenderer.removeAllListeners('app:update-progress'); ipcRenderer.removeAllListeners('shutdown-countdown'); ipcRenderer.removeAllListeners('folder-monitor:new-files'); + ipcRenderer.removeAllListeners('drop-target:files'); } }); diff --git a/renderer/app.js b/renderer/app.js index 56c6137..f48ae30 100644 --- a/renderer/app.js +++ b/renderer/app.js @@ -104,6 +104,11 @@ async function init() { } }); + // Drop target window: files dropped on the small floating window + window.api.onDropTargetFiles((paths) => { + addPathsToQueue(paths); + }); + window.api.debugLog('init complete, all listeners registered'); // Restore always-on-top state @@ -414,39 +419,10 @@ function setupDragDrop() { const dropZone = document.getElementById('dropZone'); // Allow drop on the entire upload view const uploadView = document.getElementById('upload-view'); - const dropOverlay = document.getElementById('dropOverlay'); - - // Window-level drag overlay - let dragCounter = 0; - window.addEventListener('dragenter', (e) => { - e.preventDefault(); - if (!e.dataTransfer.types.includes('Files')) return; - dragCounter++; - dropOverlay.classList.add('visible'); - }); - window.addEventListener('dragleave', (e) => { - e.preventDefault(); - dragCounter--; - if (dragCounter <= 0) { dragCounter = 0; dropOverlay.classList.remove('visible'); } - }); - window.addEventListener('dragover', (e) => { e.preventDefault(); }); - let _dropHandled = false; - window.addEventListener('drop', (e) => { - e.preventDefault(); - dragCounter = 0; - dropOverlay.classList.remove('visible'); - // Process files dropped anywhere (fallback if no specific handler caught it) - if (!_dropHandled && e.dataTransfer.files.length > 0) { - addDroppedFiles(e.dataTransfer.files).catch(console.error); - } - _dropHandled = false; - }); - dropZone.addEventListener('dragover', (e) => { e.preventDefault(); e.stopPropagation(); dropZone.classList.add('drag-over'); }); dropZone.addEventListener('dragleave', (e) => { e.preventDefault(); dropZone.classList.remove('drag-over'); }); dropZone.addEventListener('drop', (e) => { e.preventDefault(); e.stopPropagation(); dropZone.classList.remove('drag-over'); - _dropHandled = true; addDroppedFiles(e.dataTransfer.files).catch(console.error); }); dropZone.addEventListener('click', () => pickFiles()); @@ -456,7 +432,6 @@ function setupDragDrop() { uploadView.addEventListener('drop', (e) => { e.preventDefault(); if (e.target.closest('.drop-zone')) return; // handled above - _dropHandled = true; addDroppedFiles(e.dataTransfer.files).catch(console.error); }); } @@ -1665,8 +1640,8 @@ function updateStatusBar() { const uploadedSize = Math.max(0, stats.totalSize - stats.remainingSize); document.getElementById('sbTotal').textContent = `${formatSize(uploadedSize)} / ${formatSize(stats.totalSize)}`; document.getElementById('sbEta').textContent = `ETA ${etaSeconds > 0 ? formatTime(etaSeconds) : '--:--'}`; - document.getElementById('sbConnections').textContent = `Aktive Verbindungen: ${lastUploadStats.activeJobs || 0}`; - document.getElementById('sbQueueCount').textContent = `Gesamt: ${stats.total}`; + document.getElementById('sbConnections').textContent = `Connections: ${lastUploadStats.activeJobs || 0}`; + document.getElementById('sbQueueCount').textContent = `Total: ${stats.total}`; document.getElementById('sbRemainingCount').textContent = `Remaining: ${stats.remaining}`; document.getElementById('sbInProgressCount').textContent = `In Progress: ${stats.inProgress}`; document.getElementById('sbDoneCount').textContent = `Done: ${stats.done}`; @@ -1780,6 +1755,10 @@ function renderSettings() { +
+ + +
Updates
@@ -2056,6 +2035,7 @@ async function saveSettings(options = {}) { parallelUploadCount: Math.max(0, Math.min(100, parseInt(document.getElementById('parallelUploadCountInput')?.value || '0', 10) || 0)), scaleParallelUploads: !!document.getElementById('scaleParallelUploadsInput')?.checked, removeFromQueueOnDone: !!document.getElementById('removeFromQueueOnDoneInput')?.checked, + showDropTarget: !!document.getElementById('showDropTargetInput')?.checked, globalMaxSpeedKbs: Math.max(0, Math.round((parseFloat(document.getElementById('globalMaxSpeedMbsInput')?.value || '0') || 0) * 1024)), folderMonitor: { enabled: !!document.getElementById('fmEnabledInput')?.checked, @@ -2080,6 +2060,13 @@ async function saveSettings(options = {}) { } } + // Drop target window + const dtCheckbox = document.getElementById('showDropTargetInput'); + if (dtCheckbox) { + if (dtCheckbox.checked) await window.api.showDropTarget(); + else await window.api.hideDropTarget(); + } + for (const name of HOSTERS) { const hs = { ...(hosterSettings[name] || {}) }; document.querySelectorAll(`.hs-input[data-hoster="${name}"]`).forEach(input => { diff --git a/renderer/drop-target.html b/renderer/drop-target.html new file mode 100644 index 0000000..5c8216e --- /dev/null +++ b/renderer/drop-target.html @@ -0,0 +1,68 @@ + + + + + + + +
+
+
+
+ + + diff --git a/renderer/index.html b/renderer/index.html index b23afc7..19ff2d8 100644 --- a/renderer/index.html +++ b/renderer/index.html @@ -7,13 +7,6 @@ -
-
-
+
-

Dateien hier ablegen

-
-
-