feat: implement file title normalization and path handling in playlist upload

Entire-Checkpoint: c6e522b4276c
This commit is contained in:
4gray committed 2026-04-20 00:24:59 +02:00
1 parent 1870ce2aea
commit 1d340c08b8
3 files changed
+55 -5

No files matched your search

@@ -3,6 +3,7 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Store } from '@ngrx/store';
import { TranslateService } from '@ngx-translate/core';
import { PlaylistActions } from 'm3u-state';
import { DataService } from 'services';
import { PLAYLIST_PARSE_BY_URL } from 'shared-interfaces';
import { AddPlaylistDialogComponent } from './add-playlist-dialog.component';
@@ -11,6 +12,7 @@ describe('AddPlaylistDialogComponent', () => {
let component: AddPlaylistDialogComponent;
let dataService: { sendIpcEvent: jest.Mock };
let dialogRef: { close: jest.Mock };
let store: { dispatch: jest.Mock };
beforeEach(() => {
dataService = {
@@ -19,6 +21,9 @@ describe('AddPlaylistDialogComponent', () => {
dialogRef = {
close: jest.fn(),
};
store = {
dispatch: jest.fn(),
};
TestBed.configureTestingModule({
providers: [
@@ -32,9 +37,7 @@ describe('AddPlaylistDialogComponent', () => {
},
{
provide: Store,
useValue: {
dispatch: jest.fn(),
},
useValue: store,
},
{
provide: MatSnackBar,
@@ -102,4 +105,28 @@ describe('AddPlaylistDialogComponent', () => {
);
expect(dialogRef.close).toHaveBeenCalled();
});
it('strips playlist extensions from uploaded file titles', () => {
component.handlePlaylist({
uploadEvent: {
target: {
result: '#EXTM3U',
},
} as unknown as Event,
file: {
name: 'Local Source.m3u',
path: '/tmp/Local Source.m3u',
} as File,
});
expect(store.dispatch).toHaveBeenCalledWith(
PlaylistActions.parsePlaylist({
uploadType: 'FILE',
playlist: '#EXTM3U',
title: 'Local Source',
path: '/tmp/Local Source.m3u',
})
);
expect(dialogRef.close).toHaveBeenCalled();
});
});
@@ -122,7 +122,7 @@ export class AddPlaylistDialogComponent {
PlaylistActions.parsePlaylist({
uploadType: 'FILE',
playlist,
title: payload.file.name,
title: this.normalizeImportedFileTitle(payload.file.name),
path: (payload.file as File & { path?: string }).path,
})
);
@@ -181,4 +181,16 @@ 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
);
}
}
@@ -38,10 +38,21 @@ export class FileUploadComponent {
}
onPicked(input: HTMLInputElement): void {
const file = input.files?.[0];
const file = input.files?.[0] as (File & { path?: string }) | undefined;
const pathOverride = input.dataset['filePathOverride'];
if (file && !file.path && pathOverride) {
Object.defineProperty(file, 'path', {
configurable: true,
value: pathOverride,
});
}
if (file) {
this.setFile(file);
}
delete input.dataset['filePathOverride'];
input.value = '';
}