feat(xtream): Xtream playlist refresh workflow

a dedicated refresh button and workflow for Xtream in the
recent playlists UI. Show a refresh icon only when the playlist has a
serverUrl. For Xtream playlists, prompt the user with a confirmation
dialog, delete locally cached categories and streams, show a toast that
the refresh started, and navigate to the playlist to trigger re-import.
Keep existing refresh IPC logic for non-Xtream (M3U/local) playlists.

Also add English translations for the Xtream refresh tooltip and dialog
messages, and surface errors via snackbar on failure.

This enables safe re-importing of remote Xtream playlists while preserving
favorites and recent items.
This commit is contained in:
4gray committed 2025-10-18 21:55:48 +02:00
1 parent 0a934d2ac6
commit 461e2e2e7e
8 files changed
+160 -60

No files matched your search

@@ -69,8 +69,12 @@
<ng-container *ngIf="item.importDate?.length > 3">
| {{ 'HOME.PLAYLISTS.ADDED' | translate }}:
{{ item.importDate | date }}
</ng-container></ng-template
>
</ng-container>
<ng-container *ngIf="item.updateDate">
| {{ 'HOME.PLAYLISTS.UPDATED' | translate }}:
{{ item.updateDate | date: 'MMMM d, yyyy, HH:mm' }}
</ng-container>
</ng-template>
</div>
<span matListItemMeta class="action-buttons" *ngIf="showActions">
@@ -84,6 +88,16 @@
>
<mat-icon>sync</mat-icon>
</button>
<button
*ngIf="item.serverUrl"
mat-icon-button
class="refresh-btn"
color="accent"
(click)="$event.stopPropagation(); refreshClicked.emit(item)"
[matTooltip]="'HOME.PLAYLISTS.REFRESH_XTREAM' | translate"
>
<mat-icon>sync</mat-icon>
</button>
<button
mat-icon-button
color="accent"
@@ -249,10 +249,74 @@ export class RecentPlaylistsComponent implements OnInit {
* @param item playlist to update
*/
refreshPlaylist(item: PlaylistMeta) {
this.dataService.sendIpcEvent(PLAYLIST_UPDATE, {
id: item._id,
title: item.title,
...(item.url ? { url: item.url } : { filePath: item.filePath }),
if (item.serverUrl) {
// For Xtream playlists, delete and re-import
this.refreshXtreamPlaylist(item);
} else {
// For M3U playlists, use existing refresh logic
this.dataService.sendIpcEvent(PLAYLIST_UPDATE, {
id: item._id,
title: item.title,
...(item.url ? { url: item.url } : { filePath: item.filePath }),
});
}
}
/**
* Refresh Xtream playlist by deleting all data and re-importing from remote
* @param item Xtream playlist to refresh
*/
async refreshXtreamPlaylist(item: PlaylistMeta) {
this.dialogService.openConfirmDialog({
title: this.translate.instant(
'HOME.PLAYLISTS.REFRESH_XTREAM_DIALOG.TITLE'
),
message: this.translate.instant(
'HOME.PLAYLISTS.REFRESH_XTREAM_DIALOG.MESSAGE'
),
onConfirm: async () => {
try {
// Delete all content and categories for this playlist
await this.databaseService.deleteXtreamPlaylistContent(
item._id
);
const updateDate = Date.now();
// Update the updateDate timestamp in database (Electron)
await this.databaseService.updateXtreamPlaylistDetails({
id: item._id,
updateDate,
});
// Update the updateDate timestamp in IndexedDB
this.store.dispatch(
PlaylistActions.updatePlaylistMeta({
playlist: { ...item, updateDate },
})
);
this.snackBar.open(
this.translate.instant(
'HOME.PLAYLISTS.REFRESH_XTREAM_DIALOG.STARTED'
),
undefined,
{ duration: 2000 }
);
// Navigate to the playlist to trigger re-import
this.router.navigate(['xtreams', item._id]);
} catch (error) {
console.error('Error refreshing Xtream playlist:', error);
this.snackBar.open(
this.translate.instant(
'HOME.PLAYLISTS.REFRESH_XTREAM_DIALOG.ERROR'
),
undefined,
{ duration: 3000 }
);
}
},
});
}