Compare commits
No commits in common. "00bf6f126dcc47d64db8924986391a8a27baeb02" and "29ab989cbe0933376aad99de3b3c37ab39158f52" have entirely different histories.
00bf6f126d
...
29ab989cbe
@ -86,6 +86,8 @@ const DEFAULTS = {
|
|||||||
history: []
|
history: []
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MAX_HISTORY = 100;
|
||||||
|
|
||||||
class ConfigStore {
|
class ConfigStore {
|
||||||
constructor(app) {
|
constructor(app) {
|
||||||
const dir = app && app.isPackaged
|
const dir = app && app.isPackaged
|
||||||
@ -251,6 +253,9 @@ class ConfigStore {
|
|||||||
return this._enqueueWrite(() => {
|
return this._enqueueWrite(() => {
|
||||||
const config = this.load();
|
const config = this.load();
|
||||||
config.history.push(entry);
|
config.history.push(entry);
|
||||||
|
if (config.history.length > MAX_HISTORY) {
|
||||||
|
config.history = config.history.slice(-MAX_HISTORY);
|
||||||
|
}
|
||||||
return this._atomicWrite(JSON.stringify(config, null, 2));
|
return this._atomicWrite(JSON.stringify(config, null, 2));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
128
main.js
128
main.js
@ -123,97 +123,6 @@ function appendUploadLog(hoster, link, fileName) {
|
|||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
function flattenHistoryForExport(history) {
|
|
||||||
const rows = [];
|
|
||||||
const list = Array.isArray(history) ? history : [];
|
|
||||||
|
|
||||||
for (const batch of list) {
|
|
||||||
const batchId = batch && batch.id ? String(batch.id) : '';
|
|
||||||
const rawTs = batch && batch.timestamp ? String(batch.timestamp) : '';
|
|
||||||
const parsedTs = rawTs ? new Date(rawTs) : null;
|
|
||||||
const batchTimestamp = parsedTs && !Number.isNaN(parsedTs.getTime())
|
|
||||||
? parsedTs.toISOString()
|
|
||||||
: rawTs;
|
|
||||||
const files = Array.isArray(batch && batch.files) ? batch.files : [];
|
|
||||||
|
|
||||||
for (const file of files) {
|
|
||||||
const fileName = file && file.name ? String(file.name) : '';
|
|
||||||
const filePath = file && file.path ? String(file.path) : '';
|
|
||||||
const fileSize = Number.isFinite(Number(file && file.size)) ? Number(file.size) : '';
|
|
||||||
const results = Array.isArray(file && file.results) ? file.results : [];
|
|
||||||
|
|
||||||
if (results.length === 0) {
|
|
||||||
rows.push({
|
|
||||||
batchId,
|
|
||||||
batchTimestamp,
|
|
||||||
fileName,
|
|
||||||
filePath,
|
|
||||||
fileSize,
|
|
||||||
hoster: '',
|
|
||||||
status: '',
|
|
||||||
link: '',
|
|
||||||
error: ''
|
|
||||||
});
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const result of results) {
|
|
||||||
const link = result && (result.download_url || result.embed_url || result.file_code)
|
|
||||||
? String(result.download_url || result.embed_url || result.file_code)
|
|
||||||
: '';
|
|
||||||
rows.push({
|
|
||||||
batchId,
|
|
||||||
batchTimestamp,
|
|
||||||
fileName,
|
|
||||||
filePath,
|
|
||||||
fileSize,
|
|
||||||
hoster: result && result.hoster ? String(result.hoster) : '',
|
|
||||||
status: result && result.status ? String(result.status) : '',
|
|
||||||
link,
|
|
||||||
error: result && (result.error || result.message) ? String(result.error || result.message) : ''
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rows;
|
|
||||||
}
|
|
||||||
|
|
||||||
function toCsvCell(value) {
|
|
||||||
const text = value === null || value === undefined ? '' : String(value);
|
|
||||||
if (!/[",\r\n]/.test(text)) return text;
|
|
||||||
return `"${text.replace(/"/g, '""')}"`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildHistoryCsv(rows) {
|
|
||||||
const header = [
|
|
||||||
'Batch ID',
|
|
||||||
'Batch Timestamp',
|
|
||||||
'File Name',
|
|
||||||
'File Path',
|
|
||||||
'File Size Bytes',
|
|
||||||
'Hoster',
|
|
||||||
'Status',
|
|
||||||
'Link',
|
|
||||||
'Error'
|
|
||||||
];
|
|
||||||
const lines = [header.map(toCsvCell).join(',')];
|
|
||||||
for (const row of rows) {
|
|
||||||
lines.push([
|
|
||||||
row.batchId,
|
|
||||||
row.batchTimestamp,
|
|
||||||
row.fileName,
|
|
||||||
row.filePath,
|
|
||||||
row.fileSize,
|
|
||||||
row.hoster,
|
|
||||||
row.status,
|
|
||||||
row.link,
|
|
||||||
row.error
|
|
||||||
].map(toCsvCell).join(','));
|
|
||||||
}
|
|
||||||
return `${lines.join('\n')}\n`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Multi-account helpers ---
|
// --- Multi-account helpers ---
|
||||||
function hosterAccountHasCreds(name, account) {
|
function hosterAccountHasCreds(name, account) {
|
||||||
if (!account) return false;
|
if (!account) return false;
|
||||||
@ -662,43 +571,6 @@ ipcMain.handle('get-history', () => {
|
|||||||
return configStore.loadHistory();
|
return configStore.loadHistory();
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('export-history', async (_event, format) => {
|
|
||||||
const normalizedFormat = String(format || 'csv').toLowerCase() === 'json' ? 'json' : 'csv';
|
|
||||||
const history = configStore.loadHistory();
|
|
||||||
const rows = flattenHistoryForExport(history);
|
|
||||||
const datePrefix = new Date().toISOString().slice(0, 10);
|
|
||||||
|
|
||||||
const { canceled, filePath } = await dialog.showSaveDialog(mainWindow, {
|
|
||||||
title: 'Upload-Verlauf exportieren',
|
|
||||||
defaultPath: `upload-history-${datePrefix}.${normalizedFormat}`,
|
|
||||||
filters: normalizedFormat === 'json'
|
|
||||||
? [{ name: 'JSON-Datei', extensions: ['json'] }]
|
|
||||||
: [{ name: 'CSV-Datei', extensions: ['csv'] }]
|
|
||||||
});
|
|
||||||
|
|
||||||
if (canceled || !filePath) return { ok: false, canceled: true };
|
|
||||||
|
|
||||||
if (normalizedFormat === 'json') {
|
|
||||||
const payload = {
|
|
||||||
exportedAt: new Date().toISOString(),
|
|
||||||
totalBatches: Array.isArray(history) ? history.length : 0,
|
|
||||||
totalRows: rows.length,
|
|
||||||
history
|
|
||||||
};
|
|
||||||
fs.writeFileSync(filePath, JSON.stringify(payload, null, 2), 'utf-8');
|
|
||||||
} else {
|
|
||||||
fs.writeFileSync(filePath, buildHistoryCsv(rows), 'utf-8');
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
ok: true,
|
|
||||||
path: filePath,
|
|
||||||
format: normalizedFormat,
|
|
||||||
totalBatches: Array.isArray(history) ? history.length : 0,
|
|
||||||
totalRows: rows.length
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.handle('run-health-check', async (_event, payload) => {
|
ipcMain.handle('run-health-check', async (_event, payload) => {
|
||||||
const config = configStore.load();
|
const config = configStore.load();
|
||||||
const hosters = payload && Array.isArray(payload.hosters) ? payload.hosters : [];
|
const hosters = payload && Array.isArray(payload.hosters) ? payload.hosters : [];
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "multi-hoster-uploader",
|
"name": "multi-hoster-uploader",
|
||||||
"version": "2.6.6",
|
"version": "2.6.5",
|
||||||
"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,7 +6,6 @@ contextBridge.exposeInMainWorld('api', {
|
|||||||
saveConfig: (config) => ipcRenderer.invoke('save-config', config),
|
saveConfig: (config) => ipcRenderer.invoke('save-config', config),
|
||||||
getHistory: () => ipcRenderer.invoke('get-history'),
|
getHistory: () => ipcRenderer.invoke('get-history'),
|
||||||
clearHistory: () => ipcRenderer.invoke('clear-history'),
|
clearHistory: () => ipcRenderer.invoke('clear-history'),
|
||||||
exportHistory: (format) => ipcRenderer.invoke('export-history', format),
|
|
||||||
|
|
||||||
// Hoster settings
|
// Hoster settings
|
||||||
getHosterSettings: () => ipcRenderer.invoke('get-hoster-settings'),
|
getHosterSettings: () => ipcRenderer.invoke('get-hoster-settings'),
|
||||||
|
|||||||
@ -2934,26 +2934,6 @@ async function loadHistory() {
|
|||||||
renderHistoryTable(container);
|
renderHistoryTable(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function exportHistory() {
|
|
||||||
const history = await window.api.getHistory();
|
|
||||||
if (!history || history.length === 0) {
|
|
||||||
alert('Kein Verlauf zum Exportieren vorhanden.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const asCsv = confirm('Verlauf als CSV exportieren?\n\nOK = CSV\nAbbrechen = JSON');
|
|
||||||
const format = asCsv ? 'csv' : 'json';
|
|
||||||
const result = await window.api.exportHistory(format);
|
|
||||||
|
|
||||||
if (!result || result.canceled) return;
|
|
||||||
if (!result.ok) {
|
|
||||||
alert(result.error || 'Export fehlgeschlagen.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
showCopyToast(`Verlauf exportiert (${result.totalRows || 0} Zeilen)`);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sortRecentFiles(data) {
|
function sortRecentFiles(data) {
|
||||||
const sorted = data.slice();
|
const sorted = data.slice();
|
||||||
const { key, direction } = recentSortState;
|
const { key, direction } = recentSortState;
|
||||||
@ -3213,7 +3193,6 @@ function setupListeners() {
|
|||||||
await window.api.clearHistory();
|
await window.api.clearHistory();
|
||||||
loadHistory();
|
loadHistory();
|
||||||
});
|
});
|
||||||
document.getElementById('exportHistoryBtn').addEventListener('click', exportHistory);
|
|
||||||
|
|
||||||
// Auto health check toggle
|
// Auto health check toggle
|
||||||
const autoToggle = document.getElementById('autoHealthCheckToggle');
|
const autoToggle = document.getElementById('autoHealthCheckToggle');
|
||||||
|
|||||||
@ -227,11 +227,8 @@
|
|||||||
<div class="history-container">
|
<div class="history-container">
|
||||||
<div class="history-header">
|
<div class="history-header">
|
||||||
<h2>Upload-Verlauf</h2>
|
<h2>Upload-Verlauf</h2>
|
||||||
<div style="display:flex; gap:8px">
|
|
||||||
<button class="btn btn-secondary" id="exportHistoryBtn">Verlauf exportieren</button>
|
|
||||||
<button class="btn btn-secondary" id="clearHistoryBtn">Verlauf löschen</button>
|
<button class="btn btn-secondary" id="clearHistoryBtn">Verlauf löschen</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div id="historyContainer"></div>
|
<div id="historyContainer"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -93,14 +93,14 @@ describe('ConfigStore', () => {
|
|||||||
assert.equal(config.hosterSettings['doodstream.com'].retries, 10); // preserved
|
assert.equal(config.hosterSettings['doodstream.com'].retries, 10); // preserved
|
||||||
});
|
});
|
||||||
|
|
||||||
it('appendHistory keeps complete history without truncation', async () => {
|
it('appendHistory adds entries and caps at 100', async () => {
|
||||||
for (let i = 0; i < 105; i++) {
|
for (let i = 0; i < 105; i++) {
|
||||||
await store.appendHistory({ id: `batch-${i}`, timestamp: new Date().toISOString(), files: [] });
|
await store.appendHistory({ id: `batch-${i}`, timestamp: new Date().toISOString(), files: [] });
|
||||||
}
|
}
|
||||||
const history = store.loadHistory();
|
const history = store.loadHistory();
|
||||||
assert.equal(history.length, 105);
|
assert.equal(history.length, 100);
|
||||||
assert.equal(history[0].id, 'batch-0');
|
assert.equal(history[0].id, 'batch-5'); // first 5 dropped
|
||||||
assert.equal(history[104].id, 'batch-104');
|
assert.equal(history[99].id, 'batch-104');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('clearHistory empties the array', async () => {
|
it('clearHistory empties the array', async () => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user