mirror of
https://codeberg.org/VnPower/PixivFE
synced 2024-12-06 19:16:23 +01:00
246 lines
8.0 KiB
Go
246 lines
8.0 KiB
Go
package core
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"codeberg.org/vnpower/pixivfe/v2/audit"
|
|
"codeberg.org/vnpower/pixivfe/v2/server/session"
|
|
)
|
|
|
|
const (
|
|
resultsLimit = 100
|
|
fakeBookmarkTag = "虚偽users入りタグ"
|
|
)
|
|
|
|
// Suffixes for popularity thresholds in ascending order
|
|
var suffixes = []string{"50", "100", "500", "1000", "5000", "10000", "30000", "50000", "100000"}
|
|
|
|
// Suffixes for popularity thresholds in descending order
|
|
var descendingSuffixes = []string{"100000", "50000", "30000", "10000", "5000", "1000", "500", "100", "50"}
|
|
|
|
// SearchResponse represents the JSON structure returned by the Pixiv API for the search endpoint
|
|
type SearchResponse struct {
|
|
IllustManga struct {
|
|
Data []ArtworkBrief `json:"data"`
|
|
} `json:"illustManga"`
|
|
}
|
|
|
|
// isValidResult checks if an artwork meets the search criteria
|
|
func isValidResult(item ArtworkBrief) bool {
|
|
// Check for the fake bookmark count tag
|
|
for _, tag := range item.Tags {
|
|
trimmedTag := strings.TrimSpace(tag)
|
|
if strings.EqualFold(trimmedTag, fakeBookmarkTag) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Invalidate if AI-generated since most fake results are
|
|
if item.AiType == 2 {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// processSearchResults filters and adds valid results to the artworks slice
|
|
func processSearchResults(items []ArtworkBrief, artworks *[]ArtworkBrief) bool {
|
|
for _, item := range items {
|
|
if isValidResult(item) {
|
|
*artworks = append(*artworks, item)
|
|
if len(*artworks) >= resultsLimit {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// fetchResultsForSuffix retrieves all results for a specific popularity threshold
|
|
func fetchResultsForSuffix(ctx context.Context, auditor *audit.Auditor, r *http.Request, settings SearchPageSettings, suffix string, suffixArtworks *[]ArtworkBrief) error {
|
|
page := 1
|
|
|
|
for len(*suffixArtworks) < resultsLimit {
|
|
// Copy settings to avoid modifying the original
|
|
currentSettings := settings
|
|
|
|
// Adjust settings.Name to include the suffix
|
|
currentSettings.Name = fmt.Sprintf("%s%susers入り", settings.Name, suffix)
|
|
|
|
// Set page number
|
|
currentSettings.Page = strconv.Itoa(page)
|
|
|
|
// Generate URL
|
|
url := GetSearchArtworksURL(currentSettings.ReturnMap())
|
|
|
|
auditor.SugaredLogger.Debugw("Fetching search results", "url", url)
|
|
|
|
body, err := API_GET_UnwrapJson(ctx, auditor, url, "", r.Header)
|
|
if err != nil {
|
|
auditor.SugaredLogger.Errorw("Error fetching results", "error", err, "suffix", suffix, "page", page)
|
|
return err
|
|
}
|
|
|
|
proxiedBody, err := session.ProxyImageUrl(r, body)
|
|
if err != nil {
|
|
auditor.SugaredLogger.Errorw("Error proxying image URLs", "error", err)
|
|
return err
|
|
}
|
|
|
|
// Unmarshal the proxied JSON body into SearchResponse
|
|
var resp SearchResponse
|
|
err = json.Unmarshal([]byte(proxiedBody), &resp)
|
|
if err != nil {
|
|
auditor.SugaredLogger.Errorw("Failed to unmarshal JSON", "error", err, "body", proxiedBody)
|
|
return err
|
|
}
|
|
|
|
if len(resp.IllustManga.Data) == 0 {
|
|
auditor.SugaredLogger.Debugw("No more results found", "suffix", suffix, "page", page)
|
|
break
|
|
}
|
|
|
|
reachedLimit := processSearchResults(resp.IllustManga.Data, suffixArtworks)
|
|
if reachedLimit {
|
|
return nil
|
|
}
|
|
|
|
page++
|
|
auditor.SugaredLogger.Debugw("Moving to next page", "suffix", suffix, "page", page)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// searchPopular performs a search for popular artworks using a descending popularity threshold strategy.
|
|
//
|
|
// The search strategy is as follows:
|
|
//
|
|
// 1. Starts by checking the highest popularity threshold (100000 users).
|
|
// - If results are found at the highest threshold, it stores these results.
|
|
// - Continues checking lower thresholds in descending order (e.g., 50000, 30000, etc.),
|
|
// adding results until the resultsLimit is reached.
|
|
//
|
|
// 2. If no results are found at the highest threshold:
|
|
//
|
|
// - Temporarily stores results from the lowest threshold (50 users).
|
|
//
|
|
// - Then, checks all intermediate thresholds (50000 down to 100 users).
|
|
//
|
|
// - Uses the temporarily stored lowest threshold results only if the resultsLimit
|
|
// cannot be reached with results from higher thresholds.
|
|
//
|
|
// 3. Assembles the final list of results in descending order of popularity thresholds,
|
|
// stopping when the total number of results reaches resultsLimit.
|
|
func searchPopular(ctx context.Context, auditor *audit.Auditor, r *http.Request, settings SearchPageSettings, headers http.Header) (SearchArtworks, error) {
|
|
auditor.SugaredLogger.Debugw("Starting popular search", "query", settings.Name, "mode", settings.Category)
|
|
|
|
artworksPerSuffix := make(map[string][]ArtworkBrief)
|
|
var totalArtworks int
|
|
|
|
// Start with highest suffix
|
|
highestSuffix := descendingSuffixes[0]
|
|
auditor.SugaredLogger.Debugw("Searching with highest suffix", "suffix", highestSuffix)
|
|
var highestSuffixArtworks []ArtworkBrief
|
|
err := fetchResultsForSuffix(ctx, auditor, r, settings, highestSuffix, &highestSuffixArtworks)
|
|
if err != nil {
|
|
auditor.SugaredLogger.Errorw("Error fetching results for highest suffix", "suffix", highestSuffix, "error", err)
|
|
return SearchArtworks{}, err
|
|
}
|
|
|
|
if len(highestSuffixArtworks) > 0 {
|
|
// Found results with highest suffix, continue with lower thresholds
|
|
artworksPerSuffix[highestSuffix] = highestSuffixArtworks
|
|
totalArtworks += len(highestSuffixArtworks)
|
|
|
|
// Iterate over the remaining suffixes except the last one (lowest threshold)
|
|
for i := 1; i < len(descendingSuffixes)-1; i++ {
|
|
if totalArtworks >= resultsLimit {
|
|
break
|
|
}
|
|
|
|
suffix := descendingSuffixes[i]
|
|
var suffixArtworks []ArtworkBrief
|
|
err := fetchResultsForSuffix(ctx, auditor, r, settings, suffix, &suffixArtworks)
|
|
if err != nil {
|
|
auditor.SugaredLogger.Errorw("Error fetching results for suffix", "suffix", suffix, "error", err)
|
|
continue
|
|
}
|
|
artworksPerSuffix[suffix] = suffixArtworks
|
|
totalArtworks += len(suffixArtworks)
|
|
}
|
|
} else {
|
|
// No results with highest suffix, check all other thresholds before using lowest
|
|
var lowestSuffixArtworks []ArtworkBrief
|
|
lowestSuffix := descendingSuffixes[len(descendingSuffixes)-1]
|
|
|
|
// Store lowest threshold results temporarily
|
|
err := fetchResultsForSuffix(ctx, auditor, r, settings, lowestSuffix, &lowestSuffixArtworks)
|
|
if err != nil {
|
|
auditor.SugaredLogger.Errorw("Error fetching results for lowest suffix", "suffix", lowestSuffix, "error", err)
|
|
return SearchArtworks{}, err
|
|
}
|
|
|
|
// If no results are found from the lowest threshold, exit early
|
|
if len(lowestSuffixArtworks) == 0 {
|
|
auditor.SugaredLogger.Debugw("No results found for the lowest suffix. Exiting early.", "suffix", lowestSuffix)
|
|
return SearchArtworks{
|
|
Artworks: []ArtworkBrief{},
|
|
Total: 0,
|
|
}, nil
|
|
}
|
|
|
|
// Check intermediate thresholds (50000 down to 100)
|
|
for i := 1; i < len(descendingSuffixes)-1; i++ {
|
|
suffix := descendingSuffixes[i]
|
|
var suffixArtworks []ArtworkBrief
|
|
err := fetchResultsForSuffix(ctx, auditor, r, settings, suffix, &suffixArtworks)
|
|
if err != nil {
|
|
auditor.SugaredLogger.Errorw("Error fetching results for suffix", "suffix", suffix, "error", err)
|
|
continue
|
|
}
|
|
artworksPerSuffix[suffix] = suffixArtworks
|
|
totalArtworks += len(suffixArtworks)
|
|
|
|
if totalArtworks >= resultsLimit {
|
|
break
|
|
}
|
|
}
|
|
|
|
// Only use lowest threshold results if we haven't reached resultsLimit
|
|
if totalArtworks < resultsLimit && len(lowestSuffixArtworks) > 0 {
|
|
artworksPerSuffix[lowestSuffix] = lowestSuffixArtworks
|
|
totalArtworks += len(lowestSuffixArtworks)
|
|
}
|
|
}
|
|
|
|
// Assemble final results in descending order of popularity
|
|
var artworks []ArtworkBrief
|
|
for _, suffix := range descendingSuffixes {
|
|
arts := artworksPerSuffix[suffix]
|
|
if len(artworks)+len(arts) >= resultsLimit {
|
|
artworks = append(artworks, arts[:resultsLimit-len(artworks)]...)
|
|
break
|
|
} else {
|
|
artworks = append(artworks, arts...)
|
|
}
|
|
}
|
|
|
|
total := len(artworks)
|
|
auditor.SugaredLogger.Debugw("Completed popular search", "query", settings.Name, "mode", settings.Category, "resultsCount", total)
|
|
|
|
// // Sort by ID in descending order
|
|
// sort.Slice(artworks, func(i, j int) bool {
|
|
// return artworks[i].ID > artworks[j].ID
|
|
// })
|
|
|
|
return SearchArtworks{
|
|
Artworks: artworks,
|
|
Total: total,
|
|
}, nil
|
|
}
|