18 var _ = json.Decoder{}
21 DataDumpURL = "http://anidb.net/api/anime-titles.dat.gz"
28 Language string // ISO-ish language string
34 PrimaryTitle string // The primary title ("x-jat main" title in the HTTP API)
36 OfficialNames map[string][]Name
37 Synonyms map[string][]Name
38 ShortNames map[string][]Name
41 // Maps titles in the given language to AIDs
42 type TitleMap struct {
43 Language string // ISO-ish language string
45 OfficialMap map[string]AID
46 SynonymMap map[string]AID
47 ShortMap map[string]AID
50 type TitlesDatabase struct {
53 Languages []string // List of all the languages present in the database
55 LanguageMap map[string]*TitleMap // Per-language map (key is ISO-ish language string)
56 PrimaryMap map[string]AID // Primary title to AID map (language is always "x-jat")
58 AnimeMap map[AID]*Anime
61 var createdRegexp = regexp.MustCompile(`^# created: (.*)$`)
63 // Loads the database from the given io.ReadCloser.
65 // The ReadCloser must point to a file or stream with
66 // the contents of anime-titles.dat, which can be obtained
67 // from the DataDumpURL. LoadDB will automatically try to
68 // un-gzip, so the file can be stored in gzip format.
70 // Note: LoadDB will read the entire contents of the given
71 // io.ReadCloser. LoadDB will call Close() on the
72 // io.ReadCloser after reading it.
73 func (db *TitlesDatabase) LoadDB(r io.ReadCloser) {
77 all, _ := ioutil.ReadAll(r)
81 if gz, err := gzip.NewReader(bytes.NewReader(all)); err == nil {
85 rd = bytes.NewReader(all)
87 sc := bufio.NewScanner(rd)
89 if db.PrimaryMap == nil {
90 db.PrimaryMap = map[string]AID{}
92 if db.LanguageMap == nil {
93 db.LanguageMap = map[string]*TitleMap{}
95 if db.AnimeMap == nil {
96 db.AnimeMap = map[AID]*Anime{}
99 allLangs := map[string]struct{}{}
104 cr := createdRegexp.FindStringSubmatch(s)
106 if len(cr) > 1 && cr[1] != "" {
107 db.UpdateTime, _ = time.Parse(time.ANSIC, cr[1])
112 parts := strings.Split(s, "|")
117 aid, _ := strconv.ParseInt(parts[0], 10, 32)
118 typ, _ := strconv.ParseInt(parts[1], 10, 8)
120 if _, ok := db.AnimeMap[AID(aid)]; !ok {
121 db.AnimeMap[AID(aid)] = &Anime{
123 OfficialNames: map[string][]Name{},
124 Synonyms: map[string][]Name{},
125 ShortNames: map[string][]Name{},
129 lang, title := parts[2], parts[3]
130 allLangs[lang] = struct{}{}
134 db.PrimaryMap[title] = AID(aid)
136 db.AnimeMap[AID(aid)].PrimaryTitle = strings.Replace(title, "`", "'", -1)
138 lm, ok := db.LanguageMap[lang]
140 lm = db.makeLangMap(lang)
142 lm.SynonymMap[title] = AID(aid)
144 db.AnimeMap[AID(aid)].Synonyms[lang] = append(db.AnimeMap[AID(aid)].Synonyms[lang],
145 Name{Language: lang, Title: strings.Replace(title, "`", "'", -1)})
147 lm, ok := db.LanguageMap[lang]
149 lm = db.makeLangMap(lang)
151 lm.ShortMap[title] = AID(aid)
153 db.AnimeMap[AID(aid)].ShortNames[lang] = append(db.AnimeMap[AID(aid)].Synonyms[lang],
154 Name{Language: lang, Title: strings.Replace(title, "`", "'", -1)})
156 lm, ok := db.LanguageMap[lang]
158 lm = db.makeLangMap(lang)
160 lm.OfficialMap[title] = AID(aid)
162 db.AnimeMap[AID(aid)].OfficialNames[lang] = append(db.AnimeMap[AID(aid)].Synonyms[lang],
163 Name{Language: lang, Title: strings.Replace(title, "`", "'", -1)})
166 langs := make([]string, 0, len(allLangs))
167 for k, _ := range allLangs {
168 langs = append(langs, k)
174 func (db *TitlesDatabase) makeLangMap(lang string) *TitleMap {
177 OfficialMap: map[string]AID{},
178 SynonymMap: map[string]AID{},
179 ShortMap: map[string]AID{},
181 db.LanguageMap[lang] = tm