From 4c4f8214637ac8d19e16f71d20542982a5eedad7 Mon Sep 17 00:00:00 2001 From: real-zephex Date: Sat, 11 May 2024 01:33:00 +0530 Subject: UI changes, Logic change and complete re-write for anime page --- src/app/anime/components/cacher.js | 29 ++++++ src/app/anime/components/episode_buttons.jsx | 131 +++++++++++++++++++++++++++ src/app/anime/components/popularAnimes.jsx | 51 +++++++++++ src/app/anime/components/recentEpisodes.jsx | 54 +++++++++++ src/app/anime/components/search.jsx | 49 ++++++++++ src/app/anime/components/search_results.jsx | 42 +++++++++ src/app/anime/components/topAiring.jsx | 54 +++++++++++ 7 files changed, 410 insertions(+) create mode 100644 src/app/anime/components/cacher.js create mode 100644 src/app/anime/components/episode_buttons.jsx create mode 100644 src/app/anime/components/popularAnimes.jsx create mode 100644 src/app/anime/components/recentEpisodes.jsx create mode 100644 src/app/anime/components/search.jsx create mode 100644 src/app/anime/components/search_results.jsx create mode 100644 src/app/anime/components/topAiring.jsx (limited to 'src/app/anime/components') diff --git a/src/app/anime/components/cacher.js b/src/app/anime/components/cacher.js new file mode 100644 index 0000000..acc780a --- /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.episodes.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..f76704f --- /dev/null +++ b/src/app/anime/components/episode_buttons.jsx @@ -0,0 +1,131 @@ +"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"; + +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); + + async function getVideoURL(epID) { + setVideoLoading(true); + const data = await video_url(epID); + setVideoLink(data.sources[data.sources.length - 2].url); + setVideoLoading(false); + } + + function createButtonGroups(start, end) { + return ( +
+ {data.episodes && + data.episodes.slice(start, end).map((item, index) => ( + + ))} +
+ ); + } + + 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 + ) + ); + } + } + + return ( +
+ {data.episodes && ( + + )} + {buttonGroups} + {videoLoading && ( +

Loading...

+ )} + {videoLink && ( + + )} +
+ ); +}; + +function createGroups(array, size) { + const groups = []; + for (let i = 0; i < array.length; i += size) { + groups.push(array.slice(i, i + size)); + } + return groups; +} + +export default EpisodesButtons; 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 ( +
+
+

Popular Animes

+
+ {data && + data.results.map((item, index) => ( + +
+ Anime Poster Image +

{item.title}

+
+ + ))} +
+
+
+ ); +}; + +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 ( +
+
+

Recent Releases

+
+ {data && + data.results.map((item, index) => ( + +
+ Anime Poster Image +

{item.title}

+

+ Episode: {item.episodeNumber} +

+
+ + ))} +
+
+
+ ); +}; + +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..7bb22ba --- /dev/null +++ b/src/app/anime/components/search.jsx @@ -0,0 +1,49 @@ +"use client"; + +import { FaSearch } from "react-icons/fa"; +import { useState } from "react"; + +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 ( +
+
+
+ + { + 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); + } + }} + > +
+
+ {searchResults} +
+ ); +}; + +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 ( +
+ {data && + data.results.map((item, index) => ( + +
+

{item.title}

+ Anime Poster +
+ + ))} +
+ ); +}; + +export default SearchResults; 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 ( +
+
+

Top Airing Animes

+
+ {data && + data.results.map((item, index) => ( + +
+ Anime Poster Image +

{item.title}

+

+ Episode: {item.episodeNumber} +

+
+ + ))} +
+
+
+ ); +}; + +export default TopAiringAnimes; -- cgit v1.2.3 From 513eb617bc627bdc406eddd0f3124411017cbe50 Mon Sep 17 00:00:00 2001 From: real-zephex Date: Sat, 11 May 2024 01:42:48 +0530 Subject: adjustments to the cache system --- src/app/anime/components/cacher.js | 2 +- src/app/anime/components/episode_buttons.jsx | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'src/app/anime/components') diff --git a/src/app/anime/components/cacher.js b/src/app/anime/components/cacher.js index acc780a..d3008fa 100644 --- a/src/app/anime/components/cacher.js +++ b/src/app/anime/components/cacher.js @@ -17,7 +17,7 @@ export async function preFetchAnimeInfo(data) { export async function preFetchVideoLinks(data) { try { - const fetchPromises = data.episodes.map(async (element) => { + const fetchPromises = data.map(async (element) => { await fetch(watch_url(element.id), { next: { revalidate: 21600 } }); }); diff --git a/src/app/anime/components/episode_buttons.jsx b/src/app/anime/components/episode_buttons.jsx index f76704f..3336470 100644 --- a/src/app/anime/components/episode_buttons.jsx +++ b/src/app/anime/components/episode_buttons.jsx @@ -10,6 +10,7 @@ import { import styles from "../styles/buttons.module.css"; import { video_url } from "../data-fetch/request"; +import { preFetchVideoLinks } from "./cacher"; const EpisodesButtons = ({ data: data }) => { const [videoLink, setVideoLink] = useState(null); @@ -66,6 +67,12 @@ const EpisodesButtons = ({ data: data }) => { selectedGroup[selectedGroup.length - 1].number ) ); + preFetchVideoLinks( + data.episodes.slice( + selectedGroup[0].number - 1, + selectedGroup[selectedGroup.length - 1].number + ) + ); } } -- cgit v1.2.3 From 6c56263791c80676518532c9f91e0fb3d84fd079 Mon Sep 17 00:00:00 2001 From: real-zephex Date: Sat, 11 May 2024 02:01:28 +0530 Subject: adjustments to the cache system --- src/app/anime/components/episode_buttons.jsx | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src/app/anime/components') diff --git a/src/app/anime/components/episode_buttons.jsx b/src/app/anime/components/episode_buttons.jsx index 3336470..9d6e1ca 100644 --- a/src/app/anime/components/episode_buttons.jsx +++ b/src/app/anime/components/episode_buttons.jsx @@ -31,6 +31,16 @@ const EpisodesButtons = ({ data: data }) => { } function createButtonGroups(start, end) { + try { + document.getElementById("episode-button").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 (
{data.episodes && @@ -38,6 +48,7 @@ const EpisodesButtons = ({ data: data }) => {
+ + + {searchResults} -- cgit v1.2.3 From 755d67a651fa1be9d20606541b514a88938b76ce Mon Sep 17 00:00:00 2001 From: real-zephex Date: Sat, 11 May 2024 09:15:30 +0530 Subject: added anime history section --- src/app/anime/components/storeHistory.js | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/app/anime/components/storeHistory.js (limited to 'src/app/anime/components') diff --git a/src/app/anime/components/storeHistory.js b/src/app/anime/components/storeHistory.js new file mode 100644 index 0000000..bd41815 --- /dev/null +++ b/src/app/anime/components/storeHistory.js @@ -0,0 +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); +} -- cgit v1.2.3