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