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