krz/skunky-art

Alternative privacy frontend for DeviantArt.

clone: git clone https://gitbay.org/krz/skunky-art.git

v1.3.1: app/wrapper.go · raw

  1package app
  2
  3import (
  4	"encoding/json"
  5	"io"
  6	"net/http"
  7	"net/url"
  8	"regexp"
  9	"strconv"
 10	"strings"
 11	"time"
 12
 13	"git.macaw.me/skunky/devianter"
 14	"golang.org/x/net/html"
 15)
 16
 17var wr = io.WriteString
 18var Templates = make(map[string]string)
 19
 20type skunkyart struct {
 21	Writer http.ResponseWriter
 22
 23	Args            url.Values
 24	BasePath        string
 25	Type            rune
 26	Query, QueryRaw string
 27	Page            int
 28	Atom            bool
 29
 30	Templates struct {
 31		About struct {
 32			Proxy     bool
 33			Nsfw      bool
 34			Instances []settings
 35		}
 36
 37		SomeList  string
 38		DDStrips  string
 39		Deviation struct {
 40			Post       devianter.Post
 41			Related    string
 42			StringTime string
 43			Tags       string
 44			Comments   string
 45		}
 46
 47		GroupUser struct {
 48			GR           devianter.GRuser
 49			Admins       string
 50			Group        bool
 51			CreationDate string
 52
 53			About struct {
 54				A devianter.About
 55
 56				DescriptionFormatted string
 57				Interests, Social    string
 58				Comments             string
 59				BG                   string
 60				BGMeta               devianter.Deviation
 61			}
 62
 63			Gallery struct {
 64				Folders string
 65				Pages   int
 66				List    string
 67			}
 68		}
 69		Search struct {
 70			Content devianter.Search
 71			List    string
 72		}
 73	}
 74}
 75
 76func (s skunkyart) GRUser() {
 77	if len(s.Query) < 1 {
 78		s.ReturnHTTPError(400)
 79		return
 80	}
 81
 82	var g devianter.Group
 83	g.Name = s.Query
 84	var err error
 85	s.Templates.GroupUser.GR, err = g.GetGroup()
 86	try(err)
 87
 88	group := &s.Templates.GroupUser
 89
 90	switch s.Type {
 91	case 'a':
 92		g := group.GR
 93		s.Atom = false
 94		for _, x := range g.Gruser.Page.Modules {
 95			switch x.Name {
 96			case "about", "group_about":
 97				switch g.Owner.Group {
 98				case true:
 99					var about = &x.ModuleData.GroupAbout
100					group.Group = true
101					group.CreationDate = x.ModuleData.GroupAbout.FoundatedAt.UTC().String()
102					group.About.DescriptionFormatted = ParseDescription(about.Description)
103				case false:
104					group.About.A = x.ModuleData.About
105					var about = &group.About.A
106					group.CreationDate = time.Unix(time.Now().Unix()-x.ModuleData.About.RegDate, 0).UTC().String()
107
108					group.About.DescriptionFormatted = ParseDescription(about.Description)
109
110					for _, val := range x.ModuleData.About.SocialLinks {
111						var social strings.Builder
112						social.WriteString(`<a target="_blank" href="`)
113						social.WriteString(val.Value)
114						social.WriteString(`">`)
115						social.WriteString(val.Value)
116						social.WriteString("</a><br>")
117						group.About.Social += social.String()
118					}
119
120					for _, val := range x.ModuleData.About.Interests {
121						var interest strings.Builder
122						interest.WriteString(val.Label)
123						interest.WriteString(": <b>")
124						interest.WriteString(val.Value)
125						interest.WriteString("</b><br>")
126						group.About.Interests += interest.String()
127					}
128				}
129				group.About.Comments = s.ParseComments(devianter.GetComments(
130					strconv.Itoa(group.GR.Gruser.ID),
131					"",
132					s.Page,
133					4,
134				))
135
136			case "cover_deviation":
137				group.About.BGMeta = x.ModuleData.CoverDeviation.Deviation
138				group.About.BGMeta.Url = ConvertDeviantArtUrlToSkunkyArt(group.About.BGMeta.Url)
139				group.About.BG = ParseMedia(group.About.BGMeta.Media)
140			case "group_admins":
141				var htm strings.Builder
142				for _, z := range x.ModuleData.GroupAdmins.Results {
143					htm.WriteString(BuildUserPlate(z.User.Username))
144				}
145				group.Admins += htm.String()
146			}
147
148		}
149	case 'g':
150		folderid, _ := strconv.Atoi(s.Args.Get("folder"))
151		if s.Page == 0 {
152			s.Page++
153		}
154
155		gallery, err := g.GetGallery(s.Page, folderid)
156		try(err)
157
158		if folderid > 0 {
159			group.Gallery.List = s.DeviationList(gallery.Content.Results, true, DeviationList{
160				More: gallery.Content.HasMore,
161			})
162		} else {
163			for _, x := range gallery.Content.Gruser.Page.Modules {
164				if l := len(x.ModuleData.Folders.Results); l != 0 {
165					var folders strings.Builder
166					folders.WriteString(`<h1 id="folders"><a href="#folder">#</a> Folders</h1><div class="folders"><br>`)
167					for _, x := range x.ModuleData.Folders.Results {
168						folders.WriteString(`<div class="block folder-item">`)
169
170						if !(x.Thumb.NSFW && !CFG.Nsfw) {
171							folders.WriteString(`<a href="`)
172							folders.WriteString(ConvertDeviantArtUrlToSkunkyArt(x.Thumb.Url))
173							folders.WriteString(`"><img loading="lazy" src="`)
174							folders.WriteString(ParseMedia(x.Thumb.Media))
175							folders.WriteString(`" title="`)
176							folders.WriteString(x.Thumb.Title)
177							folders.WriteString(`"></a>`)
178						} else {
179							folders.WriteString(`<h1>[ <span class="nsfw">NSFW</span> ]</h1>`)
180						}
181						folders.WriteString("<br>")
182
183						folders.WriteString(`<a href="?folder=`)
184						folders.WriteString(strconv.Itoa(x.FolderId))
185						folders.WriteString("&q=")
186						folders.WriteString(s.Query)
187						folders.WriteString("&type=")
188						folders.WriteString(string(s.Type))
189						folders.WriteString(`">`)
190						folders.WriteString(x.Name)
191						folders.WriteString(`</a>`)
192
193						folders.WriteString("</div>")
194					}
195					folders.WriteString(`</div><h1 id="content"><a href="#content">#</a> Content</h1>`)
196					group.Gallery.Folders = folders.String()
197				}
198
199				if x.Name == "folder_deviations" {
200					group.Gallery.List = s.DeviationList(x.ModuleData.Folder.Deviations, true, DeviationList{
201						Pages: x.ModuleData.Folder.Pages,
202						More:  x.ModuleData.Folder.HasMore,
203					})
204				}
205			}
206		}
207	default:
208		s.ReturnHTTPError(400)
209	}
210
211	if !s.Atom {
212		s.ExecuteTemplate("gruser.htm", &s)
213	}
214}
215
216// посты
217func (s skunkyart) Deviation(author, postname string) {
218	id_search := regexp.MustCompile("[0-9]+").FindAllString(postname, -1)
219	if len(id_search) < 1 {
220		s.ReturnHTTPError(400)
221		return
222	}
223
224	post := &s.Templates.Deviation
225
226	id := id_search[len(id_search)-1]
227	post.Post = devianter.GetDeviation(id, author)
228
229	if post.Post.Deviation.TextContent.Excerpt != "" {
230		post.Post.Description = ParseDescription(post.Post.Deviation.TextContent)
231	} else {
232		post.Post.Description = ParseDescription(post.Post.Deviation.Extended.DescriptionText)
233	}
234	// время публикации
235	post.StringTime = post.Post.Deviation.PublishedTime.UTC().String()
236	post.Post.IMG = ParseMedia(post.Post.Deviation.Media)
237	for _, x := range post.Post.Deviation.Extended.RelatedContent {
238		if len(x.Deviations) != 0 {
239			post.Related += s.DeviationList(x.Deviations, false)
240		}
241	}
242
243	// хештэги
244	for _, x := range post.Post.Deviation.Extended.Tags {
245		var tag strings.Builder
246		tag.WriteString(` <a href="`)
247		tag.WriteString(UrlBuilder("search", "?q=", x.Name, "&type=tag"))
248		tag.WriteString(`">#`)
249		tag.WriteString(x.Name)
250		tag.WriteString("</a>")
251
252		post.Tags += tag.String()
253	}
254
255	if post.Post.Comments.Total <= 50 {
256		post.Post.Comments.Cursor = ""
257	}
258
259	post.Comments = s.ParseComments(devianter.GetComments(id, post.Post.Comments.Cursor, s.Page, 1))
260
261	s.ExecuteTemplate("deviantion.htm", &s)
262}
263
264func (s skunkyart) DD() {
265	dd := devianter.GetDailyDeviations(s.Page)
266	var strips strings.Builder
267	for _, x := range dd.Strips {
268		strips.WriteString(`<h3 class="`)
269		strips.WriteString(x.Codename)
270		strips.WriteString(`"> <a href="#`)
271		strips.WriteString(x.Codename)
272		strips.WriteString(`"># </a>`)
273		strips.WriteString(x.Title)
274		strips.WriteString(`</h3>`)
275
276		strips.WriteString(s.DeviationList(x.Deviations, false))
277	}
278	s.Templates.DDStrips = strips.String()
279	s.Templates.SomeList = s.DeviationList(dd.Deviations, true, DeviationList{
280		Pages: 0,
281		More:  dd.HasMore,
282	})
283	if !s.Atom {
284		s.ExecuteTemplate("daily.htm", &s)
285	}
286}
287
288func (s skunkyart) Search() {
289	var err error
290	ss := &s.Templates.Search
291	switch s.Type {
292	case 'a', 't':
293		ss.Content, err = devianter.PerformSearch(s.Query, s.Page, s.Type)
294	case 'g':
295		ss.Content, err = devianter.PerformSearch(s.Query, s.Page, s.Type, s.Args.Get("usr"))
296	case 'r': // скраппер, поскольку девиантартовцы зажопили гостевое API для поиска групп
297		var (
298			usernames = make(map[int]string)
299			url       strings.Builder
300			num       int
301		)
302
303		s.Page++
304
305		url.WriteString("https://www.deviantart.com/groups/?q=")
306		url.WriteString(s.Query)
307		if s.Page > 1 {
308			url.WriteString("&offset=")
309			url.WriteString(strconv.Itoa(10 * s.Page))
310		}
311
312		dwnld := Download(url.String())
313
314		for z := html.NewTokenizer(strings.NewReader(string(dwnld.Body))); ; {
315			if n, token := z.Next(), z.Token(); n == html.StartTagToken && token.Data == "a" {
316				for _, x := range token.Attr {
317					if x.Key == "class" && x.Val == "u regular username" {
318						usernames[num] = GetValueOfTag(z)
319						num++
320					}
321				}
322			} else if n == 0 {
323				break
324			} else {
325				continue
326			}
327		}
328
329		if l := len(usernames); l != 0 {
330			ss.List += `<div class="content plates">`
331			for x := 0; x < len(usernames); x++ {
332				ss.List += BuildUserPlate(usernames[x])
333			}
334			ss.List += `</div>`
335			ss.List += s.NavBase(DeviationList{
336				More: true,
337			})
338		}
339	default:
340		s.ReturnHTTPError(400)
341	}
342	try(err)
343
344	if s.Type != 'r' {
345		ss.List = s.DeviationList(ss.Content.Results, false, DeviationList{
346			Pages: ss.Content.Pages,
347			More:  ss.Content.HasMore,
348		})
349	}
350
351	s.ExecuteTemplate("search.htm", &s)
352}
353
354func (s skunkyart) Emojitar(name string) {
355	if name == "" || !(s.Type == 'a' || s.Type == 'e') {
356		s.ReturnHTTPError(400)
357		return
358	}
359
360	ae, e := devianter.AEmedia(name, s.Type)
361	if e != nil {
362		s.ReturnHTTPError(404)
363	}
364	wr(s.Writer, ae)
365}
366
367func (s skunkyart) About() {
368	s.Templates.About.Nsfw = CFG.Nsfw
369	s.Templates.About.Proxy = CFG.Proxy
370	try(json.Unmarshal([]byte(Templates["instances.json"]), &s.Templates.About))
371	s.ExecuteTemplate("about.htm", &s)
372}