aboutsummaryrefslogtreecommitdiff
path: root/anime
diff options
context:
space:
mode:
authorVidhu Kant Sharma <vidhukant@protonmail.ch>2022-02-05 21:52:30 +0530
committerVidhu Kant Sharma <vidhukant@protonmail.ch>2022-02-05 21:52:30 +0530
commitf3ec24145da97fa7e4a5687503a6f46d59ff8c2a (patch)
treedd8f63a52b862b2ba64d562230da087d727d0fd2 /anime
parentb876e67c2b21631231a06a4f7c929cb212d01595 (diff)
added function to get seasonal anime and added fields support to all the endpoints that have it
Diffstat (limited to 'anime')
-rw-r--r--anime/anime.go128
-rw-r--r--anime/anime.structs.go5
-rw-r--r--anime/errhandlers.go71
-rw-r--r--anime/general.structs.go27
-rw-r--r--anime/seasonal.structs.go34
-rw-r--r--anime/validators.go21
6 files changed, 214 insertions, 72 deletions
diff --git a/anime/anime.go b/anime/anime.go
index e05b674..6c65138 100644
--- a/anime/anime.go
+++ b/anime/anime.go
@@ -27,25 +27,30 @@ import (
const BASE_URL string = "https://api.myanimelist.net/v2/anime"
// in MAL documentation this is named Get Anime List
-// TODO: handle errors (if any)
-func SearchAnime(token, searchString string, limit, offset int) (AnimeSearch, error) {
+func SearchAnime(token, searchString string, limit, offset int, fields []string) (AnimeSearch, error) {
var searchResults AnimeSearch
- // if limit exceeds what MAL supports
- if limit > 500 {
- return searchResults, errors.New(fmt.Sprintf("SearchAnime: Limit too high(%d). Max limit is 500", limit))
- } else if offset > 499 {
- return searchResults, errors.New(fmt.Sprintf("SearchAnime: Offset too high(%d). Max offset for mal2go is 499", offset))
+ // error handling for limit and offset
+ limitsErr := limitsErrHandler(limit, offset)
+ if limitsErr != nil {
+ log.Println(limitsErr)
+ }
+
+ // handle all the errors for the fields
+ fields, err := fieldsErrHandler(fields)
+ if err != nil {
+ log.Println(err)
}
// generate endpoint url with custom params
endpoint, _ := urlGenerator(
BASE_URL,
- []string{"q", "limit", "offset"},
- [][]string{{searchString}, {strconv.Itoa(limit)}, {strconv.Itoa(offset)}},
+ []string{"q", "limit", "offset", "fields"},
+ [][]string{{searchString}, {strconv.Itoa(limit)}, {strconv.Itoa(offset)}, fields},
true,
)
+ // gets data from API and stores it in a struct
var animeSearchData AnimeSearchRaw
data := requestHandler(token, endpoint)
json.Unmarshal([]byte(data), &animeSearchData)
@@ -69,32 +74,10 @@ func SearchAnime(token, searchString string, limit, offset int) (AnimeSearch, er
func GetAnimeById(token string, animeId int, fields []string) (Anime, error) {
var anime Anime
- // Check if given fields are valid
- for _, j := range(fields) {
- if !isValidField(j) {
- return anime, errors.New(fmt.Sprintf("GetAnimeById: Invalid field specified: \"%s\"", j))
- }
- }
-
- // default fields to use when none are specified
- defaultFields := []string{
- "id", "title", "main_picture",
- "alternative_titles", "start_date",
- "end_date", "synopsis", "mean", "rank",
- "popularity", "num_list_users",
- "num_scoring_users", "nsfw", "created_at",
- "updated_at", "media_type", "status",
- "genres", "my_list_status", "num_episodes",
- "start_season", "broadcast", "source",
- "average_episode_duration", "rating",
- "pictures", "background", "related_anime",
- "related_manga", "recommendations",
- "studios", "statistics",
- }
-
- if cap(fields) == 0 {
- fields = defaultFields
- log.Println("GetAnimeById: WARN: No fields specified, using all default fields to get data")
+ // handle all the errors for the fields
+ fields, err := fieldsErrHandler(fields)
+ if err != nil {
+ log.Println(err)
}
endpoint, _ := urlGenerator(
@@ -114,14 +97,19 @@ func GetAnimeById(token string, animeId int, fields []string) (Anime, error) {
}
// Ranking is a list of anime sorted by their rank
-func GetAnimeRanking(token string, rankingType string, limit int, offset int) (AnimeRanking, error) {
+func GetAnimeRanking(token string, rankingType string, limit, offset int, fields []string) (AnimeRanking, error) {
var animeRanking AnimeRanking
- // if limit exceeds what MAL supports
- if limit > 500 {
- return animeRanking, errors.New(fmt.Sprintf("GetAnimeRanking: Limit too high(%d). Max limit is 500", limit))
- } else if offset > 499 {
- return animeRanking, errors.New(fmt.Sprintf("GetAnimeRanking: Offset too high(%d). Max offset for mal2go is 499", offset))
+ // error handling for limit and offset
+ limitsErr := limitsErrHandler(limit, offset)
+ if limitsErr != nil {
+ log.Println(limitsErr)
+ }
+
+ // handle all the errors for the fields
+ fields, err := fieldsErrHandler(fields)
+ if err != nil {
+ log.Println(err)
}
// if ranking type is invalid
@@ -131,8 +119,8 @@ func GetAnimeRanking(token string, rankingType string, limit int, offset int) (A
endpoint, _ := urlGenerator(
BASE_URL + "/ranking",
- []string{"ranking_type", "limit", "offset"},
- [][]string{{rankingType}, {strconv.Itoa(limit)}, {strconv.Itoa(offset)}},
+ []string{"ranking_type", "limit", "offset", "fields"},
+ [][]string{{rankingType}, {strconv.Itoa(limit)}, {strconv.Itoa(offset)}, fields},
true,
)
@@ -164,3 +152,57 @@ func GetAnimeRanking(token string, rankingType string, limit int, offset int) (A
return animeRanking, nil
}
+
+// get list of animes from specified season
+func GetSeasonalAnime(token, year, season, sort string, limit, offset int, fields []string) (SeasonalAnime, error) {
+ var seasonalAnime SeasonalAnime
+
+ // error handling for limit and offset
+ limitsErr := limitsErrHandler(limit, offset)
+ if limitsErr != nil {
+ log.Println(limitsErr)
+ }
+
+ // handle all the errors for the fields
+ fields, err := fieldsErrHandler(fields)
+ if err != nil {
+ log.Println(err)
+ }
+
+ // checks if valid season is specified
+ if !isValidSeason(season) {
+ return seasonalAnime, errors.New(fmt.Sprintf("GetSeasonalAnime: Invalid season specified: \"%s\"", season))
+ }
+
+ // checks if valid sort is specified
+ if !isValidSort(sort) {
+ return seasonalAnime, errors.New(fmt.Sprintf("GetSeasonalAnime: Invalid sort specified: \"%s\"", sort))
+ }
+
+ endpoint, _ := urlGenerator(
+ BASE_URL + fmt.Sprintf("/season/%s/%s", year, season),
+ []string{"sort", "limit", "offset", "fields"},
+ [][]string{{sort}, {strconv.Itoa(limit)}, {strconv.Itoa(offset)}, fields},
+ true,
+ )
+
+ // gets data from API and stores it in a struct
+ var seasonalAnimeData SeasonalAnimeRaw
+ data := requestHandler(token, endpoint)
+ json.Unmarshal([]byte(data), &seasonalAnimeData)
+
+ // Adding all the animes to another list to get formatted results later
+ var animes []Anime
+ for _, element := range seasonalAnimeData.Data {
+ animes = append(animes, element.Anime)
+ }
+
+ // finally generate SeasonalAnime
+ seasonalAnime = SeasonalAnime {
+ Animes: animes,
+ Paging: seasonalAnimeData.Paging,
+ Season: seasonalAnimeData.Season,
+ }
+
+ return seasonalAnime, nil
+}
diff --git a/anime/anime.structs.go b/anime/anime.structs.go
index d9d4221..1599e6d 100644
--- a/anime/anime.structs.go
+++ b/anime/anime.structs.go
@@ -47,11 +47,6 @@ type ListStatus struct {
UpdatedAt string `json:"updated_at"`
}
-type Season struct {
- Year int `json:"year"`
- Name string `json:"season"`
-}
-
type Broadcast struct {
Day string `json:"day_of_the_week"`
Time string `json:"start_time"`
diff --git a/anime/errhandlers.go b/anime/errhandlers.go
new file mode 100644
index 0000000..d7f70f9
--- /dev/null
+++ b/anime/errhandlers.go
@@ -0,0 +1,71 @@
+/* mal2go - MyAnimeList V2 API wrapper for Go
+ * Copyright (C) 2022 Vidhu Kant Sharma <vidhukant@protonmail.ch>
+
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+package anime
+
+import (
+ "errors"
+ "fmt"
+)
+
+/* NOTE: MAL still seems to send some fields
+ * even if they aren't requested.
+ * those include Title, Picture, Id, etc */
+// default fields to use when none are specified
+var defaultFields []string = []string{
+ "id", "title", "main_picture",
+ "alternative_titles", "start_date",
+ "end_date", "synopsis", "mean", "rank",
+ "popularity", "num_list_users",
+ "num_scoring_users", "nsfw", "created_at",
+ "updated_at", "media_type", "status",
+ "genres", "my_list_status", "num_episodes",
+ "start_season", "broadcast", "source",
+ "average_episode_duration", "rating",
+ "pictures", "background", "related_anime",
+ "related_manga", "recommendations",
+ "studios", "statistics",
+}
+
+// if fields aren't specified
+func fieldsErrHandler(fields []string) ([]string, error) {
+ if cap(fields) == 0 {
+ // uses all the default fields if none specified
+ return defaultFields, nil
+ }
+
+ // checks if each given field is valid
+ for _, j := range(fields) {
+ if !isValidField(j) {
+ return []string{}, errors.New(fmt.Sprintf("InvalidFieldError: Invalid field specified: \"%s\"", j))
+ }
+ }
+
+ // everything's fine!
+ return fields, nil
+}
+
+// if limit or error specified are above the limit
+func limitsErrHandler(limit, offset int) error {
+ maxOffset := 500 - limit
+ if limit > 500 {
+ return errors.New(fmt.Sprintf("InvalidLimitError: Limit specified too high (%d > 500).", limit))
+ } else if offset > maxOffset {
+ return errors.New(fmt.Sprintf("InvalidOffsetError: Offset specified too high (%d > %d).", offset, maxOffset))
+ }
+ // return nil if no error
+ return nil
+}
diff --git a/anime/general.structs.go b/anime/general.structs.go
index b77fbe5..c49b762 100644
--- a/anime/general.structs.go
+++ b/anime/general.structs.go
@@ -22,28 +22,7 @@ type ListPaging struct {
PrevPage string `json:"previous"` // might need checking
}
-/*
-// this is how the API returns data (looks horrible)
-type RawRanking struct {
- Data []struct {
- Anime Anime `json:"node"`
- Ranking struct {
- Rank int `json:"rank"`
- } `json:"ranking"`
- } `json:"data"`
-
- Paging ListPaging `json:"paging"`
-}
-
-// each anime has a ranking number
-type AnimeRankingTitle struct {
- Anime Anime
- RankNum int
-}
-
-// this is how mal2go returns data
-type AnimeRanking struct {
- Titles []AnimeRankingTitle
- Paging ListPaging
+type Season struct {
+ Year int `json:"year"`
+ Name string `json:"season"`
}
-*/
diff --git a/anime/seasonal.structs.go b/anime/seasonal.structs.go
new file mode 100644
index 0000000..06bda42
--- /dev/null
+++ b/anime/seasonal.structs.go
@@ -0,0 +1,34 @@
+/* mal2go - MyAnimeList V2 API wrapper for Go
+ * Copyright (C) 2022 Vidhu Kant Sharma <vidhukant@protonmail.ch>
+
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+package anime
+
+// this is how the API returns data (looks horrible)
+type SeasonalAnimeRaw struct {
+ Data []struct {
+ Anime Anime `json:"node"`
+ } `json:"data"`
+
+ Paging ListPaging `json:"paging"`
+ Season Season `json:"season"`
+}
+
+// this is how mal2go returns data
+type SeasonalAnime struct {
+ Animes []Anime
+ Paging ListPaging
+ Season Season
+}
diff --git a/anime/validators.go b/anime/validators.go
index 416f827..7f6a7cc 100644
--- a/anime/validators.go
+++ b/anime/validators.go
@@ -73,3 +73,24 @@ func isValidField(field string) bool {
return false
}
+// Checks if given season is valid
+func isValidSeason(season string) bool {
+ switch season {
+ case
+ "winter",
+ "spring",
+ "summer",
+ "fall": return true
+ }
+ return false
+}
+
+// Checks if given sort is valid
+func isValidSort(sort string) bool {
+ switch sort {
+ case
+ "anime_score",
+ "anime_num_list_users": return true
+ }
+ return false
+}