From 88a9f8b14e2afd9429476c54895c7e663be66ffa Mon Sep 17 00:00:00 2001 From: SergeantPanda Date: Sun, 3 Aug 2025 10:40:17 -0500 Subject: [PATCH] Fix vods not displaying in the webui. --- apps/api/urls.py | 1 + apps/vod/api_urls.py | 20 +++ frontend/src/api.js | 63 +++++---- frontend/src/store/auth.jsx | 2 + frontend/src/store/useVODStore.jsx | 219 ++++++++++++++++++----------- 5 files changed, 190 insertions(+), 115 deletions(-) create mode 100644 apps/vod/api_urls.py diff --git a/apps/api/urls.py b/apps/api/urls.py index a2810f06..2b42c6f3 100644 --- a/apps/api/urls.py +++ b/apps/api/urls.py @@ -26,6 +26,7 @@ urlpatterns = [ path('hdhr/', include(('apps.hdhr.api_urls', 'hdhr'), namespace='hdhr')), path('m3u/', include(('apps.m3u.api_urls', 'm3u'), namespace='m3u')), path('core/', include(('core.api_urls', 'core'), namespace='core')), + path('vod/', include(('apps.vod.api_urls', 'vod'), namespace='vod')), # path('output/', include(('apps.output.api_urls', 'output'), namespace='output')), #path('player/', include(('apps.player.api_urls', 'player'), namespace='player')), #path('settings/', include(('apps.settings.api_urls', 'settings'), namespace='settings')), diff --git a/apps/vod/api_urls.py b/apps/vod/api_urls.py new file mode 100644 index 00000000..43c590a3 --- /dev/null +++ b/apps/vod/api_urls.py @@ -0,0 +1,20 @@ +from django.urls import path, include +from rest_framework.routers import DefaultRouter +from .api_views import ( + MovieViewSet, + EpisodeViewSet, + SeriesViewSet, + VODCategoryViewSet, + VODConnectionViewSet, +) + +app_name = 'vod' + +router = DefaultRouter() +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'connections', VODConnectionViewSet, basename='vodconnection') + +urlpatterns = router.urls diff --git a/frontend/src/api.js b/frontend/src/api.js index 1e046cbe..748754c8 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -1700,16 +1700,39 @@ export default class API { } // VOD Methods - static async getVODs(params = {}) { + static async getVODs(params = new URLSearchParams()) { try { - const searchParams = new URLSearchParams(params); - const response = await request(`${host}/api/vod/movies/?${searchParams.toString()}`); + const response = await request( + `${host}/api/vod/movies/?${params.toString()}` + ); return response; } catch (e) { errorNotification('Failed to retrieve VODs', e); } } + static async getVODSeries(params = new URLSearchParams()) { + try { + const response = await request( + `${host}/api/vod/series/?${params.toString()}` + ); + return response; + } catch (e) { + errorNotification('Failed to retrieve VOD series', e); + } + } + + static async getSeriesEpisodes(seriesId) { + try { + const response = await request( + `${host}/api/vod/series/${seriesId}/episodes/` + ); + return response; + } catch (e) { + errorNotification('Failed to retrieve series episodes', e); + } + } + static async getVODCategories() { try { const response = await request(`${host}/api/vod/categories/`); @@ -1719,43 +1742,21 @@ export default class API { } } - static async getSeries(params = {}) { + static async getVODInfo(vodId) { try { - const searchParams = new URLSearchParams(params); - const response = await request(`${host}/api/vod/series/?${searchParams.toString()}`); + const response = await request(`${host}/api/vod/movies/${vodId}/`); return response; } catch (e) { - errorNotification('Failed to retrieve series', e); + errorNotification('Failed to retrieve VOD info', e); } } - static async getSeriesEpisodes(seriesId, params = {}) { + static async getSeriesInfo(seriesId) { try { - const searchParams = new URLSearchParams(params); - const response = await request(`${host}/api/vod/series/${seriesId}/episodes/?${searchParams.toString()}`); + const response = await request(`${host}/api/vod/series/${seriesId}/`); return response; } catch (e) { - errorNotification('Failed to retrieve series episodes', e); - } - } - - static async getVODConnections() { - try { - const response = await request(`${host}/api/vod/connections/`); - return response; - } catch (e) { - errorNotification('Failed to retrieve VOD connections', e); - } - } - - static async refreshVODContent(accountId) { - try { - const response = await request(`${host}/api/m3u/accounts/${accountId}/refresh-vod/`, { - method: 'POST' - }); - return response; - } catch (e) { - errorNotification('Failed to refresh VOD content', e); + errorNotification('Failed to retrieve series info', e); } } diff --git a/frontend/src/store/auth.jsx b/frontend/src/store/auth.jsx index a42403ba..80d5e7c3 100644 --- a/frontend/src/store/auth.jsx +++ b/frontend/src/store/auth.jsx @@ -9,6 +9,7 @@ import useUserAgentsStore from './userAgents'; import useUsersStore from './users'; import API from '../api'; import { USER_LEVELS } from '../constants'; +import useVODStore from './useVODStore'; const decodeToken = (token) => { if (!token) return null; @@ -57,6 +58,7 @@ const useAuthStore = create((set, get) => ({ useChannelsStore.getState().fetchLogos(), useStreamProfilesStore.getState().fetchProfiles(), useUserAgentsStore.getState().fetchUserAgents(), + useVODStore.getState().fetchCategories(), // Add VOD categories ]); if (user.user_level >= USER_LEVELS.ADMIN) { diff --git a/frontend/src/store/useVODStore.jsx b/frontend/src/store/useVODStore.jsx index ed723c80..cb79701b 100644 --- a/frontend/src/store/useVODStore.jsx +++ b/frontend/src/store/useVODStore.jsx @@ -1,131 +1,182 @@ import { create } from 'zustand'; -import API from '../api'; - -const host = window.location.origin; +import api from '../api'; const useVODStore = create((set, get) => ({ - // State vods: {}, series: {}, categories: {}, loading: false, error: null, - - // Filters and pagination - currentPage: 1, - pageSize: 50, - totalCount: 0, filters: { type: 'all', // 'all', 'movies', 'series' - category: '', search: '', - year: null, - seriesId: null + category: '', }, + currentPage: 1, + totalCount: 0, + pageSize: 20, - // Actions - setLoading: (loading) => set({ loading }), - setError: (error) => set({ error }), + setFilters: (newFilters) => + set((state) => ({ + filters: { ...state.filters, ...newFilters }, + currentPage: 1, // Reset to first page when filters change + })), - setFilters: (newFilters) => set((state) => ({ - filters: { ...state.filters, ...newFilters }, - currentPage: 1 // Reset to first page when filters change - })), - - setPage: (page) => set({ currentPage: page }), + setPage: (page) => + set(() => ({ + currentPage: page, + })), fetchVODs: async () => { - const { filters, currentPage, pageSize } = get(); set({ loading: true, error: null }); - try { - const params = new URLSearchParams({ - page: currentPage.toString(), - page_size: pageSize.toString(), - ...Object.fromEntries( - Object.entries(filters).filter(([_, value]) => - value !== null && value !== '' && value !== 'all' - ) - ) - }); + const state = get(); + const params = new URLSearchParams(); - const response = await API.request(`${host}/api/vod/vods/?${params}`); + 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); + } + + if (state.filters.type === 'movies') { + params.append('type', 'movie'); + } + + const response = await api.getVODs(params); + + // Handle both paginated and non-paginated responses + const results = response.results || response; + const count = response.count || results.length; set({ - vods: response.results || response, - totalCount: response.count || response.length || 0, - loading: false + vods: results.reduce((acc, vod) => { + acc[vod.id] = vod; + return acc; + }, {}), + totalCount: count, + loading: false, }); } catch (error) { - set({ error: error.message, loading: false }); + console.error('Failed to fetch VODs:', error); + set({ error: 'Failed to load VODs.', loading: false }); } }, fetchSeries: async () => { - const { filters, currentPage, pageSize } = get(); set({ loading: true, error: null }); - try { - const params = new URLSearchParams({ - page: currentPage.toString(), - page_size: pageSize.toString(), - search: filters.search || '' - }); + const state = get(); + const params = new URLSearchParams(); - const response = await API.request(`${host}/api/vod/series/?${params}`); + 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.getVODSeries(params); + + // Handle both paginated and non-paginated responses + const results = response.results || response; + const count = response.count || results.length; set({ - series: response.results || response, - totalCount: response.count || response.length || 0, - loading: false + series: results.reduce((acc, series) => { + acc[series.id] = series; + return acc; + }, {}), + totalCount: count, + loading: false, }); } catch (error) { - set({ error: error.message, loading: false }); - } - }, - - fetchCategories: async () => { - set({ loading: true, error: null }); - - try { - const response = await API.request(`${host}/api/vod/categories/`); - set({ - categories: response.results || response, - loading: false - }); - } catch (error) { - set({ error: error.message, loading: false }); + console.error('Failed to fetch series:', error); + set({ error: 'Failed to load series.', loading: false }); } }, fetchSeriesEpisodes: async (seriesId) => { set({ loading: true, error: null }); - try { - const response = await API.request(`${host}/api/vod/series/${seriesId}/episodes/`); - set({ - vods: response.results || response, - totalCount: response.count || response.length || 0, - loading: false - }); + const response = await api.getSeriesEpisodes(seriesId); + + set((state) => ({ + vods: { + ...state.vods, + ...response.reduce((acc, episode) => { + acc[episode.id] = episode; + return acc; + }, {}), + }, + loading: false, + })); } catch (error) { - set({ error: error.message, loading: false }); + console.error('Failed to fetch series episodes:', error); + set({ error: 'Failed to load episodes.', loading: false }); } }, - // Clear data - clearVODs: () => set({ vods: {}, totalCount: 0 }), - clearSeries: () => set({ series: {} }), - clearFilters: () => set({ - filters: { - type: 'all', - category: '', - search: '', - year: null, - seriesId: null - }, - currentPage: 1 - }) + fetchCategories: async () => { + try { + const response = await api.getVODCategories(); + // Handle both array and paginated responses + const results = response.results || response; + + set({ + categories: results.reduce((acc, category) => { + acc[category.id] = category; + return acc; + }, {}), + }); + } catch (error) { + console.error('Failed to fetch VOD categories:', error); + set({ error: 'Failed to load categories.' }); + } + }, + + addVOD: (vod) => + set((state) => ({ + vods: { ...state.vods, [vod.id]: vod }, + })), + + updateVOD: (vod) => + set((state) => ({ + vods: { ...state.vods, [vod.id]: vod }, + })), + + removeVOD: (vodId) => + set((state) => { + const updatedVODs = { ...state.vods }; + delete updatedVODs[vodId]; + return { vods: updatedVODs }; + }), + + addSeries: (series) => + set((state) => ({ + series: { ...state.series, [series.id]: series }, + })), + + updateSeries: (series) => + set((state) => ({ + series: { ...state.series, [series.id]: series }, + })), + + removeSeries: (seriesId) => + set((state) => { + const updatedSeries = { ...state.series }; + delete updatedSeries[seriesId]; + return { series: updatedSeries }; + }), })); export default useVODStore;