Files
Anitrack/AniListTypes.go
T
john-okeefe 0a89ec3652 fix(anilist): decode array-shaped airingSchedule/relations and isolate per-service sync errors
The 1.6.1 fix exposed a latent decoding bug: AniList returns
airingSchedule.nodes and relations.nodes as arrays, but the Go Media
struct declared Nodes as single structs. Every full-media response
failed json.Unmarshal partway through - the update path turned that
into a rejected promise which skipped the MAL and Simkl syncs and all
table updates, while page loads silently continued with partially
decoded data (the reason the old tags/genres copy workaround existed).

- Media.AiringSchedule.Nodes is now []AiringScheduleNode and
  Media.Relations is an exported []MediaRelation (previously an
  unexported field silently dropped by encoding/json); title and
  fuzzy-date sub-structs promoted to named types.
- Regenerate wailsjs models for the new shapes.
- Anime.svelte: handleSubmit and deleteEntries now wrap each service
  in its own try/catch surfaced via setApiError/ErrorModal, so one
  service failing can no longer skip the others; removed the obsolete
  tags/genres copy workaround.
- AniListUpdateEntry/AniListDeleteEntry log HTTP status and response
  body on failure for terminal diagnostics.

Bump productVersion to 1.6.2.
2026-09-01 20:18:42 -04:00

279 lines
7.8 KiB
Go

package main
type AniListJWT struct {
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
}
type AniListUser struct {
Data struct {
Viewer struct {
ID int `json:"id"`
Name string `json:"name"`
Avatar struct {
Large string `json:"large"`
Medium string `json:"medium"`
} `json:"avatar"`
BannerImage string `json:"bannerImage"`
SiteUrl string `json:"siteUrl"`
} `json:"Viewer"`
} `json:"data"`
}
type AniListCurrentUserWatchList struct {
Data struct {
Page struct {
PageInfo struct {
Total int `json:"total"`
PerPage int `json:"perPage"`
CurrentPage int `json:"currentPage"`
LastPage int `json:"lastPage"`
HasNextPage bool `json:"hasNextPage"`
} `json:"pageInfo"`
MediaList []MediaList `json:"mediaList"`
} `json:"Page"`
} `json:"data"`
}
type AniListBrowseList struct {
Data struct {
Page struct {
PageInfo struct {
Total int `json:"total"`
PerPage int `json:"perPage"`
CurrentPage int `json:"currentPage"`
LastPage int `json:"lastPage"`
HasNextPage bool `json:"hasNextPage"`
} `json:"pageInfo"`
Media []Media `json:"mediaList"`
} `json:"Page"`
} `json:"data"`
}
type AniListGetSingleAnime struct {
Data struct {
MediaList MediaList `json:"MediaList"`
} `json:"data"`
}
type AniListUpdateReturn struct {
Data struct {
SaveMediaListEntry MediaList `json:"SaveMediaListEntry"`
}
}
type Media struct {
ID int `json:"id"`
IDMal int `json:"idMal"`
Title MediaTitle `json:"title"`
Description string `json:"description"`
CoverImage struct {
ExtraLarge string
Large string `json:"large"`
Medium string
Color string
} `json:"coverImage"`
BannerImage string
Format string
Season string `json:"season"`
SeasonYear int `json:"seasonYear"`
Status string `json:"status"`
Episodes int `json:"episodes"`
Duration int
CountryOfOrigin string
Source string
Synonyms []string
AverageScore int
MeanScore int
Popularity int
Trending int
Favourites int
isFavourite bool
Relations MediaRelations `json:"relations"`
StartDate MediaFuzzyDate `json:"startDate"`
EndDate MediaFuzzyDate `json:"endDate"`
NextAiringEpisode struct {
AiringAt int `json:"airingAt"`
TimeUntilAiring int `json:"timeUntilAiring"`
Episode int `json:"episode"`
} `json:"nextAiringEpisode"`
AiringSchedule MediaAiringSchedule `json:"airingSchedule"`
Genres []string `json:"genres"`
Tags []struct {
Id int `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Rank int `json:"rank"`
IsMediaSpoiler bool `json:"isMediaSpoiler"`
IsAdult bool `json:"isAdult"`
} `json:"tags"`
IsAdult bool `json:"isAdult"`
}
type MediaTitle struct {
UserPreferred string `json:"userPreferred"`
Romaji string `json:"romaji"`
English string `json:"english"`
Native string `json:"native"`
}
type MediaRelations struct {
Nodes []MediaRelation `json:"nodes"`
}
type MediaRelation struct {
Id int `json:"id"`
Title MediaTitle `json:"title"`
}
type MediaFuzzyDate struct {
Year int `json:"year"`
Month int `json:"month"`
Day int `json:"day"`
}
type MediaAiringSchedule struct {
Nodes []AiringScheduleNode `json:"nodes"`
}
type AiringScheduleNode struct {
Id int `json:"id"`
AiringAt int `json:"airingAt"`
TimeUntilAiring int `json:"timeUntilAiring"`
Episode int `json:"episode"`
MediaId int `json:"mediaId"`
}
type MediaList struct {
ID int `json:"id"`
MediaID int `json:"mediaId"`
UserID int `json:"userId"`
Status string `json:"status"`
Media Media `json:"media"`
StartedAt struct {
Year int `json:"year"`
Month int `json:"month"`
Day int `json:"day"`
} `json:"startedAt"`
CompletedAt struct {
Year int `json:"year"`
Month int `json:"month"`
Day int `json:"day"`
} `json:"completedAt"`
Notes string `json:"notes"`
Progress int `json:"progress"`
Score float64 `json:"score"`
Repeat int `json:"repeat"`
User struct {
ID int `json:"id"`
Name string `json:"name"`
Avatar struct {
Large string `json:"large"`
Medium string `json:"medium"`
} `json:"avatar"`
Statistics struct {
Anime struct {
Count int `json:"count"`
Statuses []struct {
Status string `json:"status"`
Count int `json:"count"`
} `json:"statuses"`
} `json:"anime"`
} `json:"statistics"`
} `json:"user"`
}
type StartedAt struct {
Year int `json:"year"`
Month int `json:"month"`
Day int `json:"day"`
}
type CompletedAt struct {
Year int `json:"year"`
Month int `json:"month"`
Day int `json:"day"`
}
type AniListUpdateVariables struct {
MediaId int `json:"mediaId"`
Progress int `json:"progress"`
Status string `json:"status"`
Score float64 `json:"score"`
Repeat int `json:"repeat"`
Notes string `json:"notes"`
StartedAt StartedAt `json:"startedAt"`
CompletedAt CompletedAt `json:"completedAt"`
}
type DeleteAniListReturn struct {
Data struct {
DeleteMediaListEntry struct {
Deleted bool `json:"deleted"`
} `json:"DeleteMediaListEntry"`
} `json:"data"`
}
//var MediaListSort = struct {
// MediaId string
// MediaIdDesc string
// Score string
// ScoreDesc string
// Status string
// StatusDesc string
// Progress string
// ProgressDesc string
// ProgressVolumes string
// ProgressVolumesDesc string
// Repeat string
// RepeatDesc string
// Priority string
// PriorityDesc string
// StartedOn string
// StartedOnDesc string
// FinishedOn string
// FinishedOnDesc string
// AddedTime string
// AddedTimeDesc string
// UpdatedTime string
// UpdatedTimeDesc string
// MediaTitleRomaji string
// MediaTitleRomajiDesc string
// MediaTitleEnglish string
// MediaTitleEnglishDesc string
// MediaTitleNative string
// MediaTitleNativeDesc string
// MediaPopularity string
// MediaPopularityDesc string
//}{
// MediaId: "MEDIA_ID",
// MediaIdDesc: "MEDIA_ID_DESC",
// Score: "SCORE",
// ScoreDesc: "SCORE_DESC",
// Status: "STATUS",
// StatusDesc: "STATUS_DESC",
// Progress: "PROGRESS",
// ProgressDesc: "PROGRESS_DESC",
// ProgressVolumes: "PROGRESS_VOLUMES",
// ProgressVolumesDesc: "PROGRESS_VOLUMES_DESC",
// Repeat: "REPEAT",
// RepeatDesc: "REPEAT_DESC",
// Priority: "PRIORITY",
// PriorityDesc: "PRIORITY_DESC",
// StartedOn: "STARTED_ON",
// StartedOnDesc: "STARTED_ON_DESC",
// FinishedOn: "FINISHED_ON",
// FinishedOnDesc: "FINISHED_ON_DESC",
// AddedTime: "ADDED_TIME",
// AddedTimeDesc: "ADDED_TIME_DESC",
// UpdatedTime: "UPDATED_TIME",
// UpdatedTimeDesc: "UPDATED_TIME_DESC",
// MediaTitleRomaji: "MEDIA_TITLE_ROMAJI",
// MediaTitleRomajiDesc: "MEDIA_TITLE_ROMAJI_DESC",
// MediaTitleEnglish: "MEDIA_TITLE_ENGLISH",
// MediaTitleEnglishDesc: "MEDIA_TITLE_ENGLISH_DESC",
// MediaTitleNative: "MEDIA_TITLE_NATIVE",
// MediaTitleNativeDesc: "MEDIA_TITLE_NATIVE_DESC",
// MediaPopularity: "MEDIA_POPULARITY",
// MediaPopularityDesc: "MEDIA_POPULARITY_DESC",
//}