234 lines
11 KiB
TypeScript
234 lines
11 KiB
TypeScript
import React, { useEffect, useRef, useState } from 'react';
|
|
import axios from "axios";
|
|
import AppLayout from '@/Layouts/AppLayout';
|
|
import { Channel, Clip } from "@/types";
|
|
import { usePage } from "@inertiajs/react";
|
|
|
|
import {MultiSelect} from "@/Components/MultiSelect";
|
|
import {Typography} from "@material-tailwind/react";
|
|
import {format} from "date-fns";
|
|
|
|
|
|
export default function Clips() {
|
|
// Initialize videoId state from the Inertia prop
|
|
const { video_id } = usePage().props;
|
|
const [videoId, setVideoId] = useState<string>(video_id || '');
|
|
|
|
const [clips, setClips] = useState<Clip[]>([]);
|
|
const [channels, setChannels] = useState<Channel[]>([]);
|
|
const [distinctLanguages, setDistinctLanguages] = useState<string[]>([]);
|
|
const [loadingChannels, setLoadingChannels] = useState<boolean>(true);
|
|
const [loadingVideos, setLoadingVideos] = useState<boolean>(true);
|
|
|
|
// Filter states for multi-select options
|
|
const [selectedChannelIds, setSelectedChannelIds] = useState<string[]>([]);
|
|
const [selectedLanguages, setSelectedLanguages] = useState<string[]>([]);
|
|
const [startDate, setStartDate] = useState<string>('');
|
|
const [endDate, setEndDate] = useState<string>('');
|
|
|
|
// (1) Initial fetch on mount to get all channels and compute distinct languages (runs only once)
|
|
useEffect(() => {
|
|
setLoadingChannels(true);
|
|
axios.get('/api/channels/get')
|
|
.then(response => {
|
|
const channelsData = response.data as Channel[];
|
|
setChannels(channelsData);
|
|
// Compute distinct languages once (from the full unfiltered list)
|
|
const langs = [...new Set(channelsData.map(channel => channel.language))];
|
|
setDistinctLanguages(langs);
|
|
setLoadingChannels(false);
|
|
})
|
|
.catch(error => {
|
|
console.error('Error fetching channels:', error);
|
|
setLoadingChannels(false);
|
|
});
|
|
}, []);
|
|
|
|
// (2) Fetch channels based on language selection changes
|
|
useEffect(() => {
|
|
setLoadingChannels(true);
|
|
const params: Record<string, string | string[]> = {};
|
|
if (selectedLanguages.length > 0) {
|
|
params.languages = selectedLanguages.filter(lang => lang !== '');
|
|
}
|
|
axios.get('/api/channels/get', { params })
|
|
.then(response => {
|
|
setChannels(response.data);
|
|
setLoadingChannels(false);
|
|
})
|
|
.catch(error => {
|
|
console.error('Error fetching channels:', error);
|
|
setLoadingChannels(false);
|
|
});
|
|
}, [selectedLanguages]);
|
|
|
|
// Fetch clips when filters change (including videoId)
|
|
useEffect(() => {
|
|
setLoadingVideos(true);
|
|
const params: Record<string, string | string[]> = {};
|
|
|
|
if (selectedChannelIds.length > 0) {
|
|
params.channel_ids = selectedChannelIds.filter(id => id !== '');
|
|
}
|
|
if (selectedLanguages.length > 0) {
|
|
params.languages = selectedLanguages.filter(lang => lang !== '');
|
|
}
|
|
if (startDate) params.start_date = startDate;
|
|
if (endDate) params.end_date = endDate;
|
|
if (videoId) params.video_id = videoId;
|
|
|
|
axios.get('/api/clips/get', { params })
|
|
.then(response => {
|
|
setClips(response.data);
|
|
setLoadingVideos(false);
|
|
})
|
|
.catch(error => {
|
|
console.error('Error fetching clips:', error);
|
|
setLoadingVideos(false);
|
|
});
|
|
}, [selectedChannelIds, selectedLanguages, startDate, endDate, videoId]);
|
|
|
|
function openInGoogleDrive(googleDriveFileId: string | null) {
|
|
if (googleDriveFileId != null) {
|
|
const url = `https://drive.google.com/file/d/${googleDriveFileId}/view?usp=sharing`;
|
|
window.open(url, '_blank');
|
|
}
|
|
}
|
|
|
|
return (
|
|
<AppLayout
|
|
title="Videos"
|
|
renderHeader={() => (
|
|
<h2 className="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
|
Clips
|
|
</h2>
|
|
)}
|
|
>
|
|
<div className="py-12">
|
|
<div className="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
|
<div className="bg-white dark:bg-gray-800 overflow-hidden shadow-xl sm:rounded-lg p-6">
|
|
{/* Filter Options */}
|
|
<div className="mb-6 grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4">
|
|
{/* Channel Filter */}
|
|
<div className="form-control">
|
|
<label className="label">
|
|
<span className="label-text">Channel</span>
|
|
</label>
|
|
{loadingChannels ? (
|
|
<span className="loading loading-dots loading-md"></span>
|
|
) : (
|
|
<MultiSelect
|
|
label="Channels"
|
|
options={channels.map(ch => ({
|
|
value: ch.id.toString(),
|
|
label: ch.channel_name,
|
|
}))}
|
|
selected={selectedChannelIds}
|
|
onChange={setSelectedChannelIds}
|
|
/>
|
|
)}
|
|
</div>
|
|
{/* Language Filter */}
|
|
<div className="form-control">
|
|
<label className="label">
|
|
<span className="label-text">Language</span>
|
|
</label>
|
|
{loadingChannels ? (
|
|
<span className="loading loading-dots loading-md"></span>
|
|
) : (
|
|
<MultiSelect
|
|
label="Languages"
|
|
options={distinctLanguages.map(ln => ({
|
|
value: ln,
|
|
label: ln,
|
|
}))}
|
|
selected={selectedLanguages}
|
|
onChange={setSelectedLanguages}
|
|
/>
|
|
)}
|
|
</div>
|
|
{/* Start Date Filter */}
|
|
<div className="form-control">
|
|
<label className="label">
|
|
<span className="label-text">Start Date</span>
|
|
</label>
|
|
<input
|
|
type="date"
|
|
className="input input-bordered"
|
|
value={startDate}
|
|
onChange={(e) => setStartDate(e.target.value)}
|
|
/>
|
|
</div>
|
|
{/* End Date Filter */}
|
|
<div className="form-control">
|
|
<label className="label">
|
|
<span className="label-text">End Date</span>
|
|
</label>
|
|
<input
|
|
type="date"
|
|
className="input input-bordered"
|
|
value={endDate}
|
|
onChange={(e) => setEndDate(e.target.value)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Selected Video ID Filter Display */}
|
|
{videoId && (
|
|
<div className="badge badge-ghost badge-sm mb-4">
|
|
<span className="mr-1">Video ID: {videoId}</span>
|
|
<button
|
|
className="text-red-500 hover:text-red-700"
|
|
onClick={() => setVideoId('')}
|
|
>
|
|
X
|
|
</button>
|
|
</div>
|
|
)}
|
|
|
|
{/* Clips Grid */}
|
|
{loadingVideos ? (
|
|
<div className="flex justify-center items-center">
|
|
<span className="loading loading-dots loading-lg"></span>
|
|
</div>
|
|
) : (
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
{clips.map((clip) => (
|
|
<div
|
|
key={clip.id}
|
|
className={`card bg-base-100 shadow-sm image-full w-full ${clip.gdrive_file_id ? "cursor-pointer" : "cursor-not-allowed"}`}
|
|
onClick={() => {
|
|
if (clip.gdrive_file_id) openInGoogleDrive(clip.gdrive_file_id);
|
|
}}
|
|
>
|
|
<figure>
|
|
<img
|
|
src="https://img.daisyui.com/images/stock/photo-1606107557195-0e29a4b5b4aa.webp"
|
|
alt="Video Thumbnail"
|
|
/>
|
|
</figure>
|
|
<div className="card-body">
|
|
<h2 className="card-title">
|
|
<div>
|
|
{clip.video.channel.channel_name}
|
|
</div>
|
|
<Typography className="opacity-50">
|
|
{clip.filename.split('/').pop() || 'No filename'}
|
|
</Typography>
|
|
</h2>
|
|
<div>Stream: {clip.video.external_date ? format(clip.video.external_date, 'dd-MM-yy') : ''}</div>
|
|
<div>Clip ID: {clip.id}</div>
|
|
<div>Video ID: {clip.video.id}</div>
|
|
{/* Additional clip information can be added here */}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</AppLayout>
|
|
);
|
|
}
|