diff --git a/assets/views/fragments/thumbnail-dt.jet.html b/assets/views/fragments/thumbnail-dt.jet.html
index c7e6b83..e9fe895 100644
--- a/assets/views/fragments/thumbnail-dt.jet.html
+++ b/assets/views/fragments/thumbnail-dt.jet.html
@@ -41,7 +41,12 @@
{{- else if AiType == 2 && hideAi }}
{{- else }}
-
+
{{- end }}
diff --git a/assets/views/mangaSeries.jet.html b/assets/views/mangaSeries.jet.html
index 3783855..92f91a4 100644
--- a/assets/views/mangaSeries.jet.html
+++ b/assets/views/mangaSeries.jet.html
@@ -17,7 +17,7 @@
diff --git a/core/artwork.go b/core/artwork.go
index 70d4196..bbbd7ee 100644
--- a/core/artwork.go
+++ b/core/artwork.go
@@ -113,18 +113,13 @@ type ArtworkBrief struct {
ArtistName string `json:"userName"`
ArtistAvatar string `json:"profileImageUrl"`
Thumbnail string `json:"url"`
- Thumbnails struct {
- Small string `json:"250x250"`
- Medium string `json:"360x360"`
- Large string `json:"540x540"`
- Master string `json:"1200x1200"`
- } `json:"urls"`
- Pages int `json:"pageCount"`
- XRestrict int `json:"xRestrict"`
- AiType int `json:"aiType"`
- Bookmarked any `json:"bookmarkData"`
- IllustType int `json:"illustType"`
- Tags []string `json:"tags"` // used by core/popular_search
+ Thumbnails Thumbnails
+ Pages int `json:"pageCount"`
+ XRestrict int `json:"xRestrict"`
+ AiType int `json:"aiType"`
+ Bookmarked any `json:"bookmarkData"`
+ IllustType int `json:"illustType"`
+ Tags []string `json:"tags"` // used by core/popular_search
}
type Illust struct {
@@ -267,6 +262,15 @@ func GetRelatedArtworks(auditor *audit.Auditor, r *http.Request, id string) ([]A
return nil, err
}
+ // Populate thumbnails for each artwork
+ for id, artwork := range body.Illusts {
+ if err := artwork.PopulateThumbnails(auditor); err != nil {
+ auditor.SugaredLogger.Errorf("Failed to populate thumbnails for artwork ID %s: %v", id, err)
+ return nil, fmt.Errorf("failed to populate thumbnails for artwork ID %s: %w", id, err)
+ }
+ body.Illusts[id] = artwork
+ }
+
return body.Illusts, nil
}
@@ -381,6 +385,16 @@ func GetArtworkByID(auditor *audit.Auditor, r *http.Request, id string, full boo
sort.Slice(recent[:], func(i, j int) bool {
return numberGreaterThan(recent[i].ID, recent[j].ID)
})
+
+ // Populate thumbnails for each artwork
+ for id, artwork := range recent {
+ if err := artwork.PopulateThumbnails(auditor); err != nil {
+ auditor.SugaredLogger.Errorf("Failed to populate thumbnails for artwork ID %s: %v", id, err)
+ return fmt.Errorf("failed to populate thumbnails for artwork ID %s: %w", id, err)
+ }
+ recent[id] = artwork
+ }
+
illust.RecentWorks = recent
return nil
})
diff --git a/core/discovery.go b/core/discovery.go
index 9254419..62623c6 100644
--- a/core/discovery.go
+++ b/core/discovery.go
@@ -39,6 +39,15 @@ func GetDiscoveryArtwork(auditor *audit.Auditor, r *http.Request, mode string) (
return nil, err
}
+ // Populate thumbnails for each artwork
+ for id, artwork := range artworks {
+ if err := artwork.PopulateThumbnails(auditor); err != nil {
+ auditor.SugaredLogger.Errorf("Failed to populate thumbnails for artwork ID %s: %v", id, err)
+ return nil, fmt.Errorf("failed to populate thumbnails for artwork ID %s: %w", id, err)
+ }
+ artworks[id] = artwork
+ }
+
return artworks, nil
}
@@ -108,6 +117,15 @@ func GetDiscoveryUsers(auditor *audit.Auditor, r *http.Request) ([]User, error)
return nil, fmt.Errorf("error unmarshalling artworks in GetDiscoveryUser: %w", err)
}
+ // Populate thumbnails for each artwork
+ for id, artwork := range artworks {
+ if err := artwork.PopulateThumbnails(auditor); err != nil {
+ auditor.SugaredLogger.Errorf("Failed to populate thumbnails for artwork ID %s: %v", id, err)
+ return nil, fmt.Errorf("failed to populate thumbnails for artwork ID %s: %w", id, err)
+ }
+ artworks[id] = artwork
+ }
+
// Extract and unmarshal novels
novelData := gjson.Get(resp, "thumbnails.novel").Raw
var novels []NovelBrief
diff --git a/core/index.go b/core/index.go
index f139604..83732be 100644
--- a/core/index.go
+++ b/core/index.go
@@ -99,6 +99,15 @@ func GetLanding(auditor *audit.Auditor, r *http.Request, mode string, isLoggedIn
return nil, err
}
+ // Populate thumbnails for each artwork
+ for id, artwork := range artworks {
+ if err := artwork.PopulateThumbnails(auditor); err != nil {
+ auditor.SugaredLogger.Errorf("Failed to populate thumbnails for artwork ID %s: %v", id, err)
+ return nil, fmt.Errorf("failed to populate thumbnails for artwork ID %s: %w", id, err)
+ }
+ artworks[id] = artwork
+ }
+
var pages Pages
pagesStr := gjson.Get(resp, "page").String()
diff --git a/core/mangaseries.go b/core/mangaseries.go
index b269560..5b0852b 100644
--- a/core/mangaseries.go
+++ b/core/mangaseries.go
@@ -1,6 +1,7 @@
package core
import (
+ "fmt"
"net/http"
"strconv"
"time"
@@ -10,6 +11,7 @@ import (
"github.com/goccy/go-json"
)
+// MangaSeries represents the metadata of a manga series.
type MangaSeries struct {
ID string `json:"id"`
UserID string `json:"userId"`
@@ -18,7 +20,7 @@ type MangaSeries struct {
Caption string `json:"caption"`
Total int `json:"total"`
ContentOrder any `json:"content_order"`
- URL string `json:"url"`
+ Thumbnail string `json:"url"`
CoverImageSl int `json:"coverImageSl"`
FirstIllustID string `json:"firstIllustId"`
LatestIllustID string `json:"latestIllustId"`
@@ -29,6 +31,7 @@ type MangaSeries struct {
IsNotifying bool `json:"isNotifying"`
}
+// MangaSeriesContent encapsulates the content details of a manga series.
type MangaSeriesContent struct {
Series []struct {
WorkID string `json:"workId"`
@@ -45,6 +48,7 @@ type MangaSeriesContent struct {
Brief MangaSeries
}
+// GetMangaSeriesContentByID retrieves the content of a manga series by its ID and page number.
func GetMangaSeriesContentByID(auditor *audit.Auditor, r *http.Request, id string, page int) (MangaSeriesContent, error) {
var series_content MangaSeriesContent
@@ -60,31 +64,48 @@ func GetMangaSeriesContentByID(auditor *audit.Auditor, r *http.Request, id strin
return series_content, err
}
- var mangas struct {
+ // Define a temporary struct to match the expected JSON response from the API
+ var manga struct {
Thumbnails struct {
- List []ArtworkBrief `json:"illust"`
+ List []ArtworkBrief `json:"illust"` // List of thumbnail images for artworks
} `json:"thumbnails"`
- IllustSeries []MangaSeries `json:"illustSeries"`
- Page MangaSeriesContent `json:"page"`
+ IllustSeries []MangaSeries `json:"illustSeries"` // List of illustration series metadata
+ Page MangaSeriesContent `json:"page"` // Page-specific content and metadata
}
- err = json.Unmarshal([]byte(resp), &mangas)
+ // Unmarshal the JSON response into the temporary "manga" struct
+ err = json.Unmarshal([]byte(resp), &manga)
if err != nil {
return series_content, err
}
- series_content = mangas.Page
+ // Assign the page-specific content to the main series_content variable
+ series_content = manga.Page
+
+ // Iterate over each series entry to populate the Brief field with corresponding artwork details
for idx, content := range series_content.Series {
- for _, item := range mangas.Thumbnails.List {
+ for _, item := range manga.Thumbnails.List {
if item.ID == content.WorkID {
+ // If a match is found, assign the ArtworkBrief to the current series entry
series_content.Series[idx].Brief = item
+
+ // Populate thumbnails
+ err := series_content.Series[idx].Brief.PopulateThumbnails(auditor)
+ if err != nil {
+ return series_content, fmt.Errorf("failed to populate thumbnails for artwork ID %s: %w", id, err)
+ }
+
+ // Break the inner loop with the matching artwork having been found and processed
break
}
}
}
- for _, brief := range mangas.IllustSeries {
+ // Iterate over IllustSeries to find and assign the Brief metadata for the series
+ for _, brief := range manga.IllustSeries {
+ // Compare the series ID from the API response with the series_content's SeriesID
if brief.ID == strconv.Itoa(series_content.SeriesID) {
+ // Assign the matching MangaSeries metadata to the Brief field of series_content
series_content.Brief = brief
break
}
diff --git a/core/newest.go b/core/newest.go
index 288b559..db94213 100644
--- a/core/newest.go
+++ b/core/newest.go
@@ -1,6 +1,7 @@
package core
import (
+ "fmt"
"net/http"
"codeberg.org/vnpower/pixivfe/v2/audit"
@@ -31,5 +32,14 @@ func GetNewestArtworks(auditor *audit.Auditor, r *http.Request, worktype string,
return nil, err
}
+ // Populate thumbnails for each artwork
+ for id, artwork := range body.Artworks {
+ if err := artwork.PopulateThumbnails(auditor); err != nil {
+ auditor.SugaredLogger.Errorf("Failed to populate thumbnails for artwork ID %s: %v", id, err)
+ return nil, fmt.Errorf("failed to populate thumbnails for artwork ID %s: %w", id, err)
+ }
+ body.Artworks[id] = artwork
+ }
+
return body.Artworks, nil
}
diff --git a/core/personal.go b/core/personal.go
index 8ebf68e..de0a966 100644
--- a/core/personal.go
+++ b/core/personal.go
@@ -1,6 +1,7 @@
package core
import (
+ "fmt"
"net/http"
"codeberg.org/vnpower/pixivfe/v2/audit"
@@ -40,5 +41,14 @@ func GetNewestFromFollowing(auditor *audit.Auditor, r *http.Request, mode, page
return nil, err
}
+ // Populate thumbnails for each artwork
+ for id, artwork := range artworks.Artworks {
+ if err := artwork.PopulateThumbnails(auditor); err != nil {
+ auditor.SugaredLogger.Errorf("Failed to populate thumbnails for artwork ID %s: %v", id, err)
+ return nil, fmt.Errorf("failed to populate thumbnails for artwork ID %s: %w", id, err)
+ }
+ artworks.Artworks[id] = artwork
+ }
+
return artworks.Artworks, nil
}
diff --git a/core/ranking.go b/core/ranking.go
index 4d6ef50..2081d8d 100644
--- a/core/ranking.go
+++ b/core/ranking.go
@@ -1,6 +1,7 @@
package core
import (
+ "fmt"
"net/http"
"strings"
"time"
@@ -29,30 +30,34 @@ const (
)
type Ranking struct {
- Contents []struct {
- Title string `json:"title"`
- Thumbnail string `json:"url"`
- Pages int `json:"illust_page_count,string"`
- ArtistName string `json:"user_name"`
- ArtistAvatar string `json:"profile_img"`
- ID int `json:"illust_id"`
- ArtistID int `json:"user_id"`
- Rank int `json:"rank"`
- IllustType int `json:"illust_type,string"`
- XRestrict int // zero field for template
- } `json:"contents"`
-
- Mode string `json:"mode"`
- Content string `json:"content"`
- Page int `json:"page"`
- RankTotal int `json:"rank_total"`
- CurrentDate string `json:"date"`
- PrevDateRaw json.RawMessage `json:"prev_date"`
- NextDateRaw json.RawMessage `json:"next_date"`
+ Contents []RankingContent `json:"contents"`
+ Mode string `json:"mode"`
+ Content string `json:"content"`
+ Page int `json:"page"`
+ RankTotal int `json:"rank_total"`
+ CurrentDate string `json:"date"`
+ PrevDateRaw json.RawMessage `json:"prev_date"`
+ NextDateRaw json.RawMessage `json:"next_date"`
PrevDate string
NextDate string
}
+type RankingContent struct {
+ Title string `json:"title"`
+ Thumbnail string `json:"url"`
+ Thumbnails Thumbnails
+ Pages int `json:"illust_page_count,string"`
+ ArtistName string `json:"user_name"`
+ ArtistAvatar string `json:"profile_img"`
+ ID int `json:"illust_id"`
+ ArtistID int `json:"user_id"`
+ Rank int `json:"rank"`
+ IllustType int `json:"illust_type,string"`
+ XRestrict int // zero field for template
+}
+
+// see core/types.go for the Ranking struct
+
func GetRanking(auditor *audit.Auditor, r *http.Request, mode, content, date, page string) (Ranking, error) {
// log.Printf("GetRanking called with mode: %s, content: %s, date: %s, page: %s", mode, content, date, page)
@@ -101,6 +106,11 @@ func GetRanking(auditor *audit.Auditor, r *http.Request, mode, content, date, pa
ranking.PrevDate = strings.ReplaceAll(string(ranking.PrevDateRaw[:]), "\"", "")
ranking.NextDate = strings.ReplaceAll(string(ranking.NextDateRaw[:]), "\"", "")
+ // Populate thumbnails
+ if err := ranking.PopulateThumbnails(auditor); err != nil {
+ return ranking, fmt.Errorf("failed to populate thumbnails for ranking: %w", err)
+ }
+
// log.Printf("GetRanking completed successfully. PrevDate: %s, NextDate: %s", ranking.PrevDate, ranking.NextDate)
return ranking, nil
diff --git a/core/tag.go b/core/tag.go
index 4b74423..be03595 100644
--- a/core/tag.go
+++ b/core/tag.go
@@ -1,6 +1,7 @@
package core
import (
+ "fmt"
"net/http"
"net/url"
"regexp"
@@ -167,6 +168,15 @@ func getPopularSearch(auditor *audit.Auditor, r *http.Request, settings SearchPa
// TODO: populate Popular (the regular one) and RelatedTags
}
+ // Populate thumbnails for each artwork
+ for id, artwork := range result.Artworks.Artworks {
+ if err := artwork.PopulateThumbnails(auditor); err != nil {
+ auditor.SugaredLogger.Errorf("Failed to populate thumbnails for artwork ID %s: %v", id, err)
+ return nil, fmt.Errorf("failed to populate thumbnails for artwork ID %s: %w", id, err)
+ }
+ result.Artworks.Artworks[id] = artwork
+ }
+
return result, nil
}
@@ -217,6 +227,31 @@ func getStandardSearch(auditor *audit.Auditor, r *http.Request, settings SearchP
return nil, err
}
+ // Populate thumbnails for each artwork
+ for id, artwork := range artworks.Artworks {
+ if err := artwork.PopulateThumbnails(auditor); err != nil {
+ auditor.SugaredLogger.Errorf("Failed to populate thumbnails for artwork ID %s: %v", id, err)
+ return nil, fmt.Errorf("failed to populate thumbnails for artwork ID %s: %w", id, err)
+ }
+ artworks.Artworks[id] = artwork
+ }
+
+ for id, artwork := range resultRaw.Popular.Permanent {
+ if err := artwork.PopulateThumbnails(auditor); err != nil {
+ auditor.SugaredLogger.Errorf("Failed to populate thumbnails for artwork ID %s: %v", id, err)
+ return nil, fmt.Errorf("failed to populate thumbnails for artwork ID %s: %w", id, err)
+ }
+ resultRaw.Popular.Permanent[id] = artwork
+ }
+
+ for id, artwork := range resultRaw.Popular.Recent {
+ if err := artwork.PopulateThumbnails(auditor); err != nil {
+ auditor.SugaredLogger.Errorf("Failed to populate thumbnails for artwork ID %s: %v", id, err)
+ return nil, fmt.Errorf("failed to populate thumbnails for artwork ID %s: %w", id, err)
+ }
+ resultRaw.Popular.Recent[id] = artwork
+ }
+
// Assign artworks and popular data to the result
result.Artworks = artworks
result.Popular = resultRaw.Popular
diff --git a/core/types.go b/core/types.go
index fcceb8b..c2c4984 100644
--- a/core/types.go
+++ b/core/types.go
@@ -1,8 +1,12 @@
package core
import (
+ "fmt"
"html/template"
"net/http"
+ "net/url"
+ "path"
+ "regexp"
"codeberg.org/vnpower/pixivfe/v2/audit"
)
@@ -14,3 +18,110 @@ type RequestContext struct {
auditor *audit.Auditor
request *http.Request
}
+
+// Thumbnails encapsulates all thumbnail sizes.
+type Thumbnails struct {
+ Tiny string
+ Small string
+ Medium string
+ Large string
+ Original string
+}
+
+func (work *ArtworkBrief) PopulateThumbnails(auditor *audit.Auditor) error {
+ thumbnails, err := PopulateThumbnailsFor(auditor, work.Thumbnail)
+ if err != nil {
+ return err
+ }
+ work.Thumbnails = thumbnails
+ return nil
+}
+
+func (r *Ranking) PopulateThumbnails(auditor *audit.Auditor) error {
+ for i := range r.Contents {
+ err := r.Contents[i].PopulateThumbnails(auditor)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (content *RankingContent) PopulateThumbnails(auditor *audit.Auditor) error {
+ thumbnails, err := PopulateThumbnailsFor(auditor, content.Thumbnail)
+ if err != nil {
+ return err
+ }
+ content.Thumbnails = thumbnails
+ return nil
+}
+
+// PopulateThumbnailsFor is a helper function
+func PopulateThumbnailsFor(auditor *audit.Auditor, thumbnailURL string) (Thumbnails, error) {
+ var thumbnails Thumbnails
+
+ // auditor.SugaredLogger.Debugf("PopulateThumbnails called with Thumbnail URL: %s", thumbnailURL)
+
+ // Parse the original Thumbnail URL to ensure it's valid
+ parsedURL, err := url.Parse(thumbnailURL)
+ if err != nil {
+ auditor.SugaredLogger.Errorf("Invalid Thumbnail URL '%s': %v", thumbnailURL, err)
+ return thumbnails, fmt.Errorf("invalid Thumbnail URL: %w", err)
+ }
+
+ // Define a regex to match the "/c/{parameters}/" segment
+ re := regexp.MustCompile(`/c/[^/]+/`)
+
+ // Verify that the Thumbnail URL contains the expected pattern
+ if !re.MatchString(parsedURL.Path) {
+ auditor.SugaredLogger.Errorf("Thumbnail URL does not match expected pattern: %s", thumbnailURL)
+ return thumbnails, fmt.Errorf("Thumbnail URL does not match expected pattern")
+ }
+
+ // Define the desired sizes for the thumbnails along with corresponding fields
+ thumbSizes := []struct {
+ name string
+ size string
+ field *string
+ }{
+ {"Tiny", "250x250", &thumbnails.Tiny},
+ {"Small", "360x360", &thumbnails.Small},
+ {"Medium", "540x540", &thumbnails.Medium},
+ {"Large", "1200x1200", &thumbnails.Large},
+ }
+
+ // Iterate over each size and generate the corresponding URL
+ for _, thumb := range thumbSizes {
+ finalURL := generateThumbnailURL(parsedURL, re, thumb.size)
+ *thumb.field = finalURL
+ // auditor.SugaredLogger.Debugf("Set Thumbnails.%s to: %s", thumb.name, finalURL)
+ }
+
+ // Construct the Original URL by removing the "/c/{size}_{quality}/" segment
+ finalOriginalURL := generateOriginalURL(parsedURL, re)
+ thumbnails.Original = finalOriginalURL
+
+ // auditor.SugaredLogger.Debugf("Set Thumbnails.Original to: %s", finalOriginalURL)
+
+ return thumbnails, nil
+}
+
+// generateThumbnailURL constructs a thumbnail URL for a given size.
+func generateThumbnailURL(parsedURL *url.URL, re *regexp.Regexp, size string) string {
+ // Replace the existing size and quality segment with the new size
+ newPath := re.ReplaceAllString(parsedURL.Path, fmt.Sprintf("/c/%s/", size))
+ updatedURL := *parsedURL // Copy
+ updatedURL.Path = newPath
+ return updatedURL.String()
+}
+
+// generateOriginalURL constructs the original image URL by removing the size and quality segment.
+func generateOriginalURL(parsedURL *url.URL, re *regexp.Regexp) string {
+ // Remove the size and quality segment
+ originalPath := re.ReplaceAllString(parsedURL.Path, "/")
+ // Clean up the path to ensure it's correctly formatted
+ originalPath = path.Clean(originalPath)
+ originalURL := *parsedURL
+ originalURL.Path = originalPath
+ return originalURL.String()
+}
diff --git a/core/user.go b/core/user.go
index 64e3fc9..a09ded0 100644
--- a/core/user.go
+++ b/core/user.go
@@ -199,7 +199,7 @@ func getPopulatedWorks(auditor *audit.Auditor, r *http.Request, user *User, id s
switch cat.Value {
case "illustrations", "manga":
auditor.Logger.Debug("Fetching and populating ArtworkBriefs", zap.String("category", cat.Value))
- artworks, err := fetchPopulatedBriefs[ArtworkBrief](auditor, r, id, cat.WorkIDs, fetchArtworkIDs)
+ artworks, err := fetchPopulatedArtworkBriefs(auditor, r, id, cat.WorkIDs, fetchArtworkIDs)
if err != nil {
auditor.SugaredLogger.Error("Failed to fetch populated ArtworkBriefs", zap.Error(err))
return err
@@ -208,7 +208,7 @@ func getPopulatedWorks(auditor *audit.Auditor, r *http.Request, user *User, id s
case "novels":
auditor.Logger.Debug("Fetching and populating NovelBriefs", zap.String("category", cat.Value))
- novels, err := fetchPopulatedBriefs[NovelBrief](auditor, r, id, cat.WorkIDs, fetchNovelIDs)
+ novels, err := fetchPopulatedNovelBriefs(auditor, r, id, cat.WorkIDs, fetchNovelIDs)
if err != nil {
auditor.SugaredLogger.Error("Failed to fetch populated NovelBriefs", zap.Error(err))
return err
@@ -407,37 +407,50 @@ func extractIDs(rawMessage *json.RawMessage, categoryName string) ([]int, int, e
return ids, len(dataMap), nil
}
-// The HasID interface allows for generic handling of the ArtworkBrief
-// and NovelBrief structs, which both contain an ID string.
-type HasID interface {
- GetID() string
-}
-
-// GetID implements the HasID interface for the ArtworkBrief struct.
-func (a ArtworkBrief) GetID() string {
- return a.ID
-}
-
-// GetID implements the HasID interface for the NovelBrief struct.
-func (n NovelBrief) GetID() string {
- return n.ID
-}
-
-// fetchPopulatedBriefs is a generic function that fetches and processes items of type T,
-// where T must implement the HasID interface.
-func fetchPopulatedBriefs[T HasID](
+// fetchPopulatedArtworkBriefs fetches and processes ArtworkBrief items.
+func fetchPopulatedArtworkBriefs(
auditor *audit.Auditor,
r *http.Request,
id, ids string,
- fetchFunc func(*audit.Auditor, *http.Request, string, string) ([]T, error),
-) ([]T, error) {
+ fetchFunc func(*audit.Auditor, *http.Request, string, string) ([]ArtworkBrief, error),
+) ([]ArtworkBrief, error) {
items, err := fetchFunc(auditor, r, id, ids)
if err != nil {
return nil, err
}
+ // Sort the items based on ID in descending order
sort.Slice(items, func(i, j int) bool {
- return numberGreaterThan(items[i].GetID(), items[j].GetID())
+ return numberGreaterThan(items[i].ID, items[j].ID)
+ })
+
+ // Populate thumbnails for each artwork
+ for idx := range items {
+ artwork := &items[idx]
+ if err := artwork.PopulateThumbnails(auditor); err != nil {
+ auditor.SugaredLogger.Errorf("Failed to populate thumbnails for artwork ID %s: %v", artwork.ID, err)
+ return nil, fmt.Errorf("failed to populate thumbnails for artwork ID %s: %w", artwork.ID, err)
+ }
+ }
+
+ return items, nil
+}
+
+// fetchPopulatedNovelBriefs fetches and processes NovelBrief items.
+func fetchPopulatedNovelBriefs(
+ auditor *audit.Auditor,
+ r *http.Request,
+ id, ids string,
+ fetchFunc func(*audit.Auditor, *http.Request, string, string) ([]NovelBrief, error),
+) ([]NovelBrief, error) {
+ items, err := fetchFunc(auditor, r, id, ids)
+ if err != nil {
+ return nil, err
+ }
+
+ // Sort the items based on ID in descending order
+ sort.Slice(items, func(i, j int) bool {
+ return numberGreaterThan(items[i].ID, items[j].ID)
})
return items, nil
@@ -585,6 +598,12 @@ func fetchBookmarks(auditor *audit.Auditor, r *http.Request, id, mode string, pa
}
continue
}
+
+ // Populate thumbnails
+ if err := artwork.PopulateThumbnails(auditor); err != nil {
+ auditor.SugaredLogger.Errorf("Failed to populate thumbnails for artwork ID %s: %v", id, err)
+ return nil, -1, fmt.Errorf("failed to populate thumbnails for artwork ID %s: %w", id, err)
+ }
artworks[index] = artwork
}