aboutsummaryrefslogtreecommitdiff
path: root/lib/anilist
diff options
context:
space:
mode:
authorFactiven <[email protected]>2023-09-13 00:45:53 +0700
committerGitHub <[email protected]>2023-09-13 00:45:53 +0700
commit7327a69b55a20b99b14ee0803d6cf5f8b88c45ef (patch)
treecbcca777593a8cc4b0282e7d85a6fc51ba517e25 /lib/anilist
parentUpdate issue templates (diff)
downloadmoopa-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.js2
-rw-r--r--lib/anilist/aniAdvanceSearch.js92
-rw-r--r--lib/anilist/getMedia.js90
-rw-r--r--lib/anilist/getUpcomingAnime.js52
-rw-r--r--lib/anilist/useAnilist.js250
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,
+ };
};