diff --git a/apps/channels/models.py b/apps/channels/models.py index abcecb77..9f1b641e 100644 --- a/apps/channels/models.py +++ b/apps/channels/models.py @@ -410,6 +410,7 @@ class Recording(models.Model): start_time = models.DateTimeField() end_time = models.DateTimeField() task_id = models.CharField(max_length=255, null=True, blank=True) + custom_properties = models.TextField(null=True, blank=True) def __str__(self): return f"{self.channel.name} - {self.start_time} to {self.end_time}" diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 4fa2b9a9..a641a53b 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -13,6 +13,7 @@ import M3U from './pages/M3U'; import EPG from './pages/EPG'; import Guide from './pages/Guide'; import Stats from './pages/Stats'; +import DVR from './pages/DVR'; import Settings from './pages/Settings'; import StreamProfiles from './pages/StreamProfiles'; import useAuthStore from './store/auth'; @@ -127,6 +128,7 @@ const App = () => { element={} /> } /> + } /> } /> } /> diff --git a/frontend/src/api.js b/frontend/src/api.js index 9e1dfd55..9d8bf746 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -1035,6 +1035,19 @@ export default class API { .updateProfileChannels(channelIds, profileId, enabled); } + static async getRecordings() { + const response = await fetch(`${host}/api/channels/recordings/`, { + headers: { + Authorization: `Bearer ${await API.getAuthToken()}`, + 'Content-Type': 'application/json', + }, + }); + + const retval = await response.json(); + + return retval; + } + static async createRecording(values) { const response = await fetch(`${host}/api/channels/recordings/`, { method: 'POST', @@ -1046,7 +1059,20 @@ export default class API { }); const retval = await response.json(); + useChannelsStore.getState().fetchRecordings(); return retval; } + + static async deleteRecording(id) { + const response = await fetch(`${host}/api/channels/recordings/${id}/`, { + method: 'DELETE', + headers: { + Authorization: `Bearer ${await API.getAuthToken()}`, + 'Content-Type': 'application/json', + }, + }); + + useChannelsStore.getState().fetchRecordings(); + } } diff --git a/frontend/src/components/Sidebar.jsx b/frontend/src/components/Sidebar.jsx index 157381f0..00fb4045 100644 --- a/frontend/src/components/Sidebar.jsx +++ b/frontend/src/components/Sidebar.jsx @@ -9,6 +9,7 @@ import { Settings as LucideSettings, Copy, ChartLine, + Video, } from 'lucide-react'; import { Avatar, @@ -80,6 +81,7 @@ const Sidebar = ({ collapsed, toggleDrawer, drawerWidth, miniDrawerWidth }) => { path: '/stream-profiles', }, { label: 'TV Guide', icon: , path: '/guide' }, + { label: 'DVR', icon: