mirror of
https://github.com/wgh136/nysoure.git
synced 2025-12-16 15:51:14 +00:00
Get characters from vndb
This commit is contained in:
@@ -282,6 +282,22 @@ func handleGetPinnedResources(c fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleGetCharactorsFromVndb(c fiber.Ctx) error {
|
||||||
|
vnID := c.Query("vnid")
|
||||||
|
if vnID == "" {
|
||||||
|
return model.NewRequestError("VNDB ID is required")
|
||||||
|
}
|
||||||
|
characters, err := service.GetCharactorsFromVndb(vnID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.Status(fiber.StatusOK).JSON(model.Response[[]service.CharactorParams]{
|
||||||
|
Success: true,
|
||||||
|
Data: characters,
|
||||||
|
Message: "Characters retrieved successfully",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func AddResourceRoutes(api fiber.Router) {
|
func AddResourceRoutes(api fiber.Router) {
|
||||||
resource := api.Group("/resource")
|
resource := api.Group("/resource")
|
||||||
{
|
{
|
||||||
@@ -290,6 +306,7 @@ func AddResourceRoutes(api fiber.Router) {
|
|||||||
resource.Get("/", handleListResources)
|
resource.Get("/", handleListResources)
|
||||||
resource.Get("/random", handleGetRandomResource)
|
resource.Get("/random", handleGetRandomResource)
|
||||||
resource.Get("/pinned", handleGetPinnedResources)
|
resource.Get("/pinned", handleGetPinnedResources)
|
||||||
|
resource.Get("/vndb/characters", handleGetCharactorsFromVndb)
|
||||||
resource.Get("/:id", handleGetResource)
|
resource.Get("/:id", handleGetResource)
|
||||||
resource.Delete("/:id", handleDeleteResource)
|
resource.Delete("/:id", handleDeleteResource)
|
||||||
resource.Get("/tag/:tag", handleListResourcesWithTag)
|
resource.Get("/tag/:tag", handleListResourcesWithTag)
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"nysoure/server/config"
|
"nysoure/server/config"
|
||||||
"nysoure/server/dao"
|
"nysoure/server/dao"
|
||||||
@@ -10,6 +14,7 @@ import (
|
|||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v3"
|
"github.com/gofiber/fiber/v3"
|
||||||
"github.com/gofiber/fiber/v3/log"
|
"github.com/gofiber/fiber/v3/log"
|
||||||
@@ -590,3 +595,161 @@ func GetPinnedResources() ([]model.ResourceView, error) {
|
|||||||
}
|
}
|
||||||
return views, nil
|
return views, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetCharactorsFromVndb(vnID string) ([]CharactorParams, error) {
|
||||||
|
client := http.Client{}
|
||||||
|
jsonStr := fmt.Sprintf(`
|
||||||
|
{
|
||||||
|
"filters": ["id", "=", "%s"],
|
||||||
|
"fields": "va.character.name, va.staff.name, va.staff.original, va.character.original, va.character.image.url, va.character.vns.role"
|
||||||
|
}
|
||||||
|
`, vnID)
|
||||||
|
jsonStr = strings.TrimSpace(jsonStr)
|
||||||
|
reader := strings.NewReader(jsonStr)
|
||||||
|
resp, err := client.Post("https://api.vndb.org/kana/vn", "application/json", reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, model.NewInternalServerError("Failed to fetch data from VNDB")
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, model.NewInternalServerError("Failed to fetch data from VNDB")
|
||||||
|
}
|
||||||
|
// 定义 VNDB API 响应结构
|
||||||
|
type VndbResponse struct {
|
||||||
|
Results []struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
VA []struct {
|
||||||
|
Character struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Original string `json:"original"`
|
||||||
|
Image struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
} `json:"image"`
|
||||||
|
VNS []struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Role string `json:"role"`
|
||||||
|
} `json:"vns"`
|
||||||
|
} `json:"character"`
|
||||||
|
Staff struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Original string `json:"original"`
|
||||||
|
} `json:"staff"`
|
||||||
|
} `json:"va"`
|
||||||
|
} `json:"results"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析响应
|
||||||
|
var vndbResp VndbResponse
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&vndbResp); err != nil {
|
||||||
|
return nil, model.NewInternalServerError("Failed to parse VNDB response")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(vndbResp.Results) == 0 {
|
||||||
|
return []CharactorParams{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result := vndbResp.Results[0]
|
||||||
|
var charactors []CharactorParams
|
||||||
|
processedCharacters := make(map[string]bool) // 避免重复角色
|
||||||
|
|
||||||
|
// 遍历声优信息
|
||||||
|
for _, va := range result.VA {
|
||||||
|
// 检查角色是否为主要角色
|
||||||
|
isPrimary := false
|
||||||
|
for _, vn := range va.Character.VNS {
|
||||||
|
if vn.Role == "primary" {
|
||||||
|
isPrimary = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只处理主要角色
|
||||||
|
if !isPrimary {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 避免重复角色
|
||||||
|
if processedCharacters[va.Character.ID] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
processedCharacters[va.Character.ID] = true
|
||||||
|
|
||||||
|
// 优先使用 original 字段作为角色名,如果没有则使用 name
|
||||||
|
characterName := strings.ReplaceAll(va.Character.Original, " ", "")
|
||||||
|
if characterName == "" {
|
||||||
|
characterName = va.Character.Name
|
||||||
|
}
|
||||||
|
if characterName == "" {
|
||||||
|
continue // 跳过没有名字的角色
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 original 字段作为声优名,如果没有则使用 name
|
||||||
|
cvName := strings.ReplaceAll(va.Staff.Original, " ", "")
|
||||||
|
if cvName == "" {
|
||||||
|
cvName = va.Staff.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
charactor := CharactorParams{
|
||||||
|
Name: characterName,
|
||||||
|
Alias: []string{}, // 按要求不添加别名
|
||||||
|
CV: cvName,
|
||||||
|
Image: 0, // 默认值,下面会下载图片
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载并保存角色图片
|
||||||
|
if va.Character.Image.URL != "" {
|
||||||
|
imageID, err := downloadAndCreateImage(va.Character.Image.URL)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to download character image:", err)
|
||||||
|
// 继续处理,即使图片下载失败
|
||||||
|
} else {
|
||||||
|
charactor.Image = imageID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
charactors = append(charactors, charactor)
|
||||||
|
}
|
||||||
|
|
||||||
|
return charactors, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// downloadAndCreateImage 下载图片并使用 CreateImage 保存
|
||||||
|
func downloadAndCreateImage(imageURL string) (uint, error) {
|
||||||
|
// 创建 HTTP 客户端
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载图片
|
||||||
|
resp, err := client.Get(imageURL)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("failed to download image: %w", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return 0, fmt.Errorf("failed to download image: HTTP %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取图片数据
|
||||||
|
imageData, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("failed to read image data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 限制图片大小,防止内存溢出
|
||||||
|
if len(imageData) > 8*1024*1024 { // 8MB 限制
|
||||||
|
return 0, fmt.Errorf("image too large")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用系统用户ID (假设为1) 创建图片
|
||||||
|
// 注意:这里使用系统账户,实际使用时可能需要调整
|
||||||
|
imageID, err := CreateImage(1, "127.0.0.1", imageData)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("failed to create image: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return imageID, nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user