mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-16 22:26:16 -08:00
feat(downloads): finish manager MVP
This commit is contained in:
1 parent
eb55c17a05
commit
bcc2e36a05
48 files changed
+4145
-1844
No files matched your search
@@ -7,10 +7,7 @@ import {
|
||||
runInInjectionContext,
|
||||
signal,
|
||||
} from '@angular/core';
|
||||
import {
|
||||
DownloadItem,
|
||||
DownloadsService,
|
||||
} from './downloads.service';
|
||||
import { DownloadItem, DownloadsService } from './downloads.service';
|
||||
import { RuntimeCapabilitiesService } from './runtime-capabilities.service';
|
||||
|
||||
type TestDownloadsService = {
|
||||
@@ -51,7 +48,10 @@ describe('DownloadsService', () => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
function createDownload(id: number, playlistId = 'playlist-1'): DownloadItem {
|
||||
function createDownload(
|
||||
id: number,
|
||||
playlistId = 'playlist-1'
|
||||
): DownloadItem {
|
||||
return {
|
||||
id,
|
||||
playlistId,
|
||||
@@ -203,10 +203,7 @@ describe('DownloadsService', () => {
|
||||
await expect(service.resumeDownload(42)).resolves.toEqual({
|
||||
success: true,
|
||||
});
|
||||
expect(electron.downloadsResume).toHaveBeenCalledWith(
|
||||
42,
|
||||
'/downloads'
|
||||
);
|
||||
expect(electron.downloadsResume).toHaveBeenCalledWith(42, '/downloads');
|
||||
});
|
||||
|
||||
it('marks downloads as loaded after a failed request while preserving existing data', async () => {
|
||||
@@ -275,9 +272,7 @@ describe('DownloadsService', () => {
|
||||
pausedItem,
|
||||
createDownload(8),
|
||||
]) as unknown as DownloadsService;
|
||||
const resumeDownload = jest
|
||||
.fn()
|
||||
.mockResolvedValue({ success: true });
|
||||
const resumeDownload = jest.fn().mockResolvedValue({ success: true });
|
||||
(service as unknown as { resumeDownload: jest.Mock }).resumeDownload =
|
||||
resumeDownload;
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@ import { computed, inject, Injectable, OnDestroy, signal } from '@angular/core';
|
||||
import { RuntimeCapabilitiesService } from './runtime-capabilities.service';
|
||||
|
||||
export type DownloadStatus =
|
||||
| 'queued'
|
||||
| 'downloading'
|
||||
| 'paused'
|
||||
| 'completed'
|
||||
| 'failed'
|
||||
| 'canceled';
|
||||
'queued' | 'downloading' | 'paused' | 'completed' | 'failed' | 'canceled';
|
||||
|
||||
export interface DownloadItem {
|
||||
id: number;
|
||||
@@ -169,7 +164,8 @@ export class DownloadsService implements OnDestroy {
|
||||
episodeNumber?: number;
|
||||
// Playlist info for auto-creation if needed (Stalker playlists)
|
||||
playlistName?: string;
|
||||
playlistType?: 'xtream' | 'stalker' | 'm3u-file' | 'm3u-text' | 'm3u-url';
|
||||
playlistType?:
|
||||
'xtream' | 'stalker' | 'm3u-file' | 'm3u-text' | 'm3u-url';
|
||||
serverUrl?: string;
|
||||
portalUrl?: string;
|
||||
macAddress?: string;
|
||||
@@ -287,10 +283,7 @@ export class DownloadsService implements OnDestroy {
|
||||
try {
|
||||
return await window.electron.downloadsRetry(downloadId, folder);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'[DownloadsService] Error retrying download:',
|
||||
error
|
||||
);
|
||||
console.error('[DownloadsService] Error retrying download:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
@@ -311,10 +304,7 @@ export class DownloadsService implements OnDestroy {
|
||||
try {
|
||||
return await window.electron.downloadsRemove(downloadId);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'[DownloadsService] Error removing download:',
|
||||
error
|
||||
);
|
||||
console.error('[DownloadsService] Error removing download:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
@@ -379,10 +369,7 @@ export class DownloadsService implements OnDestroy {
|
||||
}
|
||||
return folder;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'[DownloadsService] Error selecting folder:',
|
||||
error
|
||||
);
|
||||
console.error('[DownloadsService] Error selecting folder:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -390,9 +377,7 @@ export class DownloadsService implements OnDestroy {
|
||||
/**
|
||||
* Clear completed/failed downloads
|
||||
*/
|
||||
async clearCompleted(
|
||||
playlistId?: string
|
||||
): Promise<{ success: boolean }> {
|
||||
async clearCompleted(playlistId?: string): Promise<{ success: boolean }> {
|
||||
if (!this.isAvailable()) {
|
||||
return { success: false };
|
||||
}
|
||||
@@ -444,7 +429,11 @@ export class DownloadsService implements OnDestroy {
|
||||
playlistId: string,
|
||||
contentType: 'vod' | 'episode'
|
||||
): boolean {
|
||||
const download = this.getDownloadByContent(xtreamId, playlistId, contentType);
|
||||
const download = this.getDownloadByContent(
|
||||
xtreamId,
|
||||
playlistId,
|
||||
contentType
|
||||
);
|
||||
return download?.status === 'completed' && !!download.filePath;
|
||||
}
|
||||
|
||||
@@ -456,7 +445,11 @@ export class DownloadsService implements OnDestroy {
|
||||
playlistId: string,
|
||||
contentType: 'vod' | 'episode'
|
||||
): boolean {
|
||||
const download = this.getDownloadByContent(xtreamId, playlistId, contentType);
|
||||
const download = this.getDownloadByContent(
|
||||
xtreamId,
|
||||
playlistId,
|
||||
contentType
|
||||
);
|
||||
return (
|
||||
download?.status === 'downloading' ||
|
||||
download?.status === 'queued' ||
|
||||
@@ -472,7 +465,11 @@ export class DownloadsService implements OnDestroy {
|
||||
playlistId: string,
|
||||
contentType: 'vod' | 'episode'
|
||||
): boolean {
|
||||
const download = this.getDownloadByContent(xtreamId, playlistId, contentType);
|
||||
const download = this.getDownloadByContent(
|
||||
xtreamId,
|
||||
playlistId,
|
||||
contentType
|
||||
);
|
||||
return download?.status === 'paused';
|
||||
}
|
||||
|
||||
@@ -484,7 +481,11 @@ export class DownloadsService implements OnDestroy {
|
||||
playlistId: string,
|
||||
contentType: 'vod' | 'episode'
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
const download = this.getDownloadByContent(xtreamId, playlistId, contentType);
|
||||
const download = this.getDownloadByContent(
|
||||
xtreamId,
|
||||
playlistId,
|
||||
contentType
|
||||
);
|
||||
if (!download || download.status !== 'paused') {
|
||||
return { success: false, error: 'No paused download found' };
|
||||
}
|
||||
@@ -499,7 +500,11 @@ export class DownloadsService implements OnDestroy {
|
||||
playlistId: string,
|
||||
contentType: 'vod' | 'episode'
|
||||
): string | undefined {
|
||||
const download = this.getDownloadByContent(xtreamId, playlistId, contentType);
|
||||
const download = this.getDownloadByContent(
|
||||
xtreamId,
|
||||
playlistId,
|
||||
contentType
|
||||
);
|
||||
if (download?.status === 'completed') {
|
||||
return download.filePath;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user