krz/skunky-art

Alternative privacy frontend for DeviantArt.

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

e07e1a25b26d3fd2b8fd097491d055fe7b002d2b

unsigned

author: lost+skunk <skunky@ebloid.ru> · 2024-07-08T16:01:53Z

небольшие улучшения парсинга описания/комментариев
 app/util.go    | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
 app/wrapper.go |  12 ++++---
 css/skunky.css |  28 +++++++++++++++
 go.mod         |   5 ++-
 go.sum         |   2 ++
 instances.json |  14 ++++++++
 6 files changed, 162 insertions(+), 11 deletions(-)

diff --git a/app/util.go b/app/util.go
index e22e83b..eb1c7ce 100644
--- a/app/util.go
+++ b/app/util.go
@@ -14,6 +14,7 @@ import (
 	"time"
 
 	"git.macaw.me/skunky/devianter"
+	"golang.org/x/net/html"
 )
 
 // парсинг темплейтов
@@ -95,18 +96,59 @@ func ParseDescription(dscr devianter.Text) string {
 		description[dl-1] == '}' {
 		var descr struct {
 			Blocks []struct {
-				Key, Text, Type   string
+				Text, Type        string
 				InlineStyleRanges []struct {
 					Offset, Length int
 					Style          string
 				}
+				EntityRanges []struct {
+					Offset, Length int
+					Key            int
+				}
+				Data struct {
+					TextAlignment string
+				}
+			}
+			EntityMap map[string]struct {
+				Type string
+				Data struct {
+					Config struct {
+						Aligment string
+						Width    int
+					}
+					Data devianter.Deviation
+				}
 			}
 		}
 		e := json.Unmarshal([]byte(description), &descr)
 		err(e)
 
+		entities := make(map[int]devianter.Deviation)
+		for n, x := range descr.EntityMap {
+			num, _ := strconv.Atoi(n)
+			entities[num] = x.Data.Data
+		}
+
 		for _, x := range descr.Blocks {
 			ranges := make(map[int]text)
+
+			if len(x.InlineStyleRanges) == 0 {
+				switch x.Type {
+				case "atomic":
+					d := entities[x.EntityRanges[0].Key]
+					parseddescription.WriteString(`<img width="50%" src="`)
+					parseddescription.WriteString(ParseMedia(d.Media))
+					parseddescription.WriteString(`" title="`)
+					parseddescription.WriteString(d.Author.Username)
+					parseddescription.WriteString(" - ")
+					parseddescription.WriteString(d.Title)
+					parseddescription.WriteString(`">`)
+				case "unstyled":
+					parseddescription.WriteString(x.Text)
+				}
+				parseddescription.WriteString("<br>")
+			}
+
 			for i, rngs := range x.InlineStyleRanges {
 				var tag string
 
@@ -140,7 +182,66 @@ func ParseDescription(dscr devianter.Text) string {
 			}
 		}
 	} else if dl != 0 {
-		parseddescription.WriteString(description)
+		tagval := func(t *html.Tokenizer) string {
+			for {
+				tt := t.Next()
+				switch tt {
+				case html.ErrorToken:
+					return ""
+				case html.TextToken:
+					return string(t.Text())
+				}
+			}
+		}
+
+		tt := html.NewTokenizer(strings.NewReader(dscr.Html.Markup))
+		for {
+			t := tt.Next()
+			switch t {
+			case html.ErrorToken:
+				return parseddescription.String()
+			case html.StartTagToken, html.EndTagToken, html.SelfClosingTagToken:
+				token := tt.Token()
+				switch token.Data {
+				case "a":
+					for _, a := range token.Attr {
+						if a.Key == "href" {
+							url := strings.ReplaceAll(a.Val, "https://www.deviantart.com/users/outgoing?", "")
+							if strings.Contains(url, "deviantart") {
+								url = strings.ReplaceAll(url, "https://www.deviantart.com/", "")
+								url = strings.ReplaceAll(url, url[0:strings.Index(url, "/")+1], "")
+							}
+							parseddescription.WriteString("<a target=\"_blank\" href=\"" + url + "\">" + tagval(tt) + "</a> ")
+						}
+					}
+				case "img":
+					var (
+						uri, title string
+					)
+					for b, a := range token.Attr {
+						switch a.Key {
+						case "src":
+							if len(a.Val) > 9 && a.Val[8:9] == "e" {
+								uri = UrlBuilder("media", "emojitar", a.Val[37:len(a.Val)-4], "?type=e")
+							}
+						case "title":
+							title = a.Val
+						}
+						if title != "" {
+							for x := -1; x < b; x++ {
+								parseddescription.WriteString("<img src=\"" + uri + "\" title=\"" + title + "\">")
+							}
+						}
+					}
+				case "br", "li", "ul", "p", "b":
+					parseddescription.WriteString(token.String())
+				case "div":
+					parseddescription.WriteString("<p> ")
+				}
+			case html.TextToken:
+				parseddescription.Write(tt.Text())
+			}
+		}
 	}
 
 	return parseddescription.String()
@@ -234,7 +335,7 @@ func (s skunkyart) DeviationList(devs []devianter.Deviation, content ...dlist) s
 	}
 	for _, data := range devs {
 		if !(data.NSFW && !CFG.Nsfw) {
-			url := s.ParseMedia(data.Media)
+			url := ParseMedia(data.Media)
 			if s.Atom {
 				id := strconv.Itoa(data.ID)
 				list.WriteString(`<entry><author><name>`)
@@ -307,7 +408,6 @@ func (s skunkyart) DeviationList(devs []devianter.Deviation, content ...dlist) s
 	return list.String()
 }
 
-// FIXME: первый комментарий не отображается.
 func (s skunkyart) ParseComments(c devianter.Comments) string {
 	var cmmts strings.Builder
 	replied := make(map[int]string)
@@ -354,7 +454,7 @@ func (s skunkyart) ParseComments(c devianter.Comments) string {
 		cmmts.WriteString(x.Posted.UTC().String())
 		cmmts.WriteString("]<p>")
 
-		cmmts.WriteString(x.Comment)
+		cmmts.WriteString(ParseDescription(x.TextContent))
 		cmmts.WriteString("<p>👍: ")
 		cmmts.WriteString(strconv.Itoa(x.Likes))
 		cmmts.WriteString(" ⏩: ")
@@ -369,7 +469,7 @@ func (s skunkyart) ParseComments(c devianter.Comments) string {
 	return cmmts.String()
 }
 
-func (s skunkyart) ParseMedia(media devianter.Media) string {
+func ParseMedia(media devianter.Media) string {
 	url := devianter.UrlFromMedia(media)
 	if len(url) != 0 {
 		url = url[21:]
diff --git a/app/wrapper.go b/app/wrapper.go
index 0a3d0a7..0340aed 100644
--- a/app/wrapper.go
+++ b/app/wrapper.go
@@ -127,7 +127,7 @@ func (s skunkyart) GRUser() {
 			case "cover_deviation":
 				group.About.BGMeta = x.ModuleData.CoverDeviation.Deviation
 				group.About.BGMeta.Url = s.ConvertDeviantArtUrlToSkunkyArt(group.About.BGMeta.Url)
-				group.About.BG = s.ParseMedia(group.About.BGMeta.Media)
+				group.About.BG = ParseMedia(group.About.BGMeta.Media)
 			case "group_admins":
 				var htm strings.Builder
 				for _, z := range x.ModuleData.GroupAdmins.Results {
@@ -165,7 +165,7 @@ func (s skunkyart) GRUser() {
 						folders.WriteString(`<a href="`)
 						folders.WriteString(s.ConvertDeviantArtUrlToSkunkyArt(x.Thumb.Url))
 						folders.WriteString(`"><img loading="lazy" src="`)
-						folders.WriteString(s.ParseMedia(x.Thumb.Media))
+						folders.WriteString(ParseMedia(x.Thumb.Media))
 						folders.WriteString(`" title="`)
 						folders.WriteString(x.Thumb.Title)
 						folders.WriteString(`"></a><br>`)
@@ -212,10 +212,14 @@ func (s skunkyart) Deviation(author, postname string) {
 		id := id_search[len(id_search)-1]
 		post.Post = devianter.DeviationFunc(id, author)
 
-		post.Post.Description = ParseDescription(post.Post.Deviation.TextContent)
+		if post.Post.Deviation.TextContent.Excerpt != "" {
+			post.Post.Description = ParseDescription(post.Post.Deviation.TextContent)
+		} else {
+			post.Post.Description = ParseDescription(post.Post.Deviation.Extended.DescriptionText)
+		}
 		// время публикации
 		post.StringTime = post.Post.Deviation.PublishedTime.UTC().String()
-		post.Post.IMG = s.ParseMedia(post.Post.Deviation.Media)
+		post.Post.IMG = ParseMedia(post.Post.Deviation.Media)
 		for _, x := range post.Post.Deviation.Extended.RelatedContent {
 			if len(x.Deviations) != 0 {
 				post.Related = s.DeviationList(x.Deviations)
diff --git a/css/skunky.css b/css/skunky.css
index c299dab..eb9b350 100644
--- a/css/skunky.css
+++ b/css/skunky.css
@@ -60,6 +60,7 @@ form input, button, select {
     color: rgb(160, 0, 147);
 }
 .content {
+    align-items: center;
     border-radius: 3px;
     display: flex;
     flex-wrap: wrap;
@@ -112,4 +113,31 @@ form input, button, select {
     word-break: break-all;
     text-align: center;
     width: 5%;
+}
+
+@media screen and (orientation: portrait) {
+    header {
+        scale: 155%;
+        justify-content: center;
+    }
+    .content {
+        margin: auto;
+        display: inherit;
+        scale: 100%;
+    }
+    .block {
+        max-width: 60%;
+    }
+}
+
+@media screen and (max-width: 1462px) {
+    .block {
+        max-width: 30%;
+    }
+}
+
+@media screen and (min-width: 788px) and (max-width: 884px) {
+    .block {
+        max-width: 35%;
+    }
 }
\ No newline at end of file
diff --git a/go.mod b/go.mod
index ac1e0d0..0202154 100644
--- a/go.mod
+++ b/go.mod
@@ -4,4 +4,7 @@ go 1.22.3
 
 replace git.macaw.me/skunky/devianter v0.1.0 => /home/skunk/projects/devianter
 
-require git.macaw.me/skunky/devianter v0.1.0
+require (
+	git.macaw.me/skunky/devianter v0.1.0
+	golang.org/x/net v0.27.0
+)
diff --git a/go.sum b/go.sum
index e69de29..7566714 100644
--- a/go.sum
+++ b/go.sum
@@ -0,0 +1,2 @@
+golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
+golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
diff --git a/instances.json b/instances.json
new file mode 100644
index 0000000..2de7e85
--- /dev/null
+++ b/instances.json
@@ -0,0 +1,14 @@
+{
+    "instances": [{
+        "urls": [{
+            "i2p": "http://skunky.i2p/art",
+            "tor": "http://skunky0wjkf8j8ajofgh98aOZjhu8f.onion/art",
+            "ygg": "http://[324:71e:281a:9ed3::fa11]/skunkyart",
+            "clearnet": "https://skunky.net/art"
+        }],
+        "settings": {
+            "nsfw": true,
+            "proxy": true
+        }
+    }]
+}
\ No newline at end of file