diff options
Diffstat (limited to 'components')
| -rw-r--r-- | components/anime/watch/primarySide.js | 17 | ||||
| -rw-r--r-- | components/dataAni.json | 182 | ||||
| -rw-r--r-- | components/home/content.js | 269 | ||||
| -rw-r--r-- | components/home/schedule.js | 2 | ||||
| -rw-r--r-- | components/listEditor.js | 19 | ||||
| -rw-r--r-- | components/videoPlayer.js | 143 |
6 files changed, 517 insertions, 115 deletions
diff --git a/components/anime/watch/primarySide.js b/components/anime/watch/primarySide.js index fa12711..2e28563 100644 --- a/components/anime/watch/primarySide.js +++ b/components/anime/watch/primarySide.js @@ -26,6 +26,7 @@ export default function PrimarySide({ disqus, setOnList, episodeList, + timeWatched, }) { const [episodeData, setEpisodeData] = useState(); const [open, setOpen] = useState(false); @@ -35,8 +36,6 @@ export default function PrimarySide({ useEffect(() => { setLoading(true); - setEpisodeData(); - setSkip(); async function fetchData() { if (info) { const { data } = await axios.get( @@ -47,15 +46,15 @@ export default function PrimarySide({ `https://api.aniskip.com/v2/skip-times/${info.idMal}/${parseInt( epiNumber )}?types[]=ed&types[]=mixed-ed&types[]=mixed-op&types[]=op&types[]=recap&episodeLength=` - ).then((r) => { - if (!r.ok) { - switch (r.status) { + ).then((res) => { + if (!res.ok) { + switch (res.status) { case 404: { return null; } } } - return r.json(); + return res.json(); }); const op = @@ -72,6 +71,10 @@ export default function PrimarySide({ } fetchData(); + return () => { + setEpisodeData(); + setSkip(); + }; }, [providerId, watchId, info]); useEffect(() => { @@ -141,7 +144,9 @@ export default function PrimarySide({ skip={skip} proxy={proxy} aniId={info.id} + aniTitle={info.title?.romaji || info.title?.english} track={navigation} + timeWatched={timeWatched} /> ) ) : ( diff --git a/components/dataAni.json b/components/dataAni.json new file mode 100644 index 0000000..7ffd8ee --- /dev/null +++ b/components/dataAni.json @@ -0,0 +1,182 @@ +{ + "currentPage": 1, + "hasNextPage": false, + "results": [ + { + "id": "125367", + "malId": 43608, + "title": { + "romaji": "Kaguya-sama wa Kokurasetai: Ultra Romantic", + "english": "Kaguya-sama: Love is War -Ultra Romantic-", + "native": "かぐや様は告らせたい-ウルトラロマンティック-", + "userPreferred": "Kaguya-sama wa Kokurasetai: Ultra Romantic" + }, + "status": "Completed", + "image": "https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx125367-bl5vGalMH2cC.png", + "cover": "https://s4.anilist.co/file/anilistcdn/media/anime/banner/125367-hGPJLSNfprO3.jpg", + "popularity": 174430, + "description": "The elite members of Shuchiin Academy's student council continue their competitive day-to-day antics. Council president Miyuki Shirogane clashes daily against vice-president Kaguya Shinomiya, each fighting tooth and nail to trick the other into confessing their romantic love. Kaguya struggles within the strict confines of her wealthy, uptight family, rebelling against her cold default demeanor as she warms to Shirogane and the rest of her friends.<br>\n<br>\nMeanwhile, council treasurer Yuu Ishigami suffers under the weight of his hopeless crush on Tsubame Koyasu, a popular upperclassman who helps to instill a new confidence in him. Miko Iino, the newest student council member, grows closer to the rule-breaking Ishigami while striving to overcome her own authoritarian moral code.<br>\n<br>\nAs love further blooms at Shuchiin Academy, the student council officers drag their outsider friends into increasingly comedic conflicts.<br>\n<br>\n(Source: MAL Rewrite)<br>\n<br>\n<i>Note: The first episode had an advanced screening on April 2, in both New York & Los Angeles.<br>", + "rating": 90, + "genres": ["Comedy", "Psychological", "Romance", "Slice of Life"], + "color": "#d6f1a1", + "totalEpisodes": 13, + "currentEpisodeCount": 13, + "type": "TV", + "releaseDate": 2022 + }, + { + "id": "112641", + "malId": 40591, + "title": { + "romaji": "Kaguya-sama wa Kokurasetai?: Tensaitachi no Renai Zunousen", + "english": "Kaguya-sama: Love is War?", + "native": "かぐや様は告らせたい?~天才たちの恋愛頭脳戦~", + "userPreferred": "Kaguya-sama wa Kokurasetai?: Tensaitachi no Renai Zunousen" + }, + "status": "Completed", + "image": "https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx112641-zoGC8d6FaPXU.jpg", + "cover": "https://s4.anilist.co/file/anilistcdn/media/anime/banner/112641-mKZe0zng0ndV.jpg", + "popularity": 303429, + "description": "After a slow but eventful summer vacation, Shuchiin Academy's second term is now starting in full force. As August transitions into September, Miyuki Shirogane's birthday looms ever closer, leaving Kaguya Shinomiya in a serious predicament as to how to celebrate it. Furthermore, the tenure of the school's 67th student council is coming to an end. Due to the council members being in different classes, the only time Kaguya and Miyuki have to be together will soon disappear, putting all of their cunning plans at risk.<br></br>\n\nA long and difficult election that will decide the fate of the new student council awaits, as multiple challengers fight for the coveted title of president.<br></br>\n", + "rating": 86, + "genres": ["Comedy", "Psychological", "Romance", "Slice of Life"], + "color": "#DAD797", + "totalEpisodes": 12, + "currentEpisodeCount": 12, + "type": "TV", + "releaseDate": 2020 + }, + { + "id": "101921", + "malId": 37999, + "title": { + "romaji": "Kaguya-sama wa Kokurasetai: Tensaitachi no Renai Zunousen", + "english": "Kaguya-sama: Love is War", + "native": "かぐや様は告らせたい~天才たちの恋愛頭脳戦~", + "userPreferred": "Kaguya-sama wa Kokurasetai: Tensaitachi no Renai Zunousen" + }, + "status": "Completed", + "image": "https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx101921-VvdGQy1ZySYf.jpg", + "cover": "https://s4.anilist.co/file/anilistcdn/media/anime/banner/101921-GgvvFhlNhzlF.jpg", + "popularity": 378107, + "description": "Known for being both brilliant and powerful, Miyuki Shirogane and Kaguya Shinomiya lead the illustrious Shuchiin Academy as near equals. And everyone thinks they’d make a great couple. Pride and arrogance are in ample supply, so the only logical move is to trick the other into instigating a date! Who will come out on top in this psychological war where the first move is the only one that matters?\n<br><br>\n(Source: Aniplex)", + "rating": 83, + "genres": ["Comedy", "Psychological", "Romance", "Slice of Life"], + "color": "#e45086", + "totalEpisodes": 12, + "currentEpisodeCount": 12, + "type": "TV", + "releaseDate": 2019 + }, + { + "id": "125368", + "malId": 43609, + "title": { + "romaji": "Kaguya-sama wa Kokurasetai: Tensaitachi no Renai Zunousen OVA", + "english": null, + "native": "かぐや様は告らせたい~天才たちの恋愛頭脳戦~OVA", + "userPreferred": "Kaguya-sama wa Kokurasetai: Tensaitachi no Renai Zunousen OVA" + }, + "status": "Completed", + "image": "https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx125368-QhcBkbNP0ZfU.png", + "cover": "https://s4.anilist.co/file/anilistcdn/media/anime/banner/125368-zCY0WnSQl7RG.jpg", + "popularity": 63453, + "description": "OVA adapting both chapters of <i>Kaguya-sama wa Kokurasetai Darkness</i> and Chapter 96 of the main series.", + "rating": 75, + "genres": ["Comedy", "Ecchi", "Romance", "Slice of Life"], + "color": "#c97843", + "totalEpisodes": 1, + "currentEpisodeCount": 1, + "type": "OVA", + "releaseDate": 2021 + }, + { + "id": "151384", + "malId": 52198, + "title": { + "romaji": "Kaguya-sama wa Kokurasetai: First Kiss wa Owaranai", + "english": "Kaguya-sama: Love is War -The First Kiss That Never Ends-", + "native": "かぐや様は告らせたい -ファーストキッスは終わらない-", + "userPreferred": "Kaguya-sama wa Kokurasetai: First Kiss wa Owaranai" + }, + "status": "Completed", + "image": "https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx151384-gv0q8wOE6D58.jpg", + "cover": "https://s4.anilist.co/file/anilistcdn/media/anime/banner/151384-JybfIpHr2gx6.jpg", + "popularity": 68747, + "description": "Shuchiin Academy’s student council room: the place where Student Council Vice President Kaguya Shinomiya and President Miyuki Shirogane met. After a long battle in love, these two geniuses communicated their feelings and, at the Hoshin Festival, had their very first kiss. However, there was no clear confession of love. The relationship between these two, who assumed they would be a couple, remains ambiguous. Now, overly conscious of their feelings, they must face the biggest challenge yet: Christmas. It’s Shirogane who wants it to be perfect versus Kaguya who pursues the imperfect situation. This is the very “normal” love story of two geniuses and the first kiss that never ends.\n<br><br>\n(Source: Aniplex of America)\n<br><br>\n<em>Kaguya-sama: Love is War -The First Kiss That Never Ends- first premiered in theaters across Japan on December 17, 2022. It was later released as 4 episodes on TV, streaming, and Blu-Ray/DVD.<em>", + "rating": 88, + "genres": ["Comedy", "Psychological", "Romance", "Slice of Life"], + "color": "#e45d78", + "totalEpisodes": 4, + "currentEpisodeCount": 4, + "type": "TV", + "releaseDate": 2023 + }, + { + "id": "3322", + "malId": 3322, + "title": { + "romaji": "Wagaya no Oinari-sama.", + "english": "Our Home's Fox Deity", + "native": "我が家のお稲荷さま。", + "userPreferred": "Wagaya no Oinari-sama." + }, + "status": "Completed", + "image": "https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/3322.jpg", + "cover": null, + "popularity": 4111, + "description": "The Mizuchi bloodline has long been hunted by Yokai, or monsters. Toru and Noboru Takagami are descendents of this bloodline, and under their grandmother's discretion, are given a secret weapon to combat these monsters. It is Tenko Kugen, a fox deity who can take the shape of a man or woman at will. The mischievous deity is accompanied by a shrine maiden, Ko, who will both live with the Takagami brothers at their house. Life just got complicated. <br><br>\n(Source: NIS America) ", + "rating": 66, + "genres": ["Adventure", "Fantasy", "Supernatural"], + "color": "#f1a150", + "totalEpisodes": 24, + "currentEpisodeCount": 24, + "type": "TV", + "releaseDate": 2008 + }, + { + "id": "5484", + "malId": 5484, + "title": { + "romaji": "Wagaya no Oinari-sama. Specials", + "english": null, + "native": "我が家のお稲荷さま。", + "userPreferred": "Wagaya no Oinari-sama. Specials" + }, + "status": "Completed", + "image": "https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/5484.jpg", + "cover": null, + "popularity": 458, + "description": "Short comedy sketches involving Tenko & Kou.", + "rating": 55, + "genres": ["Adventure", "Comedy", "Fantasy", "Supernatural"], + "color": "#aed678", + "totalEpisodes": 12, + "currentEpisodeCount": 12, + "type": "SPECIAL", + "releaseDate": 2008 + }, + { + "id": "165557", + "malId": 50325, + "title": { + "romaji": "Kaguya-sama wa Kokurasetai: Ultra Romantic 3rd Season Teaser PV - Ishigami Yuu wa Kataritai", + "english": "Kaguya-sama: Love is War -Ultra Romantic- \"Yu Ishigami Wants to Chat\"", + "native": "第3期『かぐや様は告らせたい-ウルトラロマンティック-』 / ティザーPV 「石上優は語りたい」", + "userPreferred": "Kaguya-sama wa Kokurasetai: Ultra Romantic 3rd Season Teaser PV - Ishigami Yuu wa Kataritai" + }, + "status": "Completed", + "image": "https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/b165557-q7A6HcZD0CHs.png", + "cover": null, + "popularity": 2412, + "description": "Every Thursday, Shuchiin Academy student council secretary Yuu Ishigami can be found in a state of anxiety. He counts down the hours until he can escape to the student council chamber to read the latest chapter of the popular seinen manga series, \"Momo Doesn't Think.\" Joining Ishigami in his weekly reading, student council president Miyuki Shirogane swiftly grows excited at the news of a third season of the manga's anime adaptation.<br><br>\n\nHowever, it is only a matter of time before vice president Kaguya Shinomiya discovers what has been engrossing the two boys. With the stigma of being labeled an otaku looming over his head, Shirogane must do everything he can to keep his embarrassing secret from coming to light.<br><br>\n\n(Source: MAL Rewrite)<br><br>\n\n<i>Note: This short PV adapts the 110th chapter of the manga and serves as a teaser for the April 2022 release of Kaguya-sama wa Kokurasetai: Ultra Romantic.</i>", + "rating": 77, + "genres": ["Comedy", "Romance"], + "color": "#f1d6c9", + "totalEpisodes": 1, + "currentEpisodeCount": 1, + "type": "ONA", + "releaseDate": 2021 + } + ] +} diff --git a/components/home/content.js b/components/home/content.js index 9d41fe9..f13c7a8 100644 --- a/components/home/content.js +++ b/components/home/content.js @@ -10,10 +10,17 @@ import { import { parseCookies } from "nookies"; import { ChevronLeftIcon } from "@heroicons/react/20/solid"; -import { ExclamationCircleIcon } from "@heroicons/react/24/solid"; +import { ExclamationCircleIcon, PlayIcon } from "@heroicons/react/24/solid"; import { useRouter } from "next/router"; -export default function Content({ ids, section, data, og, userName }) { +export default function Content({ + ids, + section, + data, + userData, + og, + userName, +}) { const router = useRouter(); const [startX, setStartX] = useState(null); @@ -115,6 +122,9 @@ export default function Content({ ids, section, data, og, userName }) { filteredData?.length > 15 ? filteredData?.slice(0, 15) : filteredData; const goToPage = () => { + if (section === "Recently Watched") { + router.push(`/${lang}/anime/recently-watched`); + } if (section === "Trending Now") { router.push(`/${lang}/anime/trending`); } @@ -159,95 +169,184 @@ export default function Content({ ids, section, data, og, userName }) { onClick={handleClick} ref={containerRef} > - {slicedData?.map((anime) => { - const progress = og?.find((i) => i.mediaId === anime.id); - - return ( - <div - key={anime.id} - className="flex flex-col gap-3 shrink-0 cursor-pointer" - > - <Link - href={`/${lang}/anime/${anime.id}`} - className="hover:scale-105 hover:shadow-lg group relative duration-300 ease-out" - > - {ids === "onGoing" && ( - <div className="h-[190px] lg:h-[265px] w-[135px] lg:w-[185px] bg-gradient-to-b from-transparent to-black absolute z-40 rounded-md whitespace-normal font-karla group"> - <div className="flex flex-col items-center h-full justify-end text-center pb-5"> - <h1 className="line-clamp-1 w-[70%] text-[10px]"> - {anime.title.romaji || anime.title.english} - </h1> - {checkProgress(progress) && - !clicked?.hasOwnProperty(anime.id) && ( - <ExclamationCircleIcon className="w-7 h-7 absolute z-40 -top-3 -right-3" /> - )} - {checkProgress(progress) && ( - <div - onClick={() => handleAlert(anime.id)} - className="group-hover:visible invisible absolute top-0 bg-black bg-opacity-20 w-full h-full z-20 text-center" - > - <h1 className="text-[12px] lg:text-sm pt-28 lg:pt-44 font-bold opacity-100"> - {checkProgress(progress)} - </h1> - </div> - )} - {anime.nextAiringEpisode && ( - <div className="flex gap-1 text-[13px] lg:text-base"> - <h1> - Episode {anime.nextAiringEpisode.episode} in + + {ids !== "recentlyWatched" + ? slicedData?.map((anime) => { + const progress = og?.find((i) => i.mediaId === anime.id); + + return ( + <div + key={anime.id} + className="flex flex-col gap-3 shrink-0 cursor-pointer" + > + <Link + href={`/${lang}/anime/${anime.id}`} + className="hover:scale-105 hover:shadow-lg duration-300 ease-out group relative" + title={anime.title.romaji} + > + {ids === "onGoing" && ( + <div className="h-[190px] lg:h-[265px] w-[135px] lg:w-[185px] bg-gradient-to-b from-transparent to-black absolute z-40 rounded-md whitespace-normal font-karla group"> + <div className="flex flex-col items-center h-full justify-end text-center pb-5"> + <h1 className="line-clamp-1 w-[70%] text-[10px]"> + {anime.title.romaji || anime.title.english} </h1> - <h1 className="font-bold"> - {convertSecondsToTime( - anime?.nextAiringEpisode?.timeUntilAiring + {checkProgress(progress) && + !clicked?.hasOwnProperty(anime.id) && ( + <ExclamationCircleIcon className="w-7 h-7 absolute z-40 -top-3 -right-3" /> )} - </h1> + {checkProgress(progress) && ( + <div + onClick={() => handleAlert(anime.id)} + className="group-hover:visible invisible absolute top-0 bg-black bg-opacity-20 w-full h-full z-20 text-center" + > + <h1 className="text-[12px] lg:text-sm pt-28 lg:pt-44 font-bold opacity-100"> + {checkProgress(progress)} + </h1> + </div> + )} + {anime.nextAiringEpisode && ( + <div className="flex gap-1 text-[13px] lg:text-base"> + <h1> + Episode {anime.nextAiringEpisode.episode} in + </h1> + <h1 className="font-bold"> + {convertSecondsToTime( + anime?.nextAiringEpisode?.timeUntilAiring + )} + </h1> + </div> + )} </div> + </div> + )} + <Image + draggable={false} + src={ + anime.image || + anime.coverImage?.extraLarge || + anime.coverImage?.large || + "https://cdn.discordapp.com/attachments/986579286397964290/1058415946945003611/gray_pfp.png" + } + alt={ + anime.title.romaji || + anime.title.english || + "coverImage" + } + width={500} + height={300} + placeholder="blur" + blurDataURL={ + anime.image || + anime.coverImage?.extraLarge || + anime.coverImage?.large || + "https://cdn.discordapp.com/attachments/986579286397964290/1058415946945003611/gray_pfp.png" + } + className="z-20 h-[190px] w-[135px] lg:h-[265px] lg:w-[185px] object-cover rounded-md brightness-90" + /> + </Link> + {ids !== "onGoing" && ( + <Link + href={`/en/anime/${anime.id}`} + className="w-[135px] lg:w-[185px] line-clamp-2" + title={anime.title.romaji} + > + <h1 className="font-karla font-semibold xl:text-base text-[15px]"> + {anime.status === "RELEASING" ? ( + <span className="dots bg-green-500" /> + ) : anime.status === "NOT_YET_RELEASED" ? ( + <span className="dots bg-red-500" /> + ) : null} + {anime.title.romaji} + </h1> + </Link> + )} + </div> + ); + }) + : userData + ?.filter((i) => i.title && i.title !== null) + ?.slice(0, 10) + .map((i) => { + const time = i.timeWatched; + const duration = i.duration; + let prog = (time / duration) * 100; + if (prog > 90) prog = 100; + + return ( + <Link + key={i.watchId} + className="flex flex-col gap-2 shrink-0 cursor-pointer" + href={`/en/anime/watch/${i.aniId}/${ + i.provider + }?id=${encodeURIComponent(i.watchId)}&num=${i.episode}`} + > + <div className="relative w-[320px] aspect-video rounded-md overflow-hidden group"> + <div className="w-full h-full bg-gradient-to-t from-black/70 from-20% to-transparent group-hover:to-black/40 transition-all duration-300 ease-out absolute z-30" /> + <div className="absolute bottom-3 left-0 mx-2 text-white flex gap-2 items-center w-[80%] z-30"> + <PlayIcon className="w-5 h-5 shrink-0" /> + <h1 + className="font-semibold font-karla line-clamp-1" + title={i?.title || i.anititle} + > + {i?.title === i.aniTitle + ? `Episode ${i.episode}` + : i?.title || i.anititle} + </h1> + </div> + <span + className={`absolute bottom-0 left-0 h-[2px] bg-red-600 z-30`} + style={{ + width: `${prog}%`, + }} + /> + {i?.image && ( + <Image + src={i?.image} + width={200} + height={200} + alt="Episode Thumbnail" + className="w-fit group-hover:scale-[1.02] duration-300 ease-out z-10" + /> )} </div> - </div> - )} - <Image - draggable={false} - src={ - anime.image || - anime.coverImage?.extraLarge || - anime.coverImage?.large || - "https://cdn.discordapp.com/attachments/986579286397964290/1058415946945003611/gray_pfp.png" - } - alt={ - anime.title.romaji || anime.title.english || "coverImage" - } - width={500} - height={300} - placeholder="blur" - blurDataURL={ - anime.image || - anime.coverImage?.extraLarge || - anime.coverImage?.large || - "https://cdn.discordapp.com/attachments/986579286397964290/1058415946945003611/gray_pfp.png" - } - className="z-20 h-[190px] w-[135px] lg:h-[265px] lg:w-[185px] object-cover rounded-md brightness-90" - /> - </Link> - {ids !== "onGoing" && ( - <Link - href={`/en/anime/${anime.id}`} - className="w-[135px] lg:w-[185px] line-clamp-2" - > - <h1 className="font-karla font-semibold xl:text-base text-[15px]"> - {anime.status === "RELEASING" ? ( - <span className="dots bg-green-500" /> - ) : anime.status === "NOT_YET_RELEASED" ? ( - <span className="dots bg-red-500" /> - ) : null} - {anime.title.romaji} - </h1> - </Link> - )} + + <div className="flex flex-col font-karla w-full"> + {/* <h1 className="font-semibold">{i.title}</h1> */} + <p className="flex items-center gap-1 text-sm text-gray-400 w-[320px]"> + <span + className="text-white" + style={{ + display: "inline-block", + maxWidth: "220px", + overflow: "hidden", + textOverflow: "ellipsis", + whiteSpace: "nowrap", + }} + title={i.aniTitle} + > + {i.aniTitle} + </span>{" "} + | Episode {i.episode} + </p> + </div> + </Link> + ); + })} + {userData?.length >= 10 && section !== "Recommendations" && ( + <div + key={section} + className="flex cursor-pointer" + onClick={goToPage} + > + <div className="w-[320px] aspect-video overflow-hidden object-cover rounded-md border-secondary border-2 flex flex-col gap-2 items-center text-center justify-center text-[#6a6a6a] hover:text-[#9f9f9f] hover:border-[#757575] transition-colors duration-200"> + <h1 className="whitespace-pre-wrap text-sm"> + More on {section} + </h1> + <ArrowRightCircleIcon className="w-5 h-5" /> </div> - ); - })} - {filteredData.length >= 10 && section !== "Recommendations" && ( + </div> + )} + {filteredData?.length >= 10 && section !== "Recommendations" && ( <div key={section} className="flex cursor-pointer" diff --git a/components/home/schedule.js b/components/home/schedule.js index 187fa17..73c63f0 100644 --- a/components/home/schedule.js +++ b/components/home/schedule.js @@ -37,6 +37,8 @@ export default function Schedule({ data, scheduleData, time }) { setCurrentPage(todayIndex >= 0 ? todayIndex : 0); }, [currentDay, days]); + // console.log({ scheduleData }); + return ( <div className="flex flex-col gap-5 px-4 lg:px-0"> <h1 className="font-bold font-karla text-[20px] lg:px-5"> diff --git a/components/listEditor.js b/components/listEditor.js index 49aa38e..fa249e3 100644 --- a/components/listEditor.js +++ b/components/listEditor.js @@ -1,11 +1,22 @@ import { useState } from "react"; import Image from "next/image"; import { toast } from "react-toastify"; +import { useRouter } from "next/router"; -const ListEditor = ({ animeId, session, stats, prg, max, image = null }) => { +const ListEditor = ({ + animeId, + session, + stats, + prg, + max, + image = null, + close, +}) => { const [status, setStatus] = useState(stats ?? "CURRENT"); const [progress, setProgress] = useState(prg ?? 0); + const router = useRouter(); + const handleSubmit = async (e) => { e.preventDefault(); console.log("Submitting", status?.name, progress); @@ -57,9 +68,11 @@ const ListEditor = ({ animeId, session, stats, prg, max, image = null }) => { draggable: true, theme: "dark", }); + close(); setTimeout(() => { - window.location.reload(); - }, 3000); + // window.location.reload(); + router.reload(); + }, 1000); // showAlert("Media list entry saved", "success"); } catch (error) { toast.error("Something went wrong", { diff --git a/components/videoPlayer.js b/components/videoPlayer.js index 3709fd0..8f08fd3 100644 --- a/components/videoPlayer.js +++ b/components/videoPlayer.js @@ -2,6 +2,7 @@ import Player from "../lib/Artplayer"; import { useEffect, useState } from "react"; import { useAniList } from "../lib/anilist/useAnilist"; import artplayerPluginHlsQuality from "artplayer-plugin-hls-quality"; +import { useRouter } from "next/router"; const fontSize = [ { @@ -31,17 +32,23 @@ export default function VideoPlayer({ proxy, provider, track, + aniTitle, + timeWatched, }) { const [url, setUrl] = useState(""); const [source, setSource] = useState([]); const { markProgress } = useAniList(session); + const router = useRouter(); + const [resolution, setResolution] = useState("auto"); const [subSize, setSubSize] = useState({ size: "16px", html: "Small" }); const [defSize, setDefSize] = useState(); const [subtitle, setSubtitle] = useState(); const [defSub, setDefSub] = useState(); + const [autoPlay, setAutoPlay] = useState(false); + useEffect(() => { const resol = localStorage.getItem("quality"); const sub = JSON.parse(localStorage.getItem("subSize")); @@ -127,7 +134,7 @@ export default function VideoPlayer({ option={{ url: `${url}`, title: `${title}`, - autoplay: true, + autoplay: false, screenshot: true, moreVideoAttr: { crossOrigin: "anonymous", @@ -136,9 +143,6 @@ export default function VideoPlayer({ ...(provider !== "gogoanime" && { plugins: [ artplayerPluginHlsQuality({ - // Show quality in control - // control: true, - // Show quality in setting setting: true, @@ -179,6 +183,8 @@ export default function VideoPlayer({ subtitles={subtitle} provider={provider} track={track} + autoplay={autoPlay} + setautoplay={setAutoPlay} style={{ width: "100%", height: "100%", @@ -186,19 +192,21 @@ export default function VideoPlayer({ }} getInstance={(art) => { art.on("ready", () => { - // console.log(art.storage.settings); const seek = art.storage.get(id); - const seekTime = seek?.time || 0; + const seekTime = seek?.timeWatched || 0; const duration = art.duration; const percentage = seekTime / duration; + const percentagedb = timeWatched / duration; if (subSize) { art.subtitle.style.fontSize = subSize?.size; } - if (percentage >= 0.9) { + if (percentage >= 0.9 || percentagedb >= 0.9) { art.currentTime = 0; console.log("Video started from the beginning"); + } else if (timeWatched) { + art.currentTime = timeWatched; } else { art.currentTime = seekTime; } @@ -206,36 +214,129 @@ export default function VideoPlayer({ let marked = 0; - art.on("video:timeupdate", () => { + art.on("video:playing", () => { if (!session) return; - const mediaSession = navigator.mediaSession; - const currentTime = art.currentTime; - const duration = art.duration; - const percentage = currentTime / duration; + const intervalId = setInterval(async () => { + const resp = await fetch("/api/user/update/episode", { + method: "PUT", + body: JSON.stringify({ + name: session?.user?.name, + id: String(aniId), + watchId: id, + title: track?.playing?.title || aniTitle, + aniTitle: aniTitle, + image: track?.playing?.image, + number: track?.playing?.number, + duration: art.duration, + timeWatched: art.currentTime, + provider: provider, + }), + }); + // console.log("updating db"); + }, 5000); + + art.on("video:pause", () => { + clearInterval(intervalId); + }); + + art.on("video:ended", () => { + clearInterval(intervalId); + }); + + art.on("destroy", () => { + clearInterval(intervalId); + // console.log("clearing interval"); + }); + }); + + art.on("video:playing", () => { + const interval = setInterval(async () => { + art.storage.set(id, { + aniId: String(aniId), + watchId: id, + title: track?.playing?.title || aniTitle, + aniTitle: aniTitle, + image: track?.playing?.image, + episode: track?.playing?.number, + duration: art.duration, + timeWatched: art.currentTime, + provider: provider, + createdAt: new Date().toISOString(), + }); + }, 5000); + + art.on("video:pause", () => { + clearInterval(interval); + }); + + art.on("video:ended", () => { + clearInterval(interval); + }); - mediaSession.setPositionState({ - duration: art.duration, - playbackRate: art.playbackRate, - position: art.currentTime, + art.on("destroy", () => { + clearInterval(interval); }); + }); + + art.on("video:timeupdate", async () => { + if (!session) return; + + var currentTime = art.currentTime; + const duration = art.duration; + const percentage = currentTime / duration; if (percentage >= 0.9) { // use >= instead of > if (marked < 1) { marked = 1; markProgress(aniId, progress, stats); - // console.log("Video progress marked"); } } }); + art.on("video:ended", () => { + if (!track?.next) return; + if (localStorage.getItem("autoplay") === "true") { + art.controls.add({ + name: "next-button", + position: "top", + html: '<div class="vid-con"><button class="next-button progress">Play Next</button></div>', + click: function (...args) { + if (track?.next) { + router.push( + `/en/anime/watch/${aniId}/${provider}?id=${encodeURIComponent( + track?.next?.id + )}&num=${track?.next?.number}` + ); + } + }, + }); + + const button = document.querySelector(".next-button"); + + function stopTimeout() { + clearTimeout(timeoutId); + button.classList.remove("progress"); + } + + let timeoutId = setTimeout(() => { + art.controls.remove("next-button"); + if (track?.next) { + router.push( + `/en/anime/watch/${aniId}/${provider}?id=${encodeURIComponent( + track?.next?.id + )}&num=${track?.next?.number}` + ); + } + }, 7000); + + button.addEventListener("mouseover", stopTimeout); + } + }); + art.on("video:timeupdate", () => { var currentTime = art.currentTime; // console.log(art.currentTime); - art.storage.set(id, { - time: art.currentTime, - duration: art.duration, - }); if ( skip?.op && |