From 4616df5d16efbf37a9348d7117cd0441de8e8e72 Mon Sep 17 00:00:00 2001 From: 4gray Date: Sat, 25 Apr 2026 22:32:08 +0200 Subject: [PATCH] refactor(playlist-import): update file upload handling and emit imported event Entire-Checkpoint: c6e522b4276c --- .../add-playlist-dialog.component.html | 2 +- .../add-playlist-dialog.component.ts | 30 ++------------- .../lib/file-upload/file-upload.component.ts | 38 ++++++++++--------- 3 files changed, 25 insertions(+), 45 deletions(-) diff --git a/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.html b/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.html index 4df139864..a6e08f317 100644 --- a/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.html +++ b/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.html @@ -45,7 +45,7 @@ @case ('file') { } diff --git a/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.ts b/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.ts index 7c09dc0f1..318b9736a 100644 --- a/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.ts +++ b/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.ts @@ -111,21 +111,11 @@ export class AddPlaylistDialogComponent { } /** - * Parse and store uploaded playlist - * @param payload + * Closes the dialog after a successful file import. The actual parse and + * dispatch happens inside `PlaylistFileImportService` (called from the + * `FileUploadComponent`). */ - handlePlaylist(payload: { uploadEvent: Event; file: File }): void { - const playlist = (payload.uploadEvent.target as FileReader) - .result as string; - - this.store.dispatch( - PlaylistActions.parsePlaylist({ - uploadType: 'FILE', - playlist, - title: this.normalizeImportedFileTitle(payload.file.name), - path: (payload.file as File & { path?: string }).path, - }) - ); + onFileImported(): void { this.closeDialog(); } @@ -181,16 +171,4 @@ export class AddPlaylistDialogComponent { const normalizedValue = value?.trim(); return normalizedValue ? normalizedValue : undefined; } - - private normalizeImportedFileTitle(filename: string): string { - const normalizedFilename = filename.trim(); - if (!normalizedFilename) { - return filename; - } - - return ( - normalizedFilename.replace(/\.(m3u8?|pls|txt)$/i, '') || - normalizedFilename - ); - } } diff --git a/libs/playlist/import/feature/src/lib/file-upload/file-upload.component.ts b/libs/playlist/import/feature/src/lib/file-upload/file-upload.component.ts index 8607ce291..bcb887d81 100644 --- a/libs/playlist/import/feature/src/lib/file-upload/file-upload.component.ts +++ b/libs/playlist/import/feature/src/lib/file-upload/file-upload.component.ts @@ -1,9 +1,9 @@ -import { Component, EventEmitter, Output, signal } from '@angular/core'; +import { Component, EventEmitter, Output, inject, signal } from '@angular/core'; import { MatIconModule } from '@angular/material/icon'; import { TranslatePipe } from '@ngx-translate/core'; +import { PlaylistFileImportService } from '@iptvnator/playlist/shared/util'; import { DragDropFileUploadDirective } from './drag-drop-file-upload.directive'; -const M3U_EXTENSIONS = ['.m3u', '.m3u8']; const MB = 1024 * 1024; const KB = 1024; @@ -14,15 +14,15 @@ const KB = 1024; styleUrls: ['./file-upload.component.scss'], }) export class FileUploadComponent { - @Output() fileSelected = new EventEmitter<{ - uploadEvent: Event; - file: File; - }>(); + private readonly importService = inject(PlaylistFileImportService); + + @Output() imported = new EventEmitter<{ title: string }>(); @Output() fileRejected = new EventEmitter(); @Output() closeDialog = new EventEmitter(); readonly selectedFile = signal(null); readonly isDragging = signal(false); + readonly isImporting = signal(false); openPicker(input: HTMLInputElement): void { input.value = ''; @@ -60,14 +60,21 @@ export class FileUploadComponent { this.selectedFile.set(null); } - confirm(): void { + async confirm(): Promise { const file = this.selectedFile(); - if (!file) return; + if (!file || this.isImporting()) return; - const reader = new FileReader(); - reader.onload = (uploadEvent) => - this.fileSelected.emit({ uploadEvent, file }); - reader.readAsText(file); + this.isImporting.set(true); + const result = await this.importService.importFile(file); + this.isImporting.set(false); + + if (result.ok === true) { + this.imported.emit({ title: result.title }); + return; + } + + this.selectedFile.set(null); + this.fileRejected.emit(file.name); } formatSize(bytes: number): string { @@ -78,15 +85,10 @@ export class FileUploadComponent { private setFile(file: File | undefined): void { if (!file) return; - if (!this.hasAllowedExtension(file.name)) { + if (!this.importService.isSupportedFile(file)) { this.fileRejected.emit(file.name); return; } this.selectedFile.set(file); } - - private hasAllowedExtension(name: string): boolean { - const lower = name.toLowerCase(); - return M3U_EXTENSIONS.some((ext) => lower.endsWith(ext)); - } }