diff options
Diffstat (limited to 'src')
62 files changed, 1058 insertions, 1760 deletions
diff --git a/src/app/anime/[id]/page.jsx b/src/app/anime/[id]/page.jsx index d338417..f8cbe0c 100644 --- a/src/app/anime/[id]/page.jsx +++ b/src/app/anime/[id]/page.jsx @@ -1,80 +1,49 @@ -import Image from "next/image";
+import { Chip, Image } from "@nextui-org/react";
import { anime_info } from "../data-fetch/request";
-import styles from "../styles/info.module.css";
-import EpisodesButtons from "../components/episode_buttons";
+import DescriptionTabs from "../components/infoTabs";
-import { preFetchVideoLinks } from "../components/cacher";
+import EpisodesContainer from "../components/vidButtonContainer";
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 (
- <main className={styles.main}>
- <div>
- {data && (
- <section className={styles.AnimeInfo}>
- <div className={styles.AnimeHeroSection}>
- <Image
- src={data.image}
- width={180}
- height={280}
- alt="Anime Poster"
- priority
- />
- <div>
- <p className={styles.animeTitle} style={{color: "white"}}>
- {data.title || "Not Found"}
- </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}
- style={{ color: "#a3a3a3" }}
- >
- {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>
- </section>
- )}
+ <section className="pt-12 lg:w-9/12 m-auto">
+ <div className="flex items-center justify-center lg:justify-start md:justify-start">
+ <Image
+ isBlurred
+ width={190}
+ src={data.image.toString()}
+ alt="Anime Title Poster"
+ className="m-2"
+ />
+ <div className="mx-5">
+ <h4 className={`text-2xl`}>
+ <strong>{data.title}</strong>
+ </h4>
+ <div>
+ {data.genres &&
+ data.genres.map((item, index) => (
+ <Chip
+ key={index}
+ color="warning"
+ variant="faded"
+ className="mr-1 mb-1"
+ >
+ <p className="text-xs">{item}</p>
+ </Chip>
+ ))}
+ </div>
+ </div>
</div>
- <EpisodesButtons data={data} />
- </main>
+ <DescriptionTabs data={data} />
+ <EpisodesContainer data={data} />
+ <br />
+ <br />
+ <br />
+ </section>
);
};
diff --git a/src/app/anime/components/cacher.js b/src/app/anime/components/cacher.js index d3008fa..164dafd 100644 --- a/src/app/anime/components/cacher.js +++ b/src/app/anime/components/cacher.js @@ -1,11 +1,11 @@ "use server"; -import { info_url, watch_url } from "../../../../utils/anime_urls"; +import { anime_info } from "../data-fetch/request"; export async function preFetchAnimeInfo(data) { try { const fetchPromises = data.results.map(async (element) => { - await fetch(info_url(element.id), { next: { revalidate: 21600 } }); + await anime_info(element.id); }); await Promise.all(fetchPromises); diff --git a/src/app/anime/components/episode_buttons.jsx b/src/app/anime/components/episode_buttons.jsx index 013dee1..71f338f 100644 --- a/src/app/anime/components/episode_buttons.jsx +++ b/src/app/anime/components/episode_buttons.jsx @@ -202,4 +202,4 @@ function store_to_local(name, image, episode, id) { } } -export default EpisodesButtons;
\ No newline at end of file +export default EpisodesButtons; diff --git a/src/app/anime/components/infoTabs.jsx b/src/app/anime/components/infoTabs.jsx new file mode 100644 index 0000000..68a1da1 --- /dev/null +++ b/src/app/anime/components/infoTabs.jsx @@ -0,0 +1,46 @@ +"use client"; + +import { Tabs, Tab, Card, CardBody } from "@nextui-org/react"; + +import { lexend, atkinson } from "../../../../config/fonts"; + +export default function DescriptionTabs({ data: data }) { + return ( + <div className="flex w-full flex-col"> + <Tabs aria-label="Options" className={lexend.className}> + <Tab key="description" title="Description"> + <Card> + <CardBody className={atkinson.className}> + {data.description || "No description found"} + </CardBody> + </Card> + </Tab> + <Tab key="episodes" title="Details"> + <Card> + <CardBody className={atkinson.className}> + <h4> + <strong>Episodes</strong>:{" "} + <span>{data.totalEpisodes}</span> + </h4> + <h4> + <strong>Type</strong>: <span>{data.type}</span> + </h4> + <h4> + <strong>SUB/DUB</strong>:{" "} + <span>{data.subOrDub.toUpperCase()}</span> + </h4> + <h4> + <strong>Status</strong>:{" "} + <span>{data.status}</span> + </h4> + <h4> + <strong>Release Year</strong>:{" "} + <span>{data.releaseDate}</span> + </h4> + </CardBody> + </Card> + </Tab> + </Tabs> + </div> + ); +} diff --git a/src/app/anime/components/popularAnimes.jsx b/src/app/anime/components/popularAnimes.jsx deleted file mode 100644 index e62f70f..0000000 --- a/src/app/anime/components/popularAnimes.jsx +++ /dev/null @@ -1,53 +0,0 @@ -import Link from "next/link"; -import Image from "next/image"; -import { Lexend_Deca } from "next/font/google"; - -import styles from "../styles/pop_recent_top.module.css"; -import { popular } from "../data-fetch/request"; -import { preFetchAnimeInfo } from "./cacher"; - -const lexend = Lexend_Deca({ 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={lexend.className} - title={item.title} - > - <section className={styles.AnimeEntry}> - <Image - src={item.image} - width={180} - height={300} - alt="Anime Poster Image" - /> - <p className={styles.AnimeTitle}> - {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 deleted file mode 100644 index 9e7899f..0000000 --- a/src/app/anime/components/recentEpisodes.jsx +++ /dev/null @@ -1,54 +0,0 @@ -import Link from "next/link"; -import Image from "next/image"; -import { Lexend_Deca } from "next/font/google"; - -import styles from "../styles/pop_recent_top.module.css"; -import { recent } from "../data-fetch/request"; -import { preFetchAnimeInfo } from "./cacher"; - -const lexend = Lexend_Deca({ 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={lexend.className} - title={item.title} - > - <section className={styles.AnimeEntry}> - <Image - src={item.image} - width={180} - height={300} - alt="Anime Poster Image" - /> - <p className={styles.AnimeTitle}> - {item.title} - </p> - <p className={styles.AnimeReleasedEpisode}>{item.episodeNumber}</p> - </section> - </Link> - ))} - </div> - </section> - </main> - ); -}; - -export default RecentAnimes; diff --git a/src/app/anime/components/saveToLocalStorage.js b/src/app/anime/components/saveToLocalStorage.js new file mode 100644 index 0000000..313b157 --- /dev/null +++ b/src/app/anime/components/saveToLocalStorage.js @@ -0,0 +1,29 @@ +"use client"; + +import { storeLocal } from "./storeHistory"; + +export default 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 + ); + } +} diff --git a/src/app/anime/components/search.jsx b/src/app/anime/components/search.jsx index 0a780f8..0fe883b 100644 --- a/src/app/anime/components/search.jsx +++ b/src/app/anime/components/search.jsx @@ -2,37 +2,37 @@ import { FaSearch } from "react-icons/fa"; import { useState } from "react"; -import Link from "next/link"; +import { Input, Link, Button, Progress } from "@nextui-org/react"; -import styles from "../styles/search.module.css"; import SearchResults from "./search_results"; -const SearcBar = () => { +const SearchBar = () => { const [title, setTitle] = useState(""); const [searchResults, setSearchResults] = useState(null); const [loading, setLoading] = useState(false); const handleSearchInput = async (title) => { setSearchResults(null); - setLoading(true); + setLoading( + <Progress + size="sm" + isIndeterminate + aria-label="Loading..." + className="w-full mt-2 lg:w-1/2" + /> + ); setSearchResults(await SearchResults(title)); setLoading(false); }; return ( <main> - <section className={styles.SearchBarContainer}> - <div className={styles.SearchInputContainer}> - <FaSearch color="white" size={22} /> - <input - placeholder="Enter anime title" - name="Anime Title" + <section> + <div className="flex w-full md:flex-nowrap gap-2 lg:w-1/2"> + <Input type="text" - onChange={(event) => { - if (event.target.value.trim() != "") { - setTitle(event.target.value); - } - }} + label="Search for anime" + placeholder="Enter anime title here" autoComplete="off" onKeyDown={async (event) => { if ( @@ -43,22 +43,23 @@ const SearcBar = () => { await handleSearchInput(title); } }} - ></input> - <Link shallow href={"/anime/continueWatching"}> - <button className={styles.animeHistoryButton}> - History - </button> + onChange={(event) => { + if (event.target.value.trim() != "") { + setTitle(event.target.value); + } + }} + startContent={<FaSearch />} + /> + + <Link href={"/anime/continueWatching"}> + <Button color="primary">History</Button> </Link> </div> + {loading} </section> - {loading && ( - <p className={styles.SearchLoading}> - Please wait while we crunch up all the data..... - </p> - )} - {searchResults} + <div className="mt-2">{searchResults}</div> </main> ); }; -export default SearcBar; +export default SearchBar; diff --git a/src/app/anime/components/search_results.jsx b/src/app/anime/components/search_results.jsx index 051124d..3097a96 100644 --- a/src/app/anime/components/search_results.jsx +++ b/src/app/anime/components/search_results.jsx @@ -1,12 +1,9 @@ -import { Lexend_Deca } 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"; +import styles from "../../page.module.css"; -const lexend = Lexend_Deca({ subsets: ["latin"], weight: "400" }); +import { Card, CardHeader, CardBody, Image, Link } from "@nextui-org/react"; +import NextImage from "next/image"; const SearchResults = async (title) => { const data = await search_results(title); @@ -14,25 +11,39 @@ const SearchResults = async (title) => { preFetchAnimeInfo(data); return ( - <section className={styles.SearchResultsContainer}> + <section + className={`flex items-center overflow-auto pb-2 ${styles.ScrollBarAdjuster}`} + > {data && data.results.map((item, index) => ( <Link - shallow - href={`/anime/${item.id}`} key={index} - className={lexend.className} - style={{ color: "white", textDecoration: "none" }} + href={`/anime/${item.id}`} + aria-label="anime redirection links" + className="flex flex-col items-center mx-1 " > - <div className={styles.AnimeEntry}> - <Image - src={item.image} - width={180} - height={300} - alt="Anime Poster" - /> - <p>{item.title}</p> - </div> + <Card className="overflow-hidden" isPressable> + <CardBody> + <Image + as={NextImage} + isBlurred + alt="Anime Poster" + src={item.image} + width={190} + height={120} + shadow="lg" + className="h-64" + priority + /> + </CardBody> + <CardHeader> + <h4 + className={`antialiased text-small text-center uppercase w-44 overflow-hidden whitespace-nowrap text-ellipsis `} + > + {item.title} + </h4> + </CardHeader> + </Card> </Link> ))} </section> diff --git a/src/app/anime/components/topAiring.jsx b/src/app/anime/components/topAiring.jsx deleted file mode 100644 index 22e8c3b..0000000 --- a/src/app/anime/components/topAiring.jsx +++ /dev/null @@ -1,56 +0,0 @@ -import Link from "next/link"; -import Image from "next/image"; -import { Lexend_Deca } 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 lexend = Lexend_Deca({ 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={lexend.className} - title={item.title} - > - <section className={styles.AnimeEntry}> - <Image - src={item.image} - width={180} - height={300} - alt="Anime Poster Image" - /> - <p className={styles.AnimeTitle}> - {item.title} - </p> - <p className={styles.AnimeReleasedEpisode}> - {item.episodeNumber} - </p> - </section> - </Link> - ))} - </div> - </section> - </main> - ); -}; - -export default TopAiringAnimes; diff --git a/src/app/anime/components/vidButtonContainer.jsx b/src/app/anime/components/vidButtonContainer.jsx new file mode 100644 index 0000000..6b872d8 --- /dev/null +++ b/src/app/anime/components/vidButtonContainer.jsx @@ -0,0 +1,133 @@ +"use client"; + +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 { Select, SelectItem, Button, Skeleton } from "@nextui-org/react"; +import { useState, useEffect } from "react"; + +import { lexend } from "../../../../config/fonts"; +import { video_url } from "../data-fetch/request"; +import store_to_local from "./saveToLocalStorage"; + +const EpisodesContainer = ({ data: data }) => { + const [videoLink, setVideoLink] = useState(""); + const [buttonGroups, setButtonGroups] = useState(<></>); + const [videoLoading, setVideoLoading] = useState(<></>); + + useEffect(() => { + setButtonGroups(createButtonGroups(0, 50)); + }, []); + + const groups = createGroups(data.episodes, 50); + + function createButtonGroups(start, end) { + setButtonGroups(<></>); + return ( + <div className={`${lexend.className} text-center`}> + {data.episodes && + data.episodes.slice(start, end).map((item, index) => ( + <Button + radius="sm" + color="default" + key={index} + className="mr-2 mt-2" + size="sm" + onClick={async () => { + await changeVideoLink(item.id); + store_to_local( + data.title, + data.image, + item.number, + data.id + ); + }} + > + {item.number} + </Button> + ))} + </div> + ); + } + + function handleSelectChange(item) { + // console.log(item[item.length - 1].number); + const start_index = item[0].number; + const end_index = item[item.length - 1].number; + setButtonGroups(createButtonGroups(start_index - 1, end_index)); + } + + async function changeVideoLink(id) { + setVideoLink(""); + setVideoLoading( + <div className="w-full flex items-center gap-3"> + <div className="w-full flex flex-col gap-2"> + <Skeleton className="h-44 rounded-lg lg:h-96" /> + </div> + </div> + ); + const videoURL = await video_url(id); + setVideoLoading(<></>); + setVideoLink(videoURL.sources[videoURL.sources.length - 2].url); + } + + return ( + <main> + {videoLoading} + {videoLink && ( + <div> + <MediaPlayer + title={data.title} + src={videoLink} + aspectRatio="16/9" + load="eager" + playsInline + volume={0.8} + autoPlay + > + <MediaProvider /> + <DefaultVideoLayout icons={defaultLayoutIcons} /> + </MediaPlayer> + </div> + )} + {data.episodes && ( + <div className="flex w-full flex-wrap md:flex-nowrap gap-4 my-2"> + <Select + label="Select Episode Group" + className={`${lexend.className} max-w-xs`} + > + {groups && + groups.map((item, index) => ( + <SelectItem + key={index} + textValue={`${item[0].number} - ${ + item[item.length - 1].number + }`} + onClick={() => handleSelectChange(item)} + className={lexend.className} + > + {item[0].number} -{" "} + {item[item.length - 1].number} + </SelectItem> + ))} + </Select> + </div> + )} + {buttonGroups} + </main> + ); +}; + +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 EpisodesContainer; diff --git a/src/app/anime/continueWatching/page.jsx b/src/app/anime/continueWatching/page.jsx index eb28a2f..69d06de 100644 --- a/src/app/anime/continueWatching/page.jsx +++ b/src/app/anime/continueWatching/page.jsx @@ -2,7 +2,6 @@ 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 = () => { diff --git a/src/app/anime/loading.jsx b/src/app/anime/loading.jsx deleted file mode 100644 index 00ce39c..0000000 --- a/src/app/anime/loading.jsx +++ /dev/null @@ -1,11 +0,0 @@ -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 7143740..a2d5777 100644 --- a/src/app/anime/page.jsx +++ b/src/app/anime/page.jsx @@ -1,18 +1,101 @@ -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";
+import { Card, CardHeader, CardBody, Image, Link } from "@nextui-org/react";
+import NextImage from "next/image";
+import styles from "../page.module.css";
+
+import { top_airing, recent, popular } from "./data-fetch/request";
+import SearchBar from "./components/search";
+import { preFetchAnimeInfo } from "./components/cacher";
+
const AnimeHomepage = async () => {
+ const popular_data = await popular();
+ const recent_data = await recent();
+ const airing_data = await top_airing();
+
+ const dataToBeLoaded = [popular_data, recent_data, airing_data];
+
+ for (let item of dataToBeLoaded) {
+ preFetchAnimeInfo(item);
+ }
+
+ const header = (title) => (
+ <>
+ <p className={`antialiased font-bold text-sky-400 text-2xl my-1`}>
+ {title}
+ </p>
+ </>
+ );
+
+ const format = (data) => (
+ <>
+ {data &&
+ data.results.map((item, index) => (
+ <Link
+ key={index}
+ href={`/anime/${item.id}`}
+ aria-label="anime redirection links"
+ className="flex flex-col items-center mx-1 "
+ >
+ <Card className="overflow-visible " isPressable>
+ <CardBody>
+ <Image
+ as={NextImage}
+ isBlurred
+ alt="Anime Poster"
+ src={item.image}
+ width={270}
+ height={160}
+ className="h-60 overflow-hidden"
+ shadow="lg"
+ priority
+ />
+ </CardBody>
+ <CardHeader>
+ <h4
+ className={`antialiased text-small text-center uppercase w-44 overflow-hidden whitespace-nowrap text-ellipsis `}
+ >
+ {item.title}
+ </h4>
+ </CardHeader>
+ </Card>
+ </Link>
+ ))}
+ </>
+ );
+
return (
- <main className={styles.Main}>
- <SearcBar />
- <TopAiringAnimes />
+ <section className="pt-12">
+ <div className="mx-2">
+ <SearchBar />
+ </div>
+
+ <div className="mx-2">
+ {header("Popular Animes")}
+ <div
+ className={`flex overflow-auto overflow-y-hidden pb-3 ${styles.ScrollBarAdjuster}`}
+ >
+ {format(popular_data)}
+ </div>
+ </div>
+
+ <div className="mx-2">
+ {header("Recent Animes")}
+ <div
+ className={`flex overflow-auto overflow-y-hidden pb-3 ${styles.ScrollBarAdjuster}`}
+ >
+ {format(recent_data)}
+ </div>
+ </div>
+ <div className="mx-2">
+ {header("Top Airing Animes")}
+ <div
+ className={`flex overflow-x-auto overflow-y-hidden pb-3 ${styles.ScrollBarAdjuster}`}
+ >
+ {format(airing_data)}
+ </div>
+ </div>
<br />
- <RecentAnimes />
<br />
- <PopularAnimes />
- </main>
+ </section>
);
};
diff --git a/src/app/anime/styles/anime.module.css b/src/app/anime/styles/anime.module.css deleted file mode 100644 index e5d402a..0000000 --- a/src/app/anime/styles/anime.module.css +++ /dev/null @@ -1,4 +0,0 @@ -.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 deleted file mode 100644 index d0b8e78..0000000 --- a/src/app/anime/styles/buttons.module.css +++ /dev/null @@ -1,98 +0,0 @@ -.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/cw.module.css b/src/app/anime/styles/cw.module.css deleted file mode 100644 index cb579c7..0000000 --- a/src/app/anime/styles/cw.module.css +++ /dev/null @@ -1,59 +0,0 @@ -.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 deleted file mode 100644 index afdc08f..0000000 --- a/src/app/anime/styles/info.module.css +++ /dev/null @@ -1,69 +0,0 @@ -.main { - width: 50%; - margin: 60px auto; - color: white; -} - -.AnimeHeroSection { - display: flex; - align-items: center; -} - -.AnimeHeroSection strong { - color: #38bdf8; -} - -.AnimeHeroSection img { - /* width: auto; - height: auto; */ - padding: 0.5rem 0.7rem 0.5rem 0.7rem; -} - -.animeDescription { - max-height: 100px; - overflow: auto; -} - -.animeDescription::-webkit-scrollbar { - width: 0px; -} - -.AnimeHeroSection p { - margin: 0; - color: #a3a3a3; -} - -.animeTitle { - font-size: 26px; - text-transform: uppercase; -} - -@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 deleted file mode 100644 index f0d0606..0000000 --- a/src/app/anime/styles/loading.module.css +++ /dev/null @@ -1,12 +0,0 @@ -.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 deleted file mode 100644 index 253f60f..0000000 --- a/src/app/anime/styles/pop_recent_top.module.css +++ /dev/null @@ -1,95 +0,0 @@ -.AnimeHeaderText { - color: white; - margin: 0.4rem 0 0 0; - text-transform: uppercase; - font-size: 30px; -} - -.AnimeContainer { - margin: 0.2rem 0 0 0; - display: grid; - grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); - grid-gap: 0.5rem; -} - -/* .AnimeEntry { - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - text-align: center; - padding: 0.8rem; - border-radius: 0.4rem; - background-color: #1a1a1a; - transition: opacity 200ms ease; -} */ - -.AnimeEntry { - position: relative; - display: inline-block; - overflow: hidden; - border-radius: 0.5rem; - transition: opacity 200ms ease; - -} - -.AnimeContainer:hover .AnimeEntry { - opacity: 0.5; -} - -.AnimeContainer:hover .AnimeEntry:hover { - opacity: 1; -} - -.AnimeEntry img { - display: block; - transition: transform 200ms ease; -} - -.AnimeEntry img:hover { - transform: translateY(-5px) scale(1.04); -} - - -.AnimeTitle { - position: absolute; - bottom: 0; - /* Adjust the value as needed */ - left: 50%; - transform: translateX(-50%); - margin: 0; - color: white; - padding: 5px; - text-align: center; - background-color: #121212ab; - backdrop-filter: blur(10px); - text-transform: uppercase; - width: 100%; - max-width: 170px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.AnimeReleasedEpisode { - position: absolute; - top: 0; - margin: 0.2rem; - padding: 0.5rem; - background-color: #121212b2; - border-radius: 0.4rem; - backdrop-filter: blur(10px); -} - -.AnimeContainer::-webkit-scrollbar { - height: 0rem; -} - - -@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 deleted file mode 100644 index abed86c..0000000 --- a/src/app/anime/styles/search.module.css +++ /dev/null @@ -1,116 +0,0 @@ -.SearchBarContainer { - padding: 1rem 0 0.8rem 0; - display: flex; - align-items: center; - width: 100%; -} - -.SearchInputContainer { - display: flex; - align-items: center; - background-color: #1f1f1f; - padding: 0.4rem; - 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.6rem; - cursor: pointer; - border-radius: 0.5rem; -} - -.SearchInputContainer input { - background-color: transparent; - border: none; - outline: none; - color: white; - margin-left: 0.5rem; - font-size: 20px; - font-family: "Atkinson Hyperlegible", serif; - width: 100%; -} - -.SearchLoading { - color: white; - text-align: center; -} - -/* Search Results */ - -.SearchResultsContainer { - display: flex; - align-items: center; - overflow-x: auto; - padding-bottom: 0.5rem; -} - -.AnimeEntry { - position: relative; - display: inline-block; - overflow: hidden; - border-radius: 0.5rem; - transition: opacity 200ms ease; - margin: 0.4rem; -} - -.SearchResultsContainer:hover .AnimeEntry { - opacity: 0.5; -} - -.SearchResultsContainer:hover .AnimeEntry:hover { - opacity: 1; -} - -.AnimeEntry p { - position: absolute; - bottom: 0; - /* Adjust the value as needed */ - left: 50%; - transform: translateX(-50%); - margin: 0; - color: white; - padding: 5px; - text-align: center; - background-color: #121212ab; - backdrop-filter: blur(10px); - text-transform: uppercase; - width: 100%; - max-width: 170px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.AnimeEntry img { - display: block; - transition: transform 200ms ease; -} - -.AnimeEntry img:hover { - transform: translateY(-5px) scale(1.04); -} - - -.SearchResultsContainer::-webkit-scrollbar { - height: 0.5rem; -} - -.SearchResultsContainer::-webkit-scrollbar-thumb { - background: rgb(83, 83, 83); - border-radius: 1rem; -} - -@media screen and (max-width: 768px) { - .SearchInputContainer { - width: 100%; - } - - -}
\ No newline at end of file diff --git a/src/app/components/header/header.jsx b/src/app/components/header/header.jsx index 9e5fc69..3c7db2e 100644 --- a/src/app/components/header/header.jsx +++ b/src/app/components/header/header.jsx @@ -1,26 +1,43 @@ import Link from "next/link";
import styles from "../../page.module.css";
+import { ThemeSwitcher } from "../themeSwitcher";
export default async function Header() {
return (
- <main className={styles.main}>
- <div className={styles.header}>
- <div className={styles.left}>
- <Link
- href={"/"}
- style={{ textDecoration: "none", color: "white" }}
- >
- <p>Dramalama</p>
- </Link>
- </div>
- <div className={styles.right}>
- <Link href="/anime">Anime</Link>
- <Link href="/kdrama">Kdrama</Link>
- <Link href="/manga">Manga</Link>
- <Link href="/movies">Movies</Link>
- <Link href="/web-series">Series</Link>
- </div>
+ <div className="fixed top-0 w-full flex items-center justify-between z-50 dark:bg-black bg-white">
+ <h4 className="text-teal-400 text-2xl p-2">
+ <Link href={"/"}>Dramalama</Link>
+ </h4>
+ <div
+ className={`mx-2 flex items-center overflow-auto ${styles.ScrollBarAdjuster}`}
+ >
+ <ThemeSwitcher />
+
+ <Link href={"/anime"} className="mx-2 hover:text-sky-400">
+ <p>Anime</p>
+ </Link>
+ <Link href={"/kdrama"} className="mx-2 hover:text-sky-400">
+ <p>Kdrama</p>
+ </Link>
+ <Link
+ href={"/components/workInProgress/"}
+ className="mx-2 hover:text-sky-400"
+ >
+ <p>Manga</p>
+ </Link>
+ <Link
+ href={"/components/workInProgress/"}
+ className="mx-2 hover:text-sky-400"
+ >
+ <p>Series</p>
+ </Link>
+ <Link
+ href={"/components/workInProgress/"}
+ className="mx-2 hover:text-sky-400"
+ >
+ <p>Movies</p>
+ </Link>
</div>
- </main>
+ </div>
);
}
diff --git a/src/app/components/moonIcon.jsx b/src/app/components/moonIcon.jsx new file mode 100644 index 0000000..6f20a38 --- /dev/null +++ b/src/app/components/moonIcon.jsx @@ -0,0 +1,17 @@ +import React from "react"; +export const MoonIcon = (props) => ( + <svg + aria-hidden="true" + focusable="false" + height="1em" + role="presentation" + viewBox="0 0 24 24" + width="1em" + {...props} + > + <path + d="M21.53 15.93c-.16-.27-.61-.69-1.73-.49a8.46 8.46 0 01-1.88.13 8.409 8.409 0 01-5.91-2.82 8.068 8.068 0 01-1.44-8.66c.44-1.01.13-1.54-.09-1.76s-.77-.55-1.83-.11a10.318 10.318 0 00-6.32 10.21 10.475 10.475 0 007.04 8.99 10 10 0 002.89.55c.16.01.32.02.48.02a10.5 10.5 0 008.47-4.27c.67-.93.49-1.519.32-1.79z" + fill="currentColor" + /> + </svg> +); diff --git a/src/app/components/sunIcon.jsx b/src/app/components/sunIcon.jsx new file mode 100644 index 0000000..cc47cfa --- /dev/null +++ b/src/app/components/sunIcon.jsx @@ -0,0 +1,17 @@ +import React from "react"; +export const SunIcon = (props) => ( + <svg + aria-hidden="true" + focusable="false" + height="1em" + role="presentation" + viewBox="0 0 24 24" + width="1em" + {...props} + > + <g fill="currentColor"> + <path d="M19 12a7 7 0 11-7-7 7 7 0 017 7z" /> + <path d="M12 22.96a.969.969 0 01-1-.96v-.08a1 1 0 012 0 1.038 1.038 0 01-1 1.04zm7.14-2.82a1.024 1.024 0 01-.71-.29l-.13-.13a1 1 0 011.41-1.41l.13.13a1 1 0 010 1.41.984.984 0 01-.7.29zm-14.28 0a1.024 1.024 0 01-.71-.29 1 1 0 010-1.41l.13-.13a1 1 0 011.41 1.41l-.13.13a1 1 0 01-.7.29zM22 13h-.08a1 1 0 010-2 1.038 1.038 0 011.04 1 .969.969 0 01-.96 1zM2.08 13H2a1 1 0 010-2 1.038 1.038 0 011.04 1 .969.969 0 01-.96 1zm16.93-7.01a1.024 1.024 0 01-.71-.29 1 1 0 010-1.41l.13-.13a1 1 0 011.41 1.41l-.13.13a.984.984 0 01-.7.29zm-14.02 0a1.024 1.024 0 01-.71-.29l-.13-.14a1 1 0 011.41-1.41l.13.13a1 1 0 010 1.41.97.97 0 01-.7.3zM12 3.04a.969.969 0 01-1-.96V2a1 1 0 012 0 1.038 1.038 0 01-1 1.04z" /> + </g> + </svg> +); diff --git a/src/app/components/themeSwitcher.jsx b/src/app/components/themeSwitcher.jsx new file mode 100644 index 0000000..995dbbf --- /dev/null +++ b/src/app/components/themeSwitcher.jsx @@ -0,0 +1,43 @@ +// app/components/ThemeSwitcher.tsx +"use client"; + +import { useTheme } from "next-themes"; +import { useEffect, useState } from "react"; + +import React from "react"; +import { Switch } from "@nextui-org/react"; +import { SunIcon } from "./sunIcon"; +import { MoonIcon } from "./moonIcon"; + +export function ThemeSwitcher() { + const [mounted, setMounted] = useState(false); + const { theme, setTheme } = useTheme(); + + useEffect(() => { + setMounted(true); + }, []); + + if (!mounted) return null; + + return ( + <Switch + defaultSelected + size="sm" + color="secondary" + thumbIcon={({ isSelected, className }) => + isSelected ? ( + <SunIcon className={className} /> + ) : ( + <MoonIcon className={className} /> + ) + } + onClick={() => { + if (theme === "light") { + setTheme("dark"); + } else { + setTheme("light"); + } + }} + ></Switch> + ); +} diff --git a/src/app/components/workInProgress/page.jsx b/src/app/components/workInProgress/page.jsx new file mode 100644 index 0000000..0af0a98 --- /dev/null +++ b/src/app/components/workInProgress/page.jsx @@ -0,0 +1,12 @@ +const WorkInProgress = async () => { + return ( + <main className="h-screen w-screen flex flex-col items-center justify-center"> + <p className="text-sky-400 uppercase"> + This section is undergoing a complete overhaul. Sorry for the + inconvenience. + </p> + </main> + ); +}; + +export default WorkInProgress; diff --git a/src/app/error.jsx b/src/app/error.jsx index 7549925..348050b 100644 --- a/src/app/error.jsx +++ b/src/app/error.jsx @@ -1,7 +1,7 @@ "use client"; // Error components must be Client Components
import { useEffect } from "react";
-import styles from "./globals.module.css";
+import { Button } from "@nextui-org/react";
export default function Error({ error, reset }) {
useEffect(() => {
@@ -9,16 +9,11 @@ export default function Error({ error, reset }) { }, [error]);
return (
- <div className={styles.ErrorContainer}>
+ <div className="w-screen h-screen flex items-center flex-col justify-center">
<p>Something went wrong!</p>
- <button
- onClick={
- // Attempt to recover by trying to re-render the segment
- () => reset()
- }
- >
+ <Button color="primary" onClick={() => reset()}>
Try again
- </button>
+ </Button>
</div>
);
}
diff --git a/src/app/globals.css b/src/app/globals.css index bcf2a8b..c414701 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,3 +1,7 @@ +@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
@import url("https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible:ital,wght@0,400;0,700;1,400;1,700&family=Lexend+Deca&family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap");
:root {
@@ -23,12 +27,6 @@ --light-sky: #CAF4FF;
}
-body {
- margin: 0;
- padding: 0;
- background-color: #121212;
-}
-
body::-webkit-scrollbar {
width: 0;
}
\ No newline at end of file diff --git a/src/app/kdrama/[id]/buttons.jsx b/src/app/kdrama/[id]/buttons.jsx deleted file mode 100644 index 66292e7..0000000 --- a/src/app/kdrama/[id]/buttons.jsx +++ /dev/null @@ -1,86 +0,0 @@ -"use client";
-import styles from "../styles/info.module.css";
-import getVideoLink from "../components/videoLink";
-import React, { useState } 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";
-
-export default function EpisodesButtons({ data: episodeData, id: dramaId }) {
- const [videoLink, setVideoLink] = useState(null);
-
- async function test(a, b) {
- let link = await getVideoLink(a, b);
- setVideoLink(link);
- }
-
- return (
- <div>
- <div className={styles.EpisodesContainer}>
- <h2>Episodes</h2>
- <div className={styles.EpisodeButtons}>
- {episodeData && episodeData.length > 0 ? (
- episodeData.map((item, index) => (
- <button
- title={item.title}
- key={index}
- onClick={(event) => {
- test(item.id, dramaId, item.title);
- event.currentTarget.style.backgroundColor =
- "var(--soft-purple)";
- }}
- >
- <p>{item.title}</p>
- </button>
- ))
- ) : (
- <p style={{ color: "white" }}>
- No episodes are available at the moment but they
- will be made available soon. Thank you for your
- patience.
- </p>
- )}
- </div>
- </div>
-
- {videoLink && (
- <div
- className={styles.videoPopUp}
- id="popup"
- onKeyDown={(event) => {
- if (event.code === "Escape") {
- setVideoLink("");
- }
- }}
- >
- <div className={styles.video}>
- <MediaPlayer
- title="dramaPlayer"
- src={videoLink}
- 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>
- )}
- </div>
- );
-}
diff --git a/src/app/kdrama/[id]/page.jsx b/src/app/kdrama/[id]/page.jsx index 9cb3cd8..d94810e 100644 --- a/src/app/kdrama/[id]/page.jsx +++ b/src/app/kdrama/[id]/page.jsx @@ -1,66 +1,46 @@ -import styles from "../styles/info.module.css";
-import Image from "next/image";
-import EpisodesButtons from "./buttons";
-import { PreFetchVideoLinks } from "../components/cacher";
+import { Chip, Image } from "@nextui-org/react";
+import DescriptionTabs from "../components/infoTabs";
+import { dramaInfo } from "../components/requests";
+import EpisodesContainer from "../components/episodesContainer";
export default async function DramaInfo({ params }) {
const id = decodeURIComponent(params.id);
- const info = await getDramaInfo(id);
-
- PreFetchVideoLinks(info.episodes, id);
+ const data = await dramaInfo(id);
return (
- <div className={styles.Main}>
- {info && (
- <div className={styles.DramaInfoContainer}>
- <div className={styles.TitleContainer}>
- <p>{info.title}</p>
- <Image
- src={`https://sup-proxy.zephex0-f6c.workers.dev/api-content?url=${info.image}`}
- width={175}
- height={256}
- alt="Drama Poster"
- priority
- />
- </div>
-
- {/* Drama description */}
- <div className={styles.DramaDescription}>
- <h2>Description</h2>
- <p>{info.description}</p>
- </div>
-
- {/* Genres */}
- <div className={styles.DramaGenre}>
- <span className={styles.genreMain}>Genres: </span>
- {info.genres &&
- info.genres.map((item, index) => (
- <p key={index}>{item}</p>
- ))}
- </div>
-
- {/* Other names */}
- <div className={styles.DramaGenre}>
- <span className={styles.genreMain}>AKA: </span>
- {info.otherNames &&
- info.otherNames.map((item, index) => (
- <p key={index}>{item}</p>
+ <section className="pt-12 lg:w-9/12 m-auto">
+ <div className="flex items-center justify-center lg:justify-start md:justify-start">
+ <Image
+ isBlurred
+ width={190}
+ src={data.image.toString()}
+ alt="Anime Title Poster"
+ className="m-2"
+ />
+ <div className="mx-5">
+ <h4 className={`text-2xl`}>
+ <strong>{data.title}</strong>
+ </h4>
+ <div className="mt-1">
+ {data.genres &&
+ data.genres.map((item, index) => (
+ <Chip
+ key={index}
+ color="warning"
+ variant="faded"
+ className="mr-1 mb-1"
+ >
+ <p className="text-xs">{item}</p>
+ </Chip>
))}
</div>
-
- {/* Episodes Buttons */}
- <EpisodesButtons data={info.episodes} id={id} />
</div>
- )}
- </div>
- );
-}
-
-async function getDramaInfo(id) {
- const res = await fetch(
- `https://consumet-jade.vercel.app/movies/dramacool/info?id=${id}`,
- { next: { revalidate: 21600 } }
+ </div>
+ <DescriptionTabs data={data} />
+ <EpisodesContainer data={data} />
+ <br />
+ <br />
+ <br />
+ </section>
);
- const data = await res.json();
- return data;
}
diff --git a/src/app/kdrama/components/cacher.js b/src/app/kdrama/components/cacher.js index 860cdca..fdfa272 100644 --- a/src/app/kdrama/components/cacher.js +++ b/src/app/kdrama/components/cacher.js @@ -1,21 +1,6 @@ -// This function pre-fetches all the video links for a drama in the background
"use server";
-export async function PreFetchVideoLinks(data, dramaId) {
- try {
- const fetchPromises = data.map(async (element) => {
- const link = `https://consumet-jade.vercel.app/movies/dramacool/watch?episodeId=${element.id}&mediaId=${dramaId}`;
- await 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) {
+export async function PreFetchKdramaInfo(data) {
try {
const fetchPromises = data.results.map(async (element) => {
const link = `https://consumet-jade.vercel.app/movies/dramacool/info?id=${element.id}`;
diff --git a/src/app/kdrama/components/episodesContainer.jsx b/src/app/kdrama/components/episodesContainer.jsx new file mode 100644 index 0000000..984ece5 --- /dev/null +++ b/src/app/kdrama/components/episodesContainer.jsx @@ -0,0 +1,83 @@ +"use client"; + +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 { Select, SelectItem, Button, Skeleton } from "@nextui-org/react"; +import { useState, useEffect } from "react"; + +import { lexend } from "../../../../config/fonts"; +import { videoLink } from "./requests"; + +const EpisodesContainer = ({ data: data }) => { + const [videolink, setVideoLink] = useState(""); + const [loading, setLoading] = useState(<></>); + + async function handleSelectChange(episodeId) { + setVideoLink(""); + setLoading( + <div className="w-full flex items-center gap-3"> + <div className="w-full flex flex-col gap-2"> + <Skeleton className="h-44 rounded-lg lg:h-96" /> + </div> + </div> + ); + const videoURL = await videoLink(episodeId, data.id); + setLoading(<></>); + setVideoLink(videoURL); + } + + return ( + <section> + <div className="flex w-full flex-wrap md:flex-nowrap gap-4 my-2"> + <Select + label="Select Episode" + className={`${lexend.className} max-w-xs`} + > + {data.episodes && data.episodes.length > 0 ? ( + data.episodes.map((item, index) => ( + <SelectItem + key={index} + textValue={item.episode} + onClick={async () => + await handleSelectChange(item.id) + } + className={lexend.className} + > + {item.episode} + </SelectItem> + )) + ) : ( + <SelectItem disabled className={lexend.className}> + No episodes available right now + </SelectItem> + )} + </Select> + </div> + + {loading} + {videolink && ( + <div> + <MediaPlayer + title={data.title} + src={videolink} + aspectRatio="16/9" + load="eager" + playsInline + volume={0.8} + autoPlay + > + <MediaProvider /> + <DefaultVideoLayout icons={defaultLayoutIcons} /> + </MediaPlayer> + </div> + )} + </section> + ); +}; + +export default EpisodesContainer; diff --git a/src/app/kdrama/components/infoTabs.jsx b/src/app/kdrama/components/infoTabs.jsx new file mode 100644 index 0000000..54c05ba --- /dev/null +++ b/src/app/kdrama/components/infoTabs.jsx @@ -0,0 +1,51 @@ +"use client"; + +import { Tabs, Tab, Card, CardBody } from "@nextui-org/react"; + +import { lexend, atkinson } from "../../../../config/fonts"; + +export default function DescriptionTabs({ data: data }) { + return ( + <div className="flex w-full flex-col"> + <Tabs aria-label="Options" className={lexend.className}> + <Tab key="description" title="Description"> + <Card> + <CardBody className={atkinson.className}> + {data.description || "No description found"} + </CardBody> + </Card> + </Tab> + <Tab key="episodes" title="Details"> + <Card> + <CardBody className={atkinson.className}> + <h4> + <strong>Episodes</strong>:{" "} + <span>{data.episodes.length}</span> + </h4> + <h4> + <strong>Duration</strong>:{" "} + <span>{data.duration || "not found"}</span> + </h4> + <h4> + <strong>Release Year</strong>:{" "} + <span>{data.releaseDate}</span> + </h4> + <h4> + <strong>Other Names</strong>:{" "} + {data.otherNames && + data.otherNames.map((item, index) => ( + <span key={index}> + {item} + {index < + data.otherNames.length - 1 && + ", "} + </span> + ))} + </h4> + </CardBody> + </Card> + </Tab> + </Tabs> + </div> + ); +} diff --git a/src/app/kdrama/components/popular.jsx b/src/app/kdrama/components/popular.jsx deleted file mode 100644 index 21d8cc3..0000000 --- a/src/app/kdrama/components/popular.jsx +++ /dev/null @@ -1,47 +0,0 @@ -import styles from "../styles/popular.module.css";
-import Image from "next/image";
-import Link from "next/link";
-import { PreFetchAnimeInfo } from "./cacher";
-
-export default async function PopularDramas() {
- const popular = await getPopular();
- PreFetchAnimeInfo(popular);
-
- return (
- <div className={styles.Main}>
- <h2 className={styles.popDramasText}>Trending Dramas</h2>
-
- <div className={styles.AnimeContainer}>
- {popular &&
- popular.results.slice(0, 24).map((item, index) => (
- <Link
- href={`/kdrama/${encodeURIComponent(item.id)}`}
- key={index}
- style={{ textDecoration: "none" }}
- >
- <div
- className={styles.AnimeEntry}
- title={item.title}
- >
- <Image
- src={`https://sup-proxy.zephex0-f6c.workers.dev/api-content?url=${item.image}`}
- width={167}
- height={267}
- alt="Drama Poster"
- />
- <p>{item.title}</p>
- </div>
- </Link>
- ))}
- </div>
- </div>
- );
-}
-
-async function getPopular() {
- const res = await fetch("https://dramacool-scraper.vercel.app/popular", {
- next: { revalidate: 21600 },
- });
- const data = await res.json();
- return data;
-}
diff --git a/src/app/kdrama/components/recent.jsx b/src/app/kdrama/components/recent.jsx deleted file mode 100644 index 2b883d6..0000000 --- a/src/app/kdrama/components/recent.jsx +++ /dev/null @@ -1,46 +0,0 @@ -import styles from "../styles/popular.module.css";
-import Image from "next/image";
-import Link from "next/link";
-import { PreFetchAnimeInfo } from "./cacher";
-
-export default async function RecentDramas() {
- const popular = await getPopular();
- PreFetchAnimeInfo(popular);
- return (
- <div className={styles.Main}>
- <h2 className={styles.popDramasText}>Recent Releases</h2>
-
- <div className={styles.AnimeContainer}>
- {popular &&
- popular.results.slice(0, 24).map((item, index) => (
- <Link
- href={`/kdrama/${encodeURIComponent(item.id)}`}
- key={index}
- style={{ textDecoration: "none" }}
- >
- <div
- className={styles.AnimeEntry}
- title={item.title}
- >
- <Image
- src={`https://sup-proxy.zephex0-f6c.workers.dev/api-content?url=${item.image}`}
- width={167}
- height={267}
- alt="Drama Poster"
- />
- <p>{item.title}</p>
- </div>
- </Link>
- ))}
- </div>
- </div>
- );
-}
-
-async function getPopular() {
- const res = await fetch("https://dramacool-scraper.vercel.app/recent", {
- next: { revalidate: 21600 },
- });
- const data = await res.json();
- return data;
-}
diff --git a/src/app/kdrama/components/requests.js b/src/app/kdrama/components/requests.js new file mode 100644 index 0000000..5609d79 --- /dev/null +++ b/src/app/kdrama/components/requests.js @@ -0,0 +1,44 @@ +"use server"; + +import { + popular_dramas_url, + recent_drama_url, + search_drama_url, + drama_info_url, + videoURL, +} from "../../../../utils/kdrama_urls"; + +export const DramaDataFetcher = async (type) => { + const options = { + popular: popular_dramas_url, + recent: recent_drama_url, + }; + const res = await fetch(options[type], { next: { revalidate: 21600 } }); + const data = await res.json(); + return data; +}; + +export const SearchedDramaData = async (title) => { + const res = await fetch(search_drama_url(title), { + next: { revalidate: 21600 }, + }); + const data = await res.json(); + return data; +}; + +export const dramaInfo = async (id) => { + const res = await fetch(drama_info_url(id), { + next: { revalidate: 21600 }, + }); + const data = await res.json(); + return data; +}; + +export const videoLink = async (epiId, mediaId) => { + const res = await fetch(videoURL(epiId, mediaId), { + next: { revalidate: 21600 }, + }); + const data = await res.json(); + const videoLink = data.sources[0].url; + return videoLink; +}; diff --git a/src/app/kdrama/components/search.jsx b/src/app/kdrama/components/search.jsx deleted file mode 100644 index f44e4bb..0000000 --- a/src/app/kdrama/components/search.jsx +++ /dev/null @@ -1,65 +0,0 @@ -"use client";
-
-import styles from "../styles/search.module.css";
-import { useState } from "react";
-import { FaSearch } from "react-icons/fa";
-import FetchSearchTitle from "./searchQuery";
-import Image from "next/image";
-import Link from "next/link";
-import { PreFetchAnimeInfo } from "./cacher";
-
-export default function DramaSearch() {
- const [title, setTitle] = useState("");
- const [infoTitle, setInfoTitle] = useState(null);
- const [loadingText, setLoadingText] = useState(null);
-
- const handleSearch = async (title) => {
- setLoadingText(true);
- const data = await FetchSearchTitle(title);
- PreFetchAnimeInfo(data);
- setLoadingText(false);
- setInfoTitle(data);
- };
-
- return (
- <div className={styles.SearchContainer}>
- <div className={styles.Search}>
- <FaSearch color="white" size={16} />
- <input
- placeholder="Search for drama"
- onChange={(event) => setTitle(event.target.value)}
- onKeyDown={async (e) => {
- if ((e.key === "Enter" || e.code === 13) && title) {
- await handleSearch(e.target.value);
- }
- }}
- ></input>
- </div>
-
- {loadingText && (
- <p className={styles.LoadingText}>Wait a moment...</p>
- )}
-
- <div className={styles.SearchResults}>
- {infoTitle &&
- infoTitle.results.map((item, index) => (
- <Link
- href={`/kdrama/${encodeURIComponent(item.id)}`}
- style={{ textDecoration: "none" }}
- key={index}
- >
- <div className={styles.SearchEntry}>
- <p>{item.title}</p>
- <Image
- src={`https://sup-proxy.zephex0-f6c.workers.dev/api-content?url=${item.image}`}
- width={140}
- height={210}
- alt="Drama Poster"
- />
- </div>
- </Link>
- ))}
- </div>
- </div>
- );
-}
diff --git a/src/app/kdrama/components/searchBar.jsx b/src/app/kdrama/components/searchBar.jsx new file mode 100644 index 0000000..5f5d89e --- /dev/null +++ b/src/app/kdrama/components/searchBar.jsx @@ -0,0 +1,57 @@ +"use client"; + +import React from "react"; +import { useState } from "react"; +import { Input, Progress } from "@nextui-org/react"; + +import { SearchedDramaData } from "./requests"; +import SearchedDataFormatter from "./searchFormatter"; +import { PreFetchKdramaInfo } from "./cacher"; + +export const Searchbar = () => { + const [loading, setLoading] = useState(<></>); + const [searchData, setSearchData] = useState(null); + const [searchTitle, setSearchTitle] = useState(""); + + async function handleSearchInput() { + setSearchData(null); + setLoading( + <Progress + size="sm" + isIndeterminate + aria-label="Loading..." + className="w-full" + /> + ); + const data = await SearchedDramaData(searchTitle); + PreFetchKdramaInfo(data); + const format = await SearchedDataFormatter(data); + setSearchData(format); + setLoading(<></>); + } + + return ( + <div> + <div className="flex w-full flex-wrap flex-col mt-2 md:flex-nowrap md:mx-2 gap-4 lg:w-1/2 lg:mx-2"> + <Input + type="text" + label="Search for k-dramas here" + placeholder="Enter k-drama title" + color="default" + onChange={(event) => { + if (event.target.value.trim() !== "") { + setSearchTitle(event.target.value); + } + }} + onKeyDown={async (event) => { + if (event.key === "Enter" || event.code === "Enter") { + await handleSearchInput(); + } + }} + /> + {loading} + </div> + <div className="w-full mt-2">{searchData}</div> + </div> + ); +}; diff --git a/src/app/kdrama/components/searchFormatter.jsx b/src/app/kdrama/components/searchFormatter.jsx new file mode 100644 index 0000000..bac2549 --- /dev/null +++ b/src/app/kdrama/components/searchFormatter.jsx @@ -0,0 +1,46 @@ +import { Card, CardHeader, CardBody, Image, Link } from "@nextui-org/react"; +import NextImage from "next/image"; + +import styles from "../../page.module.css"; + +const SearchedDataFormatter = async (data) => { + return ( + <section + className={`flex items-center overflow-auto pb-2 ${styles.ScrollBarAdjuster}`} + > + {data && + data.results.length > 0 && + data.results.map((item, index) => ( + <Link + key={index} + href={`/kdrama/${encodeURIComponent(item.id)}`} + aria-label="anime redirection links" + className="flex flex-col items-center mx-1" + > + <Card className="overflow-hidden" isPressable> + <CardBody> + <Image + as={NextImage} + isBlurred + alt="Anime Poster" + src={item.image} + width={185} + height={120} + shadow="lg" + className="h-64" + priority + /> + </CardBody> + <CardHeader> + <h4 className="antialiased text-small text-center uppercase w-44 overflow-hidden whitespace-nowrap text-ellipsis "> + {item.title} + </h4> + </CardHeader> + </Card> + </Link> + ))} + </section> + ); +}; + +export default SearchedDataFormatter; diff --git a/src/app/kdrama/components/searchQuery.js b/src/app/kdrama/components/searchQuery.js deleted file mode 100644 index 18c06a9..0000000 --- a/src/app/kdrama/components/searchQuery.js +++ /dev/null @@ -1,10 +0,0 @@ -"use server";
-
-export default async function FetchSearchTitle(title) {
- const res = await fetch(
- `https://consumet-jade.vercel.app/movies/dramacool/${title}`,
- { cache: "force-cache" }
- );
- const data = await res.json();
- return data;
-}
diff --git a/src/app/kdrama/components/videoLink.js b/src/app/kdrama/components/videoLink.js deleted file mode 100644 index e49ca18..0000000 --- a/src/app/kdrama/components/videoLink.js +++ /dev/null @@ -1,11 +0,0 @@ -"use server";
-export default async function getVideoLink(epiId, mediaId) {
- let videoLink;
- const res = await fetch(
- `https://consumet-jade.vercel.app/movies/dramacool/watch?episodeId=${epiId}&mediaId=${mediaId}`,
- { cache: "force-cache" }
- );
- const data = await res.json();
- videoLink = data.sources[0].url;
- return videoLink;
-}
diff --git a/src/app/kdrama/loading.jsx b/src/app/kdrama/loading.jsx deleted file mode 100644 index 4d5e9bd..0000000 --- a/src/app/kdrama/loading.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import styles from "./styles/loading.module.css";
-
-export default async function Loading() {
- return (
- <div className={styles.Main}>
- <div className={styles.LoadingContainer}></div>
- </div>
- );
-}
diff --git a/src/app/kdrama/page.jsx b/src/app/kdrama/page.jsx index b9cdb69..d5e2855 100644 --- a/src/app/kdrama/page.jsx +++ b/src/app/kdrama/page.jsx @@ -1,15 +1,95 @@ -import styles from "./styles/kdrama.module.css";
-import PopularDramas from "./components/popular";
-import RecentDramas from "./components/recent";
-import DramaSearch from "./components/search";
+import { Card, CardHeader, CardBody, Image, Link } from "@nextui-org/react";
+import NextImage from "next/image";
+
+import { DramaDataFetcher } from "./components/requests";
+import styles from "../page.module.css";
+import { Searchbar } from "./components/searchBar";
+import { PreFetchKdramaInfo } from "./components/cacher";
+
+const KdramaHomepage = async () => {
+ const recent_data = await DramaDataFetcher("recent");
+ const popular_data = await DramaDataFetcher("popular");
+
+ const dataToBeLoaded = [recent_data, popular_data];
+
+ for (let item of dataToBeLoaded) {
+ PreFetchKdramaInfo(item);
+ }
+
+ const header = (title) => (
+ <>
+ <p className="antialiased font-bold text-sky-400 text-2xl my-1">
+ {title}
+ </p>
+ </>
+ );
+
+ const format = (data) => (
+ <>
+ {data &&
+ data.results.map((item, index) => (
+ <Link
+ key={index}
+ href={`/kdrama/${encodeURIComponent(item.id)}`}
+ aria-label="anime redirection links"
+ className="flex flex-col items-center mx-1"
+ >
+ <Card className="overflow-visible " isPressable>
+ <CardBody>
+ <Image
+ as={NextImage}
+ isBlurred
+ alt="Anime Poster"
+ src={item.image}
+ width={270}
+ height={160}
+ className="h-60 overflow-hidden"
+ shadow="lg"
+ priority
+ />
+ </CardBody>
+ <CardHeader>
+ <h4
+ className={`antialiased text-small text-center uppercase w-44 overflow-hidden whitespace-nowrap text-ellipsis `}
+ >
+ {item.title}
+ </h4>
+ </CardHeader>
+ </Card>
+ </Link>
+ ))}
+ </>
+ );
-export default async function Kdrama() {
return (
- <div className={styles.Main}>
- <DramaSearch />
- <PopularDramas />
- <hr style={{ marginTop: 15, borderColor: "gray" }} />
- <RecentDramas />
- </div>
+ <section className="pt-12">
+ <div>
+ <Searchbar />
+ </div>
+ <div className="mx-2">
+ {header("Popular K-dramas")}
+ <div
+ className={`flex overflow-auto overflow-y-hidden pb-3 ${styles.ScrollBarAdjuster}`}
+ >
+ {format(popular_data)}
+ </div>
+ </div>
+ <div className="mx-2">
+ {header("Recent Releases")}
+ <div
+ className={`flex overflow-auto overflow-y-hidden pb-3 ${styles.ScrollBarAdjuster}`}
+ >
+ {format(recent_data)}
+ </div>
+ </div>
+
+ <br />
+ <br />
+ <br />
+ <br />
+ <br />
+ </section>
);
-}
+};
+
+export default KdramaHomepage;
diff --git a/src/app/kdrama/styles/info.module.css b/src/app/kdrama/styles/info.module.css deleted file mode 100644 index 3b60fd2..0000000 --- a/src/app/kdrama/styles/info.module.css +++ /dev/null @@ -1,173 +0,0 @@ -.Main {
- max-width: 98%;
- margin: 60px auto;
-}
-
-.TitleContainer {
- display: flex;
- align-items: center;
- justify-content: space-between;
-}
-
-.TitleContainer p {
- color: white;
- font-size: 34px;
- font-weight: 500;
- color: var(--neon-green);
- margin: 0;
-}
-
-.TitleContainer img {
- border-radius: 0.4rem;
-}
-
-.DramaDescription h2 {
- color: gray;
-}
-
-.DramaDescription p {
- color: white;
- margin-top: -10px;
- color: rgba(255, 255, 255, 0.637);
-}
-
-.DramaGenre {
- display: flex;
- align-items: center;
- overflow-x: auto;
- color: white;
- margin-top: 1rem;
-}
-.DramaGenre p {
- background-color: #1f1f1f;
- margin: 0rem 0rem 0rem 0.4rem;
- padding: 0.4rem;
- border-radius: 0.2rem;
-}
-
-.DramaGenre::-webkit-scrollbar {
- height: 2px;
-}
-
-.DramaGenre::-webkit-scrollbar-thumb {
- background-color: var(--light-green);
- border-radius: 1rem;
-}
-
-.genreMain {
- color: var(--neon-green);
- font-size: 18px;
-}
-
-.EpisodesContainer {
- margin-top: -10px;
-}
-
-.EpisodesContainer h2 {
- color: gray;
-}
-
-.EpisodeButtons {
- margin: -10px 5px;
-}
-
-.EpisodeButtons button {
- margin: 3px;
- padding: 5px;
- border: none;
- outline: none;
- border-radius: 5px;
- background-color: #3d3d3d;
- transition: background-color 0.2s linear;
- color: white;
- cursor: pointer;
- width: 100px;
-}
-
-.EpisodeButtons button p {
- text-align: center;
- font-family: "Lexend Deca", serif;
- margin: 0;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
-}
-
-.EpisodeButtons button:hover {
- background-color: #1f1f1f;
- transition: background-color 0.2s linear;
-}
-
-.videoPopUp {
- height: 100dvh;
- width: 100dvw;
- background-color: #141414ea;
- 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%;
- height: auto;
-}
-
-@media screen and (max-width: 768px) {
- .video {
- width: 100%;
- }
-
- .EpisodeButtons button {
- font-size: 14px;
- width: 80px;
- }
-
- .TitleContainer {
- flex-direction: column;
- }
-
- .TitleContainer img {
- width: auto;
- height: auto;
- border-radius: 0.2rem;
- background-color: #121212;
- padding: 0.2rem;
- }
-
- .TitleContainer p {
- font-size: 30px;
- font-weight: 400;
- margin: 0 0 0.25rem 0
- }
-
- .EpisodesContainer {
- text-align: center;
- }
-
- .EpisodeButtons button {
- width: 90px;
- }
-}
diff --git a/src/app/kdrama/styles/kdrama.module.css b/src/app/kdrama/styles/kdrama.module.css deleted file mode 100644 index deb3860..0000000 --- a/src/app/kdrama/styles/kdrama.module.css +++ /dev/null @@ -1,4 +0,0 @@ -.Main {
- margin: 80px auto;
- width: 99%;
-}
diff --git a/src/app/kdrama/styles/loading.module.css b/src/app/kdrama/styles/loading.module.css deleted file mode 100644 index 825c247..0000000 --- a/src/app/kdrama/styles/loading.module.css +++ /dev/null @@ -1,21 +0,0 @@ -.Main {
- height: 100dvh;
- 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/kdrama/styles/popular.module.css b/src/app/kdrama/styles/popular.module.css deleted file mode 100644 index ec76fb3..0000000 --- a/src/app/kdrama/styles/popular.module.css +++ /dev/null @@ -1,73 +0,0 @@ -.popDramasText {
- color: white;
- margin: 0 0 0.2rem 0;
-}
-
-.AnimeContainer {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
- grid-gap: 0.7rem;
- align-items: center;
-}
-
-.AnimeContainer::-webkit-scrollbar {
- height: 0px;
-}
-
-.AnimeContainer:hover .AnimeEntry {
- opacity: 0.5;
-}
-
-.AnimeContainer:hover .AnimeEntry:hover {
- opacity: 1;
- transform: scale(1.018);
- background-color: #272727;
-}
-
-.AnimeEntry {
- display: flex;
- flex-direction: column;
- align-items: center;
- background-color: #1f1f1fbb;
- padding: 0.5rem;
- transition: opacity 200ms ease, transform 200ms ease,
- background-color 200ms ease;
- cursor: grab;
- border-radius: 0.4rem;
- overflow: hidden;
-}
-
-.AnimeEntry img {
- border-radius: 0.4rem;
- box-shadow: 0 0 10px 5px rgba(18, 18, 18, 0.863);
-}
-
-.AnimeEntry p {
- text-align: center;
- color: white;
- width: auto;
- max-width: 160px;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- margin: 0.5rem 0rem 0rem 0rem;
-}
-
-@media screen and (max-width: 768px) {
- .popDramasText {
- text-align: start;
- font-size: 26px;
- margin: 1rem 0 1rem 0;
- }
-
- .AnimeContainer {
- display: flex;
- overflow-x: auto;
- overflow-y: hidden;
- margin-top: -8px;
- }
-
- .AnimeEntry img {
- width: auto;
- }
-}
diff --git a/src/app/kdrama/styles/search.module.css b/src/app/kdrama/styles/search.module.css deleted file mode 100644 index d7d28b2..0000000 --- a/src/app/kdrama/styles/search.module.css +++ /dev/null @@ -1,84 +0,0 @@ -
-.LoadingText {
- color: white;
- text-align: center;
- font-size: 18px;
-}
-
-.Search {
- padding: 5px;
- background-color: #121212;
- display: flex;
- align-items: center;
- max-width: 30%;
- border-radius: 0.5rem;
-}
-
-.SearchContainer input {
- margin-left: 5px;
- padding: 5px;
- border: none;
- outline: none;
- background-color: #121212;
- font-size: 16px;
- color: white;
- width: 100%;
- font-family: "Lexend Deca";
-}
-
-.SearchResults {
- display: flex;
- margin: 10px 0 10px 0;
- overflow-x: auto;
-}
-
-.SearchResults::-webkit-scrollbar {
- height: 5px;
-}
-
-.SearchResults::-webkit-scrollbar-track {
- background-color: #3333339d;
- border-radius: 5px;
-}
-
-.SearchResults::-webkit-scrollbar-thumb {
- background-color: rgb(68, 68, 68);
- border-radius: 5px;
-}
-
-.SearchEntry {
- display: flex;
- align-items: center;
- justify-content: space-between;
- margin: 5px;
- padding: 6px;
- background-color: #2e2e2eab;
- border-radius: 0.5rem;
- cursor: pointer;
- transition: opacity 200ms linear, background-color 200ms linear;
-}
-
-.SearchResults .SearchEntry {
- opacity: 0.5;
-}
-
-.SearchResults .SearchEntry:hover {
- opacity: 1;
- background-color: #333333c9;
-}
-
-.SearchEntry p {
- color: white;
- font-size: 18px;
- width: 45vh;
-}
-
-.SearchEntry img {
- border-radius: 0.5rem;
-}
-
-@media screen and (max-width: 768px) {
- .Search {
- max-width: 100%;
- }
-}
diff --git a/src/app/layout.jsx b/src/app/layout.jsx index f16ed98..9673001 100644 --- a/src/app/layout.jsx +++ b/src/app/layout.jsx @@ -1,11 +1,11 @@ -import { Lexend_Deca } from "next/font/google"; import "./globals.css"; import Header from "./components/header/header"; import Footer from "./components/footer/page"; import { SpeedInsights } from "@vercel/speed-insights/next"; import { Analytics } from "@vercel/analytics/react"; - -const lexend = Lexend_Deca({ subsets: ["latin"] }); +import { NextUIProvider } from "@nextui-org/react"; +import { ThemeProvider as NextThemesProvider } from "next-themes"; +import { lexend } from "../../config/fonts"; export const metadata = { title: "Dramalama", @@ -15,7 +15,21 @@ export const metadata = { applicationName: "Dramalama", authors: [{ name: "zephex", url: "https://github.com/real-zephex" }], creator: "Zephex", - keywords: ["Kdrama", "Anime", "Manga", "Watch Online"], + keywords: [ + "Kdrama", + "Anime", + "Manga", + "Watch Online", + "watch kdrama free", + "watch anime free online", + "kdrama for free", + "watch online", + "read mangas for free", + "mangas online", + "movies online", + "free movies online", + "watch series for free", + ], robots: { index: true, follow: true, @@ -45,9 +59,12 @@ export default function RootLayout({ children }) { <body> <SpeedInsights /> <Analytics /> - <Header /> - {children} - <Footer /> + <NextUIProvider> + <NextThemesProvider attribute="class" defaultTheme="dark"> + <Header /> + <div>{children}</div> + </NextThemesProvider> + </NextUIProvider> </body> </html> ); diff --git a/src/app/movies/components/popular.jsx b/src/app/movies/components/popular.jsx index d2d5e36..3ef0501 100644 --- a/src/app/movies/components/popular.jsx +++ b/src/app/movies/components/popular.jsx @@ -10,7 +10,7 @@ export default async function POPULAR_MOVIES() { return ( <main className={styles.Main}> - <h1>Popular Movies</h1> + <h2>Popular Movies</h2> <section className={styles.MovieContainer}> {data && data.results && @@ -23,21 +23,12 @@ export default async function POPULAR_MOVIES() { }} key={index} > - <div - style={{ - borderRadius: "0.5rem", - overflow: "hidden", - backgroundImage: `url(https://sup-proxy.zephex0-f6c.workers.dev/api-content?url=https://image.tmdb.org/t/p/original${item.backdrop_path})`, - backgroundRepeat: "no-repeat", - backgroundSize: "cover", - }} - className={styles.MovieEntryPrev} - > + <div className={styles.MovieEntryPrev}> <div className={styles.MovieEntry}> <Image src={`https://sup-proxy.zephex0-f6c.workers.dev/api-content?url=https://image.tmdb.org/t/p/original${item.poster_path}`} - width={167} - height={247} + width={180} + height={300} alt="Movie Poster" priority ></Image> diff --git a/src/app/movies/components/search.jsx b/src/app/movies/components/search.jsx index 6514b76..dca163a 100644 --- a/src/app/movies/components/search.jsx +++ b/src/app/movies/components/search.jsx @@ -19,7 +19,7 @@ export default function SEARCH_COMPONENT() { <FaSearch color="white" className={styles.SearchIcon} - size={17} + size={22} /> <input placeholder="Enter movie title here" diff --git a/src/app/movies/components/trending.jsx b/src/app/movies/components/trending.jsx index 5436dfa..8e20ba1 100644 --- a/src/app/movies/components/trending.jsx +++ b/src/app/movies/components/trending.jsx @@ -10,7 +10,7 @@ export default async function TREDNING_MOVIES() { return ( <main className={styles.Main}> - <h1>Trending Movies</h1> + <h2>Trending Movies</h2> <section className={styles.MovieContainer}> {data && data.results && @@ -23,21 +23,12 @@ export default async function TREDNING_MOVIES() { }} key={index} > - <div - style={{ - borderRadius: "0.5rem", - overflow: "hidden", - backgroundImage: `url(https://sup-proxy.zephex0-f6c.workers.dev/api-content?url=https://image.tmdb.org/t/p/original${item.backdrop_path})`, - backgroundRepeat: "no-repeat", - backgroundSize: "cover", - }} - className={styles.MovieEntryPrev} - > + <div className={styles.MovieEntryPrev}> <div className={styles.MovieEntry}> <Image src={`https://sup-proxy.zephex0-f6c.workers.dev/api-content?url=https://image.tmdb.org/t/p/original${item.poster_path}`} - width={167} - height={247} + width={180} + height={300} alt="Movie Poster" ></Image> <p>{item.title}</p> diff --git a/src/app/movies/styles/pop_trend.module.css b/src/app/movies/styles/pop_trend.module.css index 2e9ebd9..362debd 100644 --- a/src/app/movies/styles/pop_trend.module.css +++ b/src/app/movies/styles/pop_trend.module.css @@ -4,22 +4,16 @@ margin-right: 0.2rem; } -.Main h1 { - margin: 0 0 0.5rem 0; - 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; +.Main h2 { + color: white; + margin: 0.4rem 0 0 0; + text-transform: uppercase; + font-size: 30px; } .MovieContainer { display: grid; - grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); grid-gap: 0.5rem; align-items: center; } @@ -34,11 +28,12 @@ .MovieContainer:hover .MovieEntryPrev:hover { opacity: 1; - scale: 1.015; + transform: translateY(-5px) scale(1.02); + } .MovieEntryPrev { - transition: opacity 200ms ease, scale 200ms ease; + transition: opacity 200ms ease, transform 200ms ease; } .MovieEntry { @@ -51,6 +46,8 @@ flex-direction: column; cursor: pointer; backdrop-filter: blur(10px); + border-radius: 0.5rem; + overflow: hidden; } .MovieEntry img { @@ -58,6 +55,7 @@ box-shadow: 0px 0px 10px 8px rgb(32, 32, 32); } + .MovieEntry p { width: 160px; overflow: hidden; diff --git a/src/app/movies/styles/search.module.css b/src/app/movies/styles/search.module.css index ea81e3f..d3b1a75 100644 --- a/src/app/movies/styles/search.module.css +++ b/src/app/movies/styles/search.module.css @@ -5,10 +5,12 @@ .InputContainer { display: flex; align-items: center; - background-color: #121212; - /* padding: 0.2rem; */ + background-color: #1f1f1f; + padding: 0.4rem; width: 40vw; border-radius: 0.5rem; + + } .SearchIcon { @@ -25,6 +27,7 @@ font-size: large; color: white; width: 100%; + font-size: 20px; } /* Search Results */ diff --git a/src/app/page.jsx b/src/app/page.jsx index b2bcc5f..29c4857 100644 --- a/src/app/page.jsx +++ b/src/app/page.jsx @@ -1,54 +1,54 @@ -import styles from "./page.module.css"; -import Link from "next/link"; +import { + Card, + CardHeader, + CardBody, + Divider, + Link, + Image, +} from "@nextui-org/react"; export default async function Home() { - return ( - <main className={styles.newbg}> - <div className={styles.content}> - <div className={styles.contentContainer}> - <Link - href={"/anime"} - title="Click here to get redirected to the anime webpage" - > - <div className={styles.anime}> - <h2>Anime</h2> - </div> - </Link> - <Link - href={"/manga"} - title="Click here to get redirected to the manga webpage" - > - <div className={styles.manga}> - <h2>Manga</h2> - </div> - </Link> - <Link - href={"/kdrama"} - title="Click here to get redirected to the kdrama webpage" - > - <div className={styles.kdrama}> - <h2>Kdrama</h2> - </div> - </Link> - <Link - href={"/movies"} - title="Click here to get redirected to the movie webpage" - > - <div className={styles.movies}> - <h2>Movies</h2> + const homePageCards = (title, message, url) => { + return ( + <Link href={`/${title}`} className="my-1"> + <Card className="max-w-[400px] border-1 border-gray-500"> + <CardHeader className="flex gap-3"> + <Image + alt="nextui logo" + height={40} + width={40} + radius="sm" + src={url} + /> + <div className="flex flex-col"> + <p className="text-md">{title}</p> + <p className="text-small text-default-500"> + dramalama/{title} + </p> </div> - </Link> - <Link - href={"/web-series"} - title="Click here to get redirected to the series webpage" - > - <div className={styles.series}> - <h2>Series</h2> - </div> - </Link> - </div> - </div> - </main> + </CardHeader> + <Divider className="bg-slate-400" /> + <CardBody> + <p>{message}</p> + </CardBody> + </Card> + </Link> + ); + }; + + return ( + <section className="h-screen w-screen flex flex-col items-center justify-center "> + {homePageCards( + "anime", + "Gravity of anime constant state of falling into the depths of happiness and joy", + "https://i.ibb.co/bLJzm3T/Whats-App-Image-2024-05-23-at-22-05-59-09933e5f.jpg" + )} + {homePageCards( + "kdrama", + "Infinity loop I'll watch just one k-drama, ok one more, ok wait...", + "https://ih1.redbubble.net/image.2656505524.2951/poster,504x498,f8f8f8-pad,600x600,f8f8f8.jpg" + )} + </section> ); } diff --git a/src/app/page.module.css b/src/app/page.module.css index e0b9a60..6620bc9 100644 --- a/src/app/page.module.css +++ b/src/app/page.module.css @@ -1,154 +1,8 @@ -.newbg { - background: rgb(37, 37, 37); - background: radial-gradient(circle, - rgb(41, 41, 41) 2%, - rgb(12, 12, 12) 30%); +.ScrollBarAdjuster::-webkit-scrollbar { + height: 0.4rem; } -.header { - position: fixed; - top: 0; - width: 100%; - display: flex; - align-items: center; - justify-content: space-between; - background-color: #1212129d; - backdrop-filter: blur(10px); - z-index: 999; -} - -.left { - color: white; -} - -.left p { - margin: 0; - font-size: 1.5rem; - padding: 1rem; - color: transparent; - background: linear-gradient(90deg, - rgba(0, 151, 255, 0.9976365546218487) 3%, - rgba(115, 154, 242, 1) 21%, - rgba(255, 159, 224, 1) 40%, - rgba(255, 194, 133, 1) 60%, - rgba(245, 242, 81, 0.884) 80%, - rgba(255, 179, 0, 0.842) 100%); - background-size: 150% 50%; - background-clip: text; - animation: colorChange 3s linear infinite alternate-reverse; -} - -@keyframes colorChange { - 0% { - background-position: 0 50%; - } - - 100% { - background-position: 100% 100%; - } -} - -.right { - overflow: auto; -} - -.right::-webkit-scrollbar { - height: 0; -} - -.right a { - text-decoration: none; - color: white; - margin: 0 0.4rem 0 0.5rem; - transition: opacity 400ms ease; - font-size: 1rem; -} - -.header:hover>.right a { - opacity: 0.5; -} - -.header:hover>.right a:hover { - opacity: 1; - cursor: pointer; -} - -.footer { - bottom: 0; - width: 100%; - position: fixed; - background-color: #1212129a; - backdrop-filter: blur(10px); - color: white; - font-family: "Poppins", serif; - z-index: 999; -} - -.footer p { - text-align: center; - font-size: 14px; - margin: 0; - padding: 0.2rem; - font-family: "Poppins", serif; -} - -.content { - height: 100vh; - display: flex; - align-items: center; - justify-content: center; -} - -.contentContainer { - display: flex; - align-items: center; - justify-content: center; -} - -.contentContainer a { - text-decoration: none; - color: white; -} - -.manga, -.anime, -.kdrama, -.movies, -.series { - background-color: #121212e0; - color: white; - border-radius: 0.5rem; - transition: opacity 200ms ease, transform 200ms ease; - padding: 0.4rem 2rem; - margin: 0.2rem 0.2rem 0 0.2rem; - border-color: rgb(80, 80, 80); - border-style: groove; - border-width: 0.1rem; - text-align: center; -} - -.content:hover>.contentContainer div { - opacity: 0.5; -} - -.content:hover>.contentContainer div:hover { - opacity: 1; - cursor: pointer; - transform: scale(0.95) rotateX(12deg) rotateY(-8deg); -} - -@media screen and (max-width: 1024px) { - .contentContainer { - display: block; - } - - .content:hover>.contentContainer div { - opacity: 0.5; - } - - .content:hover>.contentContainer div:hover { - opacity: 1; - cursor: pointer; - transform: scale(0.95); - } +.ScrollBarAdjuster::-webkit-scrollbar-thumb { + background-color: rgb(95, 95, 95); + border-radius: 1rem; }
\ No newline at end of file diff --git a/src/app/web-series/[id]/page.jsx b/src/app/web-series/[id]/page.jsx index 63fcc3a..3d36883 100644 --- a/src/app/web-series/[id]/page.jsx +++ b/src/app/web-series/[id]/page.jsx @@ -13,7 +13,7 @@ const SeriesInfoPage = async ({ params }) => { <main style={{ // backgroundImage: `url(https://sup-proxy.zephex0-f6c.workers.dev/api-content?url=https://image.tmdb.org/t/p/original${data.backdrop_path})`, - background: `linear-gradient(to bottom, rgba(0, 0, 0, 0) 70%, rgba(0, 0, 0, 1) 100%), + background: `linear-gradient(to bottom, rgba(0, 0, 0, 0) 70%, #121212 100%), url(https://sup-proxy.zephex0-f6c.workers.dev/api-content?url=https://image.tmdb.org/t/p/original${data.backdrop_path}) no-repeat center center fixed`, backgroundSize: "cover", }} diff --git a/src/app/web-series/components/HomePageModules.jsx b/src/app/web-series/components/HomePageModules.jsx index 90af2f8..91d90bf 100644 --- a/src/app/web-series/components/HomePageModules.jsx +++ b/src/app/web-series/components/HomePageModules.jsx @@ -39,8 +39,8 @@ const Pages = async ({ type: type }) => { <section className={styles.SeriesEntry}> <Image src={`https://sup-proxy.zephex0-f6c.workers.dev/api-content?url=https://image.tmdb.org/t/p/original${item.poster_path}`} - width={167} - height={267} + width={180} + height={300} alt="Series Poster" priority /> diff --git a/src/app/web-series/components/searchBar.jsx b/src/app/web-series/components/searchBar.jsx index 81dd25f..42531bc 100644 --- a/src/app/web-series/components/searchBar.jsx +++ b/src/app/web-series/components/searchBar.jsx @@ -23,7 +23,7 @@ const SearchBar = () => { <FaSearch color="white" className={styles.SearchIcon} - size={17} + size={22} /> <input placeholder="Enter series title here" diff --git a/src/app/web-series/styles/info.module.css b/src/app/web-series/styles/info.module.css index 8873b41..d60023d 100644 --- a/src/app/web-series/styles/info.module.css +++ b/src/app/web-series/styles/info.module.css @@ -1,5 +1,4 @@ .Main { - min-height: 100vh; margin-top: 60px; } diff --git a/src/app/web-series/styles/pages.module.css b/src/app/web-series/styles/pages.module.css index 631f7db..8f15de2 100644 --- a/src/app/web-series/styles/pages.module.css +++ b/src/app/web-series/styles/pages.module.css @@ -1,10 +1,13 @@ .main h2 { color: white; + /* margin: 0.4rem 0 0 0; */ + text-transform: uppercase; + font-size: 30px; } .SeriesContainer { display: grid; - grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); grid-gap: 0.5rem; align-items: center; margin: -1rem 0 0 0; @@ -23,13 +26,13 @@ } .SeriesEntry p { - width: 160px; + width: 170px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; text-align: center; margin: 0.3rem 0 0 0; - font-family: "Atkinson Hyperlegible", serif; + font-family: "Lexend Deca", serif; } .SeriesEntry img { diff --git a/src/app/web-series/styles/search.module.css b/src/app/web-series/styles/search.module.css index 145a2aa..fdf46e3 100644 --- a/src/app/web-series/styles/search.module.css +++ b/src/app/web-series/styles/search.module.css @@ -1,9 +1,10 @@ .InputContainer { display: flex; align-items: center; - background-color: #121212; + background-color: #1f1f1f; width: 40vw; border-radius: 0.5rem; + padding: 0.4rem; } .SearchIcon { @@ -16,6 +17,7 @@ border: none; padding: 0.4rem; font-family: "Lexend Deca", serif; + font-size: 20px; margin-left: 0.2rem; font-size: large; color: white; |