From 1537da9b16dfda1ecb2730b5d1de3b9005397618 Mon Sep 17 00:00:00 2001 From: lost+skunk Date: Tue, 13 Aug 2024 15:59:52 +0300 Subject: [PATCH] =?UTF-8?q?=D1=82=D0=B5=D0=BC=D0=BF=D0=BB=D0=B5=D0=B9?= =?UTF-8?q?=D1=82=D1=8B=20=D0=B2=20=D0=B1=D0=B8=D0=BD=D0=B0=D1=80=D0=BD?= =?UTF-8?q?=D0=B8=D0=BA=D0=B5=20=D0=B8=20=D1=83=D0=BB=D1=83=D1=87=D1=88?= =?UTF-8?q?=D0=B5=D0=BD=D0=BD=D0=B0=D1=8F=20=D1=81=D0=B8=D1=81=D1=82=D0=B5?= =?UTF-8?q?=D0=BC=D0=B0=20=D0=BA=D0=B5=D1=88=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 +- SETUP-RU.md | 2 +- SETUP.md | 2 +- TODO.md | 18 ++-- app/cache.go | 132 ++++++++++++++++++++++++ app/cli.go | 63 ++++++------ app/config.go | 16 +-- app/parsers.go | 8 +- app/router.go | 33 ++++-- app/stat-freebsd.go | 13 +++ app/stat.go | 13 +++ app/util.go | 136 ++++++------------------ app/wrapper.go | 40 +++++--- config.example.json | 6 +- html/daily.htm | 28 ----- html/search.htm | 30 ------ main.go | 3 +- {css => static/css}/skunky.css | 13 ++- {html => static/html}/about.htm | 19 +--- static/html/daily.htm | 13 +++ {html => static/html}/deviantion.htm | 20 +--- {html => static/html}/gruser.htm | 16 +-- static/html/head.htm | 24 +++++ static/html/header.htm | 14 +++ {html => static/html}/index.htm | 8 +- static/html/search.htm | 16 +++ {misc => static/images}/logo.png | Bin static/templates-noembed.go | 148 +++++++++++++++++++++++++++ static/templates.go | 16 +++ 29 files changed, 555 insertions(+), 303 deletions(-) create mode 100644 app/cache.go create mode 100644 app/stat-freebsd.go create mode 100644 app/stat.go delete mode 100644 html/daily.htm delete mode 100644 html/search.htm rename {css => static/css}/skunky.css (94%) rename {html => static/html}/about.htm (74%) create mode 100644 static/html/daily.htm rename {html => static/html}/deviantion.htm (72%) rename {html => static/html}/gruser.htm (87%) create mode 100644 static/html/head.htm create mode 100644 static/html/header.htm rename {html => static/html}/index.htm (61%) create mode 100644 static/html/search.htm rename {misc => static/images}/logo.png (100%) create mode 100644 static/templates-noembed.go create mode 100644 static/templates.go diff --git a/README.md b/README.md index 6eaa831..735cc17 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ -SkunkyArt +SkunkyArt [![Matrix room](https://img.shields.io/badge/matrix-000000?style=for-the-badge&logo=Matrix&logoColor=white)](https://go.kde.org/matrix/#/#skunkyart:ebloid.ru) -Instances: [`INSTANCES.md`](https://git.macaw.me/skunky/SkunkyArt/src/branch/master/INSTANCES.md) +Instances: [`INSTANCES.md`](/skunky/SkunkyArt/src/branch/master/INSTANCES.md) # EN πŸ‡ΊπŸ‡Έ ## Description SkunkyArt 🦨 β€” alternative frontend for DevianArt, which works without JS. ## Setup The sample config is in the `config.example.json` file. For custom config, use `--config` option. -See the [`SETUP.md`](https://git.macaw.me/skunky/SkunkyArt/src/branch/master/SETUP.md) file for more info about directives. +See the [`SETUP.md`](/skunky/SkunkyArt/src/branch/master/SETUP.md) file for more info about directives. ## Adding instance to the list To do this, you must either make a PR by adding your instance to the `instances.json` and `INSTANCES.md` files (you can use `--add-instance` cli-argument to automatically add the instance to these files), or create an Issue, or report it to the room in Matrix. Keep in mind that your instance must comply with the following rules: 1. the Instance must not use Cloudflare. @@ -23,7 +23,7 @@ To do this, you must either make a PR by adding your instance to the `instances. SkunkyArt 🦨 β€” Π°Π»ΡŒΡ‚Π΅Ρ€Π½Π°Ρ‚ΠΈΠ²Π½Ρ‹ΠΉ Ρ„Ρ€ΠΎΠ½Ρ‚Π΅Π½Π΄ ΠΊ DeviantArt, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΏΠΎΠ»Π½ΠΎΡΡ‚ΡŒΡŽ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ Π±Π΅Π· JS (JavaScript). ## Настройка ΠŸΡ€ΠΈΠΌΠ΅Ρ€ ΠΊΠΎΠ½Ρ„ΠΈΠ³Π° находится Π² Ρ„Π°ΠΉΠ»Π΅ `config.example.json`. Π§Ρ‚ΠΎΠ±Ρ‹ ΡƒΠΊΠ°Π·Π°Ρ‚ΡŒ свой ΠΊΠΎΠ½Ρ„ΠΈΠ³, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅ cli-Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ `--config`. -Π‘ΠΌ. [`SETUP-RU.md`](https://git.macaw.me/skunky/SkunkyArt/src/branch/master/SETUP-RU.md) для ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠΈ ΠΎ настройки Ρ„Ρ€ΠΎΠ½Ρ‚Π΅Π½Π΄Π°. +Π‘ΠΌ. [`SETUP-RU.md`](/skunky/SkunkyArt/src/branch/master/SETUP-RU.md) для ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠΈ ΠΎ настройки Ρ„Ρ€ΠΎΠ½Ρ‚Π΅Π½Π΄Π°. ## Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ инстанса Π² список Π§Ρ‚ΠΎΠ±Ρ‹ это ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ, Π²Ρ‹ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π»ΠΈΠ±ΠΎ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ PR, Π΄ΠΎΠ±Π°Π²ΠΈΠ² Π² Ρ„Π°ΠΉΠ»Ρ‹ `instances.json` ΠΈ `INSTANCES.md` свой инстанс (ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π²ΠΎΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ cli-Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ΠΎΠΌ `--add-instance`, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ автоматичСски это сдСлаСт), Π»ΠΈΠ±ΠΎ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ Issue, ΠΈΠ»ΠΈ ΡΠΎΠΎΠ±Ρ‰ΠΈΡ‚ΡŒ ΠΎ Π½Ρ‘ΠΌ Π² ΠΊΠΎΠΌΠ½Π°Ρ‚Π΅ Π² Matrix. Π£Ρ‡Ρ‚ΠΈΡ‚Π΅, Ρ‡Ρ‚ΠΎ ваш инстанс Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΠΎΠ±Π»ΡŽΡΡ‚ΠΈ слСдущиС ΠΏΡ€Π°Π²ΠΈΠ»Π°: 1. Π˜Π½ΡΡ‚Π°Π½Ρ Π½Π΅ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Cloudflare ΠΈΡ‚ΠΏ. diff --git a/SETUP-RU.md b/SETUP-RU.md index 6ff3c23..994f193 100644 --- a/SETUP-RU.md +++ b/SETUP-RU.md @@ -1,4 +1,4 @@ -[English version πŸ‡¬πŸ‡§](https://git.macaw.me/skunky/SkunkyArt/src/branch/master/SETUP.md) +[English version πŸ‡¬πŸ‡§](/skunky/SkunkyArt/src/branch/master/SETUP.md) # Π•Π΄ΠΈΠ½ΠΈΡ†Ρ‹ измСрСния Π Π°Π·ΠΌΠ΅Ρ€ Ρ„Π°ΠΉΠ»Π° Π² кСшС измСряСтся Π² ΠΌΠ΅Π³Π°Π±Π°ΠΉΡ‚Π°Ρ….
diff --git a/SETUP.md b/SETUP.md index ab30165..169e7a0 100644 --- a/SETUP.md +++ b/SETUP.md @@ -1,4 +1,4 @@ -[ВСрсия Π½Π° русском языкС πŸ‡·πŸ‡Ί](https://git.macaw.me/skunky/SkunkyArt/src/branch/master/SETUP-RU.md) +[ВСрсия Π½Π° русском языкС πŸ‡·πŸ‡Ί](/skunky/SkunkyArt/src/branch/master/SETUP-RU.md) # Units Maximum file size in megabytes, requires numeric value.
diff --git a/TODO.md b/TODO.md index ee50111..84678b0 100644 --- a/TODO.md +++ b/TODO.md @@ -1,19 +1,21 @@ # v1.3.x * ΠŸΠΎΡ‡ΠΈΡΡ‚ΠΈΡ‚ΡŒ Π³ΠΎΠ²Π½ΠΎΠΊΠΎΠ΄ +* ~~Π‘Π΄Π΅Π»Π°Ρ‚ΡŒ ΠΏΠΎΡ€Ρ‚ ΠΏΠΎΠ΄ FreeBSD~~ βœ”οΈ * **Π”ΠΎΠ΄Π΅Π»Π°Ρ‚ΡŒ парсинг описания** -* Π˜Π·Π±Π°Π²ΠΈΡ‚ΡŒΡΡ ΠΎΡ‚ Ρ…Π°Ρ€Π΄ΠΊΠΎΠ΄Π° ΠΏΠΎΠ΄ Linux -* ~~Π Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ стрипы Π² Π΅ΠΆΠ΅Π΄Π½Π΅Π²Π½Ρ‹Ρ… Π°Ρ€Ρ‚Π°Ρ…~~ +* ~~Π Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ стрипы Π² Π΅ΠΆΠ΅Π΄Π½Π΅Π²Π½Ρ‹Ρ… Π°Ρ€Ρ‚Π°Ρ…~~ βœ”οΈ +* ~~Π˜ΡΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ Π±Π°Π³ с Π½Π°Π²ΠΈΠ³Π°Ρ†ΠΈΠ΅ΠΉ ΠΏΠΎ страницам~~ βœ”οΈ * Π‘Π΄Π΅Π»Π°Ρ‚ΡŒ Π½ΠΎΡ€ΠΌΠ°Π»ΡŒΠ½ΠΎΠ΅ ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ ошибок -* ~~Π˜ΡΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ Π±Π°Π³ с Π½Π°Π²ΠΈΠ³Π°Ρ†ΠΈΠ΅ΠΉ ΠΏΠΎ страницам~~ -* ~~Π‘Π΄Π΅Π»Π°Ρ‚ΡŒ Π΅Π΄ΠΈΠ½ΠΈΡ†Ρ‹ Π² ΠΊΠΎΠ½Ρ„ΠΈΠ³Π΅ Π±ΠΎΠ»Π΅Π΅ понятными~~ -* Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ Π²ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ Ρ‚Π΅ΠΌΠΏΠ»Π΅ΠΉΡ‚Ρ‹ Π² Π±ΠΈΠ½Π°Ρ€Π½ΠΈΠΊ -* ~~Π Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΌΠΈΠ½ΠΈΠ°Ρ‚ΡŽΡ€Ρ‹ ΠΈ ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ CSS ΠΏΠΎΠ΄ малСнькиС экраны~~ +* ~~Π‘Π΄Π΅Π»Π°Ρ‚ΡŒ Π΅Π΄ΠΈΠ½ΠΈΡ†Ρ‹ Π² ΠΊΠΎΠ½Ρ„ΠΈΠ³Π΅ Π±ΠΎΠ»Π΅Π΅ понятными~~ βœ”οΈ +* Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ просмотр ΠΏΠΎΠ½Ρ€Π°Π²ΠΈΠ²ΡˆΠΈΡ…ΡΡ Π°Ρ€Ρ‚ΠΎΠ² ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŽ +* Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ Π²ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ Ρ‚Π΅ΠΌΠΏΠ»Π΅ΠΉΡ‚Ρ‹ Π² Π±ΠΈΠ½Π°Ρ€Π½ΠΈΠΊ [P] +* ~~Π Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΌΠΈΠ½ΠΈΠ°Ρ‚ΡŽΡ€Ρ‹ ΠΈ ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ CSS ΠΏΠΎΠ΄ малСнькиС экраны~~ βœ”οΈ * ΠΠ°ΠΏΠΈΡΠ°Ρ‚ΡŒ Makefile ΠΈ скрипт для автоматичСского развёртывания инстанса * **Π Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ ΠΊΠΎΠ½Ρ‚Π΅Π½Ρ‚Π°, ΠΎΡ‚Π»ΠΈΡ‡Π½ΠΎΠ³ΠΎ ΠΎΡ‚ ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΎΠΊ (Π²ΠΈΠ΄Π΅ΠΎ, Π°ΡƒΠ΄ΠΈΠΎ, etc)** * Π˜ΡΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ Π±Π°Π³ с эмодТи, ΠΊΠΎΠ³Π΄Π° Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ кастомныС эмоции ΠΌΠΎΠ³ΡƒΡ‚ Π½Π΅ ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°Ρ‚ΡŒΡΡ -* Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Ρ„Π»Π°Π³ сборки, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΏΠΎΠ·Π²ΠΎΠ»ΠΈΡ‚ ΡΠΎΠ±Ρ€Π°Ρ‚ΡŒ Π±ΠΈΠ½Π°Ρ€Π½ΠΈΠΊ со встроСнными Ρ‚Π΅ΠΌΠΏΠ»Π΅ΠΉΡ‚Π°ΠΌΠΈ -* Π£Π»ΡƒΡ‡ΡˆΠΈΡ‚ΡŒ систСму ΠΊΠ΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡ: Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Ρ€Π΅ΠΉΡ‚ΠΈΠ½Π³ для удалСния ΠΈ ΠΊΠΎΠΏΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ Π² ΠžΠ—Π£ +* ~~Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ &filename, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹Π΄Π°Π²Π°Ρ‚ΡŒ Ρ„Π°ΠΉΠ» с Π½ΠΎΡ€ΠΌΠ°Π»ΡŒΠ½ΠΎ выглядСщСм ΠΈΠΌΠ΅Π½Π΅ΠΌ~~ βœ”οΈ +* ~~Π£Π»ΡƒΡ‡ΡˆΠΈΡ‚ΡŒ систСму ΠΊΠ΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡ: Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Ρ€Π΅ΠΉΡ‚ΠΈΠ½Π³ для удалСния ΠΈ ΠΊΠΎΠΏΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ Π² ΠžΠ—Π£~~ # v1.4 * Π Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ API * Π Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ Ρ‚Π΅ΠΌΡ‹ +* ΠŸΠ΅Ρ€Π΅ΠΉΡ‚ΠΈ Π½Π° Π°Ρ€Π΅Π½Ρ‹ Π² кСшС * Π Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ многоязычный интСрфСйс \ No newline at end of file diff --git a/app/cache.go b/app/cache.go new file mode 100644 index 0000000..dedba83 --- /dev/null +++ b/app/cache.go @@ -0,0 +1,132 @@ +// TODO: Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΊΠ΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ JSON ΠΈ ΠΏΠΎΡ‡ΠΈΡΡ‚ΠΈΡ‚ΡŒ ΠΊΠΎΠ΄ +package app + +import ( + "crypto/sha1" + "encoding/hex" + "io" + "os" + "strings" + "sync" + "syscall" + "time" +) + +type file struct { + Score int + Content []byte +} + +var tempFS = make(map[[20]byte]*file) +var mx = &sync.RWMutex{} + +func (s skunkyart) DownloadAndSendMedia(subdomain, path string) { + var url strings.Builder + url.WriteString("https://images-wixmp-") + url.WriteString(subdomain) + url.WriteString(".wixmp.com/") + url.WriteString(path) + url.WriteString("?token=") + url.WriteString(s.Args.Get("token")) + + var response []byte + + switch { + case CFG.Cache.Enabled: + fileName := sha1.Sum([]byte(subdomain + path)) + filePath := CFG.Cache.Path + "/" + hex.EncodeToString(fileName[:]) + + mx.Lock() + if tempFS[fileName] == nil { + tempFS[fileName] = &file{} + } + f := *tempFS[fileName] + mx.Unlock() + + if f.Content != nil { + f.Score += 2 + } 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)) + } else { + s.ReturnHTTPError(dwnld.Status) + return + } + } else { + file, e := io.ReadAll(file) + try(e) + f.Content = file + } + + go func() { + defer restore() + for { + time.Sleep(1 * time.Minute) + + mx.Lock() + if tempFS[fileName].Score <= 0 { + delete(tempFS, fileName) + mx.Unlock() + return + } + tempFS[fileName].Score-- + mx.Unlock() + } + }() + } + + mx.Lock() + tempFS[fileName] = &f + mx.Unlock() + response = f.Content + case CFG.Proxy: + dwnld := Download(url.String()) + if dwnld.Status != 200 { + s.ReturnHTTPError(dwnld.Status) + return + } + response = dwnld.Body + default: + s.Writer.WriteHeader(403) + response = []byte("Sorry, butt proxy on this instance are disabled.") + } + + s.Writer.Write(response) +} + +func InitCacheSystem() { + c := &CFG.Cache + os.Mkdir(c.Path, 0700) + for { + dir, e := os.Open(c.Path) + try(e) + stat, e := dir.Stat() + try(e) + + dirnames, e := dir.Readdirnames(-1) + try(e) + for _, a := range dirnames { + a = c.Path + "/" + a + if c.Lifetime != "" { + now := time.Now().UnixMilli() + + f, _ := os.Stat(a) + stat := f.Sys().(*syscall.Stat_t) + time := statTime(stat) + + if time+lifetimeParsed <= now { + try(os.RemoveAll(a)) + } + } + if c.MaxSize != 0 && stat.Size() > c.MaxSize { + try(os.RemoveAll(a)) + } + } + + dir.Close() + time.Sleep(time.Second * time.Duration(c.UpdateInterval)) + } +} diff --git a/app/cli.go b/app/cli.go index 2247223..261e103 100644 --- a/app/cli.go +++ b/app/cli.go @@ -78,16 +78,16 @@ func addInstance() { try(err) defer instancesJson.Close() - instances, err := os.OpenFile("INSTANCES.md", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + instancesFile, err := os.OpenFile("INSTANCES.md", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) try(err) - defer instances.Close() + defer instancesFile.Close() for { - if Templates["instances.json"] == "" { + if string(instances) == "" { print("\rDownloading instance list...") } else { println("\r\033[2KDownloaded!") - try(json.Unmarshal([]byte(Templates["instances.json"]), &settingsVar)) + try(json.Unmarshal(instances, &settingsVar)) settingsVar.Instances = append(settingsVar.Instances, settings{ Title: prompt("Title", true), @@ -113,51 +113,46 @@ func addInstance() { settingsVar := &settingsVar.Instances[len(settingsVar.Instances)-1] var mdstr bytes.Buffer - mdstr.WriteString("\n|") - if settingsVar.Urls.Clearnet != "" { - mdstr.WriteString("[") - mdstr.WriteString(settingsVar.Title) - mdstr.WriteString("](") - mdstr.WriteString(settingsVar.Urls.Clearnet) - mdstr.WriteString(")") - } else { - mdstr.WriteString(settingsVar.Title) + mdbuilder := func(yes bool, link string, title string) { + switch { + case yes && (title != "" && link != ""): + mdstr.WriteString("[") + mdstr.WriteString(title) + mdstr.WriteString("](") + mdstr.WriteString(link) + mdstr.WriteString(")") + case yes && link != "": + mdstr.WriteString("[Yes](") + mdstr.WriteString(link) + mdstr.WriteString(")") + case yes: + mdstr.WriteString("Yes") + default: + mdstr.WriteString("No") + } + mdstr.WriteString("|") } - mdstr.WriteString("|") + + mdstr.WriteString("\n|") + mdbuilder(settingsVar.Urls.Clearnet != "", settingsVar.Urls.Clearnet, settingsVar.Title) urls := []string{settingsVar.Urls.Ygg, settingsVar.Urls.I2P, settingsVar.Urls.Tor} for i, l := 0, len(urls); i < l; i++ { url := urls[i] - if url != "" { - mdstr.WriteString("[Yes](") - mdstr.WriteString(url) - mdstr.WriteString(")|") - } else { - mdstr.WriteString("No|") - } + mdbuilder(url != "", url, "") } settings := []bool{settingsVar.Settings.Nsfw, settingsVar.Settings.Proxy} for i, l := 0, len(settings); i < l; i++ { - if settings[i] { - mdstr.WriteString("Yes|") - } else { - mdstr.WriteString("No|") - } + mdbuilder(settings[i], "", "") } - if settingsVar.ModifiedSrc != "" { - mdstr.WriteString("[Yes](") - mdstr.WriteString(settingsVar.ModifiedSrc) - mdstr.WriteString(")|") - } else { - mdstr.WriteString("No|") - } + mdbuilder(settingsVar.ModifiedSrc != "", settingsVar.ModifiedSrc, "") mdstr.WriteString(settingsVar.Country) mdstr.WriteString("|") - instances.Write(mdstr.Bytes()) + instancesFile.Write(mdstr.Bytes()) break } time.Sleep(500 * time.Millisecond) diff --git a/app/config.go b/app/config.go index a6ac756..7d159e7 100644 --- a/app/config.go +++ b/app/config.go @@ -4,6 +4,7 @@ import ( "encoding/json" "os" "regexp" + "skunkyart/static" "strconv" "time" @@ -24,9 +25,9 @@ type config struct { URI string `json:"uri"` Cache cache_config Proxy, Nsfw bool - UserAgent string `json:"user-agent"` - DownloadProxy string `json:"download-proxy"` - Dirs []string `json:"dirs-to-memory"` + UserAgent string `json:"user-agent"` + DownloadProxy string `json:"download-proxy"` + StaticPath string `json:"static-path"` } var CFG = config{ @@ -38,10 +39,10 @@ var CFG = config{ Path: "cache", UpdateInterval: 1, }, - Dirs: []string{"html", "css", "misc"}, - UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36", - Proxy: true, - Nsfw: true, + StaticPath: "static", + UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36", + Proxy: true, + Nsfw: true, } var lifetimeParsed int64 @@ -56,6 +57,7 @@ func ExecuteConfig() { exit("Incompatible settings detected: cannot use caching media content without proxy", 1) } + static.StaticPath = CFG.StaticPath if CFG.Cache.Enabled { if CFG.Cache.Lifetime != "" { var duration int64 diff --git a/app/parsers.go b/app/parsers.go index b39e172..f93c18d 100644 --- a/app/parsers.go +++ b/app/parsers.go @@ -41,7 +41,9 @@ func (s skunkyart) ParseComments(c devianter.Comments) string { cmmts.WriteString(" ") if x.Parent > 0 { - cmmts.WriteString(` In reply to `) if replied[x.Parent] == "" { @@ -80,7 +82,7 @@ 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, 320), ParseMedia(data.Media); !(data.NSFW && !CFG.Nsfw) { + if preview, fullview := ParseMedia(data.Media, data.Title, 320), ParseMedia(data.Media, data.Title); !(data.NSFW && !CFG.Nsfw) { if allowAtom && s.Atom { id := strconv.Itoa(data.ID) listContent.WriteString(``) @@ -284,7 +286,7 @@ func ParseDescription(dscr devianter.Text) string { parsedDescription.WriteString(` c.MaxSize { - try(os.RemoveAll(a)) - } - } - - dir.Close() - time.Sleep(time.Second * time.Duration(CFG.Cache.UpdateInterval)) - } -} - -func CopyTemplatesToMemory() { - for _, dirname := range CFG.Dirs { - dir, e := os.ReadDir(dirname) - tryWithExitStatus(e, 1) - - for _, x := range dir { - file, e := os.ReadFile(dirname + "/" + x.Name()) - tryWithExitStatus(e, 1) - Templates[x.Name()] = string(file) - } - } -} - /* PARSING HELPERS */ -func ParseMedia(media devianter.Media, thumb ...int) string { - url := devianter.UrlFromMedia(media, thumb...) - if len(url) != 0 && CFG.Proxy { - url = url[21:] - dot := strings.Index(url, ".") +func ParseMedia(media devianter.Media, filename string, thumb ...int) string { + mediaUrl := devianter.UrlFromMedia(media, thumb...) + if len(mediaUrl) != 0 && CFG.Proxy { + mediaUrl = mediaUrl[21:] + dot := strings.Index(mediaUrl, ".") - return UrlBuilder("media", "file", url[:dot], url[dot+11:]) + return UrlBuilder("media", "file", mediaUrl[:dot], mediaUrl[dot+11:], "&filename=", url.QueryEscape(filename)) } - return url + return mediaUrl } func ConvertDeviantArtUrlToSkunkyArt(url string) (output string) { @@ -255,7 +181,9 @@ func (s skunkyart) NavBase(c DeviationList) string { list.WriteString("
") prevrev := func(msg string, page int, onpage bool) { if !onpage { - list.WriteString(`
`) @@ -180,7 +180,7 @@ func (s skunkyart) GRUser() { } folders.WriteString("
") - folders.WriteString(` - - - SkunkyArt | Daily Deviations - - - -
-
-

HOME | DD | RSS

-
- - - -
-
- {{if ne .Templates.DDStrips ""}} -

# Strips

- {{.Templates.DDStrips}} - {{end}} -

# Content

- {{.Templates.SomeList}} -
- \ No newline at end of file diff --git a/html/search.htm b/html/search.htm deleted file mode 100644 index df1cf85..0000000 --- a/html/search.htm +++ /dev/null @@ -1,30 +0,0 @@ - - - - SkunkyArt | Search "{{.QueryRaw}}" - - - -
-
-

HOME | DD

-
- - - -
-
- {{if ne .Templates.Search.List ""}} - {{if ne .Templates.Search.Content.Total 0}} -

Results by request '{{.QueryRaw}}': {{.Templates.Search.Content.Total}}

- {{end}} - {{.Templates.Search.List}} - {{else}} -

No results :(

- {{end}} -
- \ No newline at end of file diff --git a/main.go b/main.go index 0224816..190d79b 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "skunkyart/app" + "skunkyart/static" "time" "git.macaw.me/skunky/devianter" @@ -12,7 +13,7 @@ func main() { app.ExecuteCommandLineArguments() app.ExecuteConfig() - app.CopyTemplatesToMemory() + static.CopyTemplatesToMemory() go func() { for { diff --git a/css/skunky.css b/static/css/skunky.css similarity index 94% rename from css/skunky.css rename to static/css/skunky.css index 2126968..bdc894c 100644 --- a/css/skunky.css +++ b/static/css/skunky.css @@ -22,12 +22,19 @@ header form { header { display: flex; } +form { + font-size: 0; + border: solid #164e3e 1px; + max-width: fit-content; +} form input, button, select { background-color: #134134; padding: 5px; color: whitesmoke; - border: 0px; - border-radius: 1px; + border: 0; +} +input:focus { + outline: none; } /* BLOCKS */ @@ -146,6 +153,8 @@ form input, button, select { header form { font-size: 60%; + max-width: unset; + border: 0; } header, center { text-align: center; diff --git a/html/about.htm b/static/html/about.htm similarity index 74% rename from html/about.htm rename to static/html/about.htm index edb576d..d2278d6 100644 --- a/html/about.htm +++ b/static/html/about.htm @@ -1,23 +1,8 @@ - - SkunkyArt - - - + {{template "head" .}}
-
-

HOME | DD

-
- - - -
-
+ {{template "header" .}}

SkunkyArt is an alternative frontend for deviantart.com, written in Go.

diff --git a/static/html/daily.htm b/static/html/daily.htm new file mode 100644 index 0000000..b3b6044 --- /dev/null +++ b/static/html/daily.htm @@ -0,0 +1,13 @@ + + + {{template "head" . }} +
+ {{template "header" . }} + {{if ne .Templates.DDStrips ""}} +

# Strips

+ {{.Templates.DDStrips}} + {{end}} +

# Content

+ {{.Templates.SomeList}} +
+ \ No newline at end of file diff --git a/html/deviantion.htm b/static/html/deviantion.htm similarity index 72% rename from html/deviantion.htm rename to static/html/deviantion.htm index 50710b6..f357dd7 100644 --- a/html/deviantion.htm +++ b/static/html/deviantion.htm @@ -1,24 +1,8 @@ - - SkunkyArt | {{.Templates.Deviation.Post.Deviation.Author.Username}} - {{.Templates.Deviation.Post.Deviation.Title}} - - - - + {{template "head" . }}
-
-

HOME | DD

-
- - - -
-
+ {{template "header" . }}
{{.Templates.Deviation.Post.Deviation.Author.Username}} β€” {{if (.Templates.Deviation.Post.Deviation.DD)}} diff --git a/html/gruser.htm b/static/html/gruser.htm similarity index 87% rename from html/gruser.htm rename to static/html/gruser.htm index a219e36..c8227ef 100644 --- a/html/gruser.htm +++ b/static/html/gruser.htm @@ -1,21 +1,11 @@ - - SkunkyArt | - {{if eq .Type 'a'}} - {{.Templates.GroupUser.GR.Owner.Username}} - {{else}} - gallery of {{.Templates.GroupUser.GR.Owner.Username}} - {{end}} - - - - + {{template "head" . }}

HOME | DD - | Gallery{{else}}about">About{{end}} - | RSS

+ | Gallery{{else}}about">About{{end}} + | RSS
diff --git a/static/html/head.htm b/static/html/head.htm new file mode 100644 index 0000000..f7f6b91 --- /dev/null +++ b/static/html/head.htm @@ -0,0 +1,24 @@ +{{define "head"}} + + SkunkyArt | + {{if eq .Endpoint "search"}} + "{{.QueryRaw}}" + {{else if eq .Endpoint "post"}} + {{.Templates.Deviation.Post.Deviation.Author.Username}} β€” {{.Templates.Deviation.Post.Deviation.Title}} + {{else if eq .Type 'a'}} + {{if .Templates.GroupUser.GR.Owner.Username}} + {{.Templates.GroupUser.GR.Owner.Username}} + {{else}} + gallery of {{.Templates.GroupUser.GR.Owner.Username}} + {{end}} + {{else}} + {{.Endpoint}} + {{end}} + + + + + + + +{{end}} \ No newline at end of file diff --git a/static/html/header.htm b/static/html/header.htm new file mode 100644 index 0000000..f8537a3 --- /dev/null +++ b/static/html/header.htm @@ -0,0 +1,14 @@ +{{define "header"}} +
+

HOME | DD {{if eq .Endpoint "dd"}}| RSS{{end}}

+ + + + + +
+{{end}} \ No newline at end of file diff --git a/html/index.htm b/static/html/index.htm similarity index 61% rename from html/index.htm rename to static/html/index.htm index 08feba5..78f1d8a 100644 --- a/html/index.htm +++ b/static/html/index.htm @@ -2,12 +2,12 @@ SkunkyArt - - + +
-
+
-

Daily Deviations | About | Source Code

+

Daily Deviations | About | Source Code

\ No newline at end of file diff --git a/static/html/search.htm b/static/html/search.htm new file mode 100644 index 0000000..74bbd38 --- /dev/null +++ b/static/html/search.htm @@ -0,0 +1,16 @@ + + + {{template "head" . }} +
+ {{template "header" . }} + + {{if ne .Templates.Search.List ""}} + {{if ne .Templates.Search.Content.Total 0}} +

Results by request '{{.QueryRaw}}': {{.Templates.Search.Content.Total}}

+ {{end}} + {{.Templates.Search.List}} + {{else}} +

No results :(

+ {{end}} +
+ \ No newline at end of file diff --git a/misc/logo.png b/static/images/logo.png similarity index 100% rename from misc/logo.png rename to static/images/logo.png diff --git a/static/templates-noembed.go b/static/templates-noembed.go new file mode 100644 index 0000000..7a3c541 --- /dev/null +++ b/static/templates-noembed.go @@ -0,0 +1,148 @@ +//go:build !embed +// +build !embed + +package static + +import ( + "bytes" + "io/fs" + "os" + "strings" + "time" +) + +var Templates FS + +type file struct { + path string + name string + content []byte +} + +var templateNames = []string{} +var templates = make(map[string][]file) +var StaticPath string + +func CopyTemplatesToMemory() { + baseDir, err := os.ReadDir(StaticPath) + try(err) + + for _, c := range baseDir { + if c.IsDir() { + templateNames = append(templateNames, c.Name()) + + var filePath strings.Builder + filePath.WriteString(StaticPath) + filePath.WriteString("/") + filePath.WriteString(c.Name()) + + dir, err := os.ReadDir(filePath.String()) + try(err) + + filePath.WriteString("/") + for _, cd := range dir { + f, err := os.ReadFile(filePath.String() + cd.Name()) + try(err) + templates[c.Name()] = append(templates[c.Name()], file{ + content: f, + name: cd.Name(), + path: c.Name() + "/" + cd.Name(), + }) + } + } + } +} + +type FS struct{} + +func (FS) Open(name string) (fs.File, error) { + for i, l := 0, len(templateNames); i < l; i++ { + for _, x := range templates[templateNames[i]] { + if x.content != nil && name == x.path { + return &File{ + name: x.path, + content: bytes.NewBuffer(x.content), + }, nil + } + } + } + return nil, &fs.PathError{} +} + +func (FS) Glob(pattern string) ([]string, error) { + trimmed := strings.Split(pattern, "/") + var matches = []string{} + for x, s := range templates { + for i, l := 0, len(s); i < l && trimmed[0] == x; i++ { + s := s[i] + matches = append(matches, s.path) + } + } + if len(matches) != 0 { + return matches, nil + } + return nil, &fs.PathError{} +} + +func try(err error) { + if err != nil { + println(err.Error()) + os.Exit(1) + } +} + +/* сдСлано Π½Π° основС https://github.com/psanford/memfs; трСбуСтся для ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎΠΉ Ρ€Π°Π±ΠΎΡ‚Ρ‹ templates.ParseFS */ +type fileInfo struct { + name string +} + +func (fi fileInfo) Name() string { + return fi.name +} + +func (fi fileInfo) Size() int64 { + return 4096 +} + +func (fileInfo) Mode() fs.FileMode { + return 0 +} + +func (fileInfo) ModTime() time.Time { + return time.Time{} +} + +func (fileInfo) IsDir() bool { + return false +} + +func (fileInfo) Sys() interface{} { + return nil +} + +type File struct { + name string + content *bytes.Buffer + closed bool +} + +func (f *File) Stat() (fs.FileInfo, error) { + return fileInfo{ + name: f.name, + }, nil +} + +func (f *File) Read(b []byte) (int, error) { + if f.closed { + return 0, fs.ErrClosed + } + return f.content.Read(b) +} + +func (f *File) Close() error { + if f.closed { + return fs.ErrClosed + } + f.closed = true + return nil +} diff --git a/static/templates.go b/static/templates.go new file mode 100644 index 0000000..28a057a --- /dev/null +++ b/static/templates.go @@ -0,0 +1,16 @@ +//go:build embed +// +build embed + +package static + +import "embed" + +//go:embed * +var Templates embed.FS +var Enabled bool = true + +var StaticPath string + +func CopyTemplatesToMemory() { + _ = StaticPath +}