aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorreal-zephex <[email protected]>2024-05-14 01:02:50 +0530
committerreal-zephex <[email protected]>2024-05-14 01:02:50 +0530
commitc0e1e1a5ab734d254392e8bfcc7ace0e0ff3b6fa (patch)
tree271228b9cf8ba5bd80a79145216c84ee1144457d
parentminor changes to the kdrama page (diff)
parentMerge pull request #27 from real-zephex/improvement-2 (diff)
downloaddramalama-c0e1e1a5ab734d254392e8bfcc7ace0e0ff3b6fa.tar.xz
dramalama-c0e1e1a5ab734d254392e8bfcc7ace0e0ff3b6fa.zip
Merge branch 'master' of https://github.com/real-zephex/Dramalama-Next
-rw-r--r--README.md6
-rw-r--r--next.config.mjs10
-rw-r--r--src/app/anime/[id]/buttons.jsx153
-rw-r--r--src/app/anime/[id]/info.module.css187
-rw-r--r--src/app/anime/[id]/loading.jsx9
-rw-r--r--src/app/anime/[id]/loading.module.css22
-rw-r--r--src/app/anime/[id]/page.jsx122
-rw-r--r--src/app/anime/anime.module.css3
-rw-r--r--src/app/anime/components/cacher.js29
-rw-r--r--src/app/anime/components/episode_buttons.jsx205
-rw-r--r--src/app/anime/components/popularAnimes.jsx51
-rw-r--r--src/app/anime/components/recentEpisodes.jsx54
-rw-r--r--src/app/anime/components/search.jsx64
-rw-r--r--src/app/anime/components/search_results.jsx42
-rw-r--r--src/app/anime/components/storeHistory.js (renamed from src/app/anime/history/storeData.js)70
-rw-r--r--src/app/anime/components/topAiring.jsx54
-rw-r--r--src/app/anime/continueWatching/page.jsx (renamed from src/app/anime/history/continueWatching/page.jsx)130
-rw-r--r--src/app/anime/data-fetch/request.js54
-rw-r--r--src/app/anime/loading.jsx11
-rw-r--r--src/app/anime/page.jsx33
-rw-r--r--src/app/anime/popular/page.jsx52
-rw-r--r--src/app/anime/recent/page.jsx52
-rw-r--r--src/app/anime/search/components/fetchInfo.js14
-rw-r--r--src/app/anime/search/components/fetchedInfo.js45
-rw-r--r--src/app/anime/search/page.jsx69
-rw-r--r--src/app/anime/search/search.module.css114
-rw-r--r--src/app/anime/styles/anime.module.css4
-rw-r--r--src/app/anime/styles/buttons.module.css98
-rw-r--r--src/app/anime/styles/cw.module.css (renamed from src/app/anime/history/continueWatching/cw.module.css)118
-rw-r--r--src/app/anime/styles/info.module.css75
-rw-r--r--src/app/anime/styles/loading.module.css12
-rw-r--r--src/app/anime/styles/pop_recent_top.module.css56
-rw-r--r--src/app/anime/styles/search.module.css98
-rw-r--r--src/app/anime/top-airing/page.jsx52
-rw-r--r--src/app/anime/top-airing/trending.module.css79
-rw-r--r--src/app/anime/videoLinkfetcher.js46
-rw-r--r--src/app/globals.css16
-rw-r--r--src/app/movies/components/video_player.jsx2
-rw-r--r--src/app/movies/styles/info.module.css9
-rw-r--r--utils/anime_urls.js10
-rw-r--r--utils/movie_urls.js2
41 files changed, 1197 insertions, 1135 deletions
diff --git a/README.md b/README.md
index 1526d77..2e0948d 100644
--- a/README.md
+++ b/README.md
@@ -38,7 +38,11 @@ Dramalama doesn't stores any data. It fetches it data through Consumet API which
## Deployments
- Vercel: https://dramalama.vercel.app
-- Netlify: https://dramalama.netlify.app
+- Netlify: https://dramalama.netlify.app
+
+## Self Hosting
+
+You are allowed to self host this project on your local machines, vps etc. But, please change the site name before hosting it.
## Contributing
diff --git a/next.config.mjs b/next.config.mjs
index e38fbe2..2ec75eb 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -42,11 +42,11 @@ const nextConfig = {
fullUrl: true,
},
},
- experimental: {
- serverActions: {
- allowedOrigins: ["localhost:3000"],
- },
- },
+ // experimental: {
+ // serverActions: {
+ // allowedOrigins: ["localhost:3000"],
+ // },
+ // },
};
export default nextConfig;
diff --git a/src/app/anime/[id]/buttons.jsx b/src/app/anime/[id]/buttons.jsx
deleted file mode 100644
index c1b4615..0000000
--- a/src/app/anime/[id]/buttons.jsx
+++ /dev/null
@@ -1,153 +0,0 @@
-"use client";
-
-import styles from "./info.module.css";
-import { useState, useEffect } from "react";
-import { fetch_video_link } from "../videoLinkfetcher";
-import { MediaPlayer, MediaProvider } from "@vidstack/react";
-import "@vidstack/react/player/styles/default/theme.css";
-import "@vidstack/react/player/styles/default/layouts/video.css";
-import {
- defaultLayoutIcons,
- DefaultVideoLayout,
-} from "@vidstack/react/player/layouts/default";
-import { storeLocal } from "../history/storeData";
-
-export default function Button({ data2: info }) {
- const currentDate = new Date();
- const [videoLink, setVideoLink] = useState(null);
- const [buttonGroups, setButtonGroups] = useState(null);
-
- useEffect(() => {
- create_button_group(0, 50);
- }, []);
-
- const groups = createGroups(info.episodes, 50);
-
- async function video(id) {
- const link = await fetch_video_link(id);
- if (link === undefined) {
- alert("Sorry, but not links were found");
- } else {
- setVideoLink(link);
- }
- }
-
- function createGroups(array, size) {
- const groups = [];
- for (let i = 0; i < array.length; i += size) {
- groups.push(array.slice(i, i + size));
- }
- return groups;
- }
-
- function store_to_local(name, image, episode, id) {
- let newData = {
- name: name,
- image: image,
- episode: episode,
- id: id,
- type: "anime",
- date: `${currentDate.getDate()}-${String(
- currentDate.getMonth() + 1
- ).padStart(2, "0")}`,
- time: `${currentDate.getHours()}:${String(
- currentDate.getMinutes()
- ).padStart(2, "0")}`,
- };
- storeLocal(newData);
- }
-
- function create_button_group(start, end) {
- setButtonGroups(
- <div className={styles.buttonContainer}>
- {info.episodes &&
- info.episodes.slice(start, end).map((item, index) => (
- <button
- className={styles.dramaButton}
- key={index}
- onClick={(event) => {
- event.target.style.backgroundColor =
- "var(--soft-purple)";
- video(item.id);
- store_to_local(
- info.title,
- info.image,
- item.number,
- info.id
- );
- }}
- >
- {item.number}
- </button>
- ))}
- </div>
- );
- }
-
- function handleSelectChange(event) {
- const selectedIndex = event.target.selectedIndex;
- const selectedGroup = groups[selectedIndex];
- if (selectedGroup) {
- create_button_group(
- selectedGroup[0].number - 1,
- selectedGroup[selectedGroup.length - 1].number
- );
- }
- }
-
- return (
- <main>
- <h2 className={styles.buttonHeader}>Episodes: </h2>
- {info.episodes && (
- <select
- onChange={(event) => handleSelectChange(event)}
- className={styles.SelectClass}
- >
- {groups &&
- groups.map((item, index) => (
- <option key={index}>
- {item[0].number} -{" "}
- {item[item.length - 1].number}
- </option>
- ))}
- </select>
- )}
- {buttonGroups}
- {videoLink && (
- <div
- className={styles.videoPopUp}
- id="popup"
- onKeyDown={(event) => {
- if (event.code === "Escape") {
- setVideoLink("");
- }
- }}
- >
- <div className={styles.video}>
- <MediaPlayer
- title="dramaPlayer"
- src={videoLink}
- aspectRatio="16/9"
- load="eager"
- className={styles.VideoPlayer}
- playsInline
- id="videoPlayer"
- volume={0.8}
- >
- <MediaProvider />
- <DefaultVideoLayout icons={defaultLayoutIcons} />
- </MediaPlayer>
- <button
- className={styles.closeButton}
- onClick={() => {
- setVideoLink("");
- }}
- >
- Close
- </button>
- </div>
- </div>
- )}
- </main>
- );
-}
diff --git a/src/app/anime/[id]/info.module.css b/src/app/anime/[id]/info.module.css
deleted file mode 100644
index fd14d32..0000000
--- a/src/app/anime/[id]/info.module.css
+++ /dev/null
@@ -1,187 +0,0 @@
-.dramaInfoContainer {
- display: flex;
- flex-direction: column;
-}
-
-.dramaInfo {
- display: flex;
- flex-direction: column;
- width: 98%;
- margin: 80px auto;
-}
-
-.titleContainer {
- display: flex;
- justify-content: space-between;
- align-items: center;
-}
-
-.titleContainer p {
- color: var(--neon-green);
- width: 60%;
- font-weight: 500;
- font-size: 34px;
-}
-
-.titleContainer img {
- aspect-ratio: 16 / 9;
- border-radius: 10px;
-}
-
-.animeDescription h2 {
- color: #ffffff81;
- margin: 20px 0px -10px 0px;
-}
-
-.animeDescription p {
- color: rgba(255, 255, 255, 0.637);
-}
-
-.SelectClass {
- background-color: #272727;
- color: white;
- font-family: "Lexend Deca", serif;
- border: none;
- outline: none;
- padding: 0.5rem;
- border-radius: 0.5rem;
- text-align: center;
-}
-
-.SelectClass::-webkit-scrollbar {
- width: 0;
-}
-
-.SelectClass option:checked {
- background-color: rgb(0, 160, 223);
-}
-
-.buttonContainer {
- margin: 0px;
- height: 200px;
- overflow-y: auto;
- border-radius: 10px;
-}
-
-.buttonHeader {
- margin: 0px 10px 10px 0px;
- color: #ffffff81;
-}
-
-.buttonContainer button {
- transition: transform 200ms ease-in-out;
-}
-
-.buttonContainer button:focus {
- opacity: 0.7;
- transition: transform 200ms ease-in;
- background-color: var(--soft-purple);
- transform: scale(0.9);
-}
-
-.buttonContainer::-webkit-scrollbar {
- width: 5px;
-}
-
-.buttonContainer::-webkit-scrollbar-thumb {
- background-color: var(--soft-purple);
- border-radius: 1rem;
-}
-
-.dramaButton {
- padding: 10px;
- font-family: "Lexend Deca", serif;
- margin: 0.5rem 0.5rem 0px 0px;
- width: 55px;
- border-radius: 5px;
- text-align: center;
- border: none;
- outline: none;
- color: white;
- background-color: #3d3d3d;
- transition: background-color 200ms ease-in-out;
- cursor: pointer;
-}
-
-.dramaButton:hover {
- background-color: #1f1f1f;
- transition: background-color 200ms ease-in;
-}
-
-.infoPageContainer p {
- color: white;
-}
-
-.animeDetails {
- color: white;
-}
-
-.genreEntries {
- margin: 3px;
- padding: 4px;
- border-radius: 5px;
- color: var(--neon-yellow);
- background-color: #303030;
-}
-
-.animeType span {
- color: var(--light-green);
-}
-
-.animeRelease span {
- color: var(--soft-purple);
-}
-
-.videoPopUp {
- height: 100dvh;
- width: 100dvw;
- background-color: #141414ee;
- position: fixed;
- bottom: 0;
- left: 0;
- right: 0;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- z-index: 1;
- overflow-y: auto;
-}
-
-.closeButton {
- font-family: "Lexend Deca", serif;
- font-size: 16px;
- background-color: var(--pastel-red);
- padding: 0.5rem 1.5rem;
- border: 0;
- outline: 0;
- border-radius: 0.5rem;
- cursor: pointer;
- margin: 5px;
-}
-
-.video {
- width: 60vw;
-}
-
-.VideoPlayer {
- width: 100%;
-}
-
-@media screen and (max-width: 768px) {
- .titleContainer p {
- font-size: 28px;
- }
-
- .animeDetails {
- font-size: 14px;
- }
-
- .video {
- width: 100%;
- }
-
- .buttonContainer {
- text-align: center;
- }
-}
diff --git a/src/app/anime/[id]/loading.jsx b/src/app/anime/[id]/loading.jsx
deleted file mode 100644
index 12876eb..0000000
--- a/src/app/anime/[id]/loading.jsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import styles from "./loading.module.css";
-
-export default async function Loading() {
- return (
- <div className={styles.Main}>
- <div className={styles.LoadingContainer}></div>
- </div>
- );
-}
diff --git a/src/app/anime/[id]/loading.module.css b/src/app/anime/[id]/loading.module.css
deleted file mode 100644
index a9feceb..0000000
--- a/src/app/anime/[id]/loading.module.css
+++ /dev/null
@@ -1,22 +0,0 @@
-.Main {
- height: 100vh;
- width: 100vw;
- display: flex;
- justify-content: center;
- align-items: center;
-}
-
-.LoadingContainer {
- width: 50px;
- height: 50px;
- border-radius: 50%;
- border: 8px solid;
- border-color: #f4f4f4 #0000;
- animation: s1 1s infinite;
-}
-
-@keyframes s1 {
- to {
- transform: rotate(0.5turn);
- }
-}
diff --git a/src/app/anime/[id]/page.jsx b/src/app/anime/[id]/page.jsx
index d133118..7715dc6 100644
--- a/src/app/anime/[id]/page.jsx
+++ b/src/app/anime/[id]/page.jsx
@@ -1,68 +1,82 @@
-import styles from "./info.module.css";
import Image from "next/image";
-import Button from "./buttons";
-import { preFetchAnimeLinks } from "../videoLinkfetcher";
-export default async function AnimeInfo({ params }) {
- let animeID = params.id;
+import { anime_info } from "../data-fetch/request";
+import styles from "../styles/info.module.css";
+import EpisodesButtons from "../components/episode_buttons";
- const info = await getAnimeInfo(animeID);
+import { preFetchVideoLinks } from "../components/cacher";
- preFetchAnimeLinks(info);
+const AnimeInfoHomepage = async ({ params }) => {
+ const id = params.id;
+ const data = await anime_info(id);
+
+ const sliceLength = Math.min(data.episodes.length, 49);
+ preFetchVideoLinks(data.episodes.slice(0, sliceLength));
return (
- <div className={styles.dramaInfoContainer}>
- <div className={styles.dramaInfo}>
- {info && (
- <div>
- <div className={styles.titleContainer}>
- <p>{info.title}</p>
+ <main className={styles.main}>
+ <div>
+ {data && (
+ <section className={styles.AnimeInfo}>
+ <div className={styles.AnimeHeroSection}>
<Image
- src={info.image}
- width={180}
- height={260}
- alt="Drama"
+ src={data.image}
+ width={200}
+ height={300}
+ alt="Anime Poster"
priority
/>
+ <div>
+ <p className={styles.animeTitle}>
+ <strong
+ style={{ color: "var(--neon-green)" }}
+ >
+ {data.title || "Not Found"}
+ </strong>
+ </p>
+ <p className={styles.animeDescription}>
+ <strong>Description: </strong>
+ {data.description || "Not Found"}
+ </p>
+ <hr style={{ borderColor: "gray" }} />
+ <span>
+ <strong style={{ marginRight: 5 }}>
+ Genres:
+ </strong>
+ {data.genres &&
+ data.genres.map((item, index) => (
+ <span key={index}>
+ {item}
+ {index !==
+ data.genres.length - 1 &&
+ ", "}
+ </span>
+ ))}
+ </span>
+ <p>
+ <strong>Episodes:</strong>{" "}
+ {data.totalEpisodes || "Not Found"}
+ </p>
+ <p>
+ <strong>Release year:</strong>{" "}
+ {data.releaseDate || "Not Found"}
+ </p>
+ <p>
+ <strong>Status:</strong>{" "}
+ {data.status || "Not Found"}
+ </p>
+ <p>
+ <strong>Type:</strong>{" "}
+ {data.type || "Not Found"}
+ </p>
+ </div>
</div>
- <div className={styles.animeDescription}>
- <h2>Description</h2>
- <p>{info.description}</p>
- </div>
- </div>
+ </section>
)}
-
- <div className={styles.animeDetails}>
- <span className={styles.genre}>Genres: </span>
- {info.genres &&
- info.genres.map((item, index) => (
- <span className={styles.genreEntries} key={index}>
- {item}
- </span>
- ))}
- <p className={styles.animeType}>
- Type: <span>{info && info.type}</span>
- </p>
- <p className={styles.animeRelease}>
- Release year:
- <span>
- {" "}
- {info && info.releaseDate}, {info && info.status}
- </span>
- </p>
- </div>
-
- <Button data2={info} />
</div>
- </div>
+ <EpisodesButtons data={data} />
+ </main>
);
-}
+};
-async function getAnimeInfo(anime_id) {
- const res = await fetch(
- "https://consumet-jade.vercel.app/anime/gogoanime/info/" + anime_id,
- { next: { revalidate: 7200 } }
- );
- const data = await res.json();
- return data;
-}
+export default AnimeInfoHomepage;
diff --git a/src/app/anime/anime.module.css b/src/app/anime/anime.module.css
deleted file mode 100644
index 72c6fde..0000000
--- a/src/app/anime/anime.module.css
+++ /dev/null
@@ -1,3 +0,0 @@
-.main {
- margin: 50px auto;
-}
diff --git a/src/app/anime/components/cacher.js b/src/app/anime/components/cacher.js
new file mode 100644
index 0000000..d3008fa
--- /dev/null
+++ b/src/app/anime/components/cacher.js
@@ -0,0 +1,29 @@
+"use server";
+
+import { info_url, watch_url } from "../../../../utils/anime_urls";
+
+export async function preFetchAnimeInfo(data) {
+ try {
+ const fetchPromises = data.results.map(async (element) => {
+ await fetch(info_url(element.id), { next: { revalidate: 21600 } });
+ });
+
+ await Promise.all(fetchPromises);
+ console.log("Anime info pre-fetched successfully!");
+ } catch (error) {
+ console.error("Error occurred while pre-fetching anime info: ", error);
+ }
+}
+
+export async function preFetchVideoLinks(data) {
+ try {
+ const fetchPromises = data.map(async (element) => {
+ await fetch(watch_url(element.id), { next: { revalidate: 21600 } });
+ });
+
+ await Promise.all(fetchPromises);
+ console.log("Anime video links info pre-fetched successfully!");
+ } catch (error) {
+ console.error("Error occurred while pre-fetching anime info: ", error);
+ }
+}
diff --git a/src/app/anime/components/episode_buttons.jsx b/src/app/anime/components/episode_buttons.jsx
new file mode 100644
index 0000000..013dee1
--- /dev/null
+++ b/src/app/anime/components/episode_buttons.jsx
@@ -0,0 +1,205 @@
+"use client";
+import { useState, useEffect } from "react";
+import { MediaPlayer, MediaProvider } from "@vidstack/react";
+import "@vidstack/react/player/styles/default/theme.css";
+import "@vidstack/react/player/styles/default/layouts/video.css";
+import {
+ defaultLayoutIcons,
+ DefaultVideoLayout,
+} from "@vidstack/react/player/layouts/default";
+
+import styles from "../styles/buttons.module.css";
+import { video_url } from "../data-fetch/request";
+import { preFetchVideoLinks } from "./cacher";
+import { storeLocal } from "./storeHistory";
+
+const EpisodesButtons = ({ data: data }) => {
+ const [videoLink, setVideoLink] = useState(null);
+ const [buttonGroups, setButtonGroups] = useState(null);
+ const [videoLoading, setVideoLoading] = useState(null);
+
+ useEffect(() => {
+ setButtonGroups(createButtonGroups(0, 50));
+ }, []);
+
+ const groups = createGroups(data.episodes, 50);
+
+ /**
+ * Retrieves the video URL for a given episode ID.
+ * This function handles the initializing, changing URL, and hiding the video player.
+ * @param {string} epID - The episode ID.
+ */
+ async function getVideoURL(epID) {
+ setVideoLoading(true);
+ const data = await video_url(epID);
+ setVideoLink(data.sources[data.sources.length - 2].url);
+ setVideoLoading(false);
+ }
+
+ /**
+ * Creates button groups for a range of episodes.
+ * @param {number} start - The starting index of episodes.
+ * @param {number} end - The ending index of episodes.
+ * @returns {JSX.Element} - Button groups JSX element.
+ */
+ function createButtonGroups(start, end) {
+ try {
+ const buttons = document.getElementsByClassName("episode-button");
+ for (let i = 0; i < buttons.length; i++) {
+ buttons[i].style.backgroundColor = "#1f1f1fd2";
+ }
+ } catch (error) {
+ console.error(
+ "ERROR: This error is expected. This is done in order to reset the background color of the buttons. I can't think of a better way than this....so yeah.",
+ error.message
+ );
+ }
+
+ return (
+ <div className={styles.animeButtonContainer}>
+ {data.episodes &&
+ data.episodes.slice(start, end).map((item, index) => (
+ <button
+ className={styles.dramaButton + " episode-button"}
+ key={index}
+ onClick={(event) => {
+ event.target.style.backgroundColor =
+ "var(--soft-purple)";
+ getVideoURL(item.id);
+ store_to_local(
+ data.title,
+ data.image,
+ item.number,
+ data.id
+ );
+ }}
+ >
+ {item.number}
+ </button>
+ ))}
+ </div>
+ );
+ }
+
+ /**
+ * Handles the change event of the select element.
+ * @param {Event} event - The change event object.
+ */
+ function handleSelectChange(event) {
+ const selectedIndex = event.target.selectedIndex;
+ const selectedGroup = groups[selectedIndex];
+ if (selectedGroup) {
+ setButtonGroups(
+ createButtonGroups(
+ selectedGroup[0].number - 1,
+ selectedGroup[selectedGroup.length - 1].number
+ )
+ );
+ preFetchVideoLinks(
+ data.episodes.slice(
+ selectedGroup[0].number - 1,
+ selectedGroup[selectedGroup.length - 1].number
+ )
+ );
+ }
+ }
+
+ return (
+ <main className={styles.Main}>
+ {data.episodes && (
+ <select
+ onChange={(event) => handleSelectChange(event)}
+ className={styles.SelectClass}
+ >
+ {groups &&
+ groups.map((item, index) => (
+ <option key={index}>
+ {item[0].number} -{" "}
+ {item[item.length - 1].number}
+ </option>
+ ))}
+ </select>
+ )}
+ {buttonGroups}
+ {videoLoading && (
+ <p style={{ margin: "0.5rem 0 0 0" }}>Loading...</p>
+ )}
+ {videoLink && (
+ <div className={styles.videoPopUp} id="popup">
+ <div className={styles.video}>
+ <MediaPlayer
+ title="dramaPlayer"
+ src={videoLink}
+ aspectRatio="16/9"
+ load="eager"
+ className={styles.VideoPlayer}
+ playsInline
+ id="videoPlayer"
+ volume={0.8}
+ >
+ <MediaProvider />
+ <DefaultVideoLayout icons={defaultLayoutIcons} />
+ </MediaPlayer>
+ <button
+ className={styles.closeButton}
+ onClick={() => {
+ setVideoLink("");
+ }}
+ >
+ Close
+ </button>
+ </div>
+ </div>
+ )}
+ </main>
+ );
+};
+
+/**
+ * Divides an array into groups of a specified size.
+ * @param {Array} array - The array to be divided.
+ * @param {number} size - The size of each group.
+ * @returns {Array} - An array containing groups of elements.
+ */
+function createGroups(array, size) {
+ const groups = [];
+ for (let i = 0; i < array.length; i += size) {
+ groups.push(array.slice(i, i + size));
+ }
+ return groups;
+}
+
+/**
+ * Stores watch history to local storage.
+ * @param {string} name - The name of the episode.
+ * @param {string} image - The image URL of the episode.
+ * @param {number} episode - The episode number.
+ * @param {string} id - The ID of the episode.
+ */
+function store_to_local(name, image, episode, id) {
+ const currentDate = new Date();
+
+ try {
+ let newData = {
+ name: name,
+ image: image,
+ episode: episode,
+ id: id,
+ type: "anime",
+ date: `${currentDate.getDate()}-${String(
+ currentDate.getMonth() + 1
+ ).padStart(2, "0")}`,
+ time: `${currentDate.getHours()}:${String(
+ currentDate.getMinutes()
+ ).padStart(2, "0")}`,
+ };
+ storeLocal(newData);
+ } catch (error) {
+ console.error(
+ "Some error occurred during the process of saving your watch history to local storage. Please try again or contact us on GitHub if this issue persists.",
+ error.message
+ );
+ }
+}
+
+export default EpisodesButtons; \ No newline at end of file
diff --git a/src/app/anime/components/popularAnimes.jsx b/src/app/anime/components/popularAnimes.jsx
new file mode 100644
index 0000000..2259a42
--- /dev/null
+++ b/src/app/anime/components/popularAnimes.jsx
@@ -0,0 +1,51 @@
+import Link from "next/link";
+import Image from "next/image";
+import { Atkinson_Hyperlegible } from "next/font/google";
+
+import styles from "../styles/pop_recent_top.module.css";
+import { popular, anime_info } from "../data-fetch/request";
+import { preFetchAnimeInfo, preFetchVideoLinks } from "./cacher";
+
+const atkinson = Atkinson_Hyperlegible({ subsets: ["latin"], weight: "400" });
+
+const PopularAnimes = async () => {
+ const data = await popular();
+
+ preFetchAnimeInfo(data);
+
+ return (
+ <main className={styles.Main}>
+ <section>
+ <h2 className={styles.AnimeHeaderText}>Popular Animes</h2>
+ <div className={styles.AnimeContainer}>
+ {data &&
+ data.results.map((item, index) => (
+ <Link
+ key={index}
+ href={`/anime/${item.id}`}
+ shallow
+ style={{
+ color: "white",
+ textDecoration: "none",
+ }}
+ className={atkinson.className}
+ title={item.title}
+ >
+ <section className={styles.AnimeEntry}>
+ <Image
+ src={item.image}
+ width={167}
+ height={267}
+ alt="Anime Poster Image"
+ />
+ <p>{item.title}</p>
+ </section>
+ </Link>
+ ))}
+ </div>
+ </section>
+ </main>
+ );
+};
+
+export default PopularAnimes;
diff --git a/src/app/anime/components/recentEpisodes.jsx b/src/app/anime/components/recentEpisodes.jsx
new file mode 100644
index 0000000..43ad1de
--- /dev/null
+++ b/src/app/anime/components/recentEpisodes.jsx
@@ -0,0 +1,54 @@
+import Link from "next/link";
+import Image from "next/image";
+import { Atkinson_Hyperlegible } from "next/font/google";
+
+import styles from "../styles/pop_recent_top.module.css";
+import { recent } from "../data-fetch/request";
+import { preFetchAnimeInfo } from "./cacher";
+
+const atkinson = Atkinson_Hyperlegible({ subsets: ["latin"], weight: "400" });
+
+const RecentAnimes = async () => {
+ const data = await recent();
+
+ preFetchAnimeInfo(data);
+
+ return (
+ <main className={styles.Main}>
+ <section>
+ <h2 className={styles.AnimeHeaderText}>Recent Releases</h2>
+ <div className={styles.AnimeContainer}>
+ {data &&
+ data.results.map((item, index) => (
+ <Link
+ key={index}
+ href={`/anime/${item.id}`}
+ shallow
+ style={{
+ color: "white",
+ textDecoration: "none",
+ }}
+ className={atkinson.className}
+ title={item.title}
+ >
+ <section className={styles.AnimeEntry}>
+ <Image
+ src={item.image}
+ width={167}
+ height={267}
+ alt="Anime Poster Image"
+ />
+ <p>{item.title}</p>
+ <p className={styles.EpisodeText}>
+ Episode: {item.episodeNumber}
+ </p>
+ </section>
+ </Link>
+ ))}
+ </div>
+ </section>
+ </main>
+ );
+};
+
+export default RecentAnimes;
diff --git a/src/app/anime/components/search.jsx b/src/app/anime/components/search.jsx
new file mode 100644
index 0000000..66c491a
--- /dev/null
+++ b/src/app/anime/components/search.jsx
@@ -0,0 +1,64 @@
+"use client";
+
+import { FaSearch } from "react-icons/fa";
+import { useState } from "react";
+import Link from "next/link";
+
+import styles from "../styles/search.module.css";
+import SearchResults from "./search_results";
+
+const SearcBar = () => {
+ const [title, setTitle] = useState("");
+ const [searchResults, setSearchResults] = useState(null);
+ const [loading, setLoading] = useState(false);
+
+ const handleSearchInput = async (title) => {
+ setSearchResults(null);
+ setLoading(true);
+ setSearchResults(await SearchResults(title));
+ setLoading(false);
+ };
+
+ return (
+ <main>
+ <section className={styles.SearchBarContainer}>
+ <div className={styles.SearchInputContainer}>
+ <FaSearch color="white" />
+ <input
+ placeholder="Enter anime title"
+ name="Anime Title"
+ type="text"
+ onChange={(event) => {
+ if (event.target.value.trim() != "") {
+ setTitle(event.target.value);
+ }
+ }}
+ autoComplete="off"
+ onKeyDown={async (event) => {
+ if (
+ event.code === "Enter" ||
+ event.key === "Enter" ||
+ event.code === 13
+ ) {
+ await handleSearchInput(title);
+ }
+ }}
+ ></input>
+ </div>
+ <Link shallow href={"/anime/continueWatching"}>
+ <button className={styles.animeHistoryButton}>
+ History
+ </button>
+ </Link>
+ </section>
+ {loading && (
+ <p className={styles.SearchLoading}>
+ Please wait while we crunch up all the data.....
+ </p>
+ )}
+ {searchResults}
+ </main>
+ );
+};
+
+export default SearcBar;
diff --git a/src/app/anime/components/search_results.jsx b/src/app/anime/components/search_results.jsx
new file mode 100644
index 0000000..d4c8146
--- /dev/null
+++ b/src/app/anime/components/search_results.jsx
@@ -0,0 +1,42 @@
+import { Atkinson_Hyperlegible } from "next/font/google";
+import Link from "next/link";
+import Image from "next/image";
+
+import styles from "../styles/search.module.css";
+import { search_results } from "../data-fetch/request";
+import { preFetchAnimeInfo } from "./cacher";
+
+const atkinson = Atkinson_Hyperlegible({ subsets: ["latin"], weight: "400" });
+
+const SearchResults = async (title) => {
+ const data = await search_results(title);
+
+ preFetchAnimeInfo(data);
+
+ return (
+ <section className={styles.SearchResultsContainer}>
+ {data &&
+ data.results.map((item, index) => (
+ <Link
+ shallow
+ href={`/anime/${item.id}`}
+ key={index}
+ className={atkinson.className}
+ style={{ color: "white", textDecoration: "none" }}
+ >
+ <div className={styles.AnimeEntry}>
+ <p>{item.title}</p>
+ <Image
+ src={item.image}
+ width={140}
+ height={200}
+ alt="Anime Poster"
+ />
+ </div>
+ </Link>
+ ))}
+ </section>
+ );
+};
+
+export default SearchResults;
diff --git a/src/app/anime/history/storeData.js b/src/app/anime/components/storeHistory.js
index 4008095..bd41815 100644
--- a/src/app/anime/history/storeData.js
+++ b/src/app/anime/components/storeHistory.js
@@ -1,35 +1,35 @@
-"use client";
-
-export function storeLocal(watchData) {
- const currentDate = new Date();
- const jsonData = localStorage.getItem("data");
- const dataObject = jsonData ? JSON.parse(jsonData) : {};
-
- if (!dataObject.watchHis) {
- dataObject.watchHis = [];
- }
-
- let found = false;
- dataObject.watchHis.forEach((element) => {
- if (element.name === watchData.name) {
- let episode = watchData.episode;
- let date = `${currentDate.getDate()}-${String(
- currentDate.getMonth() + 1
- ).padStart(2, "0")}`;
- let time = `${currentDate.getHours()}:${String(
- currentDate.getMinutes()
- ).padStart(2, "0")}`;
- element.episode = episode;
- element.date = date;
- element.time = time;
- found = true;
- }
- });
-
- if (!found) {
- dataObject.watchHis.push(watchData);
- }
-
- let updatedData = JSON.stringify(dataObject);
- localStorage.setItem("data", updatedData);
-}
+"use client";
+
+export function storeLocal(watchData) {
+ const currentDate = new Date();
+ const jsonData = localStorage.getItem("data");
+ const dataObject = jsonData ? JSON.parse(jsonData) : {};
+
+ if (!dataObject.watchHis) {
+ dataObject.watchHis = [];
+ }
+
+ let found = false;
+ dataObject.watchHis.forEach((element) => {
+ if (element.name === watchData.name) {
+ let episode = watchData.episode;
+ let date = `${currentDate.getDate()}-${String(
+ currentDate.getMonth() + 1
+ ).padStart(2, "0")}`;
+ let time = `${currentDate.getHours()}:${String(
+ currentDate.getMinutes()
+ ).padStart(2, "0")}`;
+ element.episode = episode;
+ element.date = date;
+ element.time = time;
+ found = true;
+ }
+ });
+
+ if (!found) {
+ dataObject.watchHis.push(watchData);
+ }
+
+ let updatedData = JSON.stringify(dataObject);
+ localStorage.setItem("data", updatedData);
+}
diff --git a/src/app/anime/components/topAiring.jsx b/src/app/anime/components/topAiring.jsx
new file mode 100644
index 0000000..28d3f6d
--- /dev/null
+++ b/src/app/anime/components/topAiring.jsx
@@ -0,0 +1,54 @@
+import Link from "next/link";
+import Image from "next/image";
+import { Atkinson_Hyperlegible } from "next/font/google";
+
+import styles from "../styles/pop_recent_top.module.css";
+import { top_airing } from "../data-fetch/request";
+import { preFetchAnimeInfo } from "./cacher";
+
+const atkinson = Atkinson_Hyperlegible({ subsets: ["latin"], weight: "400" });
+
+const TopAiringAnimes = async () => {
+ const data = await top_airing();
+
+ preFetchAnimeInfo(data);
+
+ return (
+ <main className={styles.Main}>
+ <section>
+ <h2 className={styles.AnimeHeaderText}>Top Airing Animes</h2>
+ <div className={styles.AnimeContainer}>
+ {data &&
+ data.results.map((item, index) => (
+ <Link
+ key={index}
+ href={`/anime/${item.id}`}
+ shallow
+ style={{
+ color: "white",
+ textDecoration: "none",
+ }}
+ className={atkinson.className}
+ title={item.title}
+ >
+ <section className={styles.AnimeEntry}>
+ <Image
+ src={item.image}
+ width={167}
+ height={267}
+ alt="Anime Poster Image"
+ />
+ <p>{item.title}</p>
+ <p className={styles.EpisodeText}>
+ Episode: {item.episodeNumber}
+ </p>
+ </section>
+ </Link>
+ ))}
+ </div>
+ </section>
+ </main>
+ );
+};
+
+export default TopAiringAnimes;
diff --git a/src/app/anime/history/continueWatching/page.jsx b/src/app/anime/continueWatching/page.jsx
index ecb73fc..eb28a2f 100644
--- a/src/app/anime/history/continueWatching/page.jsx
+++ b/src/app/anime/continueWatching/page.jsx
@@ -1,65 +1,65 @@
-"use client";
-
-import React, { useState, useEffect } from "react";
-import Image from "next/image";
-import styles from "./cw.module.css";
-import Link from "next/link";
-
-const ContinueWatching = () => {
- const [localItems, setLocalItems] = useState(null);
-
- useEffect(() => {
- const newData = get_local();
- setLocalItems(newData);
- }, []); // Empty dependency array means this effect runs only once after the initial render
-
- function get_local() {
- try {
- const data = localStorage.getItem("data");
- return JSON.parse(data);
- } catch (error) {
- console.log("error", error);
- return false;
- }
- }
-
- return (
- <main className={styles.main}>
- <p className={styles.mainText}>Continue Watching</p>
- {localItems && (
- <div className={styles.animeContainer}>
- {localItems.watchHis &&
- localItems.watchHis.map((item, index) => (
- <Link
- href={`/${item.type}/${item.id}`}
- style={{ textDecoration: "none" }}
- key={index}
- >
- <div className={styles.animeEntry}>
- <div className={styles.titleContainer}>
- <h3>{item.name}</h3>
- <p className={styles.EpisodeCount}>
- Episode watching: {item.episode}
- </p>
- <p className={styles.date}>
- Last watched on: {item.date} at{" "}
- {item.time} hours
- </p>
- </div>
- <Image
- src={item.image}
- width={167}
- height={267}
- alt="Continue anime poster"
- priority
- />
- </div>
- </Link>
- ))}
- </div>
- )}
- </main>
- );
-};
-
-export default ContinueWatching;
+"use client";
+
+import React, { useState, useEffect } from "react";
+import Image from "next/image";
+import styles from "../styles/cw.module.css";
+import Link from "next/link";
+
+const ContinueWatching = () => {
+ const [localItems, setLocalItems] = useState(null);
+
+ useEffect(() => {
+ const newData = get_local();
+ setLocalItems(newData);
+ }, []); // Empty dependency array means this effect runs only once after the initial render
+
+ function get_local() {
+ try {
+ const data = localStorage.getItem("data");
+ return JSON.parse(data);
+ } catch (error) {
+ console.log("error", error);
+ return false;
+ }
+ }
+
+ return (
+ <main className={styles.main}>
+ <p className={styles.mainText}>Continue Watching</p>
+ {localItems && (
+ <div className={styles.animeContainer}>
+ {localItems.watchHis &&
+ localItems.watchHis.map((item, index) => (
+ <Link
+ href={`/${item.type}/${item.id}`}
+ style={{ textDecoration: "none" }}
+ key={index}
+ >
+ <div className={styles.animeEntry}>
+ <div className={styles.titleContainer}>
+ <h3>{item.name}</h3>
+ <p className={styles.EpisodeCount}>
+ Episode watching: {item.episode}
+ </p>
+ <p className={styles.date}>
+ Last watched on: {item.date} at{" "}
+ {item.time} hours
+ </p>
+ </div>
+ <Image
+ src={item.image}
+ width={167}
+ height={267}
+ alt="Continue anime poster"
+ priority
+ />
+ </div>
+ </Link>
+ ))}
+ </div>
+ )}
+ </main>
+ );
+};
+
+export default ContinueWatching;
diff --git a/src/app/anime/data-fetch/request.js b/src/app/anime/data-fetch/request.js
new file mode 100644
index 0000000..3d22b7a
--- /dev/null
+++ b/src/app/anime/data-fetch/request.js
@@ -0,0 +1,54 @@
+"use server";
+
+import {
+ popular_url,
+ recent_epsiodes_url,
+ top_airing_url,
+ search_url,
+ info_url,
+ watch_url,
+} from "../../../../utils/anime_urls";
+
+export const popular = async () => {
+ const res = await fetch(popular_url, { next: { revalidate: 21600 } });
+ const data = await res.json();
+ return data;
+};
+
+export const recent = async () => {
+ const res = await fetch(recent_epsiodes_url, {
+ next: { revalidate: 21600 },
+ });
+ const data = await res.json();
+ return data;
+};
+
+export const top_airing = async () => {
+ const res = await fetch(top_airing_url, { next: { revalidate: 21600 } });
+ const data = await res.json();
+ return data;
+};
+
+export const search_results = async (title) => {
+ const res = await fetch(search_url(title), {
+ next: { revalidate: 21600 },
+ });
+ const data = await res.json();
+ return data;
+};
+
+export const anime_info = async (id) => {
+ const res = await fetch(info_url(id), {
+ next: { revalidate: 21600 },
+ });
+ const data = await res.json();
+ return data;
+};
+
+export const video_url = async (episodeId) => {
+ const res = await fetch(watch_url(episodeId), {
+ next: { revalidate: 21600 },
+ });
+ const data = await res.json();
+ return data;
+};
diff --git a/src/app/anime/loading.jsx b/src/app/anime/loading.jsx
new file mode 100644
index 0000000..00ce39c
--- /dev/null
+++ b/src/app/anime/loading.jsx
@@ -0,0 +1,11 @@
+import styles from "./styles/loading.module.css";
+
+const Loading = async () => {
+ return (
+ <main className={styles.LoadingContainer}>
+ <strong>Loading...</strong>
+ </main>
+ );
+};
+
+export default Loading;
diff --git a/src/app/anime/page.jsx b/src/app/anime/page.jsx
index 8fdbf86..209105b 100644
--- a/src/app/anime/page.jsx
+++ b/src/app/anime/page.jsx
@@ -1,18 +1,19 @@
-import styles from "./anime.module.css";
-import Trending from "./top-airing/page";
-import Releases from "./recent/page";
-import Popular from "./popular/page";
-import Input from "./search/page";
-
-export default async function Anime() {
+import styles from "./styles/anime.module.css";
+import PopularAnimes from "./components/popularAnimes";
+import RecentAnimes from "./components/recentEpisodes";
+import TopAiringAnimes from "./components/topAiring";
+import SearcBar from "./components/search";
+const AnimeHomepage = async () => {
return (
- <div className={styles.main}>
- <Input />
- <Trending />
- <br />
- <Releases />
- <br />
- <Popular />
- </div>
+ <main className={styles.Main}>
+ <SearcBar />
+ <TopAiringAnimes />
+ <hr style={{ marginTop: 15, borderColor: "gray" }} />
+ <RecentAnimes />
+ <hr style={{ marginTop: 15, borderColor: "gray" }} />
+ <PopularAnimes />
+ </main>
);
-}
+};
+
+export default AnimeHomepage;
diff --git a/src/app/anime/popular/page.jsx b/src/app/anime/popular/page.jsx
deleted file mode 100644
index 3ddd251..0000000
--- a/src/app/anime/popular/page.jsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import styles from "../top-airing/trending.module.css";
-import Image from "next/image";
-import Link from "next/link";
-import { preFetchAnimeInfo } from "../videoLinkfetcher";
-
-export default async function Popular() {
- const data = await test();
- preFetchAnimeInfo(data);
-
- return (
- <div className={styles.TrendingContainer}>
- <div className={styles.TrendingText}>
- <h1>Popular</h1>
- </div>
-
- <div className={styles.trending}>
- {data &&
- data.results.map((item, index) => (
- <Link
- key={index}
- href={`/anime/${item.id}`}
- style={{ textDecoration: "none", color: "white" }}
- >
- <div
- className={styles.trendingEntries}
- title={item.title}
- >
- <Image
- src={`https://sup-proxy.zephex0-f6c.workers.dev/api-content?url=${item.image}`}
- className={styles.trendingImage}
- width={167}
- height={267}
- alt="Drama"
- priority
- />
- <p>{item.title}</p>
- </div>
- </Link>
- ))}
- </div>
- </div>
- );
-}
-
-async function test() {
- const res = await fetch(
- "https://consumet-jade.vercel.app/anime/gogoanime/popular",
- { next: { revalidate: 21600 } }
- );
- const data = res.json();
- return data;
-}
diff --git a/src/app/anime/recent/page.jsx b/src/app/anime/recent/page.jsx
deleted file mode 100644
index 44aa301..0000000
--- a/src/app/anime/recent/page.jsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import Image from "next/image";
-import Link from "next/link";
-import styles from "../top-airing/trending.module.css";
-import { preFetchAnimeInfo } from "../videoLinkfetcher";
-
-export default async function Recent() {
- const data = await test();
- preFetchAnimeInfo(data);
-
- return (
- <div className={styles.TrendingContainer}>
- <div className={styles.TrendingText}>
- <h1>Recent Releases</h1>
- </div>
-
- <div className={styles.trending}>
- {data &&
- data.results.map((item, index) => (
- <Link
- key={index}
- href={`/anime/${item.id}`}
- style={{ textDecoration: "none", color: "white" }}
- >
- <div
- className={styles.trendingEntries}
- title={item.title}
- >
- <Image
- src={`https://sup-proxy.zephex0-f6c.workers.dev/api-content?url=${item.image}`}
- className={styles.trendingImage}
- width={167}
- height={267}
- alt="Drama"
- priority
- />
- <p>{item.title}</p>
- </div>
- </Link>
- ))}
- </div>
- </div>
- );
-}
-
-async function test() {
- const res = await fetch(
- "https://consumet-jade.vercel.app/anime/gogoanime/recent-episodes",
- { next: { revalidate: 21600 } }
- );
- const data = res.json();
- return data;
-}
diff --git a/src/app/anime/search/components/fetchInfo.js b/src/app/anime/search/components/fetchInfo.js
deleted file mode 100644
index 2f81345..0000000
--- a/src/app/anime/search/components/fetchInfo.js
+++ /dev/null
@@ -1,14 +0,0 @@
-"use server";
-
-export default async function Results(id) {
- return await testFunction(id);
-}
-
-async function testFunction(title) {
- const res = await fetch(
- "https://consumet-jade.vercel.app/anime/gogoanime/" + title,
- { next: { revalidate: 21600 } }
- );
- const data = await res.json();
- return data;
-}
diff --git a/src/app/anime/search/components/fetchedInfo.js b/src/app/anime/search/components/fetchedInfo.js
deleted file mode 100644
index 359e745..0000000
--- a/src/app/anime/search/components/fetchedInfo.js
+++ /dev/null
@@ -1,45 +0,0 @@
-import styles from "../search.module.css";
-import Link from "next/link";
-import Image from "next/image";
-import { preFetchAnimeInfo } from "../../videoLinkfetcher";
-
-export default async function fetchedInfo(data) {
- preFetchAnimeInfo(data);
- return (
- <div className={styles.animeEntry}>
- {data ? (
- data.results && data.results.length > 0 ? (
- data.results.map((item, index) => (
- <Link
- key={index}
- href={`/anime/${item.id}`}
- style={{ textDecoration: "none" }}
- >
- <div className={styles.anime}>
- <p>{item.title}</p>
- <Image
- src={`https://sup-proxy.zephex0-f6c.workers.dev/api-content?url=${item.image}`}
- className={styles.animeImage}
- width={120}
- height={180}
- alt="Drama Poster"
- />
- </div>
- </Link>
- ))
- ) : (
- <div style={{ margin: "0px auto" }}>
- <p
- style={{
- color: "white",
- fontSize: 18,
- }}
- >
- No results found
- </p>
- </div>
- )
- ) : null}
- </div>
- );
-}
diff --git a/src/app/anime/search/page.jsx b/src/app/anime/search/page.jsx
deleted file mode 100644
index f543fd8..0000000
--- a/src/app/anime/search/page.jsx
+++ /dev/null
@@ -1,69 +0,0 @@
-"use client";
-
-import styles from "./search.module.css";
-import { FaSearch } from "react-icons/fa"; // Import the search icon from react-icons library
-import { useState } from "react";
-import Results from "./components/fetchInfo";
-import fetchedInfo from "./components/fetchedInfo";
-import Link from "next/link";
-
-export default function Input() {
- const [searchedAnime, setSearchedAnime] = useState(null);
- const [loading, setLoading] = useState(null);
- const [info, setInfo] = useState(null);
-
- const handleKeyPress = async (event) => {
- if (
- (event.code === "Enter" ||
- event.key === "Enter" ||
- event.code === 13) &&
- searchedAnime !== ""
- ) {
- setLoading(true);
- setInfo(await fetchedInfo(await Results(searchedAnime)));
- setLoading(false);
- } else if (
- (event.code === "Enter" ||
- event.key === "Enter" ||
- event.code === 13) &&
- searchedAnime === ""
- ) {
- alert("Input cannot be empty");
- }
- };
-
- return (
- <div style={{ marginBottom: -15 }}>
- <div className={styles.inputContainer}>
- <div className={styles.searchContainer}>
- <FaSearch className={styles.searchIcon} />
- <input
- onChange={(event) => {
- if (event.target.value.trim() !== "") {
- setSearchedAnime(event.target.value);
- }
- }}
- onKeyDown={(event) => handleKeyPress(event)}
- placeholder="Enter anime title"
- className={styles.SearchInput}
- ></input>
- </div>
- <div>
- <button>
- <Link href={"/anime/history/continueWatching"}>
- History
- </Link>
- </button>
- </div>
- </div>
-
- {loading && (
- <p className={styles.waitWhileLoading}>
- Please wait while we crunch all the data for you
- </p>
- )}
-
- {info}
- </div>
- );
-}
diff --git a/src/app/anime/search/search.module.css b/src/app/anime/search/search.module.css
deleted file mode 100644
index a185e36..0000000
--- a/src/app/anime/search/search.module.css
+++ /dev/null
@@ -1,114 +0,0 @@
-.waitWhileLoading {
- font-size: 18px;
- text-align: center;
- color: white;
-}
-
-.inputContainer {
- display: flex;
- align-items: center;
- margin: 0 0.3rem 0 0.3rem;
-}
-
-.inputContainer button {
- margin: 5px;
- background-color: #121212;
- padding: 10px;
- border-radius: 10px;
- outline: none;
- border: none;
- color: white;
- font-family: "Lexend Deca", serif;
-}
-
-.inputContainer button a {
- text-decoration: none;
- color: white;
-}
-
-.searchContainer input {
- border: none;
- border-radius: 5px;
- color: white;
- outline: none;
- background: none;
- width: 100%;
- font-family: "Lexend Deca", serif;
- font-size: 16px;
-}
-
-.searchContainer {
- display: flex;
- align-items: center;
- justify-content: center;
- margin: 20px 0px 20px 0px;
- background-color: #121212;
- padding: 10px;
- border-radius: 10px;
- width: 30%;
-}
-
-.searchIcon {
- color: white;
- margin-right: 5px;
-}
-
-.animeEntry {
- display: flex;
- overflow-x: auto;
- margin: 0 0 1rem 0.5rem;
-}
-
-.animeEntry:hover .anime {
- opacity: 0.5;
-}
-
-.animeEntry:hover .anime:hover {
- opacity: 1;
- background-color: #292929;
-}
-
-.animeEntry::-webkit-scrollbar {
- height: 5px;
-}
-
-.animeEntry::-webkit-scrollbar-track {
- background-color: #636363;
- border-radius: 5px;
-}
-
-.animeEntry::-webkit-scrollbar-thumb {
- background-color: rgba(196, 196, 196, 0.692);
- border-radius: 5px;
-}
-
-.anime {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 5px;
- margin: 0 10px 0 0;
- border-radius: 0.5rem;
- transition: opacity 200ms ease-in, background-color 200ms linear;
- background-color: #242424d0;
-}
-
-.anime p {
- color: white;
- width: 20dvw;
- font-size: 18px;
-}
-
-.animeImage {
- border-radius: 0.5rem;
-}
-
-@media screen and (max-width: 768px) {
- .searchContainer {
- width: 100%;
- }
-
- .anime p {
- width: 50dvw;
- }
-} \ No newline at end of file
diff --git a/src/app/anime/styles/anime.module.css b/src/app/anime/styles/anime.module.css
new file mode 100644
index 0000000..e5d402a
--- /dev/null
+++ b/src/app/anime/styles/anime.module.css
@@ -0,0 +1,4 @@
+.Main {
+ max-width: 99%;
+ margin: 65px auto;
+} \ No newline at end of file
diff --git a/src/app/anime/styles/buttons.module.css b/src/app/anime/styles/buttons.module.css
new file mode 100644
index 0000000..d0b8e78
--- /dev/null
+++ b/src/app/anime/styles/buttons.module.css
@@ -0,0 +1,98 @@
+.animeButtonContainer {
+ margin-top: 1rem;
+}
+
+.dramaButton {
+ background-color: #1f1f1fd2;
+ outline: none;
+ border: none;
+ color: white;
+ font-family: "Atkinson Hyperlegible", serif;
+ width: 50px;
+ padding: 0.5rem;
+ margin: 0 0.2rem 0.2rem 0;
+ border-radius: 0.5rem;
+ cursor: pointer;
+ transition: background-color 200ms ease, scale 200ms ease;
+}
+
+.dramaButton:hover {
+ background-color: #121212;
+ scale: 0.95;
+}
+
+.dramaButton:focus {
+ background-color: var(--soft-purple);
+ scale: 0.95;
+}
+
+.Main {
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+ text-align: center;
+}
+
+.SelectClass {
+ text-align: center;
+ outline: none;
+ border: none;
+ padding: 0.4rem;
+ font-family: "Lexend Deca", serif;
+ background-color: #1f1f1fd2;
+ color: white;
+ border-radius: 0.5rem;
+}
+
+.SelectClass::-webkit-scrollbar {
+ width: 0;
+}
+
+/* Video Player */
+
+.videoPopUp {
+ height: 100dvh;
+ width: 100dvw;
+ background-color: #141414ee;
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ z-index: 1;
+ overflow-y: auto;
+}
+
+.closeButton {
+ font-family: "Lexend Deca", serif;
+ font-size: 16px;
+ background-color: var(--pastel-red);
+ padding: 0.5rem 1.5rem;
+ border: 0;
+ outline: 0;
+ border-radius: 0.5rem;
+ cursor: pointer;
+ margin: 5px;
+}
+
+.video {
+ width: 60vw;
+}
+
+.VideoPlayer {
+ width: 100%;
+}
+
+@media screen and (max-width: 1024px) {
+
+ .dramaButton {
+ font-size: 14px;
+ }
+
+ .video {
+ width: 100%;
+ }
+} \ No newline at end of file
diff --git a/src/app/anime/history/continueWatching/cw.module.css b/src/app/anime/styles/cw.module.css
index cf79acc..cb579c7 100644
--- a/src/app/anime/history/continueWatching/cw.module.css
+++ b/src/app/anime/styles/cw.module.css
@@ -1,59 +1,59 @@
-.main {
- width: 99%;
- margin: 80px auto;
-}
-
-.mainText {
- color: var(--light-green);
- font-size: 24px;
-}
-
-.animeContainer {
- font-size: 18px;
- margin: 0px;
-}
-
-.animeEntry {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 5px;
- margin-bottom: 0.5rem;
- border-radius: 1rem;
- background-color: #1f1f1f;
-}
-
-.animeEntry img {
- width: 10%;
- height: auto;
- border-radius: 0.8rem;
-}
-
-.titleContainer {
- color: white;
- margin-left: 0.2rem;
-}
-
-.titleContainer h3 {
- margin: 0px;
-}
-
-.EpisodeCount {
- color: var(--soft-purple);
- margin: 0px;
-}
-
-.date {
- color: var(--neon-yellow);
- margin: 0px;
-}
-
-@media screen and (max-width: 768px) {
- .animeContainer {
- font-size: 14px;
- }
-
- .animeEntry img {
- width: 35%;
- }
-}
+.main {
+ width: 99%;
+ margin: 60px auto;
+}
+
+.mainText {
+ color: white;
+ font-size: 24px;
+ margin: 0.2rem 0 0.2rem 0;
+}
+
+.animeContainer {
+ font-size: 18px;
+ margin: 0px;
+}
+
+.animeEntry {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 5px;
+ margin-bottom: 0.5rem;
+ border-radius: 0.4rem;
+ background-color: #1f1f1f;
+}
+
+.animeEntry img {
+ border-radius: 0.4rem;
+ margin-left: 0.2rem;
+}
+
+.titleContainer {
+ color: white;
+ margin-left: 0.2rem;
+}
+
+.titleContainer h3 {
+ margin: 0px;
+}
+
+.EpisodeCount {
+ color: var(--soft-purple);
+ margin: 0px;
+}
+
+.date {
+ color: var(--neon-yellow);
+ margin: 0px;
+}
+
+@media screen and (max-width: 768px) {
+ .animeContainer {
+ font-size: 14px;
+ }
+
+ .animeEntry img {
+ width: 35%;
+ }
+} \ No newline at end of file
diff --git a/src/app/anime/styles/info.module.css b/src/app/anime/styles/info.module.css
new file mode 100644
index 0000000..4216b37
--- /dev/null
+++ b/src/app/anime/styles/info.module.css
@@ -0,0 +1,75 @@
+.main {
+ width: 50%;
+ margin: 60px auto;
+ color: white;
+}
+
+.AnimeHeroSection {
+ display: flex;
+ align-items: center;
+}
+
+.AnimeHeroSection strong {
+ color: var(--neon-yellow);
+}
+
+.AnimeHeroSection img {
+ width: auto;
+ height: auto;
+ padding: 0.5rem 0.7rem 0.5rem 0.7rem;
+ border-radius: 1rem;
+}
+
+.animeDescription {
+ max-height: 100px;
+ overflow: auto;
+ color: var(--light-sky);
+}
+
+.animeDescription::-webkit-scrollbar {
+ width: 3px;
+}
+
+.animeDescription::-webkit-scrollbar-thumb {
+ background-color: var(--nord-yellow);
+ border-radius: 1rem;
+}
+
+.AnimeHeroSection p {
+ margin: 0;
+}
+
+.animeTitle {
+ font-size: 28px;
+ color: var(--neon-green);
+}
+
+@media screen and (max-width: 1024px) {
+ .main {
+ width: 100%;
+ }
+
+ .AnimeHeroSection {
+ font-size: 14px;
+ }
+
+ .animeTitle {
+ font-size: 24px;
+ }
+
+ .AnimeHeroSection img {
+ padding: 0.4rem 0.4rem 0.4rem 0.4rem;
+ }
+}
+
+@media screen and (max-width: 425px) {
+ .AnimeHeroSection {
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+ }
+
+ .animeTitle {
+ text-align: center;
+ }
+} \ No newline at end of file
diff --git a/src/app/anime/styles/loading.module.css b/src/app/anime/styles/loading.module.css
new file mode 100644
index 0000000..f0d0606
--- /dev/null
+++ b/src/app/anime/styles/loading.module.css
@@ -0,0 +1,12 @@
+.LoadingContainer {
+ height: 100vh;
+ width: 100vw;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.LoadingContainer strong {
+ color: white;
+ font-size: 20px;
+} \ No newline at end of file
diff --git a/src/app/anime/styles/pop_recent_top.module.css b/src/app/anime/styles/pop_recent_top.module.css
new file mode 100644
index 0000000..0feda13
--- /dev/null
+++ b/src/app/anime/styles/pop_recent_top.module.css
@@ -0,0 +1,56 @@
+.AnimeHeaderText {
+ color: white;
+ margin: 0.4rem 0 0 0;
+}
+
+.AnimeContainer {
+ margin: 0.2rem 0 0 0;
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ grid-gap: 0.7rem;
+}
+
+.AnimeEntry {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-direction: column;
+ text-align: center;
+ padding: 0.8rem;
+ border-radius: 0.5rem;
+ background-color: #1f1f1f;
+}
+
+.AnimeEntry img {
+ border-radius: 0.5rem;
+}
+
+.AnimeEntry p {
+ margin: 0.2rem 0 -0.4rem 0;
+ max-width: 160px;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
+
+.EpisodeText {
+ color: var(--neon-yellow);
+ font-size: small;
+ padding-top: 0.2rem;
+}
+.AnimeContainer::-webkit-scrollbar {
+ height: 0.3rem;
+}
+
+.AnimeContainer::-webkit-scrollbar-thumb {
+ background-color: var(--neon-yellow);
+ border-radius: 1rem;
+}
+
+@media screen and (max-width: 768px) {
+ .AnimeContainer {
+ display: flex;
+ overflow: auto;
+ align-items: center;
+ }
+} \ No newline at end of file
diff --git a/src/app/anime/styles/search.module.css b/src/app/anime/styles/search.module.css
new file mode 100644
index 0000000..25fa4d4
--- /dev/null
+++ b/src/app/anime/styles/search.module.css
@@ -0,0 +1,98 @@
+.SearchBarContainer {
+ padding: 1rem 0 0.8rem 0;
+ display: flex;
+ align-items: center;
+ width: 100%;
+}
+
+.SearchInputContainer {
+ display: flex;
+ align-items: center;
+ background-color: #121212;
+ padding: 0.5rem;
+ border-radius: 0.5rem;
+ width: 40%;
+}
+
+.animeHistoryButton {
+ font-family: "Lexend Deca", serif;
+ outline: none;
+ border: none;
+ background-color: #121212;
+ color: white;
+ margin: 0 0.1rem 0 0.2rem;
+ padding: 0.5rem;
+ cursor: pointer;
+ border-radius: 0.5rem;
+}
+
+.SearchInputContainer input {
+ background-color: transparent;
+ border: none;
+ outline: none;
+ color: white;
+ margin-left: 0.5rem;
+ font-size: 18px;
+ font-family: "Atkinson Hyperlegible", serif;
+ width: 100%;
+}
+
+.SearchLoading {
+ color: white;
+ text-align: center;
+}
+
+/* Search Results */
+
+.SearchResultsContainer {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
+ grid-gap: 0.7rem;
+}
+
+.AnimeEntry {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ background-color: #252525be;
+ padding: 0.5rem;
+ border-radius: 0.5rem;
+}
+
+.AnimeEntry p {
+ font-size: 18px;
+}
+
+.AnimeEntry img {
+ border-radius: 0.5rem;
+ margin-left: 0.2rem;
+}
+
+.SearchResultsContainer::-webkit-scrollbar {
+ height: 0.2rem;
+}
+
+.SearchResultsContainer::-webkit-scrollbar-track {
+ background-color: var(--softer-purple);
+}
+
+.SearchResultsContainer::-webkit-scrollbar-thumb {
+ background-color: var(--light-green);
+ border-radius: 1rem;
+}
+
+@media screen and (max-width: 768px) {
+ .SearchInputContainer {
+ width: 100%;
+ }
+
+ .SearchResultsContainer {
+ display: flex;
+ align-items: center;
+ overflow: auto;
+ }
+
+ .AnimeEntry p {
+ width: 50vw;
+ }
+} \ No newline at end of file
diff --git a/src/app/anime/top-airing/page.jsx b/src/app/anime/top-airing/page.jsx
deleted file mode 100644
index 0102371..0000000
--- a/src/app/anime/top-airing/page.jsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import styles from "./trending.module.css";
-import Image from "next/image";
-import Link from "next/link";
-import { preFetchAnimeInfo } from "../videoLinkfetcher";
-
-export default async function Trending() {
- const data = await test();
- preFetchAnimeInfo(data);
-
- return (
- <div className={styles.TrendingContainer}>
- <div className={styles.TrendingText}>
- <h1>Trending</h1>
- </div>
-
- <div className={styles.trending}>
- {data &&
- data.results.map((item, index) => (
- <Link
- key={index}
- href={`/anime/${item.id}`}
- style={{ textDecoration: "none", color: "white" }}
- >
- <div
- className={styles.trendingEntries}
- title={item.title}
- >
- <Image
- src={`https://sup-proxy.zephex0-f6c.workers.dev/api-content?url=${item.image}`}
- className={styles.trendingImage}
- width={167}
- height={267}
- alt="Drama"
- priority
- />
- <p>{item.title}</p>
- </div>
- </Link>
- ))}
- </div>
- </div>
- );
-}
-
-async function test() {
- const res = await fetch(
- "https://consumet-jade.vercel.app/anime/gogoanime/top-airing",
- { next: { revalidate: 21600 } }
- );
- const data = res.json();
- return data;
-}
diff --git a/src/app/anime/top-airing/trending.module.css b/src/app/anime/top-airing/trending.module.css
deleted file mode 100644
index cd1a672..0000000
--- a/src/app/anime/top-airing/trending.module.css
+++ /dev/null
@@ -1,79 +0,0 @@
-.TrendingText h1 {
- margin: 0px 0px 0.5rem 0.5rem;
- text-align: center;
- color: transparent;
- background: linear-gradient(90deg,
- var(--neon-green) 40%,
- var(--light-green) 60%,
- var(--neon-yellow) 80%,
- var(--soft-purple) 100%);
- background-size: 60% 50%;
- background-clip: text;
-}
-
-.trending {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
- grid-gap: 0.5rem;
- align-items: center;
- margin-left: 0.5rem;
-}
-
-.TrendingContainer:hover .trendingEntries {
- opacity: 0.5;
-}
-
-.TrendingContainer:hover .trendingEntries:hover {
- opacity: 1;
- transform: scale(1.02);
-}
-
-.trending::-webkit-scrollbar {
- height: 0px;
-}
-
-.trendingEntries {
- transition: transform 400ms ease;
- background-color: #1f1f1fbb;
- padding: 0.5rem;
- border-radius: 0.4rem;
- transition: opacity 400ms ease, transform 400ms ease;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
-}
-
-.trendingEntries p {
- text-align: center;
- width: auto;
- max-width: 160px;
- margin: 0.5rem 0 0 0;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
-}
-
-.trendingEntries img {
- border-radius: 0.4rem;
- box-shadow: 0 0 10px 5px rgba(18, 18, 18, 0.863);
-}
-
-@media screen and (max-width: 768px) {
- .TrendingText p {
- text-align: start;
- font-size: 26px;
- margin-bottom: 1rem;
- }
-
- .trending {
- display: flex;
- overflow-x: auto;
- overflow-y: hidden;
- margin-top: -8px;
- }
-
- .trendingEntries img {
- width: auto;
- }
-} \ No newline at end of file
diff --git a/src/app/anime/videoLinkfetcher.js b/src/app/anime/videoLinkfetcher.js
deleted file mode 100644
index a718534..0000000
--- a/src/app/anime/videoLinkfetcher.js
+++ /dev/null
@@ -1,46 +0,0 @@
-"use server";
-export async function fetch_video_link(id) {
- try {
- const res = await fetch(
- `https://consumet-jade.vercel.app/anime/gogoanime/watch/${id}`,
- { cache: "force-cache" }
- );
- const data = await res.json();
- let vidLink = data.sources[data.sources.length - 2].url;
- return vidLink;
- } catch (error) {
- console.log("Mehh Error", error);
- }
-}
-
-export async function preFetchAnimeLinks(data, n = 40) {
- const limit = Math.min(n, data.episodes.length);
-
- try {
- const fetchPromises = [];
- for (let i = 0; i < limit; i++) {
- const element = data.episodes[i];
- const link = `https://consumet-jade.vercel.app/anime/gogoanime/watch/${element.id}`;
- fetchPromises.push(fetch(link, { cache: "force-cache" }));
- }
-
- await Promise.all(fetchPromises);
- console.log("Video links pre-fetched successfully!");
- } catch (error) {
- console.error("Error occurred while pre-fetching video links:", error);
- }
-}
-
-export async function preFetchAnimeInfo(data) {
- try {
- const fetchPromises = data.results.map(async (element) => {
- const link = `https://anime-sensei-api.vercel.app/anime/gogoanime/info/${element.id}`;
- await fetch(link, { next: { revalidate: 86400 } });
- });
-
- await Promise.all(fetchPromises);
- console.log("Anime info pre-fetched successfully!");
- } catch (error) {
- console.error("Error occurred while pre-fetching anime info: ", error);
- }
-}
diff --git a/src/app/globals.css b/src/app/globals.css
index db28734..233d7d1 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -7,6 +7,20 @@
--pastel-red: #ff6868;
--soft-purple: #beadfa;
--softer-purple: #d0bfff;
+
+
+ --nord-red: #BF616A;
+ --nord-green: #A3BE8C;
+ --nord-yellow: #EBCB8B;
+ --nord-orange: #D08770;
+ --nord-pink: #B48EAD;
+
+ --nord-bg-darkest: #2E3440;
+ --nord-bg-darker: #3B4252;
+ --nord-bg-dark: #434C5E;
+ --nord-bg-darklight: #4C566A;
+
+ --light-sky: #CAF4FF;
}
body {
@@ -17,4 +31,4 @@ body {
body::-webkit-scrollbar {
width: 0;
-}
+} \ No newline at end of file
diff --git a/src/app/movies/components/video_player.jsx b/src/app/movies/components/video_player.jsx
index 86ff8e5..86b6748 100644
--- a/src/app/movies/components/video_player.jsx
+++ b/src/app/movies/components/video_player.jsx
@@ -7,7 +7,7 @@ export default function VIDEO_PLAYER({ id: id }) {
const [frame, setFrame] = useState(null);
useEffect(() => {
- make_player(`https://vidsrc.pro/embed/movie/${id}`);
+ make_player(`https://vidsrc.icu/embed/movie/${id}`);
}, []);
function make_player(url) {
diff --git a/src/app/movies/styles/info.module.css b/src/app/movies/styles/info.module.css
index a0da8d9..b848f7f 100644
--- a/src/app/movies/styles/info.module.css
+++ b/src/app/movies/styles/info.module.css
@@ -53,6 +53,15 @@
overflow: auto;
}
+.MovieDescription::-webkit-scrollbar {
+ width: 5px;
+}
+
+.MovieDescription::-webkit-scrollbar-thumb {
+ background-color: var(--nord-yellow);
+ border-radius: 1rem;
+}
+
.OtherInfo {
display: flex;
flex-direction: column;
diff --git a/utils/anime_urls.js b/utils/anime_urls.js
new file mode 100644
index 0000000..e7a0457
--- /dev/null
+++ b/utils/anime_urls.js
@@ -0,0 +1,10 @@
+import { PROXY } from "./movie_urls";
+
+// Using gogoanime
+export const base_url = `${PROXY}https://consumet-jade.vercel.app/anime/gogoanime`;
+export const popular_url = `${base_url}/popular`;
+export const top_airing_url = `${base_url}/top-airing`;
+export const recent_epsiodes_url = `${base_url}/recent-episodes`;
+export const search_url = (title) => `${base_url}/${title}`;
+export const info_url = (id) => `${base_url}/info/${id}`;
+export const watch_url = (episodeId) => `${base_url}/watch/${episodeId}`;
diff --git a/utils/movie_urls.js b/utils/movie_urls.js
index eb512aa..dd39bb6 100644
--- a/utils/movie_urls.js
+++ b/utils/movie_urls.js
@@ -1,5 +1,5 @@
const API_KEY = "171fe27dbfecc58e2a18fbced644cda9";
-const PROXY = "https://sup-proxy.zephex0-f6c.workers.dev/api-json?url=";
+export const PROXY = "https://sup-proxy.zephex0-f6c.workers.dev/api-json?url=";
// MOVIES
export const TRENDING = `${PROXY}https://api.themoviedb.org/3/trending/movie/day?api_key=${API_KEY}`;