user.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. package core
  2. import (
  3. "errors"
  4. "fmt"
  5. "html/template"
  6. "math"
  7. "sort"
  8. http "codeberg.org/vnpower/pixivfe/v2/core/http"
  9. session "codeberg.org/vnpower/pixivfe/v2/core/session"
  10. "github.com/goccy/go-json"
  11. "github.com/gofiber/fiber/v2"
  12. )
  13. // pixivfe internal data type. not used by pixiv.
  14. type UserArtCategory string
  15. const (
  16. UserArt_Any UserArtCategory = ""
  17. UserArt_Illustration UserArtCategory = "illustrations"
  18. UserArt_Manga UserArtCategory = "manga"
  19. UserArt_Bookmarked UserArtCategory = "bookmarks" // what this user has bookmarked; not art by this user
  20. )
  21. func (s UserArtCategory) Validate() error {
  22. if s != UserArt_Any &&
  23. s != UserArt_Illustration &&
  24. s != UserArt_Manga &&
  25. s != UserArt_Bookmarked {
  26. return fmt.Errorf("Invalid work category: %#v. " + `only "%s", "%s", "%s" and "%s" are available`, s, UserArt_Any, UserArt_Illustration, UserArt_Manga, UserArt_Bookmarked)
  27. } else {
  28. return nil
  29. }
  30. }
  31. type FrequentTag struct {
  32. Name string `json:"tag"`
  33. TranslatedName string `json:"tag_translation"`
  34. }
  35. type User struct {
  36. ID string `json:"userId"`
  37. Name string `json:"name"`
  38. Avatar string `json:"imageBig"`
  39. Following int `json:"following"`
  40. MyPixiv int `json:"mypixivCount"`
  41. Comment template.HTML `json:"commentHtml"`
  42. Webpage string `json:"webpage"`
  43. SocialRaw json.RawMessage `json:"social"`
  44. Artworks []ArtworkBrief `json:"artworks"`
  45. Background map[string]interface{} `json:"background"`
  46. ArtworksCount int
  47. FrequentTags []FrequentTag
  48. Social map[string]map[string]string
  49. BackgroundImage string
  50. }
  51. func (s *User) ParseSocial() error {
  52. if string(s.SocialRaw[:]) == "[]" {
  53. // Fuck Pixiv
  54. return nil
  55. }
  56. err := json.Unmarshal(s.SocialRaw, &s.Social)
  57. if err != nil {
  58. return err
  59. }
  60. return nil
  61. }
  62. func GetFrequentTags(c *fiber.Ctx, ids string) ([]FrequentTag, error) {
  63. var tags []FrequentTag
  64. URL := http.GetFrequentTagsURL(ids)
  65. response, err := http.UnwrapWebAPIRequest(c.Context(), URL, "")
  66. if err != nil {
  67. return nil, err
  68. }
  69. err = json.Unmarshal([]byte(response), &tags)
  70. if err != nil {
  71. return nil, err
  72. }
  73. return tags, nil
  74. }
  75. func GetUserArtworks(c *fiber.Ctx, id, ids string) ([]ArtworkBrief, error) {
  76. var works []ArtworkBrief
  77. URL := http.GetUserFullArtworkURL(id, ids)
  78. resp, err := http.UnwrapWebAPIRequest(c.Context(), URL, "")
  79. if err != nil {
  80. return nil, err
  81. }
  82. resp = session.ProxyImageUrl(c, resp)
  83. var body struct {
  84. Illusts map[int]json.RawMessage `json:"works"`
  85. }
  86. err = json.Unmarshal([]byte(resp), &body)
  87. if err != nil {
  88. return nil, err
  89. }
  90. for _, v := range body.Illusts {
  91. var illust ArtworkBrief
  92. err = json.Unmarshal(v, &illust)
  93. if err != nil {
  94. return nil, err
  95. }
  96. works = append(works, illust)
  97. }
  98. return works, nil
  99. }
  100. func GetUserArtworksID(c *fiber.Ctx, id string, category UserArtCategory, page int) (string, int, error) {
  101. URL := http.GetUserArtworksURL(id)
  102. resp, err := http.UnwrapWebAPIRequest(c.Context(), URL, "")
  103. if err != nil {
  104. return "", -1, err
  105. }
  106. var body struct {
  107. Illusts json.RawMessage `json:"illusts"`
  108. Mangas json.RawMessage `json:"manga"`
  109. }
  110. err = json.Unmarshal([]byte(resp), &body)
  111. if err != nil {
  112. return "", -1, err
  113. }
  114. var ids []int
  115. var idsString string
  116. err = json.Unmarshal([]byte(resp), &body)
  117. if err != nil {
  118. return "", -1, err
  119. }
  120. var illusts map[int]string
  121. var mangas map[int]string
  122. count := 0
  123. if err = json.Unmarshal(body.Illusts, &illusts); err != nil {
  124. illusts = make(map[int]string)
  125. }
  126. if err = json.Unmarshal(body.Mangas, &mangas); err != nil {
  127. mangas = make(map[int]string)
  128. }
  129. // Get the keys, because Pixiv only returns IDs (very evil)
  130. if category == UserArt_Illustration || category == UserArt_Any {
  131. for k := range illusts {
  132. ids = append(ids, k)
  133. count++
  134. }
  135. }
  136. if category == UserArt_Manga || category == UserArt_Any {
  137. for k := range mangas {
  138. ids = append(ids, k)
  139. count++
  140. }
  141. }
  142. // Reverse sort the ids
  143. sort.Sort(sort.Reverse(sort.IntSlice(ids)))
  144. worksNumber := float64(count)
  145. worksPerPage := 30.0
  146. if page < 1 || float64(page) > math.Ceil(worksNumber/worksPerPage)+1.0 {
  147. return "", -1, errors.New("No page available.")
  148. }
  149. start := (page - 1) * int(worksPerPage)
  150. end := int(min(float64(page)*worksPerPage, worksNumber)) // no overflow
  151. for _, k := range ids[start:end] {
  152. idsString += fmt.Sprintf("&ids[]=%d", k)
  153. }
  154. return idsString, count, nil
  155. }
  156. func GetUserArtwork(c *fiber.Ctx, id string, category UserArtCategory, page int) (User, error) {
  157. var user User
  158. token := session.GetPixivToken(c)
  159. URL := http.GetUserInformationURL(id)
  160. resp, err := http.UnwrapWebAPIRequest(c.Context(), URL, token)
  161. if err != nil {
  162. return user, err
  163. }
  164. resp = session.ProxyImageUrl(c, resp)
  165. err = json.Unmarshal([]byte(resp), &user)
  166. if err != nil {
  167. return user, err
  168. }
  169. if category != "bookmarks" {
  170. ids, count, err := GetUserArtworksID(c, id, category, page)
  171. if err != nil {
  172. return user, err
  173. }
  174. if count > 0 {
  175. // Check if the user has artworks available or not
  176. works, err := GetUserArtworks(c, id, ids)
  177. if err != nil {
  178. return user, err
  179. }
  180. // IDK but the order got shuffled even though Pixiv sorted the IDs in the response
  181. sort.Slice(works[:], func(i, j int) bool {
  182. left := works[i].ID
  183. right := works[j].ID
  184. return numberGreaterThan(left, right)
  185. })
  186. user.Artworks = works
  187. user.FrequentTags, err = GetFrequentTags(c, ids)
  188. if err != nil {
  189. return user, err
  190. }
  191. }
  192. // Artworks count
  193. user.ArtworksCount = count
  194. } else {
  195. // Bookmarks
  196. works, count, err := GetUserBookmarks(c, id, "show", page)
  197. if err != nil {
  198. return user, err
  199. }
  200. user.Artworks = works
  201. // Public bookmarks count
  202. user.ArtworksCount = count
  203. }
  204. err = user.ParseSocial()
  205. if err != nil {
  206. return User{}, err
  207. }
  208. if user.Background != nil {
  209. user.BackgroundImage = user.Background["url"].(string)
  210. }
  211. return user, nil
  212. }
  213. func GetUserBookmarks(c *fiber.Ctx, id, mode string, page int) ([]ArtworkBrief, int, error) {
  214. page--
  215. URL := http.GetUserBookmarksURL(id, mode, page)
  216. resp, err := http.UnwrapWebAPIRequest(c.Context(), URL, "")
  217. if err != nil {
  218. return nil, -1, err
  219. }
  220. resp = session.ProxyImageUrl(c, resp)
  221. var body struct {
  222. Artworks []json.RawMessage `json:"works"`
  223. Total int `json:"total"`
  224. }
  225. err = json.Unmarshal([]byte(resp), &body)
  226. if err != nil {
  227. return nil, -1, err
  228. }
  229. artworks := make([]ArtworkBrief, len(body.Artworks))
  230. for index, value := range body.Artworks {
  231. var artwork ArtworkBrief
  232. err = json.Unmarshal([]byte(value), &artwork)
  233. if err != nil {
  234. artworks[index] = ArtworkBrief{
  235. ID: "#",
  236. Title: "Deleted or Private",
  237. Thumbnail: "https://s.pximg.net/common/images/limit_unknown_360.png",
  238. }
  239. continue
  240. }
  241. artworks[index] = artwork
  242. }
  243. return artworks, body.Total, nil
  244. }
  245. func numberGreaterThan(l, r string) bool {
  246. if len(l) > len(r) {
  247. return true
  248. }
  249. if len(l) < len(r) {
  250. return false
  251. }
  252. return l > r
  253. }