| @@ -14,6 +14,7 @@ import ( |
| 14 | "time" |
14 | "time" |
| 15 | |
15 | |
| 16 | "git.macaw.me/skunky/devianter" |
16 | "git.macaw.me/skunky/devianter" |
| |
17 | "golang.org/x/net/html" |
| 17 | ) |
18 | ) |
| 18 | |
19 | |
| 19 | // парсинг темплейтов |
20 | // парсинг темплейтов |
| @@ -95,18 +96,59 @@ func ParseDescription(dscr devianter.Text) string { |
| 95 | description[dl-1] == '}' { |
96 | description[dl-1] == '}' { |
| 96 | var descr struct { |
97 | var descr struct { |
| 97 | Blocks []struct { |
98 | Blocks []struct { |
| 98 | Key, Text, Type string |
99 | Text, Type string |
| 99 | InlineStyleRanges []struct { |
100 | InlineStyleRanges []struct { |
| 100 | Offset, Length int |
101 | Offset, Length int |
| 101 | Style string |
102 | Style string |
| 102 | } |
103 | } |
| |
104 | EntityRanges []struct { |
| |
105 | Offset, Length int |
| |
106 | Key int |
| |
107 | } |
| |
108 | Data struct { |
| |
109 | TextAlignment string |
| |
110 | } |
| |
111 | } |
| |
112 | EntityMap map[string]struct { |
| |
113 | Type string |
| |
114 | Data struct { |
| |
115 | Config struct { |
| |
116 | Aligment string |
| |
117 | Width int |
| |
118 | } |
| |
119 | Data devianter.Deviation |
| |
120 | } |
| 103 | } |
121 | } |
| 104 | } |
122 | } |
| 105 | e := json.Unmarshal([]byte(description), &descr) |
123 | e := json.Unmarshal([]byte(description), &descr) |
| 106 | err(e) |
124 | err(e) |
| 107 | |
125 | |
| |
126 | entities := make(map[int]devianter.Deviation) |
| |
127 | for n, x := range descr.EntityMap { |
| |
128 | num, _ := strconv.Atoi(n) |
| |
129 | entities[num] = x.Data.Data |
| |
130 | } |
| |
131 | |
| 108 | for _, x := range descr.Blocks { |
132 | for _, x := range descr.Blocks { |
| 109 | ranges := make(map[int]text) |
133 | ranges := make(map[int]text) |
| |
134 | |
| |
135 | if len(x.InlineStyleRanges) == 0 { |
| |
136 | switch x.Type { |
| |
137 | case "atomic": |
| |
138 | d := entities[x.EntityRanges[0].Key] |
| |
139 | parseddescription.WriteString(`<img width="50%" src="`) |
| |
140 | parseddescription.WriteString(ParseMedia(d.Media)) |
| |
141 | parseddescription.WriteString(`" title="`) |
| |
142 | parseddescription.WriteString(d.Author.Username) |
| |
143 | parseddescription.WriteString(" - ") |
| |
144 | parseddescription.WriteString(d.Title) |
| |
145 | parseddescription.WriteString(`">`) |
| |
146 | case "unstyled": |
| |
147 | parseddescription.WriteString(x.Text) |
| |
148 | } |
| |
149 | parseddescription.WriteString("<br>") |
| |
150 | } |
| |
151 | |
| 110 | for i, rngs := range x.InlineStyleRanges { |
152 | for i, rngs := range x.InlineStyleRanges { |
| 111 | var tag string |
153 | var tag string |
| 112 | |
154 | |
| @@ -140,7 +182,66 @@ func ParseDescription(dscr devianter.Text) string { |
| 140 | } |
182 | } |
| 141 | } |
183 | } |
| 142 | } else if dl != 0 { |
184 | } else if dl != 0 { |
| 143 | parseddescription.WriteString(description) |
185 | tagval := func(t *html.Tokenizer) string { |
| |
186 | for { |
| |
187 | tt := t.Next() |
| |
188 | switch tt { |
| |
189 | case html.ErrorToken: |
| |
190 | return "" |
| |
191 | case html.TextToken: |
| |
192 | return string(t.Text()) |
| |
193 | } |
| |
194 | } |
| |
195 | } |
| |
196 | |
| |
197 | tt := html.NewTokenizer(strings.NewReader(dscr.Html.Markup)) |
| |
198 | for { |
| |
199 | t := tt.Next() |
| |
200 | switch t { |
| |
201 | case html.ErrorToken: |
| |
202 | return parseddescription.String() |
| |
203 | case html.StartTagToken, html.EndTagToken, html.SelfClosingTagToken: |
| |
204 | token := tt.Token() |
| |
205 | switch token.Data { |
| |
206 | case "a": |
| |
207 | for _, a := range token.Attr { |
| |
208 | if a.Key == "href" { |
| |
209 | url := strings.ReplaceAll(a.Val, "https://www.deviantart.com/users/outgoing?", "") |
| |
210 | if strings.Contains(url, "deviantart") { |
| |
211 | url = strings.ReplaceAll(url, "https://www.deviantart.com/", "") |
| |
212 | url = strings.ReplaceAll(url, url[0:strings.Index(url, "/")+1], "") |
| |
213 | } |
| |
214 | parseddescription.WriteString("<a target=\"_blank\" href=\"" + url + "\">" + tagval(tt) + "</a> ") |
| |
215 | } |
| |
216 | } |
| |
217 | case "img": |
| |
218 | var ( |
| |
219 | uri, title string |
| |
220 | ) |
| |
221 | for b, a := range token.Attr { |
| |
222 | switch a.Key { |
| |
223 | case "src": |
| |
224 | if len(a.Val) > 9 && a.Val[8:9] == "e" { |
| |
225 | uri = UrlBuilder("media", "emojitar", a.Val[37:len(a.Val)-4], "?type=e") |
| |
226 | } |
| |
227 | case "title": |
| |
228 | title = a.Val |
| |
229 | } |
| |
230 | if title != "" { |
| |
231 | for x := -1; x < b; x++ { |
| |
232 | parseddescription.WriteString("<img src=\"" + uri + "\" title=\"" + title + "\">") |
| |
233 | } |
| |
234 | } |
| |
235 | } |
| |
236 | case "br", "li", "ul", "p", "b": |
| |
237 | parseddescription.WriteString(token.String()) |
| |
238 | case "div": |
| |
239 | parseddescription.WriteString("<p> ") |
| |
240 | } |
| |
241 | case html.TextToken: |
| |
242 | parseddescription.Write(tt.Text()) |
| |
243 | } |
| |
244 | } |
| 144 | } |
245 | } |
| 145 | |
246 | |
| 146 | return parseddescription.String() |
247 | return parseddescription.String() |
| @@ -234,7 +335,7 @@ func (s skunkyart) DeviationList(devs []devianter.Deviation, content ...dlist) s |
| 234 | } |
335 | } |
| 235 | for _, data := range devs { |
336 | for _, data := range devs { |
| 236 | if !(data.NSFW && !CFG.Nsfw) { |
337 | if !(data.NSFW && !CFG.Nsfw) { |
| 237 | url := s.ParseMedia(data.Media) |
338 | url := ParseMedia(data.Media) |
| 238 | if s.Atom { |
339 | if s.Atom { |
| 239 | id := strconv.Itoa(data.ID) |
340 | id := strconv.Itoa(data.ID) |
| 240 | list.WriteString(`<entry><author><name>`) |
341 | list.WriteString(`<entry><author><name>`) |
| @@ -307,7 +408,6 @@ func (s skunkyart) DeviationList(devs []devianter.Deviation, content ...dlist) s |
| 307 | return list.String() |
408 | return list.String() |
| 308 | } |
409 | } |
| 309 | |
410 | |
| 310 | // FIXME: первый комментарий не отображается. |
| |
| 311 | func (s skunkyart) ParseComments(c devianter.Comments) string { |
411 | func (s skunkyart) ParseComments(c devianter.Comments) string { |
| 312 | var cmmts strings.Builder |
412 | var cmmts strings.Builder |
| 313 | replied := make(map[int]string) |
413 | replied := make(map[int]string) |
| @@ -354,7 +454,7 @@ func (s skunkyart) ParseComments(c devianter.Comments) string { |
| 354 | cmmts.WriteString(x.Posted.UTC().String()) |
454 | cmmts.WriteString(x.Posted.UTC().String()) |
| 355 | cmmts.WriteString("]<p>") |
455 | cmmts.WriteString("]<p>") |
| 356 | |
456 | |
| 357 | cmmts.WriteString(x.Comment) |
457 | cmmts.WriteString(ParseDescription(x.TextContent)) |
| 358 | cmmts.WriteString("<p>👍: ") |
458 | cmmts.WriteString("<p>👍: ") |
| 359 | cmmts.WriteString(strconv.Itoa(x.Likes)) |
459 | cmmts.WriteString(strconv.Itoa(x.Likes)) |
| 360 | cmmts.WriteString(" ⏩: ") |
460 | cmmts.WriteString(" ⏩: ") |
| @@ -369,7 +469,7 @@ func (s skunkyart) ParseComments(c devianter.Comments) string { |
| 369 | return cmmts.String() |
469 | return cmmts.String() |
| 370 | } |
470 | } |
| 371 | |
471 | |
| 372 | func (s skunkyart) ParseMedia(media devianter.Media) string { |
472 | func ParseMedia(media devianter.Media) string { |
| 373 | url := devianter.UrlFromMedia(media) |
473 | url := devianter.UrlFromMedia(media) |
| 374 | if len(url) != 0 { |
474 | if len(url) != 0 { |
| 375 | url = url[21:] |
475 | url = url[21:] |