Breaking API update!!!

This commit is contained in:
VnPower
2023-05-18 22:03:21 +07:00
parent 5fc793ac21
commit ce60c9ca34
11 changed files with 541 additions and 101 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ var Configs Conf
type Conf struct {
PHPSESSID string `yaml:"PHPSESSID"`
userAgent int `yaml:"userAgent"`
UserAgent string `yaml:"userAgent"`
}
func (conf *Conf) ReadConfig() {
-59
View File
@@ -1,59 +0,0 @@
package entity
import (
"html/template"
"time"
)
type Illust struct {
ID int `json:"id"`
Title string `json:"title"`
Caption template.HTML `json:"caption"`
Artist User `json:"user"`
Date time.Time `json:"create_date"`
Pages int `json:"page_count"`
Views int `json:"total_view"`
Bookmarks int `json:"total_bookmarks"`
Tags []Tag `json:"tags"`
Images []Image
}
type Spotlight struct {
ID int `json:"id"`
Title string `json:"title"`
Thumbnail string `json:"thumbnail"`
URL string `json:"article_url"`
Date string `json:"publish_date"`
}
type Tag struct {
Name string `json:"name"`
TranslatedName string `json:"translated_name"`
}
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Account string `json:"account"`
Avatar map[string]string `json:"profile_image_urls"`
Webpage string `json:"webpage"`
Gender string `json:"gender"`
Birth string `json:"birth"`
BirthDay string `json:"birth_day"`
BirthYear int `json:"birth_year"`
Region string `json:"region"`
BackgroundImage string `json:"background_image_url"`
Followers int `json:"total_follow_users"`
MyPixiv int `json:"total_mypixiv_users"`
Illusts int `json:"total_illusts"`
Mangas int `json:"total_mangas"`
TwitterURL string `json:"twitter_url"`
PawooURL string `json:"pawoo_url"`
}
type Image struct {
Small string `json:"square_medium"`
Medium string `json:"medium"`
Large string `json:"large"`
Original string `json:"original"`
}
+326
View File
@@ -0,0 +1,326 @@
package models
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"pixivfe/models"
"regexp"
"sort"
"strconv"
)
type PixivClient struct {
Client *http.Client
Cookie map[string]string
Header map[string]string
Lang string
}
const (
ArtworkInformationURL = "https://www.pixiv.net/ajax/illust/%s"
ArtworkImagesURL = "https://www.pixiv.net/ajax/illust/%s/pages"
ArtworkRelatedURL = "https://www.pixiv.net/ajax/illust/%s/recommend/init?limit=30"
UserInformationURL = "https://www.pixiv.net/ajax/user/%s?full=1"
UserArtworksURL = "https://www.pixiv.net/ajax/user/%s/profile/all"
UserArtworksFullURL = "https://www.pixiv.net/ajax/user/%s/profile/illusts?work_category=illustManga&is_first_page=0&lang=en%s"
)
func ProxyImage(url string) string {
regex := regexp.MustCompile(`i\.pximg\.net`)
proxy := "px2.rainchan.win"
return regex.ReplaceAllString(url, proxy)
}
func (p *PixivClient) SetHeader(header map[string]string) {
p.Header = header
}
func (p *PixivClient) AddHeader(key, value string) {
p.Header[key] = value
}
func (p *PixivClient) SetUserAgent(value string) {
p.AddHeader("User-Agent", value)
}
func (p *PixivClient) SetCookie(cookie map[string]string) {
p.Cookie = cookie
}
func (p *PixivClient) AddCookie(key, value string) {
p.Cookie[key] = value
}
func (p *PixivClient) SetSessionID(value string) {
p.Cookie["PHPSESSID"] = value
}
func (p *PixivClient) SetLang(lang string) {
p.Lang = lang
}
func (p *PixivClient) Request(URL string) (*http.Response, error) {
req, _ := http.NewRequest("GET", URL, nil)
// Add headers
for k, v := range p.Header {
req.Header.Add(k, v)
}
for k, v := range p.Cookie {
req.AddCookie(&http.Cookie{Name: k, Value: v})
}
// Make a request
resp, err := p.Client.Do(req)
if err != nil {
return resp, err
}
if resp.StatusCode == http.StatusNotFound {
return resp, errors.New("404 returned")
}
if resp.StatusCode != 200 {
return resp, errors.New(fmt.Sprintf("Server returned code: %d", resp.StatusCode))
}
return resp, nil
}
func (p *PixivClient) TextRequest(URL string) (string, error) {
resp, err := p.Request(URL)
if err != nil {
return "", err
}
// Extract the bytes from server's response
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return string(body), err
}
return string(body), nil
}
func (p *PixivClient) GetArtworkImages(id string) ([]models.Image, error) {
s, _ := p.TextRequest(fmt.Sprintf(ArtworkImagesURL, id))
var pr models.PixivResponse
var resp []models.ImageResponse
var images []models.Image
err := json.Unmarshal([]byte(s), &pr)
if err != nil {
return images, errors.New(fmt.Sprintf("Failed to extract data from JSON response from server. %s", err))
}
if pr.Error {
return images, errors.New(fmt.Sprintf("Server refuses to return data. %s", err))
}
err = json.Unmarshal([]byte(pr.Body), &resp)
if err != nil {
return images, errors.New(fmt.Sprintf("Failed to extract images for illust. %s", err))
}
// Extract and proxy every images
for _, imageRaw := range resp {
var image models.Image
image.Small = ProxyImage(imageRaw.Urls["thumb_mini"])
image.Medium = ProxyImage(imageRaw.Urls["small"])
image.Large = ProxyImage(imageRaw.Urls["regular"])
image.Original = ProxyImage(imageRaw.Urls["original"])
images = append(images, image)
}
return images, nil
}
func (p *PixivClient) GetArtworkByID(id string) (*models.Illust, error) {
s, _ := p.TextRequest(fmt.Sprintf(ArtworkInformationURL, id))
var resp models.PixivResponse
var images []models.Image
// Parse Pixiv response body
err := json.Unmarshal([]byte(s), &resp)
if err != nil {
return nil, errors.New(fmt.Sprintf("Failed to extract data from JSON response from server. %s", err))
}
if resp.Error {
return nil, errors.New(fmt.Sprintf("Server refuses to return data. %s", err))
}
var illust struct {
*models.Illust
RawTags json.RawMessage `json:"tags"`
}
// Parse basic illust information
err = json.Unmarshal([]byte(resp.Body), &illust)
// Get illust images
images, err = p.GetArtworkImages(id)
if err != nil {
fmt.Printf("%s\n", err)
}
illust.Images = images
// 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 {
fmt.Printf("%s\n", err)
return nil, err
}
for _, tag := range tags.Tags {
var newTag models.Tag
newTag.Name = tag.Tag
newTag.TranslatedName = tag.Translation["en"]
illust.Tags = append(illust.Tags, newTag)
}
return illust.Illust, nil
}
func (p *PixivClient) GetUserArtworksID(id string) (*string, error) {
s, _ := p.TextRequest(fmt.Sprintf(UserArtworksURL, id))
var pr models.PixivResponse
err := json.Unmarshal([]byte(s), &pr)
if err != nil {
return nil, errors.New(fmt.Sprintf("Failed to extract data from JSON response from server. %s", err))
}
if pr.Error {
return nil, errors.New(pr.Message)
}
var ids []int
var idsString string
var body struct {
Illusts map[int]string `json:"illusts"`
}
err = json.Unmarshal(pr.Body, &body)
// Get the keys, because Pixiv only returns IDs (very evil)
for k := range body.Illusts {
ids = append(ids, k)
}
// Reverse sort the ids
sort.Sort(sort.Reverse(sort.IntSlice(ids)))
i := 0
for _, k := range ids {
if i < 30 {
idsString += fmt.Sprintf("&ids[]=%d", k)
} else {
break
}
i++
}
return &idsString, nil
}
func (p *PixivClient) GetRelatedArtworks(id string) ([]models.IllustShort, error) {
url := fmt.Sprintf(ArtworkRelatedURL, id)
var pr models.PixivResponse
s, err := p.TextRequest(url)
if err != nil {
return nil, err
}
err = json.Unmarshal([]byte(s), &pr)
var body struct {
Illusts []models.IllustShort `json:"illusts"`
}
err = json.Unmarshal([]byte(pr.Body), &body)
if err != nil {
return nil, err
}
return body.Illusts, nil
}
func (p *PixivClient) GetUserArtworks(id string) ([]models.IllustShort, error) {
ids, err := p.GetUserArtworksID(id)
if err != nil {
return nil, err
}
url := fmt.Sprintf(UserArtworksFullURL, id, *ids)
var pr models.PixivResponse
var works []models.IllustShort
s, err := p.TextRequest(url)
err = json.Unmarshal([]byte(s), &pr)
var body struct {
Illusts map[int]json.RawMessage `json:"works"`
}
err = json.Unmarshal(pr.Body, &body)
for _, v := range body.Illusts {
var illust models.IllustShort
err = json.Unmarshal(v, &illust)
works = append(works, illust)
}
// IDK but the order got shuffled even though Pixiv sorted the IDs in the response
sort.Slice(works[:], func(i, j int) bool {
left, _ := strconv.Atoi(works[i].ID)
right, _ := strconv.Atoi(works[j].ID)
return left > right
})
return works, nil
}
func (p *PixivClient) GetUserInformation(id string) (*models.User, error) {
s, _ := p.TextRequest(fmt.Sprintf(UserInformationURL, id))
var user *models.User
var pr models.PixivResponse
err := json.Unmarshal([]byte(s), &pr)
if err != nil {
return nil, errors.New(fmt.Sprintf("Failed to extract data from JSON response from server. %s", err))
}
if pr.Error {
return nil, errors.New(pr.Message)
}
// Basic user information
err = json.Unmarshal([]byte(pr.Body), &user)
// Artworks
works, _ := p.GetUserArtworks(id)
user.Artworks = works
return user, nil
}
-1
View File
@@ -33,7 +33,6 @@ func setupRouter() *gin.Engine {
func main() {
configs.Configs.ReadConfig()
println(configs.Configs.PHPSESSID)
r := setupRouter()
+120
View File
@@ -0,0 +1,120 @@
package models
import (
"html/template"
"time"
"encoding/json"
)
type PixivResponse struct {
Error bool
Message string
Body json.RawMessage
}
type ImageResponse struct {
Urls map[string]string `json:"urls"`
}
type TagResponse struct {
AuthorID string `json:"authorId"`
RawTags json.RawMessage `json:"tags"`
}
// 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: "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: "NotAI",
AI: "AI",
}
// Pixiv gives us 5 types of an image. I don't need the mini one tho.
// PS: Where tf is my 360x360 image, Pixiv? :rage:
type Image struct {
Small string `json:"thumb_mini"`
Medium string `json:"small"`
Large string `json:"regular"`
Original string `json:"original"`
}
type Tag struct {
Name string `json:"tag"`
TranslatedName string `json:"translation"`
}
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 `json:"images"`
Tags []Tag `json:"tags"`
Pages int `json:"pageCount"`
Bookmarks int `json:"bookmarkCount"`
Likes int `json:"likeCount"`
Comments int `json:"commentCount"`
Views int `json:"viewCount"`
XRestrict xRestrict `json:"xRestrict"`
AiType aiType `json:"aiType"`
}
type IllustShort struct {
ID string `json:"id"`
Title string `json:"title"`
Description template.HTML `json:"description"`
ArtistID string `json:"userId"`
ArtistName string `json:"userName"`
ArtistAvatar string `json:"profileImageUrl"`
Date time.Time `json:"uploadDate"`
Thumbnail string `json:"url"`
Pages int `json:"pageCount"`
XRestrict xRestrict `json:"xRestrict"`
AiType aiType `json:"aiType"`
}
type User struct {
ID string `json:"userId"`
Name string `json:"name"`
Avatar string `json:"image"`
BackgroundImage string `json:"background"`
Following int `json:"following"`
MyPixiv int `json:"mypixivCount"`
Comment string `json:"comment"`
Artworks []IllustShort `json:"artworks"`
}
+5 -6
View File
@@ -12,7 +12,7 @@
{{ end }}
</div>
<h2>{{ .Title }}</h3>
<p>{{ .Caption }}</p>
<p>{{ .Description }}</p>
<div class="artwork-tags">
@@ -26,15 +26,14 @@
<br />
<small>{{ .Date }}</small>
<a href="/user/{{ .Artist.ID }}" class="artwork-artist flex"><img src="{{ .Artist.Avatar.medium }}"
alt="{{ .Artist.Name }}" class="artwork-artist-avatar border-rounded" />
{{ .Artist.Name }}</a>
<a href="/user/{{ $parent.Artist.ID }}" class="artwork-artist flex"><img src="{{ $parent.Artist.Avatar }}"
alt="{{ $parent.Artist.Name }}" class="artwork-artist-avatar border-rounded" />
{{ $parent.Artist.Name }}</a>
<div class="thumbnail-container">
{{ range $parent.Recent }}
<div class="artwork-thumbnail-small artwork-thumbnail">
<a href="/artworks/{{ .ID }}">
<img data-src="{{ (index .Images 0).Small }}" alt="{{ (index .Images 0).Small }}"
class="artwork-master-image lazy" />
<img data-src="{{ .Thumbnail }}" alt="{{ .Thumbnail }}" class="artwork-master-image lazy" />
</a>
</div>
{{ end }}
+13 -4
View File
@@ -2,14 +2,23 @@
<div class="artwork-thumbnail-large artwork-thumbnail">
<div class="artwork-rank-circle">{{ $i | inc}}</div>
<a href="/artworks/{{ .ID }}">
<img data-src="{{ (index .Images 0).Small }}" alt="{{ .Title }}" class="artwork-master-image lazy" />
<img
data-src="{{ (index .Images 0).Small }}"
alt="{{ .Title }}"
class="artwork-master-image lazy"
/>
</a>
<a class="artwork-thumbnail-title" href="/artworks/{{ .ID }}">
<h3 class="no-margin">{{ .Title }}</h3>
</a>
<a href="/user/{{ .Artist.ID }}" class="artwork-thumbnail-artist flex"><img data-src="{{ .Artist.Avatar.medium }}"
alt="{{ .Artist.Name }}" class="artwork-thumbnail-artist-avatar border-rounded lazy" />
{{ .Artist.Name }}</a>
<a href="/users/{{ .ArtistID }}" class="artwork-thumbnail-artist flex"
><img
data-src="{{ .Artist.Avatar }}"
alt="{{ .ArtistName }}"
class="artwork-thumbnail-artist-avatar border-rounded lazy"
/>
{{ .ArtistName }}</a
>
</div>
{{ end }}
+13 -4
View File
@@ -1,14 +1,23 @@
{{ range . }}
<div class="artwork-thumbnail-small artwork-thumbnail">
<a href="/artworks/{{ .ID }}">
<img data-src="{{ (index .Images 0).Small }}" alt="{{ .Title }}" class="artwork-master-image lazy" />
<img
data-src="{{ .Thumbnail }}"
alt="{{ .Title }}"
class="artwork-master-image lazy"
/>
</a>
<a class="artwork-thumbnail-title" href="/artworks/{{ .ID }}">
<h3 class="no-margin">{{ .Title }}</h3>
</a>
<a href="/user/{{ .Artist.ID }}" class="artwork-thumbnail-artist flex"><img data-src="{{ .Artist.Avatar.medium }}"
alt="{{ .Artist.Name }}" class="artwork-thumbnail-artist-avatar border-rounded lazy" />
{{ .Artist.Name }}</a>
<a href="/users/{{ .ArtistID }}" class="artwork-thumbnail-artist flex"
><img
data-src="{{ .ArtistAvatar }}"
alt="{{ .ArtistName }}"
class="artwork-thumbnail-artist-avatar border-rounded lazy"
/>
{{ .ArtistName }}</a
>
</div>
{{ end }}
+4 -3
View File
@@ -5,10 +5,11 @@
<div class="container">
<div class="user-page">
<div class="user-details">
<img src="{{ .User.Avatar.medium }}" alt="avatar" class="user-avatar" />
<img src="{{ .User.Avatar }}" alt="avatar" class="user-avatar" />
<h2 class="user-name">{{ .User.Name }}</h2>
<p class="user-id">@{{ .User.Account }}</p>
<p>{{ .User.Followers }} Following | {{ .User.MyPixiv }} MyPixiv</p>
<p class="user-id">
{{ .User.Following }} Following | {{ .User.MyPixiv }} MyPixiv
</p>
</div>
</div>
<div>
+59 -23
View File
@@ -1,44 +1,80 @@
package views
import (
"github.com/gin-gonic/gin"
"net/http"
"pixivfe/configs"
"pixivfe/handler"
"strconv"
"time"
"github.com/gin-gonic/gin"
)
var PC *models.PixivClient
func artwork_page(c *gin.Context) {
illust, _ := handler.GetIllustByID(c)
related, _ := handler.GetRelatedIllust(c)
recent_by_artist, _ := handler.GetMemberIllust(c, strconv.Itoa(illust.Artist.ID))
id := c.Param("id")
illust, _ := PC.GetArtworkByID(id)
related, _ := PC.GetRelatedArtworks(id)
recent_by_artist, _ := PC.GetUserArtworks(illust.UserID)
artist_info, _ := PC.GetUserInformation(illust.UserID)
c.HTML(http.StatusOK, "artwork.html", gin.H{
"Illust": illust,
"Related": related,
"Recent": recent_by_artist,
"Artist": artist_info,
})
}
func index_page(c *gin.Context) {
recommended, _ := handler.GetRecommendedIllust(c)
ranking, _ := handler.GetRankingIllust(c, "day")
spotlight := handler.GetSpotlightArticle(c)
newest, _ := handler.GetNewestIllust(c)
c.HTML(http.StatusOK, "index.html", gin.H{
"Recommended": recommended,
"Rankings": ranking,
"Spotlights": spotlight,
"Newest": newest,
})
}
// func index_page(c *gin.Context) {
// recommended, _ := handler.GetRecommendedIllust(c)
// ranking, _ := handler.GetRankingIllust(c, "day")
// spotlight := handler.GetSpotlightArticle(c)
// newest, _ := handler.GetNewestIllust(c)
// c.HTML(http.StatusOK, "index.html", gin.H{
// "Recommended": recommended,
// "Rankings": ranking,
// "Spotlights": spotlight,
// "Newest": newest,
// })
// }
func user_page(c *gin.Context) {
user, _ := handler.GetUserInfo(c)
recent, _ := handler.GetMemberIllust(c, c.Param("id"))
id := c.Param("id")
user, _ := PC.GetUserInformation(id)
recent, _ := PC.GetUserArtworks(id)
c.HTML(http.StatusOK, "user.html", gin.H{"User": user, "Recent": recent})
}
func SetupRoutes(r *gin.Engine) {
r.GET("/", index_page)
r.GET("artworks/:id", artwork_page)
r.GET("user/:id", user_page)
func getUserInformation(c *gin.Context) {
id := c.Param("id")
data, _ := PC.GetUserInformation(id)
c.IndentedJSON(http.StatusOK, data)
}
func NewPixivClient(timeout int) *models.PixivClient {
transport := &http.Transport{Proxy: http.ProxyFromEnvironment}
client := &http.Client{
Timeout: time.Duration(timeout) * time.Millisecond,
Transport: transport,
}
pc := &models.PixivClient{
Client: client,
Header: make(map[string]string),
Cookie: make(map[string]string),
Lang: "en",
}
return pc
}
func SetupRoutes(r *gin.Engine) {
PC = NewPixivClient(5000)
PC.SetSessionID(configs.Configs.PHPSESSID)
PC.SetUserAgent(configs.Configs.UserAgent)
// r.GET("/", index_page)
r.GET("artworks/:id", artwork_page)
r.GET("users/:id", user_page)
}