dfd / index.html
Subham9126's picture
Update index.html
e8ac6b1 verified
Raw
History Blame Contribute Delete
23.6 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Video Streaming Platform</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #0f0f0f;
color: #fff;
line-height: 1.6;
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: 0 20px;
}
/* Header */
header {
background: #212121;
padding: 1rem 0;
position: sticky;
top: 0;
z-index: 100;
border-bottom: 1px solid #333;
}
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 1rem;
}
.logo {
font-size: 1.8rem;
font-weight: bold;
color: #ff6b6b;
cursor: pointer;
transition: color 0.3s ease;
}
.logo:hover {
color: #ff5252;
}
.search-container {
flex: 1;
max-width: 500px;
position: relative;
}
#searchInput {
width: 100%;
padding: 12px 20px;
background: #333;
border: 2px solid #444;
border-radius: 25px;
color: #fff;
font-size: 16px;
transition: all 0.3s ease;
}
#searchInput:focus {
outline: none;
border-color: #ff6b6b;
background: #404040;
}
#searchInput::placeholder {
color: #aaa;
}
/* Main Content */
main {
min-height: calc(100vh - 200px);
padding: 2rem 0;
}
/* Video Grid */
.video-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
}
.video-card {
background: #1e1e1e;
border-radius: 12px;
overflow: hidden;
transition: all 0.3s ease;
cursor: pointer;
border: 2px solid transparent;
}
.video-card:hover {
transform: translateY(-8px);
border-color: #ff6b6b;
box-shadow: 0 12px 30px rgba(255, 107, 107, 0.3);
}
.video-thumbnail {
width: 100%;
height: 180px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
}
.play-icon {
width: 60px;
height: 60px;
background: rgba(255, 255, 255, 0.9);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
color: #333;
transition: all 0.3s ease;
}
.video-card:hover .play-icon {
transform: scale(1.1);
background: #ff6b6b;
color: #fff;
}
.video-info {
padding: 1rem;
}
.video-title {
font-size: 1.1rem;
font-weight: 600;
color: #fff;
margin-bottom: 0.5rem;
}
.video-meta {
color: #aaa;
font-size: 0.9rem;
}
/* Video Player Page */
.player-container {
display: none;
}
.player-container.active {
display: block;
}
.video-player-section {
background: #1e1e1e;
border-radius: 12px;
padding: 1rem;
margin-bottom: 2rem;
}
.video-player {
width: 100%;
max-width: 900px;
height: auto;
border-radius: 8px;
margin-bottom: 1rem;
}
.video-details h2 {
font-size: 1.5rem;
margin-bottom: 1rem;
color: #fff;
}
.back-button {
background: #ff6b6b;
color: #fff;
border: none;
padding: 12px 24px;
border-radius: 25px;
cursor: pointer;
font-size: 16px;
transition: all 0.3s ease;
margin-bottom: 1rem;
}
.back-button:hover {
background: #ff5252;
transform: translateY(-2px);
}
.recommendations {
margin-top: 2rem;
}
.recommendations h3 {
margin-bottom: 1rem;
color: #fff;
font-size: 1.3rem;
}
.recommendations-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1rem;
}
.recommendation-card {
background: #2a2a2a;
border-radius: 8px;
padding: 1rem;
cursor: pointer;
transition: all 0.3s ease;
border: 1px solid #444;
}
.recommendation-card:hover {
background: #333;
border-color: #ff6b6b;
}
.recommendation-title {
font-weight: 600;
margin-bottom: 0.5rem;
}
/* Pagination */
.pagination {
display: flex;
justify-content: center;
align-items: center;
gap: 0.5rem;
margin: 2rem 0;
flex-wrap: wrap;
}
.pagination button {
background: #333;
color: #fff;
border: none;
padding: 10px 15px;
border-radius: 6px;
cursor: pointer;
transition: all 0.3s ease;
font-size: 14px;
}
.pagination button:hover:not(:disabled) {
background: #ff6b6b;
transform: translateY(-2px);
}
.pagination button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.pagination button.active {
background: #ff6b6b;
}
.pagination-info {
color: #aaa;
margin: 0 1rem;
font-size: 14px;
}
/* No Results */
.no-results {
text-align: center;
padding: 3rem;
color: #aaa;
}
.no-results h3 {
font-size: 1.5rem;
margin-bottom: 1rem;
}
.no-results p {
font-size: 1.1rem;
}
/* Loading */
.loading {
text-align: center;
padding: 3rem;
color: #aaa;
}
.loading-spinner {
width: 50px;
height: 50px;
border: 3px solid #333;
border-top: 3px solid #ff6b6b;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 1rem;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Responsive Design */
@media (max-width: 768px) {
.header-content {
flex-direction: column;
text-align: center;
}
.search-container {
max-width: 100%;
}
.video-grid {
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1rem;
}
.recommendations-grid {
grid-template-columns: 1fr;
}
.pagination {
gap: 0.25rem;
}
.pagination button {
padding: 8px 12px;
font-size: 12px;
}
}
@media (max-width: 480px) {
.video-grid {
grid-template-columns: 1fr;
}
.video-thumbnail {
height: 200px;
}
}
</style>
</head>
<body>
<header>
<div class="container">
<div class="header-content">
<div class="logo" onclick="showVideoList()">📺 VideoHub</div>
<div class="search-container">
<input type="text" id="searchInput" placeholder="Search videos..." oninput="handleSearch()">
</div>
</div>
</div>
</header>
<main>
<div class="container">
<!-- Video List View -->
<div id="videoListContainer">
<div id="loadingIndicator" class="loading">
<div class="loading-spinner"></div>
<p>Loading videos...</p>
</div>
<div id="videoGrid" class="video-grid"></div>
<div id="noResults" class="no-results" style="display: none;">
<h3>No videos found</h3>
<p>Try adjusting your search terms</p>
</div>
<div id="paginationContainer" class="pagination"></div>
</div>
<!-- Video Player View -->
<div id="videoPlayerContainer" class="player-container">
<button class="back-button" onclick="showVideoList()">← Back to Videos</button>
<div class="video-player-section">
<video id="videoPlayer" class="video-player" controls>
Your browser does not support the video tag.
</video>
<div class="video-details">
<h2 id="currentVideoTitle">Video Title</h2>
</div>
</div>
<div class="recommendations">
<h3>Up Next</h3>
<div id="recommendationsGrid" class="recommendations-grid"></div>
</div>
</div>
</div>
</main>
<script>
// Global state
let allVideos = [];
let filteredVideos = [];
let currentPage = 1;
const videosPerPage = 12;
let currentVideoIndex = -1;
let searchQuery = '';
// Sample data (replace with actual database.json loading)
const sampleData = [
{ "title": "E01 - Introduction", "url": "https://example.com/video1.mkv" },
{ "title": "E02 - Getting Started", "url": "https://example.com/video2.mkv" },
{ "title": "E03 - Advanced Concepts", "url": "https://example.com/video3.mkv" },
{ "title": "E04 - Best Practices", "url": "https://example.com/video4.mkv" },
{ "title": "E05 - Case Studies", "url": "https://example.com/video5.mkv" },
{ "title": "E06 - Implementation", "url": "https://example.com/video6.mkv" },
{ "title": "E07 - Testing", "url": "https://example.com/video7.mkv" },
{ "title": "E08 - Deployment", "url": "https://example.com/video8.mkv" },
{ "title": "E09 - Monitoring", "url": "https://example.com/video9.mkv" },
{ "title": "E10 - Optimization", "url": "https://example.com/video10.mkv" },
{ "title": "E11 - Security", "url": "https://example.com/video11.mkv" },
{ "title": "E12 - Maintenance", "url": "https://example.com/video12.mkv" },
{ "title": "E13 - Troubleshooting", "url": "https://example.com/video13.mkv" },
{ "title": "E14 - Future Trends", "url": "https://example.com/video14.mkv" },
{ "title": "E15 - Conclusion", "url": "https://example.com/video15.mkv" }
];
// Initialize the application
async function initApp() {
try {
// Try to load from database.json, fallback to sample data
await loadVideos();
setupEventListeners();
handleURLHash();
} catch (error) {
console.error('Error initializing app:', error);
// Use sample data as fallback
allVideos = [...sampleData];
filteredVideos = [...allVideos];
hideLoading();
renderVideoGrid();
renderPagination();
}
}
// Load videos from database.json
async function loadVideos() {
try {
const response = await fetch('database.json');
if (response.ok) {
const data = await response.json();
allVideos = data.sort((a, b) => a.title.localeCompare(b.title));
} else {
throw new Error('Failed to load database.json');
}
} catch (error) {
console.warn('Could not load database.json, using sample data:', error);
allVideos = [...sampleData];
}
filteredVideos = [...allVideos];
hideLoading();
renderVideoGrid();
renderPagination();
}
// Setup event listeners
function setupEventListeners() {
// Handle browser back/forward
window.addEventListener('popstate', handleURLHash);
// Handle video player events
const videoPlayer = document.getElementById('videoPlayer');
videoPlayer.addEventListener('error', handleVideoError);
videoPlayer.addEventListener('loadedmetadata', handleVideoLoaded);
}
// Handle URL hash changes
function handleURLHash() {
const hash = window.location.hash;
if (hash.startsWith('#play=')) {
const videoTitle = decodeURIComponent(hash.substring(6));
const videoIndex = allVideos.findIndex(v => v.title === videoTitle);
if (videoIndex !== -1) {
playVideo(videoIndex);
} else {
showVideoList();
}
} else {
showVideoList();
}
}
// Hide loading indicator
function hideLoading() {
document.getElementById('loadingIndicator').style.display = 'none';
}
// Handle search input
function handleSearch() {
const query = document.getElementById('searchInput').value.toLowerCase().trim();
searchQuery = query;
if (query === '') {
filteredVideos = [...allVideos];
} else {
filteredVideos = allVideos.filter(video =>
video.title.toLowerCase().includes(query)
);
}
currentPage = 1;
renderVideoGrid();
renderPagination();
}
// Render video grid
function renderVideoGrid() {
const grid = document.getElementById('videoGrid');
const noResults = document.getElementById('noResults');
if (filteredVideos.length === 0) {
grid.innerHTML = '';
noResults.style.display = 'block';
return;
}
noResults.style.display = 'none';
const startIndex = (currentPage - 1) * videosPerPage;
const endIndex = startIndex + videosPerPage;
const videosToShow = filteredVideos.slice(startIndex, endIndex);
grid.innerHTML = videosToShow.map((video, index) => {
const actualIndex = allVideos.findIndex(v => v.title === video.title);
return `
<div class="video-card" onclick="playVideo(${actualIndex})">
<div class="video-thumbnail">
<div class="play-icon">▶</div>
</div>
<div class="video-info">
<div class="video-title">${escapeHtml(video.title)}</div>
<div class="video-meta">Click to play</div>
</div>
</div>
`;
}).join('');
}
// Render pagination
function renderPagination() {
const container = document.getElementById('paginationContainer');
const totalPages = Math.ceil(filteredVideos.length / videosPerPage);
if (totalPages <= 1) {
container.innerHTML = '';
return;
}
let paginationHTML = '';
// Previous button
paginationHTML += `
<button onclick="changePage(${currentPage - 1})" ${currentPage === 1 ? 'disabled' : ''}>
← Previous
</button>
`;
// Page numbers
const maxVisiblePages = 5;
let startPage = Math.max(1, currentPage - Math.floor(maxVisiblePages / 2));
let endPage = Math.min(totalPages, startPage + maxVisiblePages - 1);
if (endPage - startPage + 1 < maxVisiblePages) {
startPage = Math.max(1, endPage - maxVisiblePages + 1);
}
if (startPage > 1) {
paginationHTML += `<button onclick="changePage(1)">1</button>`;
if (startPage > 2) {
paginationHTML += `<span class="pagination-info">...</span>`;
}
}
for (let i = startPage; i <= endPage; i++) {
paginationHTML += `
<button onclick="changePage(${i})" ${i === currentPage ? 'class="active"' : ''}>
${i}
</button>
`;
}
if (endPage < totalPages) {
if (endPage < totalPages - 1) {
paginationHTML += `<span class="pagination-info">...</span>`;
}
paginationHTML += `<button onclick="changePage(${totalPages})">${totalPages}</button>`;
}
// Next button
paginationHTML += `
<button onclick="changePage(${currentPage + 1})" ${currentPage === totalPages ? 'disabled' : ''}>
Next →
</button>
`;
// Page info
const startItem = (currentPage - 1) * videosPerPage + 1;
const endItem = Math.min(currentPage * videosPerPage, filteredVideos.length);
paginationHTML += `
<span class="pagination-info">
${startItem}-${endItem} of ${filteredVideos.length} videos
</span>
`;
container.innerHTML = paginationHTML;
}
// Change page
function changePage(page) {
const totalPages = Math.ceil(filteredVideos.length / videosPerPage);
if (page >= 1 && page <= totalPages) {
currentPage = page;
renderVideoGrid();
renderPagination();
// Scroll to top
window.scrollTo({ top: 0, behavior: 'smooth' });
}
}
// Play video
function playVideo(videoIndex) {
if (videoIndex < 0 || videoIndex >= allVideos.length) return;
currentVideoIndex = videoIndex;
const video = allVideos[videoIndex];
// Update URL hash
window.history.pushState(null, null, `#play=${encodeURIComponent(video.title)}`);
// Hide video list, show player
document.getElementById('videoListContainer').style.display = 'none';
document.getElementById('videoPlayerContainer').classList.add('active');
// Set up video player
const videoPlayer = document.getElementById('videoPlayer');
videoPlayer.src = video.url;
// Update video title
document.getElementById('currentVideoTitle').textContent = video.title;
// Generate recommendations
generateRecommendations();
// Scroll to top
window.scrollTo({ top: 0, behavior: 'smooth' });
}
// Generate recommendations
function generateRecommendations() {
const grid = document.getElementById('recommendationsGrid');
const recommendations = [];
// Get next 5 videos in sequence
for (let i = 1; i <= 5; i++) {
const nextIndex = currentVideoIndex + i;
if (nextIndex < allVideos.length) {
recommendations.push({ video: allVideos[nextIndex], index: nextIndex });
}
}
// If we don't have enough recommendations, add some from the beginning
let startIndex = 0;
while (recommendations.length < 5 && startIndex < allVideos.length) {
if (startIndex !== currentVideoIndex &&
!recommendations.some(r => r.index === startIndex)) {
recommendations.push({ video: allVideos[startIndex], index: startIndex });
}
startIndex++;
}
grid.innerHTML = recommendations.map(({ video, index }) => `
<div class="recommendation-card" onclick="playVideo(${index})">
<div class="recommendation-title">${escapeHtml(video.title)}</div>
<div class="video-meta">Click to play</div>
</div>
`).join('');
}
// Show video list
function showVideoList() {
// Update URL
window.history.pushState(null, null, window.location.pathname);
// Hide player, show list
document.getElementById('videoPlayerContainer').classList.remove('active');
document.getElementById('videoListContainer').style.display = 'block';
// Pause current video
const videoPlayer = document.getElementById('videoPlayer');
videoPlayer.pause();
}
// Handle video errors
function handleVideoError(event) {
console.error('Video error:', event);
alert('Error loading video. Please check the video URL or try again later.');
}
// Handle video loaded
function handleVideoLoaded(event) {
console.log('Video loaded successfully');
}
// Utility function to escape HTML
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Initialize the app when DOM is loaded
document.addEventListener('DOMContentLoaded', initApp);
</script>
</body>
</html>