fix: channel id irregular

This commit is contained in:
obvTiger 2025-06-13 23:20:44 +02:00
parent 77b597e5d8
commit c1a4025cbd
5 changed files with 1152 additions and 2026 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,479 +1,534 @@
document.addEventListener("DOMContentLoaded", () => {
const playerWrapper = document.getElementById("player-wrapper");
const preparationOverlay = document.getElementById("preparation-overlay");
const preparationStatus = document.getElementById("preparation-status");
const preparationProgressBar = document.getElementById("preparation-progress-bar");
const reloadButton = document.getElementById("reload-button");
const relatedGrid = document.getElementById("related-grid");
const logo = document.getElementById("logo");
const searchInput = document.getElementById("search-input");
const searchButton = document.getElementById("search-button");
const playerWrapper = document.getElementById("player-wrapper");
const preparationOverlay = document.getElementById("preparation-overlay");
const preparationStatus = document.getElementById("preparation-status");
const preparationProgressBar = document.getElementById(
"preparation-progress-bar"
);
const reloadButton = document.getElementById("reload-button");
const relatedGrid = document.getElementById("related-grid");
const logo = document.getElementById("logo");
const searchInput = document.getElementById("search-input");
const searchButton = document.getElementById("search-button");
const detailTitle = document.getElementById("detail-title");
const detailAvatar = document.getElementById("detail-avatar");
const detailUploader = document.getElementById("detail-uploader");
const detailUploaded = document.getElementById("detail-uploaded");
const detailViews = document.getElementById("detail-views");
const detailDuration = document.getElementById("detail-duration");
const detailDescription = document.getElementById("detail-description");
const detailTitle = document.getElementById("detail-title");
const detailAvatar = document.getElementById("detail-avatar");
const detailUploader = document.getElementById("detail-uploader");
const detailUploaded = document.getElementById("detail-uploaded");
const detailViews = document.getElementById("detail-views");
const detailDuration = document.getElementById("detail-duration");
const detailDescription = document.getElementById("detail-description");
const videoCardTemplate = document.getElementById("video-card-template");
const videoCardTemplate = document.getElementById("video-card-template");
let activeWs = null;
let hlsInstance = null;
let player = null;
let prepareInProgress = false;
let currentVideo = null;
let activeWs = null;
let hlsInstance = null;
let player = null;
let prepareInProgress = false;
let currentVideo = null;
const trendingApiUrl = "/api/trending";
const searchApiUrl = "/api/search";
const wsProtocol = window.location.protocol === "https:" ? "wss://" : "ws://";
const wsBaseUrl = wsProtocol + window.location.host + "/api/v1/prepare";
const trendingApiUrl = "/api/trending";
const searchApiUrl = "/api/search";
const wsProtocol = window.location.protocol === "https:" ? "wss://" : "ws://";
const wsBaseUrl = wsProtocol + window.location.host + "/api/v1/prepare";
function formatViews(views) {
if (views >= 1000000) {
return `${(views / 1000000).toFixed(1)}M`;
} else if (views >= 1000) {
return `${(views / 1000).toFixed(1)}K`;
} else {
return views.toString();
}
function formatViews(views) {
if (views >= 1000000) {
return `${(views / 1000000).toFixed(1)}M`;
} else if (views >= 1000) {
return `${(views / 1000).toFixed(1)}K`;
} else {
return views.toString();
}
}
function formatDuration(seconds) {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = seconds % 60;
if (hours > 0) {
return `${hours}:${minutes.toString().padStart(2, "0")}:${secs
.toString()
.padStart(2, "0")}`;
} else {
return `${minutes}:${secs.toString().padStart(2, "0")}`;
}
}
function getVideoIdFromUrl() {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get("v");
}
function getVideoElement() {
const existingVideo = playerWrapper.querySelector("video");
if (existingVideo) {
existingVideo.remove();
}
function formatDuration(seconds) {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = seconds % 60;
const videoElement = document.createElement("video");
videoElement.id = "video-player";
videoElement.setAttribute("controls", "");
videoElement.setAttribute("crossorigin", "");
videoElement.setAttribute("playsinline", "");
if (hours > 0) {
return `${hours}:${minutes.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
} else {
return `${minutes}:${secs.toString().padStart(2, "0")}`;
}
playerWrapper.appendChild(videoElement);
return videoElement;
}
function cleanupResources() {
if (activeWs && activeWs.readyState === WebSocket.OPEN) {
activeWs.close();
activeWs = null;
}
function getVideoIdFromUrl() {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get('v');
if (hlsInstance) {
hlsInstance.destroy();
hlsInstance = null;
}
function getVideoElement() {
const existingVideo = playerWrapper.querySelector("video");
if (existingVideo) {
existingVideo.remove();
}
const videoElement = document.createElement("video");
videoElement.id = "video-player";
videoElement.setAttribute("controls", "");
videoElement.setAttribute("crossorigin", "");
videoElement.setAttribute("playsinline", "");
playerWrapper.appendChild(videoElement);
return videoElement;
if (player) {
player.destroy();
player = null;
}
function cleanupResources() {
if (activeWs && activeWs.readyState === WebSocket.OPEN) {
activeWs.close();
activeWs = null;
}
prepareInProgress = false;
}
if (hlsInstance) {
hlsInstance.destroy();
hlsInstance = null;
}
if (player) {
player.destroy();
player = null;
}
prepareInProgress = false;
}
const observeElements = () => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add("visible");
observer.unobserve(entry.target);
}
});
},
{
root: null,
threshold: 0.1,
rootMargin: "0px 0px -50px 0px",
}
);
document.querySelectorAll(".animated-item:not(.visible)").forEach((item) => {
observer.observe(item);
const observeElements = () => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add("visible");
observer.unobserve(entry.target);
}
});
},
{
root: null,
threshold: 0.1,
rootMargin: "0px 0px -50px 0px",
}
);
document
.querySelectorAll(".animated-item:not(.visible)")
.forEach((item) => {
observer.observe(item);
});
};
function openVideoPlayer(video) {
sessionStorage.setItem("currentVideo", JSON.stringify(video));
const videoId = video.url.replace("/watch?v=", "");
window.location.href = `video.html?v=${videoId}`;
}
function createVideoCard(video, index) {
if (video.type !== "stream" && video.type !== undefined) {
return null;
}
const card = videoCardTemplate.content.cloneNode(true);
const thumbnail = card.querySelector(".thumbnail");
const duration = card.querySelector(".duration");
const title = card.querySelector(".video-title");
const uploaderAvatar = card.querySelector(".uploader-avatar");
const uploaderName = card.querySelector(".uploader-name");
const viewCount = card.querySelector(".view-count");
const uploadTime = card.querySelector(".upload-time");
const videoCard = card.querySelector(".video-card");
try {
thumbnail.src = video.thumbnail || "";
thumbnail.alt = video.title || "Video thumbnail";
if (typeof video.duration === "number") {
duration.textContent = formatDuration(video.duration);
} else {
duration.textContent = "0:00";
}
title.textContent = video.title || "Untitled video";
uploaderAvatar.src = video.uploaderAvatar || "";
uploaderAvatar.alt = `${video.uploaderName || "Uploader"} avatar`;
uploaderName.textContent = video.uploaderName || "Unknown uploader";
viewCount.textContent = formatViews(video.views || 0);
uploadTime.textContent = video.uploadedDate || "Unknown date";
videoCard.style.animationDelay = `${index * 0.05}s`;
videoCard.addEventListener("click", async (e) => {
const uploader = card.querySelector(".uploader");
if (
e.target === uploaderAvatar ||
e.target === uploaderName ||
(e.target.parentElement && e.target.parentElement === uploader)
) {
e.stopPropagation();
if (video.uploaderUrl) {
const username = video.uploaderUrl;
const channelId = await getChannelId(username);
window.location.href = `index.html#channelId=${channelId}`;
}
} else {
openVideoPlayer(video);
}
});
} catch (error) {
console.error("Error creating video card:", error, video);
}
return card;
}
async function findVideoById(videoId) {
const storedVideo = sessionStorage.getItem("currentVideo");
if (storedVideo) {
const video = JSON.parse(storedVideo);
if (video.url.includes(videoId)) {
return video;
}
}
const storedTrending = sessionStorage.getItem("trendingVideos");
if (storedTrending) {
const trendingVideos = JSON.parse(storedTrending);
const foundInTrending = trendingVideos.find((v) =>
v.url.includes(videoId)
);
if (foundInTrending) {
return foundInTrending;
}
}
const storedSearch = sessionStorage.getItem("searchResults");
if (storedSearch) {
const searchResults = JSON.parse(storedSearch);
const foundInSearch = searchResults.find((v) => v.url.includes(videoId));
if (foundInSearch) {
return foundInSearch;
}
}
try {
console.log("Video not found in cache, fetching from trending API...");
const response = await fetch(trendingApiUrl);
if (response.ok) {
const videos = await response.json();
const foundVideo = videos.find((v) => v.url.includes(videoId));
if (foundVideo) {
return foundVideo;
}
}
const lastSearchQuery = sessionStorage.getItem("lastSearchQuery");
if (lastSearchQuery) {
console.log("Trying search API with last query...");
const searchUrl = `${searchApiUrl}?q=${encodeURIComponent(
lastSearchQuery
)}&filter=all`;
const searchResponse = await fetch(searchUrl);
if (searchResponse.ok) {
const searchData = await searchResponse.json();
const searchResults = searchData.items
? searchData.items.filter((item) => item.type === "stream")
: [];
const foundInSearch = searchResults.find((v) =>
v.url.includes(videoId)
);
if (foundInSearch) {
return foundInSearch;
}
}
}
} catch (error) {
console.error("Error fetching video from API:", error);
}
return null;
}
function initializePlayer(streamUrl) {
const videoElement = getVideoElement();
if (player) {
player.destroy();
}
player = new Plyr(videoElement, {
controls: [
"play-large",
"play",
"progress",
"current-time",
"mute",
"volume",
"settings",
"pip",
"airplay",
"fullscreen",
],
settings: ["captions", "quality", "speed"],
tooltips: { controls: true, seek: true },
keyboard: { focused: true, global: true },
});
if (videoElement.canPlayType("application/vnd.apple.mpegurl")) {
videoElement.src = streamUrl;
} else if (Hls.isSupported()) {
if (hlsInstance) {
hlsInstance.destroy();
}
hlsInstance = new Hls({
maxBufferLength: 30,
maxMaxBufferLength: 60,
});
hlsInstance.loadSource(streamUrl);
hlsInstance.attachMedia(videoElement);
hlsInstance.on(Hls.Events.MANIFEST_PARSED, () => {
videoElement.play().catch((e) => {
console.warn("Autoplay prevented:", e);
});
});
} else {
console.error("HLS is not supported in this browser");
preparationStatus.textContent =
"Your browser does not support HLS playback";
}
}
function prepareVideo(videoUrl) {
if (prepareInProgress) {
return;
}
prepareInProgress = true;
preparationOverlay.style.display = "flex";
preparationStatus.textContent = "Preparing video...";
preparationProgressBar.style.width = "0%";
reloadButton.style.display = "none";
const cleanVideoId = videoUrl.replace("/watch?v=", "");
if (activeWs && activeWs.readyState === WebSocket.OPEN) {
activeWs.close();
}
activeWs = new WebSocket(wsBaseUrl);
activeWs.onopen = () => {
activeWs.send(JSON.stringify({ videoId: cleanVideoId }));
};
function openVideoPlayer(video) {
sessionStorage.setItem('currentVideo', JSON.stringify(video));
const videoId = video.url.replace("/watch?v=", "");
window.location.href = `video.html?v=${videoId}`;
activeWs.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.ok === "true") {
preparationStatus.textContent = "Connection established...";
} else if (data.preparing !== undefined) {
const percentage = parseFloat(data.preparing);
preparationProgressBar.style.width = `${percentage}%`;
preparationStatus.textContent = `Preparing video... ${percentage.toFixed(
0
)}%`;
} else if (data.done && data.streamUrl) {
preparationStatus.textContent = "Video ready! Starting playback...";
preparationProgressBar.style.width = "100%";
setTimeout(() => {
preparationOverlay.style.display = "none";
initializePlayer(data.streamUrl);
prepareInProgress = false;
}, 500);
activeWs.close();
activeWs = null;
} else {
preparationStatus.textContent =
"Video playback failed. Try reloading the page.";
reloadButton.style.display = "block";
prepareInProgress = false;
}
};
activeWs.onerror = (error) => {
console.error("WebSocket error:", error);
preparationStatus.textContent =
"Error preparing video. Please try again.";
reloadButton.style.display = "block";
prepareInProgress = false;
setTimeout(() => {
if (preparationOverlay.style.display !== "none") {
preparationOverlay.style.display = "none";
}
}, 3000);
};
activeWs.onclose = () => {
console.log("WebSocket connection closed");
if (activeWs === activeWs) {
activeWs = null;
}
};
}
async function getChannelId(username) {
try {
if (username.startsWith("UC")) {
return username;
}
const response = await fetch(
`/api/channel/${encodeURIComponent(username)}`
);
if (!response.ok) {
throw new Error(`Failed to fetch channel ID: ${response.status}`);
}
const data = await response.json();
return data.channelId;
} catch (error) {
console.error("Error fetching channel ID:", error);
return username;
}
}
function createVideoCard(video, index) {
if (video.type !== "stream" && video.type !== undefined) {
return null;
function displayVideoDetails(video) {
document.title = `${video.title} - Repiped`;
detailTitle.textContent = video.title;
detailAvatar.src = video.uploaderAvatar;
detailUploader.textContent = video.uploaderName;
detailUploaded.textContent = video.uploadedDate;
detailViews.textContent = formatViews(video.views);
detailDuration.textContent = formatDuration(video.duration);
detailDescription.textContent =
video.shortDescription || "No description available";
const videoDetailUploader = document.querySelector(
".video-detail-uploader"
);
if (videoDetailUploader) {
videoDetailUploader.style.cursor = "pointer";
videoDetailUploader.addEventListener("click", async () => {
if (video.uploaderUrl) {
const username = video.uploaderUrl;
const channelId = await getChannelId(username);
window.location.href = `index.html#channelId=${channelId}`;
}
const card = videoCardTemplate.content.cloneNode(true);
const thumbnail = card.querySelector(".thumbnail");
const duration = card.querySelector(".duration");
const title = card.querySelector(".video-title");
const uploaderAvatar = card.querySelector(".uploader-avatar");
const uploaderName = card.querySelector(".uploader-name");
const viewCount = card.querySelector(".view-count");
const uploadTime = card.querySelector(".upload-time");
const videoCard = card.querySelector(".video-card");
try {
thumbnail.src = video.thumbnail || "";
thumbnail.alt = video.title || "Video thumbnail";
if (typeof video.duration === "number") {
duration.textContent = formatDuration(video.duration);
} else {
duration.textContent = "0:00";
}
title.textContent = video.title || "Untitled video";
uploaderAvatar.src = video.uploaderAvatar || "";
uploaderAvatar.alt = `${video.uploaderName || "Uploader"} avatar`;
uploaderName.textContent = video.uploaderName || "Unknown uploader";
viewCount.textContent = formatViews(video.views || 0);
uploadTime.textContent = video.uploadedDate || "Unknown date";
videoCard.style.animationDelay = `${index * 0.05}s`;
videoCard.addEventListener("click", (e) => {
e.preventDefault();
openVideoPlayer(video);
});
} catch (error) {
console.error("Error creating video card:", error, video);
}
return card;
});
}
}
async function findVideoById(videoId) {
const storedVideo = sessionStorage.getItem('currentVideo');
if (storedVideo) {
const video = JSON.parse(storedVideo);
if (video.url.includes(videoId)) {
return video;
}
async function fetchRelatedVideos(currentVideo) {
try {
let videos = [];
const storedTrending = sessionStorage.getItem("trendingVideos");
if (storedTrending) {
videos = JSON.parse(storedTrending);
} else {
const response = await fetch(trendingApiUrl);
if (!response.ok) {
throw new Error("Network response was not ok");
}
videos = await response.json();
}
const storedTrending = sessionStorage.getItem('trendingVideos');
if (storedTrending) {
const trendingVideos = JSON.parse(storedTrending);
const foundInTrending = trendingVideos.find((v) => v.url.includes(videoId));
if (foundInTrending) {
return foundInTrending;
}
videos = videos
.filter((video) => video.url !== currentVideo.url)
.slice(0, 8);
relatedGrid.innerHTML = "";
videos.forEach((video, index) => {
const card = createVideoCard(video, index);
if (card) {
relatedGrid.appendChild(card);
}
});
const storedSearch = sessionStorage.getItem('searchResults');
if (storedSearch) {
const searchResults = JSON.parse(storedSearch);
const foundInSearch = searchResults.find((v) => v.url.includes(videoId));
if (foundInSearch) {
return foundInSearch;
}
}
try {
console.log('Video not found in cache, fetching from trending API...');
const response = await fetch(trendingApiUrl);
if (response.ok) {
const videos = await response.json();
const foundVideo = videos.find((v) => v.url.includes(videoId));
if (foundVideo) {
return foundVideo;
}
}
const lastSearchQuery = sessionStorage.getItem('lastSearchQuery');
if (lastSearchQuery) {
console.log('Trying search API with last query...');
const searchUrl = `${searchApiUrl}?q=${encodeURIComponent(lastSearchQuery)}&filter=all`;
const searchResponse = await fetch(searchUrl);
if (searchResponse.ok) {
const searchData = await searchResponse.json();
const searchResults = searchData.items ? searchData.items.filter((item) => item.type === "stream") : [];
const foundInSearch = searchResults.find((v) => v.url.includes(videoId));
if (foundInSearch) {
return foundInSearch;
}
}
}
} catch (error) {
console.error("Error fetching video from API:", error);
}
return null;
}
function initializePlayer(streamUrl) {
const videoElement = getVideoElement();
if (player) {
player.destroy();
}
player = new Plyr(videoElement, {
controls: [
"play-large",
"play",
"progress",
"current-time",
"mute",
"volume",
"settings",
"pip",
"airplay",
"fullscreen",
],
settings: ["captions", "quality", "speed"],
tooltips: { controls: true, seek: true },
keyboard: { focused: true, global: true },
});
if (videoElement.canPlayType("application/vnd.apple.mpegurl")) {
videoElement.src = streamUrl;
} else if (Hls.isSupported()) {
if (hlsInstance) {
hlsInstance.destroy();
}
hlsInstance = new Hls({
maxBufferLength: 30,
maxMaxBufferLength: 60,
});
hlsInstance.loadSource(streamUrl);
hlsInstance.attachMedia(videoElement);
hlsInstance.on(Hls.Events.MANIFEST_PARSED, () => {
videoElement.play().catch((e) => {
console.warn("Autoplay prevented:", e);
});
});
} else {
console.error("HLS is not supported in this browser");
preparationStatus.textContent = "Your browser does not support HLS playback";
}
}
function prepareVideo(videoUrl) {
if (prepareInProgress) {
return;
}
prepareInProgress = true;
preparationOverlay.style.display = "flex";
preparationStatus.textContent = "Preparing video...";
preparationProgressBar.style.width = "0%";
reloadButton.style.display = "none";
const cleanVideoId = videoUrl.replace("/watch?v=", "");
if (activeWs && activeWs.readyState === WebSocket.OPEN) {
activeWs.close();
}
activeWs = new WebSocket(wsBaseUrl);
activeWs.onopen = () => {
activeWs.send(JSON.stringify({ videoId: cleanVideoId }));
};
activeWs.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.ok === "true") {
preparationStatus.textContent = "Connection established...";
} else if (data.preparing !== undefined) {
const percentage = parseFloat(data.preparing);
preparationProgressBar.style.width = `${percentage}%`;
preparationStatus.textContent = `Preparing video... ${percentage.toFixed(0)}%`;
} else if (data.done && data.streamUrl) {
preparationStatus.textContent = "Video ready! Starting playback...";
preparationProgressBar.style.width = "100%";
setTimeout(() => {
preparationOverlay.style.display = "none";
initializePlayer(data.streamUrl);
prepareInProgress = false;
}, 500);
activeWs.close();
activeWs = null;
} else {
preparationStatus.textContent = "Video playback failed. Try reloading the page.";
reloadButton.style.display = "block";
prepareInProgress = false;
}
};
activeWs.onerror = (error) => {
console.error("WebSocket error:", error);
preparationStatus.textContent = "Error preparing video. Please try again.";
reloadButton.style.display = "block";
prepareInProgress = false;
setTimeout(() => {
if (preparationOverlay.style.display !== "none") {
preparationOverlay.style.display = "none";
}
}, 3000);
};
activeWs.onclose = () => {
console.log("WebSocket connection closed");
if (activeWs === activeWs) {
activeWs = null;
}
};
}
function displayVideoDetails(video) {
document.title = `${video.title} - Repiped`;
detailTitle.textContent = video.title;
detailAvatar.src = video.uploaderAvatar;
detailUploader.textContent = video.uploaderName;
detailUploaded.textContent = video.uploadedDate;
detailViews.textContent = formatViews(video.views);
detailDuration.textContent = formatDuration(video.duration);
detailDescription.textContent = video.shortDescription || "No description available";
const videoDetailUploader = document.querySelector(".video-detail-uploader");
if (videoDetailUploader) {
videoDetailUploader.style.cursor = "pointer";
videoDetailUploader.addEventListener("click", () => {
if (video.uploaderUrl) {
const channelId = video.uploaderUrl.replace("/channel/", "");
window.location.href = `index.html#channelId=${channelId}`;
}
});
}
}
async function fetchRelatedVideos(currentVideo) {
try {
let videos = [];
const storedTrending = sessionStorage.getItem('trendingVideos');
if (storedTrending) {
videos = JSON.parse(storedTrending);
} else {
const response = await fetch(trendingApiUrl);
if (!response.ok) {
throw new Error("Network response was not ok");
}
videos = await response.json();
}
videos = videos
.filter((video) => video.url !== currentVideo.url)
.slice(0, 8);
relatedGrid.innerHTML = "";
videos.forEach((video, index) => {
const card = createVideoCard(video, index);
if (card) {
relatedGrid.appendChild(card);
}
});
observeElements();
} catch (error) {
console.error("Error fetching related videos:", error);
relatedGrid.innerHTML = `
observeElements();
} catch (error) {
console.error("Error fetching related videos:", error);
relatedGrid.innerHTML = `
<div class="no-results">
<i class="fas fa-exclamation-circle fa-3x"></i>
<h3>Failed to load related videos</h3>
<p>Error loading related videos.</p>
</div>
`;
}
}
}
async function performSearch(query) {
if (!query || query.trim() === "") return;
window.location.href = `index.html#search=${encodeURIComponent(
query.trim()
)}`;
}
async function loadVideo() {
const videoId = getVideoIdFromUrl();
if (!videoId) {
alert("No video ID provided");
window.location.href = "index.html";
return;
}
async function performSearch(query) {
if (!query || query.trim() === "") return;
console.log(`Loading video: ${videoId}`);
window.location.href = `index.html#search=${encodeURIComponent(query.trim())}`;
}
try {
const video = await findVideoById(videoId);
async function loadVideo() {
const videoId = getVideoIdFromUrl();
if (!videoId) {
alert("No video ID provided");
window.location.href = "index.html";
return;
}
console.log(`Loading video: ${videoId}`);
try {
const video = await findVideoById(videoId);
if (!video) {
alert("Video not found. Redirecting to home page.");
window.location.href = "index.html";
return;
}
currentVideo = video;
sessionStorage.setItem('currentVideo', JSON.stringify(video));
displayVideoDetails(video);
prepareVideo(video.url);
fetchRelatedVideos(video);
} catch (error) {
console.error("Error loading video:", error);
alert("Error loading video. Redirecting to home page.");
window.location.href = "index.html";
}
}
logo.addEventListener("click", () => {
if (!video) {
alert("Video not found. Redirecting to home page.");
window.location.href = "index.html";
});
return;
}
searchButton.addEventListener("click", () => {
performSearch(searchInput.value);
});
currentVideo = video;
searchInput.addEventListener("keypress", (e) => {
if (e.key === "Enter") {
performSearch(searchInput.value);
}
});
sessionStorage.setItem("currentVideo", JSON.stringify(video));
reloadButton.addEventListener("click", () => {
window.location.reload();
});
displayVideoDetails(video);
window.addEventListener("beforeunload", () => {
cleanupResources();
});
prepareVideo(video.url);
loadVideo();
});
fetchRelatedVideos(video);
} catch (error) {
console.error("Error loading video:", error);
alert("Error loading video. Redirecting to home page.");
window.location.href = "index.html";
}
}
logo.addEventListener("click", () => {
window.location.href = "index.html";
});
searchButton.addEventListener("click", () => {
performSearch(searchInput.value);
});
searchInput.addEventListener("keypress", (e) => {
if (e.key === "Enter") {
performSearch(searchInput.value);
}
});
reloadButton.addEventListener("click", () => {
window.location.reload();
});
window.addEventListener("beforeunload", () => {
cleanupResources();
});
loadVideo();
});

41
routes/channel.js Normal file
View file

@ -0,0 +1,41 @@
const axios = require("axios");
const cheerio = require("cheerio");
function channelRouteHandler(app) {
app.get("/api/channel/:username", async (req, res) => {
try {
const username = req.params.username;
if (!username) {
return res.status(400).json({ error: "Username is required" });
}
const response = await axios.get(`https://www.youtube.com/${username}`);
const $ = cheerio.load(response.data);
const canonicalLink = $('link[rel="canonical"]').attr("href");
if (!canonicalLink) {
return res.status(404).json({ error: "Channel not found" });
}
const channelIdMatch = canonicalLink.match(/\/channel\/([\w-]+)/);
const channelId = channelIdMatch ? channelIdMatch[1] : null;
if (!channelId) {
return res.status(404).json({ error: "Could not extract channel ID" });
}
return res.json({
channelId: channelId,
username: username,
});
} catch (error) {
console.error("Error fetching channel ID:", error.message);
return res.status(500).json({
error: "Failed to fetch channel ID",
message: error.message,
});
}
});
}
module.exports = channelRouteHandler;

View file

@ -1,5 +1,5 @@
const https = require("https");
const yts = require('yt-search');
const yts = require("yt-search");
function searchRouteHandler(app) {
app.get("/api/search", async (req, res) => {
@ -10,32 +10,36 @@ function searchRouteHandler(app) {
}
const results = await yts(query);
console.log(results.videos[0])
console.log(results.videos[0]);
const formattedResults = {
items: results.videos.map(video => ({
items: results.videos.map((video) => ({
url: `/watch?v=${video.videoId}`,
type: "stream",
title: video.title,
thumbnail: `/api/thumbnail/${video.videoId}`,
uploaderName: video.author.name,
uploaderUrl: `/channel/${video.author.url}`,
uploaderAvatar: `/api/avatar/${video.author.url.replace("https://youtube.com/", "")}.png`,
uploaderUrl: `${video.author.url.replace(
"https://youtube.com/",
""
)}`,
uploaderAvatar: `/api/avatar/${video.author.url.replace(
"https://youtube.com/",
""
)}.png`,
uploadedDate: video.ago,
shortDescription: video.description,
duration: video.seconds,
views: video.views,
uploaded: new Date(video.timestamp * 1000).getTime(),
uploaderVerified: false,
isShort: video.duration.seconds < 60
isShort: video.duration.seconds < 60,
})),
nextpage: "",
suggestion: "",
corrected: false
corrected: false,
};
res.json(formattedResults);
} catch (err) {
console.error(`Error searching videos: ${err.message}`);
res.status(500).send("Error searching videos");