fixed, made it working

This commit is contained in:
Tejas Panchal
2026-02-15 06:01:51 +05:30
parent a80228f697
commit 530eb0d5a9
18 changed files with 406 additions and 252 deletions

View File

@@ -0,0 +1,105 @@
import React, { useState, useEffect } from "react";
import { X } from "lucide-react";
import { FaDiscord, FaTelegram } from "react-icons/fa";
const DiscordPopup = () => {
const [isVisible, setIsVisible] = useState(false);
const [shouldRender, setShouldRender] = useState(false);
useEffect(() => {
// Check if the user has opted out of seeing the popup
const isHidden = localStorage.getItem("hideDiscordPopup");
if (isHidden) return;
// Set a timer for 2 minutes (120,000 ms)
const timer = setTimeout(() => {
setShouldRender(true);
// Brief delay to trigger entrance animation
setTimeout(() => setIsVisible(true), 10);
}, 60000);
return () => clearTimeout(timer);
}, []);
const handleClose = () => {
setIsVisible(false);
// Wait for animation to finish before removing from DOM
setTimeout(() => setShouldRender(false), 300);
};
const handleNeverShowAgain = () => {
localStorage.setItem("hideDiscordPopup", "true");
handleClose();
};
if (!shouldRender) return null;
return (
<div
className={`fixed inset-0 z-[9999] flex items-center justify-center p-4 transition-all duration-300 ease-in-out ${isVisible ? "opacity-100 backdrop-blur-md pointer-events-auto" : "opacity-0 backdrop-blur-none pointer-events-none"
}`}
>
<div
className={`bg-[#1a1a1a] border border-[#2a2a2a] rounded-2xl shadow-[0_0_50px_-12px_rgba(0,0,0,0.8)] overflow-hidden max-w-sm w-full transition-all duration-300 transform ${isVisible ? "scale-100 translate-y-0" : "scale-95 translate-y-4"
}`}
>
{/* Header */}
<div className="flex items-center justify-between p-5 border-b border-[#2a2a2a] bg-[#1a1a1a]">
<div className="flex items-center gap-3">
<div className="p-2.5 bg-[#5865F2] text-white rounded-xl shadow-md">
<FaDiscord className="text-xl" />
</div>
<div className="flex flex-col">
<span className="font-bold text-white text-[15px] leading-tight">Join Our Community</span>
<span className="text-[10px] text-gray-400 uppercase tracking-widest font-bold mt-0.5">Discord & Telegram</span>
</div>
</div>
<button
onClick={handleClose}
className="p-1.5 hover:bg-[#2a2a2a] rounded-full transition-colors text-gray-400 hover:text-white"
>
<X size={18} />
</button>
</div>
{/* Content */}
<div className="p-6 bg-[#1a1a1a]">
<p className="text-[13px] text-gray-400 leading-relaxed font-medium">
Join our official channels for early updates, announcements, and to connect with other fans!
</p>
<div className="mt-6 flex flex-col gap-3">
<a
href="https://discord.gg/your-invite-link"
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-center gap-2 w-full bg-[#5865F2] hover:bg-[#4759d8] text-white py-2.5 px-4 rounded-xl font-bold transition-all transform active:scale-[0.97] shadow-lg"
>
<FaDiscord className="text-lg" />
JOIN DISCORD
</a>
<a
href="https://tinyurl.com/JustAnimeZone"
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-center gap-2 w-full bg-[#26A5E4] hover:bg-[#2295ce] text-white py-2.5 px-4 rounded-xl font-bold transition-all transform active:scale-[0.97] shadow-lg"
>
<FaTelegram className="text-lg" />
JOIN TELEGRAM
</a>
</div>
<button
onClick={handleNeverShowAgain}
className="mt-5 w-full text-[11px] text-gray-500 hover:text-white underline-offset-4 hover:underline transition-colors py-1 font-semibold tracking-wide"
>
NEVER SHOW AGAIN
</button>
</div>
</div>
</div>
);
};
export default DiscordPopup;

View File

@@ -8,6 +8,7 @@ import {
} from "@fortawesome/free-solid-svg-icons";
import { Link } from "react-router-dom";
import { useLanguage } from "@/src/context/LanguageContext";
import getSafeTitle from "@/src/utils/getSafetitle";
import "./Banner.css";
function Banner({ item, index }) {
@@ -16,19 +17,19 @@ function Banner({ item, index }) {
<section className="spotlight w-full h-full relative rounded-2xl overflow-hidden">
<img
src={`${item.poster}`}
alt={item.title}
alt={getSafeTitle(item.title, language, item.japanese_title)}
className="absolute inset-0 object-cover w-full h-full rounded-2xl"
/>
<div className="spotlight-overlay absolute inset-0 z-[1] rounded-2xl"></div>
<div className="absolute flex flex-col left-0 bottom-[40px] w-[55%] p-4 z-[2] max-[1390px]:w-[45%] max-[1390px]:bottom-[40px] max-[1300px]:w-[600px] max-[1120px]:w-[60%] max-md:w-[90%] max-md:bottom-[20px] max-[300px]:w-full">
<p className="text-[#ffbade] font-semibold text-[20px] w-fit max-[1300px]:text-[15px]">
#{index + 1} Spotlight
</p>
<h3 className="text-white line-clamp-2 text-5xl font-bold mt-4 text-left max-[1390px]:text-[45px] max-[1300px]:text-3xl max-[1300px]:mt-3 max-md:text-2xl max-md:mt-1 max-[575px]:text-[22px] max-sm:leading-6 max-sm:w-[80%] max-[320px]:w-full">
{language === "EN" ? item.title : item.japanese_title}
{getSafeTitle(item.title, language, item.japanese_title)}
</h3>
{/* Mobile Buttons */}
<div className="hidden max-md:flex max-md:mt-3 max-md:gap-x-3 max-md:w-full">
<Link

View File

@@ -6,9 +6,10 @@ import {
faPlay,
} from "@fortawesome/free-solid-svg-icons";
import { FaChevronRight } from "react-icons/fa";
import "./CategoryCard.css";
import { useLanguage } from "@/src/context/LanguageContext";
import { Link, useNavigate } from "react-router-dom";
import getSafeTitle from "@/src/utils/getSafetitle";
import "./CategoryCard.css";
const CategoryCard = React.memo(
({
@@ -23,7 +24,7 @@ const CategoryCard = React.memo(
}) => {
const { language } = useLanguage();
const navigate = useNavigate();
if (limit) {
data = data.slice(0, limit);
}
@@ -55,7 +56,7 @@ const CategoryCard = React.memo(
if (
JSON.stringify(prev.firstRow) !== JSON.stringify(newItems.firstRow) ||
JSON.stringify(prev.remainingItems) !==
JSON.stringify(newItems.remainingItems)
JSON.stringify(newItems.remainingItems)
) {
return newItems;
}
@@ -90,11 +91,10 @@ const CategoryCard = React.memo(
<>
{categoryPage && (
<div
className={`grid grid-cols-4 gap-x-3 gap-y-8 transition-all duration-300 ease-in-out ${
categoryPage && itemsToRender.firstRow.length > 0
? "mt-8 max-[758px]:hidden"
: ""
}`}
className={`grid grid-cols-4 gap-x-3 gap-y-8 transition-all duration-300 ease-in-out ${categoryPage && itemsToRender.firstRow.length > 0
? "mt-8 max-[758px]:hidden"
: ""
}`}
>
{itemsToRender.firstRow.map((item, index) => (
<div
@@ -107,17 +107,16 @@ const CategoryCard = React.memo(
className="inline-block bg-gray-900 absolute left-0 top-0 w-full h-full group hover:cursor-pointer"
onClick={() =>
navigate(
`${
path === "top-upcoming"
? `/${item.id}`
: `/watch/${item.id}`
`${path === "top-upcoming"
? `/${item.id}`
: `/watch/${item.id}`
}`
)
}
>
<img
src={`${item.poster}`}
alt={item.title}
alt={getSafeTitle(item.title, language, item.japanese_title)}
className="block w-full h-full object-cover transition-all duration-500 ease-in-out group-hover:scale-105 group-hover:blur-sm"
/>
<div className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-all duration-300 flex items-center justify-center">
@@ -131,10 +130,10 @@ const CategoryCard = React.memo(
</div>
{(item.tvInfo?.rating === "18+" ||
item?.adultContent === true) && (
<div className="text-white px-2 py-0.5 rounded-lg bg-red-600 absolute top-3 left-3 flex items-center justify-center text-[12px] font-bold">
18+
</div>
)}
<div className="text-white px-2 py-0.5 rounded-lg bg-red-600 absolute top-3 left-3 flex items-center justify-center text-[12px] font-bold">
18+
</div>
)}
<div className="absolute bottom-0 left-0 right-0 p-3 pb-2 bg-gradient-to-t from-black/80 via-black/50 to-transparent">
<div className="flex items-center justify-start w-full space-x-1.5 z-[100] flex-wrap gap-y-1.5">
{item.tvInfo?.sub && (
@@ -177,9 +176,9 @@ const CategoryCard = React.memo(
{(item.tvInfo?.duration || item.duration) && (
<div className="bg-[#2a2a2a] text-white rounded-[2px] px-2 py-1 text-[11px] font-medium">
{item.tvInfo?.duration === "m" ||
item.tvInfo?.duration === "?" ||
item.duration === "m" ||
item.duration === "?"
item.tvInfo?.duration === "?" ||
item.duration === "m" ||
item.duration === "?"
? "N/A"
: item.tvInfo?.duration || item.duration || "N/A"}
</div>
@@ -191,7 +190,7 @@ const CategoryCard = React.memo(
to={`/${item.id}`}
className="text-white font-semibold mt-3 item-title hover:text-white hover:cursor-pointer line-clamp-1"
>
{language === "EN" ? item.title : item.japanese_title}
{getSafeTitle(item.title, language, item.japanese_title)}
</Link>
{item.description && (
<div className="line-clamp-3 text-[13px] font-light text-gray-400 mt-3 max-[1200px]:hidden">
@@ -214,17 +213,16 @@ const CategoryCard = React.memo(
className="inline-block bg-gray-900 absolute left-0 top-0 w-full h-full group hover:cursor-pointer"
onClick={() =>
navigate(
`${
path === "top-upcoming"
? `/${item.id}`
: `/watch/${item.id}`
`${path === "top-upcoming"
? `/${item.id}`
: `/watch/${item.id}`
}`
)
}
>
<img
src={`${item.poster}`}
alt={item.title}
alt={getSafeTitle(item.title, language, item.japanese_title)}
className="block w-full h-full object-cover transition-all duration-500 ease-in-out group-hover:scale-105 group-hover:blur-sm"
/>
<div className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-all duration-300 flex items-center justify-center">
@@ -238,10 +236,10 @@ const CategoryCard = React.memo(
</div>
{(item.tvInfo?.rating === "18+" ||
item?.adultContent === true) && (
<div className="text-white px-2 py-0.5 rounded-lg bg-red-600 absolute top-3 left-3 flex items-center justify-center text-[12px] font-bold">
18+
</div>
)}
<div className="text-white px-2 py-0.5 rounded-lg bg-red-600 absolute top-3 left-3 flex items-center justify-center text-[12px] font-bold">
18+
</div>
)}
<div className="absolute bottom-0 left-0 right-0 p-2 bg-gradient-to-t from-black/80 via-black/50 to-transparent">
<div className="flex items-center justify-start w-full space-x-1 max-[478px]:space-x-0.5 z-[100] flex-wrap gap-y-1">
{item.tvInfo?.sub && (
@@ -284,9 +282,9 @@ const CategoryCard = React.memo(
{(item.tvInfo?.duration || item.duration) && (
<div className="bg-[#2a2a2a] text-white rounded-[2px] px-1.5 py-0.5 text-[10px] font-medium max-[478px]:py-0.5 max-[478px]:px-1 max-[478px]:hidden">
{item.tvInfo?.duration === "m" ||
item.tvInfo?.duration === "?" ||
item.duration === "m" ||
item.duration === "?"
item.tvInfo?.duration === "?" ||
item.duration === "m" ||
item.duration === "?"
? "N/A"
: item.tvInfo?.duration || item.duration || "N/A"}
</div>
@@ -298,7 +296,7 @@ const CategoryCard = React.memo(
to={`/${item.id}`}
className="text-white font-semibold mt-3 item-title hover:text-white hover:cursor-pointer line-clamp-1"
>
{language === "EN" ? item.title : item.japanese_title}
{getSafeTitle(item.title, language, item.japanese_title)}
</Link>
</div>
))}

View File

@@ -9,6 +9,7 @@ import { FaHistory, FaChevronLeft, FaChevronRight } from "react-icons/fa";
import { useLanguage } from "@/src/context/LanguageContext";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faPlay } from "@fortawesome/free-solid-svg-icons";
import getSafeTitle from "@/src/utils/getSafetitle";
const ContinueWatching = () => {
const [watchList, setWatchList] = useState([]);
@@ -92,9 +93,9 @@ const ContinueWatching = () => {
>
<img
src={`${item?.poster}`}
alt={item?.title}
alt={getSafeTitle(item?.title, language, item?.japanese_title)}
className="block w-full h-full object-cover transition-all duration-500 ease-in-out group-hover:scale-105 group-hover:blur-sm"
title={item?.title}
title={getSafeTitle(item?.title, language, item?.japanese_title)}
loading="lazy"
/>
<div className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-all duration-300 flex items-center justify-center">
@@ -113,9 +114,7 @@ const ContinueWatching = () => {
)}
<div className="absolute bottom-0 left-0 right-0 p-3 pb-2 bg-gradient-to-t from-black/90 via-black/60 to-transparent">
<p className="text-white text-[15px] font-bold text-left truncate mb-1.5 max-[450px]:text-sm drop-shadow-lg">
{language === "EN"
? item?.title
: item?.japanese_title}
{getSafeTitle(item?.title, language, item?.japanese_title)}
</p>
<p className="text-gray-200 text-[13px] font-semibold text-left max-[450px]:text-[12px] drop-shadow-md">
Episode {item.episodeNum}

View File

@@ -1,18 +1,39 @@
import logoTitle from "@/src/config/logoTitle.js";
import website_name from "@/src/config/website.js";
import { Link } from "react-router-dom";
import { FaDiscord, FaTelegram } from "react-icons/fa";
function Footer() {
return (
<footer className="w-full mt-16">
{/* Logo Section */}
<div className="max-w-[1920px] mx-auto px-4">
<div className="flex justify-center sm:justify-start items-center gap-6">
<img
src="/footer.png"
alt={logoTitle}
className="h-[100px] w-[200px] object-contain"
/>
<div className="flex flex-col sm:flex-row justify-between items-center gap-6">
<div className="flex items-center gap-6">
<img
src="/footer.png"
alt={logoTitle}
className="h-[100px] w-[200px] object-contain"
/>
<div className="flex items-center gap-4 border-l border-white/10 pl-6 h-10">
<a
href="https://discord.gg/your-invite-link"
target="_blank"
rel="noopener noreferrer"
className="text-white/40 hover:text-[#5865F2] transition-all hover:scale-110"
>
<FaDiscord size={28} />
</a>
<a
href="https://t.me/your-telegram-link"
target="_blank"
rel="noopener noreferrer"
className="text-white/40 hover:text-[#26A5E4] transition-all hover:scale-110"
>
<FaTelegram size={28} />
</a>
</div>
</div>
</div>
</div>

View File

@@ -59,11 +59,13 @@ export default function Player({
animeInfo,
episodeNum,
streamInfo,
serverName,
}) {
const artRef = useRef(null);
const leftAtRef = useRef(0);
const leftAtRef = useRef(0);
const proxy = import.meta.env.VITE_PROXY_URL;
const m3u8proxy = import.meta.env.VITE_M3U8_PROXY_URL?.split(",") || [];
const m3u8proxyHD3 = import.meta.env.VITE_M3U8_PROXY_HD3;
const [currentEpisodeIndex, setCurrentEpisodeIndex] = useState(
episodes?.findIndex(
(episode) => episode.id.match(/ep=(\d+)/)?.[1] === episodeId
@@ -117,11 +119,11 @@ export default function Player({
const currentTime = Math.round(video.currentTime);
const duration = Math.round(video.duration);
if (duration > 0 && currentTime >= duration) {
art.pause();
if (currentEpisodeIndex < episodes?.length - 1 && autoNext) {
playNext(
episodes[currentEpisodeIndex + 1].id.match(/ep=(\d+)/)?.[1]
);
art.pause();
if (currentEpisodeIndex < episodes?.length - 1 && autoNext) {
playNext(
episodes[currentEpisodeIndex + 1].id.match(/ep=(\d+)/)?.[1]
);
}
}
});
@@ -131,11 +133,11 @@ export default function Player({
const currentTime = Math.round(video.currentTime);
const duration = Math.round(video.duration);
if (duration > 0 && currentTime >= duration) {
art.pause();
if (currentEpisodeIndex < episodes?.length - 1 && autoNext) {
playNext(
episodes[currentEpisodeIndex + 1].id.match(/ep=(\d+)/)?.[1]
);
art.pause();
if (currentEpisodeIndex < episodes?.length - 1 && autoNext) {
playNext(
episodes[currentEpisodeIndex + 1].id.match(/ep=(\d+)/)?.[1]
);
}
}
});
@@ -211,18 +213,21 @@ export default function Player({
if (!streamUrl || !artRef.current) return;
const iframeUrl = streamInfo?.streamingLink?.iframe;
const headers = {};
headers.referer=new URL(iframeUrl).origin+"/";
console.log(m3u8proxy[Math.floor(Math.random() * m3u8proxy?.length)] +
encodeURIComponent(streamUrl) +
"&headers=" +
encodeURIComponent(JSON.stringify(headers)));
headers.referer = new URL(iframeUrl).origin + "/";
const finalProxy = (serverName === "hd-3" && m3u8proxyHD3)
? m3u8proxyHD3
: m3u8proxy[Math.floor(Math.random() * m3u8proxy?.length)];
console.log(finalProxy +
encodeURIComponent(streamUrl) +
"&headers=" +
encodeURIComponent(JSON.stringify(headers)));
const art = new Artplayer({
url:
m3u8proxy[Math.floor(Math.random() * m3u8proxy?.length)] +
finalProxy +
encodeURIComponent(streamUrl) +
"&headers=" +
encodeURIComponent(JSON.stringify(headers)),
"&headers=" +
encodeURIComponent(JSON.stringify(headers)),
container: artRef.current,
type: "m3u8",
autoplay: autoPlay,

View File

@@ -9,8 +9,11 @@ import {
faMicrophone,
} from "@fortawesome/free-solid-svg-icons";
import { Link } from "react-router-dom";
import getSafeTitle from "@/src/utils/getSafetitle";
import { useLanguage } from "@/src/context/LanguageContext";
function Qtip({ id }) {
const { language } = useLanguage();
const [qtip, setQtip] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
@@ -38,7 +41,7 @@ function Qtip({ id }) {
) : (
<div className="w-full flex flex-col justify-start gap-y-2">
<h1 className="text-xl font-semibold text-white text-[13px] leading-6">
{qtip.title}
{getSafeTitle(qtip.title, language, qtip.japaneseTitle)}
</h1>
<div className="w-full flex items-center relative mt-2">
{qtip?.rating && (

View File

@@ -36,12 +36,14 @@ function Servers({
setActiveServerId(matchingServer.data_id);
setActiveServerType(matchingServer.type);
} else if (servers && servers.length > 0) {
setActiveServerId(servers[0].data_id);
setActiveServerType(servers[0].type);
const defaultServer = servers.find(s => s.serverName === "HD-2") || servers[0];
setActiveServerId(defaultServer.data_id);
setActiveServerType(defaultServer.type);
}
} else if (servers && servers.length > 0) {
setActiveServerId(servers[0].data_id);
setActiveServerType(servers[0].type);
const defaultServer = servers.find(s => s.serverName === "HD-2") || servers[0];
setActiveServerId(defaultServer.data_id);
setActiveServerType(defaultServer.type);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [servers]);
@@ -77,11 +79,10 @@ function Servers({
</div>
<div className="bg-[#1f1f1f] flex flex-col max-[600px]:rounded-lg max-[600px]:p-2">
{rawServers.length > 0 && (
<div className={`servers px-2 flex items-center flex-wrap gap-y-1 ml-2 max-[600px]:py-1.5 max-[600px]:px-1 max-[600px]:ml-0 ${
dubServers.length === 0 || subServers.length === 0
<div className={`servers px-2 flex items-center flex-wrap gap-y-1 ml-2 max-[600px]:py-1.5 max-[600px]:px-1 max-[600px]:ml-0 ${dubServers.length === 0 || subServers.length === 0
? "h-1/2"
: "h-full"
}`}>
}`}>
<div className="flex items-center gap-x-2 min-w-[65px]">
<FontAwesomeIcon
icon={faFile}
@@ -93,11 +94,10 @@ function Servers({
{rawServers.map((item, index) => (
<div
key={index}
className={`px-6 py-[5px] rounded-lg cursor-pointer ${
activeServerId === item?.data_id
className={`px-6 py-[5px] rounded-lg cursor-pointer ${activeServerId === item?.data_id
? "bg-[#e0e0e0] text-black"
: "bg-[#373737] text-white"
} max-[700px]:px-3 max-[600px]:px-2 max-[600px]:py-1`}
} max-[700px]:px-3 max-[600px]:px-2 max-[600px]:py-1`}
onClick={() => handleServerSelect(item)}
>
<p className="text-[13px] font-semibold max-[600px]:text-[12px]">
@@ -109,9 +109,8 @@ function Servers({
</div>
)}
{subServers.length > 0 && (
<div className={`servers px-2 flex items-center flex-wrap gap-y-1 ml-2 max-[600px]:py-1.5 max-[600px]:px-1 max-[600px]:ml-0 ${
dubServers.length === 0 ? "h-1/2" : "h-full"
}`}>
<div className={`servers px-2 flex items-center flex-wrap gap-y-1 ml-2 max-[600px]:py-1.5 max-[600px]:px-1 max-[600px]:ml-0 ${dubServers.length === 0 ? "h-1/2" : "h-full"
}`}>
<div className="flex items-center gap-x-2 min-w-[65px]">
<FontAwesomeIcon
icon={faClosedCaptioning}
@@ -123,11 +122,10 @@ function Servers({
{subServers.map((item, index) => (
<div
key={index}
className={`px-6 py-[5px] rounded-lg cursor-pointer ${
activeServerId === item?.data_id
className={`px-6 py-[5px] rounded-lg cursor-pointer ${activeServerId === item?.data_id
? "bg-[#e0e0e0] text-black"
: "bg-[#373737] text-white"
} max-[700px]:px-3 max-[600px]:px-2 max-[600px]:py-1`}
} max-[700px]:px-3 max-[600px]:px-2 max-[600px]:py-1`}
onClick={() => handleServerSelect(item)}
>
<p className="text-[13px] font-semibold max-[600px]:text-[12px]">
@@ -139,9 +137,8 @@ function Servers({
</div>
)}
{dubServers.length > 0 && (
<div className={`servers px-2 flex items-center flex-wrap gap-y-1 ml-2 max-[600px]:py-1.5 max-[600px]:px-1 max-[600px]:ml-0 ${
subServers.length === 0 ? "h-1/2" : "h-full"
}`}>
<div className={`servers px-2 flex items-center flex-wrap gap-y-1 ml-2 max-[600px]:py-1.5 max-[600px]:px-1 max-[600px]:ml-0 ${subServers.length === 0 ? "h-1/2" : "h-full"
}`}>
<div className="flex items-center gap-x-2 min-w-[65px]">
<FontAwesomeIcon
icon={faMicrophone}
@@ -153,11 +150,10 @@ function Servers({
{dubServers.map((item, index) => (
<div
key={index}
className={`px-6 py-[5px] rounded-lg cursor-pointer ${
activeServerId === item?.data_id
className={`px-6 py-[5px] rounded-lg cursor-pointer ${activeServerId === item?.data_id
? "bg-[#e0e0e0] text-black"
: "bg-[#373737] text-white"
} max-[700px]:px-3 max-[600px]:px-2 max-[600px]:py-1`}
} max-[700px]:px-3 max-[600px]:px-2 max-[600px]:py-1`}
onClick={() => handleServerSelect(item)}
>
<p className="text-[13px] font-semibold max-[600px]:text-[12px]">

View File

@@ -8,6 +8,7 @@ import { useLanguage } from "@/src/context/LanguageContext";
import { Link } from "react-router-dom";
import useToolTipPosition from "@/src/hooks/useToolTipPosition";
import Qtip from "../qtip/Qtip";
import getSafeTitle from "@/src/utils/getSafetitle";
function Sidecard({ data, label, className }) {
const { language } = useLanguage();
@@ -50,27 +51,25 @@ function Sidecard({ data, label, className }) {
<div className="flex items-start gap-3 p-2 rounded-lg transition-colors hover:bg-[#1f1f1f]">
{hoveredItem === item.id + index && window.innerWidth > 1024 && (
<div
className={`absolute ${tooltipPosition} ${tooltipHorizontalPosition} ${
tooltipPosition === "top-1/2"
? "translate-y-[50px]"
: "translate-y-[-50px]"
} z-[100000] transform transition-all duration-300 ease-in-out ${
hoveredItem === item.id + index
className={`absolute ${tooltipPosition} ${tooltipHorizontalPosition} ${tooltipPosition === "top-1/2"
? "translate-y-[50px]"
: "translate-y-[-50px]"
} z-[100000] transform transition-all duration-300 ease-in-out ${hoveredItem === item.id + index
? "opacity-100 translate-y-0"
: "opacity-0 translate-y-2"
}`}
}`}
>
<Qtip id={item.id} />
</div>
)}
<img
src={`${item.poster}`}
alt={item.title}
alt={getSafeTitle(item.title, language, item.japanese_title)}
className="w-[50px] h-[70px] rounded object-cover"
/>
<div className="flex flex-col gap-1.5 flex-1 min-w-0">
<span className="text-sm font-medium text-gray-200 group-hover:text-white transition-colors line-clamp-1">
{language === "EN" ? item.title : item.japanese_title}
{getSafeTitle(item.title, language, item.japanese_title)}
</span>
<div className="flex flex-wrap items-center gap-2">
{item.tvInfo?.sub && (

View File

@@ -3,8 +3,11 @@ import { useEffect, useState } from "react";
import BouncingLoader from "../ui/bouncingloader/Bouncingloader";
import { FaChevronRight } from "react-icons/fa";
import { Link } from "react-router-dom";
import getSafeTitle from "@/src/utils/getSafetitle";
import { useLanguage } from "@/src/context/LanguageContext";
function Suggestion({ keyword, className, onSuggestionClick }) {
const { language } = useLanguage();
const [suggestion, setSuggestion] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
@@ -31,10 +34,9 @@ function Suggestion({ keyword, className, onSuggestionClick }) {
return (
<div
className={`bg-zinc-900 ${className} flex ${
loading ? "justify-center py-4" : "justify-start"
} ${!suggestion ? "p-2" : "justify-start"} items-center rounded-lg`}
style={{
className={`bg-zinc-900 ${className} flex ${loading ? "justify-center py-4" : "justify-start"
} ${!suggestion ? "p-2" : "justify-start"} items-center rounded-lg`}
style={{
boxShadow: "0 8px 32px rgba(0, 0, 0, 0.2)",
border: "1px solid rgba(255, 255, 255, 0.05)"
}}
@@ -61,20 +63,15 @@ function Suggestion({ keyword, className, onSuggestionClick }) {
<img
src={`${item.poster}`}
className="w-[45px] h-[65px] flex-shrink-0 object-cover rounded-md shadow-lg"
alt=""
alt={getSafeTitle(item.title, language, item.japanese_title)}
onError={(e) => {
e.target.src = "https://i.postimg.cc/HnHKvHpz/no-avatar.jpg";
}}
/>
<div className="flex flex-col gap-y-[2px]">
{item?.title && (
{(item?.title || item?.japanese_title) && (
<h1 className="line-clamp-1 leading-5 font-semibold text-[14px] text-gray-100 group-hover:text-white">
{item.title || "N/A"}
</h1>
)}
{item?.japanese_title && (
<h1 className="line-clamp-1 leading-4 text-[12px] font-normal text-gray-400">
{item.japanese_title || "N/A"}
{getSafeTitle(item.title, language, item.japanese_title)}
</h1>
)}
{(item?.releaseDate || item?.showType || item?.duration) && (

View File

@@ -8,6 +8,7 @@ import { useLanguage } from "@/src/context/LanguageContext";
import { Link, useNavigate } from "react-router-dom";
import useToolTipPosition from "@/src/hooks/useToolTipPosition";
import Qtip from "../qtip/Qtip";
import getSafeTitle from "@/src/utils/getSafetitle";
function Topten({ data, className }) {
const { language } = useLanguage();
@@ -29,8 +30,8 @@ function Topten({ data, className }) {
activePeriod === "today"
? data.today
: activePeriod === "week"
? data.week
: data.month;
? data.week
: data.month;
const { tooltipPosition, tooltipHorizontalPosition, cardRefs } =
useToolTipPosition(hoveredItem, currentData);
@@ -56,11 +57,10 @@ function Topten({ data, className }) {
{["today", "week", "month"].map((period) => (
<li
key={period}
className={`cursor-pointer p-1.5 px-4 transition-all duration-200 ${
activePeriod === period
? "bg-white text-black font-medium"
: "text-gray-400 hover:text-white hover:bg-[#2a2a2a]"
}`}
className={`cursor-pointer p-1.5 px-4 transition-all duration-200 ${activePeriod === period
? "bg-white text-black font-medium"
: "text-gray-400 hover:text-white hover:bg-[#2a2a2a]"
}`}
onClick={() => handlePeriodChange(period)}
>
{period.charAt(0).toUpperCase() + period.slice(1)}
@@ -78,11 +78,10 @@ function Topten({ data, className }) {
ref={(el) => (cardRefs.current[index] = el)}
>
<h1
className={`font-bold text-2xl transition-colors ${
index < 3
? "text-white border-b-2 border-white pb-0.5"
: "text-gray-600"
} max-[350px]:hidden`}
className={`font-bold text-2xl transition-colors ${index < 3
? "text-white border-b-2 border-white pb-0.5"
: "text-gray-600"
} max-[350px]:hidden`}
>
{`${index + 1 < 10 ? "0" : ""}${index + 1}`}
</h1>
@@ -97,7 +96,7 @@ function Topten({ data, className }) {
>
<img
src={`${item.poster}`}
alt={item.title}
alt={getSafeTitle(item.title, language, item.japanese_title)}
className="w-[55px] h-[70px] rounded-lg object-cover flex-shrink-0 cursor-pointer shadow-md transition-transform duration-200 group-hover:scale-[1.02]"
onClick={() => navigate(`/watch/${item.id}`)}
onMouseEnter={() => handleMouseEnter(item, index)}
@@ -109,17 +108,15 @@ function Topten({ data, className }) {
window.innerWidth > 1024 && (
<div
className={`absolute ${tooltipPosition} ${tooltipHorizontalPosition}
${
tooltipPosition === "top-1/2"
${tooltipPosition === "top-1/2"
? "translate-y-[50px]"
: "translate-y-[-50px]"
}
}
z-[100000] transform transition-all duration-300 ease-in-out
${
hoveredItem === item.id + index
${hoveredItem === item.id + index
? "opacity-100 translate-y-0"
: "opacity-0 translate-y-2"
}`}
}`}
onMouseEnter={() => {
if (hoverTimeout) clearTimeout(hoverTimeout);
}}
@@ -135,7 +132,7 @@ function Topten({ data, className }) {
className="text-[0.95em] font-medium text-gray-200 hover:text-white transform transition-all ease-out line-clamp-1 max-[478px]:line-clamp-2 max-[478px]:text-[14px]"
onClick={() => handleNavigate(item.id)}
>
{language === "EN" ? item.title : item.japanese_title}
{getSafeTitle(item.title, language, item.japanese_title)}
</Link>
<div className="flex flex-wrap items-center w-fit space-x-2 max-[350px]:gap-y-[3px]">
{item.tvInfo?.sub && (

View File

@@ -6,6 +6,7 @@ import {
faMicrophone,
faFire
} from "@fortawesome/free-solid-svg-icons";
import getSafeTitle from "@/src/utils/getSafetitle";
const Trending = ({ trending, className }) => {
const { language } = useLanguage();
@@ -29,7 +30,7 @@ const Trending = ({ trending, className }) => {
<div className="relative">
<img
src={item.poster}
alt={item.title}
alt={getSafeTitle(item.title, language, item.japanese_title)}
className="w-[50px] h-[70px] rounded object-cover"
/>
<div className="absolute top-0 left-0 bg-white/90 text-black text-xs font-bold px-1.5 rounded-br">
@@ -38,7 +39,7 @@ const Trending = ({ trending, className }) => {
</div>
<div className="flex flex-col gap-1.5 flex-1 min-w-0">
<span className="text-sm font-medium text-gray-200 group-hover:text-white transition-colors line-clamp-2">
{language === "EN" ? item.title : item.japanese_title}
{getSafeTitle(item.title, language, item.japanese_title)}
</span>
<div className="flex flex-wrap items-center gap-2">
{item.tvInfo?.sub && (