diff options
| author | Factiven <[email protected]> | 2023-09-13 00:45:53 +0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-09-13 00:45:53 +0700 |
| commit | 7327a69b55a20b99b14ee0803d6cf5f8b88c45ef (patch) | |
| tree | cbcca777593a8cc4b0282e7d85a6fc51ba517e25 /lib/anilist | |
| parent | Update issue templates (diff) | |
| download | moopa-7327a69b55a20b99b14ee0803d6cf5f8b88c45ef.tar.xz moopa-7327a69b55a20b99b14ee0803d6cf5f8b88c45ef.zip | |
Update v4 - Merge pre-push to main (#71)
* Create build-test.yml
* initial v4 commit
* update: github workflow
* update: push on branch
* Update .github/ISSUE_TEMPLATE/bug_report.md
* configuring next.config.js file
Diffstat (limited to 'lib/anilist')
| -rw-r--r-- | lib/anilist/AniList.js | 2 | ||||
| -rw-r--r-- | lib/anilist/aniAdvanceSearch.js | 92 | ||||
| -rw-r--r-- | lib/anilist/getMedia.js | 90 | ||||
| -rw-r--r-- | lib/anilist/getUpcomingAnime.js | 52 | ||||
| -rw-r--r-- | lib/anilist/useAnilist.js | 250 |
5 files changed, 331 insertions, 155 deletions
diff --git a/lib/anilist/AniList.js b/lib/anilist/AniList.js index f5fe19c..b8d6ed3 100644 --- a/lib/anilist/AniList.js +++ b/lib/anilist/AniList.js @@ -29,8 +29,10 @@ export async function aniListData({ sort, page = 1 }) { romaji english } + bannerImage coverImage { extraLarge + color } description } diff --git a/lib/anilist/aniAdvanceSearch.js b/lib/anilist/aniAdvanceSearch.js index 263ca9d..02a5c53 100644 --- a/lib/anilist/aniAdvanceSearch.js +++ b/lib/anilist/aniAdvanceSearch.js @@ -1,63 +1,53 @@ -const advance = ` - query ($search: String, $type: MediaType, $status: MediaStatus, $season: MediaSeason, $seasonYear: Int, $genres: [String], $tags: [String], $sort: [MediaSort], $page: Int, $perPage: Int) { - Page (page: $page, perPage: $perPage) { - pageInfo { - total - currentPage - lastPage - hasNextPage - } - media (search: $search, type: $type, status: $status, season: $season, seasonYear: $seasonYear, genre_in: $genres, tag_in: $tags, sort: $sort, isAdult: false) { - id - title { - userPreferred - } - type - episodes - chapters - status - format - season - seasonYear - coverImage { - extraLarge - color - } - averageScore - isAdult - } +import { advanceSearchQuery } from "../graphql/query"; + +export async function aniAdvanceSearch({ + search, + type, + genres, + page, + sort, + format, + season, + seasonYear, + perPage, +}) { + const categorizedGenres = genres?.reduce((result, item) => { + const existingEntry = result[item.type]; + + if (existingEntry) { + existingEntry.push(item.value); + } else { + result[item.type] = [item.value]; } - } -`; -export async function aniAdvanceSearch(options = {}) { - const { - search = null, - type = "ANIME", - seasonYear = NaN, - season = undefined, - genres = null, - page = 1, - perPage = null, - sort = "POPULARITY_DESC", - } = options; - // console.log(page); + return result; + }, {}); + const response = await fetch("https://graphql.anilist.co/", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ - query: advance, + query: advanceSearchQuery, variables: { - search: search, - type: type, - seasonYear: seasonYear, - season: season, - genres: genres, - perPage: perPage, - sort: sort, - page: page, + ...(search && { + search: search, + ...(!sort && { sort: "SEARCH_MATCH" }), + }), + ...(type && { type: type }), + ...(seasonYear && { seasonYear: seasonYear }), + ...(season && { + season: season, + ...(!seasonYear && { seasonYear: new Date().getFullYear() }), + }), + ...(categorizedGenres && { ...categorizedGenres }), + ...(format && { format: format }), + // ...(genres && { genres: genres }), + // ...(tags && { tags: tags }), + ...(perPage && { perPage: perPage }), + ...(sort && { sort: sort }), + ...(page && { page: page }), }, }), }); diff --git a/lib/anilist/getMedia.js b/lib/anilist/getMedia.js new file mode 100644 index 0000000..c4628ab --- /dev/null +++ b/lib/anilist/getMedia.js @@ -0,0 +1,90 @@ +import { useEffect, useState } from "react"; + +export default function GetMedia(session, stats) { + const [media, setMedia] = useState([]); + const [recommendations, setRecommendations] = useState([]); + const accessToken = session?.user?.token; + const username = session?.user?.name; + const status = stats || null; + + const fetchGraphQL = async (query, variables) => { + const response = await fetch("https://graphql.anilist.co/", { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: accessToken ? `Bearer ${accessToken}` : undefined, + }, + body: JSON.stringify({ query, variables }), + }); + return response.json(); + }; + + useEffect(() => { + if (!username || !accessToken) return; + const queryMedia = ` + query ($username: String, $status: MediaListStatus, $sort: [RecommendationSort]) { + Page(page: 1, perPage: 10) { + recommendations(sort: $sort, onList: true) { + mediaRecommendation { + id + title { + userPreferred + } + description + format + type + status(version: 2) + bannerImage + isAdult + coverImage { + extraLarge + } + } + } + } + MediaListCollection(userName: $username, type: ANIME, status: $status, sort: UPDATED_TIME_DESC) { + lists { + status + name + entries { + id + mediaId + status + progress + score + media { + id + status + nextAiringEpisode { + timeUntilAiring + episode + } + title { + english + romaji + } + episodes + coverImage { + large + } + } + } + } + } +} + + `; + fetchGraphQL(queryMedia, { + username, + status: status?.stats, + sort: "ID_DESC", + }).then((data) => { + setMedia(data.data.MediaListCollection.lists); + setRecommendations( + data.data.Page.recommendations.map((i) => i.mediaRecommendation) + ); + }); + }, [username, accessToken, status?.stats]); + + return { media, recommendations }; +} diff --git a/lib/anilist/getUpcomingAnime.js b/lib/anilist/getUpcomingAnime.js index fc848fd..2ab9315 100644 --- a/lib/anilist/getUpcomingAnime.js +++ b/lib/anilist/getUpcomingAnime.js @@ -19,23 +19,39 @@ const getUpcomingAnime = async () => { } const query = ` - query ($season: MediaSeason, $seasonYear: Int) { - Page(page: 1, perPage: 20) { - media(season: $season, seasonYear: $seasonYear, sort: POPULARITY_DESC, type: ANIME) { + query ($season: MediaSeason, $year: Int, $format: MediaFormat, $excludeFormat: MediaFormat, $status: MediaStatus, $minEpisodes: Int, $page: Int) { + Page(page: $page) { + pageInfo { + hasNextPage + total + } + media(season: $season, seasonYear: $year, format: $format, format_not: $excludeFormat, status: $status, episodes_greater: $minEpisodes, isAdult: false, type: ANIME, sort: TITLE_ROMAJI) { id - coverImage{ - large - } - bannerImage + idMal title { - english romaji native + english + } + startDate { + year + month + day + } + status + season + format + description + bannerImage + coverImage { + extraLarge + color } - nextAiringEpisode { - episode - airingAt - timeUntilAiring + airingSchedule(notYetAired: true, perPage: 1) { + nodes { + episode + airingAt + } } } } @@ -43,8 +59,9 @@ const getUpcomingAnime = async () => { `; const variables = { - season: currentSeason, - seasonYear: currentYear, + season: "FALL", + year: currentYear, + format: "TV", }; let response = await fetch("https://graphql.anilist.co", { @@ -63,13 +80,14 @@ const getUpcomingAnime = async () => { let currentSeasonAnime = json.data.Page.media; let nextAiringAnime = currentSeasonAnime.filter( - (anime) => - anime.nextAiringEpisode !== null && anime.nextAiringEpisode.episode === 1 + (anime) => anime.airingSchedule.nodes?.[0]?.episode === 1 ); if (nextAiringAnime.length >= 1) { nextAiringAnime.sort( - (a, b) => a.nextAiringEpisode.airingAt - b.nextAiringEpisode.airingAt + (a, b) => + a.airingSchedule.nodes?.[0].airingAt - + b.airingSchedule.nodes?.[0].airingAt ); return nextAiringAnime; // return all upcoming anime, not just the first two } diff --git a/lib/anilist/useAnilist.js b/lib/anilist/useAnilist.js index 72e11ca..17ab11b 100644 --- a/lib/anilist/useAnilist.js +++ b/lib/anilist/useAnilist.js @@ -1,63 +1,107 @@ -import { useState, useEffect } from "react"; import { toast } from "react-toastify"; -export const useAniList = (session, stats) => { - const [media, setMedia] = useState([]); +export const useAniList = (session) => { const accessToken = session?.user?.token; - const username = session?.user?.name; - const status = stats || null; const fetchGraphQL = async (query, variables) => { const response = await fetch("https://graphql.anilist.co/", { method: "POST", headers: { "Content-Type": "application/json", - Authorization: accessToken ? `Bearer ${accessToken}` : undefined, + ...(accessToken && { Authorization: `Bearer ${accessToken}` }), }, body: JSON.stringify({ query, variables }), }); return response.json(); }; - useEffect(() => { - if (!username || !accessToken) return; - const queryMedia = ` - query ($username: String, $status: MediaListStatus) { - MediaListCollection(userName: $username, type: ANIME, status: $status) { - lists { - status - name - entries { - id - mediaId - status - progress - score - media { - id - status - nextAiringEpisode { - timeUntilAiring - episode - } - title { - english - romaji - } - episodes - coverImage { - large - } - } - } - } - } + const quickSearch = async ({ search, type, isAdult = false }) => { + if (!search || search === " ") return; + const searchQuery = ` + query ($type: MediaType, $search: String, $isAdult: Boolean) { + Page(perPage: 8) { + pageInfo { + total + hasNextPage + } + results: media(type: $type, isAdult: $isAdult, search: $search) { + id + title { + userPreferred + } + coverImage { + medium } + type + format + bannerImage + isLicensed + genres + startDate { + year + } + } + } +} `; - fetchGraphQL(queryMedia, { username, status: status?.stats }).then((data) => - setMedia(data.data.MediaListCollection.lists) - ); - }, [username, accessToken, status?.stats]); + const data = await fetchGraphQL(searchQuery, { search, type, isAdult }); + return data; + }; + + const multiSearch = async (search) => { + if (!search || search === " ") return; + const searchQuery = ` + query ($search: String, $isAdult: Boolean) { + anime: Page(perPage: 8) { + pageInfo { + total + hasNextPage + } + results: media(type: ANIME, isAdult: $isAdult, search: $search) { + id + title { + userPreferred + } + coverImage { + medium + } + type + format + bannerImage + isLicensed + genres + startDate { + year + } + } + } + manga: Page(perPage: 8) { + pageInfo { + total + hasNextPage + } + results: media(type: MANGA, isAdult: $isAdult, search: $search) { + id + title { + userPreferred + } + coverImage { + medium + } + type + format + bannerImage + isLicensed + startDate { + year + } + } + } +} +`; + const data = await fetchGraphQL(searchQuery, { search }); + return data; + }; const markComplete = async (mediaId) => { if (!accessToken) return; @@ -94,7 +138,10 @@ export const useAniList = (session, stats) => { query ($id: Int) { Media(id: $id) { mediaListEntry { + progress + status customLists + repeat } id type @@ -103,6 +150,11 @@ export const useAniList = (session, stats) => { english native } + format + episodes + nextAiringEpisode { + episode + } } } `; @@ -110,23 +162,11 @@ export const useAniList = (session, stats) => { return data; }; - const customLists = async (lists) => { - const setList = ` - mutation($lists: [String]){ - UpdateUser(animeListOptions: { customLists: $lists }){ - id - } - } - `; - const data = await fetchGraphQL(setList, { lists }); - return data; - }; - const markProgress = async (mediaId, progress, stats, volumeProgress) => { if (!accessToken) return; const progressWatched = ` - mutation($mediaId: Int, $progress: Int, $status: MediaListStatus, $progressVolumes: Int, $lists: [String]) { - SaveMediaListEntry(mediaId: $mediaId, progress: $progress, status: $status, progressVolumes: $progressVolumes, customLists: $lists) { + mutation($mediaId: Int, $progress: Int, $status: MediaListStatus, $progressVolumes: Int, $lists: [String], $repeat: Int) { + SaveMediaListEntry(mediaId: $mediaId, progress: $progress, status: $status, progressVolumes: $progressVolumes, customLists: $lists, repeat: $repeat) { id mediaId progress @@ -137,46 +177,82 @@ export const useAniList = (session, stats) => { const user = await getUserLists(mediaId); const media = user?.data?.Media; - if (media) { - let checkList = media?.mediaListEntry?.customLists - ? Object.entries(media?.mediaListEntry?.customLists).map( - ([key, value]) => key - ) || [] - : []; - if (!checkList?.includes("Watched using Moopa")) { - checkList.push("Watched using Moopa"); - await customLists(checkList); + if (media && media.type !== "MANGA") { + let customList; + + if (session.user.name) { + const res = await fetch( + `/api/user/profile?name=${session.user.name}` + ).then((res) => res.json()); + customList = res?.setting === null ? true : res?.setting?.CustomLists; } - let lists = media?.mediaListEntry?.customLists - ? Object.entries(media?.mediaListEntry?.customLists) + let lists = media.mediaListEntry?.customLists + ? Object.entries(media.mediaListEntry?.customLists) .filter(([key, value]) => value === true) .map(([key, value]) => key) || [] : []; - if (!lists?.includes("Watched using Moopa")) { + + if (customList === true && !lists?.includes("Watched using Moopa")) { lists.push("Watched using Moopa"); } - if (lists.length > 0) { - await fetchGraphQL(progressWatched, { - mediaId, - progress, - status: stats, - progressVolumes: volumeProgress, - lists, - }); - console.log(`Progress Updated: ${progress}`); - toast.success(`Progress Updated: ${progress}`, { - position: "bottom-right", - autoClose: 5000, - hideProgressBar: false, - closeOnClick: true, - draggable: true, - theme: "dark", - }); + + const singleEpisode = + (!media.episodes || + (media.format === "MOVIE" && media.episodes === 1)) && + 1; + const videoEpisode = Number(progress) || singleEpisode; + const mediaEpisode = + media.nextAiringEpisode?.episode || media.episodes || singleEpisode; + const status = + media.mediaListEntry?.status === "REPEATING" ? "REPEATING" : "CURRENT"; + + let variables = { + mediaId, + progress, + status, + progressVolumes: volumeProgress, + lists, + }; + + if (videoEpisode === mediaEpisode) { + variables.status = "COMPLETED"; + if (media.mediaListEntry?.status === "REPEATING") + variables.repeat = media.mediaListEntry.repeat + 1; } + + // if (lists.length > 0) { + await fetchGraphQL(progressWatched, variables); + console.log(`Progress Updated: ${progress}`, status); + // } + } else if (media && media.type === "MANGA") { + let variables = { + mediaId, + progress, + status: stats, + progressVolumes: volumeProgress, + }; + + await fetchGraphQL(progressWatched, variables); + console.log(`Progress Updated: ${progress}`, status); + toast.success(`Progress Updated: ${progress}`, { + position: "bottom-right", + autoClose: 5000, + hideProgressBar: false, + closeOnClick: true, + draggable: true, + theme: "dark", + }); } }; - return { media, markComplete, markProgress, markPlanning, getUserLists }; + return { + markComplete, + markProgress, + markPlanning, + getUserLists, + multiSearch, + quickSearch, + }; }; |