123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399 |
- package core
- import (
- "errors"
- "fmt"
- "html/template"
- "sort"
- "strings"
- "sync"
- "time"
- "codeberg.org/vnpower/pixivfe/v2/session"
- "github.com/goccy/go-json"
- "github.com/gofiber/fiber/v2"
- )
- // Pixiv returns 0, 1, 2 to filter SFW and/or NSFW artworks.
- // Those values are saved in `xRestrict`
- // 0: Safe
- // 1: R18
- // 2: R18G
- type xRestrict int
- const (
- Safe xRestrict = 0
- R18 xRestrict = 1
- R18G xRestrict = 2
- )
- var xRestrictModel = map[xRestrict]string{
- Safe: "",
- R18: "R18",
- R18G: "R18G",
- }
- // Pixiv returns 0, 1, 2 to filter SFW and/or NSFW artworks.
- // Those values are saved in `aiType`
- // 0: Not rated / Unknown
- // 1: Not AI-generated
- // 2: AI-generated
- type aiType int
- const (
- Unrated aiType = 0
- NotAI aiType = 1
- AI aiType = 2
- )
- var aiTypeModel = map[aiType]string{
- Unrated: "Unrated",
- NotAI: "Not AI",
- AI: "AI",
- }
- type ImageResponse struct {
- Width int `json:"width"`
- Height int `json:"height"`
- Urls map[string]string `json:"urls"`
- }
- type Image struct {
- Width int
- Height int
- Small string
- Medium string
- Large string
- Original string
- }
- type Tag struct {
- Name string `json:"tag"`
- TranslatedName string `json:"translation"`
- }
- type Comment struct {
- AuthorID string `json:"userId"`
- AuthorName string `json:"userName"`
- Avatar string `json:"img"`
- Context string `json:"comment"`
- Stamp string `json:"stampId"`
- Date string `json:"commentDate"`
- }
- type UserBrief struct {
- ID string `json:"userId"`
- Name string `json:"name"`
- Avatar string `json:"imageBig"`
- }
- type ArtworkBrief struct {
- ID string `json:"id"`
- Title string `json:"title"`
- ArtistID string `json:"userId"`
- ArtistName string `json:"userName"`
- ArtistAvatar string `json:"profileImageUrl"`
- Thumbnail string `json:"url"`
- Pages int `json:"pageCount"`
- XRestrict int `json:"xRestrict"`
- AiType int `json:"aiType"`
- Bookmarked any `json:"bookmarkData"`
- IllustType int `json:"illustType"`
- }
- type Illust struct {
- ID string `json:"id"`
- Title string `json:"title"`
- Description template.HTML `json:"description"`
- UserID string `json:"userId"`
- UserName string `json:"userName"`
- UserAccount string `json:"userAccount"`
- Date time.Time `json:"uploadDate"`
- Images []Image
- Tags []Tag `json:"tags"`
- Pages int `json:"pageCount"`
- Bookmarks int `json:"bookmarkCount"`
- Likes int `json:"likeCount"`
- Comments int `json:"commentCount"`
- Views int `json:"viewCount"`
- CommentDisabled int `json:"commentOff"`
- SanityLevel int `json:"sl"`
- XRestrict xRestrict `json:"xRestrict"`
- AiType aiType `json:"aiType"`
- BookmarkData any `json:"bookmarkData"`
- Liked bool `json:"likeData"`
- User UserBrief
- RecentWorks []ArtworkBrief
- RelatedWorks []ArtworkBrief
- CommentsList []Comment
- IsUgoira bool
- BookmarkID string
- }
- func GetUserBasicInformation(c *fiber.Ctx, id string) (UserBrief, error) {
- var user UserBrief
- URL := GetUserInformationURL(id)
- response, err := UnwrapWebAPIRequest(c.Context(), URL, "")
- if err != nil {
- return user, err
- }
- response = session.ProxyImageUrl(c, response)
- err = json.Unmarshal([]byte(response), &user)
- if err != nil {
- return user, err
- }
- return user, nil
- }
- func GetArtworkImages(c *fiber.Ctx, id string) ([]Image, error) {
- var resp []ImageResponse
- var images []Image
- URL := GetArtworkImagesURL(id)
- response, err := UnwrapWebAPIRequest(c.Context(), URL, "")
- if err != nil {
- return nil, err
- }
- response = session.ProxyImageUrl(c, response)
- err = json.Unmarshal([]byte(response), &resp)
- if err != nil {
- return images, err
- }
- // Extract and proxy every images
- for _, imageRaw := range resp {
- var image Image
- // this is the original art dimention, not the "regular" art dimension
- // the image ratio of "regular" is close to Width/Height
- // maybe not useful
- image.Width = imageRaw.Width
- image.Height = imageRaw.Height
- image.Small = imageRaw.Urls["thumb_mini"]
- image.Medium = imageRaw.Urls["small"]
- image.Large = imageRaw.Urls["regular"]
- image.Original = imageRaw.Urls["original"]
- images = append(images, image)
- }
- return images, nil
- }
- func GetArtworkComments(c *fiber.Ctx, id string) ([]Comment, error) {
- var body struct {
- Comments []Comment `json:"comments"`
- }
- URL := GetArtworkCommentsURL(id)
- response, err := UnwrapWebAPIRequest(c.Context(), URL, "")
- if err != nil {
- return nil, err
- }
- response = session.ProxyImageUrl(c, response)
- err = json.Unmarshal([]byte(response), &body)
- if err != nil {
- return nil, err
- }
- return body.Comments, nil
- }
- func GetRelatedArtworks(c *fiber.Ctx, id string) ([]ArtworkBrief, error) {
- var body struct {
- Illusts []ArtworkBrief `json:"illusts"`
- }
- // TODO: keep the hard-coded limit?
- URL := GetArtworkRelatedURL(id, 96)
- response, err := UnwrapWebAPIRequest(c.Context(), URL, "")
- if err != nil {
- return nil, err
- }
- response = session.ProxyImageUrl(c, response)
- err = json.Unmarshal([]byte(response), &body)
- if err != nil {
- return nil, err
- }
- return body.Illusts, nil
- }
- func GetArtworkByID(c *fiber.Ctx, id string, full bool) (*Illust, error) {
- URL := GetArtworkInformationURL(id)
- token := session.GetPixivToken(c)
- response, err := UnwrapWebAPIRequest(c.Context(), URL, token)
- if err != nil {
- return nil, err
- }
- var illust struct {
- *Illust
- // recent illustrations by same user
- Recent map[int]any `json:"userIllusts"`
- RawTags json.RawMessage `json:"tags"`
- }
- // Parse basic illust information
- err = json.Unmarshal([]byte(response), &illust)
- if err != nil {
- return nil, err
- }
- if illust.BookmarkData != nil {
- t := illust.BookmarkData.(map[string]any)
- illust.BookmarkID = t["id"].(string)
- }
- // Begin testing here
- wg := sync.WaitGroup{}
- cerr := make(chan error, 6)
- wg.Add(3)
- go func() {
- // Get illust images
- defer wg.Done()
- images, err := GetArtworkImages(c, id)
- if err != nil {
- cerr <- err
- return
- }
- illust.Images = images
- }()
- go func() {
- // Get basic user information (the URL above does not contain avatars)
- defer wg.Done()
- var err error
- userInfo, err := GetUserBasicInformation(c, illust.UserID)
- if err != nil {
- cerr <- err
- return
- }
- illust.User = userInfo
- }()
- go func() {
- defer wg.Done()
- var err error
- // Extract tags
- var tags struct {
- Tags []struct {
- Tag string `json:"tag"`
- Translation map[string]string `json:"translation"`
- } `json:"tags"`
- }
- err = json.Unmarshal(illust.RawTags, &tags)
- if err != nil {
- cerr <- err
- return
- }
- var tagsList []Tag
- for _, tag := range tags.Tags {
- var newTag Tag
- newTag.Name = tag.Tag
- newTag.TranslatedName = tag.Translation["en"]
- tagsList = append(tagsList, newTag)
- }
- illust.Tags = tagsList
- }()
- if full {
- wg.Add(3)
- go func() {
- defer wg.Done()
- var err error
- // Get recent artworks
- ids := make([]int, 0)
- for k := range illust.Recent {
- ids = append(ids, k)
- }
- sort.Sort(sort.Reverse(sort.IntSlice(ids)))
- idsString := ""
- count := min(len(ids), 20)
- for i := 0; i < count; i++ {
- idsString += fmt.Sprintf("&ids[]=%d", ids[i])
- }
- recent, err := GetUserArtworks(c, illust.UserID, idsString)
- if err != nil {
- cerr <- err
- return
- }
- sort.Slice(recent[:], func(i, j int) bool {
- left := recent[i].ID
- right := recent[j].ID
- return numberGreaterThan(left, right)
- })
- illust.RecentWorks = recent
- }()
- go func() {
- defer wg.Done()
- var err error
- related, err := GetRelatedArtworks(c, id)
- if err != nil {
- cerr <- err
- return
- }
- illust.RelatedWorks = related
- }()
- go func() {
- defer wg.Done()
- if illust.CommentDisabled == 1 {
- return
- }
- var err error
- comments, err := GetArtworkComments(c, id)
- if err != nil {
- cerr <- err
- return
- }
- illust.CommentsList = comments
- }()
- }
- wg.Wait()
- close(cerr)
- all_errors := []error{}
- for suberr := range cerr {
- all_errors = append(all_errors, suberr)
- }
- err_summary := errors.Join(all_errors...)
- if err_summary != nil {
- return nil, err_summary
- }
- // If this artwork is an ugoira
- illust.IsUgoira = strings.Contains(illust.Images[0].Original, "ugoira")
- return illust.Illust, nil
- }
|