diff --git a/frontend/src/pages/EPG.js b/frontend/src/pages/EPG.js
index 0442c711..24909f05 100644
--- a/frontend/src/pages/EPG.js
+++ b/frontend/src/pages/EPG.js
@@ -1,27 +1,27 @@
-import React, { useState } from 'react';
-import { Box, Snackbar } from '@mui/material';
-import UserAgentsTable from '../components/tables/UserAgentsTable';
-import EPGsTable from '../components/tables/EPGsTable';
+import React from "react";
+import { Box } from "@mui/material";
+import UserAgentsTable from "../components/tables/UserAgentsTable";
+import EPGsTable from "../components/tables/EPGsTable";
const EPGPage = () => {
return (
-
+
-
+
- )
+ );
};
export default EPGPage;
diff --git a/frontend/src/pages/Guide.js b/frontend/src/pages/Guide.js
index fdfc51b9..ce292fe8 100644
--- a/frontend/src/pages/Guide.js
+++ b/frontend/src/pages/Guide.js
@@ -1,63 +1,63 @@
-import React from "react";
+import React from 'react';
import { useEpg, Epg, Layout } from 'planby';
-import API from '../api'
+import API from '../api';
function App() {
- const [channels, setChannels] = React.useState([])
- const [epg, setEpg] = React.useState([])
+ const [channels, setChannels] = React.useState([]);
+ const [epg, setEpg] = React.useState([]);
const fetchChannels = async () => {
- const channels = await API.getChannels()
- const retval = []
+ const channels = await API.getChannels();
+ const retval = [];
for (const channel of channels) {
- if (!channel.channel_name.includes("Nickelod")) {
- continue
+ if (!channel.tvg_id) {
+ continue;
}
- console.log(channel)
+ console.log(channel);
retval.push({
- uuid: "Nickelodeon (East).us",
- type: "channel",
+ uuid: channel.tvg_id,
+ type: 'channel',
title: channel.channel_name,
- country: "USA",
- provider: channel.channel_group?.name || "Default",
- logo: channel.logo_url || "/images/logo.png",
+ country: 'USA',
+ provider: channel.channel_group?.name || 'Default',
+ logo: channel.logo_url || '/images/logo.png',
year: 2025,
- })
+ });
}
- setChannels(retval)
+ setChannels(retval);
return retval;
- }
+ };
const fetchEpg = async () => {
const programs = await API.getGrid();
- const retval = []
- console.log(programs)
+ const retval = [];
+ console.log(programs);
for (const program of programs.data) {
retval.push({
id: program.id,
- channelUuid: "Nickelodeon (East).us",
+ channelUuid: 'Nickelodeon (East).us',
description: program.description,
title: program.title,
since: program.start_time,
till: program.end_time,
- })
+ });
}
- setEpg(retval)
+ setEpg(retval);
return retval;
- }
+ };
const fetchData = async () => {
- const channels = await fetchChannels()
- const epg = await fetchEpg()
+ const channels = await fetchChannels();
+ const epg = await fetchEpg();
- setChannels(channels)
- setEpg(epg)
- }
+ setChannels(channels);
+ setEpg(epg);
+ };
if (channels.length === 0) {
- fetchData()
+ fetchData();
}
const formatDate = (date) => date.toISOString().split('T')[0] + 'T00:00:00';
@@ -74,19 +74,17 @@ function App() {
} = useEpg({
epg,
channels,
- startDate: '2025/02/24', // or 2022-02-02T00:00:00
+ startDate: '2025-02-25T11:00:00', // or 2022-02-02T00:00:00
width: '100%',
height: 600,
});
return (
-
-
-
-
+
+
+
+
);
}
diff --git a/frontend/src/pages/Guide/components/ChannelItem.js b/frontend/src/pages/Guide/components/ChannelItem.js
deleted file mode 100644
index fcb67a26..00000000
--- a/frontend/src/pages/Guide/components/ChannelItem.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { ChannelBox, ChannelLogo } from "planby";
-
-export const ChannelItem = ({ channel }) => {
- const { position, logo } = channel;
- return (
-
diff --git a/frontend/src/pages/Login.js b/frontend/src/pages/Login.js
index 4e69545b..5ed4f203 100644
--- a/frontend/src/pages/Login.js
+++ b/frontend/src/pages/Login.js
@@ -1,8 +1,8 @@
-import React from 'react';
-import LoginForm from '../components/forms/LoginForm';
+import React from "react";
+import LoginForm from "../components/forms/LoginForm";
const Login = () => {
- return
+ return
;
};
export default Login;
diff --git a/frontend/src/pages/M3U.js b/frontend/src/pages/M3U.js
index ea4bde55..9fd7e2c2 100644
--- a/frontend/src/pages/M3U.js
+++ b/frontend/src/pages/M3U.js
@@ -1,17 +1,16 @@
-import React, { useState } from 'react';
-import useUserAgentsStore from '../store/userAgents';
-import { Box } from '@mui/material';
-import M3UsTable from '../components/tables/M3UsTable';
-import UserAgentsTable from '../components/tables/UserAgentsTable';
-import usePlaylistsStore from '../store/playlists';
-import API from '../api'
-import M3UForm from '../components/forms/M3U'
+import React, { useState } from "react";
+import useUserAgentsStore from "../store/userAgents";
+import { Box } from "@mui/material";
+import M3UsTable from "../components/tables/M3UsTable";
+import UserAgentsTable from "../components/tables/UserAgentsTable";
+import usePlaylistsStore from "../store/playlists";
+import API from "../api";
+import M3UForm from "../components/forms/M3U";
const M3UPage = () => {
const isLoading = useUserAgentsStore((state) => state.isLoading);
const error = useUserAgentsStore((state) => state.error);
- const playlists = usePlaylistsStore(state => state.playlists)
-
+ const playlists = usePlaylistsStore((state) => state.playlists);
const [playlist, setPlaylist] = useState(null);
const [playlistModalOpen, setPlaylistModalOpen] = useState(false);
@@ -20,26 +19,26 @@ const M3UPage = () => {
const [userAgentModalOpen, setUserAgentModalOpen] = useState(false);
const editUserAgent = async (userAgent = null) => {
- setUserAgent(userAgent)
- setUserAgentModalOpen(true)
- }
+ setUserAgent(userAgent);
+ setUserAgentModalOpen(true);
+ };
const editPlaylist = async (playlist = null) => {
- setPlaylist(playlist)
- setPlaylistModalOpen(true)
- }
+ setPlaylist(playlist);
+ setPlaylistModalOpen(true);
+ };
const deleteUserAgent = async (ids) => {
if (Array.isArray(ids)) {
- await API.deleteUserAgents(ids)
+ await API.deleteUserAgents(ids);
} else {
- await API.deleteUserAgent(ids)
+ await API.deleteUserAgent(ids);
}
- }
+ };
const deletePlaylist = async (id) => {
- await API.deletePlaylist(id)
- }
+ await API.deletePlaylist(id);
+ };
if (isLoading) return
Loading...
;
if (error) return
Error: {error}
;
@@ -47,17 +46,17 @@ const M3UPage = () => {
return (
-
+
-
+
@@ -66,10 +65,8 @@ const M3UPage = () => {
isOpen={playlistModalOpen}
onClose={() => setPlaylistModalOpen(false)}
/>
-
-
- )
+ );
};
export default M3UPage;
diff --git a/frontend/src/pages/StreamProfiles.js b/frontend/src/pages/StreamProfiles.js
index 2305f5c6..615bace2 100644
--- a/frontend/src/pages/StreamProfiles.js
+++ b/frontend/src/pages/StreamProfiles.js
@@ -1,10 +1,8 @@
-import React, { useState } from 'react';
-import StreamProfilesTable from '../components/tables/StreamProfilesTable';
+import React from "react";
+import StreamProfilesTable from "../components/tables/StreamProfilesTable";
const StreamProfilesPage = () => {
- return (
-
- )
+ return ;
};
export default StreamProfilesPage;
diff --git a/frontend/src/store/auth.js b/frontend/src/store/auth.js
index 6295a7b5..43421cca 100644
--- a/frontend/src/store/auth.js
+++ b/frontend/src/store/auth.js
@@ -1,7 +1,11 @@
-// src/auth/authStore.js
-
-import {create} from 'zustand';
-import API from '../api'
+import { create } from 'zustand';
+import API from '../api';
+import useChannelsStore from './channels';
+import useStreamsStore from './streams';
+import useUserAgentsStore from './userAgents';
+import usePlaylistsStore from './playlists';
+import useEPGsStore from './epgs';
+import useStreamProfilesStore from './streamProfiles';
const decodeToken = (token) => {
if (!token) return null;
@@ -21,30 +25,45 @@ const useAuthStore = create((set, get) => ({
tokenExpiration: localStorage.getItem('tokenExpiration') || null,
isAuthenticated: false,
- getToken: async () => {
- const expiration = localStorage.getItem('tokenExpiration')
- const tokenExpiration = localStorage.getItem('tokenExpiration');
- let accessToken = null;
- if (isTokenExpired(tokenExpiration)) {
- accessToken = await get().refreshToken();
- } else {
- accessToken = localStorage.getItem('accessToken');
- }
+ setIsAuthenticated: (isAuthenticated) => set({ isAuthenticated }),
- return accessToken;
+ initData: async () => {
+ console.log('fetching data');
+ await Promise.all([
+ useChannelsStore.getState().fetchChannels(),
+ useChannelsStore.getState().fetchChannelGroups(),
+ useStreamsStore.getState().fetchStreams(),
+ useUserAgentsStore.getState().fetchUserAgents(),
+ usePlaylistsStore.getState().fetchPlaylists(),
+ useEPGsStore.getState().fetchEPGs(),
+ useStreamProfilesStore.getState().fetchProfiles(),
+ ]);
+ },
+
+ getToken: async () => {
+ const expiration = localStorage.getItem('tokenExpiration');
+ const tokenExpiration = localStorage.getItem('tokenExpiration');
+ let accessToken = null;
+ if (isTokenExpired(tokenExpiration)) {
+ accessToken = await get().refreshToken();
+ } else {
+ accessToken = localStorage.getItem('accessToken');
+ }
+
+ return accessToken;
},
// Action to login
- login: async ({username, password}) => {
+ login: async ({ username, password }) => {
try {
- const response = await API.login(username, password)
+ const response = await API.login(username, password);
if (response.access) {
- const expiration = decodeToken(response.access)
+ const expiration = decodeToken(response.access);
set({
accessToken: response.access,
refreshToken: response.refresh,
tokenExpiration: expiration, // 1 hour from now
- isAuthenticated: true
+ isAuthenticated: true,
});
// Store in localStorage
localStorage.setItem('accessToken', response.access);
@@ -62,7 +81,7 @@ const useAuthStore = create((set, get) => ({
if (!refreshToken) return;
try {
- const data = await API.refreshToken(refreshToken)
+ const data = await API.refreshToken(refreshToken);
if (data.access) {
set({
accessToken: data.access,
@@ -76,7 +95,7 @@ const useAuthStore = create((set, get) => ({
}
} catch (error) {
console.error('Token refresh failed:', error);
- get().logout()
+ get().logout();
}
return false;
@@ -88,7 +107,7 @@ const useAuthStore = create((set, get) => ({
accessToken: null,
refreshToken: null,
tokenExpiration: null,
- isAuthenticated: false
+ isAuthenticated: false,
});
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
@@ -99,10 +118,13 @@ const useAuthStore = create((set, get) => ({
const refreshToken = localStorage.getItem('refreshToken') || null;
if (refreshToken) {
- await get().refreshToken()
- } else {
- await get().logout()
+ const loggedIn = await get().refreshToken();
+ if (loggedIn) {
+ return true;
+ }
}
+
+ return false;
},
}));
diff --git a/frontend/src/store/channels.js b/frontend/src/store/channels.js
index 24a5aa8a..10338c7d 100644
--- a/frontend/src/store/channels.js
+++ b/frontend/src/store/channels.js
@@ -1,7 +1,5 @@
-// src/stores/channelsStore.js
-
-import { create } from 'zustand';
-import api from '../api'; // Your API helper that manages token & requests
+import { create } from "zustand";
+import api from "../api";
const useChannelsStore = create((set) => ({
channels: [],
@@ -15,8 +13,8 @@ const useChannelsStore = create((set) => ({
const channels = await api.getChannels();
set({ channels: channels, isLoading: false });
} catch (error) {
- console.error('Failed to fetch channels:', error);
- set({ error: 'Failed to load channels.', isLoading: false });
+ console.error("Failed to fetch channels:", error);
+ set({ error: "Failed to load channels.", isLoading: false });
}
},
@@ -26,22 +24,41 @@ const useChannelsStore = create((set) => ({
const channelGroups = await api.getChannelGroups();
set({ channelGroups: channelGroups, isLoading: false });
} catch (error) {
- console.error('Failed to fetch channel groups:', error);
- set({ error: 'Failed to load channel groups.', isLoading: false });
+ console.error("Failed to fetch channel groups:", error);
+ set({ error: "Failed to load channel groups.", isLoading: false });
}
},
- addChannel: (newChannel) => set((state) => ({
- channels: [...state.channels, newChannel],
- })),
+ addChannel: (newChannel) =>
+ set((state) => ({
+ channels: [...state.channels, newChannel],
+ })),
- updateChannel: (userAgent) => set((state) => ({
- channels: state.channels.map(chan => chan.id === userAgent.id ? userAgent : chan),
- })),
+ updateChannel: (userAgent) =>
+ set((state) => ({
+ channels: state.channels.map((chan) =>
+ chan.id === userAgent.id ? userAgent : chan,
+ ),
+ })),
- removeChannels: (channelIds) => set((state) => ({
- channels: state.channels.filter((channel) => !channelIds.includes(channel.id)),
- })),
+ removeChannels: (channelIds) =>
+ set((state) => ({
+ channels: state.channels.filter(
+ (channel) => !channelIds.includes(channel.id),
+ ),
+ })),
+
+ addChannelGroup: (newChannelGroup) =>
+ set((state) => ({
+ channelGroups: [...state.channelGroups, newChannelGroup],
+ })),
+
+ updateChannelGroup: (channelGroup) =>
+ set((state) => ({
+ channelGroups: state.channelGroups.map((group) =>
+ group.id === channelGroup.id ? channelGroup : group,
+ ),
+ })),
}));
export default useChannelsStore;
diff --git a/frontend/src/store/epgs.js b/frontend/src/store/epgs.js
index c4c52d64..14cfd623 100644
--- a/frontend/src/store/epgs.js
+++ b/frontend/src/store/epgs.js
@@ -1,5 +1,5 @@
-import { create } from 'zustand';
-import api from '../api'; // Your API helper that manages token & requests
+import { create } from "zustand";
+import api from "../api";
const useEPGsStore = create((set) => ({
epgs: [],
@@ -12,18 +12,20 @@ const useEPGsStore = create((set) => ({
const epgs = await api.getEPGs();
set({ epgs: epgs, isLoading: false });
} catch (error) {
- console.error('Failed to fetch epgs:', error);
- set({ error: 'Failed to load epgs.', isLoading: false });
+ console.error("Failed to fetch epgs:", error);
+ set({ error: "Failed to load epgs.", isLoading: false });
}
},
- addEPG: (newPlaylist) => set((state) => ({
- epgs: [...state.epgs, newPlaylist],
- })),
+ addEPG: (newPlaylist) =>
+ set((state) => ({
+ epgs: [...state.epgs, newPlaylist],
+ })),
- removeEPGs: (epgIds) => set((state) => ({
- epgs: state.epgs.filter((epg) => !epgIds.includes(epg.id)),
- })),
+ removeEPGs: (epgIds) =>
+ set((state) => ({
+ epgs: state.epgs.filter((epg) => !epgIds.includes(epg.id)),
+ })),
}));
export default useEPGsStore;
diff --git a/frontend/src/store/playlists.js b/frontend/src/store/playlists.js
index ae5fde77..06930b73 100644
--- a/frontend/src/store/playlists.js
+++ b/frontend/src/store/playlists.js
@@ -1,5 +1,5 @@
-import { create } from 'zustand';
-import api from '../api'; // Your API helper that manages token & requests
+import { create } from "zustand";
+import api from "../api";
const usePlaylistsStore = create((set) => ({
playlists: [],
@@ -12,22 +12,29 @@ const usePlaylistsStore = create((set) => ({
const playlists = await api.getPlaylists();
set({ playlists: playlists, isLoading: false });
} catch (error) {
- console.error('Failed to fetch playlists:', error);
- set({ error: 'Failed to load playlists.', isLoading: false });
+ console.error("Failed to fetch playlists:", error);
+ set({ error: "Failed to load playlists.", isLoading: false });
}
},
- addPlaylist: (newPlaylist) => set((state) => ({
- playlists: [...state.playlists, newPlaylist],
- })),
+ addPlaylist: (newPlaylist) =>
+ set((state) => ({
+ playlists: [...state.playlists, newPlaylist],
+ })),
- updatePlaylist: (playlist) => set((state) => ({
- playlists: state.playlists.map(pl => pl.id === playlist.id ? playlist : pl),
- })),
+ updatePlaylist: (playlist) =>
+ set((state) => ({
+ playlists: state.playlists.map((pl) =>
+ pl.id === playlist.id ? playlist : pl,
+ ),
+ })),
- removePlaylists: (playlistIds) => set((state) => ({
- playlists: state.playlists.filter((playlist) => !playlistIds.includes(playlist.id)),
- })),
+ removePlaylists: (playlistIds) =>
+ set((state) => ({
+ playlists: state.playlists.filter(
+ (playlist) => !playlistIds.includes(playlist.id),
+ ),
+ })),
}));
export default usePlaylistsStore;
diff --git a/frontend/src/store/streamProfiles.js b/frontend/src/store/streamProfiles.js
index 94576f26..1b98c662 100644
--- a/frontend/src/store/streamProfiles.js
+++ b/frontend/src/store/streamProfiles.js
@@ -1,5 +1,5 @@
import { create } from 'zustand';
-import api from '../api'; // Your API helper that manages token & requests
+import api from '../api';
const useStreamProfilesStore = create((set) => ({
profiles: [],
@@ -17,17 +17,24 @@ const useStreamProfilesStore = create((set) => ({
}
},
- addStreamProfile: (profile) => set((state) => ({
- profiles: [...state.profiles, profile],
- })),
+ addStreamProfile: (profile) =>
+ set((state) => ({
+ profiles: [...state.profiles, profile],
+ })),
- updateStreamProfile: (profile) => set((state) => ({
- profiles: state.profiles.map(prof => prof.id === profile.id ? profile : prof),
- })),
+ updateStreamProfile: (profile) =>
+ set((state) => ({
+ profiles: state.profiles.map((prof) =>
+ prof.id === profile.id ? profile : prof
+ ),
+ })),
- removeStreamProfiles: (propfileIds) => set((state) => ({
- profiles: state.profiles.filter((profile) => !propfileIds.includes(profile.id)),
- })),
+ removeStreamProfiles: (propfileIds) =>
+ set((state) => ({
+ profiles: state.profiles.filter(
+ (profile) => !propfileIds.includes(profile.id)
+ ),
+ })),
}));
export default useStreamProfilesStore;
diff --git a/frontend/src/store/streams.js b/frontend/src/store/streams.js
index c330ef20..215a17c8 100644
--- a/frontend/src/store/streams.js
+++ b/frontend/src/store/streams.js
@@ -1,5 +1,5 @@
-import { create } from 'zustand';
-import api from '../api'; // Your API helper that manages token & requests
+import { create } from "zustand";
+import api from "../api";
const useStreamsStore = create((set) => ({
streams: [],
@@ -12,22 +12,25 @@ const useStreamsStore = create((set) => ({
const streams = await api.getStreams();
set({ streams: streams, isLoading: false });
} catch (error) {
- console.error('Failed to fetch streams:', error);
- set({ error: 'Failed to load streams.', isLoading: false });
+ console.error("Failed to fetch streams:", error);
+ set({ error: "Failed to load streams.", isLoading: false });
}
},
- addStream: (stream) => set((state) => ({
- streams: [...state.streams, stream],
- })),
+ addStream: (stream) =>
+ set((state) => ({
+ streams: [...state.streams, stream],
+ })),
- updateStream: (stream) => set((state) => ({
- streams: state.streams.map(st => st.id === stream.id ? stream : st),
- })),
+ updateStream: (stream) =>
+ set((state) => ({
+ streams: state.streams.map((st) => (st.id === stream.id ? stream : st)),
+ })),
- removeStreams: (streamIds) => set((state) => ({
- streams: state.streams.filter((stream) => !streamIds.includes(stream.id)),
- })),
+ removeStreams: (streamIds) =>
+ set((state) => ({
+ streams: state.streams.filter((stream) => !streamIds.includes(stream.id)),
+ })),
}));
export default useStreamsStore;
diff --git a/frontend/src/store/userAgents.js b/frontend/src/store/userAgents.js
index 8d426b13..6693e830 100644
--- a/frontend/src/store/userAgents.js
+++ b/frontend/src/store/userAgents.js
@@ -1,5 +1,5 @@
-import { create } from 'zustand';
-import api from '../api'; // Your API helper that manages token & requests
+import { create } from "zustand";
+import api from "../api";
const useUserAgentsStore = create((set) => ({
userAgents: [],
@@ -12,22 +12,29 @@ const useUserAgentsStore = create((set) => ({
const userAgents = await api.getUserAgents();
set({ userAgents: userAgents, isLoading: false });
} catch (error) {
- console.error('Failed to fetch userAgents:', error);
- set({ error: 'Failed to load userAgents.', isLoading: false });
+ console.error("Failed to fetch userAgents:", error);
+ set({ error: "Failed to load userAgents.", isLoading: false });
}
},
- addUserAgent: (userAgent) => set((state) => ({
- userAgents: [...state.userAgents, userAgent],
- })),
+ addUserAgent: (userAgent) =>
+ set((state) => ({
+ userAgents: [...state.userAgents, userAgent],
+ })),
- updateUserAgent: (userAgent) => set((state) => ({
- userAgents: state.userAgents.map(ua => ua.id === userAgent.id ? userAgent : ua),
- })),
+ updateUserAgent: (userAgent) =>
+ set((state) => ({
+ userAgents: state.userAgents.map((ua) =>
+ ua.id === userAgent.id ? userAgent : ua,
+ ),
+ })),
- removeUserAgents: (userAgentIds) => set((state) => ({
- userAgents: state.userAgents.filter((userAgent) => !userAgentIds.includes(userAgent.id)),
- })),
+ removeUserAgents: (userAgentIds) =>
+ set((state) => ({
+ userAgents: state.userAgents.filter(
+ (userAgent) => !userAgentIds.includes(userAgent.id),
+ ),
+ })),
}));
export default useUserAgentsStore;