diff options
Diffstat (limited to 'src/app/kdrama/components')
| -rw-r--r-- | src/app/kdrama/components/cacher.js | 17 | ||||
| -rw-r--r-- | src/app/kdrama/components/episodesContainer.jsx | 83 | ||||
| -rw-r--r-- | src/app/kdrama/components/infoTabs.jsx | 51 | ||||
| -rw-r--r-- | src/app/kdrama/components/popular.jsx | 47 | ||||
| -rw-r--r-- | src/app/kdrama/components/recent.jsx | 46 | ||||
| -rw-r--r-- | src/app/kdrama/components/requests.js | 44 | ||||
| -rw-r--r-- | src/app/kdrama/components/search.jsx | 65 | ||||
| -rw-r--r-- | src/app/kdrama/components/searchBar.jsx | 57 | ||||
| -rw-r--r-- | src/app/kdrama/components/searchFormatter.jsx | 46 | ||||
| -rw-r--r-- | src/app/kdrama/components/searchQuery.js | 10 | ||||
| -rw-r--r-- | src/app/kdrama/components/videoLink.js | 11 |
11 files changed, 282 insertions, 195 deletions
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;
-}
|