diff --git a/public/script.js b/public/script.js
deleted file mode 100644
index 5f31ff2..0000000
--- a/public/script.js
+++ /dev/null
@@ -1,1048 +0,0 @@
-document.addEventListener("DOMContentLoaded", () => {
- const overlay = document.createElement("div");
- overlay.id = "transition-overlay";
- overlay.className = "transition-overlay";
- document.body.appendChild(overlay);
-
- let activeVideoId = null;
- let prepareInProgress = false;
- let activeWs = null;
- let hlsInstance = null;
- let previousPage = "trending-page";
- let lastSearchQuery = "";
- let activeChannelId = null;
- let channelNextPageData = null;
- let isLoadingMoreVideos = false;
- let currentVideoData = null;
-
- const trendingPage = document.getElementById("trending-page");
- const searchResultsPage = document.getElementById("search-results-page");
- const channelPage = document.getElementById("channel-page");
- const videoPlayerPage = document.getElementById("video-player-page");
- const trendingGrid = document.getElementById("trending-grid");
- const trendingLoader = document.getElementById("trending-loader");
- const searchGrid = document.getElementById("search-grid");
- const searchLoader = document.getElementById("search-loader");
- const searchQueryText = document.getElementById("search-query");
- const searchCountText = document.getElementById("search-count");
- const searchInput = document.getElementById("search-input");
- const searchButton = document.getElementById("search-button");
- const relatedGrid = document.getElementById("related-grid");
- const playerWrapper = document.getElementById("player-wrapper");
- const logo = document.getElementById("logo");
- const channelVideosGrid = document.getElementById("channel-videos-grid");
- const channelLoader = document.getElementById("channel-loader");
-
- const channelBanner = document.getElementById("channel-banner");
- const channelAvatar = document.getElementById("channel-avatar");
- const channelTitle = document.getElementById("channel-title");
- const channelVerified = document.getElementById("channel-verified");
- const channelSubs = document.getElementById("channel-subs");
- const channelDescription = document.getElementById("channel-description");
-
- const preparationOverlay = document.getElementById("preparation-overlay");
- const preparationStatus = document.getElementById("preparation-status");
- const preparationProgressBar = document.getElementById(
- "preparation-progress-bar"
- );
-
- 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");
-
- let player = null;
-
- const videoCardTemplate = document.getElementById("video-card-template");
- const skeletonTemplate = document.getElementById("skeleton-template");
-
- const trendingApiUrl = "/api/trending";
- const searchApiUrl = "/api/search";
- const wsProcotol = window.location.protocol === "https:" ? "wss://" : "ws://";
- const wsBaseUrl = wsProcotol + window.location.host + "/api/v1/prepare";
- const reloadButton = document.getElementById("reload-button");
-
- reloadButton.addEventListener("click", () => {
- window.location.reload();
- });
-
- function switchPage(fromPageId, toPageId) {
- const fromPage = document.getElementById(fromPageId);
- const toPage = document.getElementById(toPageId);
-
- if (!fromPage || !toPage) return;
-
- if (toPageId !== "video-player-page") {
- previousPage = fromPageId;
- }
-
- fromPage.classList.add("zoom-out");
-
- setTimeout(() => {
- fromPage.classList.remove("active");
- fromPage.classList.remove("zoom-out");
-
- toPage.classList.add("zoom-in");
- toPage.classList.add("active");
-
- requestAnimationFrame(() => {
- requestAnimationFrame(() => {
- toPage.classList.remove("zoom-in");
- });
- });
- }, 300);
- }
-
- function getHashParams() {
- const hash = window.location.hash.substring(1);
- const params = {};
-
- hash.split("&").forEach((param) => {
- if (param) {
- const [key, value] = param.split("=");
- params[key] = decodeURIComponent(value);
- }
- });
-
- return params;
- }
-
- function updateHash(params = {}) {
- const hashParts = [];
-
- Object.entries(params).forEach(([key, value]) => {
- if (value) {
- hashParts.push(`${key}=${encodeURIComponent(value)}`);
- }
- });
-
- if (hashParts.length > 0) {
- history.pushState("", document.title, `#${hashParts.join("&")}`);
- } else {
- history.pushState("", document.title, window.location.pathname);
- }
- }
-
- function cleanupResources() {
- if (activeWs && activeWs.readyState === WebSocket.OPEN) {
- activeWs.close();
- activeWs = null;
- }
-
- if (hlsInstance) {
- hlsInstance.destroy();
- hlsInstance = null;
- }
-
- if (player) {
- player.destroy();
- player = null;
- }
-
- prepareInProgress = false;
- }
-
- 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;
- }
-
- async function findVideoById(videoId) {
- if (currentVideoData && currentVideoData.url.includes(videoId)) {
- return currentVideoData;
- }
-
- try {
- const trendingResponse = await fetch(trendingApiUrl);
- if (trendingResponse.ok) {
- const trendingVideos = await trendingResponse.json();
- const foundInTrending = trendingVideos.find((v) => v.url.includes(videoId));
- if (foundInTrending) {
- return foundInTrending;
- }
- }
-
- if (lastSearchQuery) {
- 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;
- }
- }
- }
-
- return null;
- } catch (error) {
- console.error("Error searching for video:", error);
- return null;
- }
- }
-
- async function fetchVideoDetails(videoId) {
- try {
- if (videoId === activeVideoId && currentVideoData) {
- return;
- }
-
- activeVideoId = videoId;
- trendingLoader.style.display = "flex";
-
- const video = await findVideoById(videoId);
-
- if (video) {
- currentVideoData = video;
- openVideoPlayer(video);
- } else {
- console.error("Video not found in any source");
- trendingLoader.style.display = "none";
- alert("Video not found. Showing trending videos instead.");
- goToTrendingPage();
- }
- } catch (error) {
- console.error("Error fetching video details:", error);
- trendingLoader.style.display = "none";
- }
- }
-
- function goToTrendingPage() {
- cleanupResources();
-
- activeVideoId = null;
- currentVideoData = null;
-
- activeChannelId = null;
-
- updateHash();
-
- if (videoPlayerPage.classList.contains("active")) {
- switchPage("video-player-page", "trending-page");
- } else if (searchResultsPage.classList.contains("active")) {
- switchPage("search-results-page", "trending-page");
- } else if (channelPage.classList.contains("active")) {
- switchPage("channel-page", "trending-page");
- }
-
- fetchTrendingVideos();
- }
-
- function checkInitialHash() {
- const params = getHashParams();
- if (params.videoId) {
- fetchVideoDetails(params.videoId);
- } else if (params.search) {
- performSearch(params.search);
- } else if (params.channelId) {
- fetchChannelData(params.channelId);
- }
- }
-
- window.addEventListener("hashchange", () => {
- const params = getHashParams();
- if (params.videoId) {
- fetchVideoDetails(params.videoId);
- } else if (params.search) {
- performSearch(params.search);
- } else if (params.channelId) {
- fetchChannelData(params.channelId);
- } else {
- goToTrendingPage();
- }
- });
-
- 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 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 showSkeletons(container, count = 12) {
- container.innerHTML = "";
- for (let i = 0; i < count; i++) {
- const skeleton = skeletonTemplate.content.cloneNode(true);
- skeleton.firstElementChild.style.animationDelay = `${i * 0.05}s`;
- container.appendChild(skeleton);
- }
- container.style.display = "grid";
-
- if (container === trendingGrid) {
- trendingLoader.style.display = "none";
- } else if (container === searchGrid) {
- searchLoader.style.display = "none";
- }
- }
-
- 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 formatUploadTime(timestamp) {
- const now = new Date();
- const uploadDate = new Date(timestamp);
- const diffMs = now - uploadDate;
- const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
-
- if (diffDays < 1) {
- const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
- if (diffHours < 1) {
- const diffMinutes = Math.floor(diffMs / (1000 * 60));
- return `${diffMinutes} minutes ago`;
- }
- return `${diffHours} hours ago`;
- } else if (diffDays < 7) {
- return `${diffDays} days ago`;
- } else if (diffDays < 30) {
- const diffWeeks = Math.floor(diffDays / 7);
- return `${diffWeeks} weeks ago`;
- } else if (diffDays < 365) {
- const diffMonths = Math.floor(diffDays / 30);
- return `${diffMonths} months ago`;
- } else {
- const diffYears = Math.floor(diffDays / 365);
- return `${diffYears} years ago`;
- }
- }
-
- function formatSubscribers(count) {
- if (count >= 1000000) {
- return `${(count / 1000000).toFixed(1)}M subscribers`;
- } else if (count >= 1000) {
- return `${(count / 1000).toFixed(1)}K subscribers`;
- } else {
- return `${count} subscribers`;
- }
- }
-
- function createVideoCard(video, index) {
- if (video.type !== "stream" && video.type !== undefined) {
- console.log(
- `Skipping non-video item: ${video.type} - ${video.title || video.name}`
- );
- 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");
- const uploader = card.querySelector(".uploader");
-
- 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) => {
- if (
- e.target === uploaderAvatar ||
- e.target === uploaderName ||
- (e.target.parentElement && e.target.parentElement === uploader)
- ) {
- e.stopPropagation();
- if (video.uploaderUrl) {
- openChannelPage(video.uploaderUrl.replace("/channel/", ""));
- }
- } else {
- openVideoPlayer(video);
- }
- });
-
- uploader.addEventListener("click", (e) => {
- e.stopPropagation();
- if (video.uploaderUrl) {
- openChannelPage(video.uploaderUrl.replace("/channel/", ""));
- }
- });
- } catch (error) {
- console.error("Error creating video card:", error, video);
- }
-
- return card;
- }
-
- async function fetchTrendingVideos() {
- try {
- trendingLoader.style.display = "flex";
- trendingGrid.style.display = "none";
-
- const response = await fetch(trendingApiUrl);
- if (!response.ok) {
- throw new Error("Network response was not ok");
- }
-
- const videos = await response.json();
-
- showSkeletons(trendingGrid);
-
- setTimeout(() => {
- trendingGrid.innerHTML = "";
-
- videos.forEach((video, index) => {
- const card = createVideoCard(video, index);
- if (card) {
- trendingGrid.appendChild(card);
- }
- });
-
- observeElements();
- }, 600);
- } catch (error) {
- console.error("Error fetching trending videos:", error);
-
- trendingLoader.style.display = "none";
- trendingGrid.style.display = "block";
- trendingGrid.innerHTML = `
-
-
-
Error loading videos. Please try again later.
-
${error.message}
-
- `;
- }
- }
-
- async function performSearch(query) {
- if (!query || query.trim() === "") return;
-
- lastSearchQuery = query.trim();
-
- updateHash({ search: lastSearchQuery });
-
- if (trendingPage.classList.contains("active")) {
- switchPage("trending-page", "search-results-page");
- } else if (videoPlayerPage.classList.contains("active")) {
- switchPage("video-player-page", "search-results-page");
- } else if (channelPage.classList.contains("active")) {
- switchPage("channel-page", "search-results-page");
- }
-
- searchQueryText.textContent = `"${lastSearchQuery}"`;
- searchCountText.textContent = "Searching...";
-
- searchGrid.style.display = "none";
- searchLoader.style.display = "flex";
-
- try {
- const url = `${searchApiUrl}?q=${encodeURIComponent(
- lastSearchQuery
- )}&filter=all`;
- console.log(`Fetching search results from: ${url}`);
-
- const response = await fetch(url);
-
- if (!response.ok) {
- throw new Error(`Network response was not ok: ${response.status}`);
- }
-
- const searchData = await response.json();
-
- const results = searchData.items
- ? searchData.items.filter((item) => item.type === "stream")
- : [];
-
- console.log(
- `Found ${results.length} video results out of ${searchData.items ? searchData.items.length : 0
- } total items`
- );
-
- searchCountText.textContent = `${results.length} videos`;
-
- if (results.length === 0) {
- searchLoader.style.display = "none";
- searchGrid.style.display = "block";
- searchGrid.innerHTML = `
-
-
-
No video results found for "${lastSearchQuery}"
-
- `;
- } else {
- showSkeletons(searchGrid, Math.min(12, results.length));
-
- setTimeout(() => {
- searchGrid.innerHTML = "";
-
- results.forEach((video, index) => {
- const card = createVideoCard(video, index);
- searchGrid.appendChild(card);
- });
-
- observeElements();
- }, 300);
- }
- } catch (error) {
- console.error("Error fetching search results:", error);
- searchLoader.style.display = "none";
- searchGrid.style.display = "block";
- searchGrid.innerHTML = `
-
-
-
Error searching videos. Please try again later.
-
${error.message}
-
- `;
- }
- }
-
- 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(videoId) {
- if (prepareInProgress) {
- return;
- }
-
- prepareInProgress = true;
-
- preparationOverlay.style.display = "flex";
- preparationStatus.textContent = "Preparing video...";
- preparationProgressBar.style.width = "0%";
-
- const cleanVideoId = videoId.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. Reloading the page fixes this.";
- preparationProgressBar.style.width = "100%";
- }
- };
-
- activeWs.onerror = (error) => {
- console.error("WebSocket error:", error);
- preparationStatus.textContent =
- "Error preparing video. Please try again.";
- prepareInProgress = false;
-
- setTimeout(() => {
- preparationOverlay.style.display = "none";
- }, 3000);
- };
-
- activeWs.onclose = () => {
- console.log("WebSocket connection closed");
- if (activeWs === activeWs) {
- activeWs = null;
- }
- };
- }
-
- function openVideoPlayer(video) {
- cleanupResources();
-
- currentVideoData = video;
-
- 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) {
- openChannelPage(video.uploaderUrl.replace("/channel/", ""));
- }
- });
- }
-
- if (trendingPage.classList.contains("active")) {
- switchPage("trending-page", "video-player-page");
- } else if (searchResultsPage.classList.contains("active")) {
- switchPage("search-results-page", "video-player-page");
- } else if (channelPage.classList.contains("active")) {
- switchPage("channel-page", "video-player-page");
- }
-
- window.scrollTo(0, 0);
-
- const videoId = video.url.replace("/watch?v=", "");
- updateHash({ videoId });
-
- activeVideoId = videoId;
-
- prepareVideo(video.url);
-
- fetchRelatedVideos(video);
- }
-
- async function fetchRelatedVideos(currentVideo) {
- try {
- const response = await fetch(trendingApiUrl);
- if (!response.ok) {
- throw new Error("Network response was not ok");
- }
-
- let 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 = `Error loading related videos.
`;
- }
- }
-
- async function fetchChannelData(channelId) {
- try {
- if (channelId === activeChannelId) {
- return;
- }
-
- activeChannelId = channelId;
-
- channelNextPageData = null;
-
- updateHash({ channelId });
-
- if (trendingPage.classList.contains("active")) {
- switchPage("trending-page", "channel-page");
- } else if (searchResultsPage.classList.contains("active")) {
- switchPage("search-results-page", "channel-page");
- } else if (videoPlayerPage.classList.contains("active")) {
- switchPage("video-player-page", "channel-page");
- }
-
- channelLoader.style.display = "flex";
- channelVideosGrid.style.display = "none";
- document.getElementById("channel-load-more").style.display = "none";
-
- channelTitle.textContent = "";
- channelSubs.textContent = "";
- channelDescription.textContent = "";
- channelAvatar.src = "";
- channelBanner.src = "";
- channelVerified.style.display = "none";
-
- const url = `https://pipedapi.wireway.ch/channel/${channelId}`;
- console.log(`Fetching channel data from: ${url}`);
-
- const response = await fetch(url);
-
- if (!response.ok) {
- throw new Error(`Network response was not ok: ${response.status}`);
- }
-
- const channelData = await response.json();
- console.log("Channel data:", channelData);
-
- if (!channelData || typeof channelData !== "object") {
- throw new Error("Invalid channel data received");
- }
-
- channelNextPageData = channelData.nextpage || null;
-
- channelTitle.textContent = channelData.name || "Unknown Channel";
- channelSubs.textContent = formatSubscribers(
- channelData.subscriberCount || 0
- );
-
- if (channelData.description) {
- channelDescription.textContent = channelData.description.replace(
- /\\n/g,
- "\n"
- );
- } else {
- channelDescription.textContent = "No description available";
- }
-
- if (channelData.avatarUrl) {
- channelAvatar.src = channelData.avatarUrl;
- channelAvatar.style.display = "block";
- } else {
- channelAvatar.style.display = "none";
- }
-
- if (channelData.bannerUrl) {
- channelBanner.src = channelData.bannerUrl;
- channelBanner.style.display = "block";
- } else {
- channelBanner.style.display = "none";
- }
-
- channelVerified.style.display = channelData.verified
- ? "inline-block"
- : "none";
-
- const relatedStreams = Array.isArray(channelData.relatedStreams)
- ? channelData.relatedStreams
- : [];
-
- const videoCount = relatedStreams.length;
- showSkeletons(channelVideosGrid, Math.min(12, videoCount || 6));
-
- setTimeout(() => {
- channelVideosGrid.innerHTML = "";
-
- if (relatedStreams.length > 0) {
- relatedStreams.forEach((video, index) => {
- if (!video.uploaderName) {
- video.uploaderName = channelData.name || "Unknown Channel";
- }
- if (!video.uploaderUrl) {
- video.uploaderUrl = `/channel/${channelData.id}`;
- }
- if (!video.uploaderAvatar) {
- video.uploaderAvatar = channelData.avatarUrl || "";
- }
-
- const card = createVideoCard(video, index);
- if (card) {
- channelVideosGrid.appendChild(card);
- }
- });
-
- observeElements();
-
- const loadMoreContainer =
- document.getElementById("channel-load-more");
- if (channelNextPageData) {
- loadMoreContainer.style.display = "flex";
- } else {
- loadMoreContainer.style.display = "none";
- }
- } else {
- channelVideosGrid.innerHTML = `
-
-
-
No videos available
-
- `;
- }
-
- channelLoader.style.display = "none";
- channelVideosGrid.style.display = "grid";
- }, 300);
- } catch (error) {
- console.error("Error fetching channel data:", error);
- channelLoader.style.display = "none";
- channelVideosGrid.style.display = "block";
- channelVideosGrid.innerHTML = `
-
-
-
Error loading channel. Please try again later.
-
${error.message}
-
- `;
- }
- }
-
- async function loadMoreChannelVideos() {
- if (!channelNextPageData || isLoadingMoreVideos) {
- return;
- }
-
- try {
- isLoadingMoreVideos = true;
-
- const loadMoreButton = document.getElementById(
- "channel-load-more-button"
- );
- const loadMoreSpinner = document.getElementById(
- "channel-load-more-spinner"
- );
- loadMoreButton.style.display = "none";
- loadMoreSpinner.style.display = "block";
-
- const url = `https://pipedapi.wireway.ch/nextpage/channel/${activeChannelId}?nextpage=${encodeURIComponent(
- channelNextPageData
- )}`;
- console.log(`Fetching next page data from: ${url}`);
-
- const response = await fetch(url);
-
- if (!response.ok) {
- throw new Error(`Network response was not ok: ${response.status}`);
- }
-
- const nextPageData = await response.json();
- console.log("Next page data:", nextPageData);
-
- if (!nextPageData || !Array.isArray(nextPageData.relatedStreams)) {
- throw new Error("Invalid next page data received");
- }
-
- channelNextPageData = nextPageData.nextpage || null;
-
- const relatedStreams = nextPageData.relatedStreams;
- const startIndex = document.querySelectorAll(
- ".channel-videos-grid .video-card"
- ).length;
-
- if (relatedStreams.length > 0) {
- relatedStreams.forEach((video, index) => {
- if (!video.uploaderName) {
- video.uploaderName = channelTitle.textContent || "Unknown Channel";
- }
- if (!video.uploaderUrl) {
- video.uploaderUrl = `/channel/${activeChannelId}`;
- }
- if (!video.uploaderAvatar) {
- video.uploaderAvatar = channelAvatar.src || "";
- }
-
- const card = createVideoCard(video, startIndex + index);
- if (card) {
- channelVideosGrid.appendChild(card);
- }
- });
-
- observeElements();
- }
-
- if (channelNextPageData) {
- loadMoreButton.style.display = "block";
- } else {
- document.getElementById("channel-load-more").style.display = "none";
- }
- } catch (error) {
- console.error("Error loading more videos:", error);
-
- const loadMoreContainer = document.getElementById("channel-load-more");
- loadMoreContainer.innerHTML = `
- Error loading more videos. Tap to try again.
- `;
-
- setTimeout(() => {
- loadMoreContainer.innerHTML = `
-
-
- `;
-
- document
- .getElementById("channel-load-more-button")
- .addEventListener("click", loadMoreChannelVideos);
- }, 3000);
- } finally {
- document.getElementById("channel-load-more-spinner").style.display =
- "none";
- isLoadingMoreVideos = false;
- }
- }
-
- function setupInfiniteScroll() {
- window.addEventListener("scroll", function () {
- if (
- !channelPage.classList.contains("active") ||
- !channelNextPageData ||
- isLoadingMoreVideos
- ) {
- return;
- }
-
- const scrollPos = window.scrollY + window.innerHeight;
- const loadMoreContainer = document.getElementById("channel-load-more");
- const loadMorePosition = loadMoreContainer.offsetTop;
-
- if (scrollPos > loadMorePosition - 300) {
- loadMoreChannelVideos();
- }
- });
-
- document
- .getElementById("channel-load-more-button")
- .addEventListener("click", loadMoreChannelVideos);
- }
-
- function openChannelPage(channelId) {
- if (videoPlayerPage.classList.contains("active")) {
- cleanupResources();
- }
-
- fetchChannelData(channelId);
- }
-
- logo.addEventListener("click", () => {
- goToTrendingPage();
- });
-
- searchButton.addEventListener("click", () => {
- performSearch(searchInput.value);
- });
-
- searchInput.addEventListener("keypress", (e) => {
- if (e.key === "Enter") {
- performSearch(searchInput.value);
- }
- });
-
- fetchTrendingVideos();
- checkInitialHash();
- setupInfiniteScroll();
-});
\ No newline at end of file
diff --git a/public/trending.js b/public/trending.js
index e05ce5d..93af7a7 100644
--- a/public/trending.js
+++ b/public/trending.js
@@ -1,644 +1,718 @@
document.addEventListener("DOMContentLoaded", () => {
- const trendingPage = document.getElementById("trending-page");
- const searchResultsPage = document.getElementById("search-results-page");
- const channelPage = document.getElementById("channel-page");
- const trendingGrid = document.getElementById("trending-grid");
- const trendingLoader = document.getElementById("trending-loader");
- const searchGrid = document.getElementById("search-grid");
- const searchLoader = document.getElementById("search-loader");
- const searchQueryText = document.getElementById("search-query");
- const searchCountText = document.getElementById("search-count");
- const searchInput = document.getElementById("search-input");
- const searchButton = document.getElementById("search-button");
- const logo = document.getElementById("logo");
- const channelVideosGrid = document.getElementById("channel-videos-grid");
- const channelLoader = document.getElementById("channel-loader");
+ const trendingPage = document.getElementById("trending-page");
+ const searchResultsPage = document.getElementById("search-results-page");
+ const channelPage = document.getElementById("channel-page");
+ const trendingGrid = document.getElementById("trending-grid");
+ const trendingLoader = document.getElementById("trending-loader");
+ const searchGrid = document.getElementById("search-grid");
+ const searchLoader = document.getElementById("search-loader");
+ const searchQueryText = document.getElementById("search-query");
+ const searchCountText = document.getElementById("search-count");
+ const searchInput = document.getElementById("search-input");
+ const searchButton = document.getElementById("search-button");
+ const logo = document.getElementById("logo");
+ const channelVideosGrid = document.getElementById("channel-videos-grid");
+ const channelLoader = document.getElementById("channel-loader");
- const channelBanner = document.getElementById("channel-banner");
- const channelAvatar = document.getElementById("channel-avatar");
- const channelTitle = document.getElementById("channel-title");
- const channelVerified = document.getElementById("channel-verified");
- const channelSubs = document.getElementById("channel-subs");
- const channelDescription = document.getElementById("channel-description");
+ const channelBanner = document.getElementById("channel-banner");
+ const channelAvatar = document.getElementById("channel-avatar");
+ const channelTitle = document.getElementById("channel-title");
+ const channelVerified = document.getElementById("channel-verified");
+ const channelSubs = document.getElementById("channel-subs");
+ const channelDescription = document.getElementById("channel-description");
- const videoCardTemplate = document.getElementById("video-card-template");
- const skeletonTemplate = document.getElementById("skeleton-template");
+ const videoCardTemplate = document.getElementById("video-card-template");
+ const skeletonTemplate = document.getElementById("skeleton-template");
- let lastSearchQuery = "";
- let activeChannelId = null;
- let channelNextPageData = null;
- let isLoadingMoreVideos = false;
+ let lastSearchQuery = "";
+ let activeChannelId = null;
+ let channelNextPageData = null;
+ let isLoadingMoreVideos = false;
- const trendingApiUrl = "/api/trending";
- const searchApiUrl = "/api/search";
+ const trendingApiUrl = "/api/trending";
+ const searchApiUrl = "/api/search";
- 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 formatSubscribers(count) {
+ if (count >= 1000000) {
+ return `${(count / 1000000).toFixed(1)}M subscribers`;
+ } else if (count >= 1000) {
+ return `${(count / 1000).toFixed(1)}K subscribers`;
+ } else {
+ return `${count} subscribers`;
+ }
+ }
+
+ function getHashParams() {
+ const hash = window.location.hash.substring(1);
+ const params = {};
+
+ hash.split("&").forEach((param) => {
+ if (param) {
+ const [key, value] = param.split("=");
+ params[key] = decodeURIComponent(value);
+ }
+ });
+
+ return params;
+ }
+
+ function updateHash(params = {}) {
+ const hashParts = [];
+
+ Object.entries(params).forEach(([key, value]) => {
+ if (value) {
+ hashParts.push(`${key}=${encodeURIComponent(value)}`);
+ }
+ });
+
+ if (hashParts.length > 0) {
+ history.pushState("", document.title, `#${hashParts.join("&")}`);
+ } else {
+ history.pushState("", document.title, window.location.pathname);
+ }
+ }
+
+ function switchPage(fromPageId, toPageId) {
+ const fromPage = document.getElementById(fromPageId);
+ const toPage = document.getElementById(toPageId);
+
+ if (!fromPage || !toPage) return;
+
+ fromPage.classList.add("zoom-out");
+
+ setTimeout(() => {
+ fromPage.classList.remove("active");
+ fromPage.classList.remove("zoom-out");
+
+ toPage.classList.add("zoom-in");
+ toPage.classList.add("active");
+
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ toPage.classList.remove("zoom-in");
+ });
+ });
+ }, 300);
+ }
+
+ function showSkeletons(container, count = 12) {
+ container.innerHTML = "";
+ for (let i = 0; i < count; i++) {
+ const skeleton = skeletonTemplate.content.cloneNode(true);
+ skeleton.firstElementChild.style.animationDelay = `${i * 0.05}s`;
+ container.appendChild(skeleton);
+ }
+ container.style.display = "grid";
+
+ if (container === trendingGrid) {
+ trendingLoader.style.display = "none";
+ } else if (container === searchGrid) {
+ searchLoader.style.display = "none";
+ }
+ }
+
+ 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));
+ sessionStorage.setItem("lastSearchQuery", lastSearchQuery);
+
+ 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) {
+ console.log(
+ `Skipping non-video item: ${video.type} - ${video.title || video.name}`
+ );
+ return null;
}
- function formatDuration(seconds) {
- const hours = Math.floor(seconds / 3600);
- const minutes = Math.floor((seconds % 3600) / 60);
- const secs = seconds % 60;
+ 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");
+ const uploader = card.querySelector(".uploader");
- if (hours > 0) {
- return `${hours}:${minutes.toString().padStart(2, "0")}:${secs
- .toString()
- .padStart(2, "0")}`;
+ 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) => {
+ if (
+ e.target === uploaderAvatar ||
+ e.target === uploaderName ||
+ (e.target.parentElement && e.target.parentElement === uploader)
+ ) {
+ e.stopPropagation();
+ if (video.uploaderUrl) {
+ const username = video.uploaderUrl;
+ openChannelPage(username);
+ }
} else {
- return `${minutes}:${secs.toString().padStart(2, "0")}`;
+ openVideoPlayer(video);
}
+ });
+
+ uploader.addEventListener("click", async (e) => {
+ e.stopPropagation();
+ if (video.uploaderUrl) {
+ const username = video.uploaderUrl;
+ openChannelPage(username);
+ }
+ });
+ } catch (error) {
+ console.error("Error creating video card:", error, video);
}
- function formatSubscribers(count) {
- if (count >= 1000000) {
- return `${(count / 1000000).toFixed(1)}M subscribers`;
- } else if (count >= 1000) {
- return `${(count / 1000).toFixed(1)}K subscribers`;
- } else {
- return `${count} subscribers`;
- }
- }
+ return card;
+ }
- function getHashParams() {
- const hash = window.location.hash.substring(1);
- const params = {};
+ async function fetchTrendingVideos() {
+ try {
+ trendingLoader.style.display = "flex";
+ trendingGrid.style.display = "none";
- hash.split("&").forEach((param) => {
- if (param) {
- const [key, value] = param.split("=");
- params[key] = decodeURIComponent(value);
- }
+ const response = await fetch(trendingApiUrl);
+ if (!response.ok) {
+ throw new Error("Network response was not ok");
+ }
+
+ const videos = await response.json();
+
+ sessionStorage.setItem("trendingVideos", JSON.stringify(videos));
+
+ showSkeletons(trendingGrid);
+
+ setTimeout(() => {
+ trendingGrid.innerHTML = "";
+
+ videos.forEach((video, index) => {
+ const card = createVideoCard(video, index);
+ if (card) {
+ trendingGrid.appendChild(card);
+ }
});
- return params;
- }
+ observeElements();
+ }, 600);
+ } catch (error) {
+ console.error("Error fetching trending videos:", error);
- function updateHash(params = {}) {
- const hashParts = [];
-
- Object.entries(params).forEach(([key, value]) => {
- if (value) {
- hashParts.push(`${key}=${encodeURIComponent(value)}`);
- }
- });
-
- if (hashParts.length > 0) {
- history.pushState("", document.title, `#${hashParts.join("&")}`);
- } else {
- history.pushState("", document.title, window.location.pathname);
- }
- }
-
- function switchPage(fromPageId, toPageId) {
- const fromPage = document.getElementById(fromPageId);
- const toPage = document.getElementById(toPageId);
-
- if (!fromPage || !toPage) return;
-
- fromPage.classList.add("zoom-out");
-
- setTimeout(() => {
- fromPage.classList.remove("active");
- fromPage.classList.remove("zoom-out");
-
- toPage.classList.add("zoom-in");
- toPage.classList.add("active");
-
- requestAnimationFrame(() => {
- requestAnimationFrame(() => {
- toPage.classList.remove("zoom-in");
- });
- });
- }, 300);
- }
-
- function showSkeletons(container, count = 12) {
- container.innerHTML = "";
- for (let i = 0; i < count; i++) {
- const skeleton = skeletonTemplate.content.cloneNode(true);
- skeleton.firstElementChild.style.animationDelay = `${i * 0.05}s`;
- container.appendChild(skeleton);
- }
- container.style.display = "grid";
-
- if (container === trendingGrid) {
- trendingLoader.style.display = "none";
- } else if (container === searchGrid) {
- searchLoader.style.display = "none";
- }
- }
-
- 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));
- sessionStorage.setItem('lastSearchQuery', lastSearchQuery);
-
- 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) {
- console.log(
- `Skipping non-video item: ${video.type} - ${video.title || video.name}`
- );
- 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");
- const uploader = card.querySelector(".uploader");
-
- 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) => {
- if (
- e.target === uploaderAvatar ||
- e.target === uploaderName ||
- (e.target.parentElement && e.target.parentElement === uploader)
- ) {
- e.stopPropagation();
- if (video.uploaderUrl) {
- openChannelPage(video.uploaderUrl.replace("/channel/", ""));
- }
- } else {
- openVideoPlayer(video);
- }
- });
-
- uploader.addEventListener("click", (e) => {
- e.stopPropagation();
- if (video.uploaderUrl) {
- openChannelPage(video.uploaderUrl.replace("/channel/", ""));
- }
- });
- } catch (error) {
- console.error("Error creating video card:", error, video);
- }
-
- return card;
- }
-
- async function fetchTrendingVideos() {
- try {
- trendingLoader.style.display = "flex";
- trendingGrid.style.display = "none";
-
- const response = await fetch(trendingApiUrl);
- if (!response.ok) {
- throw new Error("Network response was not ok");
- }
-
- const videos = await response.json();
-
- sessionStorage.setItem('trendingVideos', JSON.stringify(videos));
-
- showSkeletons(trendingGrid);
-
- setTimeout(() => {
- trendingGrid.innerHTML = "";
-
- videos.forEach((video, index) => {
- const card = createVideoCard(video, index);
- if (card) {
- trendingGrid.appendChild(card);
- }
- });
-
- observeElements();
- }, 600);
- } catch (error) {
- console.error("Error fetching trending videos:", error);
-
- trendingLoader.style.display = "none";
- trendingGrid.style.display = "block";
- trendingGrid.innerHTML = `
+ trendingLoader.style.display = "none";
+ trendingGrid.style.display = "block";
+ trendingGrid.innerHTML = `
Failed to load videos
Please try again later.
`;
- }
+ }
+ }
+
+ async function performSearch(query) {
+ if (!query || query.trim() === "") return;
+
+ lastSearchQuery = query.trim();
+ updateHash({ search: lastSearchQuery });
+
+ if (trendingPage.classList.contains("active")) {
+ switchPage("trending-page", "search-results-page");
+ } else if (channelPage.classList.contains("active")) {
+ switchPage("channel-page", "search-results-page");
}
- async function performSearch(query) {
- if (!query || query.trim() === "") return;
+ searchQueryText.textContent = `"${lastSearchQuery}"`;
+ searchCountText.textContent = "Searching...";
- lastSearchQuery = query.trim();
- updateHash({ search: lastSearchQuery });
+ searchGrid.style.display = "none";
+ searchLoader.style.display = "flex";
- if (trendingPage.classList.contains("active")) {
- switchPage("trending-page", "search-results-page");
- } else if (channelPage.classList.contains("active")) {
- switchPage("channel-page", "search-results-page");
- }
+ try {
+ const url = `${searchApiUrl}?q=${encodeURIComponent(
+ lastSearchQuery
+ )}&filter=all`;
+ console.log(`Fetching search results from: ${url}`);
- searchQueryText.textContent = `"${lastSearchQuery}"`;
- searchCountText.textContent = "Searching...";
+ const response = await fetch(url);
- searchGrid.style.display = "none";
- searchLoader.style.display = "flex";
+ if (!response.ok) {
+ throw new Error(`Network response was not ok: ${response.status}`);
+ }
- try {
- const url = `${searchApiUrl}?q=${encodeURIComponent(lastSearchQuery)}&filter=all`;
- console.log(`Fetching search results from: ${url}`);
+ const searchData = await response.json();
- const response = await fetch(url);
+ const results = searchData.items
+ ? searchData.items.filter((item) => item.type === "stream")
+ : [];
- if (!response.ok) {
- throw new Error(`Network response was not ok: ${response.status}`);
- }
+ sessionStorage.setItem("searchResults", JSON.stringify(results));
- const searchData = await response.json();
+ console.log(
+ `Found ${results.length} video results out of ${
+ searchData.items ? searchData.items.length : 0
+ } total items`
+ );
- const results = searchData.items
- ? searchData.items.filter((item) => item.type === "stream")
- : [];
+ searchCountText.textContent = `${results.length} videos`;
- sessionStorage.setItem('searchResults', JSON.stringify(results));
-
- console.log(`Found ${results.length} video results out of ${searchData.items ? searchData.items.length : 0} total items`);
-
- searchCountText.textContent = `${results.length} videos`;
-
- if (results.length === 0) {
- searchLoader.style.display = "none";
- searchGrid.style.display = "block";
- searchGrid.innerHTML = `
+ if (results.length === 0) {
+ searchLoader.style.display = "none";
+ searchGrid.style.display = "block";
+ searchGrid.innerHTML = `
No videos found
No video results found for "${lastSearchQuery}"
`;
- } else {
- showSkeletons(searchGrid, Math.min(12, results.length));
+ } else {
+ showSkeletons(searchGrid, Math.min(12, results.length));
- setTimeout(() => {
- searchGrid.innerHTML = "";
+ setTimeout(() => {
+ searchGrid.innerHTML = "";
- results.forEach((video, index) => {
- const card = createVideoCard(video, index);
- searchGrid.appendChild(card);
- });
+ results.forEach((video, index) => {
+ const card = createVideoCard(video, index);
+ searchGrid.appendChild(card);
+ });
- observeElements();
- }, 300);
- }
- } catch (error) {
- console.error("Error fetching search results:", error);
- searchLoader.style.display = "none";
- searchGrid.style.display = "block";
- searchGrid.innerHTML = `
+ observeElements();
+ }, 300);
+ }
+ } catch (error) {
+ console.error("Error fetching search results:", error);
+ searchLoader.style.display = "none";
+ searchGrid.style.display = "block";
+ searchGrid.innerHTML = `
Search failed
Error searching videos. Please try again later.
`;
- }
+ }
+ }
+
+ function goToTrendingPage() {
+ updateHash();
+
+ if (searchResultsPage.classList.contains("active")) {
+ switchPage("search-results-page", "trending-page");
+ } else if (channelPage.classList.contains("active")) {
+ switchPage("channel-page", "trending-page");
}
- function goToTrendingPage() {
- updateHash();
+ fetchTrendingVideos();
+ }
+ async function fetchChannelData(channelId) {
+ try {
+ const resolvedChannelId = await resolveChannelId(channelId);
- if (searchResultsPage.classList.contains("active")) {
- switchPage("search-results-page", "trending-page");
- } else if (channelPage.classList.contains("active")) {
- switchPage("channel-page", "trending-page");
+ if (resolvedChannelId === activeChannelId) {
+ return;
+ }
+
+ activeChannelId = resolvedChannelId;
+ channelNextPageData = null;
+
+ updateHash({ channelId });
+
+ if (trendingPage.classList.contains("active")) {
+ switchPage("trending-page", "channel-page");
+ } else if (searchResultsPage.classList.contains("active")) {
+ switchPage("search-results-page", "channel-page");
+ }
+
+ channelLoader.style.display = "flex";
+ channelVideosGrid.style.display = "none";
+ document.getElementById("channel-load-more").style.display = "none";
+
+ channelTitle.textContent = "";
+ channelSubs.textContent = "";
+ channelDescription.textContent = "";
+ channelAvatar.src = "";
+ channelBanner.src = "";
+ channelVerified.style.display = "none";
+ let finalChannelId = channelId;
+
+ if (!channelId.startsWith("UC")) {
+ const url = `/api/channel/${channelId}`;
+ console.log(`Resolving channel ID from: ${url}`);
+
+ const idResponse = await fetch(url);
+ if (!idResponse.ok) {
+ throw new Error(`Network response was not ok: ${idResponse.status}`);
}
+ const idData = await idResponse.json();
+ finalChannelId = idData.channelId;
+ }
- fetchTrendingVideos();
- }
+ const pipedUrl = `https://pipedapi.wireway.ch/channel/${finalChannelId}`;
+ console.log(`Fetching channel data from: ${pipedUrl}`);
+ const response = await fetch(pipedUrl);
- async function fetchChannelData(channelId) {
- try {
- if (channelId === activeChannelId) {
- return;
+ if (!response.ok) {
+ throw new Error(`Network response was not ok: ${response.status}`);
+ }
+
+ const channelData = await response.json();
+ console.log("Channel data:", channelData);
+
+ if (!channelData || typeof channelData !== "object") {
+ throw new Error("Invalid channel data received");
+ }
+
+ channelNextPageData = channelData.nextpage || null;
+
+ channelTitle.textContent = channelData.name || "Unknown Channel";
+ channelSubs.textContent = formatSubscribers(
+ channelData.subscriberCount || 0
+ );
+
+ if (channelData.description) {
+ channelDescription.textContent = channelData.description.replace(
+ /\\n/g,
+ "\n"
+ );
+ } else {
+ channelDescription.textContent = "No description available";
+ }
+
+ if (channelData.avatarUrl) {
+ channelAvatar.src = channelData.avatarUrl;
+ channelAvatar.style.display = "block";
+ } else {
+ channelAvatar.style.display = "none";
+ }
+
+ if (channelData.bannerUrl) {
+ channelBanner.src = channelData.bannerUrl;
+ channelBanner.style.display = "block";
+ } else {
+ channelBanner.style.display = "none";
+ }
+
+ channelVerified.style.display = channelData.verified
+ ? "inline-block"
+ : "none";
+
+ const relatedStreams = Array.isArray(channelData.relatedStreams)
+ ? channelData.relatedStreams
+ : [];
+
+ const videoCount = relatedStreams.length;
+ showSkeletons(channelVideosGrid, Math.min(12, videoCount || 6));
+
+ setTimeout(() => {
+ channelVideosGrid.innerHTML = "";
+
+ if (relatedStreams.length > 0) {
+ relatedStreams.forEach((video, index) => {
+ if (!video.uploaderName) {
+ video.uploaderName = channelData.name || "Unknown Channel";
+ }
+ if (!video.uploaderUrl) {
+ video.uploaderUrl = `/channel/${channelData.id}`;
+ }
+ if (!video.uploaderAvatar) {
+ video.uploaderAvatar = channelData.avatarUrl || "";
}
- activeChannelId = channelId;
- channelNextPageData = null;
-
- updateHash({ channelId });
-
- if (trendingPage.classList.contains("active")) {
- switchPage("trending-page", "channel-page");
- } else if (searchResultsPage.classList.contains("active")) {
- switchPage("search-results-page", "channel-page");
+ const card = createVideoCard(video, index);
+ if (card) {
+ channelVideosGrid.appendChild(card);
}
+ });
- channelLoader.style.display = "flex";
- channelVideosGrid.style.display = "none";
- document.getElementById("channel-load-more").style.display = "none";
+ observeElements();
- channelTitle.textContent = "";
- channelSubs.textContent = "";
- channelDescription.textContent = "";
- channelAvatar.src = "";
- channelBanner.src = "";
- channelVerified.style.display = "none";
-
- const url = `https://pipedapi.wireway.ch/channel/${channelId}`;
- console.log(`Fetching channel data from: ${url}`);
-
- const response = await fetch(url);
-
- if (!response.ok) {
- throw new Error(`Network response was not ok: ${response.status}`);
- }
-
- const channelData = await response.json();
- console.log("Channel data:", channelData);
-
- if (!channelData || typeof channelData !== "object") {
- throw new Error("Invalid channel data received");
- }
-
- channelNextPageData = channelData.nextpage || null;
-
- channelTitle.textContent = channelData.name || "Unknown Channel";
- channelSubs.textContent = formatSubscribers(channelData.subscriberCount || 0);
-
- if (channelData.description) {
- channelDescription.textContent = channelData.description.replace(/\\n/g, "\n");
- } else {
- channelDescription.textContent = "No description available";
- }
-
- if (channelData.avatarUrl) {
- channelAvatar.src = channelData.avatarUrl;
- channelAvatar.style.display = "block";
- } else {
- channelAvatar.style.display = "none";
- }
-
- if (channelData.bannerUrl) {
- channelBanner.src = channelData.bannerUrl;
- channelBanner.style.display = "block";
- } else {
- channelBanner.style.display = "none";
- }
-
- channelVerified.style.display = channelData.verified ? "inline-block" : "none";
-
- const relatedStreams = Array.isArray(channelData.relatedStreams) ? channelData.relatedStreams : [];
-
- const videoCount = relatedStreams.length;
- showSkeletons(channelVideosGrid, Math.min(12, videoCount || 6));
-
- setTimeout(() => {
- channelVideosGrid.innerHTML = "";
-
- if (relatedStreams.length > 0) {
- relatedStreams.forEach((video, index) => {
- if (!video.uploaderName) {
- video.uploaderName = channelData.name || "Unknown Channel";
- }
- if (!video.uploaderUrl) {
- video.uploaderUrl = `/channel/${channelData.id}`;
- }
- if (!video.uploaderAvatar) {
- video.uploaderAvatar = channelData.avatarUrl || "";
- }
-
- const card = createVideoCard(video, index);
- if (card) {
- channelVideosGrid.appendChild(card);
- }
- });
-
- observeElements();
-
- const loadMoreContainer = document.getElementById("channel-load-more");
- if (channelNextPageData) {
- loadMoreContainer.style.display = "flex";
- } else {
- loadMoreContainer.style.display = "none";
- }
- } else {
- channelVideosGrid.innerHTML = `
+ const loadMoreContainer =
+ document.getElementById("channel-load-more");
+ if (channelNextPageData) {
+ loadMoreContainer.style.display = "flex";
+ } else {
+ loadMoreContainer.style.display = "none";
+ }
+ } else {
+ channelVideosGrid.innerHTML = `
No videos available
This channel hasn't uploaded any videos yet.
`;
- }
+ }
- channelLoader.style.display = "none";
- channelVideosGrid.style.display = "grid";
- }, 300);
- } catch (error) {
- console.error("Error fetching channel data:", error);
- channelLoader.style.display = "none";
- channelVideosGrid.style.display = "block";
- channelVideosGrid.innerHTML = `
+ channelLoader.style.display = "none";
+ channelVideosGrid.style.display = "grid";
+ }, 300);
+ } catch (error) {
+ console.error("Error fetching channel data:", error);
+ channelLoader.style.display = "none";
+ channelVideosGrid.style.display = "block";
+ channelVideosGrid.innerHTML = `
Failed to load channel
Error loading channel. Please try again later.
`;
- }
+ }
+ }
+
+ async function loadMoreChannelVideos() {
+ if (!channelNextPageData || isLoadingMoreVideos) {
+ return;
}
- async function loadMoreChannelVideos() {
- if (!channelNextPageData || isLoadingMoreVideos) {
- return;
- }
+ try {
+ isLoadingMoreVideos = true;
- try {
- isLoadingMoreVideos = true;
+ const loadMoreButton = document.getElementById(
+ "channel-load-more-button"
+ );
+ const loadMoreSpinner = document.getElementById(
+ "channel-load-more-spinner"
+ );
+ loadMoreButton.style.display = "none";
+ loadMoreSpinner.style.display = "block";
- const loadMoreButton = document.getElementById("channel-load-more-button");
- const loadMoreSpinner = document.getElementById("channel-load-more-spinner");
- loadMoreButton.style.display = "none";
- loadMoreSpinner.style.display = "block";
+ const url = `https://pipedapi.wireway.ch/nextpage/channel/${activeChannelId}?nextpage=${encodeURIComponent(
+ channelNextPageData
+ )}`;
+ console.log(`Fetching next page data from: ${url}`);
- const url = `https://pipedapi.wireway.ch/nextpage/channel/${activeChannelId}?nextpage=${encodeURIComponent(channelNextPageData)}`;
- console.log(`Fetching next page data from: ${url}`);
+ const response = await fetch(url);
- const response = await fetch(url);
+ if (!response.ok) {
+ throw new Error(`Network response was not ok: ${response.status}`);
+ }
- if (!response.ok) {
- throw new Error(`Network response was not ok: ${response.status}`);
- }
+ const nextPageData = await response.json();
+ console.log("Next page data:", nextPageData);
- const nextPageData = await response.json();
- console.log("Next page data:", nextPageData);
+ if (!nextPageData || !Array.isArray(nextPageData.relatedStreams)) {
+ throw new Error("Invalid next page data received");
+ }
- if (!nextPageData || !Array.isArray(nextPageData.relatedStreams)) {
- throw new Error("Invalid next page data received");
- }
+ channelNextPageData = nextPageData.nextpage || null;
- channelNextPageData = nextPageData.nextpage || null;
+ const relatedStreams = nextPageData.relatedStreams;
+ const startIndex = document.querySelectorAll(
+ ".channel-videos-grid .video-card"
+ ).length;
- const relatedStreams = nextPageData.relatedStreams;
- const startIndex = document.querySelectorAll(".channel-videos-grid .video-card").length;
+ if (relatedStreams.length > 0) {
+ relatedStreams.forEach((video, index) => {
+ if (!video.uploaderName) {
+ video.uploaderName = channelTitle.textContent || "Unknown Channel";
+ }
+ if (!video.uploaderUrl) {
+ video.uploaderUrl = `/channel/${activeChannelId}`;
+ }
+ if (!video.uploaderAvatar) {
+ video.uploaderAvatar = channelAvatar.src || "";
+ }
- if (relatedStreams.length > 0) {
- relatedStreams.forEach((video, index) => {
- if (!video.uploaderName) {
- video.uploaderName = channelTitle.textContent || "Unknown Channel";
- }
- if (!video.uploaderUrl) {
- video.uploaderUrl = `/channel/${activeChannelId}`;
- }
- if (!video.uploaderAvatar) {
- video.uploaderAvatar = channelAvatar.src || "";
- }
+ const card = createVideoCard(video, startIndex + index);
+ if (card) {
+ channelVideosGrid.appendChild(card);
+ }
+ });
- const card = createVideoCard(video, startIndex + index);
- if (card) {
- channelVideosGrid.appendChild(card);
- }
- });
+ observeElements();
+ }
- observeElements();
- }
+ if (channelNextPageData) {
+ loadMoreButton.style.display = "block";
+ } else {
+ document.getElementById("channel-load-more").style.display = "none";
+ }
+ } catch (error) {
+ console.error("Error loading more videos:", error);
- if (channelNextPageData) {
- loadMoreButton.style.display = "block";
- } else {
- document.getElementById("channel-load-more").style.display = "none";
- }
- } catch (error) {
- console.error("Error loading more videos:", error);
-
- const loadMoreContainer = document.getElementById("channel-load-more");
- loadMoreContainer.innerHTML = `
+ const loadMoreContainer = document.getElementById("channel-load-more");
+ loadMoreContainer.innerHTML = `
Error loading more videos. Click to try again.
`;
- loadMoreContainer.addEventListener('click', () => {
- loadMoreContainer.innerHTML = `
+ loadMoreContainer.addEventListener("click", () => {
+ loadMoreContainer.innerHTML = `
`;
- document.getElementById("channel-load-more-button").addEventListener("click", loadMoreChannelVideos);
- });
- } finally {
- document.getElementById("channel-load-more-spinner").style.display = "none";
- isLoadingMoreVideos = false;
- }
+ document
+ .getElementById("channel-load-more-button")
+ .addEventListener("click", loadMoreChannelVideos);
+ });
+ } finally {
+ document.getElementById("channel-load-more-spinner").style.display =
+ "none";
+ isLoadingMoreVideos = false;
}
+ }
- function openChannelPage(channelId) {
- fetchChannelData(channelId);
+ async function openChannelPage(channelIdOrUsername) {
+ try {
+ // Remove any existing hash params
+ const channelId = await resolveChannelId(channelIdOrUsername);
+ updateHash({ channelId });
+ fetchChannelData(channelId);
+ } catch (error) {
+ console.error("Error opening channel page:", error);
}
+ }
- function checkInitialHash() {
- const params = getHashParams();
- if (params.search) {
- searchInput.value = params.search;
- performSearch(params.search);
- } else if (params.channelId) {
- fetchChannelData(params.channelId);
- }
+ function checkInitialHash() {
+ const params = getHashParams();
+ if (params.search) {
+ searchInput.value = params.search;
+ performSearch(params.search);
+ } else if (params.channelId) {
+ fetchChannelData(params.channelId);
}
+ }
- function setupInfiniteScroll() {
- window.addEventListener("scroll", function () {
- if (!channelPage.classList.contains("active") || !channelNextPageData || isLoadingMoreVideos) {
- return;
- }
+ function setupInfiniteScroll() {
+ window.addEventListener("scroll", function () {
+ if (
+ !channelPage.classList.contains("active") ||
+ !channelNextPageData ||
+ isLoadingMoreVideos
+ ) {
+ return;
+ }
- const scrollPos = window.scrollY + window.innerHeight;
- const loadMoreContainer = document.getElementById("channel-load-more");
- const loadMorePosition = loadMoreContainer.offsetTop;
+ const scrollPos = window.scrollY + window.innerHeight;
+ const loadMoreContainer = document.getElementById("channel-load-more");
+ const loadMorePosition = loadMoreContainer.offsetTop;
- if (scrollPos > loadMorePosition - 300) {
- loadMoreChannelVideos();
- }
- });
-
- document.getElementById("channel-load-more-button").addEventListener("click", loadMoreChannelVideos);
- }
-
- logo.addEventListener("click", () => {
- goToTrendingPage();
+ if (scrollPos > loadMorePosition - 300) {
+ loadMoreChannelVideos();
+ }
});
- searchButton.addEventListener("click", () => {
- performSearch(searchInput.value);
- });
+ document
+ .getElementById("channel-load-more-button")
+ .addEventListener("click", loadMoreChannelVideos);
+ }
- searchInput.addEventListener("keypress", (e) => {
- if (e.key === "Enter") {
- performSearch(searchInput.value);
- }
- });
+ logo.addEventListener("click", () => {
+ goToTrendingPage();
+ });
- window.addEventListener("hashchange", () => {
- const params = getHashParams();
- if (params.search) {
- performSearch(params.search);
- } else if (params.channelId) {
- fetchChannelData(params.channelId);
- } else {
- goToTrendingPage();
- }
- });
+ searchButton.addEventListener("click", () => {
+ performSearch(searchInput.value);
+ });
- fetchTrendingVideos();
- checkInitialHash();
- setupInfiniteScroll();
-});
\ No newline at end of file
+ searchInput.addEventListener("keypress", (e) => {
+ if (e.key === "Enter") {
+ performSearch(searchInput.value);
+ }
+ });
+
+ window.addEventListener("hashchange", () => {
+ const params = getHashParams();
+ if (params.search) {
+ performSearch(params.search);
+ } else if (params.channelId) {
+ fetchChannelData(params.channelId);
+ } else {
+ goToTrendingPage();
+ }
+ });
+
+ async function resolveChannelId(username) {
+ if (!username) return null;
+ if (!username.startsWith("@")) return username;
+
+ try {
+ const response = await fetch(
+ `/api/channel/${encodeURIComponent(username)}`
+ );
+ if (!response.ok) {
+ console.error("Failed to resolve channel ID for:", username);
+ return username;
+ }
+ const data = await response.json();
+ return data.channelId;
+ } catch (error) {
+ console.error("Error resolving channel ID:", error);
+ return username;
+ }
+ }
+
+ fetchTrendingVideos();
+ checkInitialHash();
+ setupInfiniteScroll();
+});
diff --git a/public/video.js b/public/video.js
index d01a552..58c3fc4 100644
--- a/public/video.js
+++ b/public/video.js
@@ -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 = `
Failed to load related videos
Error loading related videos.
`;
- }
+ }
+ }
+
+ 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();
-});
\ No newline at end of file
+ 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();
+});
diff --git a/routes/channel.js b/routes/channel.js
new file mode 100644
index 0000000..c78f100
--- /dev/null
+++ b/routes/channel.js
@@ -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;
diff --git a/routes/search.js b/routes/search.js
index beccacb..c2c269a 100644
--- a/routes/search.js
+++ b/routes/search.js
@@ -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");