1 // Implements parsing and searching of the anime-titles.dat database.
3 // http://wiki.anidb.info/w/API#Anime_Titles
21 var _ = json.Decoder{}
24 DataDumpURL = "http://anidb.net/api/anime-titles.dat.gz"
31 Language string // ISO-ish language string
37 PrimaryTitle string // The primary title ("x-jat main" title in the HTTP API)
39 OfficialNames map[string][]Name
40 Synonyms map[string][]Name
41 ShortNames map[string][]Name
44 // Maps titles in the given language to AIDs
45 type TitleMap struct {
46 Language string // ISO-ish language string
48 OfficialMap map[string]AID
49 SynonymMap map[string]AID
50 ShortMap map[string]AID
53 type TitlesDatabase struct {
56 Languages []string // List of all the languages present in the database
58 LanguageMap map[string]*TitleMap // Per-language map (key is ISO-ish language string)
59 PrimaryMap map[string]AID // Primary title to AID map (language is always "x-jat")
61 AnimeMap map[AID]*Anime
64 var createdRegexp = regexp.MustCompile(`^# created: (.*)$`)
66 // Loads the database from the given io.Reader.
68 // The Reader must point to a file or stream with
69 // the contents of anime-titles.dat, which can be obtained
70 // from the DataDumpURL. LoadDB will automatically try to
71 // un-gzip, so the file can be stored in gzip format.
73 // Note: LoadDB will read the entire contents of the given
75 func (db *TitlesDatabase) LoadDB(r io.Reader) {
79 all, _ := ioutil.ReadAll(r)
82 if gz, err := gzip.NewReader(bytes.NewReader(all)); err == nil {
86 rd = bytes.NewReader(all)
88 sc := bufio.NewScanner(rd)
90 if db.PrimaryMap == nil {
91 db.PrimaryMap = map[string]AID{}
93 if db.LanguageMap == nil {
94 db.LanguageMap = map[string]*TitleMap{}
96 if db.AnimeMap == nil {
97 db.AnimeMap = map[AID]*Anime{}
100 allLangs := map[string]struct{}{}
105 cr := createdRegexp.FindStringSubmatch(s)
107 if len(cr) > 1 && cr[1] != "" {
108 db.UpdateTime, _ = time.Parse(time.ANSIC, cr[1])
113 parts := strings.Split(s, "|")
118 aid, _ := strconv.ParseInt(parts[0], 10, 32)
119 typ, _ := strconv.ParseInt(parts[1], 10, 8)
121 if _, ok := db.AnimeMap[AID(aid)]; !ok {
122 db.AnimeMap[AID(aid)] = &Anime{
124 OfficialNames: map[string][]Name{},
125 Synonyms: map[string][]Name{},
126 ShortNames: map[string][]Name{},
130 lang, title := parts[2], parts[3]
131 allLangs[lang] = struct{}{}
135 db.PrimaryMap[title] = AID(aid)
137 db.AnimeMap[AID(aid)].PrimaryTitle = strings.Replace(title, "`", "'", -1)
139 lm, ok := db.LanguageMap[lang]
141 lm = db.makeLangMap(lang)
143 lm.SynonymMap[title] = AID(aid)
145 db.AnimeMap[AID(aid)].Synonyms[lang] = append(db.AnimeMap[AID(aid)].Synonyms[lang],
146 Name{Language: lang, Title: strings.Replace(title, "`", "'", -1)})
148 lm, ok := db.LanguageMap[lang]
150 lm = db.makeLangMap(lang)
152 lm.ShortMap[title] = AID(aid)
154 db.AnimeMap[AID(aid)].ShortNames[lang] = append(db.AnimeMap[AID(aid)].Synonyms[lang],
155 Name{Language: lang, Title: strings.Replace(title, "`", "'", -1)})
157 lm, ok := db.LanguageMap[lang]
159 lm = db.makeLangMap(lang)
161 lm.OfficialMap[title] = AID(aid)
163 db.AnimeMap[AID(aid)].OfficialNames[lang] = append(db.AnimeMap[AID(aid)].Synonyms[lang],
164 Name{Language: lang, Title: strings.Replace(title, "`", "'", -1)})
167 langs := make([]string, 0, len(allLangs))
168 for k, _ := range allLangs {
169 langs = append(langs, k)
175 func (db *TitlesDatabase) makeLangMap(lang string) *TitleMap {
178 OfficialMap: map[string]AID{},
179 SynonymMap: map[string]AID{},
180 ShortMap: map[string]AID{},
182 db.LanguageMap[lang] = tm