// src/App.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import './App.css';
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://127.0.0.1:4000';
function App() {
const [videos, setVideos] = useState([]);
const [channels, setChannels] = useState([]);
const [loading, setLoading] = useState(true);
const [channelsLoading, setChannelsLoading] = useState(true);
const [error, setError] = useState(null);
const [selectedChannelId, setSelectedChannelId] = useState('');
const [expandedDescriptions, setExpandedDescriptions] = useState({});
const fetchChannels = async () => {
try {
setChannelsLoading(true);
const response = await axios.get(`${API_BASE_URL}/subs-info`);
if (response.data && Array.isArray(response.data)) {
const formattedChannels = response.data.map(channel => {
// Extract the channel ID from the _id field
const channelId = channel._id.replace('yt:channel:', '');
return {
id: channelId,
_id: channel._id,
last_video_update: channel.last_video_update,
new_vids: channel.new_vids,
time_between_fetches: channel.time_between_fetches,
videos: channel.videos
};
});
setChannels(formattedChannels);
}
} catch (err) {
console.error('Error fetching channels:', err);
setError('Failed to fetch available channels.');
} finally {
setChannelsLoading(false);
}
};
const fetchVideos = async (channelId) => {
try {
setLoading(true);
setError(null);
setExpandedDescriptions({}); // Reset expanded states when fetching new videos
const apiUrl = `${API_BASE_URL}/vid-from-link/yt:channel:${channelId}`;
const response = await axios.get(apiUrl);
if (response.data && Array.isArray(response.data)) {
setVideos(response.data);
} else {
throw new Error('Invalid response format');
}
} catch (err) {
setError('Failed to fetch videos. Please check the channel and ensure the API is running.');
console.error('Error fetching videos:', err);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchChannels();
}, []);
useEffect(() => {
if (selectedChannelId) {
fetchVideos(selectedChannelId);
}
}, [selectedChannelId]);
const handleChannelChange = (e) => {
setSelectedChannelId(e.target.value);
};
const handleRefreshChannels = () => {
fetchChannels();
};
const toggleDescription = (videoId) => {
setExpandedDescriptions(prev => ({
...prev,
[videoId]: !prev[videoId]
}));
};
const formatDate = (dateString) => {
try {
return new Date(dateString).toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
} catch {
return dateString;
}
};
const formatRelativeTime = (dateString) => {
try {
const date = new Date(dateString);
const now = new Date();
const diffInHours = Math.floor((now - date) / (1000 * 60 * 60));
if (diffInHours < 1) {
return 'Just now';
} else if (diffInHours < 24) {
return `${diffInHours}h ago`;
} else {
const diffInDays = Math.floor(diffInHours / 24);
return `${diffInDays}d ago`;
}
} catch {
return dateString;
}
};
return (
{channelsLoading && Loading channels...
}
{error && (
{error}
)}
{!channelsLoading && channels.length === 0 && !error && (
No channels available
No YouTube channels found in the subscription list.
)}
{loading && selectedChannelId && (
Loading videos for {selectedChannelId}...
)}
{!loading && !error && selectedChannelId && (
Latest Videos from {selectedChannelId}
{videos.length > 0 && ` (${videos.length})`}
{videos.sort((a, b) => new Date(b.published) - new Date(a.published)).map((video) => (

{
e.target.src = 'https://via.placeholder.com/300x180/333/fff?text=No+Thumbnail';
}}
/>
▶
{video.title}
By:
{video.author}
Published: {formatDate(video.published)}
{video.updated && (
Updated: {formatDate(video.updated)}
)}
{video.summary && (
{video.summary}
{video.summary.length > 100 && (
)}
)}
))}
)}
{!loading && !error && selectedChannelId && videos.length === 0 && (
No videos found for this channel
The channel might not have any videos or there was an issue loading them.
)}
{!channelsLoading && !selectedChannelId && channels.length > 0 && (
Please select a channel to view videos
Choose a channel from the dropdown above to see its latest videos.
)}
);
}
export default App;