feat(db): add API to delete all playlists and related rows

Add a new IPC handler_DELETE_ALL_PLAYLIST that removes all
and related data from the SQLite database in a safe order that respects
foreign key relationships. The handler deletes favorites and recently
viewed entries first, then content, categories, and finally playlists.
Errors are logged and propagated.

Expose the new operation in the renderer preload API as
dbDeleteAllPlaylists() to allow UI code to invoke the purge. This
provides a single-call way to clear playlists and their dependent
records for testing or user-driven reset.
This commit is contained in:
4gray committed 2025-12-06 14:46:37 +01:00
1 parent ac39c2a9dd
commit 7ee2cc4eea
3 files changed
+67 -5

No files matched your search

@@ -136,4 +136,5 @@ contextBridge.exposeInMainWorld('electron', {
ipcRenderer.invoke('DB_REMOVE_RECENT_ITEM', contentId, playlistId),
dbGetContentByXtreamId: (xtreamId: number, playlistId: string) =>
ipcRenderer.invoke('DB_GET_CONTENT_BY_XTREAM_ID', xtreamId, playlistId),
dbDeleteAllPlaylists: () => ipcRenderer.invoke('DB_DELETE_ALL_PLAYLISTS'),
});
@@ -985,3 +985,31 @@ ipcMain.handle(
}
}
);
/**
* Delete all playlists and related data from SQLite
*/
ipcMain.handle('DB_DELETE_ALL_PLAYLISTS', async () => {
try {
const db = await getDatabase();
// Delete in order respecting foreign key constraints
// First delete favorites and recently_viewed (they reference content)
await db.delete(schema.favorites);
await db.delete(schema.recentlyViewed);
// Then delete content (references categories)
await db.delete(schema.content);
// Then delete categories (references playlists)
await db.delete(schema.categories);
// Finally delete playlists
await db.delete(schema.playlists);
return { success: true };
} catch (error) {
console.error('Error deleting all playlists:', error);
throw error;
}
});
+38 -5
View File
@@ -17,6 +17,7 @@ import {
} from 'rxjs';
import { DataService, EpgService, PlaylistsService } from 'services';
import {
GLOBAL_FAVORITES_PLAYLIST_ID,
OPEN_MPV_PLAYER,
OPEN_VLC_PLAYER,
Playlist,
@@ -71,6 +72,10 @@ export class PlaylistEffects {
return this.actions$.pipe(
ofType(PlaylistActions.setFavorites),
combineLatestWith(this.store.select(selectActivePlaylistId)),
filter(
([, playlistId]) =>
playlistId !== GLOBAL_FAVORITES_PLAYLIST_ID
),
switchMap(([action, playlistId]) =>
this.playlistsService.setFavorites(
playlistId,
@@ -207,9 +212,25 @@ export class PlaylistEffects {
() => {
return this.actions$.pipe(
ofType(PlaylistActions.removePlaylist),
switchMap((action) =>
this.playlistsService.deletePlaylist(action.playlistId)
)
switchMap(async (action) => {
// Delete from IndexedDB
await firstValueFrom(
this.playlistsService.deletePlaylist(action.playlistId)
);
// Also delete from SQLite if running in Electron
if ((window as any).electron?.dbDeletePlaylist) {
try {
await (window as any).electron.dbDeletePlaylist(
action.playlistId
);
} catch (error) {
console.error(
'Error deleting playlist from SQLite:',
error
);
}
}
})
);
},
{ dispatch: false }
@@ -350,8 +371,20 @@ export class PlaylistEffects {
() => {
return this.actions$.pipe(
ofType(PlaylistActions.removeAllPlaylists),
switchMap(() => this.playlistsService.removeAll()),
tap(() => {
switchMap(async () => {
// Delete from IndexedDB
await firstValueFrom(this.playlistsService.removeAll());
// Also delete from SQLite if running in Electron
if ((window as any).electron?.dbDeleteAllPlaylists) {
try {
await (window as any).electron.dbDeleteAllPlaylists();
} catch (error) {
console.error(
'Error deleting playlists from SQLite:',
error
);
}
}
this.snackBar.open(
this.translate.instant('SETTINGS.PLAYLISTS_REMOVED'),
undefined,