diff options
-rw-r--r-- | modules/series/series.go | 11 | ||||
-rw-r--r-- | modules/tags/tag.go | 8 | ||||
-rw-r--r-- | tools/animeimporter/ACDv2Reader.go | 59 | ||||
-rw-r--r-- | tools/animeimporter/main.go | 202 |
4 files changed, 280 insertions, 0 deletions
diff --git a/modules/series/series.go b/modules/series/series.go new file mode 100644 index 0000000..1f50903 --- /dev/null +++ b/modules/series/series.go | |||
@@ -0,0 +1,11 @@ | |||
1 | package series | ||
2 | |||
3 | type Series struct { | ||
4 | ID int | ||
5 | Name string | ||
6 | KanjiName string | ||
7 | RomajiName string | ||
8 | FuriganaName string | ||
9 | OtherNames []string | ||
10 | Tags []string | ||
11 | } | ||
diff --git a/modules/tags/tag.go b/modules/tags/tag.go new file mode 100644 index 0000000..026df0f --- /dev/null +++ b/modules/tags/tag.go | |||
@@ -0,0 +1,8 @@ | |||
1 | package tags | ||
2 | |||
3 | type TagType int | ||
4 | |||
5 | const ( | ||
6 | TagTypeGril TagType = iota | ||
7 | TagTypeSeries | ||
8 | ) | ||
diff --git a/tools/animeimporter/ACDv2Reader.go b/tools/animeimporter/ACDv2Reader.go new file mode 100644 index 0000000..36564e4 --- /dev/null +++ b/tools/animeimporter/ACDv2Reader.go | |||
@@ -0,0 +1,59 @@ | |||
1 | package main | ||
2 | |||
3 | import ( | ||
4 | "encoding/json" | ||
5 | "io/ioutil" | ||
6 | "path/filepath" | ||
7 | "strconv" | ||
8 | "strings" | ||
9 | |||
10 | "fagott.pw/grilist/modules/grils" | ||
11 | ) | ||
12 | |||
13 | func ifErrExit(err error) { | ||
14 | if err != nil { | ||
15 | panic(err) | ||
16 | } | ||
17 | } | ||
18 | |||
19 | func toInt(s string) int { | ||
20 | i, err := strconv.ParseInt(s, 10, 32) | ||
21 | ifErrExit(err) | ||
22 | return int(i) | ||
23 | } | ||
24 | |||
25 | type ACDv2Reader struct{} | ||
26 | |||
27 | type ACDv2Series struct { | ||
28 | Name struct { | ||
29 | English string `json:"english"` | ||
30 | Romaji string `json:"romaji"` | ||
31 | Japanese string `json:"japanese"` | ||
32 | Furigana string `json:"furigana"` | ||
33 | Aliases []string `json:"aliases"` | ||
34 | } `json:"name"` | ||
35 | Tags []string `json:"tags"` | ||
36 | Image string `json:"image"` | ||
37 | } | ||
38 | |||
39 | func (r *ACDv2Reader) Read(path string) WrappedSeries { | ||
40 | s := WrappedSeries{} | ||
41 | data, err := ioutil.ReadFile(path) | ||
42 | ifErrExit(err) | ||
43 | var jObj ACDv2Series | ||
44 | err = json.Unmarshal(data, &jObj) | ||
45 | ifErrExit(err) | ||
46 | s.Image = jObj.Image | ||
47 | s.Series.ID = toInt(strings.TrimSuffix(filepath.Base(path), ".json")) | ||
48 | s.Series.Name = jObj.Name.English | ||
49 | s.Series.KanjiName = jObj.Name.Japanese | ||
50 | s.Series.RomajiName = jObj.Name.Romaji | ||
51 | s.Series.FuriganaName = jObj.Name.Furigana | ||
52 | s.Series.OtherNames = jObj.Name.Aliases | ||
53 | s.Series.Tags = jObj.Tags | ||
54 | return s | ||
55 | } | ||
56 | |||
57 | func (r *ACDv2Reader) ID() int { | ||
58 | return int(grils.DataSourceACD) | ||
59 | } | ||
diff --git a/tools/animeimporter/main.go b/tools/animeimporter/main.go new file mode 100644 index 0000000..7d95b8e --- /dev/null +++ b/tools/animeimporter/main.go | |||
@@ -0,0 +1,202 @@ | |||
1 | package main | ||
2 | |||
3 | import ( | ||
4 | "database/sql" | ||
5 | "flag" | ||
6 | "fmt" | ||
7 | "os" | ||
8 | "path/filepath" | ||
9 | |||
10 | "fagott.pw/grilist/grilist" | ||
11 | "fagott.pw/grilist/modules/series" | ||
12 | "fagott.pw/grilist/modules/tags" | ||
13 | ) | ||
14 | |||
15 | var path string | ||
16 | var sourceType string | ||
17 | var imgUrlExportFile string | ||
18 | var exportFile *os.File | ||
19 | var db *sql.DB | ||
20 | var r SeriesReader | ||
21 | |||
22 | type WrappedSeries struct { | ||
23 | Series series.Series | ||
24 | Image string | ||
25 | } | ||
26 | |||
27 | type SeriesReader interface { | ||
28 | Read(path string) WrappedSeries | ||
29 | ID() int | ||
30 | } | ||
31 | |||
32 | func GetSeriesReader(rType string) SeriesReader { | ||
33 | switch rType { | ||
34 | case "ACDv2": | ||
35 | return &ACDv2Reader{} | ||
36 | } | ||
37 | return nil | ||
38 | } | ||
39 | |||
40 | func LogErr(err error) { | ||
41 | if err != nil { | ||
42 | fmt.Fprintf(os.Stderr, "%s\n", err) | ||
43 | } | ||
44 | } | ||
45 | |||
46 | func TryGetExistingSeries(s WrappedSeries, idmap map[int]int) int { | ||
47 | dbID, _ := idmap[s.Series.ID] | ||
48 | return dbID | ||
49 | } | ||
50 | |||
51 | func InsertSeries(s WrappedSeries) int { | ||
52 | var dbID int | ||
53 | row := db.QueryRow("INSERT INTO grilist.appearances (id) VALUES (DEFAULT) RETURNING id;") | ||
54 | row.Scan(&dbID) | ||
55 | fmt.Printf( | ||
56 | "Assigned %d from %s to %d\n", | ||
57 | s.Series.ID, | ||
58 | sourceType, | ||
59 | dbID) | ||
60 | _, err := db.Exec("INSERT INTO grilist.appearances_id_mappings (appearance_id, source, source_id) VALUES ($1, $2, $3);", | ||
61 | dbID, r.ID(), s.Series.ID) | ||
62 | LogErr(err) | ||
63 | if s.Series.Name != "" { | ||
64 | _, err := db.Exec("INSERT INTO grilist.appearance_names (appearance_id, name, name_type) VALUES ($1, $2, $3);", | ||
65 | dbID, s.Series.Name, 4) | ||
66 | LogErr(err) | ||
67 | } | ||
68 | if s.Series.KanjiName != "" { | ||
69 | _, err := db.Exec("INSERT INTO grilist.appearance_names (appearance_id, name, name_type) VALUES ($1, $2, $3);", | ||
70 | dbID, s.Series.Name, 0) | ||
71 | LogErr(err) | ||
72 | } | ||
73 | if s.Series.RomajiName != "" { | ||
74 | _, err := db.Exec("INSERT INTO grilist.appearance_names (appearance_id, name, name_type) VALUES ($1, $2, $3);", | ||
75 | dbID, s.Series.Name, 1) | ||
76 | LogErr(err) | ||
77 | } | ||
78 | if s.Series.FuriganaName != "" { | ||
79 | _, err := db.Exec("INSERT INTO grilist.appearance_names (appearance_id, name, name_type) VALUES ($1, $2, $3);", | ||
80 | dbID, s.Series.Name, 3) | ||
81 | LogErr(err) | ||
82 | } | ||
83 | for _, v := range s.Series.OtherNames { | ||
84 | _, err := db.Exec("INSERT INTO grilist.appearance_names (appearance_id, name, name_type) VALUES ($1, $2, $3);", | ||
85 | dbID, v, 2) | ||
86 | LogErr(err) | ||
87 | } | ||
88 | if exportFile != nil { | ||
89 | if s.Image != "" { | ||
90 | fmt.Fprintf( | ||
91 | exportFile, | ||
92 | "%d %s\n", | ||
93 | dbID, | ||
94 | s.Image) | ||
95 | } | ||
96 | } | ||
97 | return dbID | ||
98 | } | ||
99 | |||
100 | func InsertTags(dbID int, s WrappedSeries, taglist map[string]int) { | ||
101 | for _, v := range s.Series.Tags { | ||
102 | if _, ok := taglist[v]; ok { | ||
103 | continue | ||
104 | } | ||
105 | var id int | ||
106 | row := db.QueryRow("INSERT INTO grilist.tags (name, type) VALUES ($1, $2) RETURNING id;", | ||
107 | v, tags.TagTypeSeries) | ||
108 | row.Scan(&id) | ||
109 | taglist[v] = id | ||
110 | fmt.Printf("Inserted tag %s as %d\n", v, id) | ||
111 | } | ||
112 | rows, err := db.Query("SELECT tag_id FROM appearances_tags WHERE appearance_id = $1;", dbID) | ||
113 | LogErr(err) | ||
114 | existingTags := make(map[int]bool, 0) | ||
115 | for rows.Next() { | ||
116 | var tagID int | ||
117 | rows.Scan(&tagID) | ||
118 | existingTags[tagID] = true | ||
119 | } | ||
120 | for _, v := range s.Series.Tags { | ||
121 | tagID := taglist[v] | ||
122 | if _, ok := existingTags[tagID]; ok { | ||
123 | continue | ||
124 | } | ||
125 | _, err := db.Exec("INSERT INTO grilist.appearances_tags (appearance_id, tag_id) VALUES ($1, $2);", | ||
126 | dbID, tagID) | ||
127 | existingTags[tagID] = true | ||
128 | fmt.Printf("Assigned tag %s to %s\n", v, s.Series.Name) | ||
129 | LogErr(err) | ||
130 | } | ||
131 | } | ||
132 | |||
133 | func main() { | ||
134 | flag.StringVar(&path, "path", "", "path of the source files") | ||
135 | flag.StringVar(&sourceType, "type", "", "type of the files (ACDv2)") | ||
136 | flag.StringVar(&imgUrlExportFile, "imageexportfile", "", "image URLs will be exported to this file, THIS WILL OVERWRITE EVERYTHING IN THE FILE!") | ||
137 | flag.Parse() | ||
138 | if imgUrlExportFile != "" { | ||
139 | var err error | ||
140 | if exportFile, err = os.Create(imgUrlExportFile); err != nil { | ||
141 | fmt.Fprintf(os.Stderr, "%s\n", "error while accessing \"%s\"") | ||
142 | fmt.Fprintf(os.Stderr, "%v\n", err) | ||
143 | os.Exit(1) | ||
144 | } | ||
145 | } | ||
146 | if path == "" { | ||
147 | fmt.Fprintf(os.Stderr, "%s\n", "\"path\" must be specified!") | ||
148 | flag.Usage() | ||
149 | os.Exit(1) | ||
150 | } | ||
151 | r = GetSeriesReader(sourceType) | ||
152 | if r == nil { | ||
153 | fmt.Fprintf(os.Stderr, "%s\n", "\"type\" must be specified and a valid series source!") | ||
154 | flag.Usage() | ||
155 | os.Exit(1) | ||
156 | } | ||
157 | config := grilist.LoadConfig() | ||
158 | var err error | ||
159 | db, err = sql.Open("postgres", config.DBConnectionString()) | ||
160 | if err == nil { | ||
161 | err = db.Ping() | ||
162 | } | ||
163 | if err != nil { | ||
164 | fmt.Fprintf(os.Stderr, "%s\n", "error with database connection") | ||
165 | fmt.Fprintf(os.Stderr, "%v\n", err) | ||
166 | os.Exit(1) | ||
167 | } | ||
168 | |||
169 | taglist := make(map[string]int) | ||
170 | rows, err := db.Query("SELECT id, name FROM grilist.tags WHERE type = $1;", tags.TagTypeSeries) | ||
171 | LogErr(err) | ||
172 | for rows.Next() { | ||
173 | var id int | ||
174 | var name string | ||
175 | rows.Scan(&id, &name) | ||
176 | taglist[name] = id | ||
177 | } | ||
178 | idmap := make(map[int]int) | ||
179 | rows, err = db.Query("SELECT source_id, appearance_id FROM grilist.appearances_id_mappings WHERE source = $1;", r.ID()) | ||
180 | LogErr(err) | ||
181 | for rows.Next() { | ||
182 | var sourceID int | ||
183 | var appearanceID int | ||
184 | rows.Scan(&sourceID, &appearanceID) | ||
185 | idmap[sourceID] = appearanceID | ||
186 | } | ||
187 | |||
188 | filepath.Walk(path, func(path string, info os.FileInfo, err error) error { | ||
189 | if info.IsDir() { | ||
190 | return nil | ||
191 | } | ||
192 | s := r.Read(path) | ||
193 | |||
194 | var dbID int | ||
195 | if dbID = TryGetExistingSeries(s, idmap); dbID == 0 { | ||
196 | dbID = InsertSeries(s) | ||
197 | } | ||
198 | InsertTags(dbID, s, taglist) | ||
199 | |||
200 | return nil | ||
201 | }) | ||
202 | } | ||