diff --git a/apps/web/src/app/xtream-tauri/category-management-dialog/category-management-dialog.component.html b/apps/web/src/app/xtream-tauri/category-management-dialog/category-management-dialog.component.html
index 781b0e08b..589d8f41e 100644
--- a/apps/web/src/app/xtream-tauri/category-management-dialog/category-management-dialog.component.html
+++ b/apps/web/src/app/xtream-tauri/category-management-dialog/category-management-dialog.component.html
@@ -59,6 +59,7 @@
>
{{ category.name }}
+ ({{ getItemCount(category) }})
} @empty {
diff --git a/apps/web/src/app/xtream-tauri/category-management-dialog/category-management-dialog.component.scss b/apps/web/src/app/xtream-tauri/category-management-dialog/category-management-dialog.component.scss
index 079ba4d9f..f5026434d 100644
--- a/apps/web/src/app/xtream-tauri/category-management-dialog/category-management-dialog.component.scss
+++ b/apps/web/src/app/xtream-tauri/category-management-dialog/category-management-dialog.component.scss
@@ -79,6 +79,12 @@
text-overflow: ellipsis;
}
+.item-count {
+ opacity: 0.6;
+ font-size: 12px;
+ color: var(--mat-sys-on-surface-variant);
+}
+
.empty-message {
padding: 24px;
text-align: center;
diff --git a/apps/web/src/app/xtream-tauri/category-management-dialog/category-management-dialog.component.ts b/apps/web/src/app/xtream-tauri/category-management-dialog/category-management-dialog.component.ts
index 38ff9f639..373ca23e3 100644
--- a/apps/web/src/app/xtream-tauri/category-management-dialog/category-management-dialog.component.ts
+++ b/apps/web/src/app/xtream-tauri/category-management-dialog/category-management-dialog.component.ts
@@ -18,6 +18,7 @@ import { DatabaseService, XCategoryFromDb } from 'services';
export interface CategoryManagementDialogData {
playlistId: string;
contentType: 'live' | 'vod' | 'series';
+ itemCounts: Map;
}
interface CategoryWithSelection extends XCategoryFromDb {
@@ -104,6 +105,12 @@ export class CategoryManagementDialogComponent implements OnInit {
this.searchTerm.set('');
}
+ getItemCount(category: CategoryWithSelection): number {
+ // content.category_id references categories.id (internal DB id), not xtream_id
+ const categoryId = Number(category.id);
+ return this.data.itemCounts.get(categoryId) ?? 0;
+ }
+
private getDbType(): 'live' | 'movies' | 'series' {
switch (this.data.contentType) {
case 'live':
diff --git a/apps/web/src/app/xtream-tauri/category-view/category-view.component.ts b/apps/web/src/app/xtream-tauri/category-view/category-view.component.ts
index 35e73073f..ed9f9eb32 100644
--- a/apps/web/src/app/xtream-tauri/category-view/category-view.component.ts
+++ b/apps/web/src/app/xtream-tauri/category-view/category-view.component.ts
@@ -32,7 +32,9 @@ export class CategoryViewComponent {
}
getItemCount(item: XtreamCategory): number {
- const itemId = Number((item as any).category_id ?? item.id);
+ // content.category_id references categories.id (internal DB id)
+ // For DB format categories, use id; for API format, use category_id
+ const itemId = Number(item.id ?? (item as any).category_id);
return this.itemCounts().get(itemId) ?? 0;
}
}
diff --git a/apps/web/src/app/xtream-tauri/live-stream-layout/live-stream-layout.component.ts b/apps/web/src/app/xtream-tauri/live-stream-layout/live-stream-layout.component.ts
index b8ff9855c..f86d29464 100644
--- a/apps/web/src/app/xtream-tauri/live-stream-layout/live-stream-layout.component.ts
+++ b/apps/web/src/app/xtream-tauri/live-stream-layout/live-stream-layout.component.ts
@@ -118,6 +118,7 @@ export class LiveStreamLayoutComponent implements OnInit {
data: {
playlistId,
contentType,
+ itemCounts: this.categoryItemCounts(),
},
width: '500px',
maxHeight: '80vh',
diff --git a/apps/web/src/app/xtream-tauri/stores/features/with-selection.feature.ts b/apps/web/src/app/xtream-tauri/stores/features/with-selection.feature.ts
index d86e80c13..1dc659b59 100644
--- a/apps/web/src/app/xtream-tauri/stores/features/with-selection.feature.ts
+++ b/apps/web/src/app/xtream-tauri/stores/features/with-selection.feature.ts
@@ -50,7 +50,9 @@ export function withSelection() {
*/
getSelectedCategory: computed(() => {
const categoryId = store.selectedCategoryId();
- if (!categoryId) return null;
+ if (!categoryId) {
+ return { id: 0, name: 'All Items', type: store.selectedContentType() };
+ }
// Access parent store categories (from withContent)
const storeAny = store as any;
@@ -98,9 +100,6 @@ export function withSelection() {
const startIndex = store.page() * store.limit();
const endIndex = startIndex + store.limit();
const categoryId = store.selectedCategoryId();
-
- if (!categoryId) return [];
-
const categoryType = store.selectedContentType();
// Access parent store content (from withContent)
@@ -112,9 +111,20 @@ export function withSelection() {
? storeAny.vodStreams?.() || []
: storeAny.serialStreams?.() || [];
- return content
- .filter((item: any) => Number(item.category_id) === categoryId)
- .slice(startIndex, endIndex);
+ let filteredContent = content;
+ if (categoryId) {
+ filteredContent = content.filter((item: any) => Number(item.category_id) === categoryId);
+ } else {
+ // Filter nothing (all items), sort by added
+ // Clone to avoid mutation
+ filteredContent = [...content].sort((a: any, b: any) => {
+ const dateA = parseInt(categoryType === 'series' ? a.last_modified : a.added) || 0;
+ const dateB = parseInt(categoryType === 'series' ? b.last_modified : b.added) || 0;
+ return dateB - dateA;
+ });
+ }
+
+ return filteredContent.slice(startIndex, endIndex);
}),
/**
@@ -124,8 +134,6 @@ export function withSelection() {
const categoryId = store.selectedCategoryId();
const categoryType = store.selectedContentType();
- if (!categoryId) return [];
-
// Access parent store content (from withContent)
const storeAny = store as any;
const content =
@@ -135,6 +143,15 @@ export function withSelection() {
? storeAny.vodStreams?.() || []
: storeAny.serialStreams?.() || [];
+ if (!categoryId) {
+ // Return all items sorted by added
+ return [...content].sort((a: any, b: any) => {
+ const dateA = parseInt(categoryType === 'series' ? a.last_modified : a.added) || 0;
+ const dateB = parseInt(categoryType === 'series' ? b.last_modified : b.added) || 0;
+ return dateB - dateA;
+ });
+ }
+
return content.filter(
(item: any) => Number(item.category_id) === categoryId
);
@@ -145,8 +162,6 @@ export function withSelection() {
*/
getTotalPages: computed(() => {
const categoryId = store.selectedCategoryId();
- if (!categoryId) return 0;
-
const categoryType = store.selectedContentType();
// Access parent store content (from withContent)
@@ -158,9 +173,14 @@ export function withSelection() {
? storeAny.vodStreams?.() || []
: storeAny.serialStreams?.() || [];
- const totalItems = content.filter(
- (item: any) => Number(item.category_id) === categoryId
- ).length;
+ let totalItems = 0;
+ if (categoryId) {
+ totalItems = content.filter(
+ (item: any) => Number(item.category_id) === categoryId
+ ).length;
+ } else {
+ totalItems = content.length;
+ }
return Math.ceil(totalItems / store.limit());
}),
@@ -230,9 +250,9 @@ export function withSelection() {
/**
* Set the selected category
*/
- setSelectedCategory(categoryId: number): void {
+ setSelectedCategory(categoryId: number | null): void {
patchState(store, {
- selectedCategoryId: Number(categoryId),
+ selectedCategoryId: categoryId !== null ? Number(categoryId) : null,
page: 0,
});
},