From 54c109ae3b864f726ffe57b8806f490c184fbcfe Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Wed, 22 Jul 2026 15:22:58 -0400 Subject: [PATCH] rewrote standard media call and added browse function --- AniListFunctions.go | 909 ++++++++++++++++++++++++++++++++++---------- AniListTypes.go | 132 +++++-- 2 files changed, 804 insertions(+), 237 deletions(-) diff --git a/AniListFunctions.go b/AniListFunctions.go index a04cffe..c6cca03 100644 --- a/AniListFunctions.go +++ b/AniListFunctions.go @@ -74,37 +74,83 @@ func (a *App) GetAniListItem(aniId int, login bool) AniListGetSingleAnime { mediaId userId media { - id - idMal - title { - romaji - english - native - } - description - coverImage { - large - } - season - seasonYear - status - episodes - nextAiringEpisode { - airingAt - timeUntilAiring - episode - } - genres - tags{ - id - name - description - rank - isMediaSpoiler - isAdult - } - isAdult - } + id + idMal + title { + userPreferred + romaji + english + native + } + description + coverImage { + extraLarge + large + medium + color + } + startDate { + year + month + day + } + endDate { + year + month + day + } + bannerImage + format + season + seasonYear + status + episodes + duration + countryOfOrigin + source + synonyms + averageScore + meanScore + popularity + trending + favourites + isFavourite + relations { + nodes { + id + title { + userPreferred + romaji + english + native + } + } + } + nextAiringEpisode { + airingAt + timeUntilAiring + episode + } + airingSchedule { + nodes { + id + airingAt + timeUntilAiring + episode + mediaId + } + } + genres + tags { + id + name + description + rank + isMediaSpoiler + isAdult + } + isAdult + } status startedAt{ year @@ -204,37 +250,84 @@ func (a *App) AniListSearch(query string) (interface{}, error) { perPage } media (search: $search, type: $listType) { - id - idMal - title { + id + idMal + title { + userPreferred + romaji + english + native + } + description + coverImage { + extraLarge + large + medium + color + } + bannerImage + format + season + seasonYear + status + episodes + duration + countryOfOrigin + source + synonyms + averageScore + meanScore + popularity + trending + favourites + isFavourite + relations{ + nodes{ + id + title{ + userPreferred romaji english native } - description - coverImage { - large - } - season - seasonYear - status - episodes - nextAiringEpisode{ - airingAt - timeUntilAiring - episode - } - genres - tags{ - id - name - description - rank - isMediaSpoiler - isAdult - } - isAdult + } } + startDate{ + year + month + day + } + endDate{ + year + month + day + } + nextAiringEpisode{ + airingAt + timeUntilAiring + episode + } + airingSchedule{ + nodes{ + id + airingAt + timeUntilAiring + episode + mediaId + } + } + genres + tags{ + id + name + description + rank + isMediaSpoiler + isAdult + } + isAdult + } + } } `, @@ -271,93 +364,140 @@ func (a *App) GetAniListUserWatchingList(page int, perPage int, sort string) (An Variables Variables `json:"variables"` }{ Query: ` - query( - $page: Int - $perPage: Int - $userId: Int - $listType: MediaType - $status: MediaListStatus - $sort:[MediaListSort] - ) { - Page(page: $page, perPage: $perPage) { - pageInfo { - total - perPage - currentPage - lastPage - hasNextPage - } - mediaList(userId: $userId, type: $listType, status: $status, sort: $sort) { - id - mediaId - userId - media { - id + query ( + $page: Int + $perPage: Int + $userId: Int + $listType: MediaType + $status: MediaListStatus + $sort: [MediaListSort] +) { + Page(page: $page, perPage: $perPage) { + pageInfo { + total + perPage + currentPage + lastPage + hasNextPage + } + mediaList(userId: $userId, type: $listType, status: $status, sort: $sort) { + id + mediaId + userId + media { + id idMal title { + userPreferred romaji english native } description coverImage { + extraLarge large + medium + color } + bannerImage + format season seasonYear status episodes + duration + countryOfOrigin + source + synonyms + averageScore + meanScore + popularity + trending + favourites + isFavourite + relations{ + nodes{ + id + title{ + userPreferred + romaji + english + native + } + } + } + startDate{ + year + month + day + } + endDate{ + year + month + day + } nextAiringEpisode{ airingAt timeUntilAiring episode } - genres - tags{ - id - name - description - rank - isMediaSpoiler - isAdult - } - isAdult - } - status - startedAt { - year - month - day - } - completedAt { - year - month - day - } - notes - progress - score - repeat - user { - id - name - avatar{ - large - medium - } - statistics { - anime { - count - statuses { - status - count - } + airingSchedule{ + nodes{ + id + airingAt + timeUntilAiring + episode + mediaId } } - } + genres + tags{ + id + name + description + rank + isMediaSpoiler + isAdult + } + isAdult } - } - } + + status + startedAt { + year + month + day + } + completedAt { + year + month + day + } + notes + progress + score + repeat + user { + id + name + avatar { + large + medium + } + statistics { + anime { + count + statuses { + status + count + } + } + } + } + } + } +} `, Variables: Variables{ Page: page, @@ -424,86 +564,138 @@ func (a *App) AniListUpdateEntry(updateBody AniListUpdateVariables) AniListGetSi Variables AniListUpdateVariables `json:"variables"` }{ Query: ` - mutation( - $mediaId:Int, - $progress:Int, - $status:MediaListStatus, - $score:Float, - $repeat:Int, - $notes:String, - $startedAt:FuzzyDateInput, - $completedAt:FuzzyDateInput, - ){ - SaveMediaListEntry( - mediaId:$mediaId, - progress:$progress, - status:$status, - score:$score, - repeat:$repeat, - notes:$notes, - startedAt:$startedAt - completedAt:$completedAt - ){ - id - mediaId - userId - media { - id - idMal - title { - romaji - english - native - } - description - coverImage { - large - } - season - seasonYear - status - episodes - nextAiringEpisode { - airingAt - timeUntilAiring - episode - } - isAdult - } - status - startedAt{ - year - month - day - } - completedAt{ - year - month - day - } - notes - progress - score - repeat - user { - id - name - avatar{ - large - medium - } - statistics{ - anime{ - count - statuses{ - status - count - } - } - } - } - } - } + mutation ( + $mediaId: Int + $progress: Int + $status: MediaListStatus + $score: Float + $repeat: Int + $notes: String + $startedAt: FuzzyDateInput + $completedAt: FuzzyDateInput +) { + SaveMediaListEntry( + mediaId: $mediaId + progress: $progress + status: $status + score: $score + repeat: $repeat + notes: $notes + startedAt: $startedAt + completedAt: $completedAt + ) { + id + idMal + title { + userPreferred + romaji + english + native + } + description + coverImage { + extraLarge + large + medium + color + } + bannerImage + format + season + seasonYear + status + episodes + duration + countryOfOrigin + source + synonyms + averageScore + meanScore + popularity + trending + favourites + isFavourite + relations { + nodes { + id + title { + userPreferred + romaji + english + native + } + } + } + startDate { + year + month + day + } + endDate { + year + month + day + } + nextAiringEpisode { + airingAt + timeUntilAiring + episode + } + airingSchedule { + nodes { + id + airingAt + timeUntilAiring + episode + mediaId + } + } + genres + tags { + id + name + description + rank + isMediaSpoiler + isAdult + } + isAdult +} + + status + startedAt { + year + month + day + } + completedAt { + year + month + day + } + notes + progress + score + repeat + user { + id + name + avatar { + large + medium + } + statistics { + anime { + count + statuses { + status + count + } + } + } + } + } +} `, Variables: updateBody, } @@ -558,3 +750,316 @@ func (a *App) AniListDeleteEntry(mediaListId int) DeleteAniListReturn { return post } + +func (a *App) AniListBrowse( + page int, + perPage int, + id int, + isAdult bool, + search string, + format []string, + status string, + countryOfOrigin string, + source string, + season string, + seasonYear int, + year string, + onList bool, + yearLesser int, + yearGreater int, + episodeLesser int, + episodeGreater int, + durationLesser int, + durationGreater int, + chapterLesser int, + chapterGreater int, + volumeLesser int, + volumeGreater int, + licensedBy []int, + isLicensed bool, + genres []string, + excludedGenres []string, + tags []string, + excludedTags []string, + minimumTagRank int, + sort []string) (AniListCurrentUserWatchList, error) { + // user := a.GetAniListLoggedInUser() + type Variables struct { + Page int `json:"page"` + PerPage int `json:"perPage"` + Id int `json:"id"` + IsAdult bool `json:"isAdult"` + Search string `json:"search"` + Format []string `json:"format"` + Status string `json:"status"` + CountryOfOrigin string `json:"countryOfOrigin"` + Source string `json:"source"` + Season string `json:"season"` + SeasonYear int `json:"seasonYear"` + Year string `json:"year"` + OnList bool `json:"onList"` + YearLesser int `json:"yearLesser"` + YearGreater int `json:"yearGreater"` + EpisodeLesser int `json:"episodeLesser"` + EpisodeGreater int `json:"episodeGreater"` + DurationLesser int `json:"durationLesser"` + DurationGreater int `json:"durationGreater"` + ChapterLesser int `json:"chapterLesser"` + ChapterGreater int `json:"chapterGreater"` + VolumeLesser int `json:"volumeLesser"` + VolumeGreater int `json:"volumeGreater"` + LicensedBy []int `json:"licensedBy"` + IsLicensed bool `json:"isLicensed"` + Genres []string `json:"genres"` + ExcludedGenres []string `json:"excludedGenres"` + Tags []string `json:"tags"` + ExcludedTags []string `json:"excludedTags"` + MinimumTagRank int `json:"minimumTagRank"` + Sort []string `json:"sort"` + } + body := struct { + Query string `json:"query"` + Variables Variables `json:"variables"` + }{ + Query: ` + query ( + $page: Int = 1 + $perPage: Int = 20 + $id: Int + $isAdult: Boolean = false + $search: String + $format: [MediaFormat] + $status: MediaStatus + $countryOfOrigin: CountryCode + $source: MediaSource + $season: MediaSeason + $seasonYear: Int + $year: String + $onList: Boolean + $yearLesser: FuzzyDateInt + $yearGreater: FuzzyDateInt + $episodeLesser: Int + $episodeGreater: Int + $durationLesser: Int + $durationGreater: Int + $chapterLesser: Int + $chapterGreater: Int + $volumeLesser: Int + $volumeGreater: Int + $licensedBy: [Int] + $isLicensed: Boolean + $genres: [String] + $excludedGenres: [String] + $tags: [String] + $excludedTags: [String] + $minimumTagRank: Int + $sort: [MediaSort] = [POPULARITY_DESC, SCORE_DESC] + ) { + Page(page: $page, perPage: $perPage) { + pageInfo { + total + perPage + currentPage + lastPage + hasNextPage + } + media( + id: $id + type: ANIME + season: $season + format_in: $format + status: $status + countryOfOrigin: $countryOfOrigin + source: $source + search: $search + onList: $onList + seasonYear: $seasonYear + startDate_like: $year + startDate_lesser: $yearLesser + startDate_greater: $yearGreater + episodes_lesser: $episodeLesser + episodes_greater: $episodeGreater + duration_lesser: $durationLesser + duration_greater: $durationGreater + chapters_lesser: $chapterLesser + chapters_greater: $chapterGreater + volumes_lesser: $volumeLesser + volumes_greater: $volumeGreater + licensedById_in: $licensedBy + isLicensed: $isLicensed + genre_in: $genres + genre_not_in: $excludedGenres + tag_in: $tags + tag_not_in: $excludedTags + minimumTagRank: $minimumTagRank + sort: $sort + isAdult: $isAdult + ) { + id + mediaId + userId + media { + id + idMal + title { + userPreferred + romaji + english + native + } + description + coverImage { + extraLarge + large + medium + color + } + bannerImage + format + season + seasonYear + status + episodes + duration + countryOfOrigin + source + synonyms + averageScore + meanScore + popularity + trending + favourites + isFavourite + relations { + nodes { + id + title { + userPreferred + romaji + english + native + } + } + } + startDate { + year + month + day + } + endDate { + year + month + day + } + nextAiringEpisode { + airingAt + timeUntilAiring + episode + } + airingSchedule { + nodes { + id + airingAt + timeUntilAiring + episode + mediaId + } + } + genres + tags { + id + name + description + rank + isMediaSpoiler + isAdult + } + isAdult + } + + } + } + `, + Variables: Variables{ + Page: page, + PerPage: perPage, + Id: id, + IsAdult: isAdult, + Search: search, + Format: format, + Status: status, + CountryOfOrigin: countryOfOrigin, + Source: source, + Season: season, + SeasonYear: seasonYear, + Year: year, + OnList: onList, + YearLesser: yearLesser, + YearGreater: yearGreater, + EpisodeLesser: episodeLesser, + EpisodeGreater: episodeGreater, + DurationLesser: durationLesser, + DurationGreater: durationGreater, + ChapterLesser: chapterLesser, + ChapterGreater: chapterGreater, + VolumeLesser: volumeLesser, + VolumeGreater: volumeGreater, + LicensedBy: licensedBy, + IsLicensed: isLicensed, + Genres: genres, + ExcludedGenres: excludedGenres, + Tags: tags, + ExcludedTags: excludedTags, + MinimumTagRank: minimumTagRank, + Sort: sort, + }, + } + + returnedBody, status := AniListQuery(body, true) + + var badPost struct { + Errors []struct { + Message string `json:"message"` + Status int `json:"status"` + Locations []struct { + Line int `json:"line"` + Column int `json:"column"` + } `json:"locations"` + } `json:"errors"` + Data any `json:"data"` + } + var post AniListCurrentUserWatchList + if status == "200 OK" { + err := json.Unmarshal(returnedBody, &post) + if err != nil { + log.Printf("Failed at unmarshal, %s\n", err) + } + // Getting the real total, finding the real last page and storing that in the Page info + statuses := post.Data.Page.MediaList[0].User.Statistics.Anime.Statuses + var total int + for _, status := range statuses { + if status.Status == "CURRENT" { + total = status.Count + } + } + + lastPage := total / perPage + + post.Data.Page.PageInfo.Total = total + post.Data.Page.PageInfo.LastPage = lastPage + } + + if status == "403 Forbidden" { + err := json.Unmarshal(returnedBody, &badPost) + if err != nil { + log.Printf("Failed at unmarshal, %s\n", err) + return post, fmt.Errorf("API authentication error") + } + return post, fmt.Errorf("AniList API error: %s", badPost.Errors[0].Message) + } + if status != "200 OK" { + return post, fmt.Errorf("API request failed with status: %s", status) + } + + return post, nil +} diff --git a/AniListTypes.go b/AniListTypes.go index 2ede9db..991e111 100644 --- a/AniListTypes.go +++ b/AniListTypes.go @@ -37,6 +37,20 @@ type AniListCurrentUserWatchList struct { } `json:"data"` } +type AniListBrowseList struct { + Data struct { + Page struct { + PageInfo struct { + Total int `json:"total"` + PerPage int `json:"perPage"` + CurrentPage int `json:"currentPage"` + LastPage int `json:"lastPage"` + HasNextPage bool `json:"hasNextPage"` + } `json:"pageInfo"` + Media []Media `json:"mediaList"` + } `json:"Page"` + } `json:"data"` +} type AniListGetSingleAnime struct { Data struct { MediaList MediaList `json:"MediaList"` @@ -49,43 +63,91 @@ type AniListUpdateReturn struct { } } +type Media struct { + ID int `json:"id"` + IDMal int `json:"idMal"` + Title struct { + UserPreferred string `json:"userPreferred"` + Romaji string `json:"romaji"` + English string `json:"english"` + Native string `json:"native"` + } `json:"title"` + Description string `json:"description"` + CoverImage struct { + ExtraLarge string + Large string `json:"large"` + Medium string + Color string + } `json:"coverImage"` + BannerImage string + Format string + Season string `json:"season"` + SeasonYear int `json:"seasonYear"` + Status string `json:"status"` + Episodes int `json:"episodes"` + Duration int + CountryOfOrigin string + Source string + Synonyms []string + AverageScore int + MeanScore int + Popularity int + Trending int + Favourites int + isFavourite bool + Relations struct { + nodes struct { + id int + Title struct { + UserPreferred string `json:"userPreferred"` + Romaji string `json:"romaji"` + English string `json:"english"` + Native string `json:"native"` + } `json:"title"` + } + } + StartDate struct { + Year int + Month int + Day int + } + EndDate struct { + Year int + Month int + Day int + } + NextAiringEpisode struct { + AiringAt int `json:"airingAt"` + TimeUntilAiring int `json:"timeUntilAiring"` + Episode int `json:"episode"` + } `json:"nextAiringEpisode"` + AiringSchedule struct { + Nodes struct { + Id int + AiringAt int + TimeUntilAiring int + Episode int + MediaId int + } + } + Genres []string `json:"genres"` + Tags []struct { + Id int `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Rank int `json:"rank"` + IsMediaSpoiler bool `json:"isMediaSpoiler"` + IsAdult bool `json:"isAdult"` + } `json:"tags"` + IsAdult bool `json:"isAdult"` +} + type MediaList struct { - ID int `json:"id"` - MediaID int `json:"mediaId"` - UserID int `json:"userId"` - Media struct { - ID int `json:"id"` - IDMal int `json:"idMal"` - Title struct { - Romaji string `json:"romaji"` - English string `json:"english"` - Native string `json:"native"` - } `json:"title"` - Description string `json:"description"` - CoverImage struct { - Large string `json:"large"` - } `json:"coverImage"` - Season string `json:"season"` - SeasonYear int `json:"seasonYear"` - Status string `json:"status"` - Episodes int `json:"episodes"` - NextAiringEpisode struct { - AiringAt int `json:"airingAt"` - TimeUntilAiring int `json:"timeUntilAiring"` - Episode int `json:"episode"` - } `json:"nextAiringEpisode"` - Genres []string `json:"genres"` - Tags []struct { - Id int `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Rank int `json:"rank"` - IsMediaSpoiler bool `json:"isMediaSpoiler"` - IsAdult bool `json:"isAdult"` - } `json:"tags"` - IsAdult bool `json:"isAdult"` - } `json:"media"` + ID int `json:"id"` + MediaID int `json:"mediaId"` + UserID int `json:"userId"` Status string `json:"status"` + Media Media `json:"media"` StartedAt struct { Year int `json:"year"` Month int `json:"month"`