Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Related Anime, Bug fixes, and Omelette's request #140

Merged
merged 24 commits into from
Sep 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 72 additions & 7 deletions src/modules/anilist/anilistApi.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import { app, ipcRenderer } from 'electron';
import { app } from 'electron';
import Store from 'electron-store';

import {
AiringPage,
AiringScheduleData,
AnimeData,
CurrentListAnime,
ListAnimeData,
MostPopularAnime,
TrendingAnime,
} from '../../types/anilistAPITypes';
import { MediaListStatus } from '../../types/anilistGraphQLTypes';
import { AiringPage, AiringSchedule, Media, MediaListStatus} from '../../types/anilistGraphQLTypes';
import { ClientData } from '../../types/types';
import { clientData } from '../clientData';
import isAppImage from '../packaging/isAppImage';
import { getOptions, makeRequest } from '../requests';


const STORE: any = new Store();
const CLIENT_DATA: ClientData = clientData;
const PAGES: number = 20;
Expand All @@ -28,6 +27,7 @@ const HEADERS: Object = {
const MEDIA_DATA: string = `
id
idMal
type
title {
romaji
english
Expand Down Expand Up @@ -82,6 +82,71 @@ const MEDIA_DATA: string = `
site
thumbnail
}
relations {
edges {
id
relationType(version: 2)
node {
id
idMal
type
title {
romaji
english
native
userPreferred
}
format
status
description
startDate {
year
month
day
}
endDate {
year
month
day
}
season
seasonYear
episodes
duration
coverImage {
large
extraLarge
color
}
bannerImage
genres
synonyms
averageScore
meanScore
popularity
favourites
isAdult
nextAiringEpisode {
id
timeUntilAiring
episode
}
mediaListEntry {
id
mediaId
status
score(format:POINT_10)
progress
}
siteUrl
trailer {
id
site
thumbnail
}
}
}
}
`;

/**
Expand Down Expand Up @@ -313,7 +378,7 @@ export const getAnimesFromTitles = async (titles: string[]) => {
* @param {*} animeId
* @returns object with anime info
*/
export const getAnimeInfo = async (animeId: any) => {
export const getAnimeInfo = async (animeId: any): Promise<Media> => {
var query = `
query($id: Int) {
Media(id: $id, type: ANIME) {
Expand All @@ -335,7 +400,7 @@ export const getAnimeInfo = async (animeId: any) => {
const options = getOptions(query, variables);
const respData = await makeRequest(METHOD, GRAPH_QL_URL, headers, options);

return respData.data.Media;
return respData.data.Media as Media;
};

/**
Expand Down Expand Up @@ -441,7 +506,7 @@ export const getAiringSchedule = async (
const options = getOptions(query);
const respData = await makeRequest(METHOD, GRAPH_QL_URL, headers, options);

return respData.data.Page.airingSchedules as AiringScheduleData[];
return respData.data.Page.airingSchedules as AiringSchedule[];
};

/**
Expand Down
13 changes: 13 additions & 0 deletions src/modules/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ export const getHistoryEntries = (): HistoryEntries => history.entries;
*/
export const getHistory = (): History => history;

/**
* Get history entry for a specific episode
*
* @param animeId
* @param episodeNumber
* @returns history entry
*/
export const getEpisodeHistory = (
animeId: number,
episodeNumber: number
): EpisodeHistoryEntry | undefined => getAnimeHistory(animeId)?.history[episodeNumber]


/**
* Set local history.
*
Expand Down
63 changes: 62 additions & 1 deletion src/modules/requests.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,35 @@
import axios from 'axios';

var remainingRequests = 90;
var resetTime = 0;
var lockUntil = 0;

const delay = async (seconds: number) => new Promise((resolve) => setTimeout(resolve, seconds * 1000));

const handleRateLimiting = async (current: number) => {
if (current < lockUntil) {
await delay(lockUntil - current);
}

if (current >= resetTime) {
remainingRequests = 90;
}

if (remainingRequests <= 0) {
await delay(60);
}
};

const handleResponseHeaders = (headers: any) => {
if (headers['x-ratelimit-remaining']) {
remainingRequests = parseInt(headers['x-ratelimit-remaining']);
}

if (headers['x-ratelimit-reset']) {
resetTime = parseInt(headers['x-ratelimit-reset']);
}
};

/**
* Builds the data options for the request
*
Expand All @@ -24,12 +54,43 @@ export const getOptions = (query: any = {}, variables: any = {}) => {
* @returns object with the fetched data
* @throws error if the request was not successful
*/

export const makeRequest = async (
method: 'GET' | 'POST' | string,
url: string,
headers: any = {},
options: any = {},
) => {
): Promise<any> => {
if (url === 'https://graphql.anilist.co') {
const current = Date.now() / 1000;

await handleRateLimiting(current);

try {
const response = await axios({
method: method,
url: url,
headers: headers,
data: options,
});

handleResponseHeaders(response.headers);

return response.data;
} catch (error) {
let response = (error as { response?: { status: number, headers: { [key: string]: any } } }).response;

if (response && response.status === 429) {
const retryAfter = parseInt(response.headers['retry-after'] || '60', 10);
lockUntil = current + retryAfter;
await delay(retryAfter);
return makeRequest(method, url, headers, options);
}

throw error;
}
}

const response = await axios({
method: method,
url: url,
Expand Down
1 change: 1 addition & 0 deletions src/modules/storeVariables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const defaultValues = {
dubbed: false,
source_flag: 'US',
intro_skip_time: 85,
key_press_skip: 5,
show_duration: true,
trailer_volume_on: false,
volume: 1,
Expand Down
89 changes: 80 additions & 9 deletions src/modules/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AiringScheduleData, AnimeData, ListAnimeData } from '../types/anilistAPITypes';
import { Media, MediaFormat, MediaStatus } from '../types/anilistGraphQLTypes';
import { getLastWatchedEpisode } from './history';
import { AnimeData, ListAnimeData } from '../types/anilistAPITypes';
import { AiringSchedule, Media, MediaFormat, MediaStatus, MediaTypes, Relation, RelationType, RelationTypes } from '../types/anilistGraphQLTypes';
import { getAnimeHistory, getEpisodeHistory, getLastWatchedEpisode } from './history';

const MONTHS = {
'1': 'January',
Expand Down Expand Up @@ -46,18 +46,31 @@ export const getRandomDiscordPhrase = (): string =>
DISCORD_PHRASES[Math.floor(Math.random() * DISCORD_PHRASES.length)];

export const airingDataToListAnimeData = (
airingScheduleData: AiringScheduleData[]
airingScheduleData: AiringSchedule[]
): ListAnimeData[] => {
return airingScheduleData.map((value) => {
return {
id: null,
mediaId: null,
progress: null,
media: value.media
media: value.media as Media
};
});
};

export const relationsToListAnimeData = (
relations: Relation[]
): ListAnimeData[] => {
return relations.map((value) => {
return {
id: null,
mediaId: null,
progress: null,
media: value.node
}
})
}

export const animeDataToListAnimeData = (
animeData: AnimeData,
): ListAnimeData[] => {
Expand Down Expand Up @@ -129,6 +142,43 @@ export const getEpisodes = (animeEntry: Media): number | null =>
: animeEntry.nextAiringEpisode.episode - 1
: animeEntry.episodes;

/**
* Get the sequel from the media.
*
* @param {*} animeEntry
* @returns sequel
*/
export const getSequel = (animeEntry: Media): Media | null => {
const relations = animeEntry.relations;
if (!relations) return null

for(const relation of relations.edges) {
const sequel = relation.relationType === RelationTypes.Sequel &&
relation.node.type === MediaTypes.Anime &&
relation.node;
if (!sequel)
continue;

return sequel;
}

return null;
// const sequel: Relation = relations.edges.reduce((previous, value) => {
// const media = value.node;

// console.log(value.relationType === RelationTypes.Sequel, media.type)

// return value.relationType === RelationTypes.Sequel &&
// media.type === MediaTypes.Anime &&
// value || previous;
// });

// if (sequel.relationType !== "SEQUEL" && sequel.node.type !== MediaTypes.Anime)
// return null

// return sequel.node;
}

/**
* Gets the anime available episodes number from 'episodes' or 'nextAiringEpisode'
*
Expand Down Expand Up @@ -179,12 +229,27 @@ export const getProgress = (animeEntry: Media): number | undefined => {
const animeId = (animeEntry.id || animeEntry?.mediaListEntry?.id) as number;
const lastWatched = getLastWatchedEpisode(animeId);

const anilistProgress = (animeEntry.mediaListEntry === null ? 0 : animeEntry?.mediaListEntry?.progress) as number;

if(lastWatched !== undefined && lastWatched.data !== undefined) {
const progress = (lastWatched.data.episodeNumber as number) - 1;
return Number.isNaN(progress) ? 0 : progress;
let isFinished = (lastWatched.duration as number * 0.85) > lastWatched.time;
const localProgress = (parseInt(lastWatched.data.episode ?? "0")) - (isFinished ? 1 : 0);
console.log(anilistProgress, localProgress)
if(anilistProgress !== localProgress) {
const episodeEntry = getEpisodeHistory(animeId, anilistProgress);

if(episodeEntry) {
isFinished = (episodeEntry.duration as number * 0.85) > episodeEntry.time;
return anilistProgress - (isFinished ? 1 : 0);
}

return anilistProgress - 1;
}

return Number.isNaN(localProgress) ? 0 : localProgress;
}

return animeEntry.mediaListEntry == null ? 0 : animeEntry.mediaListEntry.progress;
return anilistProgress;
}

/**
Expand Down Expand Up @@ -286,7 +351,7 @@ export const getParsedStatus = (status: MediaStatus | undefined) => {
* @param {*} status
* @returns
*/
export const getParsedFormat = (format: MediaFormat | undefined) => {
export const getParsedFormat = (format: MediaFormat | RelationType | undefined) => {
switch (format) {
case 'TV':
return 'TV Show';
Expand All @@ -302,6 +367,12 @@ export const getParsedFormat = (format: MediaFormat | undefined) => {
return 'ONA';
case 'MUSIC':
return 'Music';
case 'SEQUEL':
return 'Sequel';
case 'PREQUEL':
return 'Prequel';
case 'ALTERNATIVE':
return 'Alternative';
default:
return '?';
}
Expand Down
Loading