Commit 42ea2980f9

42ea2980f9be365c97b17d5273270fa1862ee9fc

parent: 4d166ad5f9

Unsigned

lost+skunk <skunky@ebloid.ru> · 2024-07-29 22:20 UTC

Парсер медии с кастомным разрешением

Layout: unified · split

.vscode/launch.json deleted −16
@@ -1,16 +0,0 @@
1{
2 // Use IntelliSense to learn about possible attributes.
3 // Hover to view descriptions of existing attributes.
4 // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 "version": "0.2.0",
6 "configurations": [
7 {
8 "name": "Launch Package",
9 "type": "go",
10 "request": "launch",
11 "mode": "debug",
12 "program": "${fileDirname}",
13 "showLog": true
14 }
15 ]
16}\ No newline at end of file
.vscode/settings.json deleted −5
@@ -1,5 +0,0 @@
1{
2 "go.toolsEnvVars": {
3 "GOROOT": ""
4 }
5}\ No newline at end of file
README.md +2 −2
@@ -1,9 +1,9 @@
1# Devianter 1# Devianter
2 2
3[![Please don't upload to GitHub](https://nogithub.codeberg.page/badge.svg)](https://nogithub.codeberg.page) [![Go Reference](https://pkg.go.dev/badge/git.macaw.me/skunky/devianter.svg)](https://pkg.go.dev/git.macaw.me/skunky/devianter) [![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0) 3[![Please don't upload to GitHub](https://nogithub.codeberg.page/badge.svg)](https://nogithub.codeberg.page) [![Go Reference](https://pkg.go.dev/badge/git.macaw.me/skunky/devianter.svg)](https://pkg.go.dev/git.macaw.me/skunky/devianter)
4 4
5 5
6A DeviantART API library for Go. 6A DeviantART guest API library for Go.
7 7
8 8
9I'll probably write up some documentation, but more on that later. 9I'll probably write up some documentation, but more on that later.
comments.go +3 −3
@@ -11,7 +11,7 @@ type Thread struct {
11 ID int `json:"commentId"` 11 ID int `json:"commentId"`
12 Parent int `json:"parentId"` 12 Parent int `json:"parentId"`
13 13
14 Posted time 14 Posted timeStamp
15 Author bool `json:"isAuthorHighlited"` 15 Author bool `json:"isAuthorHighlited"`
16 16
17 Desctiption string 17 Desctiption string
@@ -35,7 +35,7 @@ type Comments struct {
35} 35}
36 36
37// 1 - комментарии поста; 4 - комментарии на стене группы или пользователя 37// 1 - комментарии поста; 4 - комментарии на стене группы или пользователя
38func CommentsFunc(postid string, cursor string, page int, typ int) (cmmts Comments) { 38func GetComments(postid string, cursor string, page int, typ int) (cmmts Comments) {
39 for x := 0; x <= page; x++ { 39 for x := 0; x <= page; x++ {
40 ujson( 40 ujson(
41 "dashared/comments/thread?typeid="+strconv.Itoa(typ)+ 41 "dashared/comments/thread?typeid="+strconv.Itoa(typ)+
@@ -60,7 +60,7 @@ func CommentsFunc(postid string, cursor string, page int, typ int) (cmmts Commen
60 } 60 }
61 61
62 e := json.Unmarshal([]byte(m), &content) 62 e := json.Unmarshal([]byte(m), &content)
63 err(e) 63 try(e)
64 64
65 for _, a := range content.Blocks { 65 for _, a := range content.Blocks {
66 cmmts.Thread[i].Comment = a.Text 66 cmmts.Thread[i].Comment = a.Text
deviantion.go +23 −9
@@ -4,26 +4,26 @@ import (
4 "encoding/json" 4 "encoding/json"
5 "strconv" 5 "strconv"
6 "strings" 6 "strings"
7 timelib "time" 7 "time"
8) 8)
9 9
10// хрень для парсинга времени публикации 10// хрень для парсинга времени публикации
11type time struct { 11type timeStamp struct {
12 timelib.Time 12 time.Time
13} 13}
14 14
15func (t *time) UnmarshalJSON(b []byte) (err error) { 15func (t *timeStamp) UnmarshalJSON(b []byte) (err error) {
16 if b[0] == '"' && b[len(b)-1] == '"' { 16 if b[0] == '"' && b[len(b)-1] == '"' {
17 b = b[1 : len(b)-1] 17 b = b[1 : len(b)-1]
18 } 18 }
19 t.Time, err = timelib.Parse("2006-01-02T15:04:05-0700", string(b)) 19 t.Time, err = time.Parse("2006-01-02T15:04:05-0700", string(b))
20 return 20 return
21} 21}
22 22
23// самая главная структура для поста 23// самая главная структура для поста
24type Deviation struct { 24type Deviation struct {
25 Title, Url, License string 25 Title, Url, License string
26 PublishedTime time 26 PublishedTime timeStamp
27 ID int `json:"deviationId"` 27 ID int `json:"deviationId"`
28 28
29 NSFW bool `json:"isMature"` 29 NSFW bool `json:"isMature"`
@@ -82,7 +82,7 @@ type Post struct {
82 82
83 ParsedComments []struct { 83 ParsedComments []struct {
84 Author string 84 Author string
85 Posted time 85 Posted timeStamp
86 Replies, Likes int 86 Replies, Likes int
87 } 87 }
88 88
@@ -90,12 +90,25 @@ type Post struct {
90} 90}
91 91
92// преобразование урла в правильный 92// преобразование урла в правильный
93func UrlFromMedia(m Media) string { 93func UrlFromMedia(m Media, thumb ...int) string {
94 var url strings.Builder 94 var url strings.Builder
95
96 subtractWidthHeight := func(to int, target ...*int) {
97 for i, l := 0, len(target); i < l; i++ {
98 for x := *target[i]; x > to; x -= to {
99 *target[i] = x
100 }
101 }
102 }
103
95 for _, t := range m.Types { 104 for _, t := range m.Types {
96 if t.T == "fullview" { 105 if t.T == "fullview" {
97 url.WriteString(m.BaseUri) 106 url.WriteString(m.BaseUri)
98 if m.BaseUri[len(m.BaseUri)-3:] != "gif" && t.W*t.H < 33177600 { 107 if m.BaseUri[len(m.BaseUri)-3:] != "gif" && t.W*t.H < 33177600 {
108 if len(thumb) != 0 {
109 subtractWidthHeight(thumb[0], &t.W, &t.H)
110 }
111
99 url.WriteString("/v1/fit/w_") 112 url.WriteString("/v1/fit/w_")
100 url.WriteString(strconv.Itoa(t.W)) 113 url.WriteString(strconv.Itoa(t.W))
101 url.WriteString(",h_") 114 url.WriteString(",h_")
@@ -110,11 +123,12 @@ func UrlFromMedia(m Media) string {
110 } 123 }
111 } 124 }
112 } 125 }
126
113 return url.String() 127 return url.String()
114} 128}
115 129
116// для работы функции нужно ID поста и имя пользователя. 130// для работы функции нужно ID поста и имя пользователя.
117func DeviationFunc(id string, user string) Post { 131func GetDeviation(id string, user string) Post {
118 var st Post 132 var st Post
119 ujson( 133 ujson(
120 "dadeviation/init?deviationid="+id+"&username="+user+"&type=art&include_session=false&expand=deviation.related&preload=true", 134 "dadeviation/init?deviationid="+id+"&username="+user+"&type=art&include_session=false&expand=deviation.related&preload=true",
misc.go +20 −18
@@ -4,13 +4,16 @@ import (
4 "errors" 4 "errors"
5 "log" 5 "log"
6 "math" 6 "math"
7 u "net/url" 7 "net/url"
8 "strconv" 8 "strconv"
9 "strings" 9 "strings"
10) 10)
11 11
12/* AVATARS AND EMOJIS */ 12/* AVATARS AND EMOJIS */
13func AEmedia(name string, t rune) (string, error) { 13func AEmedia(name string, t rune) (string, error) {
14 if len(name) < 2 {
15 return "", errors.New("name must be specified")
16 }
14 // список всех возможных расширений 17 // список всех возможных расширений
15 var extensions = [3]string{ 18 var extensions = [3]string{
16 ".jpg", 19 ".jpg",
@@ -25,9 +28,10 @@ func AEmedia(name string, t rune) (string, error) {
25 switch t { 28 switch t {
26 case 'a': 29 case 'a':
27 b.WriteString("https://a.deviantart.net/avatars-big/") 30 b.WriteString("https://a.deviantart.net/avatars-big/")
28 b.WriteString(name[:1]) 31 name_without_dashes := strings.ReplaceAll(name, "-", "_")
32 b.WriteString(name_without_dashes[:1])
29 b.WriteString("/") 33 b.WriteString("/")
30 b.WriteString(name[1:2]) 34 b.WriteString(name_without_dashes[1:2])
31 b.WriteString("/") 35 b.WriteString("/")
32 case 'e': 36 case 'e':
33 b.WriteString("https://e.deviantart.net/emoticons/") 37 b.WriteString("https://e.deviantart.net/emoticons/")
@@ -60,7 +64,7 @@ type DailyDeviations struct {
60 Deviations []Deviation 64 Deviations []Deviation
61} 65}
62 66
63func DailyDeviationsFunc(page int) (dd DailyDeviations) { 67func GetDailyDeviations(page int) (dd DailyDeviations) {
64 ujson("dabrowse/networkbar/rfy/deviations?page="+strconv.Itoa(page), &dd) 68 ujson("dabrowse/networkbar/rfy/deviations?page="+strconv.Itoa(page), &dd)
65 return 69 return
66} 70}
@@ -74,23 +78,21 @@ type Search struct {
74 ResultsGalleryTemp []Deviation `json:"results"` 78 ResultsGalleryTemp []Deviation `json:"results"`
75} 79}
76 80
77func SearchFunc(query string, page int, scope rune, user ...string) (ss Search, e error) { 81func PerformSearch(query string, page int, scope rune, user ...string) (ss Search, e error) {
78 var url strings.Builder 82 var buildurl strings.Builder
79 e = nil 83 e = nil
80 84
81 // о5 построение ссылок. 85 // о5 построение ссылок.
82 switch scope { 86 switch scope {
83 case 'a': // поиск артов по названию 87 case 'a': // поиск артов по названию
84 url.WriteString("dabrowse/search/all?q=") 88 buildurl.WriteString("dabrowse/search/all?q=")
85 case 't': // поиск артов по тегам 89 case 't': // поиск артов по тегам
86 url.WriteString("dabrowse/networkbar/tag/deviations?tag=") 90 buildurl.WriteString("dabrowse/networkbar/tag/deviations?tag=")
87 case 'g': // поиск артов пользователя или группы 91 case 'g': // поиск артов пользователя или группы
88 if user != nil { 92 if user != nil {
89 url.WriteString("dashared/gallection/search?username=") 93 buildurl.WriteString("dashared/gallection/search?username=")
90 for _, a := range user { 94 buildurl.WriteString(user[0])
91 url.WriteString(a) 95 buildurl.WriteString("&type=gallery&order=most-recent&init=true&limit=50&q=")
92 }
93 url.WriteString("&type=gallery&order=most-recent&init=true&limit=50&q=")
94 } else { 96 } else {
95 e = errors.New("missing username (last argument)") 97 e = errors.New("missing username (last argument)")
96 return 98 return
@@ -99,16 +101,16 @@ func SearchFunc(query string, page int, scope rune, user ...string) (ss Search,
99 log.Fatalln("Invalid type.\n- 'a' -- all;\n- 't' -- tag;\n- 'g' - gallery.") 101 log.Fatalln("Invalid type.\n- 'a' -- all;\n- 't' -- tag;\n- 'g' - gallery.")
100 } 102 }
101 103
102 url.WriteString(u.QueryEscape(query)) 104 buildurl.WriteString(url.QueryEscape(query))
103 if scope != 'g' { // если область поиска не равна поиску по группам, то активируется этот код 105 if scope != 'g' { // если область поиска не равна поиску по группам, то активируется этот код
104 url.WriteString("&page=") 106 buildurl.WriteString("&page=")
105 } else { // иначе вместо страницы будет оффсет и страница умножится на 50 107 } else { // иначе вместо страницы будет оффсет и страница умножится на 50
106 url.WriteString("&offset=") 108 buildurl.WriteString("&offset=")
107 page = 50 * page 109 page = 50 * page
108 } 110 }
109 url.WriteString(strconv.Itoa(page)) 111 buildurl.WriteString(strconv.Itoa(page))
110 112
111 ujson(url.String(), &ss) 113 ujson(buildurl.String(), &ss)
112 114
113 if scope == 'g' { 115 if scope == 'g' {
114 ss.Results = ss.ResultsGalleryTemp 116 ss.Results = ss.ResultsGalleryTemp
todo.md deleted −2
@@ -1,2 +0,0 @@
1- groups
2- images in comments\ No newline at end of file
user-group.go +43 −35
@@ -1,44 +1,12 @@
1package devianter 1package devianter
2 2
3import ( 3import (
4 "errors"
4 "strconv" 5 "strconv"
5 "strings" 6 "strings"
6) 7)
7 8
8// структура группы или пользователя 9// структура группы или пользователя
9type GroupAbout struct {
10 FoundatedAt time `json:"foundationTs"`
11 Description Text
12}
13type GroupAdmins struct {
14 Results []struct {
15 TypeId int
16 User struct {
17 Username string
18 }
19 }
20}
21
22type About struct {
23 Country, Website, WebsiteLabel, Gender string
24 RegDate int64 `json:"deviantFor"`
25 Description Text `json:"textContent"`
26
27 SocialLinks []struct {
28 Value string
29 }
30 Interests []struct {
31 Label, Value string
32 }
33}
34
35type users struct {
36 About About
37 CoverDeviation struct {
38 Deviation Deviation `json:"coverDeviation"`
39 }
40}
41
42type GRuser struct { 10type GRuser struct {
43 ErrorDescription string 11 ErrorDescription string
44 Owner struct { 12 Owner struct {
@@ -106,14 +74,21 @@ type Group struct {
106} 74}
107 75
108// подходит как группа, так и пользователь 76// подходит как группа, так и пользователь
109func (s Group) GroupFunc() (g GRuser) { 77func (s Group) GetGroup() (g GRuser, err error) {
78 if s.Name == "" {
79 return g, errors.New("missing Name field")
80 }
110 ujson("dauserprofile/init/about?username="+s.Name, &g) 81 ujson("dauserprofile/init/about?username="+s.Name, &g)
111 82
112 return 83 return
113} 84}
114 85
115// гарелея пользователя или группы 86// гарелея пользователя или группы
116func (s Group) Gallery(page int, folderid ...int) (g Group) { 87func (s Group) GetGallery(page int, folderid ...int) (g Group, err error) {
88 if s.Name == "" {
89 return g, errors.New("missing Name field")
90 }
91
117 var url strings.Builder 92 var url strings.Builder
118 if folderid[0] > 0 { 93 if folderid[0] > 0 {
119 page-- 94 page--
@@ -137,3 +112,36 @@ func (s Group) Gallery(page int, folderid ...int) (g Group) {
137 ujson(url.String(), &g.Content) 112 ujson(url.String(), &g.Content)
138 return 113 return
139} 114}
115
116type GroupAbout struct {
117 FoundatedAt timeStamp `json:"foundationTs"`
118 Description Text
119}
120type GroupAdmins struct {
121 Results []struct {
122 TypeId int
123 User struct {
124 Username string
125 }
126 }
127}
128
129type About struct {
130 Country, Website, WebsiteLabel, Gender string
131 RegDate int64 `json:"deviantFor"`
132 Description Text `json:"textContent"`
133
134 SocialLinks []struct {
135 Value string
136 }
137 Interests []struct {
138 Label, Value string
139 }
140}
141
142type users struct {
143 About About
144 CoverDeviation struct {
145 Deviation Deviation `json:"coverDeviation"`
146 }
147}
util.go +14 −16
@@ -9,7 +9,7 @@ import (
9) 9)
10 10
11// функция для высера ошибки в stderr 11// функция для высера ошибки в stderr
12func err(txt error) { 12func try(txt error) {
13 if txt != nil { 13 if txt != nil {
14 println(txt.Error()) 14 println(txt.Error())
15 } 15 }
@@ -17,11 +17,9 @@ func err(txt error) {
17 17
18// сокращение для вызова щенка и парсинга жсона 18// сокращение для вызова щенка и парсинга жсона
19func ujson(data string, output any) { 19func ujson(data string, output any) {
20 input, e := puppy(data) 20 input, err := puppy(data)
21 err(e) 21 try(err)
22 22 try(json.Unmarshal([]byte(input), output))
23 eee := json.Unmarshal([]byte(input), output)
24 err(eee)
25} 23}
26 24
27/* REQUEST SECTION */ 25/* REQUEST SECTION */
@@ -34,32 +32,32 @@ type reqrt struct {
34} 32}
35 33
36// функция для совершения запроса 34// функция для совершения запроса
35var UserAgent string
36
37func request(uri string, other ...string) reqrt { 37func request(uri string, other ...string) reqrt {
38 var r reqrt 38 var r reqrt
39 39
40 // создаём новый запрос 40 // создаём новый запрос
41 cli := &http.Client{} 41 cli := &http.Client{}
42 req, e := http.NewRequest("GET", uri, nil) 42 req, e := http.NewRequest("GET", uri, nil)
43 err(e) 43 try(e)
44 44
45 req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0.0") 45 req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0.0")
46 46
47 // куки и UA-шник 47 // куки и UA-шник
48 for num, rng := range other { 48 if UserAgent != "" {
49 switch num { 49 req.Header.Set("User-Agent", UserAgent)
50 case 1: 50 }
51 req.Header.Set("User-Agent", rng) 51 if len(other) != 0 {
52 case 0: 52 req.Header.Set("Cookie", other[0])
53 req.Header.Set("Cookie", rng)
54 }
55 } 53 }
56 54
57 resp, e := cli.Do(req) 55 resp, e := cli.Do(req)
58 err(e) 56 try(e)
59 defer resp.Body.Close() 57 defer resp.Body.Close()
60 58
61 body, e := io.ReadAll(resp.Body) 59 body, e := io.ReadAll(resp.Body)
62 err(e) 60 try(e)
63 61
64 // заполняем структуру 62 // заполняем структуру
65 r.Body = string(body) 63 r.Body = string(body)