feat(video-page): add video player page with details and comments

Add new video player page with responsive layout, video details display, and comments section. Includes video player, channel info, like/dislike buttons, and interactive elements. Also add null checks in main script to prevent errors when elements are missing.
This commit is contained in:
2026-02-11 21:46:48 +08:00
parent 7fb4ad915b
commit f6a861977c
5 changed files with 1151 additions and 48 deletions

385
css/video.css Normal file
View File

@@ -0,0 +1,385 @@
.video-content-wrapper {
flex: 1;
display: flex;
flex-direction: column;
overflow-y: auto;
padding: 24px;
background-color: #121212;
}
.video-player {
width: 70%;
aspect-ratio: 16 / 9;
margin-bottom: 20px;
border-radius: 20px;
position: static;
border: 1px solid #000000;
overflow: hidden;
box-shadow: 0 2px 8px rgb(48, 48, 48);
padding: 1px;
background-color: #000000;
box-sizing: border-box;
}
.video-player video {
width: 100%;
height: 100%;
border-radius: 20px;
}
.video-info-container {
margin-left: 30px;
margin-right: 30px;
margin-bottom: 20px;
}
.video-title {
font-size: 20px;
font-weight: 600;
margin: 0 0 12px 0;
line-height: 1.4;
}
.video-channel-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.channel-left {
display: flex;
align-items: center;
gap: 12px;
}
.channel-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
background-color: #606060;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.channel-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.channel-avatar i {
font-size: 20px;
color: #ffffff;
}
.channel-details {
display: flex;
flex-direction: column;
}
.channel-name {
font-size: 16px;
font-weight: 600;
color: #ffffff;
}
.channel-subscribers {
font-size: 12px;
color: #aaaaaa;
}
.subscribe-btn {
background-color: #cc0000;
color: #ffffff;
border: none;
padding: 10px 16px;
border-radius: 18px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s;
}
.subscribe-btn:hover {
background-color: #ff0000;
}
.subscribe-btn.subscribed {
background-color: #272727;
color: #aaaaaa;
}
.subscribe-btn.subscribed:hover {
background-color: #3d3d3d;
}
.video-actions {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 12px;
}
.like-dislike-buttons {
display: flex;
background-color: #272727;
border-radius: 18px;
overflow: hidden;
}
.action-btn {
background-color: #272727;
color: #ffffff;
border: none;
padding: 8px 16px;
border-radius: 18px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
gap: 6px;
transition: background-color 0.2s;
}
.action-btn:hover {
background-color: #3d3d3d;
}
.like-btn {
border-radius: 18px 0 0 18px;
padding-right: 12px;
}
.dislike-btn {
border-radius: 0 18px 18px 0;
padding-left: 12px;
}
.like-btn i,
.dislike-btn i {
font-size: 16px;
}
.more-options {
position: relative;
}
.more-dropdown {
position: absolute;
top: 100%;
right: 0;
background-color: #272727;
border-radius: 12px;
padding: 8px 0;
min-width: 180px;
display: none;
z-index: 1000;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.more-dropdown.show {
display: block;
}
.more-item {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 16px;
cursor: pointer;
color: #ffffff;
font-size: 14px;
}
.more-item:hover {
background-color: #3d3d3d;
}
.more-item i {
font-size: 16px;
color: #aaaaaa;
}
.video-stats {
display: flex;
gap: 12px;
font-size: 14px;
color: #aaaaaa;
margin-bottom: 12px;
}
.video-description {
background-color: #272727;
border-radius: 12px;
padding: 12px;
position: relative;
}
.description-toggle {
position: absolute;
top: 12px;
right: 12px;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
color: #ffffff;
}
.description-toggle:hover {
background-color: #3d3d3d;
border-radius: 50%;
}
.video-description p {
margin: 0;
font-size: 14px;
color: #ffffff;
line-height: 1.6;
max-height: 60px;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
}
.video-description p.expanded {
max-height: none;
display: block;
}
.comments-section {
margin-left: 30px;
margin-right: 30px;
margin-bottom: 40px;
}
.comments-header {
font-size: 16px;
font-weight: 600;
color: #ffffff;
margin: 0 0 20px 0;
}
.add-comment {
display: flex;
gap: 12px;
margin-bottom: 24px;
}
.comment-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
background-color: #606060;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
flex-shrink: 0;
}
.comment-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.comment-avatar i {
font-size: 20px;
color: #ffffff;
}
.comment-input-container {
flex: 1;
}
.comment-input {
width: 100%;
background-color: transparent;
border: none;
border-bottom: 1px solid #303030;
color: #ffffff;
font-size: 14px;
padding: 8px 0;
outline: none;
}
.comment-input::placeholder {
color: #aaaaaa;
}
.comment-input:focus {
border-bottom-color: #ffffff;
}
.comments-list {
display: flex;
flex-direction: column;
gap: 20px;
}
.comment-item {
display: flex;
gap: 12px;
}
.comment-content {
flex: 1;
}
.comment-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 4px;
}
.comment-author {
font-size: 13px;
font-weight: 600;
color: #ffffff;
}
.comment-time {
font-size: 12px;
color: #aaaaaa;
}
.comment-text {
font-size: 14px;
color: #ffffff;
margin: 0 0 8px 0;
line-height: 1.5;
}
.comment-actions {
display: flex;
gap: 8px;
}
.comment-action-btn {
background: none;
border: none;
color: #aaaaaa;
cursor: pointer;
padding: 4px;
display: flex;
align-items: center;
gap: 4px;
font-size: 12px;
}
.comment-action-btn:hover {
color: #ffffff;
}
.comment-action-btn i {
font-size: 16px;
}

View File

@@ -487,8 +487,13 @@ const subscriptionsList = document.querySelector('.subscriptions-list');
async function init() { async function init() {
await loadVideos(); await loadVideos();
// 只有在存在相应元素时才执行
if (videoGrid && shortsGrid) {
renderVideos(); renderVideos();
}
if (subscriptionsList) {
renderSubscriptions(); renderSubscriptions();
}
bindEvents(); bindEvents();
} }
@@ -741,27 +746,36 @@ function bindEvents() { // Bind events to the DOM elements
const menuBtn = document.querySelector('.menu-btn'); const menuBtn = document.querySelector('.menu-btn');
const sidebar = document.querySelector('.sidebar'); const sidebar = document.querySelector('.sidebar');
if (menuBtn && sidebar) {
menuBtn.addEventListener('click', () => { menuBtn.addEventListener('click', () => {
sidebar.classList.toggle('collapsed'); sidebar.classList.toggle('collapsed');
}); });
}
// Search event // Search event
if (searchInput) {
searchInput.addEventListener('input', (e) => { searchInput.addEventListener('input', (e) => {
performSearch(e.target.value); performSearch(e.target.value);
}); });
}
if (searchBtn) {
searchBtn.addEventListener('click', () => { searchBtn.addEventListener('click', () => {
performSearch(searchInput.value); performSearch(searchInput ? searchInput.value : '');
}); });
}
// Close the search results when clicking outside // Close the search results when clicking outside
if (searchResults) {
document.addEventListener('click', (e) => { document.addEventListener('click', (e) => {
if (!e.target.closest('.search-container') && !e.target.closest('.search-results')) { if (!e.target.closest('.search-container') && !e.target.closest('.search-results')) {
searchResults.style.display = 'none'; searchResults.style.display = 'none';
} }
}); });
}
// Category buttons event // Category buttons event
if (categoryBtns.length > 0) {
categoryBtns.forEach(btn => { categoryBtns.forEach(btn => {
btn.addEventListener('click', () => { btn.addEventListener('click', () => {
// Remove all active states // Remove all active states
@@ -771,12 +785,16 @@ function bindEvents() { // Bind events to the DOM elements
// Update current category // Update current category
currentCategory = btn.textContent; currentCategory = btn.textContent;
// Re-render videos and shorts // Re-render videos and shorts
if (videoGrid && shortsGrid) {
renderVideos(); renderVideos();
}
}); });
}); });
}
// Sidebar navigation buttons event // Sidebar navigation buttons event
const navBtns = document.querySelectorAll('.nav-btn'); const navBtns = document.querySelectorAll('.nav-btn');
if (navBtns.length > 0) {
navBtns.forEach(btn => { navBtns.forEach(btn => {
btn.addEventListener('click', () => { btn.addEventListener('click', () => {
// Remove all active states // Remove all active states
@@ -785,13 +803,16 @@ function bindEvents() { // Bind events to the DOM elements
btn.classList.add('active'); btn.classList.add('active');
}); });
}); });
}
// Subscription section expand/collapse event // Subscription section expand/collapse event
const subscriptionTitle = document.querySelector('.nav-section-title'); const subscriptionTitle = document.querySelector('.nav-section-title');
if (subscriptionTitle) {
subscriptionTitle.addEventListener('click', () => { subscriptionTitle.addEventListener('click', () => {
const subscriptionsList = document.querySelector('.subscriptions-list'); const subscriptionsList = document.querySelector('.subscriptions-list');
const chevron = subscriptionTitle.querySelector('i'); const chevron = subscriptionTitle.querySelector('i');
if (subscriptionsList && chevron) {
subscriptionsList.classList.toggle('collapsed'); subscriptionsList.classList.toggle('collapsed');
if (subscriptionsList.classList.contains('collapsed')) { if (subscriptionsList.classList.contains('collapsed')) {
@@ -799,8 +820,10 @@ function bindEvents() { // Bind events to the DOM elements
} else { } else {
chevron.style.transform = 'rotate(0deg)'; chevron.style.transform = 'rotate(0deg)';
} }
}
}); });
} }
}
// Page load event: initialize the application // Page load event: initialize the application
window.addEventListener('DOMContentLoaded', init); window.addEventListener('DOMContentLoaded', init);

601
js/video.js Normal file
View File

@@ -0,0 +1,601 @@
let videoDetails = {
1: {
title: "透明なパレット (self cover)",
channel: "Aqu3ra",
channelAvatar: "img/avatar/aqu3ra.png",
subscribers: "125K subscribers",
likes: "45.2K",
views: "576,197",
uploadDate: "1 months ago",
description: "description"
},
2: {
title: "【歌ってみた】Duvet / covered by ヰ世界情緒",
channel: "ヰ世界情緒 -Isekaijoucho-",
channelAvatar: "img/avatar/isekaijoucho.jpg",
subscribers: "2.5M subscribers",
likes: "89.3K",
views: "721,627",
uploadDate: "5 days ago",
description: "description"
},
3: {
title: "ヨルシカ 『 晴る 』 文化祭2025",
channel: "countben",
channelAvatar: "img/avatar/default.jpg",
subscribers: "45K subscribers",
likes: "12.8K",
views: "142,561",
uploadDate: "2 months ago",
description: "description"
},
4: {
title: "【Silent Witch沈默魔女的秘密】OST 主題曲「沈黙の魔女」bgmKitkit Lu COVER",
channel: "KitKit Lu",
channelAvatar: "img/avatar/default.jpg",
subscribers: "890K subscribers",
likes: "156K",
views: "1,856,264",
uploadDate: "5 months ago",
description: "description"
},
5: {
title: "I Can't Wait feat. GUMI",
channel: "d0tc0mmie",
channelAvatar: "img/avatar/default.jpg",
subscribers: "234K subscribers",
likes: "28.5K",
views: "315,293",
uploadDate: "1 month ago",
description: "description"
},
6: {
title: "MiSiDE: ZERO Update Full Gameplay + Ending (Showcase)",
channel: "RUSH PLAY",
channelAvatar: "img/avatar/default.jpg",
subscribers: "1.2M subscribers",
likes: "67.8K",
views: "900,156",
uploadDate: "4 weeks ago",
description: "description"
},
7: {
title: "Minecraftの効果音でサイエンス",
channel: "gmailアカウント",
channelAvatar: "img/avatar/default.jpg",
subscribers: "567K subscribers",
likes: "45.2K",
views: "651,192",
uploadDate: "11 months ago",
description: "description"
},
8: {
title: "osu! NEEDS to Take Notes... (00PARTS World's 1st Clear)",
channel: "bmc",
channelAvatar: "img/avatar/default.jpg",
subscribers: "345K subscribers",
likes: "78.9K",
views: "892,163",
uploadDate: "1 month ago",
description: "description"
},
9: {
title: "ヨルシカ - 春泥棒OFFICIAL VIDEO",
channel: "ヨルシカ / n-buna Official",
channelAvatar: "img/avatar/default.jpg",
subscribers: "3.5M subscribers",
likes: "2.1M",
views: "123,456,789",
uploadDate: "5 years ago",
description: "description"
},
10: {
title: "YOASOBI「アイドル」Official Music Video",
channel: "YOASOBI Official",
channelAvatar: "img/avatar/default.jpg",
subscribers: "8.9M subscribers",
likes: "5.6M",
views: "456,789,123",
uploadDate: "2 years ago",
description: "description"
},
11: {
title: "花譜 - 不可解",
channel: "花譜 -KAF-",
channelAvatar: "img/avatar/kaf.png",
subscribers: "1.2M subscribers",
likes: "890K",
views: "12,345,678",
uploadDate: "3 years ago",
description: "description"
},
12: {
title: "ZUTOMAYO - STUDY ME",
channel: "ZUTOMAYO",
channelAvatar: "img/avatar/ztmy.jpg",
subscribers: "2.3M subscribers",
likes: "1.2M",
views: "23,456,789",
uploadDate: "1 year ago",
description: "description"
},
13: {
title: "Eve - 廻廻奇譚",
channel: "Eve",
channelAvatar: "img/avatar/default.jpg",
subscribers: "4.5M subscribers",
likes: "3.2M",
views: "89,123,456",
uploadDate: "4 years ago",
description: "description"
},
14: {
title: "ClariS - コネクト",
channel: "ClariS Official",
channelAvatar: "img/avatar/default.jpg",
subscribers: "2.8M subscribers",
likes: "1.8M",
views: "45,678,901",
uploadDate: "10 years ago",
description: "description"
},
15: {
title: "米津玄師 Kenshi Yonezu - Lemon",
channel: "Kenshi Yonezu 米津玄師",
channelAvatar: "img/avatar/default.jpg",
subscribers: "5.6M subscribers",
likes: "4.2M",
views: "96,539,145",
uploadDate: "7 years ago",
description: "description"
},
16: {
title: "【初音ミク】 夜明けと蛍 【オリジナル】",
channel: "ヨルシカ / n-buna Official",
channelAvatar: "img/avatar/default.jpg",
subscribers: "3.5M subscribers",
likes: "1.5M",
views: "27,749,534",
uploadDate: "8 years ago",
description: "description"
},
17: {
title: "Empty old City - Buffer",
channel: "Empty old city",
channelAvatar: "img/avatar/default.jpg",
subscribers: "234K subscribers",
likes: "45.6K",
views: "838,946",
uploadDate: "1 year ago",
description: "description"
},
18: {
title: "I built Git for Minecraft for a hackathon and won",
channel: "MathRayyan",
channelAvatar: "img/avatar/default.jpg",
subscribers: "89K subscribers",
likes: "23.4K",
views: "288,346",
uploadDate: "12 days ago",
description: "description"
},
19: {
title: "怎麼讓遊戲越來越真PBR貼圖是怎麼做到的",
channel: "孫拓海Taku",
channelAvatar: "img/avatar/default.jpg",
subscribers: "456K subscribers",
likes: "12.3K",
views: "25,364",
uploadDate: "3 weeks ago",
description: "description"
},
20: {
title: "「空あくあ!」當さくな說出這句台詞時 全場都淚崩了....【結城さくな】【Vtuber中文/翻譯/精華】",
channel: "Dar烤肉!",
channelAvatar: "img/avatar/default.jpg",
subscribers: "567K subscribers",
likes: "34.5K",
views: "116,498",
uploadDate: "7 days ago",
description: "description"
},
21: {
title: "「明日方舟:终末地性能分析:二游画质巅峰?榨干手机!",
channel: "极客湾Geekerwan",
channelAvatar: "img/avatar/default.jpg",
subscribers: "4.5M subscribers",
likes: "234K",
views: "51,649",
uploadDate: "10 days ago",
description: "description"
},
22: {
title: "What GPU is the BEST for Linux Gaming?",
channel: "Linus Tech Tips",
channelAvatar: "img/avatar/default.jpg",
subscribers: "15.6M subscribers",
likes: "456K",
views: "96,751",
uploadDate: "7 days ago",
description: "description"
},
23: {
title: "How Hard Is It to Build Your First PC?",
channel: "PLACITECH",
channelAvatar: "img/avatar/default.jpg",
subscribers: "1.2M subscribers",
likes: "89K",
views: "4,351",
uploadDate: "2 months ago",
description: "description"
},
24: {
title: "回顾我们最中意的 2025 年产品发布 - 谷歌开发者新闻(年终特辑)",
channel: "Google for Developers",
channelAvatar: "img/avatar/default.jpg",
subscribers: "8.9M subscribers",
likes: "234K",
views: "3,475",
uploadDate: "1 month ago",
description: "description"
},
25: {
title: "Commodore 128 Alternate Universe",
channel: "The 8-Bit Guy",
channelAvatar: "img/avatar/default.jpg",
subscribers: "2.3M subscribers",
likes: "456K",
views: "17,498",
uploadDate: "4 months ago",
description: "description"
},
26: {
title: "\"The Roller Coaster\" - Behind the Scenes - Time Lapse & Commentary",
channel: "AlanBeckerTutorials",
channelAvatar: "img/avatar/default.jpg",
subscribers: "5.6M subscribers",
likes: "23.4K",
views: "193,762",
uploadDate: "7 years ago",
description: "description"
},
27: {
title: "Animation vs. Game Design",
channel: "Alan Backer",
channelAvatar: "img/avatar/default.jpg",
subscribers: "3.4M subscribers",
likes: "56.7K",
views: "694,264",
uploadDate: "2 months ago",
description: "description"
},
28: {
title: "The Far Lands - Animation vs. Minecraft Shorts Ep 38",
channel: "Alan Backer",
channelAvatar: "img/avatar/default.jpg",
subscribers: "3.4M subscribers",
likes: "78.9K",
views: "975,192",
uploadDate: "1 month ago",
description: "description"
},
29: {
title: "【Official MV】ray 超かぐや姫Versionかぐや (cv.夏吉ゆうこ) 月見ヤチヨ (cv.早見沙織) from #超かぐや姫 !【新作アニメーション】",
channel: "『超かぐや姫 ! 』公式",
channelAvatar: "img/avatar/cpk.png",
subscribers: "1.5M subscribers",
likes: "234K",
views: "78,192",
uploadDate: "5 days ago",
description: "description"
},
30: {
title: "ドッペルゲンガー feat.初音ミク#vocaloid #初音ミク",
channel: "Vocaloid Channel",
channelAvatar: "img/avatar/default.jpg",
subscribers: "2.3M subscribers",
likes: "12.3K",
views: "2,359",
uploadDate: "3 days ago",
description: "description"
},
31: {
title: "Melt CPK! Remix — English Subtitled Version",
channel: "Vocaloid Channel",
channelAvatar: "img/avatar/default.jpg",
subscribers: "2.3M subscribers",
likes: "11.2K",
views: "2,264",
uploadDate: "3 days ago",
description: "description"
},
32: {
title: "【Suite History】花譜×#KTちゃん「ギミギミ逃避行feat. #KTちゃん(Prod. peko)」 #花譜 #KTちゃん #組曲 #shorts",
channel: "花譜 -KAF-",
channelAvatar: "img/avatar/kaf.png",
subscribers: "1.2M subscribers",
likes: "23.4K",
views: "5,430",
uploadDate: "2 days ago",
description: "description"
},
33: {
title: "ヨルシカ - プレイシック #ヨルシカ #プレイシック #二人称 #yorushika #playsick #secondperson",
channel: "ヨルシカ / n-buna Official",
channelAvatar: "img/avatar/default.jpg",
subscribers: "3.5M subscribers",
likes: "156K",
views: "39,254",
uploadDate: "10 days ago",
description: "description"
},
34: {
title: "#ヨルシカ #live #前世 #yorushika",
channel: "ヨルシカ / n-buna Official",
channelAvatar: "img/avatar/default.jpg",
subscribers: "3.5M subscribers",
likes: "123K",
views: "36,181",
uploadDate: "1 month ago",
description: "description"
},
35: {
title: "【歌ってみた】蜜月アン・ドゥ・トロワ covered by ヰ世界情緒 #shorts",
channel: "ヰ世界情緒 -Isekaijoucho-",
channelAvatar: "img/avatar/isekaijoucho.jpg",
subscribers: "2.5M subscribers",
likes: "8.9K",
views: "2,163",
uploadDate: "6 days ago",
description: "description"
},
36: {
title: "Thank you 2025🩵Have a wonderful new year! #YOASOBI_2025",
channel: "YOASOBI Official",
channelAvatar: "img/avatar/default.jpg",
subscribers: "8.9M subscribers",
likes: "123K",
views: "36,165",
uploadDate: "20 days ago",
description: "description"
},
37: {
title: "初めてラブソングを書きました。新曲「ハートマーク feat.川谷絵音」#礼衣 #ハートマーク",
channel: "礼衣 / Rei",
channelAvatar: "img/avatar/rei.jpg",
subscribers: "567K subscribers",
likes: "12.3K",
views: "2,609",
uploadDate: "3 days ago",
description: "description"
},
38: {
title: "Would you play this at the school talent show for $500? #osu #osugame",
channel: "osu! Channel",
channelAvatar: "img/avatar/default.jpg",
subscribers: "1.5M subscribers",
likes: "23.4K",
views: "133,658",
uploadDate: "2 months ago",
description: "description"
},
39: {
title: "May your holidays be filled with great vibes, full combos, and lots of epic tracks! 🎵",
channel: "osu! Channel",
channelAvatar: "img/avatar/default.jpg",
subscribers: "1.5M subscribers",
likes: "3.4K",
views: "1,658",
uploadDate: "1 months ago",
description: "description"
},
40: {
title: "PANDORA PARADOXXX RE:MASTER alternative spin method #maimai #maimaiでらっくす #maimai_dx",
channel: "Rhythm Games",
channelAvatar: "img/avatar/default.jpg",
subscribers: "890K subscribers",
likes: "5.6K",
views: "25,365",
uploadDate: "12 days ago",
description: "description"
},
41: {
title: "Arch Linux VS Debian in 1 Min #linux #arch #debian",
channel: "Linux Tips",
channelAvatar: "img/avatar/default.jpg",
subscribers: "456K subscribers",
likes: "4.5K",
views: "3,565",
uploadDate: "11 days ago",
description: "description"
},
42: {
title: "#linux #homelab #opensource #tech #debian",
channel: "Linux Tips",
channelAvatar: "img/avatar/default.jpg",
subscribers: "456K subscribers",
likes: "6.7K",
views: "7,925",
uploadDate: "10 days ago",
description: "description"
},
43: {
title: "The one key keyboard #tech",
channel: "Tech Shorts",
channelAvatar: "img/avatar/default.jpg",
subscribers: "1.2M subscribers",
likes: "8.9K",
views: "4,571",
uploadDate: "1 month ago",
description: "description"
}
};
let comments = [
{
author: "AnimeFan123",
avatar: "img/avatar/default.jpg",
time: "2 hours ago",
content: "This is amazing! The quality is incredible and the editing is top-notch. Keep up the great work!"
},
{
author: "MusicLover456",
avatar: "img/avatar/default.jpg",
time: "5 hours ago",
content: "I've been listening to this on repeat for days. Can't get enough of it!"
},
{
author: "TechEnthusiast",
avatar: "img/avatar/default.jpg",
time: "1 day ago",
content: "Great explanation! Really helped me understand this topic better."
},
{
author: "GamerPro99",
avatar: "img/avatar/default.jpg",
time: "2 days ago",
content: "This gameplay is insane! How did you even manage to do that?"
},
{
author: "ArtFanatic",
avatar: "img/avatar/default.jpg",
time: "3 days ago",
content: "The animation style is so unique and beautiful. Love it!"
},
{
author: "SubscriberOne",
avatar: "img/avatar/default.jpg",
time: "4 days ago",
content: "First! Been waiting for this video for so long!"
},
{
author: "CasualViewer",
avatar: "img/avatar/default.jpg",
time: "5 days ago",
content: "Just stumbled upon this channel and I'm already in love with the content!"
},
{
author: "CommentMaster",
avatar: "img/avatar/default.jpg",
time: "1 week ago",
content: "This deserves way more views! Sharing with all my friends!"
}
];
function getVideoIdFromUrl() {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get('id');
}
function loadVideoInfo() {
const videoId = getVideoIdFromUrl();
const videoInfo = videoDetails[videoId];
if (videoInfo) {
document.getElementById('videoTitle').textContent = videoInfo.title;
document.getElementById('channelName').textContent = videoInfo.channel;
document.getElementById('channelSubscribers').textContent = videoInfo.subscribers;
document.getElementById('likeCount').textContent = videoInfo.likes;
document.getElementById('videoViews').textContent = videoInfo.views + ' views';
document.getElementById('videoUploadDate').textContent = videoInfo.uploadDate;
document.getElementById('videoDescription').textContent = videoInfo.description;
const channelAvatar = document.getElementById('channelAvatar');
if (videoInfo.channelAvatar && videoInfo.channelAvatar !== 'img/avatar/default.jpg') {
channelAvatar.innerHTML = `<img src="${videoInfo.channelAvatar}" alt="${videoInfo.channel}">`;
}
document.title = videoInfo.title + ' - YouTube Revived';
}
renderComments();
}
function renderComments() {
const commentsList = document.getElementById('commentsList');
const commentsCount = document.getElementById('commentsCount');
commentsCount.textContent = comments.length + ' Comments';
commentsList.innerHTML = '';
comments.forEach(comment => {
const commentElement = document.createElement('div');
commentElement.className = 'comment-item';
commentElement.innerHTML = `
<div class="comment-avatar">
<img src="${comment.avatar}" alt="${comment.author}">
</div>
<div class="comment-content">
<div class="comment-header">
<span class="comment-author">${comment.author}</span>
<span class="comment-time">${comment.time}</span>
</div>
<p class="comment-text">${comment.content}</p>
<div class="comment-actions">
<button class="comment-action-btn">
<i class="fas fa-thumbs-up"></i>
</button>
<button class="comment-action-btn">
<i class="fas fa-thumbs-down"></i>
</button>
<button class="comment-action-btn">
<i class="fas fa-reply"></i>
</button>
</div>
</div>
`;
commentsList.appendChild(commentElement);
});
}
function initVideoPage() {
loadVideoInfo();
setupEventListeners();
}
function setupEventListeners() {
const moreBtn = document.getElementById('moreBtn');
const moreDropdown = document.getElementById('moreDropdown');
if (moreBtn && moreDropdown) {
moreBtn.addEventListener('click', (e) => {
e.stopPropagation();
moreDropdown.classList.toggle('show');
});
document.addEventListener('click', () => {
moreDropdown.classList.remove('show');
});
}
const descriptionToggle = document.getElementById('descriptionToggle');
const videoDescription = document.getElementById('videoDescription');
if (descriptionToggle && videoDescription) {
descriptionToggle.addEventListener('click', () => {
videoDescription.classList.toggle('expanded');
const icon = descriptionToggle.querySelector('i');
if (videoDescription.classList.contains('expanded')) {
icon.classList.remove('fa-chevron-down');
icon.classList.add('fa-chevron-up');
} else {
icon.classList.remove('fa-chevron-up');
icon.classList.add('fa-chevron-down');
}
});
}
const subscribeBtn = document.getElementById('subscribeBtn');
if (subscribeBtn) {
subscribeBtn.addEventListener('click', () => {
subscribeBtn.classList.toggle('subscribed');
if (subscribeBtn.classList.contains('subscribed')) {
subscribeBtn.textContent = 'Subscribed';
} else {
subscribeBtn.textContent = 'Subscribe';
}
});
}
}
document.addEventListener('DOMContentLoaded', initVideoPage);

BIN
src/video.mp4 Normal file

Binary file not shown.

View File

@@ -6,6 +6,7 @@
<title>YouTube Revived</title> <title>YouTube Revived</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="css/style.css"> <link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/video.css">
</head> </head>
<body> <body>
<div class="container"> <div class="container">
@@ -48,7 +49,7 @@
<main class="main-content"> <main class="main-content">
<!-- Sidebar --> <!-- Sidebar -->
<aside class="sidebar"> <aside class="sidebar collapsed">
<nav class="sidebar-nav"> <nav class="sidebar-nav">
<div class="nav-section"> <div class="nav-section">
<button class="nav-btn active"> <button class="nav-btn active">
@@ -164,9 +165,102 @@
</div> </div>
</aside> </aside>
<div class="video-content-wrapper">
<!-- Video Player -->
<div class="video-player">
<video src="src/video.mp4" controls></video>
</div>
<!-- Video Info -->
<div class="video-info-container">
<h1 class="video-title" id="videoTitle">Video Title</h1>
<div class="video-channel-info">
<div class="channel-left">
<div class="channel-avatar" id="channelAvatar">
<i class="fas fa-user"></i>
</div>
<div class="channel-details">
<div class="channel-name" id="channelName">Channel Name</div>
<div class="channel-subscribers" id="channelSubscribers">0 subscribers</div>
</div>
</div>
<button class="subscribe-btn" id="subscribeBtn">Subscribe</button>
</div>
<div class="video-actions">
<div class="like-dislike-buttons">
<button class="action-btn like-btn">
<i class="fas fa-thumbs-up"></i>
<span id="likeCount">0</span>
</button>
<button class="action-btn dislike-btn">
<i class="fas fa-thumbs-down"></i>
</button>
</div>
<button class="action-btn share-btn">
<i class="fas fa-share"></i>
<span>Share</span>
</button>
<button class="action-btn save-btn">
<i class="fas fa-bookmark"></i>
<span>Save</span>
</button>
<button class="action-btn clip-btn">
<i class="fas fa-cut"></i>
<span>Clip</span>
</button>
<div class="more-options">
<button class="action-btn more-btn" id="moreBtn">
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="more-dropdown" id="moreDropdown">
<div class="more-item download-item">
<i class="fas fa-download"></i>
<span>Download</span>
</div>
<div class="more-item report-item">
<i class="fas fa-flag"></i>
<span>Report</span>
</div>
</div>
</div>
</div>
<div class="video-stats">
<span id="videoViews">0 views</span>
<span id="videoUploadDate">0 ago</span>
</div>
<div class="video-description">
<div class="description-toggle" id="descriptionToggle">
<i class="fas fa-chevron-down"></i>
</div>
<p id="videoDescription">description</p>
</div>
</div>
<!-- Comments Section -->
<div class="comments-section">
<h2 class="comments-header">
<span id="commentsCount">0 Comments</span>
</h2>
<div class="add-comment">
<div class="comment-avatar">
<i class="fas fa-user"></i>
</div>
<div class="comment-input-container">
<input type="text" class="comment-input" placeholder="Add a comment...">
</div>
</div>
<div class="comments-list" id="commentsList">
</div>
</div>
</div>
</main> </main>
</div> </div>
<script src="js/script.js"></script> <script src="js/script.js"></script>
<script src="js/video.js"></script>
</body> </body>
</html> </html>