Commit 1464584264

14645842643c4e5fa542def715ac1629e182388c

parent: 2f8c35ba32

Unsigned

lost+skunk <skunky@ebloid.ru> · 2024-09-02 08:16 UTC

instance list update

Layout: unified · split

INSTANCES.md +2 −2
@@ -3,5 +3,5 @@
3|[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 | 3|[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 |
4|[clovius.club](https://skunky.clovius.club)|No|No|No| Yes | Yes | No | Sweden | 4|[clovius.club](https://skunky.clovius.club)|No|No|No| Yes | Yes | No | Sweden |
5|[bloat.cat](https://skunky.bloat.cat)|No|No|No| Yes | Yes | No | Germany | 5|[bloat.cat](https://skunky.bloat.cat)|No|No|No| Yes | Yes | No | Germany |
6|[frontendfriendly.xyz](https://skunkyart.frontendfriendly.xyz)|No|No|No| Yes | Yes | No | Finland | 6|[lumaeris.com](https://skunkyart.lumaeris.com)|No|No|No| Yes | Yes | No | Germany |
7|[lumaeris.com](https://skunkyart.lumaeris.com)|No|No|No| Yes | Yes | No | US |\ No newline at end of file 7|[art.bloat.cat](https://art.bloat.cat)|No|No|No| Yes | Yes | No | Germany |
TODO.md +2 −2
@@ -13,9 +13,9 @@
13* **Реализовать отображение контента, отличного от картинок (видео, аудио, etc)** 13* **Реализовать отображение контента, отличного от картинок (видео, аудио, etc)**
14* Исправить баг с эмоджи, когда некоторые кастомные эмоции могут не отображаться 14* Исправить баг с эмоджи, когда некоторые кастомные эмоции могут не отображаться
15* ~~Добавить аргумент &filename, который будет выдавать файл с нормально выглядещем именем~~ ✔️ 15* ~~Добавить аргумент &filename, который будет выдавать файл с нормально выглядещем именем~~ ✔️
16* ~~Улучшить систему кеширования: добавить рейтинг для удаления и копирование изображений в ОЗУ~~ 16* ~~Улучшить систему кеширования: добавить рейтинг для удаления и копирование изображений в ОЗУ~~ BUG: почему-то всё переодически встаёт раком
17# v1.4 17# v1.4
18* Реализовать API 18* Реализовать API
19* Реализовать темы 19* Реализовать темы
20* Перейти на арены в кеше 20* Перейти на арены в кеше
21* Реализовать многоязычный интерфейс\ No newline at end of file 21* Реализовать многоязычный интерфейс
app/api.go added +34
@@ -0,0 +1,34 @@
1package app
2
3import (
4 "math/rand"
5 "strings"
6
7 "git.macaw.me/skunky/devianter"
8)
9
10type API struct {
11 skunkyartLink *skunkyart
12}
13
14func (a API) sendMedia(d *devianter.Deviation) {
15 mediaUrl, name := devianter.UrlFromMedia(d.Media)
16
17 var filename strings.Builder
18 filename.WriteString(`filename="`)
19 filename.WriteString(name)
20 filename.WriteString(`"`)
21 a.skunkyartLink.Writer.Header().Add("Content-Disposition", filename.String())
22
23 if len(mediaUrl) != 0 {
24 mediaUrl = mediaUrl[21:]
25 dot := strings.Index(mediaUrl, ".")
26 a.skunkyartLink.DownloadAndSendMedia(mediaUrl[:dot], mediaUrl[dot+11:])
27 }
28}
29
30func (a API) Random() {
31 s, err := devianter.PerformSearch(string(rand.Intn(999)), rand.Intn(30), 'a')
32 try(err)
33 a.sendMedia(&s.Results[rand.Intn(len(s.Results))])
34}
app/cache.go +17 −14
@@ -26,9 +26,11 @@ func (s skunkyart) DownloadAndSendMedia(subdomain, path string) {
26 url.WriteString(subdomain) 26 url.WriteString(subdomain)
27 url.WriteString(".wixmp.com/") 27 url.WriteString(".wixmp.com/")
28 url.WriteString(path) 28 url.WriteString(path)
29 url.WriteString("?token=") 29 if t := s.Args.Get("token"); t != "" {
30 url.WriteString(s.Args.Get("token")) 30 url.WriteString("?token=")
31 31 url.WriteString(t)
32 }
33
32 var response []byte 34 var response []byte
33 35
34 switch { 36 switch {
@@ -40,17 +42,18 @@ func (s skunkyart) DownloadAndSendMedia(subdomain, path string) {
40 if tempFS[fileName] == nil { 42 if tempFS[fileName] == nil {
41 tempFS[fileName] = &file{} 43 tempFS[fileName] = &file{}
42 } 44 }
43 f := *tempFS[fileName]
44 mx.Unlock() 45 mx.Unlock()
45 46
46 if f.Content != nil { 47 if tempFS[fileName].Content != nil {
47 f.Score += 2 48 response = tempFS[fileName].Content
49 tempFS[fileName].Score += 2
50 break
48 } else { 51 } else {
49 file, err := os.Open(filePath) 52 file, err := os.Open(filePath)
50 if err != nil { 53 if err != nil {
51 if dwnld := Download(url.String()); dwnld.Status == 200 && dwnld.Headers["Content-Type"][0][:5] == "image" { 54 if dwnld := Download(url.String()); dwnld.Status == 200 && dwnld.Headers["Content-Type"][0][:5] == "image" {
52 f.Content = dwnld.Body 55 response = dwnld.Body
53 try(os.WriteFile(filePath, f.Content, 0700)) 56 try(os.WriteFile(filePath, response, 0700))
54 } else { 57 } else {
55 s.ReturnHTTPError(dwnld.Status) 58 s.ReturnHTTPError(dwnld.Status)
56 return 59 return
@@ -58,11 +61,16 @@ func (s skunkyart) DownloadAndSendMedia(subdomain, path string) {
58 } else { 61 } else {
59 file, e := io.ReadAll(file) 62 file, e := io.ReadAll(file)
60 try(e) 63 try(e)
61 f.Content = file 64 response = file
62 } 65 }
63 66
64 go func() { 67 go func() {
65 defer restore() 68 defer restore()
69
70 mx.RLock()
71 tempFS[fileName].Content = response
72 mx.RUnlock()
73
66 for { 74 for {
67 time.Sleep(1 * time.Minute) 75 time.Sleep(1 * time.Minute)
68 76
@@ -77,11 +85,6 @@ func (s skunkyart) DownloadAndSendMedia(subdomain, path string) {
77 } 85 }
78 }() 86 }()
79 } 87 }
80
81 mx.Lock()
82 tempFS[fileName] = &f
83 mx.Unlock()
84 response = f.Content
85 case CFG.Proxy: 88 case CFG.Proxy:
86 dwnld := Download(url.String()) 89 dwnld := Download(url.String())
87 if dwnld.Status != 200 { 90 if dwnld.Status != 200 {
app/parsers.go +4 −2
@@ -82,8 +82,9 @@ func (s skunkyart) DeviationList(devs []devianter.Deviation, allowAtom bool, con
82 82
83 for i, l := 0, len(devs); i < l; i++ { 83 for i, l := 0, len(devs); i < l; i++ {
84 data := &devs[i] 84 data := &devs[i]
85 if preview, fullview := ParseMedia(data.Media, data.Title, 320), ParseMedia(data.Media, data.Title); !(data.NSFW && !CFG.Nsfw) { 85 if preview, fullview := ParseMedia(data.Media, 320), ParseMedia(data.Media); !(data.NSFW && !CFG.Nsfw) {
86 if allowAtom && s.Atom { 86 if allowAtom && s.Atom {
87 s.Writer.Header().Add("Content-type", "application/atom+xml")
87 id := strconv.Itoa(data.ID) 88 id := strconv.Itoa(data.ID)
88 listContent.WriteString(`<entry><author><name>`) 89 listContent.WriteString(`<entry><author><name>`)
89 listContent.WriteString(data.Author.Username) 90 listContent.WriteString(data.Author.Username)
@@ -183,6 +184,7 @@ type text struct {
183 To int 184 To int
184} 185}
185 186
187// переписать весь этот пиздец нахуй
186func ParseDescription(dscr devianter.Text) string { 188func ParseDescription(dscr devianter.Text) string {
187 var parsedDescription strings.Builder 189 var parsedDescription strings.Builder
188 TagBuilder := func(content string, tags ...string) string { 190 TagBuilder := func(content string, tags ...string) string {
@@ -286,7 +288,7 @@ func ParseDescription(dscr devianter.Text) string {
286 parsedDescription.WriteString(`<a href="`) 288 parsedDescription.WriteString(`<a href="`)
287 parsedDescription.WriteString(ConvertDeviantArtUrlToSkunkyArt(d.Url)) 289 parsedDescription.WriteString(ConvertDeviantArtUrlToSkunkyArt(d.Url))
288 parsedDescription.WriteString(`"><img width="50%" src="`) 290 parsedDescription.WriteString(`"><img width="50%" src="`)
289 parsedDescription.WriteString(ParseMedia(d.Media, d.Title)) 291 parsedDescription.WriteString(ParseMedia(d.Media))
290 parsedDescription.WriteString(`" title="`) 292 parsedDescription.WriteString(`" title="`)
291 parsedDescription.WriteString(d.Author.Username) 293 parsedDescription.WriteString(d.Author.Username)
292 parsedDescription.WriteString(" - ") 294 parsedDescription.WriteString(" - ")
app/router.go +9 −1
@@ -83,13 +83,15 @@ func Router() {
83 } 83 }
84 84
85 skunky.Endpoint = path[1] 85 skunky.Endpoint = path[1]
86 skunky.API.skunkyartLink = &skunky
86 87
87 // пути 88 // пути
88 switch skunky.Endpoint { 89 switch skunky.Endpoint {
89 default: 90 default:
90 skunky.ReturnHTTPError(404) 91 skunky.ReturnHTTPError(404)
92
91 case "": 93 case "":
92 w.Write(open("html/index.htm")) 94 skunky.ExecuteTemplate("index.htm", "html", &CFG.URI)
93 case "post": 95 case "post":
94 skunky.Deviation(path[2], path[3]) 96 skunky.Deviation(path[2], path[3])
95 case "search": 97 case "search":
@@ -120,6 +122,12 @@ func Router() {
120 w.Write(open("css/skunky.css")) 122 w.Write(open("css/skunky.css"))
121 case "favicon.ico": 123 case "favicon.ico":
122 w.Write(open("images/logo.png")) 124 w.Write(open("images/logo.png"))
125
126 case "api":
127 switch path[2] {
128 case "random":
129 skunky.API.Random()
130 }
123 } 131 }
124 } 132 }
125 133
app/util.go +5 −4
@@ -63,6 +63,8 @@ type skunkyart struct {
63 BasePath, Endpoint string 63 BasePath, Endpoint string
64 Query, QueryRaw string 64 Query, QueryRaw string
65 65
66 API API
67
66 Templates struct { 68 Templates struct {
67 About struct { 69 About struct {
68 Proxy bool 70 Proxy bool
@@ -182,13 +184,12 @@ func Download(urlString string) (d Downloaded) {
182} 184}
183 185
184/* PARSING HELPERS */ 186/* PARSING HELPERS */
185func ParseMedia(media devianter.Media, filename string, thumb ...int) string { 187func ParseMedia(media devianter.Media, thumb ...int) string {
186 mediaUrl := devianter.UrlFromMedia(media, thumb...) 188 mediaUrl, filename := devianter.UrlFromMedia(media, thumb...)
187 if len(mediaUrl) != 0 && CFG.Proxy { 189 if len(mediaUrl) != 0 && CFG.Proxy {
188 mediaUrl = mediaUrl[21:] 190 mediaUrl = mediaUrl[21:]
189 dot := strings.Index(mediaUrl, ".") 191 dot := strings.Index(mediaUrl, ".")
190 192 return UrlBuilder("media", "file", mediaUrl[:dot], mediaUrl[dot+11:], "&filename=", filename)
191 return UrlBuilder("media", "file", mediaUrl[:dot], mediaUrl[dot+11:], "&filename=", url.QueryEscape(filename))
192 } 193 }
193 return mediaUrl 194 return mediaUrl
194} 195}
app/wrapper.go +3 −3
@@ -74,7 +74,7 @@ func (s skunkyart) GRUser() {
74 case "cover_deviation": 74 case "cover_deviation":
75 group.About.BGMeta = x.ModuleData.CoverDeviation.Deviation 75 group.About.BGMeta = x.ModuleData.CoverDeviation.Deviation
76 group.About.BGMeta.Url = ConvertDeviantArtUrlToSkunkyArt(group.About.BGMeta.Url) 76 group.About.BGMeta.Url = ConvertDeviantArtUrlToSkunkyArt(group.About.BGMeta.Url)
77 group.About.BG = ParseMedia(group.About.BGMeta.Media, group.About.BGMeta.Title) 77 group.About.BG = ParseMedia(group.About.BGMeta.Media)
78 case "group_admins": 78 case "group_admins":
79 var htm strings.Builder 79 var htm strings.Builder
80 for _, z := range x.ModuleData.GroupAdmins.Results { 80 for _, z := range x.ModuleData.GroupAdmins.Results {
@@ -122,7 +122,7 @@ func (s skunkyart) GRUser() {
122 folders.WriteString(`<a href="`) 122 folders.WriteString(`<a href="`)
123 folders.WriteString(ConvertDeviantArtUrlToSkunkyArt(x.Thumb.Url)) 123 folders.WriteString(ConvertDeviantArtUrlToSkunkyArt(x.Thumb.Url))
124 folders.WriteString(`"><img loading="lazy" src="`) 124 folders.WriteString(`"><img loading="lazy" src="`)
125 folders.WriteString(ParseMedia(x.Thumb.Media, x.Thumb.Title)) 125 folders.WriteString(ParseMedia(x.Thumb.Media))
126 folders.WriteString(`" title="`) 126 folders.WriteString(`" title="`)
127 folders.WriteString(x.Thumb.Title) 127 folders.WriteString(x.Thumb.Title)
128 folders.WriteString(`"></a>`) 128 folders.WriteString(`"></a>`)
@@ -185,7 +185,7 @@ func (s skunkyart) Deviation(author, postname string) {
185 } 185 }
186 // время публикации 186 // время публикации
187 post.StringTime = post.Post.Deviation.PublishedTime.UTC().String() 187 post.StringTime = post.Post.Deviation.PublishedTime.UTC().String()
188 post.Post.IMG = ParseMedia(post.Post.Deviation.Media, post.Post.Deviation.Title) 188 post.Post.IMG = ParseMedia(post.Post.Deviation.Media)
189 for _, x := range post.Post.Deviation.Extended.RelatedContent { 189 for _, x := range post.Post.Deviation.Extended.RelatedContent {
190 if len(x.Deviations) != 0 { 190 if len(x.Deviations) != 0 {
191 post.Related += s.DeviationList(x.Deviations, false) 191 post.Related += s.DeviationList(x.Deviations, false)
config.example.json +2 −2
@@ -1,6 +1,6 @@
1{ 1{
2 "listen": "0:3003", 2 "listen": "0.0.0.0:3003",
3 "uri": "/", 3 "uri": "/huy/",
4 "cache": { 4 "cache": {
5 "enabled": true, 5 "enabled": true,
6 "path": "cache", 6 "path": "cache",
go.mod +2 −2
@@ -1,8 +1,8 @@
1module skunkyart 1module skunkyart
2 2
3go 1.22.3 3go 1.18
4 4
5replace git.macaw.me/skunky/devianter v0.2.5 => /home/skunk/projects/devianter 5// replace git.macaw.me/skunky/devianter v0.2.5 => /home/skunk/projects/devianter
6require ( 6require (
7 git.macaw.me/skunky/devianter v0.2.5 7 git.macaw.me/skunky/devianter v0.2.5
8 golang.org/x/net v0.27.0 8 golang.org/x/net v0.27.0
instances.json +7 −7
@@ -35,10 +35,10 @@
35 } 35 }
36 }, 36 },
37 { 37 {
38 "title": "frontendfriendly.xyz", 38 "title": "lumaeris.com",
39 "country": "Finland", 39 "country": "Germany",
40 "urls": { 40 "urls": {
41 "clearnet": "https://skunkyart.frontendfriendly.xyz" 41 "clearnet": "https://skunkyart.lumaeris.com"
42 }, 42 },
43 "settings": { 43 "settings": {
44 "nsfw": true, 44 "nsfw": true,
@@ -46,10 +46,10 @@
46 } 46 }
47 }, 47 },
48 { 48 {
49 "title": "lumaeris.com", 49 "title": "art.bloat.cat",
50 "country": "US", 50 "country": "Germany",
51 "urls": { 51 "urls": {
52 "clearnet": "https://skunkyart.lumaeris.com" 52 "clearnet": "https://art.bloat.cat"
53 }, 53 },
54 "settings": { 54 "settings": {
55 "nsfw": true, 55 "nsfw": true,
@@ -57,4 +57,4 @@
57 } 57 }
58 } 58 }
59 ] 59 ]
60}\ No newline at end of file 60}
static/html/index.htm +4 −4
@@ -2,12 +2,12 @@
2<html> 2<html>
3 <head> 3 <head>
4 <title>SkunkyArt</title> 4 <title>SkunkyArt</title>
5 <link rel="stylesheet" href="stylesheet"/> 5 <link rel="stylesheet" href="{{.}}stylesheet"/>
6 <link rel="icon" type="image/x-icon" href="favicon.ico"> 6 <link rel="icon" type="image/x-icon" href="{{.}}favicon.ico">
7 </head> 7 </head>
8 <main> 8 <main>
9 <center> 9 <center>
10 <form method="get" action="search"> 10 <form method="get" action="{{.}}search">
11 <input type="text" name="q" placeholder="Search for ..." autocomplete="off" autocapitalize="none" spellcheck="false"> 11 <input type="text" name="q" placeholder="Search for ..." autocomplete="off" autocapitalize="none" spellcheck="false">
12 <select name="type"> 12 <select name="type">
13 <option value="all">All</option> 13 <option value="all">All</option>
@@ -16,7 +16,7 @@
16 </select> 16 </select>
17 <button type="submit">Search!</button> 17 <button type="submit">Search!</button>
18 </form> 18 </form>
19 <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> 19 <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>
20 </center> 20 </center>
21 </main> 21 </main>
22</html>\ No newline at end of file 22</html>\ No newline at end of file