186 lines
5.1 KiB
Go
186 lines
5.1 KiB
Go
package mal
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
)
|
|
|
|
const (
|
|
BaseMangaFields string = "id,title,main_picture,alternative_titles,start_date,end_date,nsfw,synopsis,num_volumes,num_chapters,mean,rank,popularity,media_type,status"
|
|
)
|
|
|
|
type (
|
|
BasicManga struct {
|
|
ID int `json:"id"`
|
|
Title string `json:"title"`
|
|
MainPicture struct {
|
|
Medium string `json:"medium"`
|
|
Large string `json:"large"`
|
|
} `json:"main_picture"`
|
|
AlternativeTitles struct {
|
|
Synonyms []string `json:"synonyms"`
|
|
En string `json:"en"`
|
|
Ja string `json:"ja"`
|
|
} `json:"alternative_titles"`
|
|
StartDate string `json:"start_date"`
|
|
EndDate string `json:"end_date"`
|
|
Synopsis string `json:"synopsis"`
|
|
NSFW string `json:"nsfw"`
|
|
NumVolumes int `json:"num_volumes"`
|
|
NumChapters int `json:"num_chapters"`
|
|
Mean float32 `json:"mean"`
|
|
Rank int `json:"rank"`
|
|
Popularity int `json:"popularity"`
|
|
MediaType MediaType `json:"media_type"`
|
|
Status MediaStatus `json:"status"`
|
|
}
|
|
|
|
MangaListEntry struct {
|
|
Node struct {
|
|
ID int `json:"id"`
|
|
Title string `json:"title"`
|
|
MainPicture struct {
|
|
Medium string `json:"medium"`
|
|
Large string `json:"large"`
|
|
} `json:"main_picture"`
|
|
} `json:"node"`
|
|
ListStatus struct {
|
|
Status MediaListStatus `json:"status"`
|
|
IsRereading bool `json:"is_rereading"`
|
|
NumVolumesRead int `json:"num_volumes_read"`
|
|
NumChaptersRead int `json:"num_chapters_read"`
|
|
Score int `json:"score"`
|
|
UpdatedAt string `json:"updated_at"`
|
|
} `json:"list_status"`
|
|
}
|
|
)
|
|
|
|
func (w *Wrapper) GetMangaDetails(mId int) (*BasicManga, error) {
|
|
w.logger.Debug().Int("mId", mId).Msg("mal: Getting manga details")
|
|
|
|
reqUrl := fmt.Sprintf("%s/manga/%d?fields=%s", ApiBaseURL, mId, BaseMangaFields)
|
|
|
|
if w.AccessToken == "" {
|
|
return nil, fmt.Errorf("access token is empty")
|
|
}
|
|
|
|
var manga BasicManga
|
|
err := w.doQuery("GET", reqUrl, nil, "application/json", &manga)
|
|
if err != nil {
|
|
w.logger.Error().Err(err).Msg("mal: Failed to get manga details")
|
|
return nil, err
|
|
}
|
|
|
|
w.logger.Info().Int("mId", mId).Msg("mal: Fetched manga details")
|
|
|
|
return &manga, nil
|
|
}
|
|
|
|
func (w *Wrapper) GetMangaCollection() ([]*MangaListEntry, error) {
|
|
w.logger.Debug().Msg("mal: Getting manga collection")
|
|
|
|
reqUrl := fmt.Sprintf("%s/users/@me/mangalist?fields=list_status&limit=1000", ApiBaseURL)
|
|
|
|
type response struct {
|
|
Data []*MangaListEntry `json:"data"`
|
|
}
|
|
|
|
var data response
|
|
err := w.doQuery("GET", reqUrl, nil, "application/json", &data)
|
|
if err != nil {
|
|
w.logger.Error().Err(err).Msg("mal: Failed to get manga collection")
|
|
return nil, err
|
|
}
|
|
|
|
w.logger.Info().Msg("mal: Fetched manga collection")
|
|
|
|
return data.Data, nil
|
|
}
|
|
|
|
type MangaListProgressParams struct {
|
|
NumChaptersRead *int
|
|
}
|
|
|
|
func (w *Wrapper) UpdateMangaProgress(opts *MangaListProgressParams, mId int) error {
|
|
w.logger.Debug().Int("mId", mId).Msg("mal: Updating manga progress")
|
|
|
|
// Get manga details
|
|
manga, err := w.GetMangaDetails(mId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
status := MediaListStatusReading
|
|
if manga.Status == MediaStatusFinished && manga.NumChapters > 0 && manga.NumChapters <= *opts.NumChaptersRead {
|
|
status = MediaListStatusCompleted
|
|
}
|
|
|
|
if manga.NumChapters > 0 && *opts.NumChaptersRead > manga.NumChapters {
|
|
*opts.NumChaptersRead = manga.NumChapters
|
|
}
|
|
|
|
// Update MAL list entry
|
|
err = w.UpdateMangaListStatus(&MangaListStatusParams{
|
|
Status: &status,
|
|
NumChaptersRead: opts.NumChaptersRead,
|
|
}, mId)
|
|
|
|
if err == nil {
|
|
w.logger.Info().Int("mId", mId).Msg("mal: Updated manga progress")
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
type MangaListStatusParams struct {
|
|
Status *MediaListStatus
|
|
IsRereading *bool
|
|
NumChaptersRead *int
|
|
Score *int
|
|
}
|
|
|
|
func (w *Wrapper) UpdateMangaListStatus(opts *MangaListStatusParams, mId int) error {
|
|
w.logger.Debug().Int("mId", mId).Msg("mal: Updating manga list status")
|
|
|
|
reqUrl := fmt.Sprintf("%s/manga/%d/my_list_status", ApiBaseURL, mId)
|
|
|
|
// Build URL
|
|
urlData := url.Values{}
|
|
if opts.Status != nil {
|
|
urlData.Set("status", string(*opts.Status))
|
|
}
|
|
if opts.IsRereading != nil {
|
|
urlData.Set("is_rereading", fmt.Sprintf("%t", *opts.IsRereading))
|
|
}
|
|
if opts.NumChaptersRead != nil {
|
|
urlData.Set("num_chapters_read", fmt.Sprintf("%d", *opts.NumChaptersRead))
|
|
}
|
|
if opts.Score != nil {
|
|
urlData.Set("score", fmt.Sprintf("%d", *opts.Score))
|
|
}
|
|
encodedData := urlData.Encode()
|
|
|
|
err := w.doMutation("PATCH", reqUrl, encodedData)
|
|
if err != nil {
|
|
w.logger.Error().Err(err).Msg("mal: Failed to update manga list status")
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w *Wrapper) DeleteMangaListItem(mId int) error {
|
|
w.logger.Debug().Int("mId", mId).Msg("mal: Deleting manga list item")
|
|
|
|
reqUrl := fmt.Sprintf("%s/manga/%d/my_list_status", ApiBaseURL, mId)
|
|
|
|
err := w.doMutation("DELETE", reqUrl, "")
|
|
if err != nil {
|
|
w.logger.Error().Err(err).Msg("mal: Failed to delete manga list item")
|
|
return err
|
|
}
|
|
|
|
w.logger.Info().Int("mId", mId).Msg("mal: Deleted manga list item")
|
|
|
|
return nil
|
|
}
|