mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-01-23 02:35:14 +00:00
Fix VOD page not displaying correct order while changing pages.
This commit is contained in:
parent
f8e91155e2
commit
5e661ea208
6 changed files with 380 additions and 157 deletions
|
|
@ -5,6 +5,7 @@ from .api_views import (
|
|||
EpisodeViewSet,
|
||||
SeriesViewSet,
|
||||
VODCategoryViewSet,
|
||||
UnifiedContentViewSet,
|
||||
)
|
||||
|
||||
app_name = 'vod'
|
||||
|
|
@ -14,5 +15,6 @@ router.register(r'movies', MovieViewSet, basename='movie')
|
|||
router.register(r'episodes', EpisodeViewSet, basename='episode')
|
||||
router.register(r'series', SeriesViewSet, basename='series')
|
||||
router.register(r'categories', VODCategoryViewSet, basename='vodcategory')
|
||||
router.register(r'all', UnifiedContentViewSet, basename='unified-content')
|
||||
|
||||
urlpatterns = router.urls
|
||||
|
|
|
|||
|
|
@ -469,3 +469,203 @@ class VODCategoryViewSet(viewsets.ReadOnlyModelViewSet):
|
|||
return [perm() for perm in permission_classes_by_action[self.action]]
|
||||
except KeyError:
|
||||
return [Authenticated()]
|
||||
|
||||
|
||||
class UnifiedContentViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
"""ViewSet that combines Movies and Series for unified 'All' view"""
|
||||
queryset = Movie.objects.none() # Empty queryset, we override list method
|
||||
serializer_class = MovieSerializer # Default serializer, overridden in list
|
||||
pagination_class = VODPagination
|
||||
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
|
||||
search_fields = ['name', 'description', 'genre']
|
||||
ordering_fields = ['name', 'year', 'created_at']
|
||||
ordering = ['name']
|
||||
|
||||
def get_permissions(self):
|
||||
try:
|
||||
return [perm() for perm in permission_classes_by_action[self.action]]
|
||||
except KeyError:
|
||||
return [Authenticated()]
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
"""Override list to handle unified content properly - database-level approach"""
|
||||
import logging
|
||||
from django.db import connection
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.error("=== UnifiedContentViewSet.list() called ===")
|
||||
|
||||
try:
|
||||
# Get pagination parameters
|
||||
page_size = int(request.query_params.get('page_size', 24))
|
||||
page_number = int(request.query_params.get('page', 1))
|
||||
|
||||
logger.error(f"Page {page_number}, page_size {page_size}")
|
||||
|
||||
# Calculate offset for unified pagination
|
||||
offset = (page_number - 1) * page_size
|
||||
|
||||
# For high page numbers, use raw SQL for efficiency
|
||||
# This avoids loading and sorting massive amounts of data in Python
|
||||
|
||||
search = request.query_params.get('search', '')
|
||||
category = request.query_params.get('category', '')
|
||||
|
||||
# Build WHERE clauses
|
||||
where_conditions = [
|
||||
# Only active content
|
||||
"movies.id IN (SELECT DISTINCT movie_id FROM vod_m3umovierelation mmr JOIN m3u_m3uaccount ma ON mmr.m3u_account_id = ma.id WHERE ma.is_active = true)",
|
||||
"series.id IN (SELECT DISTINCT series_id FROM vod_m3useriesrelation msr JOIN m3u_m3uaccount ma ON msr.m3u_account_id = ma.id WHERE ma.is_active = true)"
|
||||
]
|
||||
|
||||
params = []
|
||||
|
||||
if search:
|
||||
where_conditions[0] += " AND LOWER(movies.name) LIKE %s"
|
||||
where_conditions[1] += " AND LOWER(series.name) LIKE %s"
|
||||
search_param = f"%{search.lower()}%"
|
||||
params.extend([search_param, search_param])
|
||||
|
||||
if category:
|
||||
if '|' in category:
|
||||
cat_name, cat_type = category.split('|', 1)
|
||||
if cat_type == 'movie':
|
||||
where_conditions[0] += " AND movies.id IN (SELECT movie_id FROM vod_m3umovierelation mmr JOIN vod_vodcategory c ON mmr.category_id = c.id WHERE c.name = %s)"
|
||||
where_conditions[1] = "1=0" # Exclude series
|
||||
params.append(cat_name)
|
||||
elif cat_type == 'series':
|
||||
where_conditions[1] += " AND series.id IN (SELECT series_id FROM vod_m3useriesrelation msr JOIN vod_vodcategory c ON msr.category_id = c.id WHERE c.name = %s)"
|
||||
where_conditions[0] = "1=0" # Exclude movies
|
||||
params.append(cat_name)
|
||||
else:
|
||||
where_conditions[0] += " AND movies.id IN (SELECT movie_id FROM vod_m3umovierelation mmr JOIN vod_vodcategory c ON mmr.category_id = c.id WHERE c.name = %s)"
|
||||
where_conditions[1] += " AND series.id IN (SELECT series_id FROM vod_m3useriesrelation msr JOIN vod_vodcategory c ON msr.category_id = c.id WHERE c.name = %s)"
|
||||
params.extend([category, category])
|
||||
|
||||
# Use UNION ALL with ORDER BY and LIMIT/OFFSET for true unified pagination
|
||||
# This is much more efficient than Python sorting
|
||||
sql = f"""
|
||||
WITH unified_content AS (
|
||||
SELECT
|
||||
movies.id,
|
||||
movies.uuid,
|
||||
movies.name,
|
||||
movies.description,
|
||||
movies.year,
|
||||
movies.rating,
|
||||
movies.genre,
|
||||
movies.duration_secs as duration,
|
||||
movies.created_at,
|
||||
movies.updated_at,
|
||||
movies.custom_properties,
|
||||
movies.logo_id,
|
||||
logo.name as logo_name,
|
||||
logo.url as logo_url,
|
||||
'movie' as content_type
|
||||
FROM vod_movie movies
|
||||
LEFT JOIN dispatcharr_channels_logo logo ON movies.logo_id = logo.id
|
||||
WHERE {where_conditions[0]}
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
series.id,
|
||||
series.uuid,
|
||||
series.name,
|
||||
series.description,
|
||||
series.year,
|
||||
series.rating,
|
||||
series.genre,
|
||||
NULL as duration,
|
||||
series.created_at,
|
||||
series.updated_at,
|
||||
series.custom_properties,
|
||||
series.logo_id,
|
||||
logo.name as logo_name,
|
||||
logo.url as logo_url,
|
||||
'series' as content_type
|
||||
FROM vod_series series
|
||||
LEFT JOIN dispatcharr_channels_logo logo ON series.logo_id = logo.id
|
||||
WHERE {where_conditions[1]}
|
||||
)
|
||||
SELECT * FROM unified_content
|
||||
ORDER BY LOWER(name), id
|
||||
LIMIT %s OFFSET %s
|
||||
"""
|
||||
|
||||
params.extend([page_size, offset])
|
||||
|
||||
logger.error(f"Executing SQL with LIMIT {page_size} OFFSET {offset}")
|
||||
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute(sql, params)
|
||||
columns = [col[0] for col in cursor.description]
|
||||
results = []
|
||||
|
||||
for row in cursor.fetchall():
|
||||
item_dict = dict(zip(columns, row))
|
||||
|
||||
# Build logo object in the format expected by frontend
|
||||
logo_data = None
|
||||
if item_dict['logo_id']:
|
||||
logo_data = {
|
||||
'id': item_dict['logo_id'],
|
||||
'name': item_dict['logo_name'],
|
||||
'url': item_dict['logo_url'],
|
||||
'cache_url': f"/media/logo_cache/{item_dict['logo_id']}.png" if item_dict['logo_id'] else None,
|
||||
'channel_count': 0, # We don't need this for VOD
|
||||
'is_used': True,
|
||||
'channel_names': [] # We don't need this for VOD
|
||||
}
|
||||
|
||||
# Convert to the format expected by frontend
|
||||
formatted_item = {
|
||||
'id': item_dict['id'],
|
||||
'uuid': str(item_dict['uuid']),
|
||||
'name': item_dict['name'],
|
||||
'description': item_dict['description'] or '',
|
||||
'year': item_dict['year'],
|
||||
'rating': float(item_dict['rating']) if item_dict['rating'] else 0.0,
|
||||
'genre': item_dict['genre'] or '',
|
||||
'duration': item_dict['duration'],
|
||||
'created_at': item_dict['created_at'].isoformat() if item_dict['created_at'] else None,
|
||||
'updated_at': item_dict['updated_at'].isoformat() if item_dict['updated_at'] else None,
|
||||
'custom_properties': item_dict['custom_properties'] or {},
|
||||
'logo': logo_data,
|
||||
'content_type': item_dict['content_type']
|
||||
}
|
||||
results.append(formatted_item)
|
||||
|
||||
logger.error(f"Retrieved {len(results)} results via SQL")
|
||||
|
||||
# Get total count estimate (for pagination info)
|
||||
# Use a separate efficient count query
|
||||
count_sql = f"""
|
||||
SELECT COUNT(*) FROM (
|
||||
SELECT 1 FROM vod_movie movies WHERE {where_conditions[0]}
|
||||
UNION ALL
|
||||
SELECT 1 FROM vod_series series WHERE {where_conditions[1]}
|
||||
) as total_count
|
||||
"""
|
||||
|
||||
count_params = params[:-2] # Remove LIMIT and OFFSET params
|
||||
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute(count_sql, count_params)
|
||||
total_count = cursor.fetchone()[0]
|
||||
|
||||
response_data = {
|
||||
'count': total_count,
|
||||
'next': offset + page_size < total_count,
|
||||
'previous': page_number > 1,
|
||||
'results': results
|
||||
}
|
||||
|
||||
return Response(response_data)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in UnifiedContentViewSet.list(): {e}")
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
return Response({'error': str(e)}, status=500)
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
from django.urls import path, include
|
||||
from rest_framework.routers import DefaultRouter
|
||||
from .api_views import MovieViewSet, EpisodeViewSet, SeriesViewSet, VODCategoryViewSet, VODConnectionViewSet
|
||||
from .api_views import MovieViewSet, EpisodeViewSet, SeriesViewSet, VODCategoryViewSet, UnifiedContentViewSet
|
||||
|
||||
app_name = 'vod'
|
||||
|
||||
|
|
@ -9,7 +9,7 @@ router.register(r'movies', MovieViewSet)
|
|||
router.register(r'episodes', EpisodeViewSet)
|
||||
router.register(r'series', SeriesViewSet)
|
||||
router.register(r'categories', VODCategoryViewSet)
|
||||
router.register(r'connections', VODConnectionViewSet)
|
||||
router.register(r'all', UnifiedContentViewSet, basename='unified-content')
|
||||
|
||||
urlpatterns = [
|
||||
path('api/', include(router.urls)),
|
||||
|
|
|
|||
|
|
@ -2077,7 +2077,14 @@ export default class API {
|
|||
);
|
||||
return response;
|
||||
} catch (e) {
|
||||
errorNotification('Failed to retrieve movies', e);
|
||||
// Don't show error notification for "Invalid page" errors as they're handled gracefully
|
||||
const isInvalidPage = e.body?.detail?.includes('Invalid page') ||
|
||||
e.message?.includes('Invalid page');
|
||||
|
||||
if (!isInvalidPage) {
|
||||
errorNotification('Failed to retrieve movies', e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2088,7 +2095,39 @@ export default class API {
|
|||
);
|
||||
return response;
|
||||
} catch (e) {
|
||||
errorNotification('Failed to retrieve series', e);
|
||||
// Don't show error notification for "Invalid page" errors as they're handled gracefully
|
||||
const isInvalidPage = e.body?.detail?.includes('Invalid page') ||
|
||||
e.message?.includes('Invalid page');
|
||||
|
||||
if (!isInvalidPage) {
|
||||
errorNotification('Failed to retrieve series', e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
static async getAllContent(params = new URLSearchParams()) {
|
||||
try {
|
||||
console.log('Calling getAllContent with URL:', `${host}/api/vod/all/?${params.toString()}`);
|
||||
const response = await request(
|
||||
`${host}/api/vod/all/?${params.toString()}`
|
||||
);
|
||||
console.log('getAllContent raw response:', response);
|
||||
return response;
|
||||
} catch (e) {
|
||||
console.error('getAllContent error:', e);
|
||||
console.error('Error status:', e.status);
|
||||
console.error('Error body:', e.body);
|
||||
console.error('Error message:', e.message);
|
||||
|
||||
// Don't show error notification for "Invalid page" errors as they're handled gracefully
|
||||
const isInvalidPage = e.body?.detail?.includes('Invalid page') ||
|
||||
e.message?.includes('Invalid page');
|
||||
|
||||
if (!isInvalidPage) {
|
||||
errorNotification('Failed to retrieve content', e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -264,8 +264,7 @@ const useCardColumns = () => {
|
|||
};
|
||||
|
||||
const VODsPage = () => {
|
||||
const movies = useVODStore((s) => s.movies);
|
||||
const series = useVODStore((s) => s.series);
|
||||
const currentPageContent = useVODStore((s) => s.currentPageContent); // Direct subscription
|
||||
const allCategories = useVODStore((s) => s.categories);
|
||||
const filters = useVODStore((s) => s.filters);
|
||||
const currentPage = useVODStore((s) => s.currentPage);
|
||||
|
|
@ -288,8 +287,7 @@ const VODsPage = () => {
|
|||
setPageSize(Number(value));
|
||||
localStorage.setItem('vodsPageSize', value);
|
||||
};
|
||||
const fetchMovies = useVODStore((s) => s.fetchMovies);
|
||||
const fetchSeries = useVODStore((s) => s.fetchSeries);
|
||||
const fetchContent = useVODStore((s) => s.fetchContent);
|
||||
const fetchCategories = useVODStore((s) => s.fetchCategories);
|
||||
|
||||
// const showVideo = useVideoStore((s) => s.showVideo); - removed as unused
|
||||
|
|
@ -307,36 +305,10 @@ const VODsPage = () => {
|
|||
|
||||
// Helper function to get display data based on current filters
|
||||
const getDisplayData = () => {
|
||||
if (filters.type === 'series') {
|
||||
return Object.values(series).map((item) => ({
|
||||
...item,
|
||||
_vodType: 'series',
|
||||
}));
|
||||
} else if (filters.type === 'movies') {
|
||||
return Object.values(movies).map((item) => ({
|
||||
...item,
|
||||
_vodType: 'movie',
|
||||
}));
|
||||
} else {
|
||||
// 'all' - combine movies and series, tagging each with its type, then sort alphabetically by name/title
|
||||
const combined = [
|
||||
...Object.values(movies).map((item) => ({
|
||||
...item,
|
||||
_vodType: 'movie',
|
||||
})),
|
||||
...Object.values(series).map((item) => ({
|
||||
...item,
|
||||
_vodType: 'series',
|
||||
})),
|
||||
];
|
||||
return combined.sort((a, b) => {
|
||||
const nameA = (a.name || a.title || '').toLowerCase();
|
||||
const nameB = (b.name || b.title || '').toLowerCase();
|
||||
if (nameA < nameB) return -1;
|
||||
if (nameA > nameB) return 1;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
return (currentPageContent || []).map((item) => ({
|
||||
...item,
|
||||
_vodType: item.contentType === 'movie' ? 'movie' : 'series',
|
||||
}));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -360,17 +332,8 @@ const VODsPage = () => {
|
|||
}, [fetchCategories]);
|
||||
|
||||
useEffect(() => {
|
||||
if (filters.type === 'series') {
|
||||
fetchSeries().finally(() => setInitialLoad(false));
|
||||
} else if (filters.type === 'movies') {
|
||||
fetchMovies().finally(() => setInitialLoad(false));
|
||||
} else {
|
||||
// 'all': fetch both movies and series
|
||||
Promise.all([fetchMovies(), fetchSeries()]).finally(() =>
|
||||
setInitialLoad(false)
|
||||
);
|
||||
}
|
||||
}, [filters, currentPage, pageSize, fetchMovies, fetchSeries]);
|
||||
fetchContent().finally(() => setInitialLoad(false));
|
||||
}, [filters, currentPage, pageSize, fetchContent]);
|
||||
|
||||
const handleVODCardClick = (vod) => {
|
||||
setSelectedVOD(vod);
|
||||
|
|
@ -464,46 +427,25 @@ const VODsPage = () => {
|
|||
</Flex>
|
||||
) : (
|
||||
<>
|
||||
{filters.type === 'series' ? (
|
||||
<Grid gutter="md">
|
||||
{Object.values(series).map((seriesItem) => (
|
||||
<Grid.Col
|
||||
span={12 / columns}
|
||||
key={seriesItem.id}
|
||||
style={{
|
||||
minWidth: MIN_CARD_WIDTH,
|
||||
maxWidth: MAX_CARD_WIDTH,
|
||||
margin: '0 auto',
|
||||
}}
|
||||
>
|
||||
<SeriesCard
|
||||
series={seriesItem}
|
||||
onClick={handleSeriesClick}
|
||||
/>
|
||||
</Grid.Col>
|
||||
))}
|
||||
</Grid>
|
||||
) : (
|
||||
<Grid gutter="md">
|
||||
{getDisplayData().map((item) => (
|
||||
<Grid.Col
|
||||
span={12 / columns}
|
||||
key={item.id}
|
||||
style={{
|
||||
minWidth: MIN_CARD_WIDTH,
|
||||
maxWidth: MAX_CARD_WIDTH,
|
||||
margin: '0 auto',
|
||||
}}
|
||||
>
|
||||
{item._vodType === 'series' ? (
|
||||
<SeriesCard series={item} onClick={handleSeriesClick} />
|
||||
) : (
|
||||
<VODCard vod={item} onClick={handleVODCardClick} />
|
||||
)}
|
||||
</Grid.Col>
|
||||
))}
|
||||
</Grid>
|
||||
)}
|
||||
<Grid gutter="md">
|
||||
{getDisplayData().map((item) => (
|
||||
<Grid.Col
|
||||
span={12 / columns}
|
||||
key={`${item.contentType}_${item.id}`}
|
||||
style={{
|
||||
minWidth: MIN_CARD_WIDTH,
|
||||
maxWidth: MAX_CARD_WIDTH,
|
||||
margin: '0 auto',
|
||||
}}
|
||||
>
|
||||
{item.contentType === 'series' ? (
|
||||
<SeriesCard series={item} onClick={handleSeriesClick} />
|
||||
) : (
|
||||
<VODCard vod={item} onClick={handleVODCardClick} />
|
||||
)}
|
||||
</Grid.Col>
|
||||
))}
|
||||
</Grid>
|
||||
|
||||
{/* Pagination */}
|
||||
{totalPages > 1 && (
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import { create } from 'zustand';
|
|||
import api from '../api';
|
||||
|
||||
const useVODStore = create((set, get) => ({
|
||||
movies: {},
|
||||
series: {},
|
||||
content: {}, // Store for individual content details (when fetching movie/series details)
|
||||
currentPageContent: [], // Store the current page's results
|
||||
episodes: {},
|
||||
categories: {},
|
||||
loading: false,
|
||||
|
|
@ -34,12 +34,12 @@ const useVODStore = create((set, get) => ({
|
|||
currentPage: 1, // Reset to first page when page size changes
|
||||
})),
|
||||
|
||||
fetchMovies: async () => {
|
||||
fetchContent: async () => {
|
||||
try {
|
||||
set({ loading: true, error: null });
|
||||
const state = get();
|
||||
const params = new URLSearchParams();
|
||||
|
||||
const params = new URLSearchParams();
|
||||
params.append('page', state.currentPage);
|
||||
params.append('page_size', state.pageSize);
|
||||
|
||||
|
|
@ -51,60 +51,62 @@ const useVODStore = create((set, get) => ({
|
|||
params.append('category', state.filters.category);
|
||||
}
|
||||
|
||||
const response = await api.getMovies(params);
|
||||
let allResults = [];
|
||||
let totalCount = 0;
|
||||
|
||||
// Handle both paginated and non-paginated responses
|
||||
const results = response.results || response;
|
||||
const count = response.count || results.length;
|
||||
if (state.filters.type === 'movies') {
|
||||
// Fetch only movies
|
||||
const response = await api.getMovies(params);
|
||||
const results = response.results || response;
|
||||
allResults = results.map((item) => ({ ...item, contentType: 'movie' }));
|
||||
totalCount = response.count || results.length;
|
||||
} else if (state.filters.type === 'series') {
|
||||
// Fetch only series
|
||||
const response = await api.getSeries(params);
|
||||
const results = response.results || response;
|
||||
allResults = results.map((item) => ({
|
||||
...item,
|
||||
contentType: 'series',
|
||||
}));
|
||||
totalCount = response.count || results.length;
|
||||
} else {
|
||||
// Use the new unified backend endpoint for 'all' view
|
||||
const response = await api.getAllContent(params);
|
||||
console.log('getAllContent response:', response);
|
||||
console.log('response type:', typeof response);
|
||||
console.log(
|
||||
'response keys:',
|
||||
response ? Object.keys(response) : 'no response'
|
||||
);
|
||||
|
||||
const results = response.results || response;
|
||||
console.log('results:', results);
|
||||
console.log('results type:', typeof results);
|
||||
console.log('results is array:', Array.isArray(results));
|
||||
|
||||
// Check if results is actually an array before calling map
|
||||
if (!Array.isArray(results)) {
|
||||
console.error('Results is not an array:', results);
|
||||
throw new Error('Invalid response format - results is not an array');
|
||||
}
|
||||
|
||||
// The backend already provides content_type and proper sorting/pagination
|
||||
allResults = results.map((item) => ({
|
||||
...item,
|
||||
contentType: item.content_type, // Backend provides this field
|
||||
}));
|
||||
totalCount = response.count || results.length;
|
||||
}
|
||||
|
||||
// Store the current page results directly (don't accumulate all pages)
|
||||
set({
|
||||
movies: results.reduce((acc, movie) => {
|
||||
acc[movie.id] = movie;
|
||||
return acc;
|
||||
}, {}),
|
||||
totalCount: count,
|
||||
currentPageContent: allResults, // This is the paginated data for current page
|
||||
totalCount,
|
||||
loading: false,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch movies:', error);
|
||||
set({ error: 'Failed to load movies.', loading: false });
|
||||
}
|
||||
},
|
||||
|
||||
fetchSeries: async () => {
|
||||
set({ loading: true, error: null });
|
||||
try {
|
||||
const state = get();
|
||||
const params = new URLSearchParams();
|
||||
|
||||
params.append('page', state.currentPage);
|
||||
params.append('page_size', state.pageSize);
|
||||
|
||||
if (state.filters.search) {
|
||||
params.append('search', state.filters.search);
|
||||
}
|
||||
|
||||
if (state.filters.category) {
|
||||
params.append('category', state.filters.category);
|
||||
}
|
||||
|
||||
const response = await api.getSeries(params);
|
||||
|
||||
// Handle both paginated and non-paginated responses
|
||||
const results = response.results || response;
|
||||
const count = response.count || results.length;
|
||||
|
||||
set({
|
||||
series: results.reduce((acc, series) => {
|
||||
acc[series.id] = series;
|
||||
return acc;
|
||||
}, {}),
|
||||
totalCount: count,
|
||||
loading: false,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch series:', error);
|
||||
set({ error: 'Failed to load series.', loading: false });
|
||||
console.error('Failed to fetch content:', error);
|
||||
set({ error: 'Failed to load content.', loading: false });
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -158,9 +160,12 @@ const useVODStore = create((set, get) => ({
|
|||
};
|
||||
console.log('Fetched Movie Details:', movieDetails);
|
||||
set((state) => ({
|
||||
movies: {
|
||||
...state.movies,
|
||||
[movieDetails.id]: movieDetails,
|
||||
content: {
|
||||
...state.content,
|
||||
[`movie_${movieDetails.id}`]: {
|
||||
...movieDetails,
|
||||
contentType: 'movie',
|
||||
},
|
||||
},
|
||||
loading: false,
|
||||
}));
|
||||
|
|
@ -261,36 +266,48 @@ const useVODStore = create((set, get) => ({
|
|||
|
||||
addMovie: (movie) =>
|
||||
set((state) => ({
|
||||
movies: { ...state.movies, [movie.id]: movie },
|
||||
content: {
|
||||
...state.content,
|
||||
[`movie_${movie.id}`]: { ...movie, contentType: 'movie' },
|
||||
},
|
||||
})),
|
||||
|
||||
updateMovie: (movie) =>
|
||||
set((state) => ({
|
||||
movies: { ...state.movies, [movie.id]: movie },
|
||||
content: {
|
||||
...state.content,
|
||||
[`movie_${movie.id}`]: { ...movie, contentType: 'movie' },
|
||||
},
|
||||
})),
|
||||
|
||||
removeMovie: (movieId) =>
|
||||
set((state) => {
|
||||
const updatedMovies = { ...state.movies };
|
||||
delete updatedMovies[movieId];
|
||||
return { movies: updatedMovies };
|
||||
const updatedContent = { ...state.content };
|
||||
delete updatedContent[`movie_${movieId}`];
|
||||
return { content: updatedContent };
|
||||
}),
|
||||
|
||||
addSeries: (series) =>
|
||||
set((state) => ({
|
||||
series: { ...state.series, [series.id]: series },
|
||||
content: {
|
||||
...state.content,
|
||||
[`series_${series.id}`]: { ...series, contentType: 'series' },
|
||||
},
|
||||
})),
|
||||
|
||||
updateSeries: (series) =>
|
||||
set((state) => ({
|
||||
series: { ...state.series, [series.id]: series },
|
||||
content: {
|
||||
...state.content,
|
||||
[`series_${series.id}`]: { ...series, contentType: 'series' },
|
||||
},
|
||||
})),
|
||||
|
||||
removeSeries: (seriesId) =>
|
||||
set((state) => {
|
||||
const updatedSeries = { ...state.series };
|
||||
delete updatedSeries[seriesId];
|
||||
return { series: updatedSeries };
|
||||
const updatedContent = { ...state.content };
|
||||
delete updatedContent[`series_${seriesId}`];
|
||||
return { content: updatedContent };
|
||||
}),
|
||||
|
||||
fetchSeriesInfo: async (seriesId) => {
|
||||
|
|
@ -369,9 +386,9 @@ const useVODStore = create((set, get) => ({
|
|||
}
|
||||
|
||||
set((state) => ({
|
||||
series: {
|
||||
...state.series,
|
||||
[seriesInfo.id]: seriesInfo,
|
||||
content: {
|
||||
...state.content,
|
||||
[`series_${seriesInfo.id}`]: { ...seriesInfo, contentType: 'series' },
|
||||
},
|
||||
loading: false,
|
||||
}));
|
||||
|
|
@ -387,6 +404,29 @@ const useVODStore = create((set, get) => ({
|
|||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Helper methods for getting filtered content
|
||||
getFilteredContent: () => {
|
||||
const state = get();
|
||||
// Return the current page content directly - backend handles all filtering/pagination
|
||||
return state.currentPageContent;
|
||||
},
|
||||
|
||||
getMovies: () => {
|
||||
const state = get();
|
||||
return Object.values(state.content).filter(
|
||||
(item) => item.contentType === 'movie'
|
||||
);
|
||||
},
|
||||
|
||||
getSeries: () => {
|
||||
const state = get();
|
||||
return Object.values(state.content).filter(
|
||||
(item) => item.contentType === 'series'
|
||||
);
|
||||
},
|
||||
|
||||
clearContent: () => set({ content: {}, totalCount: 0 }),
|
||||
}));
|
||||
|
||||
export default useVODStore;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue