Adding custom verniy, new sorting, fix window build

This commit is contained in:
Apo
2025-02-06 20:08:44 -05:00
parent e5ad000460
commit ddc1621634
19 changed files with 5200 additions and 0 deletions

129
verniy/character.go Normal file
View File

@@ -0,0 +1,129 @@
package verniy
import "context"
type characterResponse struct {
Data struct {
Character Character `json:"character"`
} `json:"data"`
}
func (c *Client) characterQuery(params QueryParam, fields ...CharacterField) string {
p := make([]string, len(fields))
for i := range fields {
p[i] = string(fields[i])
}
return FieldObject("Character", params, p...)
}
// GetCharacter to get character data.
func (c *Client) GetCharacter(id int, fields ...CharacterField) (*Character, error) {
return c.GetCharacterWithContext(context.Background(), id, fields...)
}
// GetCharacterWithContext to get character data with context.
func (c *Client) GetCharacterWithContext(ctx context.Context, id int, fields ...CharacterField) (*Character, error) {
if len(fields) == 0 {
fields = []CharacterField{
CharacterFieldID,
CharacterFieldName(
CharacterNameFieldFirst,
CharacterNameFieldMiddle,
CharacterNameFieldLast,
CharacterNameFieldFull,
CharacterNameFieldNative,
CharacterNameFieldAlternative,
CharacterNameFieldAlternativeSpoiler),
CharacterFieldImage(CharacterImageFieldLarge),
CharacterFieldDescription,
CharacterFieldGender,
CharacterFieldDateOfBirth,
CharacterFieldAge,
CharacterFieldFavourite,
}
}
query := FieldObject("query", QueryParam{
"$id": "Int",
}, c.characterQuery(QueryParam{
"id": "$id",
}, fields...))
var d characterResponse
err := c.post(ctx, query, queryVariable{
"id": id,
}, &d)
if err != nil {
return nil, err
}
return &d.Data.Character, nil
}
// GetCharacterAnime to get list of anime the character play.
func (c *Client) GetCharacterAnime(id int, page int, perPage int) (*Character, error) {
return c.GetCharacterAnimeWithContext(context.Background(), id, page, perPage)
}
// GetCharacterAnimeWithContext to get list of anime the character play with context.
func (c *Client) GetCharacterAnimeWithContext(ctx context.Context, id int, page int, perPage int) (*Character, error) {
return c.GetCharacterWithContext(ctx, id, CharacterFieldMedia(CharacterParamMedia{
Type: MediaTypeAnime,
Page: page,
PerPage: perPage,
},
MediaConnectionFieldPageInfo(
PageInfoFieldTotal,
PageInfoFieldPerPage,
PageInfoFieldCurrentPage,
PageInfoFieldLastPage,
PageInfoFieldHasNextPage),
MediaConnectionFieldEdges(
MediaEdgeFieldCharacterRole,
MediaEdgeFieldVoiceActors(
MediaEdgeParamVoiceActors{},
StaffFieldID,
StaffFieldLanguage,
StaffFieldName(StaffNameFieldFull),
StaffFieldImage(StaffImageFieldMedium)),
MediaEdgeFieldNode(
MediaFieldID,
MediaFieldType,
MediaFieldTitle(
MediaTitleFieldEnglish,
MediaTitleFieldNative,
MediaTitleFieldRomaji),
MediaFieldCoverImage(
MediaCoverImageFieldMedium)))))
}
// GetCharacterManga to get list of manga the character play.
func (c *Client) GetCharacterManga(id int, page int, perPage int) (*Character, error) {
return c.GetCharacterMangaWithContext(context.Background(), id, page, perPage)
}
// GetCharacterMangaWithContext to get list of manga the character play with context.
func (c *Client) GetCharacterMangaWithContext(ctx context.Context, id int, page int, perPage int) (*Character, error) {
return c.GetCharacterWithContext(ctx, id, CharacterFieldMedia(CharacterParamMedia{
Type: MediaTypeManga,
Page: page,
PerPage: perPage,
},
MediaConnectionFieldPageInfo(
PageInfoFieldTotal,
PageInfoFieldPerPage,
PageInfoFieldCurrentPage,
PageInfoFieldLastPage,
PageInfoFieldHasNextPage),
MediaConnectionFieldEdges(
MediaEdgeFieldCharacterRole,
MediaEdgeFieldNode(
MediaFieldID,
MediaFieldType,
MediaFieldTitle(
MediaTitleFieldEnglish,
MediaTitleFieldNative,
MediaTitleFieldRomaji),
MediaFieldCoverImage(
MediaCoverImageFieldMedium)))))
}

422
verniy/constant.go Normal file
View File

@@ -0,0 +1,422 @@
package verniy
// MediaType is main media type.
type MediaType string
// Options for MediaType.
const (
MediaTypeAnime MediaType = "ANIME"
MediaTypeManga MediaType = "MANGA"
)
// MediaFormat is anime & manga format.
type MediaFormat string
// Options for MediaFormat.
const (
MediaFormatTv MediaFormat = "TV"
MediaFormatTvShort MediaFormat = "TV_SHORT"
MediaFormatMovie MediaFormat = "MOVIE"
MediaFormatSpecial MediaFormat = "SPECIAL"
MediaFormatOVA MediaFormat = "OVA"
MediaFormatONA MediaFormat = "ONA"
MediaFormatMusic MediaFormat = "MUSIC"
MediaFormatManga MediaFormat = "MANGA"
MediaFormatNovel MediaFormat = "NOVEL"
MediaFormatOneShot MediaFormat = "ONE_SHOT"
)
// MediaStatus is anime & manga status.
type MediaStatus string
// Options for MediaStatus.
const (
MediaStatusFinished MediaStatus = "FINISHED"
MediaStatusReleasing MediaStatus = "RELEASING"
MediaStatusNotYetReleased MediaStatus = "NOT_YET_RELEASED"
MediaStatusCancelled MediaStatus = "CANCELLED"
MediaStatusHiatus MediaStatus = "HIATUS"
)
// MediaSeason is anime season.
type MediaSeason string
// Options for MediaSeason.
const (
MediaSeasonWinter MediaSeason = "WINTER" // 12-2
MediaSeasonSpring MediaSeason = "SPRING" // 3-5
MediaSeasonSummer MediaSeason = "SUMMER" // 6-8
MediaSeasonFall MediaSeason = "FALL" // 9-11
)
// MediaSource is anime & manga source.
type MediaSource string
// Options for MediaSource.
const (
MediaSourceOriginal MediaSource = "ORIGINAL"
MediaSourceManga MediaSource = "MANGA"
MediaSourceLightNovel MediaSource = "LIGHT_NOVEL"
MediaSourceVisualNovel MediaSource = "VISUAL_NOVEL"
MediaSourceVideoGame MediaSource = "VIDEO_GAME"
MediaSourceOther MediaSource = "OTHER"
MediaSourceNovel MediaSource = "NOVEL"
MediaSourceDoujinshi MediaSource = "DOUJINSHI"
MediaSourceAnime MediaSource = "ANIME"
)
// MediaRankType is type of ranking.
type MediaRankType string
// Options for MediaRankType
const (
MediaRankTypeRated MediaRankType = "RATED"
MediaRankTypePopular MediaRankType = "POPULAR"
)
// MediaListStatus is user's anime & manga status.
type MediaListStatus string
// Options for MediaListStatus.
const (
MediaListStatusCurrent MediaListStatus = "CURRENT"
MediaListStatusPlanning MediaListStatus = "PLANNING"
MediaListStatusCompleted MediaListStatus = "COMPLETED"
MediaListStatusDropped MediaListStatus = "DROPPED"
MediaListStatusPaused MediaListStatus = "PAUSED"
MediaListStatusRepeating MediaListStatus = "REPEATING"
)
// MediaRelation is anime & manga relation.
type MediaRelation string
// Options for MediaRelation.
const (
MediaRelationAdaptation MediaRelation = "ADAPTATION"
MediaRelationPrequel MediaRelation = "PREQUEL"
MediaRelationSequel MediaRelation = "SEQUEL"
MediaRelationParent MediaRelation = "PARENT"
MediaRelationSideStory MediaRelation = "SIDE_STORY"
MediaRelationCharacter MediaRelation = "CHARACTER"
MediaRelationSummary MediaRelation = "SUMMARY"
MediaRelationAlternative MediaRelation = "ALTERNATIVE"
MediaRelationSpinOff MediaRelation = "SPIN_OFF"
MediaRelationOther MediaRelation = "OTHER"
MediaRelationSource MediaRelation = "SOURCE"
MediaRelationCompilation MediaRelation = "COMPILATION"
MediaRelationContains MediaRelation = "CONTAINS"
)
// CharacterRole is type of character role.
type CharacterRole string
// Options for CharacterRole.
const (
CharacterRoleMain CharacterRole = "MAIN"
CharacterRoleSupporting CharacterRole = "SUPPORTING"
CharacterRoleBackground CharacterRole = "BACKGROUND"
)
// UserTitleLanguage is default user anime & manga title language.
type UserTitleLanguage string
// Options for UserTitleLanguage.
const (
UserTitleLanguageRomaji UserTitleLanguage = "ROMAJI"
UserTitleLanguageEnglish UserTitleLanguage = "ENGLISH"
UserTitleLanguageNative UserTitleLanguage = "NATIVE"
UserTitleLanguageRomajiStylised UserTitleLanguage = "ROMAJI_STYLISED"
UserTitleLanguageEnglishStylised UserTitleLanguage = "ENGLISH_STYLISED"
UserTitleLanguageNativeStylised UserTitleLanguage = "NATIVE_STYLISED"
)
// NotificationType is user notification type.
type NotificationType string
// Options for NotificationType.
const (
NotificationTypeActivityMessage NotificationType = "ACTIVITY_MESSAGE"
NotificationTypeActivityReply NotificationType = "ACTIVITY_REPLY"
NotificationTypeActivityFollowing NotificationType = "FOLLOWING"
NotificationTypeActivityMention NotificationType = "ACTIVITY_MENTION"
NotificationTypeThreadCommentMention NotificationType = "THREAD_COMMENT_MENTION"
NotificationTypeThreadSubscribed NotificationType = "THREAD_SUBSCRIBED"
NotificationTypeThreadCommentReply NotificationType = "THREAD_COMMENT_REPLY"
NotificationTypeAiring NotificationType = "AIRING"
NotificationTypeActivityLike NotificationType = "ACTIVITY_LIKE"
NotificationTypeActivityReplyLike NotificationType = "ACTIVITY_REPLY_LIKE"
NotificationTypeThreadLike NotificationType = "THREAD_LIKE"
NotificationTypeThreadCommentLike NotificationType = "THREAD_COMMENT_LIKE"
NotificationTypeActivityReplySubscribed NotificationType = "ACTIVITY_REPLY_SUBSCRIBED"
NotificationTypeRelatedMediaAddition NotificationType = "RELATED_MEDIA_ADDITION"
)
// UserStaffNameLanguage is default user staff naming language.
type UserStaffNameLanguage string
// Options for UserStaffNameLanguage
const (
UserStaffNameLanguageRomajiWestern UserStaffNameLanguage = "ROMAJI_WESTERN"
UserStaffNameLanguageRomaji UserStaffNameLanguage = "ROMAJI"
UserStaffNameLanguageNative UserStaffNameLanguage = "NATIVE"
)
// ReviewRating is type of review rating.
type ReviewRating string
// Options for ReviewRating.
const (
ReviewRatingNoVote ReviewRating = "NO_VOTE"
ReviewRatingUpVote ReviewRating = "UP_VOTE"
ReviewRatingDownVote ReviewRating = "DOWN_VOTE"
)
// RecommendationRating is type of recommendation rating.
type RecommendationRating string
// Options for RecommendationRating.
const (
RecommendationRatingNoRating RecommendationRating = "NO_RATING"
RecommendationRatingRateUp RecommendationRating = "RATE_UP"
RecommendationRatingRateDown RecommendationRating = "RATE_DOWN"
)
// ScoreFormat is scoring format.
type ScoreFormat string
// Options for ScoreFormat.
const (
ScoreFormatPoint100 ScoreFormat = "POINT_100"
ScoreFormatPoint100Decimal ScoreFormat = "POINT_10_DECIMAL"
ScoreFormatPoint10 ScoreFormat = "POINT_10"
ScoreFormatPoint5 ScoreFormat = "POINT_5"
ScoreFormatPoint3 ScoreFormat = "POINT_3"
)
// ModRole is mod role.
type ModRole string
// Options for ModRole.
const (
ModRoleAdmin ModRole = "ADMIN"
ModRoleLeadDeveloper ModRole = "LEAD_DEVELOPER"
ModRoleDeveloper ModRole = "DEVELOPER"
ModRoleLeadCommunity ModRole = "LEAD_COMMUNITY"
ModRoleCommunity ModRole = "COMMUNITY"
ModRoleDiscordCommunity ModRole = "DISCORD_COMMUNITY"
ModRoleLeadAnimeData ModRole = "LEAD_ANIME_DATA"
ModRoleAnimeData ModRole = "ANIME_DATA"
ModRoleLeadMangaData ModRole = "LEAD_MANGA_DATA"
ModRoleMangaData ModRole = "MANGA_DATA"
ModRoleLeadSocialMedia ModRole = "LEAD_SOCIAL_MEDIA"
ModRoleSocialMedia ModRole = "SOCIAL_MEDIA"
ModRoleRetired ModRole = "RETIRED"
)
// CharacterSort is sorting option for character list.
type CharacterSort string
// Options for CharacterSort.
const (
CharacterSortID CharacterSort = "ID"
CharacterSortIDDesc CharacterSort = "ID_DESC"
CharacterSortRole CharacterSort = "ROLE"
CharacterSortRoleDesc CharacterSort = "ROLE_DESC"
CharacterSortSearchMatch CharacterSort = "SEARCH_MATCH"
CharacterSortFavourites CharacterSort = "FAVOURITES"
CharacterSortFavouritesDesc CharacterSort = "FAVOURITES_DESC"
CharacterSortRelevance CharacterSort = "RELEVANCE"
)
// StaffSort is sorting option for staff list.
type StaffSort string
// Options for StaffSort.
const (
StaffSortID StaffSort = "ID"
StaffSortIDDesc StaffSort = "ID_DESC"
StaffSortRole StaffSort = "ROLE"
StaffSortRoleDesc StaffSort = "ROLE_DESC"
StaffSortLanguage StaffSort = "LANGUAGE"
StaffSortLanguageDesc StaffSort = "LANGUAGE_DESC"
StaffSortSearchMatch StaffSort = "SEARCH_MATCH"
StaffSortFavourites StaffSort = "FAVOURITES"
StaffSortFavouritesDesc StaffSort = "FAVOURITES_DESC"
StaffSortRelevance StaffSort = "RELEVANCE"
)
// MediaSort is sorting option for anime & manga list.
type MediaSort string
// Options for MediaSort.
const (
MediaSortID MediaSort = "ID"
MediaSortIDDesc MediaSort = "ID_DESC"
MediaSortTitleRomaji MediaSort = "TITLE_ROMAJI"
MediaSortTitleRomajiDesc MediaSort = "TITLE_ROMAJI_DESC"
MediaSortTitleEnglish MediaSort = "TITLE_ENGLISH"
MediaSortTitleEnglishDesc MediaSort = "TITLE_ENGLISH_DESC"
MediaSortTitleNative MediaSort = "TITLE_NATIVE"
MediaSortTitleNativeDesc MediaSort = "TITLE_NATIVE_DESC"
MediaSortType MediaSort = "TYPE"
MediaSortTypeDesc MediaSort = "TYPE_DESC"
MediaSortFormat MediaSort = "FORMAT"
MediaSortFormatDesc MediaSort = "FORMAT_DESC"
MediaSortStartDate MediaSort = "START_DATE"
MediaSortStartDateDesc MediaSort = "START_DATE_DESC"
MediaSortEndDate MediaSort = "END_DATE"
MediaSortEndDateDesc MediaSort = "END_DATE_DESC"
MediaSortScore MediaSort = "SCORE"
MediaSortScoreDesc MediaSort = "SCORE_DESC"
MediaSortPopularity MediaSort = "POPULARITY"
MediaSortPopularityDesc MediaSort = "POPULARITY_DESC"
MediaSortTrending MediaSort = "TRENDING"
MediaSortTrendingDesc MediaSort = "TRENDING_DESC"
MediaSortEpisodes MediaSort = "EPISODES"
MediaSortEpisodesDesc MediaSort = "EPISODES_DESC"
MediaSortDuration MediaSort = "DURATION"
MediaSortDurationDesc MediaSort = "DURATION_DESC"
MediaSortStatus MediaSort = "STATUS"
MediaSortStatusDesc MediaSort = "STATUS_DESC"
MediaSortChapters MediaSort = "CHAPTERS"
MediaSortChaptersDesc MediaSort = "CHAPTERS_DESC"
MediaSortVolumes MediaSort = "VOLUMES"
MediaSortVolumesDesc MediaSort = "VOLUMES_DESC"
MediaSortUpdatedAt MediaSort = "UPDATED_AT"
MediaSortUpdatedAtDesc MediaSort = "UPDATED_AT_DESC"
MediaSortSearchMatch MediaSort = "SEARCH_MATCH"
MediaSortFavourites MediaSort = "FAVOURITES"
MediaSortFavouritesDesc MediaSort = "FAVOURITES_DESC"
)
// StaffLanguage is staff language.
type StaffLanguage string
// Options for StaffLanguage.
const (
StaffLanguageJapanese StaffLanguage = "JAPANESE"
StaffLanguageEnglish StaffLanguage = "ENGLISH"
StaffLanguageKorean StaffLanguage = "KOREAN"
StaffLanguageItalian StaffLanguage = "ITALIAN"
StaffLanguageSpanish StaffLanguage = "SPANISH"
StaffLanguagePortuguese StaffLanguage = "PORTUGUESE"
StaffLanguageFrench StaffLanguage = "FRENCH"
StaffLanguageGerman StaffLanguage = "GERMAN"
StaffLanguageHebrew StaffLanguage = "HEBREW"
StaffLanguageHungarian StaffLanguage = "HUNGARIAN"
)
// StudioSort is sorting option for studio list.
type StudioSort string
// Options for StudioSort.
const (
StudioSortID StudioSort = "ID"
StudioSortIDDesc StudioSort = "ID_DESC"
StudioSortName StudioSort = "NAME"
StudioSortNameDesc StudioSort = "NAME_DESC"
StudioSortSearchMatch StudioSort = "SEARCH_MATCH"
StudioSortFavourites StudioSort = "FAVOURITES"
StudioSortFavouritesDesc StudioSort = "FAVOURITES_DESC"
)
// MediaTrendSort is sorting option for media trend list.
type MediaTrendSort string
// Options for MediaTrendSort.
const (
MediaTrendSortID MediaTrendSort = "ID"
MediaTrendSortIDDesc MediaTrendSort = "ID_DESC"
MediaTrendSortMediaID MediaTrendSort = "MEDIA_ID"
MediaTrendSortMediaIDDesc MediaTrendSort = "MEDIA_ID_DESC"
MediaTrendSortDate MediaTrendSort = "DATE"
MediaTrendSortDateDesc MediaTrendSort = "DATE_DESC"
MediaTrendSortScore MediaTrendSort = "SCORE"
MediaTrendSortScoreDesc MediaTrendSort = "SCORE_DESC"
MediaTrendSortPopularity MediaTrendSort = "POPULARITY"
MediaTrendSortPopularityDesc MediaTrendSort = "POPULARITY_DESC"
MediaTrendSortTrending MediaTrendSort = "TRENDING"
MediaTrendSortTrendingDesc MediaTrendSort = "TRENDING_DESC"
MediaTrendSortEpisode MediaTrendSort = "EPISODE"
MediaTrendSortEpisodeDesc MediaTrendSort = "EPISODE_DESC"
)
// ReviewSort is sorting option for review list.
type ReviewSort string
// Options for ReviewSort.
const (
ReviewSortID ReviewSort = "ID"
ReviewSortIDDesc ReviewSort = "ID_DESC"
ReviewSortScore ReviewSort = "SCORE"
ReviewSortScoreDesc ReviewSort = "SCORE_DESC"
ReviewSortRating ReviewSort = "RATING"
ReviewSortRatingDesc ReviewSort = "RATING_DESC"
ReviewSortCreatedAt ReviewSort = "CREATED_AT"
ReviewSortCreatedAtDesc ReviewSort = "CREATED_AT_DESC"
ReviewSortUpdatedAt ReviewSort = "UPDATED_AT"
ReviewSortUpdatedAtDesc ReviewSort = "UPDATED_AT_DESC"
)
// RecommendationSort is sorting option for recommendation list.
type RecommendationSort string
// Options for RecommendationSort.
const (
RecommendationSortID RecommendationSort = "ID"
RecommendationSortIDDesc RecommendationSort = "ID_DESC"
RecommendationSortRating RecommendationSort = "RATING"
RecommendationSortRatingDesc RecommendationSort = "RATING_DESC"
)
// UserStatisticsSort is sorting option for user statistics list.
type UserStatisticsSort string
// Options for UserStatisticsSort.
const (
UserStatisticsSortID UserStatisticsSort = "ID"
UserStatisticsSortIDDesc UserStatisticsSort = "ID_DESC"
UserStatisticsSortCount UserStatisticsSort = "COUNT"
UserStatisticsSortCountDesc UserStatisticsSort = "COUNT_DESC"
UserStatisticsSortProgress UserStatisticsSort = "PROGRESS"
UserStatisticsSortProgessDesc UserStatisticsSort = "PROGRESS_DESC"
UserStatisticsSortMeanScore UserStatisticsSort = "MEAN_SCORE"
UserStatisticsSortMeanScoreDesc UserStatisticsSort = "MEAN_SCORE_DESC"
)
// MediaListSort is sorting option for media list.
type MediaListSort string
// Options for MediaListSort.
const (
MediaListSortMediaID MediaListSort = "MEDIA_ID"
MediaListSortMediaIDDesc MediaListSort = "MEDIA_ID_DESC"
MediaListSortScore MediaListSort = "SCORE"
MediaListSortScoreDesc MediaListSort = "SCORE_DESC"
MediaListSortStatus MediaListSort = "STATUS"
MediaListSortStatusDesc MediaListSort = "STATUS_DESC"
MediaListSortProgress MediaListSort = "PROGRESS"
MediaListSortProgressDesc MediaListSort = "PROGRESS_DESC"
MediaListSortProgressVolumes MediaListSort = "PROGRESS_VOLUMES"
MediaListSortProgressVolumesDesc MediaListSort = "PROGRESS_VOLUMES_DESC"
MediaListSortRepeat MediaListSort = "REPEAT"
MediaListSortRepeatDesc MediaListSort = "REPEAT_DESC"
MediaListSortPriority MediaListSort = "PRIORITY"
MediaListSortPriorityDesc MediaListSort = "PRIORITY_DESC"
MediaListSortStartedOn MediaListSort = "STARTED_ON"
MediaListSortStartedOnDesc MediaListSort = "STARTED_ON_DESC"
MediaListSortFinishedOn MediaListSort = "FINISHED_ON"
MediaListSortFinishedOnDesc MediaListSort = "FINISHED_ON_DESC"
MediaListSortAddedTime MediaListSort = "ADDED_TIME"
MediaListSortAddedTimeDesc MediaListSort = "ADDED_TIME_DESC"
MediaListSortUpdatedTime MediaListSort = "UPDATED_TIME"
MediaListSortUpdatedTimeDesc MediaListSort = "UPDATED_TIME_DESC"
MediaListSortMediaTitleRomaji MediaListSort = "MEDIA_TITLE_ROMAJI"
MediaListSortMediaTitleRomajiDesc MediaListSort = "MEDIA_TITLE_ROMAJI_DESC"
MediaListSortMediaTitleEnglish MediaListSort = "MEDIA_TITLE_ENGLISH"
MediaListSortMediaTitleEnglishDesc MediaListSort = "MEDIA_TITLE_ENGLISH_DESC"
MediaListSortMediaTitleNative MediaListSort = "MEDIA_TITLE_NATIVE"
MediaListSortMediaTitleNativeDesc MediaListSort = "MEDIA_TITLE_NATIVE_DESC"
MediaListSortMediaPopularity MediaListSort = "MEDIA_POPULARITY"
MediaListSortMediaPopularityDesc MediaListSort = "MEDIA_POPULARITY_DESC"
)

74
verniy/doc.go Normal file
View File

@@ -0,0 +1,74 @@
// Package verniy is unofficial Anilist GraphQL API library.
//
// Verniy will generate graphql query string according to your request,
// make request to Anilist, parse response body, and convert it
// to struct. The goal of this library is to make a flexible and easy to
// call Anilist API.
//
// // Init verniy.
// v := verniy.New()
//
// // Get anime One Piece data (with default fields).
// data, err := v.GetAnime(21)
//
// // Get anime One Piece data (with custom fields).
// data, err := v.GetAnime(21,
// verniy.MediaFieldID,
// verniy.MediaFieldTitle(
// verniy.MediaTitleFieldRomaji,
// verniy.MediaTitleFieldEnglish,
// verniy.MediaTitleFieldNative),
// verniy.MediaFieldType,
// verniy.MediaFieldFormat,
// verniy.MediaFieldStatusV2,
// verniy.MediaFieldDescription,
// verniy.MediaFieldStartDate,
// verniy.MediaFieldEndDate,
// verniy.MediaFieldSeason,
// verniy.MediaFieldSeasonYear)
//
// # Functions
//
// There are alot of functions in verniy package but
// most of the time, you just need functions in `Client` struct.
// And it's recommended to use them to make your life easier.
// If you want to make a custom request, you can use function
// `MakeRequest()` (go to the function for more details).
//
// # Parameters
//
// Yes, there are tons of custom types and functions but let
// your IDE auto-complete helps you choose the params for the
// functions. Not only constant value but also function (like
// `MediaFieldTitle` in the example) to fill the params.
// But, most of the functions have variadic parameters,
// so you don't need to actually fill it. There is already default
// value and you can read the code yourself to see the default
// value of the variadic parameters.
//
// If you want the complete list of available params,
// read the official docs (https://anilist.github.io/ApiV2-GraphQL-Docs/).
//
// # Response
//
// Most of the functions in `Client` struct return a struct and error.
// Yes, most of the fields in the returned struct are pointers because,
// like any graphql, Anilist will not return fields that we didn't
// request. It is recommended to make your own pointer handler when
// using the field. For example.
//
// // Handle *string to string.
// func ptrToString(str *string) string {
// if str == nil {
// return ""
// }
// return *str
// }
//
// # Rate Limit
//
// Anilist has default rate limit 90 requests per minute. If you go over
// the rate limit you'll receive a 1-minute timeout. But, verniy has
// a built-in rate limiter to prevent the requests going over the limit.
// It will put your request on hold until the limit is available again.
package verniy

1968
verniy/field.go Normal file

File diff suppressed because it is too large Load Diff

27
verniy/genres.go Normal file
View File

@@ -0,0 +1,27 @@
package verniy
import "context"
type genreResponse struct {
Data struct {
Genres []string `json:"genreCollection"`
} `json:"data"`
}
// GetGenres to get all genre list.
func (c *Client) GetGenres() ([]string, error) {
return c.GetGenresWithContext(context.Background())
}
// GetGenresWithContext to get all genre list with context.
func (c *Client) GetGenresWithContext(ctx context.Context) ([]string, error) {
query := FieldObject("query", nil, "GenreCollection")
var d genreResponse
err := c.post(ctx, query, nil, &d)
if err != nil {
return nil, err
}
return d.Data.Genres, nil
}

130
verniy/http.go Normal file
View File

@@ -0,0 +1,130 @@
package verniy
import (
"bytes"
"context"
"encoding/json"
"errors"
"io/ioutil"
"net/http"
"strings"
)
type queryRequest struct {
Query string `json:"query"`
Variables queryVariable `json:"variables"`
}
type queryVariable map[string]interface{}
type errorResponse struct {
Errors []struct {
Message string `json:"message"`
Status int `json:"status"`
} `json:"errors"`
}
func (c *Client) handleError(body []byte) error {
var e errorResponse
if err := json.Unmarshal(body, &e); err != nil {
return err
}
errMsgs := make([]string, len(e.Errors))
for i, b := range e.Errors {
errMsgs[i] = b.Message
}
return errors.New(strings.Join(errMsgs, " | "))
}
func (c *Client) post(ctx context.Context, query string, v map[string]interface{}, model interface{}) error {
d, err := json.Marshal(queryRequest{
Query: query,
Variables: v,
})
if err != nil {
return err
}
body, code, err := c.MakeRequest(ctx, d)
if err != nil {
return err
}
if code != http.StatusOK {
return c.handleError(body)
}
if err = json.Unmarshal(body, &model); err != nil {
return err
}
return nil
}
// MakeRequest to make direct HTTP request without any wrapper.
// Prepare your body request and handle the response by yourself.
//
// Use this if you want to make custom request. Also, need to read the
// docs of how to prepare the body request and read the response
// (https://anilist.github.io/ApiV2-GraphQL-Docs/).
//
// Params requestBody is your data in JSON format. So, marshal your data
// first before passing it to this function. And will return response body,
// response code, and error.
//
// Example:
//
// query := verniy.FieldObject("query", verniy.QueryParam{
// "$id": "Int",
// "$type": "MediaType",
// }, verniy.FieldObject("Media", verniy.QueryParam{
// "id": "$id",
// "type": "$type",
// }, "id"))
//
// body := map[string]interface{}{
// "query": query,
// "variables": map[string]interface{}{
// "id": 1,
// "type": "ANIME",
// },
// }
//
// jsonBody, _ := json.Marshal(body)
//
// data, code, err := c.MakeRequest(jsonBody)
// if err != nil {
// panic(err)
// }
//
// fmt.Println(code)
// fmt.Println(string(data))
func (c *Client) MakeRequest(ctx context.Context, requestBody []byte) ([]byte, int, error) {
c.Limiter.Take()
req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.Host, bytes.NewBuffer(requestBody))
if err != nil {
return nil, http.StatusInternalServerError, err
}
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Accept", "application/json")
if c.AccessToken != "" {
req.Header.Add("Authorization", "Bearer "+c.AccessToken)
}
resp, err := c.Http.Do(req)
if err != nil {
return nil, http.StatusInternalServerError, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, http.StatusInternalServerError, err
}
return body, resp.StatusCode, nil
}

75
verniy/limiter/limiter.go Normal file
View File

@@ -0,0 +1,75 @@
// Package limiter is a copy of "github.com/uber-go/ratelimit" library.
package limiter
import (
"sync"
"time"
)
// Limiter is interface for rate limiter.
type Limiter interface {
Take() time.Time
}
type mutexLimiter struct {
sync.Mutex
last time.Time
sleepFor time.Duration
perRequest time.Duration
maxSlack time.Duration
clock clocker
}
type clocker interface {
Now() time.Time
Sleep(time.Duration)
}
// New returns a new atomic based limiter.
func New(rate int, interval time.Duration) Limiter {
perRequest := interval / time.Duration(rate)
return &mutexLimiter{
perRequest: perRequest,
maxSlack: -1 * time.Duration(10) * perRequest,
clock: newClock(),
}
}
// Take blocks to ensure that the time spent between multiple
// Take calls is on average time.Second/rate.
func (t *mutexLimiter) Take() time.Time {
t.Lock()
defer t.Unlock()
now := t.clock.Now()
// If this is our first request, then we allow it.
if t.last.IsZero() {
t.last = now
return t.last
}
// sleepFor calculates how much time we should sleep based on
// the perRequest budget and how long the last request took.
// Since the request may take longer than the budget, this number
// can get negative, and is summed across requests.
t.sleepFor += t.perRequest - now.Sub(t.last)
// We shouldn't allow sleepFor to get too negative, since it would mean that
// a service that slowed down a lot for a short period of time would get
// a much higher RPS following that.
if t.sleepFor < t.maxSlack {
t.sleepFor = t.maxSlack
}
// If sleepFor is positive, then we should sleep now.
if t.sleepFor > 0 {
t.clock.Sleep(t.sleepFor)
t.last = now.Add(t.sleepFor)
t.sleepFor = 0
} else {
t.last = now
}
return t.last
}

24
verniy/limiter/time.go Normal file
View File

@@ -0,0 +1,24 @@
package limiter
import (
"time"
)
// newClock returns an instance of a real-time clock.
// Taken from `github.com/andres-erbsen/clock`.
func newClock() *clock {
return &clock{}
}
// clock implements a real-time clock by simply wrapping the time package functions.
type clock struct{}
// Now to get time now.
func (c *clock) Now() time.Time {
return time.Now()
}
// Sleep to sleep.
func (c *clock) Sleep(d time.Duration) {
time.Sleep(d)
}

364
verniy/media.go Normal file
View File

@@ -0,0 +1,364 @@
package verniy
import "context"
type mediaResponse struct {
Data struct {
Media Media `json:"media"`
} `json:"data"`
}
func (c *Client) mediaQuery(params QueryParam, fields ...MediaField) string {
p := make([]string, len(fields))
for i := range fields {
p[i] = string(fields[i])
}
return FieldObject("Media", params, p...)
}
func (c *Client) getMedia(ctx context.Context, id int, mediaType MediaType, fields ...MediaField) (*Media, error) {
query := FieldObject("query", QueryParam{
"$id": "Int",
"$type": "MediaType",
}, c.mediaQuery(QueryParam{
"id": "$id",
"type": "$type",
}, fields...))
var d mediaResponse
err := c.post(ctx, query, queryVariable{
"id": id,
"type": mediaType,
}, &d)
if err != nil {
return nil, err
}
return &d.Data.Media, nil
}
// GetAnime to get anime data.
func (c *Client) GetAnime(id int, fields ...MediaField) (*Media, error) {
return c.GetAnimeWithContext(context.Background(), id, fields...)
}
// GetAnimeWithContext to get anime data with context.
func (c *Client) GetAnimeWithContext(ctx context.Context, id int, fields ...MediaField) (*Media, error) {
if len(fields) == 0 {
fields = []MediaField{
MediaFieldID,
MediaFieldTitle(
MediaTitleFieldRomaji,
MediaTitleFieldEnglish,
MediaTitleFieldNative),
MediaFieldType,
MediaFieldFormat,
MediaFieldStatusV2,
MediaFieldDescription,
MediaFieldStartDate,
MediaFieldEndDate,
MediaFieldCountryOfOrigin,
MediaFieldSourceV2,
MediaFieldCoverImage(
MediaCoverImageFieldExtraLarge,
MediaCoverImageFieldLarge,
MediaCoverImageFieldMedium,
MediaCoverImageFieldColor),
MediaFieldBannerImage,
MediaFieldGenres,
MediaFieldSynonyms,
MediaFieldAverageScore,
MediaFieldMeanScore,
MediaFieldPopularity,
MediaFieldFavourites,
MediaFieldTags(
MediaTagFieldName,
MediaTagFieldDescription,
MediaTagFieldCategory,
MediaTagFieldRank,
MediaTagFieldIsGeneralSpoiler,
MediaTagFieldIsMediaSpoiler,
MediaTagFieldIsAdult),
MediaFieldRelations(
MediaConnectionFieldPageInfo(
PageInfoFieldTotal,
PageInfoFieldPerPage,
PageInfoFieldCurrentPage,
PageInfoFieldLastPage,
PageInfoFieldHasNextPage),
MediaConnectionFieldEdges(
MediaEdgeFieldRelationTypeV2,
MediaEdgeFieldNode(
MediaFieldID,
MediaFieldTitle(
MediaTitleFieldRomaji,
MediaTitleFieldEnglish,
MediaTitleFieldNative),
MediaFieldFormat,
MediaFieldType,
MediaFieldStatusV2,
MediaFieldCoverImage(
MediaCoverImageFieldMedium)))),
MediaFieldIsAdult,
MediaFieldRankings(
MediaRankFieldID,
MediaRankFieldRank,
MediaRankFieldType,
MediaRankFieldFormat,
MediaRankFieldYear,
MediaRankFieldSeason,
MediaRankFieldAllTime,
MediaRankFieldContext,
),
MediaFieldSeason,
MediaFieldSeasonYear,
MediaFieldEpisodes,
MediaFieldDuration,
MediaFieldStudios(
MediaParamStudios{},
StudioConnectionFieldEdges(
StudioEdgeFieldIsMain,
StudioEdgeFieldNode(
StudioFieldID,
StudioFieldName,
StudioFieldIsAnimationStudio))),
}
}
return c.getMedia(ctx, id, MediaTypeAnime, fields...)
}
// GetAnimeCharacters to get list of characters in anime.
func (c *Client) GetAnimeCharacters(id int, page int, perPage int, fields ...MediaField) (*Media, error) {
return c.GetAnimeCharactersWithContext(context.Background(), id, page, perPage, fields...)
}
// GetAnimeCharactersWithContext to get list of characters in anime with context.
func (c *Client) GetAnimeCharactersWithContext(ctx context.Context, id int, page int, perPage int, fields ...MediaField) (*Media, error) {
if len(fields) == 0 {
fields = []MediaField{
MediaFieldCharacters(
MediaParamCharacters{
Page: page,
PerPage: perPage,
Sort: []CharacterSort{CharacterSortRole, CharacterSortRelevance, CharacterSortID},
},
CharacterConnectionFieldPageInfo(
PageInfoFieldTotal,
PageInfoFieldPerPage,
PageInfoFieldCurrentPage,
PageInfoFieldLastPage,
PageInfoFieldHasNextPage),
CharacterConnectionFieldEdges(
CharacterEdgeFieldRole,
CharacterEdgeFieldNode(
CharacterFieldID,
CharacterFieldName(CharacterNameFieldFull),
CharacterFieldImage(CharacterImageFieldMedium)),
CharacterEdgeFieldVoiceActors(
CharacterEdgeParamVoiceActors{},
StaffFieldID,
StaffFieldName(StaffNameFieldFull),
StaffFieldImage(StaffImageFieldMedium),
StaffFieldLanguage))),
}
}
return c.getMedia(ctx, id, MediaTypeAnime, fields...)
}
// GetAnimeStaff to get list of staff in anime.
func (c *Client) GetAnimeStaff(id int, page int, perPage int, fields ...MediaField) (*Media, error) {
return c.GetAnimeStaffWithContext(context.Background(), id, page, perPage, fields...)
}
// GetAnimeStaffWithContext to get list of staff in anime with context.
func (c *Client) GetAnimeStaffWithContext(ctx context.Context, id int, page int, perPage int, fields ...MediaField) (*Media, error) {
if len(fields) == 0 {
fields = []MediaField{
MediaFieldStaff(
MediaParamStaff{
Page: page,
PerPage: perPage,
Sort: []StaffSort{StaffSortRelevance, StaffSortID},
},
StaffConnectionFieldEdges(
StaffEdgeFieldRole,
StaffEdgeFieldNode(
StaffFieldID,
StaffFieldName(StaffNameFieldFull),
StaffFieldImage(StaffImageFieldMedium)))),
}
}
return c.getMedia(ctx, id, MediaTypeAnime, fields...)
}
// GetAnimeStats to get anime stats.
func (c *Client) GetAnimeStats(id int, fields ...MediaField) (*Media, error) {
return c.GetAnimeStatsWithContext(context.Background(), id, fields...)
}
// GetAnimeStatsWithContext to get anime stats with context.
func (c *Client) GetAnimeStatsWithContext(ctx context.Context, id int, fields ...MediaField) (*Media, error) {
if len(fields) == 0 {
fields = []MediaField{
MediaFieldStats(
MediaStatsFieldScoreDistribution,
MediaStatsFieldStatusDistribution),
}
}
return c.getMedia(ctx, id, MediaTypeAnime, fields...)
}
// GetManga to get manga data.
func (c *Client) GetManga(id int, fields ...MediaField) (*Media, error) {
return c.GetMangaWithContext(context.Background(), id, fields...)
}
// GetMangaWithContext to get manga data with context.
func (c *Client) GetMangaWithContext(ctx context.Context, id int, fields ...MediaField) (*Media, error) {
if len(fields) == 0 {
fields = []MediaField{
MediaFieldID,
MediaFieldTitle(
MediaTitleFieldRomaji,
MediaTitleFieldEnglish,
MediaTitleFieldNative),
MediaFieldType,
MediaFieldFormat,
MediaFieldStatusV2,
MediaFieldDescription,
MediaFieldStartDate,
MediaFieldEndDate,
MediaFieldCountryOfOrigin,
MediaFieldSourceV2,
MediaFieldCoverImage(
MediaCoverImageFieldExtraLarge,
MediaCoverImageFieldLarge,
MediaCoverImageFieldMedium,
MediaCoverImageFieldColor),
MediaFieldBannerImage,
MediaFieldGenres,
MediaFieldSynonyms,
MediaFieldAverageScore,
MediaFieldMeanScore,
MediaFieldPopularity,
MediaFieldFavourites,
MediaFieldTags(
MediaTagFieldName,
MediaTagFieldDescription,
MediaTagFieldCategory,
MediaTagFieldRank,
MediaTagFieldIsGeneralSpoiler,
MediaTagFieldIsMediaSpoiler,
MediaTagFieldIsAdult),
MediaFieldRelations(
MediaConnectionFieldPageInfo(
PageInfoFieldTotal,
PageInfoFieldPerPage,
PageInfoFieldCurrentPage,
PageInfoFieldLastPage,
PageInfoFieldHasNextPage),
MediaConnectionFieldEdges(
MediaEdgeFieldRelationTypeV2,
MediaEdgeFieldNode(
MediaFieldID,
MediaFieldTitle(
MediaTitleFieldRomaji,
MediaTitleFieldEnglish,
MediaTitleFieldNative),
MediaFieldFormat,
MediaFieldType,
MediaFieldStatusV2,
MediaFieldCoverImage(
MediaCoverImageFieldMedium)))),
MediaFieldIsAdult,
MediaFieldRankings(
MediaRankFieldID,
MediaRankFieldRank,
MediaRankFieldType,
MediaRankFieldFormat,
MediaRankFieldYear,
MediaRankFieldSeason,
MediaRankFieldAllTime,
MediaRankFieldContext,
),
MediaFieldChapters,
MediaFieldVolumes,
}
}
return c.getMedia(ctx, id, MediaTypeManga, fields...)
}
// GetMangaCharacters to get list of characters in manga.
func (c *Client) GetMangaCharacters(id int, page int, perPage int, fields ...MediaField) (*Media, error) {
return c.GetMangaCharactersWithContext(context.Background(), id, page, perPage, fields...)
}
// GetMangaCharactersWithContext to get list of characters in manga with context.
func (c *Client) GetMangaCharactersWithContext(ctx context.Context, id int, page int, perPage int, fields ...MediaField) (*Media, error) {
if len(fields) == 0 {
fields = []MediaField{
MediaFieldCharacters(
MediaParamCharacters{
Page: page,
PerPage: perPage,
Sort: []CharacterSort{CharacterSortRole, CharacterSortRelevance, CharacterSortID},
},
CharacterConnectionFieldPageInfo(
PageInfoFieldTotal,
PageInfoFieldPerPage,
PageInfoFieldCurrentPage,
PageInfoFieldLastPage,
PageInfoFieldHasNextPage),
CharacterConnectionFieldEdges(
CharacterEdgeFieldRole,
CharacterEdgeFieldNode(
CharacterFieldID,
CharacterFieldName(CharacterNameFieldFull),
CharacterFieldImage(CharacterImageFieldMedium)))),
}
}
return c.getMedia(ctx, id, MediaTypeManga, fields...)
}
// GetMangaStaff to get list of staff in manga.
func (c *Client) GetMangaStaff(id int, page int, perPage int, fields ...MediaField) (*Media, error) {
return c.GetMangaStaffWithContext(context.Background(), id, page, perPage, fields...)
}
// GetMangaStaffWithContext to get list of staff in manga with context.
func (c *Client) GetMangaStaffWithContext(ctx context.Context, id int, page int, perPage int, fields ...MediaField) (*Media, error) {
if len(fields) == 0 {
fields = []MediaField{
MediaFieldStaff(
MediaParamStaff{
Page: page,
PerPage: perPage,
Sort: []StaffSort{StaffSortRelevance, StaffSortID},
},
StaffConnectionFieldEdges(
StaffEdgeFieldRole,
StaffEdgeFieldNode(
StaffFieldID,
StaffFieldName(StaffNameFieldFull),
StaffFieldImage(StaffImageFieldMedium)))),
}
}
return c.getMedia(ctx, id, MediaTypeManga, fields...)
}
// GetMangaStats to get manga stats.
func (c *Client) GetMangaStats(id int, fields ...MediaField) (*Media, error) {
return c.GetMangaStatsWithContext(context.Background(), id, fields...)
}
// GetMangaStatsWithContext to get manga stats with context.
func (c *Client) GetMangaStatsWithContext(ctx context.Context, id int, fields ...MediaField) (*Media, error) {
if len(fields) == 0 {
fields = []MediaField{
MediaFieldStats(
MediaStatsFieldScoreDistribution,
MediaStatsFieldStatusDistribution),
}
}
return c.getMedia(ctx, id, MediaTypeManga, fields...)
}

694
verniy/model.go Normal file
View File

@@ -0,0 +1,694 @@
package verniy
// Media is main anime & manga model.
type Media struct {
ID int `json:"id"`
IDMAL *int `json:"idMal"`
Title *MediaTitle `json:"title"`
Type *MediaType `json:"type"`
Format *MediaFormat `json:"format"`
Status *MediaStatus `json:"status"`
Description *string `json:"description"`
StartDate *FuzzyDate `json:"startDate"`
EndDate *FuzzyDate `json:"endDate"`
Season *MediaSeason `json:"season"`
SeasonYear *int `json:"seasonYear"`
SeasonInt *int `json:"seasonInt"`
Episodes *int `json:"episodes"`
Duration *int `json:"duration"` // in minutes
Chapters *int `json:"chapters"`
Volumes *int `json:"volumes"`
CountryOfOrigin *string `json:"countryOfOrigin"`
IsLicensed *bool `json:"isLicensed"`
Source *MediaSource `json:"source"`
HashTag *string `json:"hashTag"`
Trailer *MediaTrailer `json:"trailer"`
UpdatedAt *int `json:"updatedAt"`
CoverImage *MediaCoverImage `json:"coverImage"`
BannerImage *string `json:"bannerImage"`
Genres []string `json:"genres"`
Synonyms []string `json:"synonyms"`
AverageScore *int `json:"averageScore"`
MeanScore *int `json:"meanScore"`
Popularity *int `json:"popularity"`
IsLocked *bool `json:"isLocked"`
Trending *int `json:"trending"`
Favourites *int `json:"favourites"`
Tags []MediaTag `json:"tags"`
Relations *MediaConnection `json:"relations"`
Characters *CharacterConnection `json:"characters"`
Staff *StaffConnection `json:"staff"`
Studios *StudioConnection `json:"studios"`
IsFavourite *bool `json:"isFavourite"`
IsAdult *bool `json:"isAdult"`
NextAiringEpisode *AiringSchedule `json:"nextAiringEpisode"`
AiringSchedule *AiringScheduleConnection `json:"airingSchedule"`
Trends *MediaTrendConnection `json:"trends"`
ExternalLinks []MediaExternalLink `json:"externalLinks"`
StreamingEpisodes []MediaStreamingEpisode `json:"streamingEpisodes"`
Rankings []MediaRank `json:"rankings"`
MediaListEntry *MediaList `json:"mediaListEntry"`
Reviews *ReviewConnection `json:"reviews"`
Recommendations *RecommendationConnection `json:"recommendations"`
Stats *MediaStats `json:"stats"`
SiteURL *string `json:"siteUrl"`
AutoCreateForumThread *bool `json:"autoCreateForumThread"`
IsRecommendationBlocked *bool `json:"isRecommendationBlocked"`
ModNotes *string `json:"modNotes"`
}
// MediaTitle is anime & manga titles.
type MediaTitle struct {
Romaji *string `json:"romaji"`
English *string `json:"english"`
Native *string `json:"native"`
UserPreferred *string `json:"userPreferred"`
}
// FuzzyDateInt is 8 digit long date integer (YYYYMMDD).
type FuzzyDateInt int
// FuzzyDate is common date format.
type FuzzyDate struct {
Year *int `json:"year"`
Month *int `json:"month"`
Day *int `json:"day"`
}
// MediaTrailer is anime & manga trailer data.
type MediaTrailer struct {
ID *string `json:"id"`
Site *string `json:"site"`
Thumbnail *string `json:"thumbnail"`
}
// MediaCoverImage is anime & manga cover image model.
type MediaCoverImage struct {
ExtraLarge *string `json:"extraLarge"`
Large *string `json:"large"`
Medium *string `json:"medium"`
Color *string `json:"color"`
}
// MediaTag is anime & manga tag model.
type MediaTag struct {
ID int `json:"id"`
Name string `json:"name"`
Description *string `json:"description"`
Category *string `json:"category"`
Rank *int `json:"rank"`
IsGeneralSpoiler *bool `json:"isGeneralSpoiler"`
IsMediaSpoiler *bool `json:"isMediaSpoiler"`
IsAdult *bool `json:"isAdult"`
}
// MediaConnection is anime & manga related model.
type MediaConnection struct {
Edges []MediaEdge `json:"edges"`
Nodes []Media `json:"nodes"`
PageInfo *PageInfo `json:"pageInfo"`
}
// MediaEdge is anime & manga detail related model.
type MediaEdge struct {
Node *Media `json:"node"`
ID *int `json:"id"`
RelationType *MediaRelation `json:"relationType"`
IsMainStudio bool `json:"isMainStudio"`
Characters []Character `json:"characters"`
CharacterRole *CharacterRole `json:"characterRole"`
CharacterName *string `json:"characterName"`
RoleNotes *string `json:"roleNotes"`
DubGroup *string `json:"dubGroup"`
StaffRole *string `json:"staffRole"`
VoiceActors []Staff `json:"voiceActors"`
VoiceActorRoles []StaffRoleType `json:"voiceActorRoles"`
FavouriteOrder *int `json:"favouriteOrder"`
}
// StaffRoleType is type of staff role.
type StaffRoleType struct {
VoiceActor *Staff `json:"voiceActor"`
RoleNotes *string `json:"roleNotes"`
DubGroup *string `json:"dubGroup"`
}
// PageInfo is common pagination model.
type PageInfo struct {
Total *int `json:"total"`
PerPage *int `json:"perPage"`
CurrentPage *int `json:"currentPage"`
LastPage *int `json:"lastPage"`
HasNextPage *bool `json:"hasNextPage"`
}
// CharacterConnection is character related model.
type CharacterConnection struct {
Edges []CharacterEdge `json:"edges"`
Nodes []Character `json:"nodes"`
PageInfo *PageInfo `json:"pageInfo"`
}
// CharacterEdge is detail character related model.
type CharacterEdge struct {
Node *Character `json:"node"`
ID *int `json:"id"`
Role *CharacterRole `json:"role"`
Name *string `json:"name"`
VoiceActors []Staff `json:"voiceActors"`
VoiceActorRoles []StaffRoleType `json:"voiceActorRoles"`
Media []Media `json:"media"`
FavouriteOrder *int `json:"favouriteOrder"`
}
// Character is main character model.
type Character struct {
ID int `json:"id"`
Name *CharacterName `json:"name"`
Image *CharacterImage `json:"image"`
Description *string `json:"description"`
Gender *string `json:"gender"`
DateOfBirth *FuzzyDate `json:"dateOfBirth"`
Age *string `json:"age"`
IsFavourite *bool `json:"isFavourite"`
IsFavouriteBlocked *bool `json:"isFavouriteBlocked"`
SiteURL *string `json:"siteURL"`
Media *MediaConnection `json:"media"`
Favourites *int `json:"favourites"`
ModNotes *string `json:"modNotes"`
}
// CharacterName is character names.
type CharacterName struct {
First *string `json:"first"`
Middle *string `json:"middle"`
Last *string `json:"last"`
Full *string `json:"full"`
Native *string `json:"native"`
Alternative []string `json:"alternative"`
AlternativeSpoiler []string `json:"alternativeSpoiler"`
UserPreferred *string `json:"userPreferred"`
}
// CharacterImage is character image.
type CharacterImage struct {
Large *string `json:"large"`
Medium *string `json:"medium"`
}
// StaffConnection is staff related model.
type StaffConnection struct {
Edges []StaffEdge `json:"edges"`
Nodes []Staff `json:"nodes"`
PageInfo *PageInfo `json:"pageInfo"`
}
// StaffEdge is detail staff related model.
type StaffEdge struct {
Node *Staff `json:"node"`
ID *int `json:"id"`
Role *string `json:"role"`
FavouriteOrder *int `json:"favouriteOrder"`
}
// Staff is main staff model.
type Staff struct {
ID int `json:"id"`
Name *StaffName `json:"name"`
LanguageV2 *string `json:"languageV2"`
Image *StaffImage `json:"image"`
Description *string `json:"description"`
PrimaryOccupations []string `json:"primaryOccupation"`
Gender *string `json:"gender"`
DateOfBirth *FuzzyDate `json:"dateOfBirth"`
DateOfDeath *FuzzyDate `json:"dateOfDeath"`
Age *int `json:"age"`
YearsActive []int `json:"yearsActive"`
HomeTown *string `json:"homeTown"`
IsFavourite bool `json:"isFavourite"`
IsFavouriteBlocked bool `json:"isFavouriteBlocked"`
SiteURL *string `json:"siteURL"`
StaffMedia *MediaConnection `json:"staffMedia"`
Characters *CharacterConnection `json:"characters"`
CharacterMedia *MediaConnection `json:"characterMedia"`
Staff *Staff `json:"staff"`
Submitter *User `json:"submitter"`
SubmissionStatus *int `json:"submissionStatus"`
SubmissionNotes *string `json:"submissionNotes"`
Favourites *int `json:"favourites"`
ModNotes *string `json:"modNotes"`
}
// StaffName is staff names.
type StaffName struct {
First *string `json:"first"`
Middle *string `json:"middle"`
Last *string `json:"last"`
Full *string `json:"full"`
Native *string `json:"native"`
Alternative []string `json:"alternative"`
UserPreferred *string `json:"userPreferred"`
}
// StaffImage is staff image.
type StaffImage struct {
Large *string `json:"large"`
Medium *string `json:"medium"`
}
// StudioConnection is studio related model.
type StudioConnection struct {
Edges []StudioEdge `json:"edges"`
Nodes []Studio `json:"nodes"`
PageInfo *PageInfo `json:"pageInfo"`
}
// StudioEdge is detail studio related model.
type StudioEdge struct {
Node *Studio `json:"node"`
ID *int `json:"id"`
IsMain bool `json:"isMain"`
FavouriteOrder *int `json:"favouriteOrder"`
}
// Studio is main studio model.
type Studio struct {
ID int `json:"id"`
Name string `json:"name"`
IsAnimationStudio bool `json:"isAnimationStudio"`
Media *MediaConnection `json:"media"`
SiteURL *string `json:"siteURL"`
IsFavourite bool `json:"isFavourite"`
Favourites *int `json:"favourites"`
}
// AiringSchedule is anime airing schedule model.
type AiringSchedule struct {
ID int `json:"id"`
AiringAt int `json:"airingAt"`
TimeUntilAiring int `json:"timeUntilAiring"`
Episode int `json:"episode"`
MediaID int `json:"mediaId"`
Media *Media `json:"media"`
}
// AiringScheduleConnection is anime related airing schedule model.
type AiringScheduleConnection struct {
Edges []AiringScheduleEdge `json:"edges"`
Nodes []AiringSchedule `json:"nodes"`
PageInfo *PageInfo `json:"pageInfo"`
}
// AiringScheduleEdge is airing schedule edge model.
type AiringScheduleEdge struct {
Node *AiringSchedule `json:"node"`
ID *int `json:"id"`
}
// MediaTrendConnection is media trend connection model.
type MediaTrendConnection struct {
Edges []MediaTrendEdge `json:"edges"`
Nodes []MediaTrend `json:"nodes"`
PageInfo *PageInfo `json:"pageInfo"`
}
// MediaTrendEdge is media trend edge model.
type MediaTrendEdge struct {
Node *MediaTrend `json:"node"`
}
// MediaTrend is media trend model.
type MediaTrend struct {
MediaID int `json:"mediaId"`
Date int `json:"date"`
Trending int `json:"trending"`
AverageScore *int `json:"averageScore"`
Popularity *int `json:"popularity"`
InProgress *int `json:"inProgress"`
Releasing bool `json:"releasing"`
Episode *int `json:"episode"`
Media *Media `json:"media"`
}
// MediaExternalLink is anime & manga site links.
type MediaExternalLink struct {
ID int `json:"id"`
URL string `json:"url"`
Site string `json:"site"`
}
// MediaStreamingEpisode is anime streaming site links.
type MediaStreamingEpisode struct {
Title *string `json:"title"`
Thumbnail *string `json:"thumbnail"`
URL *string `json:"url"`
Site *string `json:"site"`
}
// MediaRank is anime & manga ranking model.
type MediaRank struct {
ID int `json:"id"`
Rank int `json:"rank"`
Type MediaRankType `json:"type"`
Format MediaFormat `json:"format"`
Year *int `json:"year"`
Season *MediaSeason `json:"season"`
AllTime *bool `json:"allTime"`
Context string `json:"context"`
}
// MediaList is user's anime & manga model.
type MediaList struct {
ID int `json:"id"`
UserID int `json:"userId"`
MediaID int `json:"mediaId"`
Status *MediaListStatus `json:"status"`
Score *float64 `json:"score"`
Progress *int `json:"progress"`
ProgressVolumes *int `json:"progressVolumes"`
Repeat *int `json:"repeat"`
Priority *int `json:"priority"`
Private *bool `json:"private"`
Notes *string `json:"notes"`
HiddenFromStatusLists *bool `json:"hiddenFromStatusLists"`
CustomLists *string `json:"customLists"` // json
AdvancedScores *string `json:"advancedScores"` // json
StartedAt *FuzzyDate `json:"startedAt"`
CompletedAt *FuzzyDate `json:"completedAt"`
UpdatedAt *int `json:"updatedAt"`
CreatedAt *int `json:"createdAt"`
Media *Media `json:"media"`
User *User `json:"user"`
}
// User is main user model.
type User struct {
ID int `json:"id"`
Name string `json:"name"`
About *string `json:"about"`
Avatar *UserAvatar `json:"avatar"`
BannerImage *string `json:"bannerImage"`
IsFollowing *bool `json:"isFollowing"`
IsFollower *bool `json:"isFollower"`
IsBlocked *bool `json:"isBlocked"`
Bans *string `json:"bans"` // json
Options *UserOptions `json:"options"`
MediaListOptions *MediaListOptions `json:"mediaListOptions"`
Favourites *Favourites `json:"favourites"`
Statistics *UserStatisticTypes `json:"statistics"`
UnreadNotificationCount *int `json:"unreadNotificationCount"`
SiteURL *string `json:"siteUrl"`
DonatorTier *int `json:"donatorTier"`
DonatorBadge *string `json:"donatorBadge"`
ModeratorRoles []ModRole `json:"moderatorRoles"`
CreatedAt *int `json:"createdAt"`
UpdatedAt *int `json:"updatedAt"`
}
// UserAvatar is user avatar image.
type UserAvatar struct {
Large *string `json:"large"`
Medium *string `json:"medium"`
}
// UserOptions is user option model.
type UserOptions struct {
TitleLanguage *UserTitleLanguage `json:"titleLanguage"`
DisplayAdultContent *bool `json:"displayAdultContent"`
AiringNotifications *bool `json:"airingNotification"`
ProfileColor *string `json:"profileColor"`
NotificationOptions []NotificationOption `json:"notificationOptions"`
Timezone *string `json:"timezone"`
ActivityMergeTime *int `json:"activityMergeTime"`
StaffNameLanguage *UserStaffNameLanguage `json:"staffNameLanguage"`
}
// NotificationOption is user notification option model.
type NotificationOption struct {
Type *NotificationType `json:"type"`
Enabled *bool `json:"enabled"`
}
// MediaListOptions is user anime & manga option model.
type MediaListOptions struct {
ScoreFormat *ScoreFormat `json:"scoreFormat"`
RowOrder *string `json:"rowOrder"`
AnimeList *MediaListTypeOptions `json:"animeList"`
MangaList *MediaListTypeOptions `json:"mangaList"`
}
// MediaListTypeOptions is media list type options model.
type MediaListTypeOptions struct {
SectionOrder []string `json:"sectionOrder"`
SplitCompletedSectionByFormat *bool `json:"splitCompletedSectionByFormat"`
CustomLists []string `json:"customLists"`
AdvancedScoring []string `json:"advancedScoring"`
AdvancedScoringEnabled *bool `json:"advancedScoringEnabled"`
}
// Favourites is user's favourite model.
type Favourites struct {
Anime *MediaConnection `json:"anime"`
Manga *MediaConnection `json:"manga"`
Characters *CharacterConnection `json:"characters"`
Staff *StaffConnection `json:"staff"`
Studios *StudioConnection `json:"studios"`
}
// UserStatisticTypes is user statistic data.
type UserStatisticTypes struct {
Anime *UserStatistics `json:"anime"`
Manga *UserStatistics `json:"manga"`
}
// UserStatistics is detail user statistic data.
type UserStatistics struct {
Count int `json:"count"`
MeanScore float64 `json:"meanScore"`
StandardDeviation float64 `json:"standardDeviation"`
MinutesWatched int `json:"minutesWatched"`
EpisodesWatched int `json:"episodesWatched"`
ChaptersRead int `json:"chaptersRead"`
VolumesRead int `json:"volumesRead"`
Formats []UserFormatStatistic `json:"formats"`
Statuses []UserStatusStatistic `json:"statuses"`
Scores []UserScoreStatistic `json:"scores"`
Lengths []UserLengthStatistic `json:"lengths"`
ReleaseYears []UserReleaseYearStatistic `json:"releaseYears"`
StartYears []UserStartYearStatistic `json:"startYears"`
Genres []UserGenreStatistic `json:"genres"`
Tags []UserTagStatistic `json:"tags"`
Countries []UserCountryStatistic `json:"countries"`
VoiceActors []UserVoiceActorStatistic `json:"voiceActors"`
Staff []UserStaffStatistic `json:"staff"`
Studios []UserStudioStatistic `json:"studios"`
}
// UserFormatStatistic is user format stats.
type UserFormatStatistic struct {
Count int `json:"count"`
MeanScore float64 `json:"meanScore"`
MinutesWatched int `json:"minutesWatched"`
ChapterRead int `json:"chapterRead"`
MediaIDs []int `json:"mediaIDs"`
Format *MediaFormat `json:"format"`
}
// UserStatusStatistic is user status stats.
type UserStatusStatistic struct {
Count int `json:"count"`
MeanScore float64 `json:"meanScore"`
MinutesWatched int `json:"minutesWatched"`
ChaptersRead int `json:"chaptersRead"`
MediaIDs []int `json:"mediaIDs"`
Status *MediaListStatus `json:"status"`
}
// UserScoreStatistic is user score stats.
type UserScoreStatistic struct {
Count int `json:"count"`
MeanScore float64 `json:"meanScore"`
MinutesWatched int `json:"minutesWatched"`
ChaptersRead int `json:"chaptersRead"`
MediaIDs []int `json:"mediaIDs"`
Score *int `json:"score"`
}
// UserLengthStatistic is user watch/read duration stats.
type UserLengthStatistic struct {
Count int `json:"count"`
MeanScore float64 `json:"meanScore"`
MinutesWatched int `json:"minutesWatched"`
ChaptersRead int `json:"chaptersRead"`
MediaIDs []int `json:"mediaIDs"`
Length *string `json:"length"`
}
// UserReleaseYearStatistic is user anime & manga release year stats.
type UserReleaseYearStatistic struct {
Count int `json:"count"`
MeanScore float64 `json:"meanScore"`
MinutesWatched int `json:"minutesWatched"`
ChaptersRead int `json:"chaptersRead"`
MediaIDs []int `json:"mediaIDs"`
ReleaseYear *int `json:"releaseYear"`
}
// UserStartYearStatistic is user start year stats.
type UserStartYearStatistic struct {
Count int `json:"count"`
MeanScore float64 `json:"meanScore"`
MinutesWatched int `json:"minutesWatched"`
ChaptersRead int `json:"chaptersRead"`
MediaIDs []int `json:"mediaIDs"`
StartYear *int `json:"startYear"`
}
// UserGenreStatistic is user genre stats.
type UserGenreStatistic struct {
Count int `json:"count"`
MeanScore float64 `json:"meanScore"`
MinutesWatched int `json:"minutesWatched"`
ChaptersRead int `json:"chaptersRead"`
MediaIDs []int `json:"mediaIDs"`
Genre *string `json:"genre"`
}
// UserTagStatistic is user tag stats.
type UserTagStatistic struct {
Count int `json:"count"`
MeanScore float64 `json:"meanScore"`
MinutesWatched int `json:"minutesWatched"`
ChaptersRead int `json:"chaptersRead"`
MediaIDs []int `json:"mediaIDs"`
Tag *MediaTag `json:"tag"`
}
// UserCountryStatistic is user anime & manga country stats.
type UserCountryStatistic struct {
Count int `json:"count"`
MeanScore float64 `json:"meanScore"`
MinutesWatched int `json:"minutesWatched"`
ChaptersRead int `json:"chaptersRead"`
MediaIDs []int `json:"mediaIDs"`
Country *string `json:"country"`
}
// UserVoiceActorStatistic is user voice actor stats.
type UserVoiceActorStatistic struct {
Count int `json:"count"`
MeanScore float64 `json:"meanScore"`
MinutesWatched int `json:"minutesWatched"`
ChaptersRead int `json:"chaptersRead"`
MediaIDs []int `json:"mediaIDs"`
VoiceActor *Staff `json:"voiceActor"`
CharacterIDs []int `json:"characterIDs"`
}
// UserStaffStatistic is user staff stats.
type UserStaffStatistic struct {
Count int `json:"count"`
MeanScore float64 `json:"meanScore"`
MinutesWatched int `json:"minutesWatched"`
ChaptersRead int `json:"chaptersRead"`
MediaIDs []int `json:"mediaIDs"`
Staff *Staff `json:"staff"`
}
// UserStudioStatistic is user studio stats.
type UserStudioStatistic struct {
Count int `json:"count"`
MeanScore float64 `json:"meanScore"`
MinutesWatched int `json:"minutesWatched"`
ChaptersRead int `json:"chaptersRead"`
MediaIDs []int `json:"mediaIDs"`
Studio *Studio `json:"studio"`
}
// ReviewConnection is anime & manga related review.
type ReviewConnection struct {
Edges []ReviewEdge `json:"edges"`
Nodes []Review `json:"nodes"`
PageInfo *PageInfo `json:"pageInfo"`
}
// ReviewEdge is detail anime & manga related review.
type ReviewEdge struct {
Node *Review `json:"node"`
}
// Review is main review model.
type Review struct {
ID int `json:"id"`
UserID int `json:"userId"`
MediaID int `json:"mediaId"`
MediaType *MediaType `json:"mediaType"`
Summary *string `json:"summary"`
Body *string `json:"body"`
Rating *int `json:"rating"`
RatingAmount *int `json:"ratingAmount"`
UserRating *ReviewRating `json:"userRating"`
Score *int `json:"score"`
Private *bool `json:"private"`
SiteURL *string `json:"siteUrl"`
CreatedAt int `json:"createdAt"`
UpdatedAt int `json:"updatedAt"`
User *User `json:"user"`
Media *Media `json:"media"`
}
// RecommendationConnection is anime & manga related recommendation.
type RecommendationConnection struct {
Edges []RecommendationEdge `json:"edges"`
Nodes []Recommendation `json:"nodes"`
PageInfo *PageInfo `json:"pageInfo"`
}
// RecommendationEdge is detail anime & manga related recommendation.
type RecommendationEdge struct {
Node *Recommendation `json:"node"`
}
// Recommendation is main recommendation model.
type Recommendation struct {
ID int `json:"id"`
Rating *int `json:"rating"`
UserRating *RecommendationRating `json:"userRating"`
Media *Media `json:"media"`
MediaRecommendation *Media `json:"mediaRecommendation"`
User *User `json:"user"`
}
// MediaStats is anime & manga stats.
type MediaStats struct {
ScoreDistribution []ScoreDistribution `json:"scoreDistribution"`
StatusDistribution []StatusDistribution `json:"statusDistribution"`
}
// ScoreDistribution is detail anime & manga score stats.
type ScoreDistribution struct {
Score *int `json:"score"`
Amount *int `json:"amount"`
}
// StatusDistribution is detail anime & manga status stats.
type StatusDistribution struct {
Status *MediaListStatus `json:"status"`
Amount *int `json:"amount"`
}
// MediaListCollection is collection of media list.
type MediaListCollection struct {
Lists []MediaListGroup `json:"lists"`
User *User `json:"user"`
HasNextChunk *bool `json:"hasNextChunk"`
}
// MediaListGroup is group of media list.
type MediaListGroup struct {
Entries []MediaList `json:"entries"`
Name *string `json:"name"`
IsCustomList *bool `json:"isCustomList"`
IsSplitCompletedList *bool `json:"isSplitCompletedList"`
Status *MediaListStatus `json:"status"`
}

55
verniy/page.go Normal file
View File

@@ -0,0 +1,55 @@
package verniy
import "context"
type pageResponse struct {
Data struct {
Page Page `json:"Page"`
} `json:"data"`
}
// Page is pagination response from anilist.
type Page struct {
PageInfo PageInfo `json:"pageInfo"`
Media []Media `json:"media"`
Characters []Character `json:"characters"`
Staff []Staff `json:"staff"`
Studios []Studio `json:"studios"`
}
func (c *Client) pageQuery(params QueryParam, fields ...PageField) string {
p := make([]string, len(fields))
for i := range fields {
p[i] = string(fields[i])
}
return FieldObject("Page", params, p...)
}
func (c *Client) page(ctx context.Context, page int, perPage int, fields ...PageField) (*Page, error) {
fields = append(fields, PageFieldPageInfo(
PageInfoFieldTotal,
PageInfoFieldPerPage,
PageInfoFieldCurrentPage,
PageInfoFieldLastPage,
PageInfoFieldHasNextPage,
))
query := FieldObject("query", QueryParam{
"$page": "Int",
"$perPage": "Int",
}, c.pageQuery(QueryParam{
"page": "$page",
"perPage": "$perPage",
}, fields...))
var d pageResponse
err := c.post(ctx, query, queryVariable{
"page": page,
"perPage": perPage,
}, &d)
if err != nil {
return nil, err
}
return &d.Data.Page, nil
}

352
verniy/param.go Normal file
View File

@@ -0,0 +1,352 @@
package verniy
// MediaEdgeParamVoiceActors is media edge param for voice actors.
type MediaEdgeParamVoiceActors struct {
Language StaffLanguage
Sort []StaffSort
}
// CharacterEdgeParamVoiceActors is character edge param for voice actors.
type CharacterEdgeParamVoiceActors struct {
Language StaffLanguage
Sort []StaffSort
}
// MediaEdgeParamVoiceActorRoles is media edge param for voice actor roles.
type MediaEdgeParamVoiceActorRoles struct {
Language StaffLanguage
Sort []StaffSort
}
// CharacterEdgeParamVoiceActorRoles is character edge param for voice actor roles.
type CharacterEdgeParamVoiceActorRoles struct {
Language StaffLanguage
Sort []StaffSort
}
// MediaParamCharacters is media param for characters.
type MediaParamCharacters struct {
Page int
PerPage int
Role CharacterRole
Sort []CharacterSort
}
// CharacterParamMedia is character param for media.
type CharacterParamMedia struct {
Type MediaType
OnList *bool
Page int
PerPage int
Sort []MediaSort
}
// StaffParamStaffMedia is staff param for staff media.
type StaffParamStaffMedia struct {
Type MediaType
OnList *bool
Page int
PerPage int
Sort []MediaSort
}
// StaffParamCharacters is staff param for characters.
type StaffParamCharacters struct {
Page int
PerPage int
Sort []CharacterSort
}
// StaffParamCharacterMedia is staff param for character media.
type StaffParamCharacterMedia struct {
OnList *bool
Page int
PerPage int
Sort []MediaSort
}
// MediaParamStaff is media param for staff.
type MediaParamStaff struct {
Page int
PerPage int
Sort []StaffSort
}
// MediaParamStudios is media param for studios.
type MediaParamStudios struct {
IsMain *bool
Sort []StudioSort
}
// StudioParamMedia is studio param for media.
type StudioParamMedia struct {
IsMain *bool
OnList *bool
Page int
PerPage int
Sort []MediaSort
}
// MediaParamAiringSchedule is media param for airing schedule.
type MediaParamAiringSchedule struct {
NotYetAired *bool
Page int
PerPage int
}
// MediaParamTrends is media param for trends.
type MediaParamTrends struct {
Page int
PerPage int
Releasing *bool
Sort []MediaTrendSort
}
// MediaParamReviews is media param for reviews.
type MediaParamReviews struct {
Page int
PerPage int
Sort []ReviewSort
Limit int
}
// MediaParamRecommendations is media param for recommendation
type MediaParamRecommendations struct {
Page int
PerPage int
Sort []RecommendationSort
}
// PageParamStudios is page param for studios.
type PageParamStudios struct {
Search string
ID int
IDNot int
IDIn []int
IDNotIn []int
Sort []StudioSort
}
// PageParamMedia is page param for media.
type PageParamMedia struct {
ID int
IDMAL int
StartDate int
EndDate int
Season MediaSeason
SeasonYear int
Type MediaType
Format MediaFormat
Status MediaStatus
Episodes int
Duration int
Chapters int
Volumes int
IsAdult *bool
Genre string
Tag string
MinimumTagRank int
TagCategory string
OnList *bool
LicensedBy string
AverageScore int
Popularity int
Source MediaSource
CountryOfOrigin string
Search string
IDNot int
IDIn []int
IDNotIn []int
IDMALNot int
IDMALIn []int
IDMALNotIn []int
StartDateGreater int
StartDateLesser int
StartDateLike string
EndDateGreater int
EndDateLesser int
EndDateLike string
FormatIn []MediaFormat
FormatNot MediaFormat
FormatNotIn []MediaFormat
StatusIn []MediaStatus
StatusNot MediaStatus
StatusNotIn []MediaStatus
EpisodesGreater int
EpisodesLesser int
DurationGreater int
DurationLesser int
ChaptersGreater int
ChaptersLesser int
VolumesGreater int
VolumesLesser int
GenreIn []string
GenreNotIn []string
TagIn []string
TagNotIn []string
TagCategoryIn []string
TagCategoryNotIn []string
LicensedByIn []string
AverageScoreNot int
AverageScoreGreater int
AverageScoreLesser int
PopularityNot int
PopularityGreater int
PopularityLesser int
SourceIn []MediaSource
Sort []MediaSort
}
// PageParamCharacters is page param for characters.
type PageParamCharacters struct {
ID int
IsBirthday *bool
Search string
IDNot int
IDIn []int
IDNotIn []int
Sort []CharacterSort
}
// PageParamStaff is page param for staff.
type PageParamStaff struct {
ID int
IsBirthday *bool
Search string
IDNot int
IDIn []int
IDNotIn []int
Sort []StaffSort
}
// UserParamFavourites is user param for favourites.
type UserParamFavourites struct {
Page int
}
// FavouritesParamAnime is favourites param for anime.
type FavouritesParamAnime struct {
Page int
PerPage int
}
// FavouritesParamManga is favourites param for manga.
type FavouritesParamManga struct {
Page int
PerPage int
}
// FavouritesParamCharacters is favourites param for characters.
type FavouritesParamCharacters struct {
Page int
PerPage int
}
// FavouritesParamStaff is favourites param for staff.
type FavouritesParamStaff struct {
Page int
PerPage int
}
// FavouritesParamStudios is favourites param for studios.
type FavouritesParamStudios struct {
Page int
PerPage int
}
// UserStatisticsParamFormats is user statistics param for formats.
type UserStatisticsParamFormats struct {
Limit int
Sort []UserStatisticsSort
}
// UserStatisticsParamStatuses is user statistics param for statuses.
type UserStatisticsParamStatuses struct {
Limit int
Sort []UserStatisticsSort
}
// UserStatisticsParamScores is user statistics param for scores.
type UserStatisticsParamScores struct {
Limit int
Sort []UserStatisticsSort
}
// UserStatisticsParamLengths is user statistics param for lengths.
type UserStatisticsParamLengths struct {
Limit int
Sort []UserStatisticsSort
}
// UserStatisticsParamReleaseYears is user statistics param for release years.
type UserStatisticsParamReleaseYears struct {
Limit int
Sort []UserStatisticsSort
}
// UserStatisticsParamStartYears is user statistics param for start years.
type UserStatisticsParamStartYears struct {
Limit int
Sort []UserStatisticsSort
}
// UserStatisticsParamGenres is user statistics param for genres.
type UserStatisticsParamGenres struct {
Limit int
Sort []UserStatisticsSort
}
// UserStatisticsParamTags is user statistics param for tags.
type UserStatisticsParamTags struct {
Limit int
Sort []UserStatisticsSort
}
// UserStatisticsParamCountries is user statistics param for countries.
type UserStatisticsParamCountries struct {
Limit int
Sort []UserStatisticsSort
}
// UserStatisticsParamVoiceActors is user statistics param for voice actors.
type UserStatisticsParamVoiceActors struct {
Limit int
Sort []UserStatisticsSort
}
// UserStatisticsParamStaff is user statistics param for staff.
type UserStatisticsParamStaff struct {
Limit int
Sort []UserStatisticsSort
}
// UserStatisticsParamStudios is user statistics param for studios.
type UserStatisticsParamStudios struct {
Limit int
Sort []UserStatisticsSort
}
// MediaListCollectionParam is media list collection param.
type MediaListCollectionParam struct {
UserID int
Username string
Type MediaType
Status MediaListStatus
Notes string
StartedAt FuzzyDateInt
CompletedAt FuzzyDateInt
ForceSingleCompletedList *bool
Chunk int
PerChunk int
StatusIn []MediaListStatus
StatusNotIn []MediaListStatus
StatusNot MediaListStatus
NotesLike string
StartedAtGreater FuzzyDateInt
StartedAtLesser FuzzyDateInt
StartedAtLike string
CompletedAtGreater FuzzyDateInt
CompletedAtLesser FuzzyDateInt
CompletedAtLike string
Sort []MediaListSort
}

131
verniy/search.go Normal file
View File

@@ -0,0 +1,131 @@
package verniy
import "context"
// SearchAnime to search anime.
func (c *Client) SearchAnime(query PageParamMedia, page int, perPage int, fields ...MediaField) (*Page, error) {
return c.SearchAnimeWithContext(context.Background(), query, page, perPage, fields...)
}
// SearchAnimeWithContext to search anime with context.
func (c *Client) SearchAnimeWithContext(ctx context.Context, query PageParamMedia, page int, perPage int, fields ...MediaField) (*Page, error) {
query.Type = MediaTypeAnime
if len(fields) == 0 {
isMain := true
fields = []MediaField{
MediaFieldID,
MediaFieldTitle(
MediaTitleFieldEnglish,
MediaTitleFieldRomaji,
MediaTitleFieldNative),
MediaFieldSynonyms,
MediaFieldCoverImage(
MediaCoverImageFieldLarge,
MediaCoverImageFieldColor),
MediaFieldStartDate,
MediaFieldEndDate,
MediaFieldSeason,
MediaFieldSeasonYear,
MediaFieldDescription,
MediaFieldType,
MediaFieldFormat,
MediaFieldStatusV2,
MediaFieldEpisodes,
MediaFieldDuration,
MediaFieldGenres,
MediaFieldIsAdult,
MediaFieldAverageScore,
MediaFieldPopularity,
MediaFieldStudios(
MediaParamStudios{IsMain: &isMain},
StudioConnectionFieldEdges(
StudioEdgeFieldIsMain,
StudioEdgeFieldNode(
StudioFieldID,
StudioFieldName,
StudioFieldIsAnimationStudio))),
}
}
if len(query.Sort) == 0 {
query.Sort = []MediaSort{MediaSortPopularityDesc, MediaSortScoreDesc}
}
return c.page(ctx, page, perPage, PageFieldMedia(query, "", fields...))
}
// SearchManga to search manga.
func (c *Client) SearchManga(query PageParamMedia, page int, perPage int, fields ...MediaField) (*Page, error) {
return c.SearchMangaWithContext(context.Background(), query, page, perPage, fields...)
}
// SearchMangaWithContext to search manga with context.
func (c *Client) SearchMangaWithContext(ctx context.Context, query PageParamMedia, page int, perPage int, fields ...MediaField) (*Page, error) {
query.Type = MediaTypeManga
if len(fields) == 0 {
fields = []MediaField{
MediaFieldID,
MediaFieldTitle(
MediaTitleFieldEnglish,
MediaTitleFieldRomaji,
MediaTitleFieldNative),
MediaFieldCoverImage(
MediaCoverImageFieldLarge,
MediaCoverImageFieldColor),
MediaFieldStartDate,
MediaFieldEndDate,
MediaFieldDescription,
MediaFieldType,
MediaFieldFormat,
MediaFieldStatusV2,
MediaFieldChapters,
MediaFieldVolumes,
MediaFieldGenres,
MediaFieldIsAdult,
MediaFieldAverageScore,
MediaFieldPopularity,
}
}
if len(query.Sort) == 0 {
query.Sort = []MediaSort{MediaSortPopularityDesc, MediaSortScoreDesc}
}
return c.page(ctx, page, perPage, PageFieldMedia(query, "", fields...))
}
// SearchCharacter to search character.
func (c *Client) SearchCharacter(query PageParamCharacters, page int, perPage int, fields ...CharacterField) (*Page, error) {
return c.SearchCharacterWithContext(context.Background(), query, page, perPage, fields...)
}
// SearchCharacterWithContext to search character with context.
func (c *Client) SearchCharacterWithContext(ctx context.Context, query PageParamCharacters, page int, perPage int, fields ...CharacterField) (*Page, error) {
if len(fields) == 0 {
fields = []CharacterField{
CharacterFieldID,
CharacterFieldName(CharacterNameFieldFull),
CharacterFieldImage(CharacterImageFieldLarge),
}
}
if len(query.Sort) == 0 {
query.Sort = []CharacterSort{CharacterSortSearchMatch, CharacterSortFavouritesDesc}
}
return c.page(ctx, page, perPage, PageFieldCharacters(query, "", fields...))
}
// SearchStaff to search staff.
func (c *Client) SearchStaff(query PageParamStaff, page int, perPage int, fields ...StaffField) (*Page, error) {
return c.SearchStaffWithContext(context.Background(), query, page, perPage, fields...)
}
// SearchStaffWithContext to search staff with context.
func (c *Client) SearchStaffWithContext(ctx context.Context, query PageParamStaff, page int, perPage int, fields ...StaffField) (*Page, error) {
if len(fields) == 0 {
fields = []StaffField{
StaffFieldID,
StaffFieldName(StaffNameFieldFull),
StaffFieldImage(StaffImageFieldLarge),
}
}
if len(query.Sort) == 0 {
query.Sort = []StaffSort{StaffSortSearchMatch, StaffSortFavouritesDesc}
}
return c.page(ctx, page, perPage, PageFieldStaff(query, "", fields...))
}

140
verniy/staff.go Normal file
View File

@@ -0,0 +1,140 @@
package verniy
import "context"
type staffResponse struct {
Data struct {
Staff Staff `json:"staff"`
} `json:"data"`
}
func (c *Client) staffQuery(params QueryParam, fields ...StaffField) string {
p := make([]string, len(fields))
for i := range fields {
p[i] = string(fields[i])
}
return FieldObject("Staff", params, p...)
}
// GetStaff to get staff & voice actor data.
func (c *Client) GetStaff(id int, fields ...StaffField) (*Staff, error) {
return c.GetStaffWithContext(context.Background(), id, fields...)
}
// GetStaffWithContext to get staff & voice actor data with context.
func (c *Client) GetStaffWithContext(ctx context.Context, id int, fields ...StaffField) (*Staff, error) {
if len(fields) == 0 {
fields = []StaffField{
StaffFieldID,
StaffFieldName(
StaffNameFieldFirst,
StaffNameFieldMiddle,
StaffNameFieldLast,
StaffNameFieldFull,
StaffNameFieldNative,
StaffNameFieldAlternative),
StaffFieldImage(StaffImageFieldLarge),
StaffFieldDescription,
StaffFieldFavourites,
StaffFieldAge,
StaffFieldHomeTown,
StaffFieldDateOfBirth,
StaffFieldDateOfDeath,
}
}
query := FieldObject("query", QueryParam{
"$id": "Int",
}, c.staffQuery(QueryParam{
"id": "$id",
}, fields...))
var d staffResponse
err := c.post(ctx, query, queryVariable{
"id": id,
}, &d)
if err != nil {
return nil, err
}
return &d.Data.Staff, nil
}
// GetStaffCharacters to get list of character the staff play.
func (c *Client) GetStaffCharacters(id int, page int, perPage int) (*Staff, error) {
return c.GetStaffCharactersWithContext(context.Background(), id, page, perPage)
}
// GetStaffCharactersWithContext to get list of character the staff play with context.
func (c *Client) GetStaffCharactersWithContext(ctx context.Context, id int, page int, perPage int) (*Staff, error) {
return c.GetStaffWithContext(ctx, id, StaffFieldCharacterMedia(StaffParamCharacterMedia{
Page: page,
PerPage: perPage,
Sort: []MediaSort{MediaSortStartDateDesc},
},
MediaConnectionFieldPageInfo(
PageInfoFieldTotal,
PageInfoFieldPerPage,
PageInfoFieldCurrentPage,
PageInfoFieldLastPage,
PageInfoFieldHasNextPage),
MediaConnectionFieldEdges(
MediaEdgeFieldCharacterRole,
MediaEdgeFieldNode(
MediaFieldID,
MediaFieldType,
MediaFieldTitle(
MediaTitleFieldEnglish,
MediaTitleFieldNative,
MediaTitleFieldRomaji),
MediaFieldCoverImage(MediaCoverImageFieldMedium)),
MediaEdgeFieldCharacters(
CharacterFieldID,
CharacterFieldName(CharacterNameFieldFull),
CharacterFieldImage(CharacterImageFieldMedium)))))
}
func (c *Client) getStaffMedia(ctx context.Context, id int, mediaType MediaType, page int, perPage int) (*Staff, error) {
return c.GetStaffWithContext(ctx, id, StaffFieldStaffMedia(StaffParamStaffMedia{
Page: page,
PerPage: perPage,
Type: mediaType,
Sort: []MediaSort{MediaSortStartDateDesc},
},
MediaConnectionFieldPageInfo(
PageInfoFieldTotal,
PageInfoFieldPerPage,
PageInfoFieldCurrentPage,
PageInfoFieldLastPage,
PageInfoFieldHasNextPage),
MediaConnectionFieldEdges(
MediaEdgeFieldStaffRole,
MediaEdgeFieldNode(
MediaFieldID,
MediaFieldType,
MediaFieldTitle(
MediaTitleFieldEnglish,
MediaTitleFieldNative,
MediaTitleFieldRomaji),
MediaFieldCoverImage(MediaCoverImageFieldMedium)))))
}
// GetStaffAnime to get staff anime list.
func (c *Client) GetStaffAnime(id int, page int, perPage int) (*Staff, error) {
return c.GetStaffAnimeWithContext(context.Background(), id, page, perPage)
}
// GetStaffAnimeWithContext to get staff anime list with context.
func (c *Client) GetStaffAnimeWithContext(ctx context.Context, id int, page int, perPage int) (*Staff, error) {
return c.getStaffMedia(ctx, id, MediaTypeAnime, page, perPage)
}
// GetStaffManga to get staff manga list.
func (c *Client) GetStaffManga(id int, page int, perPage int) (*Staff, error) {
return c.GetStaffMangaWithContext(context.Background(), id, page, perPage)
}
// GetStaffMangaWithContext to get staff manga list with context.
func (c *Client) GetStaffMangaWithContext(ctx context.Context, id int, page int, perPage int) (*Staff, error) {
return c.getStaffMedia(ctx, id, MediaTypeManga, page, perPage)
}

103
verniy/studio.go Normal file
View File

@@ -0,0 +1,103 @@
package verniy
import "context"
type studioResponse struct {
Data struct {
Studio Studio `json:"studio"`
} `json:"data"`
}
func (c *Client) studioQuery(params QueryParam, fields ...StudioField) string {
p := make([]string, len(fields))
for i := range fields {
p[i] = string(fields[i])
}
return FieldObject("Studio", params, p...)
}
// GetStudio to get anime list produced by the studio.
func (c *Client) GetStudio(id int, page int, perPage int, fields ...StudioField) (*Studio, error) {
return c.GetStudioWithContext(context.Background(), id, page, perPage, fields...)
}
// GetStudioWithContext to get anime list produced by the studio with context.
func (c *Client) GetStudioWithContext(ctx context.Context, id int, page int, perPage int, fields ...StudioField) (*Studio, error) {
if len(fields) == 0 {
fields = []StudioField{
StudioFieldID,
StudioFieldName,
StudioFieldIsAnimationStudio,
StudioFieldFavourites,
StudioFieldMedia(StudioParamMedia{
Page: page,
PerPage: perPage,
Sort: []MediaSort{MediaSortStartDateDesc},
},
MediaConnectionFieldPageInfo(
PageInfoFieldTotal,
PageInfoFieldPerPage,
PageInfoFieldCurrentPage,
PageInfoFieldLastPage,
PageInfoFieldHasNextPage),
MediaConnectionFieldEdges(
MediaEdgeFieldIsMainStudio,
MediaEdgeFieldNode(
MediaFieldID,
MediaFieldTitle(
MediaTitleFieldEnglish,
MediaTitleFieldNative,
MediaTitleFieldRomaji),
MediaFieldCoverImage(MediaCoverImageFieldMedium),
MediaFieldStartDate,
MediaFieldEndDate,
MediaFieldDescription,
MediaFieldSeason,
MediaFieldSeasonYear,
MediaFieldType,
MediaFieldFormat,
MediaFieldStatusV2,
MediaFieldGenres,
MediaFieldIsAdult,
MediaFieldAverageScore,
MediaFieldPopularity))),
}
}
query := FieldObject("query", QueryParam{
"$id": "Int",
}, c.studioQuery(QueryParam{
"id": "$id",
}, fields...))
var d studioResponse
err := c.post(ctx, query, QueryParam{
"id": id,
}, &d)
if err != nil {
return nil, err
}
return &d.Data.Studio, nil
}
// GetStudios to get list of studios.
func (c *Client) GetStudios(page int, perPage int, fields ...StudioField) (*Page, error) {
return c.GetStudiosWithContext(context.Background(), page, perPage, fields...)
}
// GetStudiosWithContext to get list of studios with context.
func (c *Client) GetStudiosWithContext(ctx context.Context, page int, perPage int, fields ...StudioField) (*Page, error) {
if len(fields) == 0 {
fields = []StudioField{
StudioFieldID,
StudioFieldName,
StudioFieldIsAnimationStudio,
StudioFieldFavourites,
}
}
pageFields := PageFieldStudios(PageParamStudios{
Sort: []StudioSort{StudioSortName},
}, "", fields...)
return c.page(ctx, page, perPage, pageFields)
}

44
verniy/tag.go Normal file
View File

@@ -0,0 +1,44 @@
package verniy
import "context"
type tagResponse struct {
Data struct {
Tags []MediaTag `json:"mediaTagCollection"`
} `json:"data"`
}
func (c *Client) tagQuery(fields ...MediaTagField) string {
p := make([]string, len(fields))
for i := range fields {
p[i] = string(fields[i])
}
return FieldObject("MediaTagCollection", nil, p...)
}
// GetTags to get all tag list.
func (c *Client) GetTags(fields ...MediaTagField) ([]MediaTag, error) {
return c.GetTagsWithContext(context.Background(), fields...)
}
// GetTagsWithContext to get all tag list with context.
func (c *Client) GetTagsWithContext(ctx context.Context, fields ...MediaTagField) ([]MediaTag, error) {
if len(fields) == 0 {
fields = []MediaTagField{
MediaTagFieldName,
MediaTagFieldDescription,
MediaTagFieldCategory,
MediaTagFieldIsAdult,
}
}
query := FieldObject("query", nil, c.tagQuery(fields...))
var d tagResponse
err := c.post(ctx, query, nil, &d)
if err != nil {
return nil, err
}
return d.Data.Tags, nil
}

357
verniy/user.go Normal file
View File

@@ -0,0 +1,357 @@
package verniy
import (
"context"
)
type userResponse struct {
Data struct {
User User `json:"user"`
} `json:"data"`
}
func (c *Client) userQuery(params QueryParam, fields ...UserField) string {
p := make([]string, len(fields))
for i := range fields {
p[i] = string(fields[i])
}
return FieldObject("User", params, p...)
}
// GetUser to get user data.
func (c *Client) GetUser(username string, fields ...UserField) (*User, error) {
return c.GetUserWithContext(context.Background(), username, fields...)
}
// GetUserWithContext to get user data with context.
func (c *Client) GetUserWithContext(ctx context.Context, username string, fields ...UserField) (*User, error) {
if len(fields) == 0 {
fields = []UserField{
UserFieldID,
UserFieldName,
UserFieldAbout,
UserFieldAvatar(UserAvatarFieldLarge),
UserFieldBannerImage,
UserFieldStatistics(
UserStatisticTypesFieldAnime(
UserStatisticsFieldCount,
UserStatisticsFieldMeanScore,
UserStatisticsFieldStandardDeviation,
UserStatisticsFieldMinutesWatched,
UserStatisticsFieldEpisodesWatched),
UserStatisticTypesFieldManga(
UserStatisticsFieldCount,
UserStatisticsFieldMeanScore,
UserStatisticsFieldStandardDeviation,
UserStatisticsFieldChaptersRead,
UserStatisticsFieldVolumesRead)),
}
}
query := FieldObject("query", QueryParam{
"$name": "String",
}, c.userQuery(QueryParam{
"name": "$name",
}, fields...))
var d userResponse
err := c.post(ctx, query, queryVariable{
"name": username,
}, &d)
if err != nil {
return nil, err
}
return &d.Data.User, nil
}
// GetUserFavouriteAnime to get user's favourite anime.
func (c *Client) GetUserFavouriteAnime(username string, page int, perPage int) (*User, error) {
return c.GetUserFavouriteAnimeWithContext(context.Background(), username, page, perPage)
}
// GetUserFavouriteAnimeWithContext to get user's favourite anime with context.
func (c *Client) GetUserFavouriteAnimeWithContext(ctx context.Context, username string, page int, perPage int) (*User, error) {
return c.GetUserWithContext(ctx, username, UserFieldFavourites(UserParamFavourites{
Page: page,
}, FavouritesFieldAnime(FavouritesParamAnime{
Page: page,
PerPage: perPage,
},
MediaConnectionFieldPageInfo(
PageInfoFieldTotal,
PageInfoFieldPerPage,
PageInfoFieldCurrentPage,
PageInfoFieldLastPage,
PageInfoFieldHasNextPage),
MediaConnectionFieldEdges(
MediaEdgeFieldFavouriteOrder,
MediaEdgeFieldNode(
MediaFieldID,
MediaFieldIsAdult,
MediaFieldTitle(
MediaTitleFieldRomaji,
MediaTitleFieldEnglish,
MediaTitleFieldNative),
MediaFieldCoverImage(
MediaCoverImageFieldLarge,
MediaCoverImageFieldLarge))))))
}
// GetUserFavouriteManga to get user's favourite manga.
func (c *Client) GetUserFavouriteManga(username string, page int, perPage int) (*User, error) {
return c.GetUserFavouriteMangaWithContext(context.Background(), username, page, perPage)
}
// GetUserFavouriteMangaWithContext to get user's favourite manga with context.
func (c *Client) GetUserFavouriteMangaWithContext(ctx context.Context, username string, page int, perPage int) (*User, error) {
return c.GetUserWithContext(ctx, username, UserFieldFavourites(UserParamFavourites{
Page: page,
}, FavouritesFieldManga(FavouritesParamManga{
Page: page,
PerPage: perPage,
},
MediaConnectionFieldPageInfo(
PageInfoFieldTotal,
PageInfoFieldPerPage,
PageInfoFieldCurrentPage,
PageInfoFieldLastPage,
PageInfoFieldHasNextPage),
MediaConnectionFieldEdges(
MediaEdgeFieldFavouriteOrder,
MediaEdgeFieldNode(
MediaFieldID,
MediaFieldIsAdult,
MediaFieldTitle(
MediaTitleFieldRomaji,
MediaTitleFieldEnglish,
MediaTitleFieldNative),
MediaFieldCoverImage(
MediaCoverImageFieldLarge,
MediaCoverImageFieldLarge))))))
}
// GetUserFavouriteCharacters to get user's favourite characters.
func (c *Client) GetUserFavouriteCharacters(username string, page int, perPage int) (*User, error) {
return c.GetUserFavouriteCharactersWithContext(context.Background(), username, page, perPage)
}
// GetUserFavouriteCharactersWithContext to get user's favourite characters with context.
func (c *Client) GetUserFavouriteCharactersWithContext(ctx context.Context, username string, page int, perPage int) (*User, error) {
return c.GetUserWithContext(ctx, username, UserFieldFavourites(UserParamFavourites{
Page: page,
}, FavouritesFieldCharacters(FavouritesParamCharacters{
Page: page,
PerPage: perPage,
},
CharacterConnectionFieldPageInfo(
PageInfoFieldTotal,
PageInfoFieldPerPage,
PageInfoFieldCurrentPage,
PageInfoFieldLastPage,
PageInfoFieldHasNextPage),
CharacterConnectionFieldEdges(
CharacterEdgeFieldFavouriteOrder,
CharacterEdgeFieldNode(
CharacterFieldID,
CharacterFieldName(
CharacterNameFieldFull,
CharacterNameFieldNative),
CharacterFieldImage(CharacterImageFieldLarge))))))
}
// GetUserFavouriteStaff to get user's favourite staff.
func (c *Client) GetUserFavouriteStaff(username string, page int, perPage int) (*User, error) {
return c.GetUserFavouriteStaffWithContext(context.Background(), username, page, perPage)
}
// GetUserFavouriteStaffWithContext to get user's favourite staff with context.
func (c *Client) GetUserFavouriteStaffWithContext(ctx context.Context, username string, page int, perPage int) (*User, error) {
return c.GetUserWithContext(ctx, username, UserFieldFavourites(UserParamFavourites{
Page: page,
}, FavouritesFieldStaff(FavouritesParamStaff{
Page: page,
PerPage: perPage,
},
StaffConnectionFieldPageInfo(
PageInfoFieldTotal,
PageInfoFieldPerPage,
PageInfoFieldCurrentPage,
PageInfoFieldLastPage,
PageInfoFieldHasNextPage),
StaffConnectionFieldEdges(
StaffEdgeFieldFavouriteOrder,
StaffEdgeFieldNode(
StaffFieldID,
StaffFieldName(
StaffNameFieldFull,
StaffNameFieldNative),
StaffFieldImage(StaffImageFieldLarge))))))
}
// GetUserFavouriteStudios to get user's favourite studios.
func (c *Client) GetUserFavouriteStudios(username string, page int, perPage int) (*User, error) {
return c.GetUserFavouriteStudiosWithContext(context.Background(), username, page, perPage)
}
// GetUserFavouriteStudiosWithContext to get user's favourite studios with context.
func (c *Client) GetUserFavouriteStudiosWithContext(ctx context.Context, username string, page int, perPage int) (*User, error) {
return c.GetUserWithContext(ctx, username, UserFieldFavourites(UserParamFavourites{
Page: page,
}, FavouritesFieldStudios(FavouritesParamStudios{
Page: page,
PerPage: perPage,
},
StudioConnectionFieldPageInfo(
PageInfoFieldTotal,
PageInfoFieldPerPage,
PageInfoFieldCurrentPage,
PageInfoFieldLastPage,
PageInfoFieldHasNextPage),
StudioConnectionFieldEdges(
StudioEdgeFieldFavouriteOrder,
StudioEdgeFieldNode(
StudioFieldID,
StudioFieldName)))))
}
type userAnimeListResponse struct {
Data struct {
MediaListCollection *MediaListCollection `json:"mediaListCollection"`
} `json:"data"`
}
func (c *Client) mediaListCollectionQuery(params QueryParam, fields ...MediaListCollectionField) string {
p := make([]string, len(fields))
for i := range fields {
p[i] = string(fields[i])
}
return FieldObject("MediaListCollection", params, p...)
}
func (c *Client) getMediaListCollection(ctx context.Context, username string, mediaType MediaType, fields ...MediaListGroupField) ([]MediaListGroup, error) {
query := FieldObject("query", QueryParam{
"$username": "String",
"$type": "MediaType",
}, c.mediaListCollectionQuery(QueryParam{
"userName": "$username",
"type": "$type",
}, MediaListCollectionFieldLists(MediaListGroupFieldStatus, fields...)))
var d userAnimeListResponse
err := c.post(ctx, query, queryVariable{
"username": username,
"type": mediaType,
}, &d)
if err != nil {
return nil, err
}
return d.Data.MediaListCollection.Lists, nil
}
// GetUserAnimeList to get user's anime list.
func (c *Client) GetUserAnimeList(username string, fields ...MediaListGroupField) ([]MediaListGroup, error) {
return c.GetUserAnimeListWithContext(context.Background(), username, fields...)
}
// GetUserAnimeListSort Added custom function to get user's anime list sorted
func (c *Client) GetUserAnimeListSort(username string, sort MediaListSort, fields ...MediaListGroupField) ([]MediaListGroup, error) {
ctx := context.Background()
query := FieldObject("query", QueryParam{
"$username": "String",
"$type": "MediaType",
"$sort": "[MediaListSort]",
}, c.mediaListCollectionQuery(QueryParam{
"userName": "$username",
"type": "$type",
"sort": "$sort",
}, MediaListCollectionFieldLists(MediaListGroupFieldStatus, fields...)))
var d userAnimeListResponse
err := c.post(ctx, query, queryVariable{
"username": username,
"type": MediaTypeAnime,
"sort": sort,
}, &d)
if err != nil {
return nil, err
}
return d.Data.MediaListCollection.Lists, nil
}
// GetUserAnimeListWithContext to get user's anime list with context.
func (c *Client) GetUserAnimeListWithContext(ctx context.Context, username string, fields ...MediaListGroupField) ([]MediaListGroup, error) {
if len(fields) == 0 {
fields = []MediaListGroupField{
MediaListGroupFieldName,
MediaListGroupFieldStatus,
MediaListGroupFieldEntries(
MediaListFieldID,
MediaListFieldStatus,
MediaListFieldScore,
MediaListFieldProgress,
MediaListFieldNotes,
MediaListFieldStartedAt,
MediaListFieldCompletedAt,
MediaListFieldMedia(
MediaFieldID,
MediaFieldTitle(
MediaTitleFieldRomaji,
MediaTitleFieldEnglish,
MediaTitleFieldNative),
MediaFieldType,
MediaFieldFormat,
MediaFieldStatusV2,
MediaFieldCoverImage(MediaCoverImageFieldLarge),
MediaFieldAverageScore,
MediaFieldPopularity,
MediaFieldIsAdult,
MediaFieldEpisodes)),
}
}
return c.getMediaListCollection(ctx, username, MediaTypeAnime, fields...)
}
// GetUserMangaList to get user's manga list.
func (c *Client) GetUserMangaList(username string, fields ...MediaListGroupField) ([]MediaListGroup, error) {
return c.GetUserMangaListWithContext(context.Background(), username, fields...)
}
// GetUserMangaListWithContext to get user's manga list with context.
func (c *Client) GetUserMangaListWithContext(ctx context.Context, username string, fields ...MediaListGroupField) ([]MediaListGroup, error) {
if len(fields) == 0 {
fields = []MediaListGroupField{
MediaListGroupFieldName,
MediaListGroupFieldStatus,
MediaListGroupFieldEntries(
MediaListFieldID,
MediaListFieldStatus,
MediaListFieldScore,
MediaListFieldProgress,
MediaListFieldProgressVolumes,
MediaListFieldNotes,
MediaListFieldStartedAt,
MediaListFieldCompletedAt,
MediaListFieldMedia(
MediaFieldID,
MediaFieldTitle(
MediaTitleFieldRomaji,
MediaTitleFieldEnglish,
MediaTitleFieldNative),
MediaFieldType,
MediaFieldFormat,
MediaFieldStatusV2,
MediaFieldCoverImage(MediaCoverImageFieldLarge),
MediaFieldAverageScore,
MediaFieldPopularity,
MediaFieldIsAdult,
MediaFieldChapters,
MediaFieldVolumes)),
}
}
return c.getMediaListCollection(ctx, username, MediaTypeManga, fields...)
}

84
verniy/utils.go Normal file
View File

@@ -0,0 +1,84 @@
package verniy
import (
"fmt"
"reflect"
"strings"
)
// FieldObject to generate query string.
//
// Example:
//
// FieldObject("key", map[string]interface{}{
// "name1": "value1",
// "name2": []string{"value2", "value3"},
// }, "field1", "field2")
//
// Will generate:
//
// key(name1:value1, name2:[value2,value3]) {
// field1
// field2
// }
//
// Or just see usage example in some functions in verniy package.
func FieldObject(key string, params map[string]interface{}, fields ...string) string {
var paramStr string
if params != nil || len(params) > 0 {
var paramArr []string
for k, p := range params {
if p == nil {
continue
}
switch reflect.TypeOf(p).Kind() {
case reflect.Slice, reflect.Array:
v := reflect.ValueOf(p)
if v.Len() > 0 {
vArr := make([]string, v.Len())
for i := 0; i < v.Len(); i++ {
vArr[i] = fmt.Sprintf("%v", v.Index(i))
}
paramArr = append(paramArr, fmt.Sprintf("%v:[%v]", k, strings.Join(vArr, ",")))
}
case reflect.String:
if reflect.ValueOf(p).Len() != 0 {
paramArr = append(paramArr, fmt.Sprintf("%v:%v", k, p))
}
case reflect.Int:
if reflect.ValueOf(p).Int() != 0 {
paramArr = append(paramArr, fmt.Sprintf("%v:%v", k, p))
}
case reflect.Ptr:
if !reflect.ValueOf(p).IsNil() {
paramArr = append(paramArr, fmt.Sprintf("%v:%v", k, reflect.ValueOf(p).Elem()))
}
default:
paramArr = append(paramArr, fmt.Sprintf("%v:%v", k, p))
}
}
if len(paramArr) > 0 {
paramStr = fmt.Sprintf("(%s)", strings.Join(paramArr, ","))
}
}
var fieldStr string
if len(fields) > 0 {
fieldStr = fmt.Sprintf("{%s}", strings.Join(fields, " "))
}
return fmt.Sprintf("%s%s%s", key, paramStr, fieldStr)
}
func toQueryString(str string) string {
if str == "" {
return str
}
return fmt.Sprintf(`"%s"`, str)
}
func toQueryStringArray(strs []string) []string {
for i := range strs {
strs[i] = fmt.Sprintf(`"%s"`, strs[i])
}
return strs
}

27
verniy/verniy.go Normal file
View File

@@ -0,0 +1,27 @@
package verniy
import (
"net/http"
"time"
"AnimeGUI/verniy/limiter"
)
// Client is anilist client.
type Client struct {
Host string
Http http.Client
Limiter limiter.Limiter
AccessToken string
}
// New to create new anilist client.
func New() *Client {
return &Client{
Host: "https://graphql.anilist.co",
Http: http.Client{
Timeout: 10 * time.Second,
},
Limiter: limiter.New(90, time.Minute),
}
}