Fix vods not displaying in the webui.

This commit is contained in:
SergeantPanda 2025-08-03 10:40:17 -05:00
parent d9192d003d
commit 88a9f8b14e
5 changed files with 190 additions and 115 deletions

View file

@ -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')),

20
apps/vod/api_urls.py Normal file
View file

@ -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

View file

@ -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);
}
}

View file

@ -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) {

View file

@ -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;