fix(downloads): preserve metadata error conventions

This commit is contained in:
4gray committed 2026-08-01 16:29:50 +02:00
1 parent 9d0b324805
commit 103354df71
3 files changed
+71 -18

No files matched your search

@@ -0,0 +1,29 @@
import type { DownloadMetadataSnapshot } from '@iptvnator/shared/interfaces';
interface DownloadMetadataHost {
isAvailable(): boolean;
loadDownloads(): Promise<void>;
}
export async function updateDownloadMetadata(
host: DownloadMetadataHost,
downloadId: number,
metadataSnapshot: DownloadMetadataSnapshot
): Promise<{ success: boolean; error?: string }> {
if (!host.isAvailable()) return { success: false };
try {
const result = await window.electron.downloadsUpdateMetadata(
downloadId,
metadataSnapshot
);
if (result.success) await host.loadDownloads();
return result;
} catch (error) {
console.error('[DownloadsService] Metadata update error:', error);
return {
success: false,
error: error instanceof Error ? error.message : String(error),
};
}
}
@@ -413,6 +413,33 @@ describe('DownloadsService', () => {
);
});
it('stringifies a non-Error metadata update failure', async () => {
const error = { message: 404 };
jest.spyOn(console, 'error').mockImplementation(() => undefined);
const electron = {
downloadsGetList: jest.fn(async () => []),
downloadsUpdateMetadata: jest
.fn<
Promise<{ success: boolean; error?: string }>,
[number, DownloadMetadataSnapshot]
>()
.mockRejectedValue(error),
};
testWindow.electron = electron;
const service = createService();
await expect(
service.updateMetadata(42, metadataSnapshot)
).resolves.toEqual({
error: '[object Object]',
success: false,
});
expect(console.error).toHaveBeenCalledWith(
'[DownloadsService] Metadata update error:',
error
);
});
it('returns a safe failure when the metadata reload throws', async () => {
const error = new Error('metadata reload failed');
jest.spyOn(console, 'error').mockImplementation(() => undefined);
+15 -18
View File
@@ -1,5 +1,6 @@
import { computed, inject, Injectable, OnDestroy, signal } from '@angular/core';
import type { DownloadMetadataSnapshot } from '@iptvnator/shared/interfaces';
import { updateDownloadMetadata } from './downloads-metadata-update';
import type { DownloadItem, DownloadStartInput } from './downloads.models';
import { formatDownloadBytes } from './downloads.utils';
import { RuntimeCapabilitiesService } from './runtime-capabilities.service';
@@ -132,7 +133,9 @@ export class DownloadsService implements OnDestroy {
/**
* Start a new download
*/
async startDownload(data: DownloadStartInput) {
async startDownload(
data: DownloadStartInput
): Promise<{ success: boolean; id?: number; error?: string }> {
if (!this.isAvailable()) {
return { success: false, error: 'Downloads not available' };
}
@@ -143,15 +146,17 @@ export class DownloadsService implements OnDestroy {
}
try {
return await window.electron.downloadsStart({
const result = await window.electron.downloadsStart({
...data,
downloadFolder: folder,
});
return result;
} catch (error) {
console.error('[DownloadsService] Error starting download:', error);
const message =
error instanceof Error ? error.message : String(error);
return { success: false, error: message };
return {
success: false,
error: error instanceof Error ? error.message : String(error),
};
}
}
@@ -298,19 +303,11 @@ export class DownloadsService implements OnDestroy {
}
/** Update the offline metadata snapshot for a managed download. */
async updateMetadata(id: number, snapshot: DownloadMetadataSnapshot) {
if (!this.isAvailable()) return { success: false };
try {
const bridge = window.electron;
const result = await bridge.downloadsUpdateMetadata(id, snapshot);
if (result.success) await this.loadDownloads();
return result;
} catch (error) {
console.error('[DownloadsService] Metadata update error:', error);
const message = (error as Error)?.message ?? String(error);
return { success: false, error: message };
}
async updateMetadata(
id: number,
snapshot: DownloadMetadataSnapshot
): Promise<{ success: boolean; error?: string }> {
return updateDownloadMetadata(this, id, snapshot);
}
/**