From 92793d7a76c364f6309b3b5a03f810b0b6c081a4 Mon Sep 17 00:00:00 2001 From: t0is Date: Fri, 2 May 2025 12:50:33 +0200 Subject: [PATCH] channels page --- .../Http/Controllers/ChannelController.php | 32 +++ src/resources/js/Layouts/AppLayout.tsx | 6 + src/resources/js/Pages/Channels.tsx | 231 ++++++++++++++++++ src/routes/api.php | 2 + src/routes/web.php | 4 + 5 files changed, 275 insertions(+) create mode 100644 src/resources/js/Pages/Channels.tsx diff --git a/src/app/Http/Controllers/ChannelController.php b/src/app/Http/Controllers/ChannelController.php index 0e952e5..9d29ef7 100644 --- a/src/app/Http/Controllers/ChannelController.php +++ b/src/app/Http/Controllers/ChannelController.php @@ -14,6 +14,18 @@ class ChannelController extends Controller * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\JsonResponse */ + public function toggleFetch(Request $request, $channel_id) + { + // Start a query on the Video model + $channel = Channel::where('id', $channel_id)->first(); + + $channel->update([ + 'fetching_enabled' => !$channel->fetching_enabled + ]); + + // Return the videos as a JSON response + return response()->json(true); + } public function getChannels(Request $request) { // Start a query on the Video model @@ -29,4 +41,24 @@ class ChannelController extends Controller // Return the videos as a JSON response return response()->json($channels); } + + public function getChannelsFull(Request $request) + { + // Start a query on the Video model + $query = Channel::query()->with(['videos', 'videos.clips']); + + if ($request->has('languages')) { + $query->whereIn('language', $request->input('languages')); + } + + if ($request->has('channelFetchEnabled') && $request->input('channelFetchEnabled') == "true") { + $query->where('fetching_enabled', true); + } + + // Retrieve the videos (you can add pagination if desired) + $channels = $query->get(); + + // Return the videos as a JSON response + return response()->json($channels); + } } diff --git a/src/resources/js/Layouts/AppLayout.tsx b/src/resources/js/Layouts/AppLayout.tsx index e9c372a..5fb3a07 100644 --- a/src/resources/js/Layouts/AppLayout.tsx +++ b/src/resources/js/Layouts/AppLayout.tsx @@ -84,6 +84,12 @@ export default function AppLayout({ > Clips + + Channels + diff --git a/src/resources/js/Pages/Channels.tsx b/src/resources/js/Pages/Channels.tsx new file mode 100644 index 0000000..a3aae22 --- /dev/null +++ b/src/resources/js/Pages/Channels.tsx @@ -0,0 +1,231 @@ +import React, { useEffect, useState } from 'react'; +import axios from "axios"; +import AppLayout from '@/Layouts/AppLayout'; +import { Video, Channel } from "@/types"; +import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; +import {faDownload, faFileVideo, faFont, faSquareCheck} from "@fortawesome/free-solid-svg-icons"; +import { router } from '@inertiajs/core'; +import { Link, Head } from '@inertiajs/react'; +import classNames from 'classnames'; +import useRoute from '@/Hooks/useRoute'; +import {MultiSelect} from "@/Components/MultiSelect"; +import { format } from 'date-fns'; +export default function Channels() { + + const route = useRoute(); + const [channels, setChannels] = useState([]); + const [distinctLanguages, setDistinctLanguages] = useState([]); + const [loadingVideos, setLoadingVideos] = useState(true); + const [loadingFetch, setLoadingFetch] = useState(false); + + // Filter states + const [selectedLanguages, setSelectedLanguages] = useState([]); + const [channelFetchEnabled, setChannelFetchEnabled] = useState(false); + + + const toggleFetchingEnabled = async (channelId: number) => { + setLoadingFetch(true); + try { + // fire your API; adjust the URL/method to match your backend + const { data: updatedChannel } = await axios.post( + `/api/channels/${channelId}/toggleFetch` + ); + + // 2) update local state + setChannels(prev => + prev.map(ch => + ch.id === channelId + ? { ...ch, fetching_enabled: !ch.fetching_enabled } + : ch + ) + ); + setLoadingFetch(false); + } catch (error) { + console.error("Failed to toggle fetching:", error); + setLoadingFetch(false); + // you might want to show a toast here + } + }; + + useEffect(() => { + setLoadingVideos(true); + const params: Record = {}; + + if (selectedLanguages.length > 0) { + params.languages = selectedLanguages.filter(lang => lang !== ''); + } + params.channelFetchEnabled = channelFetchEnabled; + + axios.get('/api/channels/getFull', { params }) + .then(response => { + console.log(response.data); + setChannels(response.data); + setLoadingVideos(false); + }) + .catch(error => { + console.error('Error fetching clips:', error); + setLoadingVideos(false); + }); + }, [selectedLanguages, channelFetchEnabled]); + + return ( + ( +

+ Videos +

+ )} + > +
+
+
+ {/* Filter Options */} +
+ {/* Language Filter */} +
+ + {loadingVideos ? ( + + ) : ( + ({ + value: ln, + label: ln, + }))} + selected={selectedLanguages} + onChange={setSelectedLanguages} + /> + )} +
+ {/* Clip exists filter */} +
+ + {loadingVideos ? ( + + ) : ( + setChannelFetchEnabled(e.target.checked)} + /> + )} +
+
+ +
+ + {/* head */} + + + + + + + + + + + + {loadingVideos ? ( +
+ +
+ ) : + channels.map((channel) => ( + + + + + + + + )) + } + + + +
+ + ChannelTitleFetching enabled...
+ + +
+
+
+ {channel.channel_name}/ +
+
+
+
{channel.channel_name}
+
{channel.twitch_id ? "Twitch" : channel.youtube_id ? "YouTube" : ""}
+
+
+
+
+
+ + Average clips per VOD:{' '} + {channel.videos.length > 0 + ? ( + channel.videos.reduce( + (total, video) => total + (video.clips?.length || 0), + 0 + ) / channel.videos.length + ).toFixed(2) + : 'NaN'} + +
+
+ + Clip count: { + channel.videos.reduce( + (total, video) => total + (video.clips?.length || 0), + 0 + ) + } + + + VOD count: {channel.videos.length || 0} + +
+ +
+
+
+ {loadingFetch ? ( + + ) : ( + toggleFetchingEnabled(channel.id)} + + /> + )} +
+
+ +
+
+
+
+
+
+ ); +} diff --git a/src/routes/api.php b/src/routes/api.php index 3796aa7..bea3da7 100644 --- a/src/routes/api.php +++ b/src/routes/api.php @@ -16,4 +16,6 @@ Route::get('/user', function (Request $request) { Route::get('videos/get', [VideoController::class, 'getVideos']); Route::get('clips/get', [ClipController::class, 'getClips']); Route::get('channels/get', [ChannelController::class, 'getChannels']); +Route::get('channels/getFull', [ChannelController::class, 'getChannelsFull']); +Route::post('channels/{id}/toggleFetch', [ChannelController::class, 'toggleFetch']); Route::get('dashboard/stats', [StatsController::class, 'getStats']); diff --git a/src/routes/web.php b/src/routes/web.php index 56dcac0..36db023 100644 --- a/src/routes/web.php +++ b/src/routes/web.php @@ -28,6 +28,10 @@ Route::middleware([ return Inertia::render('Videos'); })->name('videos'); + Route::get('/channels', function () { + return Inertia::render('Channels'); + })->name('channels'); + Route::get('/clips', function (Request $request) { return Inertia::render('Clips', [ 'video_id' => $request->get('video_id', null),