From c8e5d6fa84c02a7f551b473ad986b414d74ef70d Mon Sep 17 00:00:00 2001 From: Tejas Panchal Date: Wed, 18 Feb 2026 22:48:45 +0530 Subject: [PATCH 1/8] splash screen --- src/components/splashscreen/SplashScreen.css | 34 +++++++++----------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/components/splashscreen/SplashScreen.css b/src/components/splashscreen/SplashScreen.css index ef16103..bc301d4 100644 --- a/src/components/splashscreen/SplashScreen.css +++ b/src/components/splashscreen/SplashScreen.css @@ -19,20 +19,17 @@ html { /* Container and background */ .splash-container { min-height: 100vh; - width: 100vw; - position: fixed; + width: 100%; + position: relative; background: url('/splash.jpg'); background-position: center; background-repeat: no-repeat; - background-size: 100% auto; + background-size: cover; display: flex; justify-content: center; align-items: flex-start; padding: 0 30px; - overflow: auto; margin: 0; - left: 0; - top: 0; } @media (max-width: 768px) { @@ -73,7 +70,8 @@ html { display: flex; flex-direction: column; align-items: center; - padding-top: 140px; + padding-top: 120px; + padding-bottom: 50px; } /* Logo */ @@ -82,7 +80,7 @@ html { } .logo { - height: 75px; + height: 65px; width: auto; } @@ -96,12 +94,12 @@ html { .search-input { width: 100%; - padding: 14px 48px 14px 20px; + padding: 12px 42px 12px 18px; background: rgba(17, 17, 17, 0.75); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 8px; color: white; - font-size: 16px; + font-size: 15px; outline: none; transition: border-color 0.2s; } @@ -136,7 +134,7 @@ html { .enter-button { background: rgba(255, 255, 255, 0.9); color: black; - padding: 14px 28px; + padding: 12px 24px; border-radius: 8px; text-decoration: none; font-weight: 500; @@ -175,10 +173,10 @@ html { } .faq-title { - font-size: 32px; + font-size: 28px; font-weight: 700; text-align: center; - margin-bottom: 40px; + margin-bottom: 30px; color: white; } @@ -197,14 +195,14 @@ html { .faq-question { width: 100%; - padding: 18px 24px; + padding: 16px 20px; display: flex; justify-content: space-between; align-items: center; background: none; border: none; color: white; - font-size: 17px; + font-size: 16px; text-align: left; cursor: pointer; transition: all 0.2s ease; @@ -226,10 +224,10 @@ html { } .faq-answer { - padding: 0 24px 18px; + padding: 0 20px 16px; color: #999; line-height: 1.6; - font-size: 15px; + font-size: 14px; } /* Responsive adjustments */ @@ -335,4 +333,4 @@ html { font-size: 20px; margin-bottom: 20px; } -} +} \ No newline at end of file From a473595ed239d68444508f42fae4ce452e797dbe Mon Sep 17 00:00:00 2001 From: Tejas Panchal Date: Thu, 19 Feb 2026 01:59:12 +0530 Subject: [PATCH 2/8] home page done --- src/components/banner/Banner.jsx | 7 ++--- src/components/categorycard/CategoryCard.jsx | 2 +- src/components/footer/Footer.jsx | 2 +- src/components/genres/Genre.jsx | 8 +++--- src/components/spotlight/Spotlight.jsx | 5 ++-- .../tabbed-anime/TabbedAnimeSection.jsx | 7 ++--- src/components/trending/Trending.jsx | 2 +- src/pages/Home/Home.jsx | 26 ++++++++++++++----- 8 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/components/banner/Banner.jsx b/src/components/banner/Banner.jsx index d67ac3b..d6fb28a 100644 --- a/src/components/banner/Banner.jsx +++ b/src/components/banner/Banner.jsx @@ -14,13 +14,14 @@ import "./Banner.css"; function Banner({ item, index }) { const { language } = useLanguage(); return ( -
+
{getSafeTitle(item.title, -
+

diff --git a/src/components/categorycard/CategoryCard.jsx b/src/components/categorycard/CategoryCard.jsx index 5a0dd8f..4781191 100644 --- a/src/components/categorycard/CategoryCard.jsx +++ b/src/components/categorycard/CategoryCard.jsx @@ -201,7 +201,7 @@ const CategoryCard = React.memo( ))}

)} -
+
{itemsToRender.remainingItems.map((item, index) => (
-

© {website_name}. All rights reserved.

+

© 2026 {website_name}. All rights reserved.

diff --git a/src/components/genres/Genre.jsx b/src/components/genres/Genre.jsx index dd7c60e..fb89543 100644 --- a/src/components/genres/Genre.jsx +++ b/src/components/genres/Genre.jsx @@ -23,10 +23,10 @@ function Genre({ data }) { }, []); return ( -
+
{/* Content first for proper stacking */} -
-
); diff --git a/src/components/trending/Trending.jsx b/src/components/trending/Trending.jsx index cfde3de..e400ce1 100644 --- a/src/components/trending/Trending.jsx +++ b/src/components/trending/Trending.jsx @@ -12,7 +12,7 @@ const Trending = ({ trending, className }) => { const { language } = useLanguage(); return ( -
+

Trending Now

diff --git a/src/pages/Home/Home.jsx b/src/pages/Home/Home.jsx index 2dd6a9e..2c0d861 100644 --- a/src/pages/Home/Home.jsx +++ b/src/pages/Home/Home.jsx @@ -1,3 +1,4 @@ +import { useState, useEffect } from "react"; import website_name from "@/src/config/website.js"; import Spotlight from "@/src/components/spotlight/Spotlight.jsx"; import Trending from "@/src/components/trending/Trending.jsx"; @@ -19,6 +20,17 @@ import { function Home() { const { homeInfo, homeInfoLoading, error } = useHomeInfo(); + const [itemLimit, setItemLimit] = useState(window.innerWidth > 1400 ? 10 : 12); + + useEffect(() => { + const handleResize = () => { + setItemLimit(window.innerWidth > 1400 ? 10 : 12); + }; + + window.addEventListener("resize", handleResize); + return () => window.removeEventListener("resize", handleResize); + }, []); + if (homeInfoLoading) return ; if (error) return ; if (!homeInfo) return ; @@ -63,26 +75,28 @@ function Home() {
-
+
-
+
- +
From 09f29686842d826269f2cced514454455e38a120 Mon Sep 17 00:00:00 2001 From: Tejas Panchal Date: Thu, 19 Feb 2026 02:07:04 +0530 Subject: [PATCH 3/8] cleaned home --- src/components/categorycard/CategoryCard.jsx | 359 +++++++------------ src/components/continue/ContinueWatching.jsx | 10 +- src/components/genres/Genre.jsx | 5 +- src/components/schedule/Schedule.jsx | 61 ++-- src/pages/Home/Home.jsx | 7 +- 5 files changed, 153 insertions(+), 289 deletions(-) diff --git a/src/components/categorycard/CategoryCard.jsx b/src/components/categorycard/CategoryCard.jsx index 4781191..24f87da 100644 --- a/src/components/categorycard/CategoryCard.jsx +++ b/src/components/categorycard/CategoryCard.jsx @@ -11,6 +11,98 @@ import { Link, useNavigate } from "react-router-dom"; import getSafeTitle from "@/src/utils/getSafetitle"; import "./CategoryCard.css"; +const AnimeCard = ({ item, navigate, path, language, isFirstRow = false }) => { + return ( +
+
+
+ navigate( + `${path === "top-upcoming" + ? `/${item.id}` + : `/watch/${item.id}` + }` + ) + } + > + {getSafeTitle(item.title, +
+
+ +
+
+
+ {(item.tvInfo?.rating === "18+" || item?.adultContent === true) && ( +
+ 18+ +
+ )} +
+
+ {item.tvInfo?.sub && ( +
+ +

{item.tvInfo.sub}

+
+ )} + {item.tvInfo?.dub && ( +
+ +

{item.tvInfo.dub}

+
+ )} + {item.tvInfo?.showType && ( +
+ {item.tvInfo.showType.split(" ").shift()} +
+ )} + {item.releaseDate && ( +
+ {item.releaseDate} +
+ )} + {!item.tvInfo?.showType && item.type && ( +
+ {item.type} +
+ )} + {(item.tvInfo?.duration || item.duration) && ( +
+ {item.tvInfo?.duration === "m" || item.tvInfo?.duration === "?" || item.duration === "m" || item.duration === "?" + ? "N/A" + : item.tvInfo?.duration || item.duration || "N/A"} +
+ )} +
+
+
+ + {getSafeTitle(item.title, language, item.japanese_title)} + + {isFirstRow && item.description && ( +
+ {item.description} +
+ )} +
+ ); +}; + const CategoryCard = React.memo( ({ label, @@ -25,49 +117,29 @@ const CategoryCard = React.memo( const { language } = useLanguage(); const navigate = useNavigate(); - if (limit) { - data = data.slice(0, limit); - } + const displayData = limit ? data.slice(0, limit) : data; - const [itemsToRender, setItemsToRender] = useState({ - firstRow: [], - remainingItems: [], - }); - - const getItemsToRender = useCallback(() => { - if (categoryPage) { - const firstRow = - window.innerWidth > 758 && data.length > 4 ? data.slice(0, 4) : []; - const remainingItems = - window.innerWidth > 758 && data.length > 4 - ? data.slice(4) - : data.slice(0); - return { firstRow, remainingItems }; + const [itemsToRender, setItemsToRender] = useState(() => { + if (categoryPage && window.innerWidth > 758 && displayData.length > 4) { + return { firstRow: displayData.slice(0, 4), remainingItems: displayData.slice(4) }; } - return { firstRow: [], remainingItems: data.slice(0) }; - }, [categoryPage, data]); + return { firstRow: [], remainingItems: displayData }; + }); useEffect(() => { const handleResize = () => { - setItemsToRender(getItemsToRender()); - }; - const newItems = getItemsToRender(); - setItemsToRender((prev) => { - if ( - JSON.stringify(prev.firstRow) !== JSON.stringify(newItems.firstRow) || - JSON.stringify(prev.remainingItems) !== - JSON.stringify(newItems.remainingItems) - ) { - return newItems; + if (categoryPage && window.innerWidth > 758 && displayData.length > 4) { + setItemsToRender({ firstRow: displayData.slice(0, 4), remainingItems: displayData.slice(4) }); + } else { + setItemsToRender({ firstRow: [], remainingItems: displayData }); } - return prev; - }); + }; window.addEventListener("resize", handleResize); - return () => { - window.removeEventListener("resize", handleResize); - }; - }, [getItemsToRender]); + handleResize(); // Initial call to sync state + + return () => window.removeEventListener("resize", handleResize); + }, [categoryPage, displayData]); return (
@@ -89,216 +161,29 @@ const CategoryCard = React.memo( )}
<> - {categoryPage && ( -
0 - ? "mt-8 max-[758px]:hidden" - : "" - }`} - > + {categoryPage && itemsToRender.firstRow.length > 0 && ( +
{itemsToRender.firstRow.map((item, index) => ( -
-
-
- navigate( - `${path === "top-upcoming" - ? `/${item.id}` - : `/watch/${item.id}` - }` - ) - } - > - {getSafeTitle(item.title, -
-
- -
-
-
- {(item.tvInfo?.rating === "18+" || - item?.adultContent === true) && ( -
- 18+ -
- )} -
-
- {item.tvInfo?.sub && ( -
- -

- {item.tvInfo.sub} -

-
- )} - {item.tvInfo?.dub && ( -
- -

- {item.tvInfo.dub} -

-
- )} - {item.tvInfo?.showType && ( -
- {item.tvInfo.showType.split(" ").shift()} -
- )} - {item.releaseDate && ( -
- {item.releaseDate} -
- )} - {!item.tvInfo?.showType && item.type && ( -
- {item.type} -
- )} - {(item.tvInfo?.duration || item.duration) && ( -
- {item.tvInfo?.duration === "m" || - item.tvInfo?.duration === "?" || - item.duration === "m" || - item.duration === "?" - ? "N/A" - : item.tvInfo?.duration || item.duration || "N/A"} -
- )} -
-
-
- - {getSafeTitle(item.title, language, item.japanese_title)} - - {item.description && ( -
- {item.description} -
- )} -
+ item={item} + navigate={navigate} + path={path} + language={language} + isFirstRow={true} + /> ))}
)}
{itemsToRender.remainingItems.map((item, index) => ( -
-
-
- navigate( - `${path === "top-upcoming" - ? `/${item.id}` - : `/watch/${item.id}` - }` - ) - } - > - {getSafeTitle(item.title, -
-
- -
-
-
- {(item.tvInfo?.rating === "18+" || - item?.adultContent === true) && ( -
- 18+ -
- )} -
-
- {item.tvInfo?.sub && ( -
- -

- {item.tvInfo.sub} -

-
- )} - {item.tvInfo?.dub && ( -
- -

- {item.tvInfo.dub} -

-
- )} - {item.tvInfo?.showType && ( -
- {item.tvInfo.showType.split(" ").shift()} -
- )} - {item.releaseDate && ( -
- {item.releaseDate} -
- )} - {!item.tvInfo?.showType && item.type && ( -
- {item.type} -
- )} - {(item.tvInfo?.duration || item.duration) && ( -
- {item.tvInfo?.duration === "m" || - item.tvInfo?.duration === "?" || - item.duration === "m" || - item.duration === "?" - ? "N/A" - : item.tvInfo?.duration || item.duration || "N/A"} -
- )} -
-
-
- - {getSafeTitle(item.title, language, item.japanese_title)} - -
+ item={item} + navigate={navigate} + path={path} + language={language} + /> ))}
diff --git a/src/components/continue/ContinueWatching.jsx b/src/components/continue/ContinueWatching.jsx index 53fbe3e..1f3f457 100644 --- a/src/components/continue/ContinueWatching.jsx +++ b/src/components/continue/ContinueWatching.jsx @@ -18,22 +18,20 @@ const ContinueWatching = () => { useEffect(() => { const data = JSON.parse(localStorage.getItem("continueWatching") || "[]"); - setWatchList(data); + setWatchList(data.reverse()); }, []); - const memoizedWatchList = useMemo(() => watchList, [watchList]); - const removeFromWatchList = (episodeId) => { setWatchList((prevList) => { const updatedList = prevList.filter( (item) => item.episodeId !== episodeId ); - localStorage.setItem("continueWatching", JSON.stringify(updatedList)); + localStorage.setItem("continueWatching", JSON.stringify([...updatedList].reverse())); return updatedList; }); }; - if (memoizedWatchList.length === 0) return null; + if (watchList.length === 0) return null; return (
@@ -74,7 +72,7 @@ const ContinueWatching = () => { prevEl: ".continue-btn-prev", }} > - {memoizedWatchList.slice().reverse().map((item, index) => ( + {watchList.map((item, index) => ( { if (scrollContainerRef.current) { - const scrollAmount = direction === 'left' ? -300 : 300; scrollContainerRef.current.scrollBy({ - left: scrollAmount, + left: direction === 'left' ? -300 : 300, behavior: 'smooth' }); } }; - // Instant scroll on mount without animation useEffect(() => { if (scrollContainerRef.current) { - // Direct manipulation of scrollLeft for instant scroll scrollContainerRef.current.scrollLeft = 300; } }, []); diff --git a/src/components/schedule/Schedule.jsx b/src/components/schedule/Schedule.jsx index ad50748..8ab839f 100644 --- a/src/components/schedule/Schedule.jsx +++ b/src/components/schedule/Schedule.jsx @@ -24,39 +24,28 @@ const Schedule = () => { const month = currentDate.getMonth(); const monthName = currentDate.toLocaleString("default", { month: "short" }); const daysInMonth = new Date(year, month + 1, 0).getDate(); - const GMTOffset = `GMT ${ - new Date().getTimezoneOffset() > 0 ? "-" : "+" - }${String(Math.floor(Math.abs(new Date().getTimezoneOffset()) / 60)).padStart( - 2, - "0" - )}:${String(Math.abs(new Date().getTimezoneOffset()) % 60).padStart(2, "0")}`; - const months = []; + const GMTOffset = `GMT ${new Date().getTimezoneOffset() <= 0 ? "+" : "-"}${String(Math.floor(Math.abs(new Date().getTimezoneOffset()) / 60)).padStart(2, "0")}:${String(Math.abs(new Date().getTimezoneOffset()) % 60).padStart(2, "0")}`; useEffect(() => { + const monthsArr = []; for (let day = 1; day <= daysInMonth; day++) { const date = new Date(year, month, day); - const dayname = date.toLocaleString("default", { weekday: "short" }); - const yearr = date.getFullYear(); - const monthh = String(date.getMonth() + 1).padStart(2, "0"); - const dayy = String(date.getDate()).padStart(2, "0"); - const fulldate = `${yearr}-${monthh}-${dayy}`; - months.push({ day, monthName, dayname, fulldate }); + monthsArr.push({ + day, + monthName: date.toLocaleString("default", { month: "short" }), + dayname: date.toLocaleString("default", { weekday: "short" }), + fulldate: date.toISOString().split('T')[0] + }); } - setDates(months); - const timer = setInterval(() => { - setCurrentTime(new Date()); - }, 1000); + setDates(monthsArr); + + const timer = setInterval(() => setCurrentTime(new Date()), 1000); return () => clearInterval(timer); - }, []); + }, [year, month, daysInMonth]); useEffect(() => { - const todayIndex = dates.findIndex( - (date) => - date.fulldate === - `${currentDate.getFullYear()}-${String( - currentDate.getMonth() + 1 - ).padStart(2, "0")}-${String(currentDate.getDate()).padStart(2, "0")}` - ); + const today = new Date().toISOString().split('T')[0]; + const todayIndex = dates.findIndex((date) => date.fulldate === today); if (todayIndex !== -1) { setCurrentActiveIndex(todayIndex); @@ -150,21 +139,19 @@ const Schedule = () => {
(cardRefs.current[index] = el)} onClick={() => toggleActive(index)} - className={`h-[60px] flex flex-col justify-center items-center w-full text-center rounded-lg cursor-pointer transition-all duration-200 ${ - currentActiveIndex === index - ? "bg-white text-black" - : "bg-zinc-800 text-white hover:bg-zinc-700" - }`} + className={`h-[60px] flex flex-col justify-center items-center w-full text-center rounded-lg cursor-pointer transition-all duration-200 ${currentActiveIndex === index + ? "bg-white text-black" + : "bg-zinc-800 text-white hover:bg-zinc-700" + }`} >
{date.dayname}
{date.monthName} {date.day}
@@ -197,8 +184,8 @@ const Schedule = () => { {(showAll ? scheduleData : Array.isArray(scheduleData) - ? scheduleData.slice(0, 7) - : [] + ? scheduleData.slice(0, 7) + : [] ).map((item, idx) => ( 1400 ? 10 : 12); + const [itemLimit, setItemLimit] = useState(() => (window.innerWidth > 1400 ? 10 : 12)); useEffect(() => { - const handleResize = () => { - setItemLimit(window.innerWidth > 1400 ? 10 : 12); - }; - + const handleResize = () => setItemLimit(window.innerWidth > 1400 ? 10 : 12); window.addEventListener("resize", handleResize); return () => window.removeEventListener("resize", handleResize); }, []); From 3f915e8b176cb74b51a8a985e6368de4c014a3e9 Mon Sep 17 00:00:00 2001 From: Tejas Panchal Date: Thu, 19 Feb 2026 02:15:28 +0530 Subject: [PATCH 4/8] search + category card --- src/components/categorycard/CategoryCard.jsx | 7 ++++--- src/components/continue/ContinueWatching.jsx | 2 +- src/components/topten/Topten.jsx | 2 +- src/components/trending/Trending.jsx | 4 ++-- src/pages/search/Search.jsx | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/components/categorycard/CategoryCard.jsx b/src/components/categorycard/CategoryCard.jsx index 24f87da..de020be 100644 --- a/src/components/categorycard/CategoryCard.jsx +++ b/src/components/categorycard/CategoryCard.jsx @@ -113,6 +113,7 @@ const CategoryCard = React.memo( cardStyle, path, limit, + gridClass, }) => { const { language } = useLanguage(); const navigate = useNavigate(); @@ -143,7 +144,7 @@ const CategoryCard = React.memo( return (
-
+

{label}

@@ -162,7 +163,7 @@ const CategoryCard = React.memo(
<> {categoryPage && itemsToRender.firstRow.length > 0 && ( -
+
{itemsToRender.firstRow.map((item, index) => ( )} -
+
{itemsToRender.remainingItems.map((item, index) => ( { return (
-
+

diff --git a/src/components/topten/Topten.jsx b/src/components/topten/Topten.jsx index 22cf745..30ccfce 100644 --- a/src/components/topten/Topten.jsx +++ b/src/components/topten/Topten.jsx @@ -50,7 +50,7 @@ function Topten({ data, className }) { }; return ( -
+

Top 10

    diff --git a/src/components/trending/Trending.jsx b/src/components/trending/Trending.jsx index e400ce1..9cf2dd3 100644 --- a/src/components/trending/Trending.jsx +++ b/src/components/trending/Trending.jsx @@ -13,11 +13,11 @@ const Trending = ({ trending, className }) => { return (
    -
    +

    Trending Now

    -
    +
    {trending && trending.map((item, index) => (
    diff --git a/src/pages/search/Search.jsx b/src/pages/search/Search.jsx index ce185c9..5df8546 100644 --- a/src/pages/search/Search.jsx +++ b/src/pages/search/Search.jsx @@ -43,7 +43,7 @@ function Search() { setSearchParams({ keyword, page: newPage }); }; - const searchGridClass = "grid-cols-8 max-[1600px]:grid-cols-6 max-[1200px]:grid-cols-4 max-[758px]:grid-cols-3 max-[478px]:grid-cols-3 max-[478px]:gap-x-2"; + const searchGridClass = "grid-cols-6 max-[1200px]:grid-cols-4 max-[758px]:grid-cols-3 max-[478px]:gap-x-2"; const { title, description, keywords } = generateSearchMeta(keyword); const canonicalUrl = generateCanonicalUrl(`/search?keyword=${keyword || ''}${page > 1 ? `&page=${page}` : ''}`); From b9156d9c773e350b9e67aabf131eca77c20a45af Mon Sep 17 00:00:00 2001 From: Tejas Panchal Date: Thu, 19 Feb 2026 02:25:03 +0530 Subject: [PATCH 5/8] info --- src/pages/animeInfo/AnimeInfo.jsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/pages/animeInfo/AnimeInfo.jsx b/src/pages/animeInfo/AnimeInfo.jsx index 9f004bc..e10e220 100644 --- a/src/pages/animeInfo/AnimeInfo.jsx +++ b/src/pages/animeInfo/AnimeInfo.jsx @@ -196,12 +196,12 @@ function AnimeInfo({ random = false }) {
    -
    +
    {/* Main Content */} -
    +
    {/* Mobile Layout */} -
    +
    {/* Poster Section */}
    @@ -490,7 +490,7 @@ function AnimeInfo({ random = false }) { {/* Seasons Section */} {seasons?.length > 0 && ( -
    +

    More Seasons

    {seasons.map((season, index) => ( @@ -540,19 +540,20 @@ function AnimeInfo({ random = false }) { {/* Voice Actors Section */} {animeInfo?.charactersVoiceActors.length > 0 && ( -
    +
    )} {/* Recommendations Section */} {animeInfo.recommended_data.length > 0 && ( -
    +
    )} From 8732e1e9d383613dd5024b9e2d74c9e4fd111896 Mon Sep 17 00:00:00 2001 From: Tejas Panchal Date: Thu, 19 Feb 2026 02:29:42 +0530 Subject: [PATCH 6/8] wach --- src/pages/watch/Watch.jsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/pages/watch/Watch.jsx b/src/pages/watch/Watch.jsx index 4d88684..3f637ae 100644 --- a/src/pages/watch/Watch.jsx +++ b/src/pages/watch/Watch.jsx @@ -277,7 +277,7 @@ export default function Watch() { )}
    -
    +
    {/* Left Column - Player, Controls, Servers */}
    @@ -613,7 +613,11 @@ export default function Watch() {
    {/* Related Anime Section */} - {animeInfo && animeInfo.related_data ? ( + {animeInfoLoading ? ( +
    + +
    + ) : animeInfo?.related_data?.length > 0 && (

    Related Anime

    - ) : ( -
    - -
    )}
    {/* Mobile-only Related Section */} - {animeInfo && animeInfo.related_data && ( + {!animeInfoLoading && animeInfo?.related_data?.length > 0 && (

    Related Anime

    Date: Thu, 19 Feb 2026 03:01:34 +0530 Subject: [PATCH 7/8] resolved info and watch --- src/components/ui/InfoTag/InfoTag.jsx | 18 + src/pages/animeInfo/AnimeInfo.jsx | 692 ++++++++++---------------- src/pages/watch/Watch.jsx | 653 ++++++++---------------- 3 files changed, 477 insertions(+), 886 deletions(-) create mode 100644 src/components/ui/InfoTag/InfoTag.jsx diff --git a/src/components/ui/InfoTag/InfoTag.jsx b/src/components/ui/InfoTag/InfoTag.jsx new file mode 100644 index 0000000..99ac2f6 --- /dev/null +++ b/src/components/ui/InfoTag/InfoTag.jsx @@ -0,0 +1,18 @@ +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import React from "react"; + +const InfoTag = ({ icon, text, bgColor, className = "" }) => { + if (!text) return null; + + return ( +
    + {icon && } +

    {text}

    +
    + ); +}; + +export default React.memo(InfoTag); diff --git a/src/pages/animeInfo/AnimeInfo.jsx b/src/pages/animeInfo/AnimeInfo.jsx index e10e220..5d217a6 100644 --- a/src/pages/animeInfo/AnimeInfo.jsx +++ b/src/pages/animeInfo/AnimeInfo.jsx @@ -5,18 +5,16 @@ import { faClosedCaptioning, faMicrophone, } from "@fortawesome/free-solid-svg-icons"; -import { useEffect, useState } from "react"; +import { useEffect, useState, useMemo } from "react"; import { Link, useNavigate, useParams } from "react-router-dom"; -import website_name from "@/src/config/website"; import CategoryCard from "@/src/components/categorycard/CategoryCard"; -import Sidecard from "@/src/components/sidecard/Sidecard"; import Loader from "@/src/components/Loader/Loader"; import Error from "@/src/components/error/Error"; import { useLanguage } from "@/src/context/LanguageContext"; -import { useHomeInfo } from "@/src/context/HomeInfoContext"; import Voiceactor from "@/src/components/voiceactor/Voiceactor"; import getSafeTitle from "@/src/utils/getSafetitle"; import { Helmet } from 'react-helmet-async'; +import InfoTag from "@/src/components/ui/InfoTag/InfoTag"; import { generateDescription, generateKeywords, @@ -27,536 +25,356 @@ import { optimizeTitle, } from '@/src/utils/seo.utils'; -function InfoItem({ label, value, isProducer = true }) { - return ( - value && ( -
    - {`${label}: `} - - {Array.isArray(value) ? ( - value.map((item, index) => - isProducer ? ( - :;,.?/\\|{}[\]`~*_]/g, "") - .split(" ") - .join("-") - .replace(/-+/g, "-")}`} - key={index} - className="cursor-pointer transition-colors duration-300 hover:text-gray-300" - > - {item} - {index < value.length - 1 && ", "} - - ) : ( - - {item} - {index < value.length - 1 && ", "} - - ) - ) - ) : isProducer ? ( +const InfoItem = ({ label, value, isProducer = true }) => { + if (!value) return null; + + const renderValue = () => { + if (Array.isArray(value)) { + return value.map((item, index) => ( + + {isProducer ? ( :;,.?/\\|{}[\]`~*_]/g, "") - .split(" ") - .join("-") - .replace(/-+/g, "-")}`} + to={`/producer/${item.replace(/[&'"^%$#@!()+=<>:;,.?/\\|{}[\]`~*_]/g, "").split(" ").join("-").replace(/-+/g, "-")}`} className="cursor-pointer transition-colors duration-300 hover:text-gray-300" > - {value} + {item} ) : ( - {value} + item )} + {index < value.length - 1 && ", "} -
    - ) - ); -} + )); + } + + if (isProducer) { + return ( + :;,.?/\\|{}[\]`~*_]/g, "").split(" ").join("-").replace(/-+/g, "-")}`} + className="cursor-pointer transition-colors duration-300 hover:text-gray-300" + > + {value} + + ); + } + + return value; + }; -function Tag({ bgColor, index, icon, text }) { return ( -
    - {icon && } -

    {text}

    +
    + {`${label}: `} + {renderValue()}
    ); -} +}; + +const Synopsis = ({ text, isFull, onToggle, isMobile = false }) => { + if (!text) return null; + const limit = isMobile ? 150 : 270; + const isTooLong = text.length > limit; + + return ( +
    + {isTooLong ? ( + <> + {isFull ? text : (isMobile ?
    {text}
    : `${text.slice(0, limit)}...`)} + + + ) : text} +
    + ); +}; + +const TagsList = ({ tags }) => ( +
    + {tags.map((tag, index) => tag.condition && ( + + ))} +
    +); + +const DetailGrid = ({ info, isMobile = false }) => { + const items = [ + { label: "Japanese", value: info?.Japanese }, + { label: "Synonyms", value: info?.Synonyms }, + { label: "Aired", value: info?.Aired }, + { label: "Premiered", value: info?.Premiered }, + { label: "Duration", value: info?.Duration }, + { label: "Status", value: info?.Status }, + { label: "MAL Score", value: info?.["MAL Score"] }, + ]; + + return ( +
    + {items.map((item, index) => ( + + ))} + + {info?.Genres && ( +
    +

    Genres

    +
    + {info.Genres.map((genre, index) => ( + + {genre} + + ))} +
    +
    + )} + +
    + + +
    +
    + ); +}; function AnimeInfo({ random = false }) { const { language } = useLanguage(); const { id: paramId } = useParams(); const id = random ? null : paramId; + const { id: currentId } = useParams(); + const navigate = useNavigate(); + const [isFull, setIsFull] = useState(false); const [animeInfo, setAnimeInfo] = useState(null); const [seasons, setSeasons] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); - const { homeInfo } = useHomeInfo(); - const { id: currentId } = useParams(); - const navigate = useNavigate(); + useEffect(() => { - if (id === "404-not-found-page") { - console.log("404 got!"); - return null; - } else { - const fetchAnimeInfo = async () => { - setLoading(true); - try { - const data = await getAnimeInfo(id, random); - setSeasons(data?.seasons); - setAnimeInfo(data.data); - } catch (err) { - console.error("Error fetching anime info:", err); - setError(err); - } finally { - setLoading(false); - } - }; - fetchAnimeInfo(); - window.scrollTo({ top: 0, behavior: "smooth" }); - } + if (id === "404-not-found-page") return; + + const fetchAnimeInfo = async () => { + setLoading(true); + try { + const data = await getAnimeInfo(id, random); + if (!data?.data) throw new Error("Anime not found"); + setSeasons(data?.seasons); + setAnimeInfo(data.data); + } catch (err) { + console.error("Error fetching anime info:", err); + setError(err); + } finally { + setLoading(false); + } + }; + fetchAnimeInfo(); + window.scrollTo({ top: 0, behavior: "smooth" }); }, [id, random]); + + const seoData = useMemo(() => { + if (!animeInfo) return null; + const { title, japanese_title, poster, animeInfo: info } = animeInfo; + const safeTitle = getSafeTitle(title, language, japanese_title); + return { + safeTitle, + title: optimizeTitle(`Watch ${safeTitle} Sub Dub Online Free`), + description: generateDescription(info?.Overview), + keywords: generateKeywords(animeInfo), + canonical: generateCanonicalUrl(`/${animeInfo.id}`), + ogImage: generateOGImage(poster), + structured: generateAnimeStructuredData(animeInfo), + breadcrumb: generateBreadcrumbStructuredData([ + { name: 'Home', url: '/' }, + { name: animeInfo.title, url: `/${animeInfo.id}` } + ]) + }; + }, [animeInfo, language]); + + const tags = useMemo(() => { + if (!animeInfo?.animeInfo?.tvInfo) return []; + const info = animeInfo.animeInfo; + return [ + { condition: info.tvInfo.rating, text: info.tvInfo.rating, bgColor: "#ffffff" }, + { condition: info.tvInfo.quality, text: info.tvInfo.quality, bgColor: "#FFBADE" }, + { condition: info.tvInfo.sub, text: info.tvInfo.sub, icon: faClosedCaptioning, bgColor: "#B0E3AF" }, + { condition: info.tvInfo.dub, text: info.tvInfo.dub, icon: faMicrophone, bgColor: "#B9E7FF" }, + ]; + }, [animeInfo]); + if (loading) return ; - if (error) { - return ; - } + if (error || (!animeInfo && !loading)) return ; if (!animeInfo) { navigate("/404-not-found-page"); - return undefined; + return null; } - const { title, japanese_title, poster, animeInfo: info } = animeInfo; - const safeTitle = getSafeTitle(title, language, japanese_title); - const displayTitle = safeTitle; // Use safeTitle as the display title - const pageTitle = optimizeTitle(`Watch ${displayTitle} Sub Dub Online Free`); - const pageDescription = generateDescription(info?.Overview); - const pageKeywords = generateKeywords(animeInfo); - const canonicalUrl = generateCanonicalUrl(`/${animeInfo.id}`); - const ogImage = generateOGImage(poster); - - const animeStructuredData = generateAnimeStructuredData(animeInfo); - const breadcrumbData = generateBreadcrumbStructuredData([ - { name: 'Home', url: '/' }, - { name: animeInfo.title, url: `/${animeInfo.id}` } - ]); - - const tags = [ - { - condition: info.tvInfo?.rating, - bgColor: "#ffffff", - text: info.tvInfo.rating, - }, - { - condition: info.tvInfo?.quality, - bgColor: "#FFBADE", - text: info.tvInfo.quality, - }, - { - condition: info.tvInfo?.sub, - icon: faClosedCaptioning, - bgColor: "#B0E3AF", - text: info.tvInfo.sub, - }, - { - condition: info.tvInfo?.dub, - icon: faMicrophone, - bgColor: "#B9E7FF", - text: info.tvInfo.dub, - }, - ]; + const { poster, japanese_title, animeInfo: info } = animeInfo; + const isAiring = animeInfo?.animeInfo?.Status?.toLowerCase() !== "not-yet-aired"; return ( <> - {pageTitle} - - - - - - - - + {seoData.title} + + + + + + + - - - - - - - + + + + + +
    +
    - {/* Main Content */} -
    {/* Mobile Layout */}
    - {/* Poster Section */}
    -
    - {`${safeTitle} +
    + {seoData.safeTitle} {animeInfo.adultContent && ( -
    - 18+ -
    +
    18+
    )}
    - {/* Basic Info Section */}
    - {/* Title */}
    -

    - {safeTitle} -

    +

    {seoData.safeTitle}

    {language === "EN" && japanese_title && ( -

    JP Title: {japanese_title}

    +

    JP: {japanese_title}

    )}
    - - {/* Tags */} -
    - {tags.map(({ condition, icon, text }, index) => - condition && ( - - ) - )} -
    - - {/* Overview - Limited for mobile */} - {info?.Overview && ( -
    - {info.Overview.length > 150 ? ( - <> - {isFull ? ( - info.Overview - ) : ( -
    {info.Overview}
    - )} - - - ) : ( - info.Overview - )} -
    - )} + + setIsFull(!isFull)} isMobile />
    - {/* Watch Button - Full Width on Mobile */}
    - {animeInfo?.animeInfo?.Status?.toLowerCase() !== "not-yet-aired" ? ( - - + {isAiring ? ( + + Watch Now ) : ( -
    - Not released +
    + Not yet released
    )}
    - {/* Details Section - Full Width on Mobile */} -
    -
    - {[ - { label: "Japanese", value: info?.Japanese }, - { label: "Synonyms", value: info?.Synonyms }, - { label: "Aired", value: info?.Aired }, - { label: "Premiered", value: info?.Premiered }, - { label: "Duration", value: info?.Duration }, - { label: "Status", value: info?.Status }, - { label: "MAL Score", value: info?.["MAL Score"] }, - ].map((item, index) => ( - - ))} -
    - - {/* Genres */} - {info?.Genres && ( -
    -

    Genres

    -
    - {info.Genres.map((genre, index) => ( - - {genre} - - ))} -
    -
    - )} - - {/* Studios & Producers */} -
    - {[ - { label: "Studios", value: info?.Studios }, - { label: "Producers", value: info?.Producers }, - ].map((item, index) => ( - - ))} -
    +
    +
    - {/* Desktop Layout - Existing Code */} + {/* Desktop Layout */}
    - {/* Poster Section */}
    -
    - {`${safeTitle} +
    + {seoData.safeTitle} {animeInfo.adultContent && ( -
    - 18+ -
    +
    18+
    )}
    - {/* Info Section */}
    - {/* Title */}
    -

    - {safeTitle} -

    +

    {seoData.safeTitle}

    {language === "EN" && japanese_title && (

    JP Title: {japanese_title}

    )}
    - {/* Tags */} -
    - {tags.map(({ condition, icon, text }, index) => - condition && ( - - ) - )} -
    + + setIsFull(!isFull)} /> - {/* Overview */} - {info?.Overview && ( -
    - {info.Overview.length > 270 ? ( - <> - {isFull - ? info.Overview - : `${info.Overview.slice(0, 270)}...`} - - - ) : ( - info.Overview - )} -
    - )} - - {/* Watch Button */} - {animeInfo?.animeInfo?.Status?.toLowerCase() !== "not-yet-aired" ? ( - - + {isAiring ? ( + + Watch Now ) : ( -
    - Not released +
    + Not yet released
    )} - {/* Details Section */} -
    -
    - {[ - { label: "Japanese", value: info?.Japanese }, - { label: "Synonyms", value: info?.Synonyms }, - { label: "Aired", value: info?.Aired }, - { label: "Premiered", value: info?.Premiered }, - { label: "Duration", value: info?.Duration }, - { label: "Status", value: info?.Status }, - { label: "MAL Score", value: info?.["MAL Score"] }, - ].map((item, index) => ( - - ))} -
    - - {/* Genres */} - {info?.Genres && ( -
    -

    Genres

    -
    - {info.Genres.map((genre, index) => ( - - {genre} - - ))} -
    -
    - )} - - {/* Studios & Producers */} -
    - {[ - { label: "Studios", value: info?.Studios }, - { label: "Producers", value: info?.Producers }, - ].map((item, index) => ( - - ))} -
    -
    +
    - {/* Seasons Section */} - {seasons?.length > 0 && ( -
    -

    More Seasons

    -
    - {seasons.map((season, index) => ( - - {season.season} - {/* Dots Pattern Overlay */} -
    ')`, - backgroundSize: '3px 3px' - }} - /> - {/* Dark Gradient Overlay */} -
    - {/* Title Container */} -
    -

    - {season.season} -

    -
    - - ))} + {/* Sections */} +
    + {seasons?.length > 0 && ( +
    +

    More Seasons

    +
    + {seasons.map((season, index) => ( + + {season.season} +
    ')`, + backgroundSize: '3px 3px' + }} + /> +
    +
    +

    {season.season}

    +
    + + ))} +
    -
    - )} + )} - {/* Voice Actors Section */} - {animeInfo?.charactersVoiceActors.length > 0 && ( -
    - -
    - )} + {animeInfo?.charactersVoiceActors?.length > 0 && ( +
    + +
    + )} - {/* Recommendations Section */} - {animeInfo.recommended_data.length > 0 && ( -
    - -
    - )} + {animeInfo?.recommended_data?.length > 0 && ( +
    + +
    + )} +
    ); diff --git a/src/pages/watch/Watch.jsx b/src/pages/watch/Watch.jsx index 3f637ae..92ffc59 100644 --- a/src/pages/watch/Watch.jsx +++ b/src/pages/watch/Watch.jsx @@ -1,8 +1,7 @@ /* eslint-disable react/prop-types */ -import { useEffect, useRef, useState } from "react"; +import { useEffect, useRef, useState, useMemo, useCallback } from "react"; import { useLocation, useParams, Link, useNavigate } from "react-router-dom"; import { useLanguage } from "@/src/context/LanguageContext"; -import { useHomeInfo } from "@/src/context/HomeInfoContext"; import { useWatch } from "@/src/hooks/useWatch"; import BouncingLoader from "@/src/components/ui/bouncingloader/Bouncingloader"; import IframePlayer from "@/src/components/player/IframePlayer"; @@ -12,6 +11,7 @@ import Sidecard from "@/src/components/sidecard/Sidecard"; import { faClosedCaptioning, faMicrophone, + faPlay, } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import Servers from "@/src/components/servers/Servers"; @@ -22,6 +22,7 @@ import useWatchControl from "@/src/hooks/useWatchControl"; import Player from "@/src/components/player/Player"; import getSafeTitle from "@/src/utils/getSafetitle"; import { Helmet } from 'react-helmet-async'; +import InfoTag from "@/src/components/ui/InfoTag/InfoTag"; import { generateDescription, generateKeywords, @@ -39,13 +40,11 @@ export default function Watch() { const { id: animeId } = useParams(); const queryParams = new URLSearchParams(location.search); let initialEpisodeId = queryParams.get("ep"); - const [tags, setTags] = useState([]); const { language } = useLanguage(); - const { homeInfo } = useHomeInfo(); const isFirstSet = useRef(true); const [showNextEpisodeSchedule, setShowNextEpisodeSchedule] = useState(true); + const { - // error, buffering, streamInfo, streamUrl, @@ -61,7 +60,6 @@ export default function Watch() { thumbnail, setIsFullOverview, activeEpisodeNum, - seasons, episodeId, setEpisodeId, activeServerId, @@ -71,8 +69,10 @@ export default function Watch() { activeServerType, setActiveServerType, activeServerName, - setActiveServerName + setActiveServerName, + seasons } = useWatch(animeId, initialEpisodeId); + const { autoPlay, setAutoPlay, @@ -81,511 +81,280 @@ export default function Watch() { autoNext, setAutoNext, } = useWatchControl(); - const playerRef = useRef(null); + const videoContainerRef = useRef(null); const controlsRef = useRef(null); const episodesRef = useRef(null); + // Sync URL with episodeId useEffect(() => { - if (!episodes || episodes.length === 0) return; + if (!episodes?.length) return; - const isValidEpisode = episodes.some(ep => { - const epNumber = ep.id.split('ep=')[1]; - return epNumber === episodeId; - }); + const currentEpNum = episodeId; + const isValidEpisode = episodes.some(ep => ep.id.split('ep=')[1] === currentEpNum); - // If missing or invalid episodeId, fallback to first - if (!episodeId || !isValidEpisode) { + if (!currentEpNum || !isValidEpisode) { const fallbackId = episodes[0].id.match(/ep=(\d+)/)?.[1]; - if (fallbackId && fallbackId !== episodeId) { - setEpisodeId(fallbackId); - } + if (fallbackId && fallbackId !== currentEpNum) setEpisodeId(fallbackId); return; } - const newUrl = `/watch/${animeId}?ep=${episodeId}`; + const newUrl = `/watch/${animeId}?ep=${currentEpNum}`; if (isFirstSet.current) { navigate(newUrl, { replace: true }); isFirstSet.current = false; } else { navigate(newUrl); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [episodeId, animeId, navigate, episodes]); - - - - // ... inside Watch component ... - // Update document title + }, [episodeId, animeId, navigate, episodes, setEpisodeId]); // Redirect if no episodes useEffect(() => { - if (totalEpisodes !== null && totalEpisodes === 0) { - navigate(`/${animeId}`); + if (totalEpisodes === 0) navigate(`/${animeId}`); + }, [animeId, totalEpisodes, navigate]); + + // Height adjustment logic + const adjustHeight = useCallback(() => { + if (window.innerWidth > 1200) { + if (videoContainerRef.current && controlsRef.current && episodesRef.current) { + const totalHeight = videoContainerRef.current.offsetHeight + controlsRef.current.offsetHeight; + episodesRef.current.style.height = `${totalHeight}px`; + } + } else if (episodesRef.current) { + episodesRef.current.style.height = 'auto'; } - }, [streamInfo, episodeId, animeId, totalEpisodes, navigate]); + }, []); useEffect(() => { - // Function to adjust the height of episodes list to match only video + controls - const adjustHeight = () => { - if (window.innerWidth > 1200) { - if (videoContainerRef.current && controlsRef.current && episodesRef.current) { - // Calculate combined height of video container and controls - const videoHeight = videoContainerRef.current.offsetHeight; - const controlsHeight = controlsRef.current.offsetHeight; - const totalHeight = videoHeight + controlsHeight; + const resizeObserver = new ResizeObserver(adjustHeight); - // Apply the combined height to episodes container - episodesRef.current.style.height = `${totalHeight}px`; - } - } else { - if (episodesRef.current) { - episodesRef.current.style.height = 'auto'; - } - } - }; + if (videoContainerRef.current) resizeObserver.observe(videoContainerRef.current); + if (controlsRef.current) resizeObserver.observe(controlsRef.current); - // Initial adjustment with delay to ensure player is fully rendered - const initialTimer = setTimeout(() => { - adjustHeight(); - }, 500); - - // Set up resize listener window.addEventListener('resize', adjustHeight); + adjustHeight(); - // Create MutationObserver to monitor player changes - const observer = new MutationObserver(() => { - setTimeout(adjustHeight, 100); - }); - - // Start observing both video container and controls - if (videoContainerRef.current) { - observer.observe(videoContainerRef.current, { - attributes: true, - childList: true, - subtree: true - }); - } - - if (controlsRef.current) { - observer.observe(controlsRef.current, { - attributes: true, - childList: true, - subtree: true - }); - } - - // Set up additional interval for continuous adjustments - const intervalId = setInterval(adjustHeight, 1000); - - // Clean up return () => { - clearTimeout(initialTimer); - clearInterval(intervalId); - observer.disconnect(); + resizeObserver.disconnect(); window.removeEventListener('resize', adjustHeight); }; - }, [buffering, activeServerType, activeServerName, episodeId, streamUrl, episodes]); + }, [adjustHeight, buffering, animeInfoLoading]); - function Tag({ bgColor, index, icon, text }) { - return ( -
    - {icon && } -

    {text}

    -
    - ); - } + const seoData = useMemo(() => { + if (!animeInfo) return null; + const safeT = getSafeTitle(animeInfo.title, language, animeInfo.japanese_title); + return { + safeTitle: safeT, + pageTitle: optimizeTitle(`Watch ${safeT} Episode ${activeEpisodeNum} Sub Dub Online Free`), + pageDescription: generateDescription(`Stream ${safeT} Episode ${activeEpisodeNum} in HD with English Sub and Dub. ${animeInfo.animeInfo?.Overview}`), + pageKeywords: `${generateKeywords(animeInfo)}, episode ${activeEpisodeNum}`, + canonicalUrl: generateCanonicalUrl(`/watch/${animeId}?ep=${episodeId}`), + ogImage: generateOGImage(animeInfo.poster), + structured: generateAnimeStructuredData(animeInfo, { number: activeEpisodeNum, id: episodeId }), + videoStructured: generateVideoStructuredData(animeInfo, { number: activeEpisodeNum, id: episodeId }, streamUrl), + breadcrumb: generateBreadcrumbStructuredData([ + { name: 'Home', url: '/' }, + { name: animeInfo.title, url: `/${animeId}` }, + { name: `Episode ${activeEpisodeNum}`, url: `/watch/${animeId}?ep=${episodeId}` } + ]) + }; + }, [animeId, animeInfo, activeEpisodeNum, episodeId, language, streamUrl]); - useEffect(() => { - setTags([ - { - condition: animeInfo?.animeInfo?.tvInfo?.rating, - bgColor: "#ffffff", - text: animeInfo?.animeInfo?.tvInfo?.rating, - }, - { - condition: animeInfo?.animeInfo?.tvInfo?.quality, - bgColor: "#FFBADE", - text: animeInfo?.animeInfo?.tvInfo?.quality, - }, - { - condition: animeInfo?.animeInfo?.tvInfo?.sub, - icon: faClosedCaptioning, - bgColor: "#B0E3AF", - text: animeInfo?.animeInfo?.tvInfo?.sub, - }, - { - condition: animeInfo?.animeInfo?.tvInfo?.dub, - icon: faMicrophone, - bgColor: "#B9E7FF", - text: animeInfo?.animeInfo?.tvInfo?.dub, - }, - ]); - }, [animeId, animeInfo]); + const tags = useMemo(() => { + const info = animeInfo?.animeInfo?.tvInfo; + if (!info) return []; + return [ + { condition: info.rating, text: info.rating, bgColor: "#ffffff" }, + { condition: info.quality, text: info.quality, bgColor: "#FFBADE" }, + { condition: info.sub, text: info.sub, icon: faClosedCaptioning, bgColor: "#B0E3AF" }, + { condition: info.dub, text: info.dub, icon: faMicrophone, bgColor: "#B9E7FF" }, + ]; + }, [animeInfo]); - const safeTitle = animeInfo ? getSafeTitle(animeInfo.title, language, animeInfo.japanese_title) : ''; - const pageTitle = animeInfo ? optimizeTitle(`Watch ${safeTitle} Episode ${activeEpisodeNum} Sub Dub Online Free`) : `${website_name} | Free anime streaming platform`; - const pageDescription = animeInfo ? generateDescription(`Stream ${safeTitle} Episode ${activeEpisodeNum} in HD with English Sub and Dub. ${animeInfo.animeInfo?.Overview}`) : ''; - const pageKeywords = animeInfo ? generateKeywords(animeInfo) + `, episode ${activeEpisodeNum}` : ''; - const canonicalUrl = generateCanonicalUrl(`/watch/${animeId}?ep=${episodeId}`); - const ogImage = animeInfo ? generateOGImage(animeInfo.poster) : ''; - - const animeStructuredData = animeInfo ? generateAnimeStructuredData(animeInfo, { number: activeEpisodeNum, id: episodeId }) : null; - const videoStructuredData = animeInfo ? generateVideoStructuredData(animeInfo, { number: activeEpisodeNum, id: episodeId }, streamUrl) : null; - const breadcrumbData = animeInfo ? generateBreadcrumbStructuredData([ - { name: 'Home', url: '/' }, - { name: animeInfo.title, url: `/${animeId}` }, - { name: `Episode ${activeEpisodeNum}`, url: `/watch/${animeId}?ep=${episodeId}` } - ]) : null; return ( <> - - {pageTitle} - - - + {seoData && ( + + {seoData.pageTitle} + + + + + + + + + + + + + + )} - - - - - - - - - - - - {animeStructuredData && ( - - )} - {videoStructuredData && ( - - )} - {breadcrumbData && ( - - )} -
    -
    -
    - {/* Left Column - Player, Controls, Servers */} +
    +
    + + {/* Left Column */}
    -
    - {/* Video Container */} +
    - {!buffering ? (["hd-1", "hd-4"].includes(activeServerName.toLowerCase()) ? - setEpisodeId(id)} - autoNext={autoNext} - /> : setEpisodeId(id)} - animeInfo={animeInfo} - episodeNum={activeEpisodeNum} - streamInfo={streamInfo} - /> + {!buffering ? ( + ["hd-1", "hd-4"].includes(activeServerName.toLowerCase()) ? ( + + ) : ( + + ) ) : (
    )} -

    - {!buffering && !activeServerType ? ( - servers ? ( - <> - Probably this server is down, try other servers -
    - Either reload or try again after sometime - - ) : ( - <> - Probably streaming server is down -
    - Either reload or try again after sometime - - ) - ) : null} -

    + {!buffering && !activeServerType && ( +
    +

    + Streaming server seems to be down. Please try another server or reload the page. +

    +
    + )}
    - {/* Controls Section */}
    {!buffering && (
    setEpisodeId(id)} + autoPlay={autoPlay} setAutoPlay={setAutoPlay} + autoSkipIntro={autoSkipIntro} setAutoSkipIntro={setAutoSkipIntro} + autoNext={autoNext} setAutoNext={setAutoNext} + episodes={episodes} totalEpisodes={totalEpisodes} + episodeId={episodeId} onButtonClick={setEpisodeId} />
    )} - {/* Title and Server Selection */}
    -
    - -
    +
    - {/* Next Episode Schedule */} {nextEpisodeSchedule?.nextEpisodeSchedule && showNextEpisodeSchedule && (
    -
    +
    - 🚀 -
    - Next episode estimated at - - {new Date( - new Date(nextEpisodeSchedule.nextEpisodeSchedule).getTime() - - new Date().getTimezoneOffset() * 60000 - ).toLocaleDateString("en-GB", { - day: "2-digit", - month: "2-digit", - year: "numeric", - hour: "2-digit", - minute: "2-digit", - second: "2-digit", - hour12: true, + 🚀 +
    + Next episode around: + + {new Date(nextEpisodeSchedule.nextEpisodeSchedule).toLocaleString("en-GB", { + day: "2-digit", month: "2-digit", year: "numeric", + hour: "2-digit", minute: "2-digit", hour12: true })}
    - +
    )}
    - {/* Mobile-only Seasons Section */} - {seasons?.length > 0 && ( -
    -

    More Seasons

    -
    - {seasons.map((season, index) => ( - - {season.season} - {/* Dots Pattern Overlay */} -
    ')`, - backgroundSize: '3px 3px' - }} - /> - {/* Dark Gradient Overlay */} -
    - {/* Title Container */} -
    -

    - {season.season} -

    -
    - - ))} + {/* Info Section */} +
    +
    +
    + {animeInfo ? ( + {seoData?.safeTitle} + ) : }
    -
    - )} - - {/* Mobile-only Episodes Section */} -
    -
    - {!episodes ? ( -
    - -
    - ) : ( - setEpisodeId(id)} - totalEpisodes={totalEpisodes} - /> - )} -
    -
    - - {/* Anime Info Section */} -
    -
    - {animeInfo && animeInfo?.poster ? ( - - ) : ( - - )} -
    - {animeInfo && animeInfo?.title ? ( - -

    - {getSafeTitle(animeInfo.title, language, animeInfo.japanese_title)} +
    + {animeInfo ? ( + +

    + {seoData?.safeTitle}

    -
    +
    View Details - - - +
    - ) : ( - - )} -
    - {animeInfo ? ( - tags.map( - ({ condition, icon, text }, index) => - condition && ( - - {icon && } - {text} - - ) - ) - ) : ( - - )} + ) : } + +
    + {tags.map((tag, idx) => tag.condition && ( + + ))}
    + {animeInfo?.animeInfo?.Overview && ( -

    - {animeInfo?.animeInfo?.Overview.length > 270 ? ( +

    + {animeInfo.animeInfo.Overview.length > 270 ? ( <> - {isFullOverview - ? animeInfo?.animeInfo?.Overview - : `${animeInfo?.animeInfo?.Overview.slice(0, 270)}...`} - - ) : ( - animeInfo?.animeInfo?.Overview - )} -

    + ) : animeInfo.animeInfo.Overview} +
    )}
    - {/* Desktop-only Seasons Section */} + {/* Seasons (Mobile only) */} {seasons?.length > 0 && ( -
    -

    More Seasons

    -
    - {seasons.map((season, index) => ( +
    +

    More Seasons

    +
    + {seasons.map((season, idx) => ( - {season.season} - {/* Dots Pattern Overlay */} -
    ')`, - backgroundSize: '3px 3px' - }} - /> - {/* Dark Gradient Overlay */} -
    - {/* Title Container */} -
    -

    - {season.season} -

    + {season.season} +
    +
    +

    {season.season}

    ))} @@ -594,48 +363,34 @@ export default function Watch() { )}
    - {/* Right Column - Episodes and Related (Desktop Only) */} -
    - {/* Episodes Section */} -
    + {/* Right Column (Desktop Only) */} +
    +
    {!episodes ? ( -
    - -
    +
    ) : ( setEpisodeId(id)} + onEpisodeClick={setEpisodeId} totalEpisodes={totalEpisodes} /> )}
    - {/* Related Anime Section */} - {animeInfoLoading ? ( -
    - -
    - ) : animeInfo?.related_data?.length > 0 && ( -
    -

    Related Anime

    - + {!animeInfoLoading && animeInfo?.related_data?.length > 0 && ( +
    +

    Related Anime

    +
    )}
    - {/* Mobile-only Related Section */} + {/* Related Anime (Mobile only) */} {!animeInfoLoading && animeInfo?.related_data?.length > 0 && ( -
    -

    Related Anime

    - +
    +

    Related Anime

    +
    )}
    From 9f91a9ae75f05487fc7e908308c988e5f24c1839 Mon Sep 17 00:00:00 2001 From: Tejas Panchal Date: Thu, 19 Feb 2026 11:57:30 +0530 Subject: [PATCH 8/8] episode container resolved --- src/pages/watch/Watch.jsx | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/pages/watch/Watch.jsx b/src/pages/watch/Watch.jsx index 92ffc59..3af6777 100644 --- a/src/pages/watch/Watch.jsx +++ b/src/pages/watch/Watch.jsx @@ -83,7 +83,7 @@ export default function Watch() { } = useWatchControl(); const videoContainerRef = useRef(null); - const controlsRef = useRef(null); + const playerRef = useRef(null); const episodesRef = useRef(null); // Sync URL with episodeId @@ -116,20 +116,20 @@ export default function Watch() { // Height adjustment logic const adjustHeight = useCallback(() => { if (window.innerWidth > 1200) { - if (videoContainerRef.current && controlsRef.current && episodesRef.current) { - const totalHeight = videoContainerRef.current.offsetHeight + controlsRef.current.offsetHeight; - episodesRef.current.style.height = `${totalHeight}px`; + if (playerRef.current && episodesRef.current) { + episodesRef.current.style.height = 'auto'; + episodesRef.current.style.maxHeight = `${playerRef.current.offsetHeight}px`; } } else if (episodesRef.current) { episodesRef.current.style.height = 'auto'; + episodesRef.current.style.maxHeight = 'none'; } }, []); useEffect(() => { const resizeObserver = new ResizeObserver(adjustHeight); - if (videoContainerRef.current) resizeObserver.observe(videoContainerRef.current); - if (controlsRef.current) resizeObserver.observe(controlsRef.current); + if (playerRef.current) resizeObserver.observe(playerRef.current); window.addEventListener('resize', adjustHeight); adjustHeight(); @@ -193,11 +193,11 @@ export default function Watch() {
    -
    +
    {/* Left Column */}
    -
    +
    {!buffering ? ( ["hd-1", "hd-4"].includes(activeServerName.toLowerCase()) ? ( @@ -246,7 +246,7 @@ export default function Watch() {
    {!buffering && ( -
    +
    -
    +
    {!episodes ? ( -
    +
    +
    ) : (