Commit 0f528ee9fa
Unsigned
Layout: unified · split
app/util.go +25 −15
| @@ -66,12 +66,35 @@ func (s skunkyart) ConvertDeviantArtUrlToSkunkyArt(url string) (output string) { | |||
| 66 | return | 66 | return |
| 67 | } | 67 | } |
| 68 | 68 | ||
| 69 | func BuildUserPlate(name string) string { | ||
| 70 | var htm strings.Builder | ||
| 71 | htm.WriteString(`<div class="user-plate"><img src="`) | ||
| 72 | htm.WriteString(UrlBuilder("media", "emojitar", name, "?type=a")) | ||
| 73 | htm.WriteString(`"><a href="`) | ||
| 74 | htm.WriteString(UrlBuilder("group_user", "?type=about&q=", name)) | ||
| 75 | htm.WriteString(`">`) | ||
| 76 | htm.WriteString(name) | ||
| 77 | htm.WriteString(`</a></div>`) | ||
| 78 | return htm.String() | ||
| 79 | } | ||
| 80 | |||
| 69 | type text struct { | 81 | type text struct { |
| 70 | TXT string | 82 | TXT string |
| 71 | from int | 83 | from int |
| 72 | to int | 84 | to int |
| 73 | } | 85 | } |
| 74 | 86 | ||
| 87 | func tagval(t *html.Tokenizer) string { | ||
| 88 | for tt := t.Next(); ; { | ||
| 89 | switch tt { | ||
| 90 | default: | ||
| 91 | return "" | ||
| 92 | case html.TextToken: | ||
| 93 | return string(t.Text()) | ||
| 94 | } | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 75 | func ParseDescription(dscr devianter.Text) string { | 98 | func ParseDescription(dscr devianter.Text) string { |
| 76 | var parseddescription strings.Builder | 99 | var parseddescription strings.Builder |
| 77 | TagBuilder := func(tag string, content string) string { | 100 | TagBuilder := func(tag string, content string) string { |
| @@ -182,20 +205,7 @@ func ParseDescription(dscr devianter.Text) string { | |||
| 182 | } | 205 | } |
| 183 | } | 206 | } |
| 184 | } else if dl != 0 { | 207 | } else if dl != 0 { |
| 185 | tagval := func(t *html.Tokenizer) string { | 208 | for tt := html.NewTokenizer(strings.NewReader(dscr.Html.Markup)); ; { |
| 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() | 209 | t := tt.Next() |
| 200 | switch t { | 210 | switch t { |
| 201 | case html.ErrorToken: | 211 | case html.ErrorToken: |
| @@ -314,7 +324,7 @@ func (s skunkyart) NavBase(c dlist) string { | |||
| 314 | } | 324 | } |
| 315 | 325 | ||
| 316 | // вперёд-назад | 326 | // вперёд-назад |
| 317 | if c.More { | 327 | if p != 417 || c.More { |
| 318 | prevrev("| Next =>", p+1, false) | 328 | prevrev("| Next =>", p+1, false) |
| 319 | } | 329 | } |
| 320 | 330 | ||
app/wrapper.go +53 −11
| @@ -10,6 +10,7 @@ import ( | |||
| 10 | "time" | 10 | "time" |
| 11 | 11 | ||
| 12 | "git.macaw.me/skunky/devianter" | 12 | "git.macaw.me/skunky/devianter" |
| 13 | "golang.org/x/net/html" | ||
| 13 | ) | 14 | ) |
| 14 | 15 | ||
| 15 | var wr = io.WriteString | 16 | var wr = io.WriteString |
| @@ -131,13 +132,7 @@ func (s skunkyart) GRUser() { | |||
| 131 | case "group_admins": | 132 | case "group_admins": |
| 132 | var htm strings.Builder | 133 | var htm strings.Builder |
| 133 | for _, z := range x.ModuleData.GroupAdmins.Results { | 134 | for _, z := range x.ModuleData.GroupAdmins.Results { |
| 134 | htm.WriteString(`<div class="admin"><img src="`) | 135 | htm.WriteString(BuildUserPlate(z.User.Username)) |
| 135 | htm.WriteString(UrlBuilder("media", "emojitar", z.User.Username, "?type=a")) | ||
| 136 | htm.WriteString(`"><a href="`) | ||
| 137 | htm.WriteString(UrlBuilder("group_user", "?type=about&q=", z.User.Username)) | ||
| 138 | htm.WriteString(`">`) | ||
| 139 | htm.WriteString(z.User.Username) | ||
| 140 | htm.WriteString(`</a></div>`) | ||
| 141 | } | 136 | } |
| 142 | group.Admins += htm.String() | 137 | group.Admins += htm.String() |
| 143 | } | 138 | } |
| @@ -270,15 +265,62 @@ func (s skunkyart) Search() { | |||
| 270 | ss.Content, e = devianter.SearchFunc(s.Query, s.Page, s.Type) | 265 | ss.Content, e = devianter.SearchFunc(s.Query, s.Page, s.Type) |
| 271 | case 'g': | 266 | case 'g': |
| 272 | ss.Content, e = devianter.SearchFunc(s.Query, s.Page, s.Type, s.Args.Get("usr")) | 267 | ss.Content, e = devianter.SearchFunc(s.Query, s.Page, s.Type, s.Args.Get("usr")) |
| 268 | case 'r': // скраппер, поскольку девиантартовцы зажопили гостевое API для поиска групп | ||
| 269 | var ( | ||
| 270 | usernames = make(map[int]string) | ||
| 271 | url strings.Builder | ||
| 272 | num int | ||
| 273 | ) | ||
| 274 | |||
| 275 | s.Page++ | ||
| 276 | |||
| 277 | url.WriteString("https://www.deviantart.com/groups/?q=") | ||
| 278 | url.WriteString(s.Query) | ||
| 279 | if s.Page > 1 { | ||
| 280 | url.WriteString("&offset=") | ||
| 281 | url.WriteString(strconv.Itoa(10 * s.Page)) | ||
| 282 | } | ||
| 283 | |||
| 284 | r, err := http.Get(url.String()) | ||
| 285 | if err != nil { | ||
| 286 | s.ReturnHTTPError(502) | ||
| 287 | } | ||
| 288 | defer r.Body.Close() | ||
| 289 | |||
| 290 | for z := html.NewTokenizer(r.Body); ; { | ||
| 291 | if n, token := z.Next(), z.Token(); n == html.StartTagToken && token.Data == "a" { | ||
| 292 | for _, x := range token.Attr { | ||
| 293 | if x.Key == "class" && x.Val == "u regular username" { | ||
| 294 | usernames[num] = tagval(z) | ||
| 295 | num++ | ||
| 296 | } | ||
| 297 | } | ||
| 298 | } else if n == 0 { | ||
| 299 | break | ||
| 300 | } else { | ||
| 301 | continue | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | if l := len(usernames); l != 0 { | ||
| 306 | ss.List += `<div class="content plates">` | ||
| 307 | for x := 0; x < len(usernames); x++ { | ||
| 308 | ss.List += BuildUserPlate(usernames[x]) | ||
| 309 | } | ||
| 310 | ss.List += `</div>` | ||
| 311 | ss.List += s.NavBase(dlist{}) | ||
| 312 | } | ||
| 273 | default: | 313 | default: |
| 274 | s.ReturnHTTPError(400) | 314 | s.ReturnHTTPError(400) |
| 275 | } | 315 | } |
| 276 | err(e) | 316 | err(e) |
| 277 | 317 | ||
| 278 | ss.List = s.DeviationList(ss.Content.Results, dlist{ | 318 | if s.Type != 'r' { |
| 279 | Pages: ss.Content.Pages, | 319 | ss.List = s.DeviationList(ss.Content.Results, dlist{ |
| 280 | More: ss.Content.HasMore, | 320 | Pages: ss.Content.Pages, |
| 281 | }) | 321 | More: ss.Content.HasMore, |
| 322 | }) | ||
| 323 | } | ||
| 282 | 324 | ||
| 283 | s.ExecuteTemplate("html/search.htm", &s) | 325 | s.ExecuteTemplate("html/search.htm", &s) |
| 284 | } | 326 | } |
css/skunky.css +9 −5
| @@ -82,7 +82,7 @@ form input, button, select { | |||
| 82 | border: 3px solid #4d27d6; | 82 | border: 3px solid #4d27d6; |
| 83 | transition: 400ms; | 83 | transition: 400ms; |
| 84 | } | 84 | } |
| 85 | .block img, .admins .admin img{ | 85 | .block img, .plates .user-plate img{ |
| 86 | width: 100%; | 86 | width: 100%; |
| 87 | } | 87 | } |
| 88 | .block p { | 88 | .block p { |
| @@ -103,16 +103,20 @@ form input, button, select { | |||
| 103 | background-color: #060820; | 103 | background-color: #060820; |
| 104 | border: 3px solid #060820; | 104 | border: 3px solid #060820; |
| 105 | } | 105 | } |
| 106 | .admins { | 106 | .plates { |
| 107 | display: flex | 107 | display: flex |
| 108 | } | 108 | } |
| 109 | .admins .admin { | 109 | .admins .user-plate { |
| 110 | margin-left: 1%; | 110 | width: 5%; |
| 111 | background-color: #011522; | 111 | background-color: #011522; |
| 112 | } | ||
| 113 | .plates .user-plate { | ||
| 114 | margin-left: 1%; | ||
| 115 | background-color: #091f19; | ||
| 112 | padding: 3px; | 116 | padding: 3px; |
| 113 | word-break: break-all; | 117 | word-break: break-all; |
| 114 | text-align: center; | 118 | text-align: center; |
| 115 | width: 5%; | 119 | max-width: 15%; |
| 116 | } | 120 | } |
| 117 | 121 | ||
| 118 | @media screen and (orientation: portrait) { | 122 | @media screen and (orientation: portrait) { |
html/about.htm +1
| @@ -12,6 +12,7 @@ | |||
| 12 | <select name="type"> | 12 | <select name="type"> |
| 13 | <option value="all">All</option> | 13 | <option value="all">All</option> |
| 14 | <option value="tag">Tag</option> | 14 | <option value="tag">Tag</option> |
| 15 | <option value="r">Groups</option> | ||
| 15 | </select> | 16 | </select> |
| 16 | <button type="submit">Search!</button> | 17 | <button type="submit">Search!</button> |
| 17 | </form> | 18 | </form> |
html/deviantion.htm +1
| @@ -12,6 +12,7 @@ | |||
| 12 | <select name="type"> | 12 | <select name="type"> |
| 13 | <option value="all">All</option> | 13 | <option value="all">All</option> |
| 14 | <option value="tag">Tag</option> | 14 | <option value="tag">Tag</option> |
| 15 | <option value="r">Groups</option> | ||
| 15 | </select> | 16 | </select> |
| 16 | <button type="submit">Search!</button> | 17 | <button type="submit">Search!</button> |
| 17 | </form> | 18 | </form> |
html/gruser.htm +2 −1
| @@ -22,6 +22,7 @@ | |||
| 22 | <option value="gallery">Gallery</option> | 22 | <option value="gallery">Gallery</option> |
| 23 | <option value="all">All</option> | 23 | <option value="all">All</option> |
| 24 | <option value="tag">Tag</option> | 24 | <option value="tag">Tag</option> |
| 25 | <option value="r">Groups</option> | ||
| 25 | </select> | 26 | </select> |
| 26 | <button type="submit">Search!</button> | 27 | <button type="submit">Search!</button> |
| 27 | </form> <h1>| {{.Templates.GroupUser.GR.Owner.Username}}</h1> | 28 | </form> <h1>| {{.Templates.GroupUser.GR.Owner.Username}}</h1> |
| @@ -57,7 +58,7 @@ | |||
| 57 | 58 | ||
| 58 | {{if ne .Templates.GroupUser.Admins ""}} | 59 | {{if ne .Templates.GroupUser.Admins ""}} |
| 59 | <h3 id="admins"><a href="#admins">#</a> Group admins</h3> | 60 | <h3 id="admins"><a href="#admins">#</a> Group admins</h3> |
| 60 | <div class="admins"> | 61 | <div class="plates admins"> |
| 61 | {{.Templates.GroupUser.Admins}} | 62 | {{.Templates.GroupUser.Admins}} |
| 62 | </div> | 63 | </div> |
| 63 | {{end}} | 64 | {{end}} |
html/index.htm +1
| @@ -11,6 +11,7 @@ | |||
| 11 | <select name="type"> | 11 | <select name="type"> |
| 12 | <option value="all">All</option> | 12 | <option value="all">All</option> |
| 13 | <option value="tag">Tag</option> | 13 | <option value="tag">Tag</option> |
| 14 | <option value="r">Groups</option> | ||
| 14 | </select> | 15 | </select> |
| 15 | <button type="submit">Search!</button> | 16 | <button type="submit">Search!</button> |
| 16 | </form> | 17 | </form> |
html/list.htm +1
| @@ -12,6 +12,7 @@ | |||
| 12 | <select name="type"> | 12 | <select name="type"> |
| 13 | <option value="all">All</option> | 13 | <option value="all">All</option> |
| 14 | <option value="tag">Tag</option> | 14 | <option value="tag">Tag</option> |
| 15 | <option value="r">Groups</option> | ||
| 15 | </select> | 16 | </select> |
| 16 | <button type="submit">Search!</button> | 17 | <button type="submit">Search!</button> |
| 17 | </form> | 18 | </form> |
html/search.htm +1
| @@ -12,6 +12,7 @@ | |||
| 12 | <select name="type"> | 12 | <select name="type"> |
| 13 | <option value="all">All</option> | 13 | <option value="all">All</option> |
| 14 | <option value="tag">Tag</option> | 14 | <option value="tag">Tag</option> |
| 15 | <option value="r">Groups</option> | ||
| 15 | </select> | 16 | </select> |
| 16 | <button type="submit">Search!</button> | 17 | <button type="submit">Search!</button> |
| 17 | </form> | 18 | </form> |