├── .env.example
├── .eslintrc.json
├── app
├── favicon.ico
├── not-found.js
├── loading.js
├── (home)
│ ├── layout.js
│ ├── page.js
│ ├── download
│ │ └── [id]
│ │ │ └── page.js
│ ├── watch
│ │ └── [id]
│ │ │ ├── watch.module.css
│ │ │ └── page.js
│ ├── info
│ │ └── [id]
│ │ │ ├── info.module.css
│ │ │ └── page.js
│ └── search
│ │ ├── search.module.css
│ │ └── page.js
├── globals.css
├── (auth)
│ └── login
│ │ ├── page.js
│ │ └── login.module.css
└── layout.js
├── components
├── ui
│ ├── banner
│ │ ├── videoPlay
│ │ │ ├── videoPlay.module.css
│ │ │ └── VideoPlay.jsx
│ │ ├── AnimeInfo
│ │ │ ├── AnimeInfo.jsx
│ │ │ └── AnimeInfo.module.css
│ │ ├── Banner.jsx
│ │ └── banner.module.css
│ ├── card
│ │ ├── loading.module.css
│ │ ├── Card.jsx
│ │ └── card.module.css
│ ├── recentReleaseCard
│ │ ├── loading.module.css
│ │ └── RecentReleasesCard.jsx
│ ├── AtoZalphabet
│ │ ├── AtoZalphabet.jsx
│ │ └── AtoZalphabet.module.css
│ └── episodeSelector
│ │ ├── episodeSelector.module.css
│ │ └── EpisodeSelector.jsx
└── layout
│ ├── navbar
│ ├── responsive
│ │ └── Tablet
│ │ │ ├── Tablet.jsx
│ │ │ └── Tablet.module.css
│ ├── Items
│ │ ├── Items.module.css
│ │ └── Items.jsx
│ ├── Navbar.jsx
│ ├── search
│ │ ├── Search.jsx
│ │ └── search.module.css
│ └── navbar.module.css
│ ├── footer
│ ├── items
│ │ ├── items.module.css
│ │ └── Items.jsx
│ ├── Footer.jsx
│ └── footer.module.css
│ ├── loading
│ ├── loading.module.css
│ └── Loading.jsx
│ └── error
│ ├── __Error.jsx
│ └── error.module.css
├── jsconfig.json
├── public
└── images
│ ├── cover
│ └── jjk.jpg
│ ├── logo
│ └── logo-2.png
│ ├── wallpapers
│ ├── naruto.webp
│ ├── one piece.jpg
│ └── demon slayer.jpg
│ └── banner
│ ├── LoginWallpaper.jpg
│ ├── Fearless and dark.jpeg
│ ├── Like a Pro Player.jpeg
│ ├── Stronger than ever.jpeg
│ └── Wallpaper-Solo-Leveling-Manga-Anime-Digital61.jpg
├── lib
├── CleanURL.js
├── FetchData.js
└── DB
│ └── AddWatched.js
├── content
├── HomePage
│ ├── Home
│ │ ├── slidingBanner
│ │ │ ├── slidingBanner.module.css
│ │ │ └── SlidingBanner.jsx
│ │ ├── discover
│ │ │ ├── options
│ │ │ │ ├── Options.jsx
│ │ │ │ └── options.module.css
│ │ │ ├── discover.module.css
│ │ │ └── Discover.jsx
│ │ └── recentRelease
│ │ │ ├── RecentRelease.jsx
│ │ │ └── recentRelease.module.css
│ ├── Watch
│ │ ├── left
│ │ │ ├── animeSeasons
│ │ │ │ ├── card
│ │ │ │ │ ├── SeasonSelectorCard.jsx
│ │ │ │ │ └── card.module.css
│ │ │ │ ├── animeSeasons.module.css
│ │ │ │ └── AnimeSeasons.jsx
│ │ │ ├── videoOption
│ │ │ │ ├── videoOption.module.css
│ │ │ │ └── VideoOption.jsx
│ │ │ ├── animeInfo
│ │ │ │ ├── AnimeInfo.jsx
│ │ │ │ └── animeInfo.module.css
│ │ │ ├── videoSelector
│ │ │ │ ├── videoSelector.module.css
│ │ │ │ └── VideoSelector.jsx
│ │ │ └── videoPlayer
│ │ │ │ ├── videoPlayer.module.css
│ │ │ │ └── VideoPlayer.jsx
│ │ └── right
│ │ │ ├── recommendation
│ │ │ └── Recommendation.jsx
│ │ │ └── mostPopular
│ │ │ ├── MostPopular.jsx
│ │ │ └── mostPopular.module.css
│ ├── Search
│ │ └── userSelection
│ │ │ ├── catalog
│ │ │ ├── Type
│ │ │ │ ├── status.module.css
│ │ │ │ └── Type.jsx
│ │ │ ├── season
│ │ │ │ ├── season.module.css
│ │ │ │ └── Season.jsx
│ │ │ ├── status
│ │ │ │ ├── status.module.css
│ │ │ │ └── Status.jsx
│ │ │ ├── genres
│ │ │ │ ├── genres.module.css
│ │ │ │ └── Genres.jsx
│ │ │ ├── catalog.module.css
│ │ │ ├── year
│ │ │ │ ├── year.module.css
│ │ │ │ └── Year.jsx
│ │ │ └── Catalog.jsx
│ │ │ ├── UserSelection.jsx
│ │ │ ├── options
│ │ │ ├── options.module.css
│ │ │ └── Option.jsx
│ │ │ └── userSelection.module.css
│ └── Info
│ │ └── animeInfo
│ │ ├── Info
│ │ ├── Info.module.css
│ │ └── Info.jsx
│ │ ├── AnimeInfo.jsx
│ │ └── animeInfo.module.css
└── AuthPage
│ └── login
│ └── left
│ ├── formField
│ ├── FormField.jsx
│ └── formField.module.css
│ ├── LeftContainer.jsx
│ └── left.module.css
├── .gitignore
├── next.config.mjs
├── package.json
└── README.md
/.env.example:
--------------------------------------------------------------------------------
1 | API_URL=Your api key goes here
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Amritanshu312/animeverse/HEAD/app/favicon.ico
--------------------------------------------------------------------------------
/components/ui/banner/videoPlay/videoPlay.module.css:
--------------------------------------------------------------------------------
1 | .videoBanner {
2 | object-fit: cover;
3 | }
4 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@/*": ["./*"]
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/public/images/cover/jjk.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Amritanshu312/animeverse/HEAD/public/images/cover/jjk.jpg
--------------------------------------------------------------------------------
/public/images/logo/logo-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Amritanshu312/animeverse/HEAD/public/images/logo/logo-2.png
--------------------------------------------------------------------------------
/public/images/wallpapers/naruto.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Amritanshu312/animeverse/HEAD/public/images/wallpapers/naruto.webp
--------------------------------------------------------------------------------
/public/images/banner/LoginWallpaper.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Amritanshu312/animeverse/HEAD/public/images/banner/LoginWallpaper.jpg
--------------------------------------------------------------------------------
/public/images/wallpapers/one piece.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Amritanshu312/animeverse/HEAD/public/images/wallpapers/one piece.jpg
--------------------------------------------------------------------------------
/public/images/wallpapers/demon slayer.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Amritanshu312/animeverse/HEAD/public/images/wallpapers/demon slayer.jpg
--------------------------------------------------------------------------------
/public/images/banner/Fearless and dark.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Amritanshu312/animeverse/HEAD/public/images/banner/Fearless and dark.jpeg
--------------------------------------------------------------------------------
/public/images/banner/Like a Pro Player.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Amritanshu312/animeverse/HEAD/public/images/banner/Like a Pro Player.jpeg
--------------------------------------------------------------------------------
/public/images/banner/Stronger than ever.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Amritanshu312/animeverse/HEAD/public/images/banner/Stronger than ever.jpeg
--------------------------------------------------------------------------------
/app/not-found.js:
--------------------------------------------------------------------------------
1 | import Error from "@/components/layout/error/__Error"
2 |
3 | const Page = () => {
4 | return
5 | }
6 |
7 | export default Page
--------------------------------------------------------------------------------
/app/loading.js:
--------------------------------------------------------------------------------
1 | import Loading from "@/components/layout/loading/Loading"
2 |
3 | const Page = () => {
4 | return
5 | }
6 |
7 | export default Page
--------------------------------------------------------------------------------
/public/images/banner/Wallpaper-Solo-Leveling-Manga-Anime-Digital61.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Amritanshu312/animeverse/HEAD/public/images/banner/Wallpaper-Solo-Leveling-Manga-Anime-Digital61.jpg
--------------------------------------------------------------------------------
/lib/CleanURL.js:
--------------------------------------------------------------------------------
1 | export const cleanParam = (param) => {
2 | if (!param || typeof param !== 'string') {
3 | return '';
4 | }
5 | // Replace %20 with +
6 | return param.replace(/%20/g, '+');
7 | };
8 |
9 |
--------------------------------------------------------------------------------
/app/(home)/layout.js:
--------------------------------------------------------------------------------
1 | import Navbar from "@/components/layout/navbar/Navbar";
2 | import Footer from "@/components/layout/footer/Footer";
3 |
4 | export default function RootLayout({ children }) {
5 | return (
6 | <>
7 |
8 | {children}
9 |
10 | >
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/content/HomePage/Home/slidingBanner/slidingBanner.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | display: flex;
3 | justify-content: center;
4 | align-items: center;
5 | overflow-x: scroll;
6 | gap: 2rem;
7 | scrollbar-width: none;
8 | }
9 |
10 | .swiperSlide {
11 | display: flex !important;
12 | justify-content: center;
13 | }
14 |
--------------------------------------------------------------------------------
/components/layout/navbar/responsive/Tablet/Tablet.jsx:
--------------------------------------------------------------------------------
1 | import Items from "../../Items/Items"
2 | import styles from "./Tablet.module.css"
3 |
4 | const Tablet = () => {
5 | return (
6 |
";
3 | initial-value: 0deg;
4 | inherits: false;
5 | }
6 |
7 | .loading {
8 | background: #0f0f13;
9 | height: 400px;
10 | border-radius: 14px;
11 | position: relative;
12 | animation: loading 8s linear infinite;
13 | }
14 |
15 | .loading::after {
16 | content: "";
17 | top: 50%;
18 | left: 50%;
19 | width: 102%;
20 | z-index: -1;
21 | height: 101.5%;
22 | position: absolute;
23 | transform: translate(-50%, -50%);
24 | border-radius: 14px;
25 | animation: rotation 4s linear infinite;
26 | background: linear-gradient(var(--gradient-angle), #141416, #2a608a8c);
27 | }
28 |
29 | @keyframes rotation {
30 | 0% {
31 | --gradient-angle: 0deg;
32 | }
33 | 100% {
34 | --gradient-angle: 360deg;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/(home)/download/[id]/page.js:
--------------------------------------------------------------------------------
1 | 'use client';
2 | import Loading from '@/components/layout/loading/Loading';
3 | import { fetchData } from '@/lib/FetchData';
4 | import { useRouter } from 'next/navigation';
5 | import { useEffect } from 'react';
6 |
7 | const Download = ({ params }) => {
8 | const { push } = useRouter();
9 |
10 | useEffect(() => {
11 | const fetchVideos = async () => {
12 | try {
13 | const data = await fetchData(`/meta/anilist/watch/${params.id}`);
14 | if (data.ok) {
15 | // console.log(data.data.download);
16 | push(data.data.download);
17 | }
18 | } catch (error) {
19 | console.error("Error fetching Download url:", error);
20 | }
21 | };
22 |
23 | fetchVideos();
24 | }, []);
25 |
26 | return ;
27 | };
28 |
29 | export default Download;
30 |
--------------------------------------------------------------------------------
/content/HomePage/Info/animeInfo/Info/Info.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | background: #070709;
3 | padding: 0px 20px 38px;
4 | border-radius: 15px;
5 | border: 2px solid #23252b;
6 | }
7 |
8 | .list {
9 | display: flex;
10 | flex-direction: column;
11 | align-items: center;
12 | justify-content: center;
13 | margin-top: 30px;
14 | padding-bottom: 10px;
15 | border-bottom: 2px solid #23252bbd;
16 | }
17 |
18 | .list div:first-child {
19 | color: #8991a3;
20 | font-weight: 500;
21 | margin-bottom: 10px;
22 | }
23 |
24 | .list div:last-child {
25 | color: #c8c8c8;
26 | font-weight: 500;
27 | }
28 |
29 | .list:last-child {
30 | color: #c8c8c8;
31 | font-weight: 500;
32 | border: none;
33 | }
34 |
35 | @media screen and (max-width: 1000px) {
36 | .container {
37 | width: 100%;
38 | max-width: 40rem;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/components/layout/loading/Loading.jsx:
--------------------------------------------------------------------------------
1 | import styles from "./loading.module.css"
2 |
3 | const Loading = () => {
4 | return (
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | );
20 | };
21 |
22 | export default Loading;
23 |
--------------------------------------------------------------------------------
/content/HomePage/Home/discover/options/Options.jsx:
--------------------------------------------------------------------------------
1 | import styles from "./options.module.css"
2 |
3 | const Options = ({ categoryselector }) => {
4 | const { setCategory, category } = categoryselector
5 | const genreList = ["Action", "Comedy", "Drama", "Fantasy", "Psychological", "Romance", "Sci-Fi", "Thriller", "Slice of Life", "Supernatural"]
6 | return (
7 |
8 |
9 |
10 | setCategory("Trending")}>Trending Series
11 | {genreList.map((genre) => (
12 | setCategory(genre)}>{genre}
13 | ))}
14 |
15 |
16 | )
17 | }
18 |
19 | export default Options
--------------------------------------------------------------------------------
/content/HomePage/Search/userSelection/catalog/genres/genres.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | display: flex;
3 | flex-direction: column;
4 | justify-content: space-between;
5 | margin-top: 10px;
6 | }
7 |
8 | .heading {
9 | font-family: "poppins", sans-serif;
10 | font-size: 16px;
11 | border-bottom: 2px solid #28282e;
12 | padding: 10px 0px;
13 | }
14 |
15 | .options {
16 | display: flex;
17 | flex-direction: column;
18 | gap: 10px;
19 | margin-top: 8px;
20 | }
21 |
22 | .select {
23 | display: flex;
24 | align-items: center;
25 | gap: 5px;
26 | color: #a2a3a5e8;
27 | font-size: 16px;
28 | cursor: pointer;
29 | transition: 0.3s;
30 | }
31 |
32 | .active {
33 | color: #ffffff;
34 | }
35 |
36 | .viewall {
37 | color: #57944a;
38 | display: flex;
39 | align-items: center;
40 | gap: 5px;
41 | font-size: 16px;
42 | cursor: pointer;
43 | transition: 0.3s;
44 | }
45 |
46 | .viewall:hover {
47 | color: #3ea027;
48 | }
49 |
--------------------------------------------------------------------------------
/components/ui/AtoZalphabet/AtoZalphabet.jsx:
--------------------------------------------------------------------------------
1 | import styles from "./AtoZalphabet.module.css"
2 |
3 | const AtoZalphabet = () => {
4 | const alphabets = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
5 | return (
6 |
7 |
A-Z
8 |
9 |
Dive Deep! Explore Animes A to Z Alphabetically.
10 |
11 | Discover your next favorite Anime series, all organized alphabetically. Explore a vast collection of genres and discover hidden gems.
12 |
13 |
14 |
15 | {alphabets.map((letter, index) => (
16 |
17 | {letter}
18 |
19 | ))}
20 |
21 |
22 | )
23 | }
24 |
25 | export default AtoZalphabet
--------------------------------------------------------------------------------
/components/ui/banner/AnimeInfo/AnimeInfo.jsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link"
2 | import styles from "./AnimeInfo.module.css"
3 |
4 | const AnimeInfo = ({ info }) => {
5 | const { name, description, type, genre, id } = info
6 | return (
7 |
8 |
{name}
9 |
{description}
10 |
11 |
12 |
13 | Type: {type}
14 |
15 |
16 | Genre:
17 | {genre.map((genre, index) => {genre})}
18 |
19 |
20 |
21 |
22 |
Watch Now
23 |
24 | )
25 | }
26 |
27 | export default AnimeInfo
--------------------------------------------------------------------------------
/content/HomePage/Search/userSelection/catalog/catalog.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | background: #0f1014;
3 | height: max-content;
4 | width: 100%;
5 | max-width: 20rem;
6 | position: relative;
7 | border-radius: 20px;
8 | padding: 10px 10px 9px 15px;
9 | border: 2px solid #1c1c1e;
10 | }
11 |
12 | .title {
13 | font-family: "poppins", sans-serif;
14 | font-size: 32px;
15 | font-weight: 500;
16 | }
17 |
18 | .apply {
19 | background: linear-gradient(174deg, #d5a687, #e79a69);
20 | width: 100%;
21 | height: 40px;
22 | border: 2px solid #efac87;
23 | margin-top: 10px;
24 | border-radius: 10px;
25 | color: black;
26 | font-family: "poppins", sans-serif;
27 | font-size: 15px;
28 | font-weight: 600;
29 | cursor: pointer;
30 | }
31 |
32 | @media screen and (max-width: 1000px) {
33 | .container {
34 | max-width: -webkit-fill-available !important;
35 | max-width: -moz-fill-available !important;
36 | margin-right: 1rem;
37 | margin-bottom: 2rem;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/content/AuthPage/login/left/formField/FormField.jsx:
--------------------------------------------------------------------------------
1 | import styles from "./formField.module.css"
2 | import { FaUserCircle, FaLock } from "react-icons/fa";
3 | import { HiOutlineMailOpen } from "react-icons/hi";
4 |
5 |
6 | const FormField = () => {
7 | return (
8 |
31 | )
32 | }
33 |
34 | export default FormField
--------------------------------------------------------------------------------
/app/(home)/watch/[id]/watch.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | display: flex;
3 | width: -webkit-fill-available;
4 | width: -moz-available;
5 | position: relative;
6 | top: -6rem;
7 | margin-inline: 150px;
8 | justify-content: center;
9 | }
10 | .left {
11 | display: flex;
12 | flex-direction: column;
13 | width: 60%;
14 | /* align-items: end; */
15 | }
16 | .right {
17 | width: 100%;
18 | max-width: 360px;
19 | margin-left: 2rem;
20 | position: relative;
21 | }
22 |
23 | @media screen and (max-width: 1050px) {
24 | .container {
25 | margin-inline: 20px;
26 | }
27 | }
28 |
29 | @media screen and (max-width: 780px) {
30 | .right {
31 | flex-direction: column;
32 | }
33 | }
34 |
35 | @media screen and (max-width: 1450px) {
36 | .container {
37 | flex-direction: column;
38 | }
39 | .left {
40 | width: 100%;
41 | }
42 |
43 | .right {
44 | max-width: 98rem;
45 | display: flex;
46 | justify-content: space-between;
47 | margin: 0;
48 | margin-top: 3rem;
49 | gap: 0.5rem;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/content/HomePage/Watch/left/animeSeasons/card/card.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | margin-top: 1rem;
3 | position: relative;
4 | cursor: pointer;
5 | }
6 | .container .image {
7 | position: relative;
8 | width: 100%;
9 | height: 200px;
10 | transition: 0.3s;
11 | overflow: hidden;
12 | border: 2px solid #373a439b;
13 | border-radius: 20px;
14 | }
15 | .container .image img {
16 | filter: brightness(50%);
17 | border-radius: 20px;
18 | object-fit: cover;
19 | width: 100%;
20 | transition: 0.3s;
21 | }
22 |
23 | .info {
24 | position: absolute;
25 | top: 50%;
26 | left: 50%;
27 | transform: translate(-50%, -50%);
28 | }
29 |
30 | .title {
31 | font-family: "poppins", sans-serif;
32 | font-weight: 600;
33 | font-size: 18px;
34 | background: -webkit-linear-gradient(#dcdcde, #5a5a5a);
35 | background-clip: text;
36 | -webkit-text-fill-color: transparent;
37 | text-align: center;
38 | }
39 |
40 | .container:hover .image {
41 | filter: brightness(40%);
42 | }
43 | .container:hover .image img {
44 | scale: 1.1;
45 | }
46 |
--------------------------------------------------------------------------------
/content/HomePage/Search/userSelection/catalog/season/Season.jsx:
--------------------------------------------------------------------------------
1 | import styles from "./season.module.css"
2 | import { FaRegSquare, FaSquareCheck } from "react-icons/fa6";
3 |
4 | const Season = ({ callback }) => {
5 | const seasons = ["WINTER", "SPRING", "SUMMER", "FALL"]
6 | const { searchData, setSearchData } = callback
7 |
8 | const handleClick = (selected) => {
9 | if (selected === searchData.season) {
10 | setSearchData({ ...searchData, season: "" })
11 | }
12 | else {
13 | setSearchData({ ...searchData, season: selected })
14 | }
15 | }
16 |
17 | return (
18 |
19 |
Season
20 |
21 | {seasons.map((season, index) => (
22 |
handleClick(season)}>{searchData.season === season ? : } {season}
23 | ))}
24 |
25 |
26 | )
27 | }
28 |
29 | export default Season
--------------------------------------------------------------------------------
/content/HomePage/Search/userSelection/catalog/status/Status.jsx:
--------------------------------------------------------------------------------
1 | import { FaRegSquare, FaSquareCheck } from "react-icons/fa6";
2 | import styles from "../season/season.module.css"
3 |
4 | const Status = ({ callback }) => {
5 | const status = ["RELEASING", "NOT_YET_RELEASED", "FINISHED", "CANCELLED", "HIATUS"]
6 | const { searchData, setSearchData } = callback
7 |
8 | const handleClick = (selected) => {
9 | if (selected === searchData.status) {
10 | setSearchData({ ...searchData, status: "" })
11 | }
12 | else {
13 | setSearchData({ ...searchData, status: selected })
14 | }
15 | }
16 | return (
17 |
18 |
Status
19 |
20 | {status.map((status, index) => (
21 |
handleClick(status)}>{searchData.status === status ? : } {status}
22 | ))}
23 |
24 |
25 | )
26 | }
27 |
28 | export default Status
--------------------------------------------------------------------------------
/components/layout/footer/footer.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | background: #0d0e12;
3 | padding: 2rem 5rem;
4 | }
5 |
6 | .top {
7 | display: flex;
8 | justify-content: space-between;
9 | align-items: center;
10 | padding: 0rem 0 2rem;
11 | border-bottom: 2px solid #1c1d22;
12 | }
13 |
14 | .socials {
15 | display: flex;
16 | gap: 1rem;
17 | }
18 |
19 | .socials button {
20 | background: #040406;
21 | color: white;
22 | padding: 12px;
23 | font-size: 16px;
24 | display: flex;
25 | border: 2px solid #17181e;
26 | border-radius: 50px;
27 | cursor: pointer;
28 | transition: 0.5s;
29 | }
30 |
31 | .socials button:hover {
32 | background: #000000;
33 | color: rgb(200, 200, 200);
34 | }
35 |
36 | .bottom {
37 | display: flex;
38 | flex-direction: column;
39 | align-items: center;
40 | padding: 2rem 0px 0px;
41 | line-height: 30px;
42 | color: #525762;
43 | }
44 |
45 | @media screen and (max-width: 950px) {
46 | .container {
47 | padding: 2rem 2rem;
48 | }
49 | }
50 |
51 | @media screen and (max-width: 780px) {
52 | .top {
53 | flex-direction: column;
54 | gap: 2rem;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/content/HomePage/Search/userSelection/catalog/Type/Type.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import { useState } from "react";
3 | import { FaRegSquare, FaSquareCheck } from "react-icons/fa6";
4 | import styles from "../season/season.module.css"
5 |
6 | const Type = ({ callback }) => {
7 | const types = ["TV", "TV_SHORT", "OVA", "ONA", "MOVIE", "SPECIAL", "MUSIC"]
8 | const { searchData, setSearchData } = callback
9 |
10 | const handleClick = (selected) => {
11 | if (selected === searchData.type) {
12 | setSearchData({ ...searchData, type: "" })
13 | }
14 | else {
15 | setSearchData({ ...searchData, type: selected })
16 | }
17 | }
18 |
19 | return (
20 |
21 |
Type
22 |
23 | {types.map((type, index) => (
24 |
handleClick(type)}>{searchData.type === type ? : } {type}
25 | ))}
26 |
27 |
28 | )
29 | }
30 |
31 | export default Type
--------------------------------------------------------------------------------
/content/HomePage/Search/userSelection/catalog/year/year.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | display: flex;
3 | flex-direction: column;
4 | justify-content: space-between;
5 | }
6 |
7 | .heading {
8 | font-family: "poppins", sans-serif;
9 | font-size: 16px;
10 | border-bottom: 2px solid #28282e;
11 | padding: 10px 0px;
12 | display: flex;
13 | align-items: center;
14 | justify-content: space-between;
15 | }
16 |
17 | .options {
18 | display: flex;
19 | align-items: center;
20 | gap: 20px;
21 | margin-top: 8px;
22 | }
23 |
24 | .options span {
25 | letter-spacing: -4px;
26 | color: #2d2d39;
27 | }
28 |
29 | .options input {
30 | padding: 10px;
31 | border-radius: 10px;
32 | border: 2px solid #1f1f23;
33 | background: #0a0a0d;
34 | color: white;
35 | font-family: "poppins", sans-serif;
36 | font-size: 15px;
37 | outline: none;
38 | width: 100%;
39 | }
40 |
41 | .options input:focus {
42 | border: 2px solid #333741;
43 | }
44 |
45 | .yearSelected {
46 | background: rgb(65, 60, 60);
47 | padding: 4px 15px;
48 | border-radius: 10px;
49 | display: flex;
50 | align-items: center;
51 | gap: 5px;
52 | cursor: pointer;
53 | }
54 |
--------------------------------------------------------------------------------
/components/layout/error/error.module.css:
--------------------------------------------------------------------------------
1 | .error {
2 | height: 100vh;
3 | width: 100%;
4 | display: flex;
5 | flex-direction: column;
6 | justify-content: center;
7 | align-items: center;
8 | }
9 |
10 | .error h1 {
11 | max-width: 1224px;
12 | text-align: center;
13 | margin-top: 20px;
14 | margin-bottom: 0px;
15 | font-size: 10rem;
16 | font-family: "Protest Revolution", sans-serif;
17 | font-weight: 600;
18 | color: #03a9f4;
19 | letter-spacing: 15px;
20 | }
21 |
22 | .description {
23 | max-width: 1224px;
24 | text-align: center;
25 | margin-top: 20px;
26 | margin-bottom: 20px;
27 | font-size: 20px;
28 | font-family: "poppins", sans-serif;
29 | font-weight: 500;
30 | color: #a4a4a4;
31 | }
32 |
33 | .error button {
34 | display: flex;
35 | background: linear-gradient(174deg, #d5a687, #e79a69);
36 | outline: none;
37 | border: 2px solid #efac87;
38 | padding: 10px 15px;
39 | color: black;
40 | font-size: 18px;
41 | gap: 5px;
42 | font-family: "poppins", sans-serif;
43 | align-items: center;
44 | border-radius: 35px;
45 | font-weight: 600;
46 | cursor: pointer;
47 | width: 100%;
48 | max-width: max-content;
49 | }
50 |
--------------------------------------------------------------------------------
/content/HomePage/Info/animeInfo/Info/Info.jsx:
--------------------------------------------------------------------------------
1 | import styles from "./Info.module.css"
2 |
3 | const Info = ({ data }) => {
4 | const { status, studios, season, duration, type, startDate } = data
5 | const months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
6 |
7 | return (
8 |
9 |
10 |
Type
11 |
{type}
12 |
13 |
14 |
Studio
15 |
{studios[0]}
16 |
17 |
18 |
Published
19 |
{months[startDate?.month]} {startDate?.day}, {startDate?.year}
20 |
21 |
22 |
duration
23 |
{duration} min
24 |
25 |
26 |
Status
27 |
{status}
28 |
29 |
30 |
Season
31 |
{season}
32 |
33 |
34 | )
35 | }
36 |
37 | export default Info
--------------------------------------------------------------------------------
/app/(auth)/login/page.js:
--------------------------------------------------------------------------------
1 | import LeftContainer from '@/content/AuthPage/login/left/LeftContainer';
2 | import styles from './login.module.css'
3 | import Image from "next/image";
4 |
5 |
6 | const Login = () => {
7 | return (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
Jujutsu Kaisen
23 |
Young Yuji unwittingly joins his school's Occult Club, discovers they're sorcerers, and becomes host to the demon Sukuna. Mastering his magic, he enrolls in Tokyo Metropolitan Magic Technical College to collect Sukuna's fingers for a full exorcism.
24 |
25 |
26 |
27 |
28 | )
29 | }
30 |
31 | export default Login
--------------------------------------------------------------------------------
/content/AuthPage/login/left/formField/formField.module.css:
--------------------------------------------------------------------------------
1 | .input {
2 | display: flex;
3 | align-items: center;
4 | gap: 14px;
5 | width: 100%;
6 | max-width: 30rem;
7 | border: 2px solid #2d3138;
8 | padding: 5px 12px;
9 | border-radius: 30px;
10 | margin-top: 10px;
11 | border-width: 2px 1px 1px 3px;
12 | }
13 |
14 | .input svg {
15 | height: 26px;
16 | width: 19px;
17 | }
18 |
19 | .input:nth-child(3) svg,
20 | .input:nth-child(4) svg {
21 | height: 26px;
22 | width: 14px;
23 | }
24 |
25 | .input input {
26 | width: 100%;
27 | height: 40px;
28 | background: transparent;
29 | border: none;
30 | outline: none;
31 | font-family: "poppins", sans-serif;
32 | font-size: 15px;
33 | color: #dbdcde;
34 | }
35 |
36 | .form input[type="submit"] {
37 | background: linear-gradient(174deg, #d5a687, #e79a69);
38 | border: 2px solid #efac87;
39 | padding: 10px 15px;
40 | color: black;
41 | font-family: "poppins", sans-serif;
42 | border-radius: 35px;
43 | font-weight: 600;
44 | cursor: pointer;
45 | width: 100%;
46 | max-width: 30rem;
47 | margin-top: 12px;
48 | font-size: 15px;
49 | }
50 | @media screen and (max-width: 990px) {
51 | .input,
52 | .form input[type="submit"] {
53 | max-width: 100% !important;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/content/AuthPage/login/left/LeftContainer.jsx:
--------------------------------------------------------------------------------
1 | import styles from "./left.module.css"
2 | import FormField from "@/content/AuthPage/login/left/formField/FormField";
3 | import Link from "next/link";
4 | import { IoArrowBackOutline } from "react-icons/io5";
5 | import Image from "next/image";
6 |
7 | const LeftContainer = () => {
8 | return (
9 |
10 |
Back To HomePage
11 |
12 |
13 |
14 |
15 |
AnimeVerse
16 |
17 |
18 |
19 |
Enter The Anime Universe
20 |
Be Part Of The journey
21 |
22 |
23 |
Do you have a AnimeVerse account? Login Now
24 |
25 |
26 |
27 |
28 | This Site is Protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply
29 |
30 |
31 |
32 | )
33 | }
34 |
35 | export default LeftContainer
--------------------------------------------------------------------------------
/content/HomePage/Search/userSelection/UserSelection.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useState } from "react";
4 | import { IoIosSearch } from "react-icons/io";
5 | import styles from "./userSelection.module.css";
6 | import { usePathname, useRouter, useSearchParams } from "next/navigation";
7 |
8 | const UserSelection = ({ q }) => {
9 | const { searchData, setSearchData } = q;
10 | const [value, setValue] = useState(searchData.q);
11 |
12 | const Param = useSearchParams();
13 | const router = useRouter();
14 | const handleSearch = () => {
15 | router.push(`/search?q=${value}&${Param.toString()}`);
16 | setSearchData({ ...searchData, q: value });
17 | };
18 |
19 | return (
20 |
21 |
Welcome to Animeverse Search!
22 |
23 | setValue(e.target.value)}
29 | onKeyUp={(e) => (e.key === "Enter" ? handleSearch() : null)}
30 | />
31 |
32 |
33 |
34 |
35 |
36 | );
37 | };
38 |
39 | export default UserSelection;
40 |
--------------------------------------------------------------------------------
/content/HomePage/Search/userSelection/options/options.module.css:
--------------------------------------------------------------------------------
1 | .optionsContainer {
2 | position: relative;
3 | margin-bottom: 10px;
4 | }
5 |
6 | .genres {
7 | display: flex;
8 | align-items: center;
9 | justify-content: space-between;
10 | width: max-content;
11 | gap: 8rem;
12 | background: #0f0f13;
13 | border-radius: 20px;
14 | padding: 15px 10px;
15 | border: 2px solid #1c1c1e;
16 | cursor: pointer;
17 | }
18 |
19 | .left,
20 | .right,
21 | .right svg {
22 | display: flex;
23 | align-items: center;
24 | gap: 5px;
25 | height: 100%;
26 | }
27 |
28 | .options {
29 | position: absolute;
30 | display: grid;
31 | background: #0f0f13;
32 | gap: 10px;
33 | padding: 8px;
34 | width: 100%;
35 | margin-top: 4px;
36 | z-index: 99;
37 | border-radius: 10px;
38 | border: 2px solid #18181b;
39 | backdrop-filter: blur(10px);
40 | }
41 |
42 | .options .item {
43 | background: #17171b;
44 | border-radius: 20px;
45 | padding: 15px 26px;
46 | text-align: center;
47 | display: flex;
48 | cursor: pointer;
49 | font-family: "poppins", sans-serif;
50 | align-items: center;
51 | gap: 6px;
52 | }
53 |
54 | .options .item:hover {
55 | background: #212124;
56 | }
57 |
58 | .active {
59 | background: linear-gradient(174deg, #d5a687, #e79a69) !important;
60 | outline: none;
61 | color: black;
62 | }
63 |
--------------------------------------------------------------------------------
/content/HomePage/Watch/left/videoOption/videoOption.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | display: flex;
3 | justify-content: space-between;
4 | width: -webkit-fill-available;
5 | width: -moz-available;
6 | margin-top: 20px;
7 | /* margin-inline: 10px; */
8 | }
9 |
10 | .container button {
11 | background: #070709;
12 | border: 2px solid #23252b;
13 | border-radius: 50px;
14 | padding: 0.9rem 1.2rem;
15 | font-family: "poppins", sans-serif;
16 | font-weight: 500;
17 | color: #bbbbbb;
18 | display: flex;
19 | align-items: center;
20 | gap: 5px;
21 | cursor: pointer;
22 | transition: 0.4s;
23 | width: 18%;
24 | min-width: max-content;
25 | justify-content: center;
26 | }
27 |
28 | .container button:hover {
29 | background: #14141a;
30 | /* scale: 1.1; */
31 | }
32 |
33 | .container button span {
34 | display: flex;
35 | align-items: center;
36 | color: #848b9d;
37 | gap: 5px;
38 | }
39 |
40 | .container button span svg {
41 | margin-bottom: 2px;
42 | }
43 |
44 | .container button:nth-child(3) {
45 | color: #848b9d;
46 | }
47 |
48 | .container button:nth-child(4) {
49 | color: #e79a69;
50 | }
51 |
52 | @media screen and (max-width: 1540px) {
53 | .container button {
54 | padding: 0.9rem 0.9rem;
55 | }
56 | }
57 |
58 | @media screen and (max-width: 750px) {
59 | .container {
60 | flex-wrap: wrap;
61 | justify-content: center;
62 | gap: 15px;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/content/HomePage/Home/discover/options/options.module.css:
--------------------------------------------------------------------------------
1 | .options {
2 | display: flex;
3 | justify-content: center;
4 | margin-top: 22px;
5 | }
6 |
7 | .items {
8 | display: flex;
9 | align-items: center;
10 | justify-content: center;
11 | max-width: 52rem;
12 | flex-wrap: wrap;
13 | gap: 13px;
14 | }
15 |
16 | .button {
17 | display: flex;
18 | padding: 9px 15px;
19 | font-size: 16px;
20 | border-radius: 50px;
21 | transition: 0.5s;
22 | background: #0a0a0d;
23 |
24 | font-family: "poppins", sans-serif;
25 | border: 2px solid #2e3138;
26 | color: #bbbbbb;
27 | gap: 6px;
28 | font-weight: 500;
29 | cursor: pointer;
30 | position: relative;
31 | }
32 |
33 | .button:hover {
34 | background: #232324;
35 | }
36 |
37 | .active {
38 | border: none;
39 | background: #232324;
40 | }
41 |
42 | .active::after {
43 | content: "";
44 | position: absolute;
45 | z-index: -1;
46 | width: 102%;
47 | height: 104%;
48 | background: linear-gradient(30deg, #ffffff85, #ffffff29);
49 | top: 49%;
50 | left: 50.1%;
51 | transform: translate(-50%, -50%);
52 | border-radius: 50px;
53 | }
54 |
55 | .active:hover::after {
56 | content: "";
57 | position: absolute;
58 | z-index: -1;
59 | width: 102%;
60 | height: 104%;
61 | background: linear-gradient(121deg, #ffffff85, #ffffff29);
62 | top: 49%;
63 | left: 49.9%;
64 | transform: translate(-50%, -50%);
65 | border-radius: 50px;
66 | }
67 |
--------------------------------------------------------------------------------
/content/HomePage/Watch/right/recommendation/Recommendation.jsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import styles from "../mostPopular/mostPopular.module.css"
3 | import { IoIosRocket as TagIcon } from "react-icons/io";
4 | import { BiSolidLike } from "react-icons/bi";
5 | import { BsStack } from "react-icons/bs";
6 | import Link from "next/link";
7 |
8 | const Recommendation = ({ data }) => {
9 | return (
10 |
11 |
12 |
13 | Recommended Anime Series
14 |
15 |
16 |
17 | {data.map((items, index) => (
18 |
19 |
{index + 1}
20 |
21 |
22 |
23 |
24 |
25 |
{items.title?.english}
26 |
{items.rating * 10 / 100} Rating
27 |
{items?.episodes} Episodes
28 |
29 |
30 | ))}
31 |
32 |
33 |
34 | )
35 | }
36 |
37 | export default Recommendation
--------------------------------------------------------------------------------
/content/HomePage/Watch/left/videoOption/VideoOption.jsx:
--------------------------------------------------------------------------------
1 | import styles from "./videoOption.module.css"
2 | import { FaArrowLeft } from "react-icons/fa6";
3 | import { IoBookmark } from "react-icons/io5";
4 | import { FaArrowRight, FaCommentAlt } from "react-icons/fa";
5 | import { useRouter } from "next/navigation";
6 |
7 | const VideoOption = ({ id, currentEpisode, VideoOptionToggler }) => {
8 | const router = useRouter();
9 |
10 | const previousEpisode = () => {
11 | if (currentEpisode > 1) {
12 | router.push("/watch/" + id + "?episodeID=" + VideoOptionToggler[0].id + "&episode=" + VideoOptionToggler[0].number)
13 | }
14 | }
15 | const NextEpisode = () => {
16 | if (VideoOptionToggler[2] !== null) {
17 | router.push("/watch/" + id + "?episodeID=" + VideoOptionToggler[2].id + "&episode=" + VideoOptionToggler[2].number)
18 | }
19 | }
20 |
21 | return (
22 |
23 | Prev: Episode {VideoOptionToggler?.[0]?.number || 1}
24 | Bookmark
25 | router.push("/info/" + id)}>View Details
26 | Comments (45)
27 | Next: Episode {VideoOptionToggler?.[2]?.number || VideoOptionToggler?.[1]?.number}
28 |
29 | )
30 | }
31 |
32 | export default VideoOption
--------------------------------------------------------------------------------
/components/layout/navbar/Items/Items.module.css:
--------------------------------------------------------------------------------
1 | .items {
2 | display: flex;
3 | align-items: center;
4 | gap: 2rem;
5 | list-style: none;
6 | margin-left: 40px;
7 | }
8 |
9 | .listitem a {
10 | all: unset;
11 | z-index: 2;
12 | position: relative;
13 | font-family: "poppins", sans-serif;
14 | font-size: 13px;
15 | }
16 |
17 | .listitem {
18 | position: relative;
19 | padding: 9px 15px;
20 | cursor: pointer;
21 | font-size: 16px;
22 | border-radius: 50px;
23 | transition: 0.5s;
24 | }
25 |
26 | .listitem:hover {
27 | background: #000000;
28 | }
29 | .listitem:hover::after {
30 | content: "";
31 | top: 50%;
32 | left: 50%;
33 | height: 110%;
34 | z-index: 1;
35 | width: 105%;
36 | background: linear-gradient(30deg, #ffffff29, #ffffff0a);
37 | position: absolute;
38 | border-radius: 50px;
39 | transform: translate(-50%, -50%);
40 | }
41 |
42 | .active {
43 | background: #000000;
44 | }
45 | .active::after {
46 | content: "";
47 | top: 50%;
48 | left: 50%;
49 | height: 110%;
50 | z-index: 1;
51 | width: 105%;
52 | background: linear-gradient(30deg, #ffffff29, #ffffff0a);
53 | position: absolute;
54 | border-radius: 50px;
55 | transform: translate(-50%, -50%);
56 | }
57 |
58 | .itemsTablet {
59 | margin-left: 0px;
60 | flex-direction: column;
61 | align-items: normal;
62 | text-align: center;
63 | gap: 6px;
64 | }
65 |
66 | @media screen and (max-width: 1080px) {
67 | .items {
68 | display: none;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/components/ui/banner/Banner.jsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image"
2 | import styles from "./banner.module.css"
3 | import AnimeInfo from "./AnimeInfo/AnimeInfo"
4 | import { useState } from "react"
5 | import VideoPlay from "./videoPlay/VideoPlay"
6 |
7 | const Banner = ({ info }) => {
8 | const [isVideoPlaying, setIsVideoPlaying] = useState(false)
9 | const [isImageLoaded, setIsImageLoaded] = useState(false)
10 | const HoverTime = 1000
11 | let hoverTimer = null;
12 |
13 | const onMouseEnter = () => {
14 | if (!isVideoPlaying && window.innerWidth >= 890) {
15 | hoverTimer = setTimeout(() => {
16 | setIsVideoPlaying(true);
17 | }, HoverTime);
18 | }
19 | }
20 |
21 | const onMouseLeave = () => {
22 | clearTimeout(hoverTimer); // Cancel the timeout if mouse leaves before HoverTime
23 | setIsVideoPlaying(false);
24 | }
25 |
26 | return (
27 |
31 |
32 | {!isVideoPlaying || !info?.video || !isImageLoaded ? setIsImageLoaded(true)} /> : null}
33 | {info?.video && isVideoPlaying ? : null}
34 |
35 |
36 |
37 |
38 | )
39 | }
40 |
41 | export default Banner
42 |
--------------------------------------------------------------------------------
/components/layout/navbar/Navbar.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import Image from "next/image"
3 | import styles from "./navbar.module.css"
4 | import Items from "./Items/Items"
5 | import { FaUserCircle as AccountIcon } from "react-icons/fa";
6 | import Search from "./search/Search";
7 | import { HiOutlineMenuAlt2 as HamburgerIcon } from "react-icons/hi";
8 | import Tablet from "./responsive/Tablet/Tablet";
9 | import { useState } from "react";
10 | import Link from "next/link";
11 |
12 | const Navbar = () => {
13 | const [isHamburgerToggled, setIsHamburgerToggled] = useState(false)
14 | return (
15 |
16 |
17 |
18 | {/* for table to mobile phone viewport */}
19 |
setIsHamburgerToggled(prev => !prev)}>
20 |
21 |
22 |
23 | {isHamburgerToggled ?
: null}
24 |
25 |
26 |
27 |
AnimeVerse
28 |
29 |
30 |
31 |
32 |
33 |
34 |
My Account
35 |
36 |
37 |
38 |
39 |
40 | )
41 | }
42 |
43 | export default Navbar
--------------------------------------------------------------------------------
/content/HomePage/Watch/left/animeInfo/AnimeInfo.jsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image"
2 | import styles from "./animeInfo.module.css"
3 | import { FaHouse as HomeIcon } from "react-icons/fa6";
4 | import { FaAngleRight as RightArrow } from "react-icons/fa6";
5 | import Link from "next/link";
6 | import { useSearchParams } from "next/navigation";
7 |
8 | const AnimeInfo = ({ info, episode }) => {
9 | const episodeNo = useSearchParams().get('episode')
10 |
11 | if (!info) return null
12 | const { title, id } = info
13 | return (
14 |
15 |
16 |
21 |
22 |
23 |
24 |
25 | HomePage
26 | {title?.english}
27 | {title?.english} Episode {episodeNo}
28 |
29 |
30 |
31 | {title?.english}: Episode {episode}
32 |
33 |
34 |
Read This anime and mangas on MangaGrok . Don't forgot to explore our other manga series as well!
35 |
36 |
37 | )
38 | }
39 |
40 | export default AnimeInfo
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | # or
14 | bun dev
15 | ```
16 |
17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18 |
19 | You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
22 |
23 | ## Learn More
24 |
25 | To learn more about Next.js, take a look at the following resources:
26 |
27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29 |
30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
31 |
32 | ## Deploy on Vercel
33 |
34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35 |
36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
37 |
--------------------------------------------------------------------------------
/app/(home)/info/[id]/info.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | display: flex;
3 | width: -webkit-fill-available;
4 | width: -moz-available;
5 | position: relative;
6 | top: -16rem;
7 | margin-inline: 150px;
8 | }
9 |
10 | .backgroundImage {
11 | position: relative;
12 | width: 100%;
13 | height: 500px;
14 | object-fit: cover;
15 | border-radius: 20px;
16 | filter: brightness(50%);
17 | }
18 | .backgroundImage::after {
19 | content: "";
20 | position: absolute;
21 | bottom: 0;
22 | left: 0;
23 | width: 100%;
24 | height: 100%;
25 | background: linear-gradient(
26 | 360deg,
27 | #0c0d10,
28 | #0c0d10a1,
29 | #0c0d1000,
30 | transparent
31 | );
32 | }
33 |
34 | .backgroundImage img {
35 | object-fit: cover;
36 | filter: brightness(0.5);
37 | }
38 |
39 | @media screen and (max-width: 1600px) {
40 | .container {
41 | margin-inline: 40px;
42 | }
43 | }
44 |
45 | @media screen and (max-width: 1380px) {
46 | .container {
47 | flex-direction: column;
48 | }
49 | .showanotheranime {
50 | display: flex;
51 | width: 100%;
52 | justify-content: space-between;
53 | margin-top: 2.5rem;
54 | }
55 | }
56 |
57 | @media screen and (max-width: 1250px) {
58 | .container {
59 | margin-inline: 20px;
60 | }
61 | .showanotheranime {
62 | margin-left: 0px !important;
63 | display: flex;
64 | gap: 20px;
65 | }
66 | }
67 |
68 | @media screen and (max-width: 890px) {
69 | .showanotheranime {
70 | flex-direction: column;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/lib/DB/AddWatched.js:
--------------------------------------------------------------------------------
1 | let version = 1;
2 | let db;
3 |
4 | export const Watched = {
5 | AnimeWatched: 'watched',
6 | };
7 |
8 | export const initDB = () => {
9 | return new Promise((resolve, reject) => {
10 | let request = indexedDB.open('Anime', version);
11 |
12 | request.onupgradeneeded = () => {
13 | db = request.result;
14 |
15 | if (!db.objectStoreNames.contains(Watched.AnimeWatched)) {
16 | console.log('Creating watched store');
17 | db.createObjectStore(Watched.AnimeWatched, { keyPath: 'id' });
18 | }
19 | };
20 |
21 | request.onsuccess = () => {
22 | db = request.result;
23 | version = db.version;
24 | console.log('Database opened successfully');
25 | resolve(true);
26 | };
27 |
28 | request.onerror = () => {
29 | console.error('Error opening database');
30 | reject(new Error('Failed to open database'));
31 | };
32 | });
33 | };
34 |
35 | export const addAnimeEpisodes = (anime) => {
36 | return new Promise((resolve, reject) => {
37 | const transaction = db.transaction([Watched.AnimeWatched], 'readwrite');
38 | const objectStore = transaction.objectStore(Watched.AnimeWatched);
39 |
40 | const request = objectStore.add(anime);
41 |
42 | request.onsuccess = () => {
43 | console.log('Anime added to watched:', anime);
44 | resolve(true);
45 | };
46 |
47 | request.onerror = () => {
48 | console.error('Error adding anime to watched:', anime);
49 | reject(new Error('Failed to add anime to watched'));
50 | };
51 | });
52 | };
53 |
--------------------------------------------------------------------------------
/components/ui/AtoZalphabet/AtoZalphabet.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | display: flex;
3 | flex-direction: column;
4 | align-items: center;
5 | margin-top: 20px;
6 | margin-bottom: 20px;
7 | }
8 |
9 | .logo {
10 | padding: 14px;
11 | color: #d9a789;
12 | font-size: 24px;
13 | font-family: "poppins", sans-serif;
14 | }
15 |
16 | .title {
17 | font-family: "poppins", sans-serif;
18 | font-size: 20px;
19 | font-weight: 500;
20 | color: #a4a4a4;
21 | text-align: center;
22 | }
23 |
24 | .description {
25 | color: #636a79;
26 | font-family: "poppins", sans-serif;
27 | font-size: 18px;
28 | margin-top: 10px;
29 | max-width: 75rem;
30 | text-align: center;
31 | line-height: 25px;
32 | }
33 |
34 | .letters {
35 | display: flex;
36 | gap: 0.6rem;
37 | margin-top: 30px;
38 | margin-bottom: 30px;
39 | flex-wrap: wrap;
40 | justify-content: center;
41 | }
42 |
43 | .letter {
44 | background: linear-gradient(174deg, #d5a687, #e79a69);
45 | outline: none;
46 | border: 2px solid #efac87;
47 | padding: 10px 15px;
48 | color: black;
49 | border-radius: 40%;
50 | font-weight: 600;
51 | cursor: pointer;
52 | width: 45px;
53 | height: 45px;
54 | display: flex;
55 | align-items: center;
56 | justify-content: center;
57 | }
58 |
59 | .letter:hover {
60 | background: linear-gradient(360deg, #d5a687, #e79a69);
61 | }
62 |
63 | @media screen and (max-width: 950px) {
64 | .container {
65 | padding: 2rem 2rem;
66 | }
67 | }
68 |
69 | @media screen and (max-width: 550px) {
70 | .container {
71 | padding: 1rem;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/content/HomePage/Watch/left/animeSeasons/animeSeasons.module.css:
--------------------------------------------------------------------------------
1 | .identifier {
2 | display: flex;
3 | padding: 1rem 20px;
4 | margin-top: 2rem;
5 | border-radius: 16px;
6 | border: 2px solid #353841;
7 | gap: 1rem;
8 | align-items: center;
9 | background: linear-gradient(90deg, #38393b, #07070900);
10 | position: relative;
11 | overflow: hidden;
12 | }
13 |
14 | .identifier::after {
15 | content: "";
16 | width: 3px;
17 | border-radius: 50px;
18 | height: 30px;
19 | left: 0px;
20 | top: 50%;
21 | transform: translateY(-50%);
22 | position: absolute;
23 | background: linear-gradient(180deg, #c7c8ca, #f6f6f68f);
24 | }
25 |
26 | .identifier svg {
27 | height: 18px;
28 | width: 18px;
29 | fill: #c9c9c9;
30 | }
31 |
32 | .container {
33 | display: grid;
34 | gap: 20px;
35 | grid-template-columns: repeat(auto-fit, minmax(264px, 1fr));
36 | }
37 | .pagination {
38 | display: flex;
39 | flex-wrap: wrap;
40 | width: 100%;
41 | justify-content: flex-end;
42 | gap: 0.5rem;
43 | margin-top: 8px;
44 | }
45 |
46 | .pagination button {
47 | display: flex;
48 | padding: 9px;
49 | font-size: 18px;
50 | border-radius: 16px;
51 | transition: 0.5s;
52 | background: #070709;
53 | border: 2px solid #9f9e9e85;
54 | color: #bbbbbb;
55 | cursor: pointer;
56 | }
57 | .pagination button:hover,
58 | .pagination button.active {
59 | border: 2px solid #daa28185;
60 | }
61 |
62 | .pagination .pageNumbers {
63 | display: flex;
64 | gap: 0.3rem;
65 | }
66 |
67 | .pagination .pageNumbers button {
68 | min-width: 42px;
69 | display: grid;
70 | }
71 |
--------------------------------------------------------------------------------
/content/HomePage/Home/discover/discover.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | margin-top: 56px;
3 | display: flex;
4 | flex-direction: column;
5 | align-items: center;
6 | width: 100%;
7 | }
8 |
9 | .discoverContainer {
10 | max-width: 90rem;
11 | width: 100%;
12 | display: flex;
13 | flex-direction: column;
14 | align-items: center;
15 | }
16 |
17 | .title {
18 | text-align: center;
19 | font-family: "poppins", sans-serif;
20 | line-height: 33px;
21 | font-size: 30px;
22 | font-weight: 600;
23 | background: -webkit-linear-gradient(#eee, #444);
24 | -webkit-background-clip: text;
25 | background-clip: text;
26 | -webkit-text-fill-color: transparent;
27 | }
28 |
29 | .cards {
30 | margin-top: 26px;
31 | display: grid;
32 | gap: 20px;
33 | grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
34 | width: 94%;
35 | }
36 |
37 | @media screen and (max-width: 1200px) {
38 | .cards {
39 | grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
40 | }
41 | }
42 |
43 | /* pagi */
44 |
45 | .pagitation {
46 | display: flex;
47 | width: 94%;
48 | justify-content: flex-end;
49 | gap: 0.5rem;
50 | }
51 |
52 | .pagitation button {
53 | display: flex;
54 | padding: 9px;
55 | font-size: 18px;
56 | border-radius: 50px;
57 | transition: 0.5s;
58 | background: #070709;
59 | border: 2px solid #9f9e9e85;
60 | color: #bbbbbb;
61 | cursor: pointer;
62 | }
63 | .pagitation button:hover,
64 | .pagitation button.active {
65 | border: 2px solid #daa28185;
66 | }
67 |
68 | @media screen and (max-width: 890px) {
69 | .pagitation {
70 | margin-top: 15px;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/app/layout.js:
--------------------------------------------------------------------------------
1 | import { Analytics } from "@vercel/analytics/next"
2 | import { Inter } from "next/font/google";
3 | import "./globals.css";
4 |
5 | const inter = Inter({ subsets: ["latin"] });
6 |
7 | export const metadata = {
8 | title: `Animeverse || Watch Anime Online For Free`,
9 | description: "Welcome to Animeverse – your ultimate anime destination! Stream your favorite anime titles in sync with friends and explore a vast library of series and movies. Join our vibrant community for an unforgettable anime-watching experience!",
10 | keywords: [
11 | "anime",
12 | "streaming",
13 | "download",
14 | "anime streaming",
15 | "free anime",
16 | "english anime",
17 | "free download",
18 | "anime list",
19 | "english sub",
20 | "kiss-anime",
21 | "english dub",
22 | "watch anime online",
23 | ],
24 | authors: [
25 | {
26 | name: "Animeverse",
27 | url: "https://animeverse-sagas.vercel.app/",
28 | },
29 | ],
30 | revision: "1.0",
31 | robots: {
32 | index: false,
33 | follow: false,
34 | googleBot: {
35 | index: false,
36 | follow: false,
37 | 'max-video-preview': -1,
38 | 'max-image-preview': 'large',
39 | 'max-snippet': -1,
40 | },
41 | },
42 |
43 | other: {
44 | 'google-site-verification': 'ls1OUoOoLjxYsmKMPQ1ML9P99TWDsm7d5hfnGQjW7Tw',
45 | "X-Frame-Options": "SAMEORIGIN",
46 | }
47 | };
48 |
49 | export default function RootLayout({ children }) {
50 | return (
51 |
52 |
53 | {children}
54 |
55 |
56 |
57 | );
58 | }
59 |
--------------------------------------------------------------------------------
/content/HomePage/Search/userSelection/catalog/year/Year.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 | import styles from "./year.module.css"
3 | import { MdOutlineClose } from "react-icons/md";
4 |
5 | const Year = ({ callback }) => {
6 | const { searchData, setSearchData } = callback
7 | const [isYearOff, setIsYearOff] = useState(false)
8 |
9 | useEffect(() => {
10 | if (isYearOff === false) {
11 | setSearchData({ ...searchData, year: "" });
12 | }
13 | // eslint-disable-next-line react-hooks/exhaustive-deps
14 | }, [isYearOff])
15 |
16 | const checkYear = (value) => {
17 | const year = parseInt(value);
18 | if (isYearOff) {
19 | if (!isNaN(year)) {
20 | const currentYear = new Date().getFullYear();
21 | if (year >= 1900 && year <= currentYear) {
22 | setSearchData({ ...searchData, year });
23 | }
24 | } else {
25 | console.error("Please enter a year between 1900 and the current year.");
26 | }
27 | } else {
28 | console.error("Invalid year input.");
29 | }
30 | }
31 |
32 | return (
33 |
34 |
35 | Year
36 | {isYearOff ?
37 | setIsYearOff(false)}>{searchData.year}
38 | : null
39 | }
40 |
41 |
42 | checkYear(e.target.value)} onClick={() => setIsYearOff(true)} />
43 |
44 |
45 | )
46 | }
47 |
48 | export default Year
--------------------------------------------------------------------------------
/components/layout/navbar/Items/Items.jsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link"
2 | import styles from "./Items.module.css"
3 | import { motion } from "framer-motion"
4 |
5 | const Items = ({ display, IsTablet }) => {
6 | const items = [
7 | {
8 | name: "HomePage",
9 | link: "/"
10 | },
11 | {
12 | name: "Series",
13 | link: "/series"
14 | },
15 | {
16 | name: "Animes",
17 | link: "/animes"
18 | },
19 | {
20 | name: "Bookmarks",
21 | link: "/bookmarks"
22 | },
23 | {
24 | name: "Alphabetical List",
25 | link: "/alphabetical"
26 | },
27 | ]
28 |
29 | const containerVariants = {
30 | hidden: { opacity: 0 },
31 | show: {
32 | opacity: 1,
33 | transition: { staggerChildren: 0.15, delayChildren: 0.3, duration: 0.5 }
34 | }
35 | };
36 |
37 | const listItemVariants = {
38 | hidden: { opacity: 0, x: -50 },
39 | show: {
40 | opacity: 1,
41 | x: 0,
42 | transition: { type: 'spring', damping: 15, stiffness: 200 }
43 | }
44 | };
45 |
46 | const listItemTransition = {
47 | duration: 0.1,
48 | ease: "easeInOut"
49 | };
50 |
51 | return (
52 |
53 | {
54 | items.map((item, index) =>
55 | {item.name}
56 | )
57 | }
58 |
59 | )
60 | }
61 |
62 | export default Items
63 |
--------------------------------------------------------------------------------
/components/layout/navbar/search/Search.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useState } from "react";
4 | import styles from "./search.module.css"
5 | import { IoSearch } from "react-icons/io5";
6 | import { useRouter } from "next/navigation";
7 |
8 | const Search = () => {
9 | const [isSearchToggled, setIsSearchToggled] = useState(false)
10 | const [searchText, setSearchText] = useState("")
11 | const router = useRouter()
12 |
13 | const onEnter = (e) => {
14 | if (e.key === "Enter") {
15 | if (searchText !== "") {
16 | router.push(`/search?q=${searchText}`)
17 | }
18 | }
19 | }
20 |
21 | return (
22 | <>
23 | {
24 | isSearchToggled ?
25 |
26 | setSearchText(e.target.value)} onKeyDown={onEnter} />
27 |
: null
28 | }
29 |
30 |
31 |
32 | setSearchText(e.target.value)} onKeyDown={onEnter} />
33 |
34 |
searchText !== "" ? router.push(`/search?q=${searchText}`) : setIsSearchToggled(prev => !prev)}>
35 |
36 |
37 | searchText !== "" ? router.push(`/search?q=${searchText}`) : setIsSearchToggled(prev => !prev)}>
38 | >
39 | )
40 | }
41 |
42 | export default Search
--------------------------------------------------------------------------------
/components/ui/recentReleaseCard/RecentReleasesCard.jsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image"
2 | import loadingStyles from "./loading.module.css"
3 | import styles from "./../card/card.module.css"
4 | import { FaBookmark } from "react-icons/fa6";
5 | import { BsStack } from "react-icons/bs";
6 | import { FaPlayCircle } from "react-icons/fa";
7 | import Link from "next/link";
8 | import { motion } from "framer-motion";
9 |
10 |
11 | const RecentReleasesCard = ({ isLoading = false, info, }) => {
12 | if (isLoading) {
13 | return
14 | }
15 |
16 | const { id, title, image, currentEpisode } = info
17 | return (
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
{title.english}
37 |
38 |
39 |
40 | {currentEpisode} Episodes
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | )
51 | }
52 |
53 | export default RecentReleasesCard
--------------------------------------------------------------------------------
/content/HomePage/Search/userSelection/userSelection.module.css:
--------------------------------------------------------------------------------
1 | .searchInfo {
2 | position: absolute;
3 | top: 50%;
4 | left: 50%;
5 | transform: translate(-50%, -50%);
6 | z-index: 5;
7 | width: 100%;
8 | display: flex;
9 | flex-direction: column;
10 | align-items: center;
11 | }
12 |
13 | .searchInfo .title {
14 | text-align: center;
15 | font-family: "poppins", sans-serif;
16 | line-height: 33px;
17 | font-size: 3rem;
18 | font-weight: 600;
19 | background: -webkit-linear-gradient(#eee, #444);
20 | -webkit-background-clip: text;
21 | background-clip: text;
22 | -webkit-text-fill-color: transparent;
23 | width: 100%;
24 | line-height: 42px;
25 | }
26 |
27 | .searchInfo .search {
28 | background: #0f0f13;
29 | border-radius: 20px;
30 | padding: 10px 10px 9px 15px;
31 | border: 2px solid #1c1c1e;
32 | display: flex;
33 | margin-top: 1.9rem;
34 | width: -webkit-fill-available;
35 | width: -moz-fill-available;
36 | max-width: 72rem;
37 | height: 5rem;
38 | margin-inline: 20px;
39 | }
40 |
41 | .searchInfo .search .searchInput {
42 | background: transparent;
43 | width: 100%;
44 | color: white;
45 | font-family: "poppins", sans-serif;
46 | font-size: 15px;
47 | border: none;
48 | outline: none;
49 | height: 100%;
50 | }
51 |
52 | .searchInfo .search button {
53 | width: 62px;
54 | position: relative;
55 | padding: 11px;
56 | cursor: pointer;
57 | font-size: 20px;
58 | border-radius: 15px;
59 | display: flex;
60 | align-items: center;
61 | z-index: 2;
62 | justify-content: center;
63 | background: transparent;
64 | border: 2px solid #252527;
65 | color: white;
66 | }
67 |
68 | .selectionArea {
69 | display: flex;
70 | width: 100%;
71 | max-width: 86rem;
72 | justify-content: center;
73 | gap: 10px;
74 | margin-top: 20px;
75 | flex-wrap: wrap;
76 | }
77 |
--------------------------------------------------------------------------------
/components/ui/banner/videoPlay/VideoPlay.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useEffect, useRef, useState } from "react";
4 | import styles from "./videoPlay.module.css";
5 |
6 | const VideoPlay = ({ url, display, play = false }) => {
7 | const [videoLoaded, setVideoLoaded] = useState(false);
8 | const vidRef = useRef(null);
9 |
10 | useEffect(() => {
11 | const video = vidRef.current;
12 |
13 | const handleCanPlay = () => {
14 | if (play && video) {
15 | video.play().catch(error => {
16 | console.error('Failed to start playback:', error);
17 | });
18 | }
19 | };
20 |
21 | const handleLoadedMetadata = () => {
22 | setVideoLoaded(true);
23 | };
24 |
25 | if (video) {
26 | video.addEventListener('canplay', handleCanPlay);
27 | video.addEventListener('loadedmetadata', handleLoadedMetadata);
28 |
29 | // Start loading the video when component mounts
30 | video.load();
31 |
32 | return () => {
33 | video.removeEventListener('canplay', handleCanPlay);
34 | video.removeEventListener('loadedmetadata', handleLoadedMetadata);
35 | };
36 | }
37 | }, [play]);
38 |
39 | const handleVideoClick = () => {
40 | const video = vidRef.current;
41 | if (video && !videoLoaded) {
42 | video.play().catch(error => {
43 | console.error('Failed to start playback:', error);
44 | });
45 | }
46 | };
47 |
48 | return (
49 |
50 |
51 |
52 |
53 |
54 | );
55 | };
56 |
57 | export default VideoPlay;
58 |
--------------------------------------------------------------------------------
/app/(home)/info/[id]/page.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react-hooks/exhaustive-deps */
2 | "use client"
3 |
4 | import Image from "next/image"
5 | import styles from "./info.module.css"
6 | import AnimeInfo from "@/content/HomePage/Info/animeInfo/AnimeInfo"
7 | import Recommendation from "@/content/HomePage/Watch/right/recommendation/Recommendation"
8 | import MostPopular from "@/content/HomePage/Watch/right/mostPopular/MostPopular"
9 | import { useEffect, useState } from "react"
10 | import { fetchData } from "@/lib/FetchData"
11 | import Loading from "@/components/layout/loading/Loading"
12 | import AtoZalphabet from "@/components/ui/AtoZalphabet/AtoZalphabet"
13 |
14 | const Info = ({ params }) => {
15 | const { id } = params
16 | const [animeInfo, setAnimeInfo] = useState({})
17 | const [isLoaded, setIsLoaded] = useState(false)
18 |
19 | useEffect(() => {
20 | const fetchAnimeInfo = async () => {
21 | setIsLoaded(false)
22 | let data = await fetchData(`/meta/anilist/data/${id}`, 86400)
23 | setAnimeInfo(data.data)
24 | document.title = `Animeverse - ${data.data.title.english}`;
25 | if (data.ok) {
26 | setIsLoaded(true)
27 | }
28 | }
29 | fetchAnimeInfo()
30 | }, [])
31 |
32 | return !isLoaded ? : (
33 | <>
34 |
35 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | >
52 | )
53 | }
54 |
55 | export default Info
56 |
--------------------------------------------------------------------------------
/content/HomePage/Home/recentRelease/RecentRelease.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useEffect, useState } from "react"
4 | import styles from "./recentRelease.module.css"
5 | import RecentReleasesCard from "@/components/ui/recentReleaseCard/RecentReleasesCard"
6 | import { fetchData } from "@/lib/FetchData"
7 |
8 | const RecentRelease = () => {
9 | const [isLoaded, setIsLoaded] = useState(true)
10 | const [datas, setDatas] = useState(null)
11 | const [page, setPage] = useState(1)
12 |
13 |
14 | useEffect(() => {
15 | setIsLoaded(false)
16 | fetchData(`/meta/anilist/advanced-search?status=RELEASING&page=${page}&perPage=10`).then(data => {
17 | {
18 | datas === null ? setDatas(data.data) : setDatas({
19 | ...datas,
20 | ...data.data,
21 | results: [...datas?.results, ...data.data?.results],
22 | totalResults: datas?.totalResults + data.data?.totalResults
23 | })
24 | }
25 |
26 | }).finally(() => setIsLoaded(true))
27 | // eslint-disable-next-line react-hooks/exhaustive-deps
28 | }, [page])
29 |
30 | return (
31 |
32 |
33 |
34 |
Recent Episode Releases
35 |
Explore And Find Best Anime!
36 |
37 |
38 |
39 |
40 | {!isLoaded ? Array.from({ length: 10 * page }).map((_, index) => ) : datas?.results?.map((data, index) => (
41 |
42 | ))}
43 |
44 |
45 |
datas.hasNextPage && setPage(prev => prev + 1)}>Show More
46 |
47 |
48 |
49 | )
50 | }
51 |
52 | export default RecentRelease
--------------------------------------------------------------------------------
/content/HomePage/Watch/left/videoSelector/videoSelector.module.css:
--------------------------------------------------------------------------------
1 | .identifier {
2 | display: flex;
3 | padding: 1rem 20px;
4 | margin-top: 2rem;
5 | border-radius: 16px;
6 | border: 2px solid #353841;
7 | gap: 1rem;
8 | align-items: center;
9 | background: linear-gradient(90deg, #38393b, #07070900);
10 | position: relative;
11 | overflow: hidden;
12 | }
13 |
14 | .identifier::after {
15 | content: "";
16 | width: 3px;
17 | border-radius: 50px;
18 | height: 30px;
19 | left: 0px;
20 | top: 50%;
21 | transform: translateY(-50%);
22 | position: absolute;
23 | background: linear-gradient(180deg, #c7c8ca, #f6f6f68f);
24 | }
25 |
26 | .identifier svg {
27 | height: 18px;
28 | width: 18px;
29 | fill: #c9c9c9;
30 | }
31 |
32 | .selector {
33 | width: -webkit-fill-available;
34 | width: -moz-available;
35 | display: flex;
36 | gap: 2rem;
37 | margin-top: 1.4rem;
38 | }
39 |
40 | .selector button {
41 | background: #070709;
42 | border: 2px solid #373a43;
43 | border-radius: 20px;
44 | font-size: 15px;
45 | padding: 10px 20px;
46 | min-width: max-content;
47 | color: #8991a3;
48 | font-family: "poppins", sans-serif;
49 | font-weight: 500;
50 | cursor: pointer;
51 | transition: 0.5s;
52 | }
53 |
54 | .selector button:hover {
55 | color: #c99372;
56 | background: #131315;
57 | }
58 |
59 | .selector button:last-child {
60 | display: flex;
61 | align-items: center;
62 | gap: 4px;
63 | }
64 |
65 | @media screen and (max-width: 750px) {
66 | .selector {
67 | flex-wrap: wrap;
68 | justify-content: center;
69 | gap: 15px;
70 | }
71 | }
72 |
73 | @media screen and (max-width: 1850px) {
74 | .selector {
75 | flex-wrap: wrap;
76 | justify-content: center;
77 | gap: 15px;
78 | }
79 | }
80 |
81 | .active {
82 | background: linear-gradient(174deg, #d5a687, #e79a69) !important;
83 | border: 2px solid #efac87 !important;
84 | color: black !important;
85 | }
86 |
--------------------------------------------------------------------------------
/content/HomePage/Watch/right/mostPopular/MostPopular.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import Image from "next/image";
4 | import styles from "./mostPopular.module.css"
5 | import { IoMdPricetag as TagIcon } from "react-icons/io";
6 | import { BiSolidLike } from "react-icons/bi";
7 | import { BsStack } from "react-icons/bs";
8 | import Link from "next/link";
9 | import { useEffect, useState } from "react";
10 | import { fetchData } from "@/lib/FetchData";
11 |
12 | const MostPopular = () => {
13 | const [popularSeries, setPopularSeries] = useState([])
14 |
15 | useEffect(() => {
16 | const fetchPopularAnimes = async () => {
17 | const data = await fetchData(`/meta/anilist/trending?perPage=5`)
18 |
19 | if (data.ok) {
20 | setPopularSeries(data.data)
21 | }
22 | }
23 |
24 | fetchPopularAnimes()
25 | }, [])
26 |
27 |
28 | return (
29 |
30 |
31 |
32 | Top Airing Series
33 |
34 |
35 |
36 | {popularSeries.results?.map((data, index) => (
37 |
38 |
39 |
{index + 1}
40 |
41 |
42 |
43 |
44 |
45 |
{data?.title?.english}
46 |
{data.rating * 10 / 100} Rating
47 |
{data?.totalEpisodes} Episodes
48 |
49 |
50 | ))}
51 |
52 |
53 |
54 | )
55 | }
56 |
57 | export default MostPopular
--------------------------------------------------------------------------------
/content/HomePage/Info/animeInfo/AnimeInfo.jsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image"
2 | import styles from "./animeInfo.module.css"
3 | import Link from "next/link"
4 | import { FaHouse as HomeIcon } from "react-icons/fa6";
5 | import { FaAngleRight as RightArrow } from "react-icons/fa6";
6 | import { FaBookmark } from "react-icons/fa6";
7 | import Info from "./Info/Info";
8 | import EpisodeSelector from "@/components/ui/episodeSelector/EpisodeSelector";
9 |
10 | const AnimeInfo = ({ info }) => {
11 | const {
12 | id,
13 | title,
14 | image,
15 | description,
16 | status,
17 | duration,
18 | genres,
19 | studios,
20 | startDate,
21 | season,
22 | type
23 | } = info
24 | return (
25 |
26 |
27 |
28 |
29 | Write a review
30 |
31 |
32 |
33 |
34 |
35 |
36 | HomePage
37 | {title?.english}
38 |
39 |
40 |
{title?.english}
41 |
42 |
43 |
44 |
45 |
46 | {genres?.map((genre) => (
47 | {genre}
48 | ))}
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | )
60 | }
61 |
62 | export default AnimeInfo
--------------------------------------------------------------------------------
/content/HomePage/Watch/left/videoPlayer/videoPlayer.module.css:
--------------------------------------------------------------------------------
1 | iframe.container {
2 | aspect-ratio: 16 / 9.1;
3 | }
4 | iframe.container::-webkit-scrollbar {
5 | display: none;
6 | }
7 |
8 | .container {
9 | max-width: 100%;
10 | border-radius: 15px !important;
11 | border: 2px solid #32353e !important;
12 | }
13 |
14 | .mediaPoster {
15 | display: block;
16 | position: absolute;
17 | top: 0;
18 | left: 0;
19 | object-fit: cover;
20 | opacity: 0;
21 | width: 100%;
22 | height: 100%;
23 | background-color: black;
24 | }
25 |
26 | .mediaPoster img {
27 | width: 100%;
28 | height: 100%;
29 | object-fit: cover;
30 | }
31 |
32 | .mediaPoster[data-visible] {
33 | opacity: 1;
34 | }
35 |
36 | .loading {
37 | aspect-ratio: 16 / 9.1;
38 | max-width: 100%;
39 | border-radius: 15px !important;
40 | position: relative;
41 | border-radius: 15px;
42 | display: grid;
43 | place-content: center;
44 | background: linear-gradient(
45 | 20deg,
46 | #0c0d10,
47 | #0c0d10a1,
48 | #0c0d1000,
49 | transparent
50 | );
51 | }
52 |
53 | .i {
54 | display: inline-block;
55 | position: relative;
56 | width: 80px;
57 | height: 80px;
58 | }
59 | .i div {
60 | position: absolute;
61 | border: 4px solid #fff;
62 | opacity: 1;
63 | border-radius: 50%;
64 | animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite;
65 | }
66 | .i div:nth-child(2) {
67 | animation-delay: -0.5s;
68 | }
69 | @keyframes lds-ripple {
70 | 0% {
71 | top: 36px;
72 | left: 36px;
73 | width: 0;
74 | height: 0;
75 | opacity: 0;
76 | }
77 | 4.9% {
78 | top: 36px;
79 | left: 36px;
80 | width: 0;
81 | height: 0;
82 | opacity: 0;
83 | }
84 | 5% {
85 | top: 36px;
86 | left: 36px;
87 | width: 0;
88 | height: 0;
89 | opacity: 1;
90 | }
91 | 100% {
92 | top: 0px;
93 | left: 0px;
94 | width: 72px;
95 | height: 72px;
96 | opacity: 0;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/content/HomePage/Search/userSelection/options/Option.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import { IoIosArrowDown } from "react-icons/io";
3 | import styles from "./options.module.css"
4 | import { useState } from "react";
5 | import { FaRegCircle, FaRegDotCircle } from "react-icons/fa";
6 |
7 | const Option = ({ defaultOption, items, icon, multipleSelection }) => {
8 | const [isSelected, setIsSelected] = useState(false)
9 | const [selected, setSelected] = useState([])
10 |
11 | const onHandleSelect = (itemSelected) => {
12 | if (multipleSelection) {
13 | if (!selected.includes(itemSelected)) {
14 | setSelected([...selected, itemSelected]);
15 | } else {
16 | setSelected(selected.filter(item => item !== itemSelected));
17 | }
18 | }
19 |
20 | else {
21 | if (itemSelected !== selected) {
22 | setSelected(itemSelected)
23 | }
24 | else {
25 | setSelected("")
26 | }
27 | }
28 | };
29 |
30 | return (
31 |
32 |
setIsSelected(prev => !prev)}>
33 |
35 | {icon}
36 | {defaultOption}
37 |
38 |
{typeof selected === "string" ? selected.length > 8 ? `${selected.slice(0, 8)}...` : selected : selected.slice(0, 2).join(", ")}
39 |
40 |
41 | {isSelected &&
42 |
43 | {items.map((item, index) => (
44 |
onHandleSelect(item)}>
45 | {selected.includes(item) ? : }
46 | {item}
47 |
48 | ))}
49 |
50 | }
51 |
52 |
53 | )
54 | }
55 |
56 | export default Option
--------------------------------------------------------------------------------
/components/ui/banner/banner.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | max-width: 90rem;
3 | width: -webkit-fill-available;
4 | width: -moz-available;
5 | min-width: 369px;
6 | height: 25rem;
7 | background: #0c0d10;
8 | position: relative;
9 | border: 2px solid #141416;
10 | border-radius: 20px;
11 | overflow: hidden;
12 | }
13 |
14 | .container::after {
15 | content: "";
16 | position: absolute;
17 | top: 1rem;
18 | left: 0;
19 | width: 4px;
20 | border-radius: 50%;
21 | height: 173px;
22 | background: linear-gradient(50deg, transparent, #3d3d3d, transparent);
23 | transition: 0.5s;
24 | z-index: 6;
25 | }
26 |
27 | .container::before {
28 | content: "";
29 | position: absolute;
30 | top: 0;
31 | left: 3rem;
32 | height: 4px;
33 | border-radius: 50%;
34 | width: 173px;
35 | background: linear-gradient(50deg, transparent, #3d3d3d, transparent);
36 | transition: 0.5s;
37 | z-index: 6;
38 | }
39 |
40 | .container:hover::after {
41 | top: 5rem;
42 | }
43 |
44 | .container:hover::before {
45 | left: 5rem;
46 | }
47 |
48 | .bannerImage {
49 | position: absolute;
50 | width: 1000px;
51 | height: 100%;
52 | object-fit: cover;
53 | z-index: 1;
54 | right: 0;
55 | top: 0;
56 | /* transform: scaleX(-1); */
57 | overflow: hidden;
58 | }
59 |
60 | .bannerImage img {
61 | object-fit: cover;
62 | }
63 |
64 | .bannerImage::before,
65 | .bannerImage::after {
66 | content: "";
67 | position: absolute;
68 | top: 0;
69 | left: 0;
70 | width: 48rem;
71 | height: 100%;
72 | background: linear-gradient(
73 | 90deg,
74 | #0c0d10,
75 | #0c0d10a1,
76 | #0c0d1000,
77 | transparent
78 | );
79 | }
80 |
81 | @media screen and (max-width: 850px) {
82 | .bannerImage img {
83 | object-position: 100% 50%;
84 | }
85 | }
86 |
87 | @media screen and (max-width: 530px) {
88 | .bannerImage::before,
89 | .bannerImage::after {
90 | left: 29rem;
91 | }
92 | .bannerImage img {
93 | object-position: 30rem;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/components/ui/card/Card.jsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image"
2 | import styles from "./card.module.css"
3 | import loadingStyles from "./loading.module.css"
4 | import { FaBookmark } from "react-icons/fa6";
5 | import { AiFillLike } from "react-icons/ai";
6 | import { BsStack } from "react-icons/bs";
7 | import { FaPlayCircle } from "react-icons/fa";
8 | import Link from "next/link";
9 |
10 | const Card = ({ hasborder = false, isLoading = false, data }) => {
11 |
12 | if (isLoading) {
13 | return
;
14 | }
15 |
16 | const { id, title, image, rating, totalEpisodes, status } = data
17 | return (
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
{status}
27 | {/*
New
*/}
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
{title.english || title.romaji}
38 |
39 |
40 |
{rating} Rating
41 |
42 |
43 |
44 | {totalEpisodes} Episodes
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | {hasborder ?
:
}
55 |
56 | )
57 | }
58 |
59 | export default Card
--------------------------------------------------------------------------------
/content/HomePage/Search/userSelection/catalog/Catalog.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useState } from "react";
4 | import Type from "./Type/Type";
5 | import styles from "./catalog.module.css"
6 | import Genres from "./genres/Genres";
7 | import Season from "./season/Season";
8 | import Status from "./status/Status";
9 | import Year from "./year/Year";
10 | import { useRouter, useSearchParams } from "next/navigation";
11 |
12 | const Catalog = () => {
13 | const param = useSearchParams()
14 | const router = useRouter()
15 |
16 | const [searchData, setSearchData] = useState({
17 | genres: param.get("genres") || [],
18 | status: param.get("status") || "",
19 | season: param.get("season") || "",
20 | type: param.get("type") || "",
21 | year: param.get("year") || new Date().getFullYear(),
22 | })
23 |
24 |
25 | const FilterClick = () => {
26 | const searchDataString = Object.keys(searchData)
27 | .map(k => {
28 | if (k === 'genres') {
29 | return searchData[k].length ? encodeURIComponent(k) + '=' + encodeURIComponent(JSON.stringify(searchData[k])) : null;
30 | } else {
31 | return searchData[k] !== "" ? encodeURIComponent(k) + '=' + encodeURIComponent(searchData[k]) : null;
32 | }
33 | })
34 | .filter(Boolean) // Filter out null values
35 | .join('&');
36 |
37 | router.push(`/search?${searchDataString}`);
38 | }
39 |
40 |
41 |
42 | return (
43 |
44 |
Catalog
45 |
46 | {/* year */}
47 |
48 | {/* season */}
49 |
50 |
51 |
52 |
53 |
54 |
Apply
55 |
56 |
57 | )
58 | }
59 |
60 | export default Catalog
--------------------------------------------------------------------------------
/content/HomePage/Watch/right/mostPopular/mostPopular.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | border: 2px solid #23252b;
3 | padding: 30px 20px;
4 | width: 100%;
5 | border-radius: 15px;
6 | overflow: hidden;
7 | margin-top: 20px;
8 | position: relative;
9 | top: -20px;
10 | }
11 |
12 | .heading {
13 | display: flex;
14 | gap: 20px;
15 | font-family: "poppins", sans-serif;
16 | font-size: 16px;
17 | font-weight: 500;
18 | align-items: center;
19 | }
20 |
21 | .heading::before {
22 | content: "";
23 | top: 32px;
24 | left: 0.9px;
25 | height: 44px;
26 | width: 3px;
27 | border-radius: 50rem;
28 | background: #e29b73;
29 | position: absolute;
30 | }
31 |
32 | .heading .icon {
33 | display: grid;
34 | place-content: center;
35 | width: 44px;
36 | background: linear-gradient(308deg, #d5a687, #e79a69);
37 | height: 44px;
38 | border-radius: 15px;
39 | font-size: 25px;
40 | color: black;
41 | }
42 |
43 | .series {
44 | text-decoration: none;
45 | color: inherit;
46 | margin-top: 20px;
47 | display: flex;
48 | align-items: center;
49 | gap: 15px;
50 | background: linear-gradient(180deg, #0c0d11, #060708);
51 | border-radius: 10px;
52 | }
53 |
54 | .series .number {
55 | height: 35px;
56 | width: 54px;
57 | background: #070709;
58 | display: grid;
59 | place-content: center;
60 | border-radius: 12px;
61 | border: 2px solid #353841;
62 | }
63 |
64 | .series .image img {
65 | object-fit: cover;
66 | border-radius: 15px;
67 | }
68 |
69 | .info {
70 | display: flex;
71 | flex-direction: column;
72 | gap: 0.5rem;
73 | width: 100%;
74 | }
75 |
76 | .title {
77 | overflow: hidden;
78 | width: 100%;
79 | color: #fefefe;
80 | font-size: 16px;
81 | max-width: 15rem;
82 | display: -webkit-box;
83 | -webkit-line-clamp: 1;
84 | -webkit-box-orient: vertical;
85 | }
86 |
87 | .rating,
88 | .episodes {
89 | font-size: 14px;
90 | color: #a9a9ab;
91 | display: flex;
92 | gap: 4px;
93 | align-items: center;
94 | }
95 |
96 | .rating {
97 | font-size: 14px;
98 | color: #e0a688;
99 | }
100 |
--------------------------------------------------------------------------------
/content/HomePage/Home/discover/Discover.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import Card from "@/components/ui/card/Card"
3 | import styles from "./discover.module.css"
4 | import Options from "./options/Options"
5 | import { useEffect, useState } from "react"
6 | import { fetchData } from "@/lib/FetchData"
7 | import { IoIosArrowBack as LeftArrow, IoIosArrowForward as RightArrow } from "react-icons/io";
8 |
9 | const Discover = () => {
10 | const [datas, setDatas] = useState([])
11 | const [page, setPage] = useState(1)
12 | const [category, setCategory] = useState("Trending")
13 | const [isLoaded, setIsLoaded] = useState(false)
14 |
15 |
16 | useEffect(() => {
17 | setIsLoaded(false)
18 | if (category === "Trending") {
19 | fetchData(`/meta/anilist/trending?page=${page}&perPage=8`).then(data => setDatas(data.data)).finally(() => setIsLoaded(true))
20 | }
21 | else {
22 | fetchData(`/meta/anilist/advanced-search?genres=["${category}"]&page=${page}`).then(data => setDatas(data.data)).finally(() => setIsLoaded(true))
23 | }
24 | }, [page, category])
25 |
26 |
27 | return (
28 |
29 |
30 |
31 |
Discover Best Anime
32 |
Explore Our Hottest Categories!
33 |
34 |
35 |
36 |
37 |
38 | datas.currentPage >= 1 ? setPage(prev => prev - 1) : null}>
39 | datas.hasNextPage ? setPage(prev => prev + 1) : null} className={styles.active}>
40 |
41 |
42 |
43 |
44 | {!isLoaded ? Array.from({ length: 8 }).map((_, index) => ) : datas?.results?.map((data) => (
45 |
46 | //
47 | ))}
48 |
49 |
50 |
51 |
52 | )
53 | }
54 |
55 | export default Discover
--------------------------------------------------------------------------------
/content/HomePage/Watch/left/videoPlayer/VideoPlayer.jsx:
--------------------------------------------------------------------------------
1 | import '@vidstack/react/player/styles/default/theme.css';
2 | import '@vidstack/react/player/styles/default/layouts/video.css';
3 |
4 | import styles from "./videoPlayer.module.css"
5 |
6 | import { defaultLayoutIcons, DefaultVideoLayout } from '@vidstack/react/player/layouts/default';
7 | import { MediaPlayer, MediaProvider, Poster } from '@vidstack/react';
8 | import { useSearchParams } from 'next/navigation';
9 | import { useEffect, useState } from 'react';
10 | import { fetchData } from '@/lib/FetchData';
11 |
12 |
13 | const VideoPlayer = ({ data }) => {
14 | const { animeInfo, videoSelected } = data;
15 |
16 | const { cover, episodes } = animeInfo;
17 |
18 | const [watch, setWatch] = useState(videoSelected?.url)
19 | const [loading, setLoading] = useState(true);
20 |
21 | const searchParams = useSearchParams();
22 |
23 | useEffect(() => {
24 | const fetchVideos = async () => {
25 | setLoading(true);
26 | try {
27 | if (animeInfo !== null) {
28 | const data = await fetchData(`/meta/anilist/watch/${searchParams.get('episodeID') || episodes?.[0]?.id}`);
29 | if (data.ok) {
30 | setWatch(data.data);
31 | }
32 | }
33 | } catch (error) {
34 | console.error("Error fetching videos:", error);
35 | } finally {
36 | setLoading(false);
37 | }
38 | };
39 |
40 | fetchVideos();
41 | }, [searchParams, animeInfo, episodes]);
42 |
43 | return loading ?
44 |
45 | : videoSelected?.server === "default" ? (
46 | item.quality === "default")[0].url} className={styles.container} >
47 |
48 |
49 |
50 |
51 |
52 | ) : (
53 |
54 | )
55 | }
56 |
57 | export default VideoPlayer
--------------------------------------------------------------------------------
/content/HomePage/Watch/left/animeSeasons/AnimeSeasons.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import styles from "./animeSeasons.module.css";
4 | import { FaSwatchbook } from "react-icons/fa6";
5 | import SeasonSelectorCard from "./card/SeasonSelectorCard";
6 | import { useEffect, useState } from "react";
7 | import { IoIosArrowBack as LeftArrow, IoIosArrowForward as RightArrow } from "react-icons/io";
8 |
9 | const AnimeSeasons = ({ data }) => {
10 | const pageSize = 6; // Number of items per page
11 | const [dataPage, setDataPage] = useState(1);
12 | const [totalPages, setTotalPages] = useState(Math.ceil(data.length / pageSize));
13 | const [datas, setDatas] = useState([]);
14 |
15 | useEffect(() => {
16 | const updateDatas = () => {
17 | const start = (dataPage - 1) * pageSize;
18 | const end = start + pageSize;
19 | setDatas(datas.slice(start, end));
20 | setTotalPages(Math.ceil(datas.length / pageSize));
21 | };
22 | updateDatas();
23 | }, [dataPage, datas]);
24 |
25 | const goToPage = (pageNumber) => {
26 | if (pageNumber >= 1 && pageNumber <= totalPages) {
27 | setDataPage(pageNumber);
28 | }
29 | };
30 |
31 | return (
32 | <>
33 |
34 |
35 | Other Seasons
36 |
37 |
38 |
39 |
goToPage(dataPage - 1)} disabled={dataPage === 1}>
40 |
41 | {Array.from({ length: totalPages }, (_, index) => (
42 | goToPage(index + 1)} className={dataPage === index + 1 ? styles.active : ""}>
43 | {index + 1}
44 |
45 | ))}
46 |
47 |
goToPage(dataPage + 1)} disabled={dataPage === totalPages}>
48 |
49 |
50 |
51 | {data.map((items, index) => (
52 |
53 |
54 |
55 | ))}
56 |
57 | >
58 | );
59 | };
60 |
61 | export default AnimeSeasons;
62 |
--------------------------------------------------------------------------------
/components/layout/navbar/search/search.module.css:
--------------------------------------------------------------------------------
1 | .searchcontainer {
2 | background: #0f0f13;
3 | border-radius: 50px;
4 | padding: 10px 50px 9px 15px;
5 | position: absolute;
6 | right: 24px;
7 | z-index: 1;
8 | overflow: hidden;
9 | border: 2px solid #1c1c1e;
10 | animation: opensearch 0.5s forwards;
11 | }
12 |
13 | .searchInput {
14 | background: transparent;
15 | width: 100%;
16 | color: white;
17 | font-family: "poppins", sans-serif;
18 | font-size: 15px;
19 | border: none;
20 | outline: none;
21 | }
22 |
23 | .search {
24 | position: relative;
25 | padding: 11px;
26 | cursor: pointer;
27 | font-size: 16px;
28 | border-radius: 50px;
29 | display: flex;
30 | align-items: center;
31 | z-index: 2;
32 | justify-content: center;
33 | background: transparent;
34 | border: 2px solid #252527;
35 | color: white;
36 | }
37 |
38 | .DISsearch {
39 | position: relative;
40 | padding: 11px;
41 | cursor: pointer;
42 | font-size: 16px;
43 | border-radius: 50px;
44 | display: flex;
45 | align-items: center;
46 | z-index: 2;
47 | justify-content: center;
48 | background: transparent;
49 | border: 2px solid #252527;
50 | color: white;
51 | }
52 |
53 | @keyframes opensearch {
54 | 0% {
55 | width: 0px;
56 | }
57 | 100% {
58 | width: 252px;
59 | }
60 | }
61 |
62 | /* responsive */
63 |
64 | .searchcontainerResponsive {
65 | display: none;
66 | }
67 |
68 | @media screen and (max-width: 1080px) and (min-width: 770px) {
69 | .searchcontainerResponsive {
70 | display: block;
71 | position: relative;
72 | width: 100% !important;
73 | right: 0;
74 | }
75 | .search {
76 | display: none;
77 | }
78 | .searchDisplay {
79 | display: flex;
80 | width: 100%;
81 | justify-content: flex-end;
82 | position: relative;
83 | margin-inline: 18px;
84 | }
85 | .DISsearch {
86 | position: absolute;
87 | margin-top: 2px;
88 | }
89 | .isShow {
90 | display: none;
91 | }
92 | }
93 |
94 | @media screen and (min-width: 1080px) and (min-width: 770px) {
95 | .DISsearch {
96 | display: none;
97 | }
98 | }
99 |
100 | @media screen and (max-width: 770px) {
101 | .searchDisplay {
102 | display: none;
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/content/HomePage/Watch/left/videoSelector/VideoSelector.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 | import { useRouter, useSearchParams } from 'next/navigation'
3 | import styles from "./videoSelector.module.css"
4 | import { FaCirclePlay } from "react-icons/fa6";
5 | import { FaDownload } from "react-icons/fa6";
6 | import { fetchData } from "@/lib/FetchData";
7 |
8 | const VideoSelector = ({ episodeID, setVideoSelected, videoSelected }) => {
9 | const [servers, setServers] = useState([])
10 | const router = useRouter()
11 | const searchParams = useSearchParams();
12 |
13 | useEffect(() => {
14 | const fetchedServer = async () => {
15 | try {
16 | if (episodeID !== undefined) {
17 | const data = await fetchData(`/meta/anilist/servers/${episodeID}`)
18 |
19 | if (data.ok) {
20 | setServers(data?.data)
21 | if (videoSelected?.server !== "default") {
22 | let url = data?.data?.find(server => server.name === videoSelected?.server)
23 | setVideoSelected({ server: url?.name, url: url?.url })
24 | }
25 | // setVideoSelected()
26 | }
27 | }
28 | } catch (error) {
29 | console.log(error);
30 | }
31 | }
32 | fetchedServer()
33 | // eslint-disable-next-line react-hooks/exhaustive-deps
34 | }, [searchParams])
35 |
36 | const handleServerSelection = (server, url) => {
37 | setVideoSelected({ server, url });
38 | };
39 |
40 | return (
41 | <>
42 |
43 |
44 | Select Video Quality or Download it
45 |
46 |
47 |
48 | {servers.slice(0, 4)?.map((server, index) => (
49 | handleServerSelection(server.name, server.url)} className={videoSelected?.server === server.name ? styles.active : ""}>
50 | {server.name}
51 |
52 | ))}
53 | handleServerSelection("default", "")} className={videoSelected?.server === "default" ? styles.active : ""}>Default
54 | router.push(`/download/${episodeID}`)}> Download
55 |
56 | >
57 | )
58 | }
59 |
60 | export default VideoSelector;
61 |
--------------------------------------------------------------------------------
/content/HomePage/Watch/left/animeInfo/animeInfo.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | position: relative;
3 | font-family: "poppins", sans-serif;
4 | }
5 |
6 | .backgroundImage {
7 | position: relative;
8 | width: 100%;
9 | height: 500px;
10 | object-fit: cover;
11 | border-radius: 20px;
12 | filter: brightness(50%);
13 | }
14 | .backgroundImage::after {
15 | content: "";
16 | position: absolute;
17 | bottom: 0;
18 | left: 0;
19 | width: 100%;
20 | height: 100%;
21 | background: linear-gradient(
22 | 360deg,
23 | #0c0d10,
24 | #0c0d10a1,
25 | #0c0d1000,
26 | transparent
27 | );
28 | }
29 |
30 | .backgroundImage img {
31 | object-fit: cover;
32 | filter: brightness(0.5);
33 | }
34 |
35 | .info {
36 | position: absolute;
37 | top: 50%;
38 | left: 50%;
39 | transform: translate(-50%, -50%);
40 | display: flex;
41 | flex-direction: column;
42 | align-items: center;
43 | width: 100%;
44 | text-align: center;
45 | }
46 |
47 | .pagination {
48 | display: flex;
49 | align-items: center;
50 | gap: 8px;
51 | color: #eeefef;
52 | font-size: 16px;
53 | font-weight: 500;
54 | }
55 |
56 | .pagination span {
57 | display: flex;
58 | align-items: center;
59 | gap: 2px;
60 | }
61 |
62 | .pagination svg {
63 | font-size: medium;
64 | }
65 |
66 | .pagination a svg {
67 | fill: #e49a70 !important;
68 | font-size: 20px;
69 | }
70 |
71 | .pagination a {
72 | text-decoration: none;
73 | color: inherit;
74 | display: flex;
75 | align-items: center;
76 | gap: 2px;
77 | }
78 |
79 | .pagination a:last-child {
80 | color: #7c8394;
81 | }
82 |
83 | .pagination a:last-child svg {
84 | fill: #7c8394 !important;
85 | font-size: medium;
86 | }
87 |
88 | .title {
89 | text-align: center;
90 | font-size: 34px;
91 | font-weight: 600;
92 | background: -webkit-linear-gradient(#dcdcde, #5a5a5a);
93 | background-clip: text;
94 | -webkit-text-fill-color: transparent;
95 | margin-top: 16px;
96 | display: -webkit-box;
97 | -webkit-line-clamp: 3;
98 | -webkit-box-orient: vertical;
99 | }
100 |
101 | .notification {
102 | color: #7f8798;
103 | font-size: 16px;
104 | font-weight: 500;
105 | margin-top: 10px;
106 | }
107 |
108 | .notification span {
109 | color: #e49b71;
110 | font-weight: 600;
111 | }
112 |
--------------------------------------------------------------------------------
/content/HomePage/Home/recentRelease/recentRelease.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | margin-top: 56px;
3 | display: flex;
4 | flex-direction: column;
5 | align-items: center;
6 | width: 100%;
7 | }
8 |
9 | .recentReleaseContainer {
10 | max-width: 90rem;
11 | width: 100%;
12 | display: flex;
13 | flex-direction: column;
14 | align-items: center;
15 | }
16 |
17 | .title {
18 | text-align: center;
19 | font-family: "poppins", sans-serif;
20 | line-height: 33px;
21 | font-size: 30px;
22 | font-weight: 600;
23 | background: -webkit-linear-gradient(#eee, #444);
24 | -webkit-background-clip: text;
25 | background-clip: text;
26 | -webkit-text-fill-color: transparent;
27 | }
28 |
29 | .cards {
30 | margin-top: 26px;
31 | display: grid;
32 | gap: 20px;
33 | grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
34 | width: 94%;
35 | }
36 |
37 | @media screen and (max-width: 1200px) {
38 | .cards {
39 | grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
40 | }
41 | }
42 |
43 | @media screen and (max-width: 840px) {
44 | .cards {
45 | grid-template-columns: repeat(auto-fit, minmax(195px, 1fr));
46 | }
47 | }
48 |
49 | @media screen and (max-width: 530px) {
50 | .cards {
51 | grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
52 | }
53 | }
54 |
55 | /* pagi */
56 |
57 | .pagitation {
58 | display: flex;
59 | width: 94%;
60 | justify-content: flex-end;
61 | gap: 0.5rem;
62 | }
63 |
64 | .pagitation button {
65 | display: flex;
66 | padding: 9px;
67 | font-size: 18px;
68 | border-radius: 50px;
69 | transition: 0.5s;
70 | background: #070709;
71 | border: 2px solid #9f9e9e85;
72 | color: #bbbbbb;
73 | cursor: pointer;
74 | }
75 | .pagitation button:hover,
76 | .pagitation button.active {
77 | border: 2px solid #daa28185;
78 | }
79 |
80 | .showMoreBtn {
81 | margin-top: 20px;
82 | width: 94%;
83 | max-width: 30rem;
84 | margin-inline: 30px;
85 | padding: 9px 15px;
86 | font-size: 16px;
87 | border-radius: 50px;
88 | transition: 0.5s;
89 | background: #0a0a0d;
90 | font-family: "poppins", sans-serif;
91 | border: 2px solid #2e3138;
92 | color: #bbbbbb;
93 | cursor: pointer;
94 | text-align: center;
95 | margin-bottom: 20px;
96 | }
97 |
98 | .showMoreBtn:hover,
99 | .showMoreBtn:focus {
100 | background: #252529;
101 | }
102 |
--------------------------------------------------------------------------------
/content/HomePage/Search/userSelection/catalog/genres/Genres.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useState } from "react";
4 | import styles from "./genres.module.css"
5 | import { FaRegSquare, FaSquareCheck } from "react-icons/fa6";
6 | import { IoIosArrowDown } from "react-icons/io";
7 | import { motion } from "framer-motion"
8 |
9 | const Genres = ({ callback }) => {
10 | const genres = ["Action", "Adventure", "Cars", "Comedy", "Drama", "Fantasy", "Horror", "Mahou Shoujo", "Mecha", "Music", "Mystery", "Psychological", "Romance", "Sci-Fi", "Slice of Life", "Sports", "Supernatural", "Thriller"]
11 | const [viewAll, setViewAll] = useState(false)
12 |
13 | const { searchData, setSearchData } = callback
14 |
15 | const onHandleSelect = (itemSelected) => {
16 | if (!searchData.genres.includes(itemSelected)) {
17 | setSearchData({ ...searchData, genres: [...searchData.genres, itemSelected] });
18 | } else {
19 | setSearchData({ ...searchData, genres: searchData.genres.filter(item => item !== itemSelected) });
20 | }
21 |
22 | };
23 |
24 | const containerVariants = {
25 | hidden: { opacity: 0 },
26 | show: {
27 | opacity: 1,
28 | transition: { staggerChildren: 0.1 }
29 | }
30 | };
31 |
32 | const listItemVariants = {
33 | hidden: { opacity: 0 },
34 | show: {
35 | opacity: 1,
36 | transition: { duration: 0.1 }
37 | }
38 | };
39 |
40 | const listItemTransition = {
41 | duration: 0.1,
42 | ease: "easeInOut"
43 | };
44 |
45 |
46 | return (
47 |
48 |
Genres
49 |
50 | {genres.slice(0, viewAll ? genres.length : 5).map((genre, index) => (
51 | onHandleSelect(genre)}
55 | variants={listItemVariants}
56 | transition={listItemTransition}
57 | >{searchData.genres.includes(genre) ? : } {genre}
58 | ))}
59 |
60 | setViewAll(prev => !prev)}>View All
61 |
62 |
63 | )
64 | }
65 |
66 | export default Genres
--------------------------------------------------------------------------------
/content/HomePage/Home/slidingBanner/SlidingBanner.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import styles from "./slidingBanner.module.css"
3 | import Banner from "@/components/ui/banner/Banner"
4 | import { Swiper, SwiperSlide } from "swiper/react";
5 | import 'swiper/css';
6 |
7 | const SlidingBanner = () => {
8 |
9 | const items = [
10 | {
11 | id: `1735`,
12 | name: "Naruto: Shippuden",
13 | image: "/images/wallpapers/naruto.webp",
14 | video: "https://firebasestorage.googleapis.com/v0/b/anime-nsfw.appspot.com/o/edits%2Fnaruto.mp4?alt=media&token=211e39d7-1079-4b15-ac23-41ce1639ad6c",
15 | description: "Naruto Uzumaki, is a loud, hyperactive, adolescent ninja who constantly searches for approval and recognition, as well as to become Hokage, who is acknowledged as the leader and strongest of all ninja in the village.",
16 | type: "TV",
17 | genre: ["Action", "Shounen", "Adventure"],
18 | },
19 | {
20 | id: `101922`,
21 | name: "Demon Slayer",
22 | image: "/images/wallpapers/demon slayer.jpg",
23 | video: 'https://firebasestorage.googleapis.com/v0/b/anime-nsfw.appspot.com/o/edits%2Fdemon%20slayer%20edit%20(2).mp4?alt=media&token=dd51ecd4-2dcd-4ce8-b783-3612aea528f6',
24 | description: "A family is attacked by demons and only two members survive - Tanjiro and his sister Nezuko, who is turning into a demon slowly. Tanjiro sets out to become a demon slayer to avenge his family and cure his sister.",
25 | type: "TV",
26 | genre: ["Action", "Shounen", "Revenge"],
27 | },
28 | {
29 | id: `21`,
30 | name: "ONE PIECE",
31 | image: "/images/wallpapers/one piece.jpg",
32 | description: "In the world of pirates, Gol D. Roger, known as the Pirate King, left behind the legendary treasure, One Piece, sparking a global quest. Monkey D. Luffy, inspired by Roger's legacy, sets sail with a diverse crew to find One Piece and claim the title of Pirate King.",
33 | type: "TV",
34 | genre: ["Action", "Shounen", "Adventure"],
35 | },
36 | ]
37 |
38 | return (
39 | //
40 |
45 | {items.map((item, index) => )}
46 | {/* */}
47 |
48 | //
49 | )
50 | }
51 |
52 | export default SlidingBanner
--------------------------------------------------------------------------------
/content/AuthPage/login/left/left.module.css:
--------------------------------------------------------------------------------
1 | .left {
2 | width: 50%;
3 | background: #070709;
4 | margin-left: 100px;
5 | margin-top: 20px;
6 | }
7 |
8 | .back {
9 | display: flex;
10 | align-items: center;
11 | color: white;
12 | width: max-content;
13 | padding: 10px 15px;
14 | border-radius: 30px;
15 | border: 2px solid #2d3138;
16 | border-width: 1px 1px 1px 3px;
17 | color: #8991a3;
18 | gap: 5px;
19 | cursor: pointer;
20 | }
21 |
22 | .center {
23 | margin-top: 12vh;
24 | }
25 |
26 | .logo {
27 | all: unset;
28 | display: flex;
29 | align-items: center;
30 | cursor: pointer;
31 | gap: 0.2rem;
32 | user-select: none;
33 | }
34 |
35 | .logo span {
36 | color: #f7f7f7;
37 | font-family: "Protest Revolution", sans-serif;
38 | font-size: 22px;
39 | }
40 |
41 | .heading {
42 | font-family: "poppins", sans-serif;
43 | margin-top: 2vh;
44 | line-height: 33px;
45 | font-size: 30px;
46 | font-weight: 600;
47 | background: -webkit-linear-gradient(#eee, #444);
48 | -webkit-background-clip: text;
49 | background-clip: text;
50 | -webkit-text-fill-color: transparent;
51 | }
52 |
53 | .highlight {
54 | color: #8991a3;
55 | /* font-weight: 500; */
56 | overflow: hidden;
57 | display: -webkit-box;
58 | -webkit-line-clamp: 9;
59 | -webkit-box-orient: vertical;
60 | line-height: 26px;
61 | font-size: 15px;
62 | margin-top: 1vh;
63 | margin-bottom: 1vh;
64 | max-width: 30rem;
65 | }
66 |
67 | .highlight a {
68 | color: #e9a070;
69 | text-decoration: none;
70 | font-weight: 500;
71 | }
72 |
73 | @media screen and (max-width: 1280px) {
74 | .left {
75 | margin-left: 2rem;
76 | }
77 | }
78 |
79 | @media screen and (max-width: 1080px) {
80 | .right {
81 | display: none;
82 | }
83 | .left {
84 | width: 100%;
85 | margin-right: 2rem;
86 | }
87 | }
88 |
89 | @media screen and (max-width: 990px) {
90 | .highlight {
91 | max-width: 100%;
92 | }
93 | .input,
94 | .left form input[type="submit"] {
95 | max-width: 100% !important;
96 | }
97 | .heading {
98 | display: flex;
99 | gap: 14px;
100 | }
101 | }
102 |
103 | @media screen and (max-width: 790px) {
104 | .heading {
105 | display: block;
106 | }
107 | .highlight {
108 | max-width: 100%;
109 | }
110 | }
111 |
112 | @media screen and (max-width: 470px) {
113 | .heading {
114 | font-size: x-large;
115 | }
116 | }
117 |
118 | @media screen and (max-width: 370px) {
119 | .heading {
120 | font-size: 20px;
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/components/ui/banner/AnimeInfo/AnimeInfo.module.css:
--------------------------------------------------------------------------------
1 | .info {
2 | display: flex;
3 | flex-direction: column;
4 | z-index: 5;
5 | position: absolute;
6 | top: 50%;
7 | transform: translateY(-50%);
8 | left: 60px;
9 | }
10 |
11 | .title {
12 | font-family: "poppins", sans-serif;
13 | font-weight: 600;
14 | }
15 |
16 | .description {
17 | font-family: "poppins", sans-serif;
18 | font-size: 15px;
19 | max-width: 36rem;
20 | color: #cbcbcb;
21 | overflow: hidden;
22 | display: -webkit-box;
23 | -webkit-line-clamp: 3;
24 | -webkit-box-orient: vertical;
25 | }
26 |
27 | .animedetail {
28 | display: flex;
29 | align-items: center;
30 | gap: 2rem;
31 | margin-top: 10px;
32 | }
33 |
34 | .type {
35 | display: flex;
36 | position: relative;
37 | padding: 9px 15px;
38 | font-size: 16px;
39 | border-radius: 50px;
40 | transition: 0.5s;
41 | background: #0e0e15;
42 | border: 2px solid #2e3138;
43 | font-family: "poppins", sans-serif;
44 | color: #7b8292;
45 | gap: 6px;
46 | font-weight: 500;
47 | align-items: center;
48 | }
49 |
50 | .type a {
51 | text-decoration: none;
52 | color: #d1d1d1;
53 | cursor: pointer;
54 | }
55 |
56 | .genre {
57 | display: flex;
58 | position: relative;
59 | padding: 9px 15px;
60 | font-size: 16px;
61 | border-radius: 50px;
62 | transition: 0.5s;
63 | background: #0e0e15;
64 | border: 2px solid #2e3138;
65 | font-family: "poppins", sans-serif;
66 | color: #7b8292;
67 | gap: 6px;
68 | align-items: center;
69 | }
70 |
71 | .genre span {
72 | display: flex;
73 | align-items: center;
74 | gap: 8px;
75 | text-decoration: none;
76 | color: #d1d1d1;
77 | cursor: pointer;
78 | }
79 |
80 | .watch {
81 | display: flex;
82 | background: linear-gradient(174deg, #d5a687, #e79a69);
83 | outline: none;
84 | border: 2px solid #efac87;
85 | padding: 10px 15px;
86 | color: black;
87 | gap: 5px;
88 | font-family: "poppins", sans-serif;
89 | align-items: center;
90 | border-radius: 35px;
91 | font-weight: 600;
92 | cursor: pointer;
93 | width: 100%;
94 | max-width: max-content;
95 | text-decoration: none;
96 | margin-top: 15px;
97 | }
98 |
99 | .watch:hover {
100 | background: linear-gradient(0deg, #d5a687, #e79a69);
101 | }
102 |
103 | @media screen and (max-width: 850px) {
104 | .animedetail {
105 | left: 20px;
106 | }
107 | }
108 |
109 | @media screen and (max-width: 640px) {
110 | .info {
111 | left: 20px;
112 | }
113 | .animedetail {
114 | flex-direction: column;
115 | align-items: start;
116 | gap: 8px;
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/app/(home)/search/search.module.css:
--------------------------------------------------------------------------------
1 | .backgroundImage {
2 | position: relative;
3 | width: 100%;
4 | height: 660px;
5 | object-fit: cover;
6 | border-radius: 20px;
7 | }
8 | .backgroundImage::after {
9 | content: "";
10 | position: absolute;
11 | bottom: 0;
12 | left: 0;
13 | width: 100%;
14 | height: 100%;
15 | background: linear-gradient(
16 | 360deg,
17 | #070709,
18 | #0c0d10a1,
19 | #0c0d1000,
20 | transparent
21 | );
22 | }
23 |
24 | .backgroundImage img {
25 | object-fit: cover;
26 | filter: brightness(50%);
27 | }
28 |
29 | .container {
30 | position: relative;
31 | top: -8rem;
32 | display: flex;
33 | justify-content: center;
34 | margin-left: 1rem;
35 | }
36 |
37 | .sectionContainer {
38 | max-width: 90rem;
39 | display: flex;
40 | width: 100%;
41 | }
42 |
43 | .right {
44 | gap: 1rem;
45 | width: 100%;
46 | display: grid;
47 | margin-left: 1rem;
48 | margin-right: 1rem;
49 | grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
50 | }
51 |
52 | .pagination {
53 | display: flex;
54 | justify-content: center;
55 | gap: 10px;
56 | font-family: "poppins", sans-serif;
57 | align-items: center;
58 | flex-wrap: wrap;
59 | margin-bottom: 3rem;
60 | }
61 | .pagination .PageLink {
62 | list-style: none;
63 | display: flex;
64 | align-items: center;
65 | }
66 | .pagination .break {
67 | list-style: none;
68 | display: flex;
69 | align-items: center;
70 | border: 2px solid #212328;
71 | border-radius: 21px;
72 | height: 48px;
73 | width: 60px;
74 | display: grid;
75 | place-content: center;
76 | font-size: 22px;
77 | }
78 | .pagination .aLink {
79 | cursor: pointer;
80 | width: 60px;
81 | border-radius: 21px;
82 | border: 2px solid #212328;
83 | color: white;
84 | height: 48px;
85 | display: grid;
86 | place-content: center;
87 | }
88 | .pagination .aLink {
89 | cursor: pointer;
90 | width: 60px;
91 | border-radius: 21px;
92 | border: 2px solid #212328;
93 | color: white;
94 | height: 48px;
95 | display: grid;
96 | place-content: center;
97 | }
98 | .pagination .activePage a {
99 | background: linear-gradient(174deg, #d5a687, #e79a69);
100 | outline: none;
101 | border: 2px solid #efac87;
102 | color: black;
103 | }
104 | .pagination li {
105 | list-style: none;
106 | }
107 |
108 | .pagination .pageChanger {
109 | display: none;
110 | }
111 |
112 | @media screen and (max-width: 1000px) {
113 | .sectionContainer {
114 | flex-direction: column;
115 | }
116 | .right {
117 | margin-right: 1rem;
118 | width: -webkit-fill-available !important;
119 | width: -moz-fill-available !important;
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/app/(auth)/login/login.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | display: flex;
3 | width: 100%;
4 | }
5 |
6 | .right {
7 | width: 50%;
8 | height: 100vh;
9 | overflow: hidden;
10 | position: relative;
11 | }
12 |
13 | .right .background {
14 | width: 100%;
15 | height: 100vh;
16 | position: relative;
17 | }
18 |
19 | .right .background::after {
20 | content: "";
21 | position: absolute;
22 | bottom: 0;
23 | left: 0;
24 | width: 100%;
25 | height: 100%;
26 | background: linear-gradient(
27 | 360deg,
28 | #070709,
29 | #0c0d10a1,
30 | #0c0d1000,
31 | transparent
32 | );
33 | }
34 |
35 | .right .background img {
36 | object-fit: cover;
37 | object-position: 70%;
38 | filter: blur(10px);
39 | }
40 |
41 | .info {
42 | position: absolute;
43 | z-index: 7;
44 | top: 50%;
45 | left: 50%;
46 | transform: translate(-50%, -50%);
47 | display: flex;
48 | flex-direction: column;
49 | align-items: center;
50 | }
51 |
52 | .info .image img {
53 | background: #ffffff12;
54 | padding: 2px;
55 | border-radius: 20rem;
56 | object-fit: cover;
57 | }
58 |
59 | .info .image {
60 | position: relative;
61 | }
62 | .info .image::after {
63 | content: "";
64 | position: absolute;
65 | bottom: 0;
66 | left: 0;
67 | width: 100%;
68 | height: 100%;
69 | background: linear-gradient(0deg, #000000, #00000014, #0c0d1000);
70 | border-radius: 100rem;
71 | }
72 |
73 | .animeinfo {
74 | position: absolute;
75 | top: 75%;
76 | text-align: center;
77 | }
78 | .animeinfo .title {
79 | font-family: "poppins", sans-serif;
80 | font-size: 24px;
81 | font-weight: 600;
82 | }
83 | .animeinfo .description {
84 | font-family: "poppins", sans-serif;
85 | font-size: 15px;
86 | font-weight: 600;
87 | overflow: hidden;
88 | display: -webkit-box;
89 | -webkit-line-clamp: 4;
90 | -webkit-box-orient: vertical;
91 | text-align: center;
92 | background: -webkit-linear-gradient(#eee, #444);
93 | -webkit-background-clip: text;
94 | background-clip: text;
95 | -webkit-text-fill-color: #ffffff0f;
96 | }
97 |
98 | @media screen and (max-width: 1280px) {
99 | .left {
100 | margin-left: 2rem;
101 | }
102 | }
103 |
104 | @media screen and (max-width: 1080px) {
105 | .right {
106 | display: none;
107 | }
108 | .left {
109 | width: 100%;
110 | margin-right: 2rem;
111 | }
112 | }
113 |
114 | @media screen and (max-width: 990px) {
115 | .highlight {
116 | max-width: 100%;
117 | text-align: center;
118 | }
119 | .input,
120 | .left form input[type="submit"] {
121 | max-width: 100% !important;
122 | }
123 | .heading {
124 | display: flex;
125 | gap: 14px;
126 | }
127 | }
128 |
129 | @media screen and (max-width: 790px) {
130 | .heading {
131 | display: block;
132 | }
133 | .highlight {
134 | max-width: 100%;
135 | text-align: start;
136 | }
137 | }
138 |
139 | @media screen and (max-width: 470px) {
140 | .heading {
141 | font-size: x-large;
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/app/(home)/watch/[id]/page.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react-hooks/exhaustive-deps */
2 | "use client";
3 |
4 | import { useEffect, useState } from "react";
5 | import { useSearchParams } from "next/navigation";
6 | import Loading from "@/components/layout/loading/Loading";
7 | import AnimeInfo from "@/content/HomePage/Watch/left/animeInfo/AnimeInfo";
8 | import VideoPlayer from "@/content/HomePage/Watch/left/videoPlayer/VideoPlayer";
9 | import VideoOption from "@/content/HomePage/Watch/left/videoOption/VideoOption";
10 | import VideoSelector from "@/content/HomePage/Watch/left/videoSelector/VideoSelector";
11 | import EpisodeSelector from "@/components/ui/episodeSelector/EpisodeSelector";
12 | import MostPopular from "@/content/HomePage/Watch/right/mostPopular/MostPopular";
13 | import AtoZalphabet from "@/components/ui/AtoZalphabet/AtoZalphabet";
14 | import { fetchData } from "@/lib/FetchData";
15 | import styles from "./watch.module.css";
16 | import Recommendation from "@/content/HomePage/Watch/right/recommendation/Recommendation";
17 | import AnimeSeasons from "@/content/HomePage/Watch/left/animeSeasons/AnimeSeasons";
18 |
19 | const Watch = ({ params }) => {
20 | const { id } = params;
21 | const searchParams = useSearchParams();
22 |
23 | const [animeInfo, setAnimeInfo] = useState(null);
24 | const [isLoaded, setIsLoaded] = useState(false);
25 | const [VideoOptionToggler, setVideoOptionToggler] = useState([]);
26 | const [videoSelected, setVideoSelected] = useState({ server: "default", url: null });
27 |
28 | const episode = searchParams.get('episode') || animeInfo?.episodes?.[0]?.number || 1;
29 |
30 |
31 |
32 |
33 | useEffect(() => {
34 | const fetchAnimeInfo = async () => {
35 | try {
36 | setIsLoaded(false);
37 | const data = await fetchData(`/meta/anilist/data/${id}`, 86400);
38 | setAnimeInfo(data.data);
39 | document.title = `Watch ${data.data.title.english} - Animeverse`;
40 | } catch (error) {
41 | console.error("Error fetching anime info:", error);
42 | }
43 | finally {
44 | setIsLoaded(true);
45 | }
46 | };
47 |
48 | fetchAnimeInfo();
49 | }, [id]);
50 |
51 |
52 | return !isLoaded ? : (
53 | <>
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | {/*
*/}
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | >
70 | );
71 | };
72 |
73 | export default Watch;
74 |
--------------------------------------------------------------------------------
/components/layout/navbar/navbar.module.css:
--------------------------------------------------------------------------------
1 | .nav {
2 | width: 100%;
3 | max-width: 90rem;
4 | left: 50%;
5 | transform: translate(-50%, 0);
6 | position: fixed;
7 | top: 10px;
8 | z-index: 99;
9 | }
10 |
11 | .container {
12 | display: flex;
13 | background: linear-gradient(50deg, #0c0d10, rgb(16, 16, 21));
14 | justify-content: space-between;
15 | position: relative;
16 | border-radius: 100px;
17 | padding: 10px 0;
18 | color: white;
19 | }
20 |
21 | .left {
22 | display: flex;
23 | align-items: center;
24 | }
25 |
26 | .logo {
27 | all: unset;
28 | display: flex;
29 | align-items: center;
30 | margin-left: 1.5rem;
31 | cursor: pointer;
32 | gap: 0.2rem;
33 | user-select: none;
34 | }
35 |
36 | .logo span {
37 | color: #f7f7f7;
38 | font-family: "Protest Revolution", sans-serif;
39 | font-size: 22px;
40 | }
41 |
42 | .nav::after {
43 | content: "";
44 | width: 100.3%;
45 | height: 104%;
46 | background: linear-gradient(30deg, #ffffff29, #ffffff0a);
47 | position: absolute;
48 | border-radius: 50px;
49 | z-index: -1;
50 | top: 50%;
51 | left: 50%;
52 | transform: translate(-50%, -50%);
53 | }
54 |
55 | .right {
56 | display: flex;
57 | align-items: center;
58 | justify-content: center;
59 | margin-right: 24px;
60 | gap: 0.4rem;
61 | }
62 |
63 | .account {
64 | display: flex;
65 | background: linear-gradient(174deg, #d5a687, #e79a69);
66 | outline: none;
67 | border: 2px solid #efac87;
68 | padding: 10px 15px;
69 | color: black;
70 | gap: 5px;
71 | font-family: "poppins", sans-serif;
72 | align-items: center;
73 | border-radius: 35px;
74 | font-weight: 600;
75 | cursor: pointer;
76 | width: 100%;
77 | max-width: max-content;
78 | }
79 |
80 | .account:hover {
81 | background: linear-gradient(0deg, #d5a687, #e79a69);
82 | }
83 |
84 | /* responsive */
85 |
86 | .hamburger {
87 | width: 40px;
88 | height: 40px;
89 | font-size: 30px;
90 | border-radius: 50%;
91 | cursor: pointer;
92 | place-content: center;
93 | margin-left: 0.9rem;
94 | display: none;
95 | color: #727272;
96 | transition: 0.4s;
97 | }
98 |
99 | .hamburger:hover,
100 | .hamburger:focus {
101 | color: #848484;
102 | }
103 |
104 | @media screen and (max-width: 1080px) and (min-width: 770px) {
105 | .logo {
106 | margin-left: 0.6rem;
107 | }
108 | .hamburger {
109 | display: grid;
110 | }
111 | .nav {
112 | display: flex;
113 | }
114 | .container {
115 | width: 100%;
116 | justify-content: normal;
117 | }
118 | .right {
119 | flex-direction: row-reverse;
120 | width: 100%;
121 | }
122 | }
123 |
124 | @media screen and (max-width: 1080px) {
125 | .hamburger {
126 | display: grid;
127 | }
128 | .logo {
129 | margin-left: 0.6rem;
130 | }
131 | .nav {
132 | width: 96%;
133 | }
134 | }
135 |
136 | @media screen and (max-width: 450px) {
137 | .logo {
138 | margin-left: 0em;
139 | gap: 0px;
140 | transform: scale(0.9);
141 | }
142 | .hamburger {
143 | margin-left: 0.5rem;
144 | }
145 | .account {
146 | font-size: 12px;
147 | padding: 10px 8px;
148 | }
149 | }
150 |
151 | @media screen and (max-width: 380px) {
152 | .account {
153 | font-size: 10px;
154 | padding: 10px 6px;
155 | }
156 |
157 | .logo {
158 | margin-left: -10px;
159 | transform: scale(0.8);
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/content/HomePage/Info/animeInfo/animeInfo.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | display: flex;
3 | gap: 20px;
4 | }
5 |
6 | .left {
7 | display: flex;
8 | flex-direction: column;
9 | gap: 20px;
10 | }
11 |
12 | .left button {
13 | background: linear-gradient(174deg, #d5a687, #e79a69);
14 | outline: none;
15 | border: 2px solid #efac87;
16 | padding: 10px 15px;
17 | color: black;
18 | gap: 5px;
19 | font-family: "poppins", sans-serif;
20 | text-align: center;
21 | border-radius: 35px;
22 | font-weight: 600;
23 | cursor: pointer;
24 | width: 100%;
25 | }
26 |
27 | .left img {
28 | object-fit: cover;
29 | border-radius: 14px;
30 | }
31 |
32 | .pagination {
33 | display: flex;
34 | align-items: center;
35 | gap: 8px;
36 | color: #eeefef;
37 | font-size: 16px;
38 | font-weight: 500;
39 | }
40 |
41 | .pagination span {
42 | display: flex;
43 | align-items: center;
44 | gap: 2px;
45 | }
46 |
47 | .pagination svg {
48 | font-size: medium;
49 | }
50 |
51 | .pagination a svg {
52 | fill: #e49a70 !important;
53 | font-size: 18px;
54 | }
55 |
56 | .pagination a {
57 | text-decoration: none;
58 | color: inherit;
59 | display: flex;
60 | align-items: center;
61 | gap: 6px;
62 | }
63 |
64 | .pagination a:last-child {
65 | color: #7c8394;
66 | }
67 |
68 | .pagination a:last-child svg {
69 | fill: #7c8394 !important;
70 | font-size: medium;
71 | }
72 |
73 | .title {
74 | font-size: 34px;
75 | font-weight: 700;
76 | background: -webkit-linear-gradient(#dcdcde, #5a5a5a);
77 | background-clip: text;
78 | -webkit-text-fill-color: transparent;
79 | margin-top: 20px;
80 | display: -webkit-box;
81 | -webkit-line-clamp: 1;
82 | -webkit-box-orient: vertical;
83 | }
84 |
85 | .options {
86 | display: flex;
87 | align-items: center;
88 | margin-top: 16px;
89 | gap: 10px;
90 | }
91 |
92 | .bookmark {
93 | display: flex;
94 | padding: 10px;
95 | font-size: 14px;
96 | border-radius: 50px;
97 | transition: 0.5s;
98 | background: #1b1b1c;
99 | border: 2px solid #2e3138;
100 | color: #ffffff;
101 | cursor: pointer;
102 | }
103 |
104 | .genres {
105 | display: flex;
106 | gap: 10px;
107 | }
108 |
109 | .genres a {
110 | text-decoration: none;
111 | background: #070709;
112 | border: 2px solid #23252b;
113 | border-radius: 50px;
114 | padding: 8px 18px;
115 | font-family: "poppins", sans-serif;
116 | font-weight: 500;
117 | font-size: 14px;
118 | color: #bbbbbb;
119 | cursor: pointer;
120 | transition: 0.4s;
121 | }
122 |
123 | .genres a:hover {
124 | background: #141418;
125 | }
126 |
127 | .description {
128 | color: #8991a3;
129 | margin-top: 24px;
130 | max-width: 1000px;
131 | }
132 |
133 | .description span:first-child {
134 | overflow: hidden;
135 | display: -webkit-box;
136 | -webkit-line-clamp: 9;
137 | -webkit-box-orient: vertical;
138 | line-height: 26px;
139 | }
140 |
141 | /* .description span:last-child {
142 | color: #7d91c0;
143 | cursor: pointer;
144 | font-weight: 600; */
145 | /* } */
146 | @media screen and (max-width: 1000px) {
147 | .container {
148 | flex-direction: column;
149 | align-items: center;
150 | }
151 |
152 | .left {
153 | width: 100%;
154 | align-items: center;
155 | }
156 |
157 | .title,
158 | .description {
159 | text-align: center;
160 | }
161 |
162 | .pagination,
163 | .options {
164 | width: 100%;
165 | display: flex;
166 | justify-content: center;
167 | }
168 |
169 | .options,
170 | .options .genres {
171 | flex-wrap: wrap;
172 | justify-content: center;
173 | }
174 | }
175 |
176 | @media screen and (max-width: 1380px) {
177 | .right {
178 | width: 100%;
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/components/ui/episodeSelector/episodeSelector.module.css:
--------------------------------------------------------------------------------
1 | .identifier {
2 | display: flex;
3 | padding: 1rem 20px;
4 | margin-top: 2rem;
5 | border-radius: 16px;
6 | border: 2px solid #353841;
7 | gap: 1rem;
8 | align-items: center;
9 | background: linear-gradient(90deg, #d8a2835e, #00000000, #0c0d1000);
10 | position: relative;
11 | overflow: hidden;
12 | }
13 |
14 | .identifier::after {
15 | content: "";
16 | width: 3px;
17 | border-radius: 50px;
18 | height: 30px;
19 | left: 0px;
20 | top: 50%;
21 | transform: translateY(-50%);
22 | position: absolute;
23 | background: linear-gradient(180deg, #d5a487, #e49a6f);
24 | }
25 |
26 | .identifier svg {
27 | height: 18px;
28 | width: 18px;
29 | fill: #e49a6f;
30 | }
31 |
32 | .episodecontainer {
33 | display: flex;
34 | align-items: center;
35 | gap: 10px;
36 | }
37 |
38 | .searchEp {
39 | width: 100%;
40 | background: #070709;
41 | height: 56px;
42 | margin-top: 0.5rem;
43 | border: 2px solid #353841;
44 | border-radius: 16px;
45 | padding: 10px 20px;
46 | display: flex;
47 | align-items: center;
48 | }
49 |
50 | .searchEp input {
51 | width: 100%;
52 | border: none;
53 | outline: none;
54 | background: transparent;
55 | color: white;
56 | font-size: 16px;
57 | font-family: "poppins", sans-serif;
58 | }
59 |
60 | .searchEp svg {
61 | height: 20px;
62 | width: 20px;
63 | margin-right: 5px;
64 | fill: rgb(223, 223, 223);
65 | }
66 |
67 | .episodecontainer .selection {
68 | display: flex;
69 | align-items: center;
70 | gap: 5px;
71 | }
72 |
73 | .episodecontainer .selection button {
74 | text-decoration: none;
75 | background: #070709;
76 | border: 2px solid #23252b;
77 | border-radius: 12px;
78 | padding: 8px 18px;
79 | font-family: "poppins", sans-serif;
80 | font-weight: 500;
81 | font-size: 14px;
82 | color: #bbbbbb;
83 | cursor: pointer;
84 | }
85 |
86 | .episodecontainer .selection button.active {
87 | background: linear-gradient(174deg, #d5a687, #e79a69) !important;
88 | color: black;
89 | border: 2px solid #efac87;
90 | }
91 |
92 | .episodes {
93 | display: grid;
94 | grid-template-columns: repeat(auto-fit, minmax(190px, 4fr));
95 | gap: 1rem;
96 | margin-top: 1rem;
97 | max-height: 34rem;
98 | scrollbar-width: thin;
99 | scrollbar-color: #d9a383 #0e0f13;
100 | padding-right: 6px;
101 | overflow-y: scroll;
102 | }
103 |
104 | .episode {
105 | text-decoration: none;
106 | color: inherit;
107 | background: #0f1014;
108 | padding: 20px;
109 | height: max-content;
110 | border-radius: 10px;
111 | border: 2px solid #25282f;
112 | display: flex;
113 | flex-direction: column;
114 | gap: 0.2rem;
115 | font-family: "poppins", sans-serif;
116 | cursor: pointer;
117 | }
118 |
119 | .episode:hover {
120 | background: #191b21;
121 | }
122 |
123 | .episode span:first-child {
124 | font-size: 17px;
125 | font-weight: 500;
126 | }
127 | .episode span:last-child {
128 | font-size: 14px;
129 | color: #6d7483;
130 | }
131 |
132 | .watched {
133 | background: #373a43a6;
134 | border: 2px solid #ffffff24;
135 | }
136 |
137 | .loading {
138 | background: #0f0f13;
139 | height: 34rem;
140 | border-radius: 14px;
141 | background-image: linear-gradient(90deg, #0f0f13, #353841, #0f0f13);
142 | background-repeat: no-repeat;
143 | animation: loading 4s linear infinite;
144 | }
145 |
146 | @keyframes loading {
147 | 0% {
148 | background-position: left -60rem top 0px;
149 | }
150 | 50% {
151 | background-position: left 60rem top 0px;
152 | }
153 | 100% {
154 | background-position: left -60rem top 0px;
155 | }
156 | }
157 |
158 | @media screen and (max-width: 480px) {
159 | .episodecontainer {
160 | flex-direction: column;
161 | }
162 | .selection {
163 | width: 100%;
164 | }
165 | .episodecontainer .selection button {
166 | width: 100%;
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/components/ui/episodeSelector/EpisodeSelector.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useCallback, useEffect, useState } from "react";
4 | import styles from "./episodeSelector.module.css";
5 | import { BsStack } from "react-icons/bs";
6 | import { IoIosSearch } from "react-icons/io";
7 | import { useRouter } from "next/navigation";
8 | import useSWR from 'swr';
9 |
10 | const EpisodeSelector = ({ episode, activeEpisdoe, setVideoOptionToggler }) => {
11 | const router = useRouter();
12 | const [language, setLanguage] = useState(localStorage.getItem("language") || "sub");
13 | const [search, setSearch] = useState("");
14 | const [filteredEpisodes, setFilteredEpisodes] = useState([]);
15 |
16 | const createQueryString = useCallback(
17 | (name, value) => {
18 | const params = new URLSearchParams();
19 | params.set(name, value);
20 | return params.toString();
21 | },
22 | []
23 | );
24 |
25 | const fetcher = (...args) => fetch(...args).then((res) => res.json());
26 | const { data: episodes, isLoading } = useSWR(`${process.env.API_URL}/meta/anilist/episodes/${episode}?dub=${language === "dub"}`, fetcher);
27 |
28 | useEffect(() => {
29 | if (language) {
30 | localStorage.setItem("language", language);
31 | }
32 | }, [language]);
33 |
34 | useEffect(() => {
35 | if (episodes?.message !== undefined) { }
36 | else if (episodes) {
37 | const filter = episodes?.filter((item) => item.number.toString().toLowerCase().includes(search.toLowerCase()));
38 | setFilteredEpisodes(filter);
39 | }
40 | }, [episodes, search]);
41 |
42 | useEffect(() => {
43 | if (!episodes || episodes?.message !== undefined) {
44 | return;
45 | }
46 | const foundIndex = episodes?.findIndex(item => item.number === parseInt(activeEpisdoe));
47 | if (foundIndex !== -1) {
48 | const prevEpisode = foundIndex > 0 ? episodes[foundIndex - 1] : null;
49 | const currentEpisode = episodes[foundIndex];
50 | const nextEpisode = foundIndex < episodes?.length - 1 ? episodes[foundIndex + 1] : null;
51 | const episodeList = [prevEpisode, currentEpisode, nextEpisode];
52 | setVideoOptionToggler(episodeList);
53 | }
54 | }, [activeEpisdoe, episodes, setVideoOptionToggler]);
55 |
56 | return (
57 | <>
58 |
59 |
60 | Select Episodes
61 |
62 |
63 |
64 |
65 |
66 | setSearch(e.target.value)} value={search} />
67 |
68 |
69 |
70 | setLanguage("dub")} className={language === "dub" ? styles.active : ""}>Dub
71 | setLanguage("sub")} className={language !== "dub" ? styles.active : ""}>Sub
72 |
73 |
74 |
75 | {isLoading ?
:
76 |
77 |
78 | {filteredEpisodes?.map((item, index) =>
79 |
router.push('/watch/' + episode + '?' + createQueryString('episodeID', item.id) + '&' + createQueryString('episode', item.number), { scroll: false })}
81 | className={`${styles.episode} ${activeEpisdoe === item.number.toString() && styles.watched}`}
82 | key={index}>
83 | Episode {item?.number}
84 | {item.airDate || "Unknown"}
85 |
)}
86 |
87 | {episodes?.length === 0 || episodes?.message !== undefined ?
88 |
89 |
90 | {episodes?.message ||
91 | `No ${language}bed episodes found`
92 | }
93 |
94 |
95 | : null}
96 |
97 |
98 | }
99 | >
100 | );
101 | };
102 |
103 | export default EpisodeSelector;
--------------------------------------------------------------------------------
/components/ui/card/card.module.css:
--------------------------------------------------------------------------------
1 | @property --gradient-angle {
2 | syntax: "";
3 | initial-value: 0deg;
4 | inherits: false;
5 | }
6 |
7 | .container {
8 | position: relative;
9 | cursor: pointer;
10 | }
11 |
12 | .backgroundImage {
13 | height: 100%;
14 | overflow: hidden;
15 | border-radius: 14px;
16 | }
17 |
18 | .backgroundImage::before {
19 | content: "";
20 | width: 100%;
21 | height: 50%;
22 | background: linear-gradient(
23 | 360deg,
24 | #0c0d10,
25 | #0c0d10a1,
26 | #0c0d1000,
27 | transparent
28 | );
29 | z-index: 1;
30 | bottom: 0;
31 | left: 0;
32 | position: absolute;
33 | border-radius: 14px;
34 | }
35 |
36 | .backgroundImage img {
37 | object-fit: cover;
38 | width: 100%;
39 | height: 100%;
40 | }
41 |
42 | .info {
43 | position: absolute;
44 | top: 0;
45 | left: 0;
46 | width: 100%;
47 | height: 100%;
48 | display: flex;
49 | flex-direction: column;
50 | justify-content: space-between;
51 | padding: 16px;
52 | }
53 |
54 | .top {
55 | display: flex;
56 | justify-content: space-between;
57 | position: relative;
58 | z-index: 2;
59 | }
60 |
61 | .left {
62 | display: flex;
63 | gap: 10px;
64 | z-index: 6;
65 | }
66 |
67 | .ranking {
68 | display: flex;
69 | background: linear-gradient(174deg, #d5a687, #e79a69);
70 | outline: none;
71 | border: 2px solid #efac87;
72 | padding: 5px 8px;
73 | color: black;
74 | gap: 5px;
75 | font-size: 14px;
76 | font-family: "poppins", sans-serif;
77 | align-items: center;
78 | border-radius: 35px;
79 | font-weight: 600;
80 | cursor: pointer;
81 | max-width: max-content;
82 | }
83 |
84 | .new {
85 | display: flex;
86 | background: #ffffff75;
87 | backdrop-filter: blur(8px);
88 | border: 2px solid #28282800;
89 | outline: none;
90 | padding: 5px 10px;
91 | color: black;
92 | gap: 5px;
93 | font-size: 14px;
94 | font-family: "poppins", sans-serif;
95 | align-items: center;
96 | border-radius: 35px;
97 | font-weight: 600;
98 | cursor: pointer;
99 | max-width: max-content;
100 | }
101 |
102 | .bookmark {
103 | display: flex;
104 | padding: 10px;
105 | font-size: 12px;
106 | border-radius: 50px;
107 | transition: 0.5s;
108 | background: #1b1b1c;
109 | border: 2px solid #2e3138;
110 | color: #ffffff;
111 | gap: 6px;
112 | font-weight: 500;
113 | cursor: pointer;
114 | }
115 |
116 | .animeDetail {
117 | display: flex;
118 | flex-direction: column;
119 | align-items: center;
120 | position: relative;
121 | z-index: 2;
122 | }
123 |
124 | .name {
125 | font-family: "poppins", sans-serif;
126 | font-size: 18px;
127 | cursor: pointer;
128 | text-overflow: ellipsis;
129 | white-space: nowrap;
130 | overflow: hidden;
131 | width: 100%;
132 | text-align: center;
133 | }
134 |
135 | .re {
136 | display: flex;
137 | gap: 14px;
138 | color: #929396;
139 | font-size: 14px;
140 | font-family: "poppins", sans-serif;
141 | }
142 |
143 | .rating,
144 | .episodes {
145 | display: flex;
146 | gap: 4px;
147 | align-items: center;
148 | font-size: 14px;
149 | }
150 |
151 | .container:hover .overlay svg {
152 | scale: 1;
153 | transition: 0.5s;
154 | }
155 | .container:hover .overlay {
156 | transition: 0.5s;
157 | opacity: 1;
158 | }
159 |
160 | .overlay {
161 | position: absolute;
162 | top: 0;
163 | opacity: 0;
164 | z-index: 1;
165 | bottom: 0;
166 | left: 0;
167 | right: 0;
168 | background: rgba(0, 0, 0, 0.261);
169 | display: grid;
170 | place-content: center;
171 | font-size: 50px;
172 | }
173 |
174 | .overlay svg {
175 | fill: #e29b72;
176 | scale: 0;
177 | }
178 |
179 | .border {
180 | position: absolute;
181 | top: 0;
182 | left: 0;
183 | background: linear-gradient(var(--gradient-angle), #d5a6874d, #e79a69);
184 | width: 101.5%;
185 | height: 101.5%;
186 | z-index: -1;
187 | transform: translate(-50%, -50%);
188 | top: 50%;
189 | left: 50%;
190 | border-radius: 16px;
191 | animation: rotation 10s linear infinite;
192 | }
193 |
194 | .normalborder {
195 | position: absolute;
196 | top: 0;
197 | left: 0;
198 | background: linear-gradient(322deg, #465c6d00, #2e3138);
199 | width: 101.5%;
200 | height: 101.5%;
201 | z-index: -1;
202 | transform: translate(-50%, -50%);
203 | top: 50%;
204 | left: 50%;
205 | border-radius: 16px;
206 | }
207 |
208 | @keyframes rotation {
209 | 0% {
210 | --gradient-angle: 0deg;
211 | }
212 | 100% {
213 | --gradient-angle: 360deg;
214 | }
215 | }
216 |
--------------------------------------------------------------------------------
/app/(home)/search/page.js:
--------------------------------------------------------------------------------
1 | "use client"
2 | import Image from 'next/image';
3 | import styles from './search.module.css';
4 | import UserSelection from '@/content/HomePage/Search/userSelection/UserSelection';
5 | import Catalog from '@/content/HomePage/Search/userSelection/catalog/Catalog';
6 | import Card from '@/components/ui/card/Card';
7 | import { useSearchParams } from 'next/navigation';
8 | import { useEffect, useState } from 'react';
9 | import { FaAngleLeft, FaAngleRight } from "react-icons/fa6";
10 | import ReactPaginate from 'react-paginate';
11 |
12 | const Search = () => {
13 | const [datas, setDatas] = useState([]);
14 | const [isLoaded, setIsLoaded] = useState(false);
15 | const [error, setError] = useState(null);
16 | const [pageCount, setPageCount] = useState(1);
17 |
18 | const param = useSearchParams();
19 | const [searchData, setSearchData] = useState({
20 | q: param.get("q") || "",
21 | genres: param.get("genres") || "",
22 | status: param.get("status") || "",
23 | season: param.get("season") || "",
24 | type: param.get("type") || "",
25 | year: param.get("year") || "",
26 | });
27 |
28 |
29 | useEffect(() => {
30 | setSearchData({
31 | q: param.get("q") || "",
32 | genres: param.get("genres") || "",
33 | status: param.get("status") || "",
34 | season: param.get("season") || "",
35 | type: param.get("type") || "",
36 | year: param.get("year") || "",
37 | });
38 | }, [param]);
39 |
40 | useEffect(() => {
41 | setSearchData(searchData);
42 |
43 | const fetchData = async () => {
44 | setIsLoaded(false);
45 | setError(null);
46 | try {
47 | const params = {
48 | "query": decodeURIComponent(searchData.q),
49 | "season": searchData.season,
50 | "format": searchData.type,
51 | "genres": searchData.genres,
52 | "status": searchData.status,
53 | "year": searchData.year
54 | };
55 |
56 | const searchDataString = Object.keys(params)
57 | .map(k => params[k] !== "" ? encodeURIComponent(k) + '=' + encodeURIComponent(params[k]) : null)
58 | .filter(Boolean) // Filter out null values
59 | .join('&');
60 |
61 | const response = await fetch(`${process.env.API_URL}/meta/anilist/advanced-search?${searchDataString}&page=${pageCount}`);
62 |
63 | if (!response.ok) {
64 | throw new Error('Failed to fetch data');
65 | }
66 | const data = await response.json();
67 | setDatas(data || []);
68 | setIsLoaded(true);
69 | } catch (error) {
70 | console.error('Error fetching data:', error);
71 | setError('Failed to fetch data. Please try again.');
72 | setIsLoaded(true);
73 | }
74 | };
75 |
76 | fetchData();
77 |
78 | }, [searchData, pageCount]);
79 |
80 | const handlePageChange = (selectedPage) => {
81 | setPageCount(selectedPage + 1);
82 | }
83 |
84 | return (
85 | <>
86 |
87 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 | {isLoaded ? (
101 | error ? (
102 |
{error}
103 | ) : (
104 | datas?.results?.map((data, index) => (
105 |
106 | ))
107 | )
108 | ) :
109 | Array.from({ length: 10 }).map((_, index) => (
110 |
111 | ))
112 | }
113 |
114 |
115 |
116 |
117 | }
126 | nextLabel={ }
127 | onPageChange={(event) => handlePageChange(event.selected)}
128 | pageCount={datas?.totalPages}
129 | breakLabel="..."
130 | />
131 | >
132 | );
133 | };
134 |
135 | export default Search;
--------------------------------------------------------------------------------