diff --git a/apps/electron-backend/src/app/api/main.preload.ts b/apps/electron-backend/src/app/api/main.preload.ts index 771fe67b2..db4ee78c8 100644 --- a/apps/electron-backend/src/app/api/main.preload.ts +++ b/apps/electron-backend/src/app/api/main.preload.ts @@ -129,8 +129,19 @@ contextBridge.exposeInMainWorld('electron', { ipcRenderer.invoke('DB_HAS_CATEGORIES', playlistId, type), dbGetCategories: (playlistId: string, type: string) => ipcRenderer.invoke('DB_GET_CATEGORIES', playlistId, type), - dbSaveCategories: (playlistId: string, categories: any[], type: string) => - ipcRenderer.invoke('DB_SAVE_CATEGORIES', playlistId, categories, type), + dbSaveCategories: ( + playlistId: string, + categories: any[], + type: string, + hiddenCategoryXtreamIds?: number[] + ) => + ipcRenderer.invoke( + 'DB_SAVE_CATEGORIES', + playlistId, + categories, + type, + hiddenCategoryXtreamIds + ), dbGetAllCategories: (playlistId: string, type: string) => ipcRenderer.invoke('DB_GET_ALL_CATEGORIES', playlistId, type), dbUpdateCategoryVisibility: (categoryIds: number[], hidden: boolean) => diff --git a/apps/electron-backend/src/app/events/database/category.events.ts b/apps/electron-backend/src/app/events/database/category.events.ts index dbb7913bb..77cc65f51 100644 --- a/apps/electron-backend/src/app/events/database/category.events.ts +++ b/apps/electron-backend/src/app/events/database/category.events.ts @@ -62,6 +62,7 @@ ipcMain.handle( /** * Save categories in bulk + * Optionally accepts hidden category data to restore visibility preferences */ ipcMain.handle( 'DB_SAVE_CATEGORIES', @@ -72,7 +73,8 @@ ipcMain.handle( category_name: string; category_id: number; }>, - type: 'live' | 'movies' | 'series' + type: 'live' | 'movies' | 'series', + hiddenCategoryXtreamIds?: number[] ) => { try { // Skip if no categories to save @@ -81,11 +83,17 @@ ipcMain.handle( } const db = await getDatabase(); + + // Create a Set of hidden xtreamIds for efficient lookup + const hiddenSet = new Set(hiddenCategoryXtreamIds || []); + const values = categories.map((cat) => ({ playlistId, name: cat.category_name, type, xtreamId: cat.category_id, + // Restore hidden status if the category was previously hidden + hidden: hiddenSet.has(cat.category_id), })); await db.insert(schema.categories).values(values); diff --git a/apps/electron-backend/src/app/events/database/xtream.events.ts b/apps/electron-backend/src/app/events/database/xtream.events.ts index 0a04c68d4..34218c713 100644 --- a/apps/electron-backend/src/app/events/database/xtream.events.ts +++ b/apps/electron-backend/src/app/events/database/xtream.events.ts @@ -18,14 +18,27 @@ ipcMain.handle( try { const db = await getDatabase(); - // First, get all category IDs for this playlist + // First, get all categories for this playlist (including hidden status) const categories = await db - .select({ id: schema.categories.id }) + .select({ + id: schema.categories.id, + xtreamId: schema.categories.xtreamId, + type: schema.categories.type, + hidden: schema.categories.hidden, + }) .from(schema.categories) .where(eq(schema.categories.playlistId, playlistId)); const categoryIds = categories.map((c) => c.id); + // Save hidden categories info to restore after refresh + const hiddenCategories = categories + .filter((c) => c.hidden) + .map((c) => ({ + xtreamId: c.xtreamId, + type: c.type, + })); + // Before deleting content, save xtreamIds of favorited and recently viewed items // so they can be restored after refresh let favoritedXtreamIds: number[] = []; @@ -89,11 +102,12 @@ ipcMain.handle( .delete(schema.categories) .where(eq(schema.categories.playlistId, playlistId)); - // Return the saved xtreamIds so favorites and recently viewed can be restored + // Return the saved xtreamIds so favorites, recently viewed, and hidden categories can be restored return { success: true, favoritedXtreamIds, recentlyViewedXtreamIds, + hiddenCategories, }; } catch (error) { console.error('Error deleting Xtream content:', error); diff --git a/apps/web/src/app/xtream-tauri/data-sources/electron-xtream-data-source.ts b/apps/web/src/app/xtream-tauri/data-sources/electron-xtream-data-source.ts index 2ee794e14..18b022ef8 100644 --- a/apps/web/src/app/xtream-tauri/data-sources/electron-xtream-data-source.ts +++ b/apps/web/src/app/xtream-tauri/data-sources/electron-xtream-data-source.ts @@ -98,10 +98,15 @@ export class ElectronXtreamDataSource implements IXtreamDataSource { ); if (remoteData && Array.isArray(remoteData) && remoteData.length > 0) { + // Check if there are saved hidden categories to restore + const hiddenCategoryXtreamIds = + this.getHiddenCategoryXtreamIds(playlistId, dbType); + await this.dbService.saveXtreamCategories( playlistId, remoteData, - dbType + dbType, + hiddenCategoryXtreamIds ); } @@ -109,6 +114,40 @@ export class ElectronXtreamDataSource implements IXtreamDataSource { return this.dbService.getXtreamCategories(playlistId, dbType); } + /** + * Get hidden category xtreamIds from localStorage for a specific playlist and type + * Used to restore visibility preferences after playlist refresh + */ + private getHiddenCategoryXtreamIds( + playlistId: string, + type: 'live' | 'movies' | 'series' + ): number[] | undefined { + const restoreKey = `xtream-restore-${playlistId}`; + const restoreDataStr = localStorage.getItem(restoreKey); + + if (!restoreDataStr) { + return undefined; + } + + try { + const restoreData = JSON.parse(restoreDataStr); + const hiddenCategories = restoreData.hiddenCategories as + | { xtreamId: number; type: string }[] + | undefined; + + if (!hiddenCategories || hiddenCategories.length === 0) { + return undefined; + } + + // Filter for the specific type and return xtreamIds + return hiddenCategories + .filter((cat) => cat.type === type) + .map((cat) => cat.xtreamId); + } catch { + return undefined; + } + } + async getAllCategories( playlistId: string, type: DbCategoryType diff --git a/global.d.ts b/global.d.ts index 7ad9673e2..5efe28ee2 100644 --- a/global.d.ts +++ b/global.d.ts @@ -101,6 +101,10 @@ declare global { xtreamId: number; viewedAt: string; }[]; + hiddenCategories: { + xtreamId: number; + type: string; + }[]; }>; dbRestoreXtreamUserData: ( playlistId: string, @@ -121,7 +125,8 @@ declare global { dbSaveCategories: ( playlistId: string, categories: any[], - type: string + type: string, + hiddenCategoryXtreamIds?: number[] ) => Promise<{ success: boolean }>; dbGetAllCategories: ( playlistId: string, diff --git a/libs/services/src/lib/database-electron.service.ts b/libs/services/src/lib/database-electron.service.ts index 41aec7929..e51f8498d 100644 --- a/libs/services/src/lib/database-electron.service.ts +++ b/libs/services/src/lib/database-electron.service.ts @@ -68,12 +68,13 @@ export class DatabaseService { /** * Delete all content and categories for an Xtream playlist (for refresh) * Keeps the playlist entry but removes all imported data - * Returns saved favorites and recently viewed xtreamIds for restoration + * Returns saved favorites, recently viewed xtreamIds, and hidden categories for restoration */ async deleteXtreamPlaylistContent(playlistId: string): Promise<{ success: boolean; favoritedXtreamIds: number[]; recentlyViewedXtreamIds: { xtreamId: number; viewedAt: string }[]; + hiddenCategories: { xtreamId: number; type: string }[]; }> { return await window.electron.dbDeleteXtreamContent(playlistId); } @@ -160,13 +161,20 @@ export class DatabaseService { /** * Save categories in bulk + * Optionally accepts hidden category xtreamIds to restore visibility preferences */ async saveXtreamCategories( playlistId: string, categories: any[], - type: 'live' | 'movies' | 'series' + type: 'live' | 'movies' | 'series', + hiddenCategoryXtreamIds?: number[] ): Promise { - await window.electron.dbSaveCategories(playlistId, categories, type); + await window.electron.dbSaveCategories( + playlistId, + categories, + type, + hiddenCategoryXtreamIds + ); } /** diff --git a/libs/ui/components/src/lib/recent-playlists/recent-playlists.component.ts b/libs/ui/components/src/lib/recent-playlists/recent-playlists.component.ts index b291f3b87..73e16ce99 100644 --- a/libs/ui/components/src/lib/recent-playlists/recent-playlists.component.ts +++ b/libs/ui/components/src/lib/recent-playlists/recent-playlists.component.ts @@ -222,11 +222,14 @@ export class RecentPlaylistsComponent { onConfirm: async () => { try { // Delete all content and categories for this playlist - // This returns the xtreamIds of favorites and recently viewed items - const { favoritedXtreamIds, recentlyViewedXtreamIds } = - await this.databaseService.deleteXtreamPlaylistContent( - item._id - ); + // This returns the xtreamIds of favorites, recently viewed items, and hidden categories + const { + favoritedXtreamIds, + recentlyViewedXtreamIds, + hiddenCategories, + } = await this.databaseService.deleteXtreamPlaylistContent( + item._id + ); const updateDate = Date.now(); @@ -250,6 +253,7 @@ export class RecentPlaylistsComponent { JSON.stringify({ favoritedXtreamIds, recentlyViewedXtreamIds, + hiddenCategories, }) );