krz/skunky-art

Alternative privacy frontend for DeviantArt.

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

14645842643c4e5fa542def715ac1629e182388c

unsigned

author: lost+skunk <skunky@ebloid.ru> · 2024-09-02T08:16:54Z

instance list update
 INSTANCES.md          |  4 ++--
 TODO.md               |  4 ++--
 app/api.go            | 34 ++++++++++++++++++++++++++++++++++
 app/cache.go          | 31 +++++++++++++++++--------------
 app/parsers.go        |  6 ++++--
 app/router.go         | 10 +++++++++-
 app/util.go           |  9 +++++----
 app/wrapper.go        |  6 +++---
 config.example.json   |  4 ++--
 go.mod                |  4 ++--
 instances.json        | 14 +++++++-------
 static/html/index.htm |  8 ++++----
 12 files changed, 91 insertions(+), 43 deletions(-)

diff --git a/INSTANCES.md b/INSTANCES.md
index d032d24..70b68dc 100644
--- a/INSTANCES.md
+++ b/INSTANCES.md
@@ -3,5 +3,5 @@
 |[skunky.ebloid.ru](https://skunky.ebloid.ru/art)|[Yes](http://[201:eba5:d1fc:bf7b:cfcb:a811:4b8b:7ea3]/art)|No|No| No | No | No | Russia |
 |[clovius.club](https://skunky.clovius.club)|No|No|No| Yes | Yes | No | Sweden |
 |[bloat.cat](https://skunky.bloat.cat)|No|No|No| Yes | Yes | No | Germany |
-|[frontendfriendly.xyz](https://skunkyart.frontendfriendly.xyz)|No|No|No| Yes | Yes | No | Finland |
-|[lumaeris.com](https://skunkyart.lumaeris.com)|No|No|No| Yes | Yes | No | US |
\ No newline at end of file
+|[lumaeris.com](https://skunkyart.lumaeris.com)|No|No|No| Yes | Yes | No | Germany |
+|[art.bloat.cat](https://art.bloat.cat)|No|No|No| Yes | Yes | No | Germany |
diff --git a/TODO.md b/TODO.md
index 5a0da71..0853df6 100644
--- a/TODO.md
+++ b/TODO.md
@@ -13,9 +13,9 @@
 * **Реализовать отображение контента, отличного от картинок (видео, аудио, etc)**
 * Исправить баг с эмоджи, когда некоторые кастомные эмоции могут не отображаться
 * ~~Добавить аргумент &filename, который будет выдавать файл с нормально выглядещем именем~~ ✔️
-* ~~Улучшить систему кеширования: добавить рейтинг для удаления и копирование изображений в ОЗУ~~
+* ~~Улучшить систему кеширования: добавить рейтинг для удаления и копирование изображений в ОЗУ~~ BUG: почему-то всё переодически встаёт раком
 # v1.4
 * Реализовать API
 * Реализовать темы
 * Перейти на арены в кеше
-* Реализовать многоязычный интерфейс
\ No newline at end of file
+* Реализовать многоязычный интерфейс
diff --git a/app/api.go b/app/api.go
new file mode 100644
index 0000000..ddc67c1
--- /dev/null
+++ b/app/api.go
@@ -0,0 +1,34 @@
+package app
+
+import (
+	"math/rand"
+	"strings"
+
+	"git.macaw.me/skunky/devianter"
+)
+
+type API struct {
+  skunkyartLink *skunkyart
+}
+
+func (a API) sendMedia(d *devianter.Deviation) {
+  mediaUrl, name := devianter.UrlFromMedia(d.Media)
+  
+  var filename strings.Builder
+	filename.WriteString(`filename="`)
+  filename.WriteString(name)
+	filename.WriteString(`"`)
+	a.skunkyartLink.Writer.Header().Add("Content-Disposition", filename.String())
+	
+  if len(mediaUrl) != 0 {
+    mediaUrl = mediaUrl[21:]
+		dot := strings.Index(mediaUrl, ".")
+    a.skunkyartLink.DownloadAndSendMedia(mediaUrl[:dot], mediaUrl[dot+11:])
+  }
+}
+
+func (a API) Random() {
+  s, err := devianter.PerformSearch(string(rand.Intn(999)), rand.Intn(30), 'a')
+  try(err) 
+  a.sendMedia(&s.Results[rand.Intn(len(s.Results))])
+}
diff --git a/app/cache.go b/app/cache.go
index 6506cfc..cbf2432 100644
--- a/app/cache.go
+++ b/app/cache.go
@@ -26,9 +26,11 @@ func (s skunkyart) DownloadAndSendMedia(subdomain, path string) {
 	url.WriteString(subdomain)
 	url.WriteString(".wixmp.com/")
 	url.WriteString(path)
-	url.WriteString("?token=")
-	url.WriteString(s.Args.Get("token"))
-
+	if t := s.Args.Get("token"); t != "" {
+			url.WriteString("?token=")
+			url.WriteString(t)
+	}
+	
 	var response []byte
 
 	switch {
@@ -40,17 +42,18 @@ func (s skunkyart) DownloadAndSendMedia(subdomain, path string) {
 		if tempFS[fileName] == nil {
 			tempFS[fileName] = &file{}
 		}
-		f := *tempFS[fileName]
 		mx.Unlock()
 
-		if f.Content != nil {
-			f.Score += 2
+		if tempFS[fileName].Content != nil {
+			response = tempFS[fileName].Content
+			tempFS[fileName].Score += 2
+			break
 		} else {
 			file, err := os.Open(filePath)
 			if err != nil {
 				if dwnld := Download(url.String()); dwnld.Status == 200 && dwnld.Headers["Content-Type"][0][:5] == "image" {
-					f.Content = dwnld.Body
-					try(os.WriteFile(filePath, f.Content, 0700))
+					response = dwnld.Body
+					try(os.WriteFile(filePath, response, 0700))
 				} else {
 					s.ReturnHTTPError(dwnld.Status)
 					return
@@ -58,11 +61,16 @@ func (s skunkyart) DownloadAndSendMedia(subdomain, path string) {
 			} else {
 				file, e := io.ReadAll(file)
 				try(e)
-				f.Content = file
+				response = file
 			}
 
 			go func() {
 				defer restore()
+
+				mx.RLock()
+				tempFS[fileName].Content = response
+				mx.RUnlock()
+
 				for {
 					time.Sleep(1 * time.Minute)
 
@@ -77,11 +85,6 @@ func (s skunkyart) DownloadAndSendMedia(subdomain, path string) {
 				}
 			}()
 		}
-
-		mx.Lock()
-		tempFS[fileName] = &f
-		mx.Unlock()
-		response = f.Content
 	case CFG.Proxy:
 		dwnld := Download(url.String())
 		if dwnld.Status != 200 {
diff --git a/app/parsers.go b/app/parsers.go
index f93c18d..3a318be 100644
--- a/app/parsers.go
+++ b/app/parsers.go
@@ -82,8 +82,9 @@ func (s skunkyart) DeviationList(devs []devianter.Deviation, allowAtom bool, con
 
 	for i, l := 0, len(devs); i < l; i++ {
 		data := &devs[i]
-		if preview, fullview := ParseMedia(data.Media, data.Title, 320), ParseMedia(data.Media, data.Title); !(data.NSFW && !CFG.Nsfw) {
+		if preview, fullview := ParseMedia(data.Media, 320), ParseMedia(data.Media); !(data.NSFW && !CFG.Nsfw) {
 			if allowAtom && s.Atom {
+				s.Writer.Header().Add("Content-type", "application/atom+xml")
 				id := strconv.Itoa(data.ID)
 				listContent.WriteString(`<entry><author><name>`)
 				listContent.WriteString(data.Author.Username)
@@ -183,6 +184,7 @@ type text struct {
 	To      int
 }
 
+// переписать весь этот пиздец нахуй
 func ParseDescription(dscr devianter.Text) string {
 	var parsedDescription strings.Builder
 	TagBuilder := func(content string, tags ...string) string {
@@ -286,7 +288,7 @@ func ParseDescription(dscr devianter.Text) string {
 					parsedDescription.WriteString(`<a href="`)
 					parsedDescription.WriteString(ConvertDeviantArtUrlToSkunkyArt(d.Url))
 					parsedDescription.WriteString(`"><img width="50%" src="`)
-					parsedDescription.WriteString(ParseMedia(d.Media, d.Title))
+					parsedDescription.WriteString(ParseMedia(d.Media))
 					parsedDescription.WriteString(`" title="`)
 					parsedDescription.WriteString(d.Author.Username)
 					parsedDescription.WriteString(" - ")
diff --git a/app/router.go b/app/router.go
index 35550f8..2bcaf41 100644
--- a/app/router.go
+++ b/app/router.go
@@ -83,13 +83,15 @@ func Router() {
 		}
 
 		skunky.Endpoint = path[1]
+		skunky.API.skunkyartLink = &skunky
 
 		// пути
 		switch skunky.Endpoint {
 		default:
 			skunky.ReturnHTTPError(404)
+
 		case "":
-			w.Write(open("html/index.htm"))
+			skunky.ExecuteTemplate("index.htm", "html", &CFG.URI)
 		case "post":
 			skunky.Deviation(path[2], path[3])
 		case "search":
@@ -120,6 +122,12 @@ func Router() {
 			w.Write(open("css/skunky.css"))
 		case "favicon.ico":
 			w.Write(open("images/logo.png"))
+
+		case "api":
+			switch path[2] {
+				case "random":
+					skunky.API.Random()
+			}
 		}
 	}
 
diff --git a/app/util.go b/app/util.go
index 8e8ef6e..71e7d89 100644
--- a/app/util.go
+++ b/app/util.go
@@ -63,6 +63,8 @@ type skunkyart struct {
 	BasePath, Endpoint string
 	Query, QueryRaw    string
 
+  API API
+
 	Templates struct {
 		About struct {
 			Proxy     bool
@@ -182,13 +184,12 @@ func Download(urlString string) (d Downloaded) {
 }
 
 /* PARSING HELPERS */
-func ParseMedia(media devianter.Media, filename string, thumb ...int) string {
-	mediaUrl := devianter.UrlFromMedia(media, thumb...)
+func ParseMedia(media devianter.Media, thumb ...int) string {
+	mediaUrl, filename := devianter.UrlFromMedia(media, thumb...)
 	if len(mediaUrl) != 0 && CFG.Proxy {
 		mediaUrl = mediaUrl[21:]
 		dot := strings.Index(mediaUrl, ".")
-
-		return UrlBuilder("media", "file", mediaUrl[:dot], mediaUrl[dot+11:], "&filename=", url.QueryEscape(filename))
+		return UrlBuilder("media", "file", mediaUrl[:dot], mediaUrl[dot+11:], "&filename=", filename)
 	}
 	return mediaUrl
 }
diff --git a/app/wrapper.go b/app/wrapper.go
index e0e5054..f78d8c8 100644
--- a/app/wrapper.go
+++ b/app/wrapper.go
@@ -74,7 +74,7 @@ func (s skunkyart) GRUser() {
 			case "cover_deviation":
 				group.About.BGMeta = x.ModuleData.CoverDeviation.Deviation
 				group.About.BGMeta.Url = ConvertDeviantArtUrlToSkunkyArt(group.About.BGMeta.Url)
-				group.About.BG = ParseMedia(group.About.BGMeta.Media, group.About.BGMeta.Title)
+				group.About.BG = ParseMedia(group.About.BGMeta.Media)
 			case "group_admins":
 				var htm strings.Builder
 				for _, z := range x.ModuleData.GroupAdmins.Results {
@@ -122,7 +122,7 @@ func (s skunkyart) GRUser() {
 								folders.WriteString(`<a href="`)
 								folders.WriteString(ConvertDeviantArtUrlToSkunkyArt(x.Thumb.Url))
 								folders.WriteString(`"><img loading="lazy" src="`)
-								folders.WriteString(ParseMedia(x.Thumb.Media, x.Thumb.Title))
+								folders.WriteString(ParseMedia(x.Thumb.Media))
 								folders.WriteString(`" title="`)
 								folders.WriteString(x.Thumb.Title)
 								folders.WriteString(`"></a>`)
@@ -185,7 +185,7 @@ func (s skunkyart) Deviation(author, postname string) {
 	}
 	// время публикации
 	post.StringTime = post.Post.Deviation.PublishedTime.UTC().String()
-	post.Post.IMG = ParseMedia(post.Post.Deviation.Media, post.Post.Deviation.Title)
+	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, false)
diff --git a/config.example.json b/config.example.json
index 73eb32d..5397722 100644
--- a/config.example.json
+++ b/config.example.json
@@ -1,6 +1,6 @@
 {
-    "listen": "0:3003",
-    "uri": "/",
+    "listen": "0.0.0.0:3003",
+    "uri": "/huy/",
     "cache": {
         "enabled": true,
         "path": "cache",
diff --git a/go.mod b/go.mod
index 2b4844e..c7dc4e6 100644
--- a/go.mod
+++ b/go.mod
@@ -1,8 +1,8 @@
 module skunkyart
 
-go 1.22.3
+go 1.18
 
-replace git.macaw.me/skunky/devianter v0.2.5 => /home/skunk/projects/devianter
+// replace git.macaw.me/skunky/devianter v0.2.5 => /home/skunk/projects/devianter
 require (
 	git.macaw.me/skunky/devianter v0.2.5
 	golang.org/x/net v0.27.0
diff --git a/instances.json b/instances.json
index 74049be..4b43a06 100644
--- a/instances.json
+++ b/instances.json
@@ -35,10 +35,10 @@
             }
         },
         {
-            "title": "frontendfriendly.xyz",
-            "country": "Finland",
+            "title": "lumaeris.com",
+            "country": "Germany",
             "urls": {
-                "clearnet": "https://skunkyart.frontendfriendly.xyz"
+                "clearnet": "https://skunkyart.lumaeris.com"
             },
             "settings": {
                 "nsfw": true,
@@ -46,10 +46,10 @@
             }
         },
         {
-            "title": "lumaeris.com",
-            "country": "US",
+            "title": "art.bloat.cat",
+            "country": "Germany",
             "urls": {
-                "clearnet": "https://skunkyart.lumaeris.com"
+                "clearnet": "https://art.bloat.cat"
             },
             "settings": {
                 "nsfw": true,
@@ -57,4 +57,4 @@
             }
         }
     ]
-}
\ No newline at end of file
+}
diff --git a/static/html/index.htm b/static/html/index.htm
index 78f1d8a..08feba5 100644
--- a/static/html/index.htm
+++ b/static/html/index.htm
@@ -2,12 +2,12 @@
 <html>
     <head>
         <title>SkunkyArt</title>
-        <link rel="stylesheet" href="stylesheet"/>
-        <link rel="icon" type="image/x-icon" href="favicon.ico">
+        <link rel="stylesheet" href="{{.}}stylesheet"/>
+        <link rel="icon" type="image/x-icon" href="{{.}}favicon.ico">
     </head>
     <main>
         <center>
-            <form method="get" action="search">
+            <form method="get" action="{{.}}search">
                 <input type="text" name="q" placeholder="Search for ..." autocomplete="off" autocapitalize="none" spellcheck="false">
                 <select name="type">
                     <option value="all">All</option>
@@ -16,7 +16,7 @@
                 </select>
                 <button type="submit">Search!</button>
             </form>
-            <h1><a href="dd">Daily Deviations</a> | <a href="about">About</a> | <a href="https://git.macaw.me/skunky/SkunkyArt" target="_blank">Source Code</a></h1>
+            <h1><a href="{{.}}dd">Daily Deviations</a> | <a href="{{.}}about">About</a> | <a href="https://git.macaw.me/skunky/SkunkyArt" target="_blank">Source Code</a></h1>
         </center>
     </main>
 </html>
\ No newline at end of file