mal

package
v0.9.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 29, 2023 License: MIT Imports: 11 Imported by: 8

Documentation

Overview

Package mal provides a client for accessing the MyAnimeList API: https://myanimelist.net/apiconfig/references/api/v2.

Installation

This package can be installed using:

go get github.com/nstratos/go-myanimelist/mal

Usage

Import the package using:

import "github.com/nstratos/go-myanimelist/mal"

First construct a new mal client:

c := mal.NewClient(nil)

Then use one of the client's services (User, Anime, Manga and Forum) to access the different MyAnimeList API methods.

Authentication

When creating a new client, pass an `http.Client` that can handle authentication for you.

Accessing publicly available information

To access public information, you need to add the ` X-MAL-CLIENT-ID` header in your requests. You can achieve this by creating an `http.Client` with a custom transport and use it as shown below:

type clientIDTransport struct {
	Transport http.RoundTripper
	ClientID  string
}

func (c *clientIDTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	if c.Transport == nil {
		c.Transport = http.DefaultTransport
	}
	req.Header.Add("X-MAL-CLIENT-ID", c.ClientID)
	return c.Transport.RoundTrip(req)
}

func main() {
	publicInfoClient := &http.Client{
		// Create client ID from https://myanimelist.net/apiconfig.
		Transport: &clientIDTransport{ClientID: "<Your application client ID>"},
	}

	c := mal.NewClient(publicInfoClient)
	// ...
}

Authenticating using OAuth2

The recommended way is to use the golang.org/x/oauth2 package (https://github.com/golang/oauth2). After completing the OAuth2 flow, you will get an oauth2 token containing an access token, a refresh token and an expiration date. The oauth2 token can easily be stored in JSON format and used like this:

const storedToken = `
{
	"token_type": "Bearer",
	"access_token": "yourAccessToken",
	"refresh_token": "yourRefreshToken",
	"expiry": "2021-06-01T16:12:56.1319122Z"
}`

oauth2Token := new(oauth2.Token)
_ = json.Unmarshal([]byte(storedToken), oauth2Token)

// Create client ID and secret from https://myanimelist.net/apiconfig.
//
// Secret is currently optional if you choose App Type 'other'.
oauth2Conf := &oauth2.Config{
	ClientID:     "<Enter your registered MyAnimeList.net application client ID>",
	ClientSecret: "<Enter your registered MyAnimeList.net application client secret>",
	Endpoint: oauth2.Endpoint{
	    AuthURL:   "https://myanimelist.net/v1/oauth2/authorize",
	    TokenURL:  "https://myanimelist.net/v1/oauth2/token",
	    AuthStyle: oauth2.AuthStyleInParams,
	},
}

oauth2Client := oauth2Conf.Client(ctx, oauth2Token)

// The oauth2Client will refresh the token if it expires.
c := mal.NewClient(oauth2Client)

Note that all calls made by the client above will include the specified oauth2 token which is specific for an authenticated user. Therefore, authenticated clients should almost never be shared between different users.

Performing the OAuth2 flow involves registering a MAL API application and then asking for the user's consent to allow the application to access their data.

There is a detailed example of how to perform the Oauth2 flow and get an oauth2 token through the terminal under example/malauth. The only thing you need to run the example is a client ID and a client secret which you can acquire after registering your MAL API application. Here's how:

  1. Navigate to https://myanimelist.net/apiconfig or go to your MyAnimeList profile, click Edit Profile and select the API tab on the far right.

  2. Click Create ID and submit the form with your application details.

After registering your application, you can run the example and pass the client ID and client secret through flags:

cd example/malauth
go run main.go democlient.go --client-id=... --client-secret=...

or

go install github.com/nstratos/go-myanimelist/example/malauth
malauth --client-id=... --client-secret=...

After you perform a successful authentication once, the oauth2 token will be cached in a file under the same directory which makes it easier to run the example multiple times.

Official MAL API OAuth2 docs: https://myanimelist.net/apiconfig/references/authorization

List

To search and get anime and manga data:

list, _, err := c.Anime.List(ctx, "hokuto no ken",
	mal.Fields{"rank", "popularity", "my_list_status"},
	mal.Limit(5),
)
// ...

list, _, err := c.Manga.List(ctx, "hokuto no ken",
	mal.Fields{"rank", "popularity", "my_list_status"},
	mal.Limit(5),
)
// ...

You may get user specific data for a certain record by using the optional field "my_list_status".

Official docs:

- https://myanimelist.net/apiconfig/references/api/v2#operation/anime_get

- https://myanimelist.net/apiconfig/references/api/v2#operation/manga_get

UserList

To get the anime or manga list of a user:

// Get the authenticated user's anime list, filter only watching anime, sort by
// last updated, include list status.
anime, _, err := c.User.AnimeList(ctx, "@me",
    mal.Fields{"list_status"},
    mal.AnimeStatusWatching,
    mal.SortAnimeListByListUpdatedAt,
    mal.Limit(5),
)
// ...

// Get the authenticated user's manga list's second page, sort by score,
// include list status, comments and tags.
manga, _, err := c.User.MangaList(ctx, "@me",
    mal.SortMangaListByListScore,
    mal.Fields{"list_status{comments, tags}"},
    mal.Limit(5),
    mal.Offset(1),
)
// ...

You may provide the username of the user or "@me" to get the authenticated user's list.

Official docs:

- https://myanimelist.net/apiconfig/references/api/v2#operation/users_user_id_animelist_get

- https://myanimelist.net/apiconfig/references/api/v2#operation/users_user_id_mangalist_get

MyInfo

To get information about the authenticated user:

user, _, err := c.User.MyInfo(ctx)
// ...

This method can use the Fields option but the API doesn't seem to be able to send optional fields like "anime_statistics" at the time of writing.

Official docs:

- https://myanimelist.net/apiconfig/references/api/v2#operation/users_user_id_get

Details

To get details for a certain anime or manga:

a, _, err := c.Anime.Details(ctx, 967,
	mal.Fields{
		"alternative_titles",
		"media_type",
		"num_episodes",
		"start_season",
		"source",
		"genres",
		"studios",
		"average_episode_duration",
	},
)
// ...

m, _, err := c.Manga.Details(ctx, 401,
	mal.Fields{
		"alternative_titles",
		"media_type",
		"num_volumes",
		"num_chapters",
		"authors{last_name, first_name}",
		"genres",
		"status",
	},
)
// ...

By default most fields are not populated so use the Fields option to request the fields you need.

Official docs:

- https://myanimelist.net/apiconfig/references/api/v2#operation/anime_anime_id_get

- https://myanimelist.net/apiconfig/references/api/v2#operation/manga_manga_id_get

Ranking

To get anime or manga based on a certain ranking:

anime, _, err := c.Anime.Ranking(ctx,
	mal.AnimeRankingAiring,
	mal.Fields{"rank", "popularity"},
	mal.Limit(6),
)
// ...

manga, _, err := c.Manga.Ranking(ctx,
	mal.MangaRankingByPopularity,
	mal.Fields{"rank", "popularity"},
	mal.Limit(6),
)
// ...

Official docs:

- https://myanimelist.net/apiconfig/references/api/v2#operation/anime_ranking_get

- https://myanimelist.net/apiconfig/references/api/v2#operation/manga_ranking_get

Add or Update List

To add or update an entry in an authenticated user's list, provide the anime or manga ID and then options to specify the status, score, comments, tags etc.

_, _, err := c.Anime.UpdateMyListStatus(ctx, 967,
	mal.AnimeStatusWatching,
	mal.NumEpisodesWatched(73),
	mal.Score(8),
	mal.Comments("You wa shock!"),
	mal.StartDate(time.Date(2022, 02, 20, 0, 0, 0, 0, time.UTC)),
	mal.FinishDate(time.Time{}), // Remove an existing date.
)
// ...

_, _, err := c.Manga.UpdateMyListStatus(ctx, 401,
	mal.MangaStatusReading,
	mal.NumVolumesRead(1),
	mal.NumChaptersRead(5),
	mal.Comments("Migi"),
	mal.StartDate(time.Date(2022, 02, 20, 0, 0, 0, 0, time.UTC)),
	mal.FinishDate(time.Time{}), // Remove an existing date.
)
// ...

Official docs:

- https://myanimelist.net/apiconfig/references/api/v2#operation/anime_anime_id_my_list_status_put

- https://myanimelist.net/apiconfig/references/api/v2#operation/manga_manga_id_my_list_status_put

Delete

To delete anime or manga from a user's list, simply provide their IDs:

_, err := c.Anime.DeleteMyListItem(ctx, 967)
// ...

_, err := c.Manga.DeleteMyListItem(ctx, 401)
// ...

Official docs:

- https://myanimelist.net/apiconfig/references/api/v2#operation/anime_anime_id_my_list_status_delete

- https://myanimelist.net/apiconfig/references/api/v2#operation/manga_manga_id_my_list_status_delete

More Examples

See package examples: https://pkg.go.dev/github.com/nstratos/go-myanimelist/mal#pkg-examples

Unit Testing

To run all unit tests:

go test -cover

To see test coverage in your browser:

go test -covermode=count -coverprofile=count.out && go tool cover -html count.out

Integration Testing

The integration tests will exercise the entire package against the live MyAnimeList API. As a result, these tests take much longer to run and there is also a much higher chance of false positives in test failures due to network issues etc.

These tests are meant to be run using a dedicated test account that contains empty anime and manga lists. A valid oauth2 token needs to be provided every time. Check the authentication section to learn how to get one.

By default the integration tests are skipped when an oauth2 token is not provided. To run all tests including the integration tests:

go test --client-id='<your app client ID>' --oauth2-token='<your oauth2 token>'

License

MIT

Example (OAuth2)
package main

import (
	"context"
	_ "embed"
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/nstratos/go-myanimelist/mal"
	"golang.org/x/oauth2"
)

func newOAuth2Client(ctx context.Context) *http.Client {
	// In order to create a client ID and secret for your application:
	//
	//  1. Navigate to https://myanimelist.net/apiconfig or go to your MyAnimeList
	//     profile, click Edit Profile and select the API tab on the far right.
	//  2. Click Create ID and submit the form with your application details.
	oauth2Conf := &oauth2.Config{
		ClientID:     "<Enter your MyAnimeList.net application client ID>",
		ClientSecret: "<Enter your MyAnimeList.net application client secret>", // Optional if you chose App Type 'other'.
		Endpoint: oauth2.Endpoint{
			AuthURL:   "https://myanimelist.net/v1/oauth2/authorize",
			TokenURL:  "https://myanimelist.net/v1/oauth2/token",
			AuthStyle: oauth2.AuthStyleInParams,
		},
	}

	// To get your first token you need to complete the oauth2 flow. There is a
	// detailed example that uses the terminal under `example/malauth` which you
	// should adjust for your application.
	//
	// Here we assume we have already received our first valid token and stored
	// it somewhere in JSON format.
	const storedToken = `
	{
		"token_type": "Bearer",
		"access_token": "yourAccessToken",
		"refresh_token": "yourRefreshToken",
		"expiry": "2021-06-01T16:12:56.1319122Z"
	}`

	// Decode stored token to oauth2.Token struct.
	oauth2Token := new(oauth2.Token)
	_ = json.Unmarshal([]byte(storedToken), oauth2Token)

	// The oauth2 client returned here with the above configuration and valid
	// token will refresh the token seamlessly when it expires.
	return oauth2Conf.Client(ctx, oauth2Token)
}

func main() {
	ctx := context.Background()
	oauth2Client := newOAuth2Client(ctx)

	c := mal.NewClient(oauth2Client)

	user, _, err := c.User.MyInfo(ctx)
	if err != nil {
		fmt.Printf("User.MyInfo error: %v", err)
		return
	}
	fmt.Printf("ID: %5d, Joined: %v, Username: %s\n", user.ID, user.JoinedAt.Format("Jan 2006"), user.Name)
}
Output:

Index

Examples

Constants

View Source
const SortTopicsRecent sortTopics = "recent"

SortTopicsRecent is the default and only sorting value for topics.

Variables

This section is empty.

Functions

This section is empty.

Types

type Anime

type Anime struct {
	ID                     int                `json:"id"`
	Title                  string             `json:"title"`
	MainPicture            Picture            `json:"main_picture"`
	AlternativeTitles      Titles             `json:"alternative_titles"`
	StartDate              string             `json:"start_date"`
	EndDate                string             `json:"end_date"`
	Synopsis               string             `json:"synopsis"`
	Mean                   float64            `json:"mean"`
	Rank                   int                `json:"rank"`
	Popularity             int                `json:"popularity"`
	NumListUsers           int                `json:"num_list_users"`
	NumScoringUsers        int                `json:"num_scoring_users"`
	NSFW                   string             `json:"nsfw"`
	CreatedAt              time.Time          `json:"created_at"`
	UpdatedAt              time.Time          `json:"updated_at"`
	MediaType              string             `json:"media_type"`
	Status                 string             `json:"status"`
	Genres                 []Genre            `json:"genres"`
	MyListStatus           AnimeListStatus    `json:"my_list_status"`
	NumEpisodes            int                `json:"num_episodes"`
	StartSeason            StartSeason        `json:"start_season"`
	Broadcast              Broadcast          `json:"broadcast"`
	Source                 string             `json:"source"`
	AverageEpisodeDuration int                `json:"average_episode_duration"`
	Rating                 string             `json:"rating"`
	Pictures               []Picture          `json:"pictures"`
	Background             string             `json:"background"`
	RelatedAnime           []RelatedAnime     `json:"related_anime"`
	RelatedManga           []RelatedManga     `json:"related_manga"`
	Recommendations        []RecommendedAnime `json:"recommendations"`
	Studios                []Studio           `json:"studios"`
	Statistics             Statistics         `json:"statistics"`
}

Anime represents a MyAnimeList anime.

type AnimeListOption

type AnimeListOption interface {
	// contains filtered or unexported methods
}

AnimeListOption are options specific to the UserService.AnimeList method.

type AnimeListStatus

type AnimeListStatus struct {
	Status             AnimeStatus `json:"status"`
	Score              int         `json:"score"`
	NumEpisodesWatched int         `json:"num_episodes_watched"`
	IsRewatching       bool        `json:"is_rewatching"`
	UpdatedAt          time.Time   `json:"updated_at"`
	Priority           int         `json:"priority"`
	NumTimesRewatched  int         `json:"num_times_rewatched"`
	RewatchValue       int         `json:"rewatch_value"`
	Tags               []string    `json:"tags"`
	Comments           string      `json:"comments"`
	StartDate          string      `json:"start_date"`
	FinishDate         string      `json:"finish_date"`
}

AnimeListStatus shows the status of each anime in a user's anime list.

type AnimeRanking

type AnimeRanking string

AnimeRanking allows to choose how the anime will be ranked.

const (
	// AnimeRankingAll returns the top anime series.
	AnimeRankingAll AnimeRanking = "all"
	// AnimeRankingAiring returns the top airing anime.
	AnimeRankingAiring AnimeRanking = "airing"
	// AnimeRankingUpcoming returns the top upcoming anime.
	AnimeRankingUpcoming AnimeRanking = "upcoming"
	// AnimeRankingTV returns the top Anime TV series.
	AnimeRankingTV AnimeRanking = "tv"
	// AnimeRankingOVA returns the top anime OVA series.
	AnimeRankingOVA AnimeRanking = "ova"
	// AnimeRankingMovie returns the top anime movies.
	AnimeRankingMovie AnimeRanking = "movie"
	// AnimeRankingSpecial returns the top anime specials.
	AnimeRankingSpecial AnimeRanking = "special"
	// AnimeRankingByPopularity returns the top anime by popularity.
	AnimeRankingByPopularity AnimeRanking = "bypopularity"
	// AnimeRankingFavorite returns the top favorite Anime.
	AnimeRankingFavorite AnimeRanking = "favorite"
)

type AnimeSeason

type AnimeSeason string

AnimeSeason is the airing season of the anime.

const (
	// AnimeSeasonWinter is the winter season of January, February and March.
	AnimeSeasonWinter AnimeSeason = "winter"
	// AnimeSeasonSpring is the spring season of April, May and June.
	AnimeSeasonSpring AnimeSeason = "spring"
	// AnimeSeasonSummer is the summer season of July, August and September.
	AnimeSeasonSummer AnimeSeason = "summer"
	// AnimeSeasonFall is the fall season of October, November and December.
	AnimeSeasonFall AnimeSeason = "fall"
)

type AnimeService

type AnimeService struct {
	// contains filtered or unexported fields
}

AnimeService handles communication with the anime related methods of the MyAnimeList API:

https://myanimelist.net/apiconfig/references/api/v2#tag/anime https://myanimelist.net/apiconfig/references/api/v2#tag/user-animelist

func (*AnimeService) DeleteMyListItem

func (s *AnimeService) DeleteMyListItem(ctx context.Context, animeID int) (*Response, error)

DeleteMyListItem deletes an anime from the user's list. If the anime does not exist in the user's list, 404 Not Found error is returned.

Example
ctx := context.Background()

c := mal.NewClient(nil)

// Ignore the 3 following lines. A stub server is used instead of the real
// API to produce testable examples. See: https://go.dev/blog/examples
server := newStubServer()
defer server.Close()
c.BaseURL, _ = url.Parse(server.URL)

resp, err := c.Anime.DeleteMyListItem(ctx, 967)
if err != nil {
	fmt.Printf("Anime.DeleteMyListItem error: %v", err)
	return
}
fmt.Println(resp.Status)
Output:

200 OK

func (*AnimeService) Details

func (s *AnimeService) Details(ctx context.Context, animeID int, options ...DetailsOption) (*Anime, *Response, error)

Details returns details about an anime. By default, few anime fields are populated. Use the Fields option to specify which fields should be included.

Example
ctx := context.Background()

c := mal.NewClient(nil)

// Ignore the 3 following lines. A stub server is used instead of the real
// API to produce testable examples. See: https://go.dev/blog/examples
server := newStubServer()
defer server.Close()
c.BaseURL, _ = url.Parse(server.URL)

a, _, err := c.Anime.Details(ctx, 967,
	mal.Fields{
		"alternative_titles",
		"media_type",
		"num_episodes",
		"start_season",
		"source",
		"genres",
		"studios",
		"average_episode_duration",
	},
)
if err != nil {
	fmt.Printf("Anime.Details error: %v", err)
	return
}

fmt.Printf("%s\n", a.Title)
fmt.Printf("ID: %d\n", a.ID)
fmt.Printf("English: %s\n", a.AlternativeTitles.En)
fmt.Printf("Type: %s\n", strings.ToUpper(a.MediaType))
fmt.Printf("Episodes: %d\n", a.NumEpisodes)
fmt.Printf("Premiered: %s %d\n", strings.Title(a.StartSeason.Season), a.StartSeason.Year)
fmt.Print("Studios: ")
delim := ""
for _, s := range a.Studios {
	fmt.Printf("%s%s", delim, s.Name)
	delim = " "
}
fmt.Println()
fmt.Printf("Source: %s\n", strings.Title(a.Source))
fmt.Print("Genres: ")
delim = ""
for _, g := range a.Genres {
	fmt.Printf("%s%s", delim, g.Name)
	delim = " "
}
fmt.Println()
fmt.Printf("Duration: %d min. per ep.\n", a.AverageEpisodeDuration/60)
Output:

Hokuto no Ken
ID: 967
English: Fist of the North Star
Type: TV
Episodes: 109
Premiered: Fall 1984
Studios: Toei Animation
Source: Manga
Genres: Action Drama Martial Arts Sci-Fi Shounen
Duration: 25 min. per ep.

func (*AnimeService) List

func (s *AnimeService) List(ctx context.Context, search string, options ...Option) ([]Anime, *Response, error)

List allows an authenticated user to search and list anime data. You may get user specific data by using the optional field "my_list_status".

Example
ctx := context.Background()

c := mal.NewClient(nil)

// Ignore the 3 following lines. A stub server is used instead of the real
// API to produce testable examples. See: https://go.dev/blog/examples
server := newStubServer()
defer server.Close()
c.BaseURL, _ = url.Parse(server.URL)

anime, _, err := c.Anime.List(ctx, "hokuto no ken",
	mal.Fields{"rank", "popularity", "start_season"},
	mal.Limit(5),
	mal.Offset(0),
)
if err != nil {
	fmt.Printf("Anime.List error: %v", err)
	return
}
for _, a := range anime {
	fmt.Printf("ID: %5d, Rank: %5d, Popularity: %5d %s (%d)\n", a.ID, a.Rank, a.Popularity, a.Title, a.StartSeason.Year)
}
Output:

ID:   967, Rank:   556, Popularity:  1410 Hokuto no Ken (1984)
ID:  1356, Rank:  1423, Popularity:  3367 Hokuto no Ken 2 (1987)
ID:  1358, Rank:  2757, Popularity:  3964 Hokuto no Ken Movie (1986)

func (*AnimeService) Ranking

func (s *AnimeService) Ranking(ctx context.Context, ranking AnimeRanking, options ...Option) ([]Anime, *Response, error)

Ranking allows an authenticated user to receive the top anime based on a certain ranking.

Example
ctx := context.Background()

c := mal.NewClient(nil)

// Ignore the 3 following lines. A stub server is used instead of the real
// API to produce testable examples. See: https://go.dev/blog/examples
server := newStubServer()
defer server.Close()
c.BaseURL, _ = url.Parse(server.URL)

anime, _, err := c.Anime.Ranking(ctx,
	mal.AnimeRankingAiring,
	mal.Fields{"rank", "popularity"},
	mal.Limit(6),
)
if err != nil {
	fmt.Printf("Anime.Ranking error: %v", err)
	return
}
for _, a := range anime {
	fmt.Printf("Rank: %5d, Popularity: %5d %s\n", a.Rank, a.Popularity, a.Title)
}
Output:

Rank:     2, Popularity:   104 Shingeki no Kyojin: The Final Season
Rank:    59, Popularity:   371 Re:Zero kara Hajimeru Isekai Seikatsu 2nd Season Part 2
Rank:    67, Popularity:  1329 Yuru Camp△ Season 2
Rank:    69, Popularity:   109 Jujutsu Kaisen (TV)
Rank:    83, Popularity:  3714 Holo no Graffiti
Rank:    85, Popularity:   304 Horimiya

func (*AnimeService) Seasonal

func (s *AnimeService) Seasonal(ctx context.Context, year int, season AnimeSeason, options ...SeasonalAnimeOption) ([]Anime, *Response, error)

Seasonal allows an authenticated user to receive the seasonal anime by providing the year and season. The results can be sorted using an option.

Example
ctx := context.Background()

c := mal.NewClient(nil)

// Ignore the 3 following lines. A stub server is used instead of the real
// API to produce testable examples. See: https://go.dev/blog/examples
server := newStubServer()
defer server.Close()
c.BaseURL, _ = url.Parse(server.URL)

anime, _, err := c.Anime.Seasonal(ctx, 2020, mal.AnimeSeasonFall,
	mal.Fields{"rank", "popularity"},
	mal.SortSeasonalByAnimeNumListUsers,
	mal.Limit(3),
	mal.Offset(0),
)
if err != nil {
	fmt.Printf("Anime.Seasonal error: %v", err)
	return
}
for _, a := range anime {
	fmt.Printf("Rank: %5d, Popularity: %5d %s\n", a.Rank, a.Popularity, a.Title)
}
Output:

Rank:    93, Popularity:    31 One Piece
Rank:  1870, Popularity:    92 Black Clover
Rank:    62, Popularity:   106 Jujutsu Kaisen (TV)

func (*AnimeService) Suggested

func (s *AnimeService) Suggested(ctx context.Context, options ...Option) ([]Anime, *Response, error)

Suggested returns suggested anime for the authorized user. If the user is new comer, this endpoint returns an empty list.

Example
ctx := context.Background()

c := mal.NewClient(nil)

// Ignore the 3 following lines. A stub server is used instead of the real
// API to produce testable examples. See: https://go.dev/blog/examples
server := newStubServer()
defer server.Close()
c.BaseURL, _ = url.Parse(server.URL)

anime, _, err := c.Anime.Suggested(ctx,
	mal.Limit(10),
	mal.Fields{"rank", "popularity"},
)
if err != nil {
	fmt.Printf("Anime.Suggested error: %v", err)
	return
}
for _, a := range anime {
	fmt.Printf("Rank: %5d, Popularity: %5d %s\n", a.Rank, a.Popularity, a.Title)
}
Output:

Rank:   971, Popularity:   916 Kill la Kill Specials
Rank:   726, Popularity:  4972 Osomatsu-san Movie
Rank:   943, Popularity:  4595 Watashi no Ashinaga Ojisan

func (*AnimeService) UpdateMyListStatus

func (s *AnimeService) UpdateMyListStatus(ctx context.Context, animeID int, options ...UpdateMyAnimeListStatusOption) (*AnimeListStatus, *Response, error)

UpdateMyListStatus adds the anime specified by animeID to the user's anime list with one or more options added to update the status. If the anime already exists in the list, only the status is updated.

Example
ctx := context.Background()

c := mal.NewClient(nil)

// Ignore the 3 following lines. A stub server is used instead of the real
// API to produce testable examples. See: https://go.dev/blog/examples
server := newStubServer()
defer server.Close()
c.BaseURL, _ = url.Parse(server.URL)

s, _, err := c.Anime.UpdateMyListStatus(ctx, 967,
	mal.AnimeStatusWatching,
	mal.NumEpisodesWatched(73),
	mal.Score(8),
	mal.Comments("You wa shock!"),
	mal.StartDate(time.Date(2022, 02, 20, 0, 0, 0, 0, time.UTC)),
	mal.FinishDate(time.Time{}), // Remove an existing date.
)
if err != nil {
	fmt.Printf("Anime.UpdateMyListStatus error: %v", err)
	return
}
fmt.Printf("Status: %q, Score: %d, Episodes Watched: %d, Comments: %q, Start Date: %s\n", s.Status, s.Score, s.NumEpisodesWatched, s.Comments, s.StartDate)
Output:

Status: "watching", Score: 8, Episodes Watched: 73, Comments: "You wa shock!", Start Date: 2022-02-20

type AnimeStatistics

type AnimeStatistics struct {
	NumItemsWatching    int     `json:"num_items_watching"`
	NumItemsCompleted   int     `json:"num_items_completed"`
	NumItemsOnHold      int     `json:"num_items_on_hold"`
	NumItemsDropped     int     `json:"num_items_dropped"`
	NumItemsPlanToWatch int     `json:"num_items_plan_to_watch"`
	NumItems            int     `json:"num_items"`
	NumDaysWatched      float64 `json:"num_days_watched"`
	NumDaysWatching     float64 `json:"num_days_watching"`
	NumDaysCompleted    float64 `json:"num_days_completed"`
	NumDaysOnHold       float64 `json:"num_days_on_hold"`
	NumDaysDropped      float64 `json:"num_days_dropped"`
	NumDays             float64 `json:"num_days"`
	NumEpisodes         int     `json:"num_episodes"`
	NumTimesRewatched   int     `json:"num_times_rewatched"`
	MeanScore           float64 `json:"mean_score"`
}

AnimeStatistics about the user.

type AnimeStatus

type AnimeStatus string

AnimeStatus is an option that allows to filter the returned anime list by the specified status when using the UserService.AnimeList method. It can also be passed as an option when updating the anime list.

const (
	// AnimeStatusWatching returns the anime with status 'watching' from a
	// user's list or sets the status of a list item as such.
	AnimeStatusWatching AnimeStatus = "watching"
	// AnimeStatusCompleted returns the anime with status 'completed' from a
	// user's list or sets the status of a list item as such.
	AnimeStatusCompleted AnimeStatus = "completed"
	// AnimeStatusOnHold returns the anime with status 'on hold' from a user's
	// list or sets the status of a list item as such.
	AnimeStatusOnHold AnimeStatus = "on_hold"
	// AnimeStatusDropped returns the anime with status 'dropped' from a user's
	// list or sets the status of a list item as such.
	AnimeStatusDropped AnimeStatus = "dropped"
	// AnimeStatusPlanToWatch returns the anime with status 'plan to watch' from
	// a user's list or sets the status of a list item as such.
	AnimeStatusPlanToWatch AnimeStatus = "plan_to_watch"
)

type Author

type Author struct {
	Person Person `json:"node"`
	Role   string `json:"role"`
}

Author of the manga.

type BoardID

type BoardID int

BoardID is an option that filters topics based on the board ID.

type Broadcast

type Broadcast struct {
	DayOfTheWeek string `json:"day_of_the_week"`
	StartTime    string `json:"start_time"`
}

Broadcast is the day and time that the show broadcasts.

type Client

type Client struct {

	// Base URL for MyAnimeList API requests.
	BaseURL *url.URL

	Anime *AnimeService
	Manga *MangaService
	User  *UserService
	Forum *ForumService
	// contains filtered or unexported fields
}

Client manages communication with the MyAnimeList API.

func NewClient

func NewClient(httpClient *http.Client) *Client

NewClient returns a new MyAnimeList API client. The httpClient parameter allows to specify the http.client that will be used for all API requests. If a nil httpClient is provided, a new http.Client will be used.

In the typical case, you will want to provide an http.Client that will perform the authentication for you. Such a client is provided by the golang.org/x/oauth2 package. Check out the example directory of the project for a full authentication example.

func (*Client) Do

func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Response, error)

Do sends an API request and returns the API response. The API response is JSON decoded and stored in the value pointed to by v. If v implements the io.Writer interface, the raw response body will be written to v, without attempting to first decode it.

If the provided ctx is nil then an error will be returned.

func (*Client) NewRequest

func (c *Client) NewRequest(method, urlStr string, urlOptions ...func(v *url.Values)) (*http.Request, error)

NewRequest creates an API request. A relative URL can be provided in urlStr, in which case it is resolved relative to the BaseURL of the Client. Relative URLs should always be specified without a preceding slash. If data is passed as an argument then it will first be encoded in XML and then added to the request body as URL encoded value data=<xml>... This is how the MyAnimeList requires to receive the data when adding or updating entries.

MyAnimeList API docs: http://myanimelist.net/modules.php?go=api

type Comments

type Comments string

Comments is an option that allows to update the comment of anime and manga in the user's list.

type CreatedBy

type CreatedBy struct {
	ID          int    `json:"id"`
	Name        string `json:"name"`
	ForumAvator string `json:"forum_avator"`
}

CreatedBy shows the name of the user that created the post or topic.

type DetailsOption

type DetailsOption interface {
	// contains filtered or unexported methods
}

DetailsOption is an option specific for the anime and manga details methods.

type ErrorResponse

type ErrorResponse struct {
	Response *http.Response // HTTP response that caused this error
	Message  string         `json:"message"`
	Err      string         `json:"error"`
}

An ErrorResponse reports an error caused by an API request.

https://myanimelist.net/apiconfig/references/api/v2#section/Common-formats

func (*ErrorResponse) Error

func (r *ErrorResponse) Error() string

type Fields

type Fields []string

Fields is an option that allows to choose the fields that should be returned as by default, the API doesn't return all fields.

Example:

Fields{"synopsis", "my_list_status{priority,comments}"}

type FinishDate added in v0.9.5

type FinishDate time.Time

FinishDate is an option that allows to update the finish date of anime and manga in the user's list.

type Forum

type Forum struct {
	Categories []ForumCategory `json:"categories"`
}

The Forum of MyAnimeList.

type ForumBoard

type ForumBoard struct {
	ID          int             `json:"id"`
	Title       string          `json:"title"`
	Description string          `json:"description"`
	Subboards   []ForumSubboard `json:"subboards"`
}

ForumBoard is a board of the forum.

type ForumCategory

type ForumCategory struct {
	Title  string       `json:"title"`
	Boards []ForumBoard `json:"boards"`
}

ForumCategory is a category of the forum.

type ForumService

type ForumService struct {
	// contains filtered or unexported fields
}

ForumService handles communication with the forum related methods of the MyAnimeList API:

https://myanimelist.net/apiconfig/references/api/v2#tag/forum

func (*ForumService) Boards

func (s *ForumService) Boards(ctx context.Context) (*Forum, *Response, error)

Boards returns the forum boards.

Example
ctx := context.Background()

c := mal.NewClient(nil)

// Ignore the 3 following lines. A stub server is used instead of the real
// API to produce testable examples. See: https://go.dev/blog/examples
server := newStubServer()
defer server.Close()
c.BaseURL, _ = url.Parse(server.URL)

forum, _, err := c.Forum.Boards(ctx)
if err != nil {
	fmt.Printf("Forum.Boards error: %v", err)
	return
}
for _, category := range forum.Categories {
	fmt.Printf("%s\n", category.Title)
	for _, b := range category.Boards {
		fmt.Printf("|-> %s\n", b.Title)
		for _, b := range b.Subboards {
			fmt.Printf("    |-> %s\n", b.Title)
		}
	}
	fmt.Println("---")
}
Output:

MyAnimeList
|-> Updates & Announcements
|-> MAL Guidelines & FAQ
|-> DB Modification Requests
    |-> Anime DB
    |-> Character & People DB
    |-> Manga DB
|-> Support
|-> Suggestions
|-> MAL Contests
---
Anime & Manga
|-> News Discussion
|-> Anime & Manga Recommendations
|-> Series Discussion
    |-> Anime Series
    |-> Manga Series
|-> Anime Discussion
|-> Manga Discussion
---
General
|-> Introductions
|-> Games, Computers & Tech Support
|-> Music & Entertainment
|-> Current Events
|-> Casual Discussion
|-> Creative Corner
|-> Forum Games
---

func (*ForumService) TopicDetails

func (s *ForumService) TopicDetails(ctx context.Context, topicID int, options ...PagingOption) (TopicDetails, *Response, error)

TopicDetails returns details about the forum topic specified by topicID.

Example
ctx := context.Background()

c := mal.NewClient(nil)

// Ignore the 3 following lines. A stub server is used instead of the real
// API to produce testable examples. See: https://go.dev/blog/examples
server := newStubServer()
defer server.Close()
c.BaseURL, _ = url.Parse(server.URL)

topicDetails, _, err := c.Forum.TopicDetails(ctx, 1877721, mal.Limit(3), mal.Offset(0))
if err != nil {
	fmt.Printf("Forum.TopicDetails error: %v", err)
	return
}
fmt.Printf("Topic title: %q\n", topicDetails.Title)
if topicDetails.Poll != nil {
	fmt.Printf("Poll: %q\n", topicDetails.Poll.Question)
	for _, o := range topicDetails.Poll.Options {
		fmt.Printf("- %-25s %2d\n", o.Text, o.Votes)
	}
}
for _, p := range topicDetails.Posts {
	fmt.Printf("Post: %2d created by %q\n", p.Number, p.CreatedBy.Name)
}
Output:

Topic title: "What is the best JoJo opening?"
Poll: "What is the best JoJo opening?"
- Sono Chi No Sadame        23
- Bloody Stream             61
- Stand Proud               12
- End Of The World          14
- Crazy Noisy Bizarre Town  22
- Chase                     13
- Great Days                34
- Fighting Gold             15
- Traitor's Requiem         11
Post:  1 created by "Ringtomb"
Post:  2 created by "Kenzolo-folk"
Post:  3 created by "MechKingKillbot"

func (*ForumService) Topics

func (s *ForumService) Topics(ctx context.Context, options ...TopicsOption) ([]Topic, *Response, error)

Topics returns the forum's topics. Make sure to pass at least the Query option or you will get an API error.

Example
ctx := context.Background()

c := mal.NewClient(nil)

// Ignore the 3 following lines. A stub server is used instead of the real
// API to produce testable examples. See: https://go.dev/blog/examples
server := newStubServer()
defer server.Close()
c.BaseURL, _ = url.Parse(server.URL)

topics, _, err := c.Forum.Topics(ctx,
	mal.Query("JoJo opening"),
	mal.SortTopicsRecent,
	mal.Limit(2),
)
if err != nil {
	fmt.Printf("Forum.Topics error: %v", err)
	return
}
for _, t := range topics {
	fmt.Printf("ID: %5d, Title: %5q created by %q\n", t.ID, t.Title, t.CreatedBy.Name)
}
Output:

ID: 1877721, Title: "What is the best JoJo opening?" created by "Ringtomb"
ID: 1851738, Title: "JoJo's Bizarre Adventures but its Yu Yu Hakusho Opening" created by "TinTin_29"

type ForumSubboard

type ForumSubboard struct {
	ID    int    `json:"id"`
	Title string `json:"title"`
}

ForumSubboard is a subboard of the forum.

type Genre

type Genre struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

The Genre of the anime.

type IsRereading

type IsRereading bool

IsRereading is an option that can update if a user is rereading a manga in their list.

type IsRewatching

type IsRewatching bool

IsRewatching is an option that can update if a user is rewatching an anime in their list.

type Limit

type Limit int

Limit is an option that limits the results returned by a request.

type Magazine

type Magazine struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

Magazine in which the manga was serialized.

type Manga

type Manga struct {
	ID                int                `json:"id"`
	Title             string             `json:"title"`
	MainPicture       Picture            `json:"main_picture"`
	AlternativeTitles Titles             `json:"alternative_titles"`
	StartDate         string             `json:"start_date"`
	Synopsis          string             `json:"synopsis"`
	Mean              float64            `json:"mean"`
	Rank              int                `json:"rank"`
	Popularity        int                `json:"popularity"`
	NumListUsers      int                `json:"num_list_users"`
	NumScoringUsers   int                `json:"num_scoring_users"`
	Nsfw              string             `json:"nsfw"`
	CreatedAt         time.Time          `json:"created_at"`
	UpdatedAt         time.Time          `json:"updated_at"`
	MediaType         string             `json:"media_type"`
	Status            string             `json:"status"`
	Genres            []Genre            `json:"genres"`
	MyListStatus      MangaListStatus    `json:"my_list_status"`
	NumVolumes        int                `json:"num_volumes"`
	NumChapters       int                `json:"num_chapters"`
	Authors           []Author           `json:"authors"`
	Pictures          []Picture          `json:"pictures"`
	Background        string             `json:"background"`
	RelatedAnime      []RelatedAnime     `json:"related_anime"`
	RelatedManga      []RelatedManga     `json:"related_manga"`
	Recommendations   []RecommendedManga `json:"recommendations"`
	Serialization     []Serialization    `json:"serialization"`
}

Manga represents a MyAnimeList manga.

type MangaListOption

type MangaListOption interface {
	// contains filtered or unexported methods
}

MangaListOption are options specific to the UserService.MangaList method.

type MangaListStatus

type MangaListStatus struct {
	Status          MangaStatus `json:"status"`
	IsRereading     bool        `json:"is_rereading"`
	NumVolumesRead  int         `json:"num_volumes_read"`
	NumChaptersRead int         `json:"num_chapters_read"`
	Score           int         `json:"score"`
	UpdatedAt       time.Time   `json:"updated_at"`
	Priority        int         `json:"priority"`
	NumTimesReread  int         `json:"num_times_reread"`
	RereadValue     int         `json:"reread_value"`
	Tags            []string    `json:"tags"`
	Comments        string      `json:"comments"`
	StartDate       string      `json:"start_date"`
	FinishDate      string      `json:"finish_date"`
}

MangaListStatus shows the status of each manga in a user's manga list.

type MangaRanking

type MangaRanking string

MangaRanking allows to choose how the manga will be ranked.

const (
	// MangaRankingAll returns all types ranked.
	MangaRankingAll MangaRanking = "all"
	// MangaRankingManga returns the top manga.
	MangaRankingManga MangaRanking = "manga"
	// MangaRankingOneshots returns the top one-shots.
	MangaRankingOneshots MangaRanking = "oneshots"
	// MangaRankingDoujinshi returns the top doujinshi.
	MangaRankingDoujinshi MangaRanking = "doujin"
	// MangaRankingLightNovels returns the top light novels.
	MangaRankingLightNovels MangaRanking = "lightnovels"
	// MangaRankingNovels returns the top novels.
	MangaRankingNovels MangaRanking = "novels"
	// MangaRankingManhwa returns the top manhwa.
	MangaRankingManhwa MangaRanking = "manhwa"
	// MangaRankingManhua returns the top manhua.
	MangaRankingManhua MangaRanking = "manhua"
	// MangaRankingByPopularity returns the most popular entries.
	MangaRankingByPopularity MangaRanking = "bypopularity"
	// MangaRankingFavorite returns the most favorite entries.
	MangaRankingFavorite MangaRanking = "favorite"
)

type MangaService

type MangaService struct {
	// contains filtered or unexported fields
}

MangaService handles communication with the manga related methods of the MyAnimeList API:

https://myanimelist.net/apiconfig/references/api/v2#tag/manga https://myanimelist.net/apiconfig/references/api/v2#tag/user-mangalist

func (*MangaService) DeleteMyListItem

func (s *MangaService) DeleteMyListItem(ctx context.Context, mangaID int) (*Response, error)

DeleteMyListItem deletes a manga from the user's list. If the manga does not exist in the user's list, 404 Not Found error is returned.

Example
ctx := context.Background()

c := mal.NewClient(nil)

// Ignore the 3 following lines. A stub server is used instead of the real
// API to produce testable examples. See: https://go.dev/blog/examples
server := newStubServer()
defer server.Close()
c.BaseURL, _ = url.Parse(server.URL)

resp, err := c.Manga.DeleteMyListItem(ctx, 401)
if err != nil {
	fmt.Printf("Manga.DeleteMyListItem error: %v", err)
	return
}
fmt.Println(resp.Status)
Output:

200 OK

func (*MangaService) Details

func (s *MangaService) Details(ctx context.Context, mangaID int, options ...DetailsOption) (*Manga, *Response, error)

Details returns details about a manga. By default, few manga fields are populated. Use the Fields option to specify which fields should be included.

Example
ctx := context.Background()

c := mal.NewClient(nil)

// Ignore the 3 following lines. A stub server is used instead of the real
// API to produce testable examples. See: https://go.dev/blog/examples
server := newStubServer()
defer server.Close()
c.BaseURL, _ = url.Parse(server.URL)

m, _, err := c.Manga.Details(ctx, 401,
	mal.Fields{
		"alternative_titles",
		"media_type",
		"num_volumes",
		"num_chapters",
		"authors{last_name, first_name}",
		"genres",
		"status",
	},
)
if err != nil {
	fmt.Printf("Manga.Details error: %v", err)
	return
}

fmt.Printf("%s\n", m.Title)
fmt.Printf("ID: %d\n", m.ID)
fmt.Printf("English: %s\n", m.AlternativeTitles.En)
fmt.Printf("Type: %s\n", strings.Title(m.MediaType))
fmt.Printf("Volumes: %d\n", m.NumVolumes)
fmt.Printf("Chapters: %d\n", m.NumChapters)
fmt.Print("Studios: ")
delim := ""
for _, s := range m.Authors {
	fmt.Printf("%s%s, %s (%s)", delim, s.Person.LastName, s.Person.FirstName, s.Role)
	delim = " "
}
fmt.Println()
fmt.Print("Genres: ")
delim = ""
for _, g := range m.Genres {
	fmt.Printf("%s%s", delim, g.Name)
	delim = " "
}
fmt.Println()
fmt.Printf("Status: %s\n", strings.Title(m.Status))
Output:

Kiseijuu
ID: 401
English: Parasyte
Type: Manga
Volumes: 10
Chapters: 64
Studios: Iwaaki, Hitoshi (Story & Art)
Genres: Action Psychological Sci-Fi Drama Horror Seinen
Status: Finished

func (*MangaService) List

func (s *MangaService) List(ctx context.Context, search string, options ...Option) ([]Manga, *Response, error)

List allows an authenticated user to search and list manga data. You may get user specific data by using the optional field "my_list_status".

Example
ctx := context.Background()

c := mal.NewClient(nil)

// Ignore the 3 following lines. A stub server is used instead of the real
// API to produce testable examples. See: https://go.dev/blog/examples
server := newStubServer()
defer server.Close()
c.BaseURL, _ = url.Parse(server.URL)

manga, _, err := c.Manga.List(ctx, "parasyte",
	mal.Fields{"num_volumes", "num_chapters", "alternative_titles"},
	mal.Limit(3),
	mal.Offset(0),
)
if err != nil {
	fmt.Printf("Manga.List error: %v", err)
	return
}
for _, m := range manga {
	fmt.Printf("ID: %5d, Volumes: %3d, Chapters: %3d %s (%s)\n", m.ID, m.NumVolumes, m.NumChapters, m.Title, m.AlternativeTitles.En)
}
Output:

ID:   401, Volumes:  10, Chapters:  64 Kiseijuu (Parasyte)
ID: 78789, Volumes:   1, Chapters:  12 Neo Kiseijuu (Neo Parasyte m)
ID: 80797, Volumes:   1, Chapters:  15 Neo Kiseijuu f (Neo Parasyte f)

func (*MangaService) Ranking

func (s *MangaService) Ranking(ctx context.Context, ranking MangaRanking, options ...Option) ([]Manga, *Response, error)

Ranking allows an authenticated user to receive the top manga based on a certain ranking.

Example
ctx := context.Background()

c := mal.NewClient(nil)

// Ignore the 3 following lines. A stub server is used instead of the real
// API to produce testable examples. See: https://go.dev/blog/examples
server := newStubServer()
defer server.Close()
c.BaseURL, _ = url.Parse(server.URL)

manga, _, err := c.Manga.Ranking(ctx,
	mal.MangaRankingByPopularity,
	mal.Fields{"rank", "popularity"},
	mal.Limit(6),
)
if err != nil {
	fmt.Printf("Manga.Ranking error: %v", err)
	return
}
for _, m := range manga {
	fmt.Printf("Rank: %5d, Popularity: %5d %s\n", m.Rank, m.Popularity, m.Title)
}
Output:

Rank:    38, Popularity:     1 Shingeki no Kyojin
Rank:     3, Popularity:     2 One Piece
Rank:     1, Popularity:     3 Berserk
Rank:   566, Popularity:     4 Naruto
Rank:   106, Popularity:     5 Tokyo Ghoul
Rank:    39, Popularity:     6 One Punch-Man

func (*MangaService) UpdateMyListStatus

func (s *MangaService) UpdateMyListStatus(ctx context.Context, mangaID int, options ...UpdateMyMangaListStatusOption) (*MangaListStatus, *Response, error)

UpdateMyListStatus adds the manga specified by mangaID to the user's manga list with one or more options added to update the status. If the manga already exists in the list, only the status is updated.

Example
ctx := context.Background()

c := mal.NewClient(nil)

// Ignore the 3 following lines. A stub server is used instead of the real
// API to produce testable examples. See: https://go.dev/blog/examples
server := newStubServer()
defer server.Close()
c.BaseURL, _ = url.Parse(server.URL)

s, _, err := c.Manga.UpdateMyListStatus(ctx, 401,
	mal.MangaStatusReading,
	mal.NumVolumesRead(1),
	mal.NumChaptersRead(5),
	mal.Comments("Migi"),
	mal.StartDate(time.Date(2022, 02, 20, 0, 0, 0, 0, time.UTC)),
	mal.FinishDate(time.Time{}), // Remove an existing date.
)
if err != nil {
	fmt.Printf("Manga.UpdateMyListStatus error: %v", err)
	return
}
fmt.Printf("Status: %q, Volumes Read: %d, Chapters Read: %d, Comments: %q, Start Date: %s\n", s.Status, s.NumVolumesRead, s.NumChaptersRead, s.Comments, s.StartDate)
Output:

Status: "reading", Volumes Read: 1, Chapters Read: 5, Comments: "Migi", Start Date: 2022-02-20

type MangaStatus

type MangaStatus string

MangaStatus is an option that allows to filter the returned manga list by the specified status when using the UserService.MangaList method. It can also be passed as an option when updating the manga list.

const (
	// MangaStatusReading returns the manga with status 'reading' from a user's
	// list or sets the status of a list item as such.
	MangaStatusReading MangaStatus = "reading"
	// MangaStatusCompleted returns the manga with status 'completed' from a
	// user's list or sets the status of a list item as such.
	MangaStatusCompleted MangaStatus = "completed"
	// MangaStatusOnHold returns the manga with status 'on hold' from a user's
	// list or sets the status of a list item as such.
	MangaStatusOnHold MangaStatus = "on_hold"
	// MangaStatusDropped returns the manga with status 'dropped' from a user's
	// list or sets the status of a list item as such.
	MangaStatusDropped MangaStatus = "dropped"
	// MangaStatusPlanToRead returns the manga with status 'plan to read' from a
	// user's list or sets the status of a list item as such.
	MangaStatusPlanToRead MangaStatus = "plan_to_read"
)

type MyInfoOption

type MyInfoOption interface {
	// contains filtered or unexported methods
}

MyInfoOption are options specific to the User.MyInfo method.

type NSFW added in v0.9.4

type NSFW bool

NSFW is an option which sets the NSFW query option. By default this is set to false.

type NumChaptersRead

type NumChaptersRead int

NumChaptersRead is an option that can update the number of chapters read of a manga in the user's list.

type NumEpisodesWatched

type NumEpisodesWatched int

NumEpisodesWatched is an option that can update the number of episodes watched of an anime in the user's list.

type NumTimesReread

type NumTimesReread int

NumTimesReread is an option that can update the number of times the user has reread a manga in their list.

type NumTimesRewatched

type NumTimesRewatched int

NumTimesRewatched is an option that can update the number of times the user has rewatched an anime in their list.

type NumVolumesRead

type NumVolumesRead int

NumVolumesRead is an option that can update the number of volumes read of a manga in the user's list.

type Offset

type Offset int

Offset is an option that sets the offset of the results.

type Option

type Option interface {
	// contains filtered or unexported methods
}

Option is implemented by types that can be used as options in most methods such as Limit, Offset and Fields.

type Paging

type Paging struct {
	Next     string `json:"next"`
	Previous string `json:"previous"`
}

Paging provides access to the next and previous page URLs when there are pages of results.

type PagingOption

type PagingOption interface {
	// contains filtered or unexported methods
}

A PagingOption includes the Limit and Offset options which are used for controlling pagination in results.

type Person

type Person struct {
	ID        int    `json:"id"`
	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"`
}

Person is usually the creator of a manga.

type Picture

type Picture struct {
	Medium string `json:"medium"`
	Large  string `json:"large"`
}

Picture is a representative picture from the show.

type Poll

type Poll struct {
	ID       int          `json:"id"`
	Question string       `json:"question"`
	Closed   bool         `json:"closed"`
	Options  []PollOption `json:"options"`
}

Poll is an optional poll in a forum post.

type PollOption

type PollOption struct {
	ID    int    `json:"id"`
	Text  string `json:"text"`
	Votes int    `json:"votes"`
}

PollOption is one of the choices of a poll.

type Post

type Post struct {
	ID        int       `json:"id"`
	Number    int       `json:"number"`
	CreatedAt time.Time `json:"created_at"`
	CreatedBy CreatedBy `json:"created_by"`
	Body      string    `json:"body"`
	Signature string    `json:"signature"`
}

Post is a forum post.

type Priority

type Priority int

Priority is an option that allows to update the priority of an anime or manga in the user's list with values 0=Low, 1=Medium, 2=High.

type Query

type Query string

Query is an option that allows to search for a term.

type RecommendedAnime

type RecommendedAnime struct {
	Node               Anime `json:"node"`
	NumRecommendations int   `json:"num_recommendations"`
}

RecommendedAnime is a recommended anime.

type RecommendedManga

type RecommendedManga struct {
	Node               Manga `json:"node"`
	NumRecommendations int   `json:"num_recommendations"`
}

RecommendedManga is a manga recommended to the user.

type RelatedAnime

type RelatedAnime struct {
	Node                  Anime  `json:"node"`
	RelationType          string `json:"relation_type"`
	RelationTypeFormatted string `json:"relation_type_formatted"`
}

RelatedAnime contains a related anime.

type RelatedManga

type RelatedManga struct {
	Node                  Manga  `json:"node"`
	RelationType          string `json:"relation_type"`
	RelationTypeFormatted string `json:"relation_type_formatted"`
}

RelatedManga shows manga related with the returned entry.

type RereadValue

type RereadValue int

RereadValue is an option that can update the reread value of a manga in the user's list with values:

0 = No value
1 = Very Low
2 = Low
3 = Medium
4 = High
5 = Very High

type Response

type Response struct {
	*http.Response
	Body []byte

	NextOffset int
	PrevOffset int
}

Response wraps http.Response and is returned in all the library functions that communicate with the MyAnimeList API. Even if an error occurs the response will always be returned along with the actual error so that the caller can further inspect it if needed. For the same reason it also keeps a copy of the http.Response.Body that was read when the response was first received.

type RewatchValue

type RewatchValue int

RewatchValue is an option that can update the rewatch value of an anime in the user's list with values:

0 = No value
1 = Very Low
2 = Low
3 = Medium
4 = High
5 = Very High

type Score

type Score int

Score is an option that can update the anime and manga list scores with values 0-10.

type SeasonalAnimeOption

type SeasonalAnimeOption interface {
	// contains filtered or unexported methods
}

SeasonalAnimeOption are options specific to the AnimeService.Seasonal method.

type Serialization

type Serialization struct {
	Node Magazine `json:"node"`
	Role string   `json:"role"`
}

Serialization is a serialized manga series.

type SortAnimeList

type SortAnimeList string

SortAnimeList is an option that sorts the results when getting the user's anime list.

const (
	// SortAnimeListByListScore sorts results by the score of each item in the
	// list in descending order.
	SortAnimeListByListScore SortAnimeList = "list_score"
	// SortAnimeListByListUpdatedAt sorts results by the most updated entries in
	// the list in descending order.
	SortAnimeListByListUpdatedAt SortAnimeList = "list_updated_at"
	// SortAnimeListByAnimeTitle sorts results by the anime title in ascending
	// order.
	SortAnimeListByAnimeTitle SortAnimeList = "anime_title"
	// SortAnimeListByAnimeStartDate sorts results by the anime start date in
	// descending order.
	SortAnimeListByAnimeStartDate SortAnimeList = "anime_start_date"
	// SortAnimeListByAnimeID sorts results by the anime ID in ascending order.
	// Note: Currently under development.
	SortAnimeListByAnimeID SortAnimeList = "anime_id"
)

type SortMangaList

type SortMangaList string

SortMangaList is an option that sorts the results when getting the user's manga list.

const (
	// SortMangaListByListScore sorts results by the score of each item in the
	// list in descending order.
	SortMangaListByListScore SortMangaList = "list_score"
	// SortMangaListByListUpdatedAt sorts results by the most updated entries in
	// the list in descending order.
	SortMangaListByListUpdatedAt SortMangaList = "list_updated_at"
	// SortMangaListByMangaTitle sorts results by the manga title in ascending
	// order.
	SortMangaListByMangaTitle SortMangaList = "manga_title"
	// SortMangaListByMangaStartDate sorts results by the manga start date in
	// descending order.
	SortMangaListByMangaStartDate SortMangaList = "manga_start_date"
	// SortMangaListByMangaID sorts results by the manga ID in ascending order.
	// Note: Currently under development.
	SortMangaListByMangaID SortMangaList = "manga_id"
)

type SortSeasonalAnime

type SortSeasonalAnime string

SortSeasonalAnime is an option that allows to sort the anime results.

const (
	// SortSeasonalByAnimeScore sorts seasonal results by anime score in
	// descending order.
	SortSeasonalByAnimeScore SortSeasonalAnime = "anime_score"
	// SortSeasonalByAnimeNumListUsers sorts seasonal results by anime num list
	// users in descending order.
	SortSeasonalByAnimeNumListUsers SortSeasonalAnime = "anime_num_list_users"
)

type StartDate added in v0.9.5

type StartDate time.Time

StartDate is an option that allows to update the start date of anime and manga in the user's list.

type StartSeason

type StartSeason struct {
	Year   int    `json:"year"`
	Season string `json:"season"`
}

StartSeason is the season an anime starts.

type Statistics

type Statistics struct {
	Status       Status `json:"status"`
	NumListUsers int    `json:"num_list_users"`
}

Statistics about the anime.

type Status

type Status struct {
	Watching    string `json:"watching"`
	Completed   string `json:"completed"`
	OnHold      string `json:"on_hold"`
	Dropped     string `json:"dropped"`
	PlanToWatch string `json:"plan_to_watch"`
}

Status of the user's anime list contained in statistics.

type Studio

type Studio struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

The Studio that created the anime.

type SubboardID

type SubboardID int

SubboardID is an option that filters topics based on the subboard ID.

type Tags

type Tags []string

Tags is an option that allows to update the comma-separated tags of anime and manga in the user's list.

type Titles

type Titles struct {
	Synonyms []string `json:"synonyms"`
	En       string   `json:"en"`
	Ja       string   `json:"ja"`
}

Titles of the anime in English and Japanese.

type Topic

type Topic struct {
	ID                int       `json:"id"`
	Title             string    `json:"title"`
	CreatedAt         time.Time `json:"created_at"`
	CreatedBy         CreatedBy `json:"created_by"`
	NumberOfPosts     int       `json:"number_of_posts"`
	LastPostCreatedAt time.Time `json:"last_post_created_at"`
	LastPostCreatedBy CreatedBy `json:"last_post_created_by"`
	IsLocked          bool      `json:"is_locked"`
}

A Topic of the forum.

type TopicDetails

type TopicDetails struct {
	Title string `json:"title"`
	Posts []Post `json:"posts"`
	Poll  *Poll  `json:"poll"`
}

TopicDetails contain the posts of a forum topic and an optional poll.

type TopicUserName

type TopicUserName string

TopicUserName is an option that filters topics based on the topic username.

type TopicsOption

type TopicsOption interface {
	// contains filtered or unexported methods
}

TopicsOption are options specific to the ForumService.Topics method.

type UpdateMyAnimeListStatusOption

type UpdateMyAnimeListStatusOption interface {
	// contains filtered or unexported methods
}

UpdateMyAnimeListStatusOption are options specific to the AnimeService.UpdateMyListStatus method.

type UpdateMyMangaListStatusOption

type UpdateMyMangaListStatusOption interface {
	// contains filtered or unexported methods
}

UpdateMyMangaListStatusOption are options specific to the MangaService.UpdateMyListStatus method.

type User

type User struct {
	ID              int64           `json:"id"`
	Name            string          `json:"name"`
	Picture         string          `json:"picture"`
	Gender          string          `json:"gender"`
	Birthday        string          `json:"birthday"`
	Location        string          `json:"location"`
	JoinedAt        time.Time       `json:"joined_at"`
	AnimeStatistics AnimeStatistics `json:"anime_statistics"`
	TimeZone        string          `json:"time_zone"`
	IsSupporter     bool            `json:"is_supporter"`
}

User represents a MyAnimeList user.

type UserAnime

type UserAnime struct {
	Anime  Anime           `json:"node"`
	Status AnimeListStatus `json:"list_status"`
}

UserAnime contains an anime record along with its status on the user's list.

type UserManga

type UserManga struct {
	Manga  Manga           `json:"node"`
	Status MangaListStatus `json:"list_status"`
}

UserManga contains a manga record along with its status on the user's list.

type UserName

type UserName string

UserName is an option that filters topics based on a username.

type UserService

type UserService struct {
	// contains filtered or unexported fields
}

UserService handles communication with the user related methods of the MyAnimeList API:

https://myanimelist.net/apiconfig/references/api/v2#tag/user https://myanimelist.net/apiconfig/references/api/v2#operation/users_user_id_animelist_get https://myanimelist.net/apiconfig/references/api/v2#operation/users_user_id_mangalist_get

func (*UserService) AnimeList

func (s *UserService) AnimeList(ctx context.Context, username string, options ...AnimeListOption) ([]UserAnime, *Response, error)

AnimeList gets the anime list of the user indicated by username (or use @me). The anime can be sorted and filtered using the AnimeStatus and SortAnimeList option functions respectively.

Example
ctx := context.Background()

c := mal.NewClient(nil)

// Ignore the 3 following lines. A stub server is used instead of the real
// API to produce testable examples. See: https://go.dev/blog/examples
server := newStubServer()
defer server.Close()
c.BaseURL, _ = url.Parse(server.URL)

anime, _, err := c.User.AnimeList(ctx, "@me",
	mal.Fields{"list_status"},
	mal.SortAnimeListByListUpdatedAt,
	mal.Limit(5),
)
if err != nil {
	fmt.Printf("User.AnimeList error: %v", err)
	return
}
for _, a := range anime {
	fmt.Printf("ID: %5d, Status: %15q, Episodes Watched: %3d %s\n", a.Anime.ID, a.Status.Status, a.Status.NumEpisodesWatched, a.Anime.Title)
}
Output:

ID:   967, Status:      "watching", Episodes Watched:  73 Hokuto no Ken
ID:   820, Status:      "watching", Episodes Watched:   1 Ginga Eiyuu Densetsu
ID: 42897, Status:      "watching", Episodes Watched:   2 Horimiya
ID:  1453, Status:      "watching", Episodes Watched:  28 Maison Ikkoku
ID: 37521, Status:     "completed", Episodes Watched:  24 Vinland Saga

func (*UserService) MangaList

func (s *UserService) MangaList(ctx context.Context, username string, options ...MangaListOption) ([]UserManga, *Response, error)

MangaList gets the manga list of the user indicated by username (or use @me). The manga can be sorted and filtered using the MangaStatus and SortMangaList option functions respectively.

Example
ctx := context.Background()

c := mal.NewClient(nil)

// Ignore the 3 following lines. A stub server is used instead of the real
// API to produce testable examples. See: https://go.dev/blog/examples
server := newStubServer()
defer server.Close()
c.BaseURL, _ = url.Parse(server.URL)

manga, _, err := c.User.MangaList(ctx, "@me",
	mal.Fields{"list_status"},
	mal.SortMangaListByListUpdatedAt,
	mal.Limit(2),
)
if err != nil {
	fmt.Printf("User.MangaList error: %v", err)
	return
}
for _, m := range manga {
	fmt.Printf("ID: %5d, Status: %15q, Volumes Read: %3d, Chapters Read: %3d %s\n", m.Manga.ID, m.Status.Status, m.Status.NumVolumesRead, m.Status.NumChaptersRead, m.Manga.Title)
}
Output:

ID:    21, Status:     "completed", Volumes Read:  12, Chapters Read: 108 Death Note
ID:   401, Status:       "reading", Volumes Read:   1, Chapters Read:   5 Kiseijuu

func (*UserService) MyInfo

func (s *UserService) MyInfo(ctx context.Context, options ...MyInfoOption) (*User, *Response, error)

MyInfo returns information about the authenticated user.

Example
ctx := context.Background()

c := mal.NewClient(nil)

// Ignore the 3 following lines. A stub server is used instead of the real
// API to produce testable examples. See: https://go.dev/blog/examples
server := newStubServer()
defer server.Close()
c.BaseURL, _ = url.Parse(server.URL)

user, _, err := c.User.MyInfo(ctx)
if err != nil {
	fmt.Printf("User.MyInfo error: %v", err)
	return
}
fmt.Printf("ID: %5d, Joined: %v, Username: %s\n", user.ID, user.JoinedAt.Format("Jan 2006"), user.Name)
Output:

ID: 4592783, Joined: May 2015, Username: nstratos

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL