]> git.lizzy.rs Git - go-anidb.git/blob - http/anime.go
http: Recognize more Resource types
[go-anidb.git] / http / anime.go
1 // Low-level wrapper around the AniDB HTTP API.
2 // Only implements the 'anime' and 'categorylist' requests.
3 //
4 // This wrapper does not implement caching. The API requires
5 // aggressive caching.
6 //
7 // http://wiki.anidb.info/w/HTTP_API_Definition
8 package httpapi
9
10 import (
11         "encoding/xml"
12         "fmt"
13         "log"
14         "net/http"
15         "net/url"
16         "strings"
17 )
18
19 const (
20         AniDBImageBaseURL = "http://img7.anidb.net/pics/anime/" // Base URL for the various Pictures in the response
21
22         DateFormat = "2006-01-02" // Use to convert the various YYYY-MM-DD timestamps to a time.Time.
23
24         // Base URLs for the various resources.
25         // Meant for use with fmt.Sprintf.
26         ANNFormat          = "http://www.animenewsnetwork.com/encyclopedia/anime.php?id=%v" // Type 1
27         MyAnimeListFormat  = "http://myanimelist.net/anime/%v"                              // Type 2
28         AnimeNfoFormat     = "http://www.animenfo.com/animetitle,%v,%v,a.html"              // Type 3
29         _                                                                                   // Type 4
30         _                                                                                   // Type 5
31         WikiEnglishFormat  = "http://en.wikipedia.org/wiki/%v"                              // Type 6
32         WikiJapaneseFormat = "http://ja.wikipedia.org/wiki/%v"                              // Type 7
33         SyoboiFormat       = "http://cal.syoboi.jp/tid/%v/time"                             // Type 8
34         AllCinemaFormat    = "http://www.allcinema.net/prog/show_c.php?num_c=%v"            // Type 9
35         AnisonFormat       = "http://anison.info/data/program/%v.html"                      // Type 10
36         LainGrJpFormat     = "http://lain.gr.jp/%v"                                         // Type 11
37         _                                                                                   // Type 12
38         _                                                                                   // Type 13
39         VNDBFormat         = "http://vndb.org/v%v"                                          // Type 14
40         MaruMeganeFormat   = "http://www.anime.marumegane.com/%v.html"                      // Type 15
41         _                                                                                   // Type 16
42         TVAnimationMuseum  = "http://home-aki.cool.ne.jp/anime-list/%s.htm"                 // Type 17 (broken)
43         _                                                                                   // Type 18
44         WikiKoreanformat   = "http://ko.wikipedia.org/wiki/%v"                              // Type 19
45         WikiChineseFormat  = "http://zh.wikipedia.org/wiki/%v"                              // Type 20
46 )
47
48 const (
49         aniDBHTTPAPIBaseURL = "http://api.anidb.net:9001/httpapi"
50         aniDBProtoVer       = 1
51         clientStr           = "goanidbhttp"
52         clientVer           = 1
53 )
54
55 // Requests information about the given Anime ID.
56 func GetAnime(AID int) (a Anime, err error) {
57         if res, err := doRequest("anime", reqMap{"aid": AID}); err != nil {
58                 return a, err
59         } else {
60                 dec := xml.NewDecoder(res.Body)
61                 err = dec.Decode(&a)
62                 res.Body.Close()
63
64                 a.Error = strings.TrimSpace(a.Error)
65
66                 title := ""
67                 for _, t := range a.Titles {
68                         if t.Type == "main" {
69                                 title = t.Title
70                                 break
71                         }
72                 }
73
74                 for _, r := range a.Resources {
75                         switch r.Type {
76                         case 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, 17, 19, 20:
77                                 // documentation knows about these
78                         default:
79                                 log.Printf("HTTP -- Anime %d (%s) has unknown resource type %d", a.ID, title, r.Type)
80                                 log.Printf("HTTP -- Type %d external entities: %#v", r.Type, r.ExternalEntity)
81                         }
82                 }
83
84                 return a, err
85         }
86 }
87
88 type reqMap map[string]interface{}
89
90 func doRequest(request string, reqMap reqMap) (*http.Response, error) {
91         v := url.Values{}
92         v.Set("protover", fmt.Sprint(aniDBProtoVer))
93         v.Set("client", clientStr)
94         v.Set("clientver", fmt.Sprint(clientVer))
95         v.Set("request", request)
96
97         for k, val := range reqMap {
98                 v.Add(k, fmt.Sprint(val))
99         }
100
101         u, _ := url.Parse(aniDBHTTPAPIBaseURL)
102         u.RawQuery = v.Encode()
103         return http.Get(u.String())
104 }
105
106 // Title with language and type identifier.
107 //
108 // Title with Lang = ja, Type = official is the official Kanji title.
109 //
110 // Title with Lang = x-jat, Type = main is the romanized version, also known in other APIs as the Primary Title.
111 type AnimeTitle struct {
112         Lang  string `xml:"lang,attr"` // Language in ISO-ish format
113         Type  string `xml:"type,attr"` // "official", "short", etc
114         Title string `xml:",chardata"`
115 }
116
117 type RelatedAnime struct {
118         ID    int    `xml:"id,attr"`   // AID of the related anime
119         Type  string `xml:"type,attr"` // "prequel", "sequel", etc
120         Title string `xml:",chardata"` // Primary title of the related anime
121 }
122
123 type SimilarAnime struct {
124         ID       int    `xml:"id,attr"`       // AID of the similar anime
125         Approval int    `xml:"approval,attr"` // How many users have approved of this connection
126         Total    int    `xml:"total,attr"`    // Total of votes in this connection
127         Title    string `xml:",chardata"`     // Primary title of the recommended anime
128 }
129
130 type Recommendation struct {
131         Type string `xml:"type,attr"` // "Recommended", "Must See", etc
132         ID   int    `xml:"uid,attr"`  // User ID of the recommending user
133         Text string `xml:",chardata"` // Text of the user's recommendation
134 }
135
136 type Creator struct {
137         ID   int    `xml:"id,attr"` // Creator ID
138         Type string `xml:"type,attr"`
139         Name string `xml:",chardata"` // Always romaji
140 }
141
142 // Separate from regular Rating because the XML structure is different.
143 type AnimeRating struct {
144         Count  int     `xml:"count,attr"` // Amount of votes/reviews
145         Rating float32 `xml:",chardata"`  // Average
146 }
147
148 type AnimeRatings struct {
149         Permanent AnimeRating `xml:"permanent"` // Votes from people who watched everything
150         Temporary AnimeRating `xml:"temporary"` // Votes from people who are still watching it
151         Review    AnimeRating `xml:"review"`    // Votes from reviews
152 }
153
154 type Category struct {
155         ID       int  `xml:"id,attr"`       // Category ID
156         ParentID int  `xml:"parentid,attr"` // ID of the parent category
157         R18      bool `xml:"hentai,attr"`   // Whether the category represents porn works or not
158         Weight   int  `xml:"weight,attr"`   // Weight of the category for this anime
159
160         Name        string `xml:"name"`        // Category name
161         Description string `xml:"description"` // Category description
162 }
163
164 type ExternalEntity struct {
165         Identifiers []string `xml:"identifier"`
166         URL         []string `xml:"url"` // Used for some types
167 }
168
169 // Completely undocumented.
170 // Most entries just have one or two numbers as Identifiers.
171 //
172 // Empiric documentation:
173 //
174 // Type 1 is the ANN id.
175 //
176 // Type 2 is the MyAnimeList ID.
177 //
178 // Type 3 is the AnimeNfo ID tuple.
179 //
180 // Type 4 is the official japanese webpage. URL may contain additional URLs (official PV, etc)
181 //
182 // Type 5 is the official english webpage.
183 //
184 // Type 6 is the english wikipedia page name.
185 //
186 // Type 7 is the japanese wikipedia page name.
187 //
188 // Type 8 is the cal.syoboi.jp schedule ID.
189 //
190 // Type 9 is the AllCinema ID.
191 //
192 // Type 10 is the anison.info ID.
193 //
194 // Type 11 is the lain.gr.jp path.
195 //
196 // Type 14 is the VNDB ID.
197 //
198 // Type 15 is the MaruMegane ID.
199 //
200 // Type 17 would be the TV Animation Museum identifier, but the website is no more.
201 //
202 // Type 19 is the korean wikipedia page name.
203 //
204 // Type 20 is the chinese wikipedia page name.
205 type Resource struct {
206         Type           int              `xml:"type,attr"`
207         ExternalEntity []ExternalEntity `xml:"externalentity"`
208 }
209
210 type Tag struct {
211         ID            int    `xml:"id,attr"`            // Tag ID
212         Approval      int    `xml:"approval,attr"`      // How many users have approved of the tag
213         Spoiler       bool   `xml:"localspoiler,attr"`  // undocumented
214         GlobalSpoiler bool   `xml:"globalspoiler,attr"` // undocumented
215         Updated       string `xml:"update,attr"`        // YYYY-MM-DD
216
217         Name  string `xml:"name"`  // Tag name
218         Count int    `xml:"count"` // undocumented
219 }
220
221 type Seiyuu struct {
222         ID      int    `xml:"id,attr"`      // Creator ID
223         Name    string `xml:",chardata"`    // Always romaji
224         Picture string `xml:"picture,attr"` // Picture basename; combine with AniDBImageBaseURL for full URL
225 }
226
227 type Character struct {
228         ID      int    `xml:"id,attr"`     // Character ID
229         Type    string `xml:"type,attr"`   // "main character in", "secondary cast in", "appears in"
230         Updated string `xml:"update,attr"` // YYYY-MM-DD
231
232         Rating        Rating `xml:"rating"`
233         Name          string `xml:"name"`   // Always romaji
234         Gender        string `xml:"gender"` // "male", "female", "unknown", sometimes blank
235         Description   string `xml:"description"`
236         CharacterType string `xml:"charactertype"` // "Character", "Organization", "Vessel", etc
237         Episodes      string `xml:"episodes"`      // List of episodes where character appears
238         Picture       string `xml:"picture"`       // Picture basename; combine with AniDBImageBaseURL for full URL
239
240         Seiyuu *Seiyuu `xml:"seiyuu"` // The voice actor, if present
241 }
242
243 type Characters []Character // Implements sort.Interface; groups by Type and sorts by Name
244
245 type EpisodeTitle struct {
246         Lang  string `xml:"lang,attr"`
247         Title string `xml:",chardata"`
248 }
249
250 type Rating struct {
251         Votes  int     `xml:"votes,attr"`
252         Rating float32 `xml:",chardata"`
253 }
254
255 type EpNo struct {
256         Type int    `xml:"type,attr"` // 1 for regular episodes, 2 for specials, etc
257         EpNo string `xml:",chardata"` // Not necessarily a plain integer; may be prefixed by a single letter indicating the Type
258 }
259
260 type Episode struct {
261         ID      int    `xml:"id,attr"`     // Episode ID
262         Updated string `xml:"update,attr"` // YYYY-MM-DD
263
264         EpNo    EpNo           `xml:"epno"`
265         Length  int            `xml:"length"`  // Length in minutes (rounding method undocumented)
266         AirDate string         `xml:"airdate"` // YYYY-MM-DD
267         Rating  Rating         `xml:"rating"`
268         Titles  []EpisodeTitle `xml:"title"`
269 }
270
271 type Episodes []Episode // Implements sort.Interface; groups by EpNo.Type, orders by the integer portion of EpNo.EpNo
272
273 type Anime struct {
274         Error string `xml:",chardata"` // API request encountered an error if this is not ""
275
276         ID  int  `xml:"id,attr"`         // AID of the anime
277         R18 bool `xml:"restricted,attr"` // Whether the anime is considered porn
278
279         Type         string `xml:"type"`         // "TV Series", "Movie", "OVA", etc
280         EpisodeCount int    `xml:"episodecount"` // Unreliable, has a set value even when the total number is unknown
281         StartDate    string `xml:"startdate"`    // YYYY-MM-DD
282         EndDate      string `xml:"enddate"`      // YYYY-MM-DD
283
284         Titles       []AnimeTitle   `xml:"titles>title"`
285         RelatedAnime []RelatedAnime `xml:"relatedanime>anime"`
286         SimilarAnime []SimilarAnime `xml:"similaranime>anime"`
287
288         Recommendations []Recommendation `xml:"recommendations>recommendation"`
289
290         URL string `xml:"url"` // Official URL
291
292         Creators []Creator `xml:"creators>name"`
293
294         Description string `xml:"description"`
295
296         Ratings AnimeRatings `xml:"ratings"`
297
298         Picture string `xml:"picture"` // Picture basename; combine with AniDBImageBaseURL for full URL
299
300         Categories []Category `xml:"categories>category"`  // Unsorted
301         Resources  []Resource `xml:"resources>resource"`   // undocumented
302         Tags       []Tag      `xml:"tags>tag"`             // Unsorted
303         Characters Characters `xml:"characters>character"` // Unsorted
304         Episodes   Episodes   `xml:"episodes>episode"`     // Unsorted
305 }