diff options
| author | real-zephex <[email protected]> | 2024-05-11 19:14:29 +0530 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-05-11 19:14:29 +0530 |
| commit | e8875b008268c5873454b120bfb358cb4180093f (patch) | |
| tree | 2a0f8a15fb42f093e1d35f52eb3f7e14f1b0fbe5 | |
| parent | Merge pull request #24 from zephex-alt/master (diff) | |
| parent | added anime history section (diff) | |
| download | dramalama-e8875b008268c5873454b120bfb358cb4180093f.tar.xz dramalama-e8875b008268c5873454b120bfb358cb4180093f.zip | |
Merge pull request #25 from real-zephex/improvement-2
Improvement 2
36 files changed, 1012 insertions, 1133 deletions
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..f107313 100644 --- a/src/app/anime/[id]/page.jsx +++ b/src/app/anime/[id]/page.jsx @@ -1,68 +1,76 @@ -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}>
+ {data.title}
+ </p>
+ <p className={styles.animeDescription}>
+ <strong>Description: </strong>
+ {data.description}
+ </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}
+ </p>
+ <p>
+ <strong>Release year:</strong>{" "}
+ {data.releaseDate}
+ </p>
+ <p>
+ <strong>Status:</strong> {data.status}
+ </p>
+ <p>
+ <strong>Type:</strong> {data.type}
+ </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..f916217 --- /dev/null +++ b/src/app/anime/components/search.jsx @@ -0,0 +1,55 @@ +"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 handleSearchInput = async (title) => { + setSearchResults(await SearchResults(title)); + }; + + 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={"/"}> + <button className={styles.animeHistoryButton}> + History + </button> + </Link> + </section> + {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/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/history/continueWatching/cw.module.css b/src/app/anime/history/continueWatching/cw.module.css deleted file mode 100644 index cf79acc..0000000 --- a/src/app/anime/history/continueWatching/cw.module.css +++ /dev/null @@ -1,59 +0,0 @@ -.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%;
- }
-}
diff --git a/src/app/anime/history/continueWatching/page.jsx b/src/app/anime/history/continueWatching/page.jsx deleted file mode 100644 index ecb73fc..0000000 --- a/src/app/anime/history/continueWatching/page.jsx +++ /dev/null @@ -1,65 +0,0 @@ -"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;
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/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/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..d3564f7 --- /dev/null +++ b/src/app/anime/styles/search.module.css @@ -0,0 +1,90 @@ +.SearchBarContainer { + padding: 1rem 0 0.8rem 0; + display: flex; + align-items: center; +} + +.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 0 0.4rem; + padding: 0.5rem; + cursor: pointer; +} + +.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%; +} + +/* 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; +} + +.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) { + .SearchBarContainer { + 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/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}`; |