Compare commits
No commits in common. "04413599d8df41d744d2107c79c13f65a1677767" and "ca477733172df704ceca0171d3f51d1a1c75250b" have entirely different histories.
04413599d8
...
ca47773317
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "real-debrid-downloader",
|
"name": "real-debrid-downloader",
|
||||||
"version": "1.7.139",
|
"version": "1.7.138",
|
||||||
"description": "Desktop downloader",
|
"description": "Desktop downloader",
|
||||||
"main": "build/main/main/main.js",
|
"main": "build/main/main/main.js",
|
||||||
"author": "Sucukdeluxe",
|
"author": "Sucukdeluxe",
|
||||||
|
|||||||
@ -1578,17 +1578,6 @@ export class DownloadManager extends EventEmitter {
|
|||||||
this.settingsSnapshotCacheAt = 0;
|
this.settingsSnapshotCacheAt = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** State-diffing tracking: hashes of items/packages as last sent to the
|
|
||||||
* renderer. Allows getSnapshotForEmit() to return only changed entries
|
|
||||||
* plus a list of removed IDs, drastically cutting IPC payload size for
|
|
||||||
* large queues (5000+ items) where most items are idle between emits. */
|
|
||||||
private lastEmittedItemHashes = new Map<string, string>();
|
|
||||||
private lastEmittedPackageHashes = new Map<string, string>();
|
|
||||||
private firstEmitDone = false;
|
|
||||||
private lastFullEmitAt = 0;
|
|
||||||
/** Force a full resync every 30 seconds to recover from any potential drift. */
|
|
||||||
private static readonly FULL_RESYNC_INTERVAL_MS = 30000;
|
|
||||||
|
|
||||||
private lastPersistAt = 0;
|
private lastPersistAt = 0;
|
||||||
private lastSettingsPersistAt = 0;
|
private lastSettingsPersistAt = 0;
|
||||||
private appSessionStartedAt = 0;
|
private appSessionStartedAt = 0;
|
||||||
@ -2030,104 +2019,6 @@ export class DownloadManager extends EventEmitter {
|
|||||||
this.emitState();
|
this.emitState();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Compact hash of the visible/mutable item fields. Two items with identical
|
|
||||||
* hashes are considered "no visible change" and can be excluded from delta
|
|
||||||
* emits. Field selection covers everything ItemRow/PackageCard render. */
|
|
||||||
private buildItemHash(item: DownloadItem): string {
|
|
||||||
return `${item.updatedAt}|${item.status}|${item.progressPercent}|${item.speedBps}|${item.downloadedBytes}|${item.totalBytes}|${item.retries}|${item.fullStatus || ""}|${item.fileName}|${item.providerLabel || ""}|${item.provider || ""}|${item.onlineStatus || ""}|${item.lastError || ""}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Compact hash of the visible/mutable package fields. */
|
|
||||||
private buildPackageHash(pkg: PackageEntry): string {
|
|
||||||
return `${pkg.updatedAt}|${pkg.status}|${pkg.name}|${pkg.enabled ? 1 : 0}|${pkg.cancelled ? 1 : 0}|${pkg.priority || ""}|${pkg.itemIds.length}|${pkg.postProcessLabel || ""}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns a snapshot suitable for IPC emit. On the first emit (or every
|
|
||||||
* 30s for safety, or when explicitly forced), returns a "full" payload
|
|
||||||
* containing all items/packages. Otherwise returns a "delta" with only
|
|
||||||
* items/packages that changed since the last emit, plus removed IDs. */
|
|
||||||
public getSnapshotForEmit(forceFull = false): UiSnapshot {
|
|
||||||
const base = this.getSnapshot();
|
|
||||||
const now = nowMs();
|
|
||||||
const needsFullResync = !this.firstEmitDone || forceFull
|
|
||||||
|| (now - this.lastFullEmitAt) > DownloadManager.FULL_RESYNC_INTERVAL_MS;
|
|
||||||
|
|
||||||
if (needsFullResync) {
|
|
||||||
// Refresh tracking state to current snapshot
|
|
||||||
this.lastEmittedItemHashes.clear();
|
|
||||||
this.lastEmittedPackageHashes.clear();
|
|
||||||
for (const id in base.session.items) {
|
|
||||||
this.lastEmittedItemHashes.set(id, this.buildItemHash(base.session.items[id]));
|
|
||||||
}
|
|
||||||
for (const id in base.session.packages) {
|
|
||||||
this.lastEmittedPackageHashes.set(id, this.buildPackageHash(base.session.packages[id]));
|
|
||||||
}
|
|
||||||
this.firstEmitDone = true;
|
|
||||||
this.lastFullEmitAt = now;
|
|
||||||
return { ...base, payloadKind: "full" };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build deltas: include only items/packages whose hash changed since last emit
|
|
||||||
const changedItems: Record<string, DownloadItem> = {};
|
|
||||||
const removedItemIds: string[] = [];
|
|
||||||
const seenItemIds = new Set<string>();
|
|
||||||
let itemChangeCount = 0;
|
|
||||||
for (const id in base.session.items) {
|
|
||||||
seenItemIds.add(id);
|
|
||||||
const item = base.session.items[id];
|
|
||||||
const newHash = this.buildItemHash(item);
|
|
||||||
const oldHash = this.lastEmittedItemHashes.get(id);
|
|
||||||
if (oldHash !== newHash) {
|
|
||||||
changedItems[id] = item;
|
|
||||||
this.lastEmittedItemHashes.set(id, newHash);
|
|
||||||
itemChangeCount += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Detect removed items
|
|
||||||
for (const id of this.lastEmittedItemHashes.keys()) {
|
|
||||||
if (!seenItemIds.has(id)) {
|
|
||||||
removedItemIds.push(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const id of removedItemIds) {
|
|
||||||
this.lastEmittedItemHashes.delete(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
const changedPackages: Record<string, PackageEntry> = {};
|
|
||||||
const removedPackageIds: string[] = [];
|
|
||||||
const seenPackageIds = new Set<string>();
|
|
||||||
for (const id in base.session.packages) {
|
|
||||||
seenPackageIds.add(id);
|
|
||||||
const pkg = base.session.packages[id];
|
|
||||||
const newHash = this.buildPackageHash(pkg);
|
|
||||||
const oldHash = this.lastEmittedPackageHashes.get(id);
|
|
||||||
if (oldHash !== newHash) {
|
|
||||||
changedPackages[id] = pkg;
|
|
||||||
this.lastEmittedPackageHashes.set(id, newHash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const id of this.lastEmittedPackageHashes.keys()) {
|
|
||||||
if (!seenPackageIds.has(id)) {
|
|
||||||
removedPackageIds.push(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const id of removedPackageIds) {
|
|
||||||
this.lastEmittedPackageHashes.delete(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...base,
|
|
||||||
session: {
|
|
||||||
...base.session,
|
|
||||||
items: changedItems,
|
|
||||||
packages: changedPackages,
|
|
||||||
},
|
|
||||||
payloadKind: "delta",
|
|
||||||
removedItemIds,
|
|
||||||
removedPackageIds,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public getSnapshot(): UiSnapshot {
|
public getSnapshot(): UiSnapshot {
|
||||||
const now = nowMs();
|
const now = nowMs();
|
||||||
this.ensureProviderDailyUsageFresh(now, true);
|
this.ensureProviderDailyUsageFresh(now, true);
|
||||||
@ -5389,7 +5280,7 @@ export class DownloadManager extends EventEmitter {
|
|||||||
this.stateEmitTimer = null;
|
this.stateEmitTimer = null;
|
||||||
}
|
}
|
||||||
this.lastStateEmitAt = now;
|
this.lastStateEmitAt = now;
|
||||||
this.emit("state", this.getSnapshotForEmit());
|
this.emit("state", this.getSnapshot());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Too soon — replace any pending timer with a shorter forced-emit timer
|
// Too soon — replace any pending timer with a shorter forced-emit timer
|
||||||
@ -5400,7 +5291,7 @@ export class DownloadManager extends EventEmitter {
|
|||||||
this.stateEmitTimer = setTimeout(() => {
|
this.stateEmitTimer = setTimeout(() => {
|
||||||
this.stateEmitTimer = null;
|
this.stateEmitTimer = null;
|
||||||
this.lastStateEmitAt = nowMs();
|
this.lastStateEmitAt = nowMs();
|
||||||
this.emit("state", this.getSnapshotForEmit());
|
this.emit("state", this.getSnapshot());
|
||||||
}, MIN_FORCE_GAP_MS - sinceLastEmit);
|
}, MIN_FORCE_GAP_MS - sinceLastEmit);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -5420,7 +5311,7 @@ export class DownloadManager extends EventEmitter {
|
|||||||
this.stateEmitTimer = setTimeout(() => {
|
this.stateEmitTimer = setTimeout(() => {
|
||||||
this.stateEmitTimer = null;
|
this.stateEmitTimer = null;
|
||||||
this.lastStateEmitAt = nowMs();
|
this.lastStateEmitAt = nowMs();
|
||||||
this.emit("state", this.getSnapshotForEmit());
|
this.emit("state", this.getSnapshot());
|
||||||
}, emitDelay);
|
}, emitDelay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1532,11 +1532,6 @@ export function App(): ReactElement {
|
|||||||
const settingsDraftRevisionRef = useRef(0);
|
const settingsDraftRevisionRef = useRef(0);
|
||||||
const panelDirtyRevisionRef = useRef(0);
|
const panelDirtyRevisionRef = useRef(0);
|
||||||
const latestStateRef = useRef<UiSnapshot | null>(null);
|
const latestStateRef = useRef<UiSnapshot | null>(null);
|
||||||
// Master state used to apply incoming delta payloads. The wire format from
|
|
||||||
// the main process sends only changed items/packages (with payloadKind="delta")
|
|
||||||
// most of the time and a full snapshot every 30s for safety. Without this
|
|
||||||
// master, we'd only see the changed slice each emit.
|
|
||||||
const masterSnapshotRef = useRef<UiSnapshot | null>(null);
|
|
||||||
const snapshotRef = useRef(snapshot);
|
const snapshotRef = useRef(snapshot);
|
||||||
snapshotRef.current = snapshot;
|
snapshotRef.current = snapshot;
|
||||||
const tabRef = useRef(tab);
|
const tabRef = useRef(tab);
|
||||||
@ -1868,8 +1863,6 @@ export function App(): ReactElement {
|
|||||||
if (!mountedRef.current) {
|
if (!mountedRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Seed the master snapshot — incoming delta payloads will merge into this.
|
|
||||||
masterSnapshotRef.current = state;
|
|
||||||
setSnapshot(state);
|
setSnapshot(state);
|
||||||
if (state.settings.columnOrder?.length > 0) {
|
if (state.settings.columnOrder?.length > 0) {
|
||||||
setColumnOrder(state.settings.columnOrder);
|
setColumnOrder(state.settings.columnOrder);
|
||||||
@ -1890,36 +1883,11 @@ export function App(): ReactElement {
|
|||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
showToast(`Snapshot konnte nicht geladen werden: ${String(error)}`, 2800);
|
showToast(`Snapshot konnte nicht geladen werden: ${String(error)}`, 2800);
|
||||||
});
|
});
|
||||||
unsubscribe = window.rd.onStateUpdate((wireState) => {
|
unsubscribe = window.rd.onStateUpdate((state) => {
|
||||||
// Merge delta payloads into the master snapshot. Full payloads replace
|
latestStateRef.current = state;
|
||||||
// the master entirely (initial sync + periodic 30s resync).
|
|
||||||
let merged: UiSnapshot;
|
|
||||||
const master = masterSnapshotRef.current;
|
|
||||||
if (wireState.payloadKind === "delta" && master) {
|
|
||||||
const newItems: Record<string, DownloadItem> = { ...master.session.items, ...wireState.session.items };
|
|
||||||
if (wireState.removedItemIds && wireState.removedItemIds.length > 0) {
|
|
||||||
for (const id of wireState.removedItemIds) delete newItems[id];
|
|
||||||
}
|
|
||||||
const newPackages: Record<string, PackageEntry> = { ...master.session.packages, ...wireState.session.packages };
|
|
||||||
if (wireState.removedPackageIds && wireState.removedPackageIds.length > 0) {
|
|
||||||
for (const id of wireState.removedPackageIds) delete newPackages[id];
|
|
||||||
}
|
|
||||||
merged = {
|
|
||||||
...wireState,
|
|
||||||
session: {
|
|
||||||
...wireState.session,
|
|
||||||
items: newItems,
|
|
||||||
packages: newPackages,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
merged = wireState;
|
|
||||||
}
|
|
||||||
masterSnapshotRef.current = merged;
|
|
||||||
latestStateRef.current = merged;
|
|
||||||
if (stateFlushTimerRef.current) { return; }
|
if (stateFlushTimerRef.current) { return; }
|
||||||
|
|
||||||
const itemCount = Object.keys(merged.session.items).length;
|
const itemCount = Object.keys(state.session.items).length;
|
||||||
let flushDelay = itemCount >= 1500
|
let flushDelay = itemCount >= 1500
|
||||||
? 900
|
? 900
|
||||||
: itemCount >= 700
|
: itemCount >= 700
|
||||||
@ -1927,7 +1895,7 @@ export function App(): ReactElement {
|
|||||||
: itemCount >= 250
|
: itemCount >= 250
|
||||||
? 400
|
? 400
|
||||||
: 150;
|
: 150;
|
||||||
if (!merged.session.running) {
|
if (!state.session.running) {
|
||||||
flushDelay = Math.min(flushDelay, 200);
|
flushDelay = Math.min(flushDelay, 200);
|
||||||
}
|
}
|
||||||
if (activeTabRef.current !== "downloads") {
|
if (activeTabRef.current !== "downloads") {
|
||||||
|
|||||||
@ -230,15 +230,6 @@ export interface UiSnapshot {
|
|||||||
clipboardActive: boolean;
|
clipboardActive: boolean;
|
||||||
reconnectSeconds: number;
|
reconnectSeconds: number;
|
||||||
packageSpeedBps: Record<string, number>;
|
packageSpeedBps: Record<string, number>;
|
||||||
/** When set to "delta", session.items contains ONLY items that changed since
|
|
||||||
* the last emit, and removedItemIds lists items that were removed. The
|
|
||||||
* renderer must merge these into its master state. When undefined or "full",
|
|
||||||
* session.items is the complete set (initial sync or periodic resync). */
|
|
||||||
payloadKind?: "full" | "delta";
|
|
||||||
/** Item IDs to remove from the renderer's master state when payloadKind="delta". */
|
|
||||||
removedItemIds?: string[];
|
|
||||||
/** Package IDs to remove from the renderer's master state when payloadKind="delta". */
|
|
||||||
removedPackageIds?: string[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AddLinksPayload {
|
export interface AddLinksPayload {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user