0%

Hexo添加Live2D(Go重构后端)

Go+Gin代理所有静态资源

  1. 远端不再使用nodojs工具,如hexo server等,直接使用go框架代理全部资源,减少部署流程。一个go srever即可启动所有站点

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // 使用Gin框架的Static和StaticFile接口,生成静态资源服务器
    func (u *StaticDir) Bind() {
    for _, param := range config_site.Site.ParamStaticService {
    println("路由路径:", param.Path, "=>", param.Index)
    dirs, err := os.ReadDir(param.Path)
    if err != nil {
    log.Println("读取静态文件夹失败:", param.Path)
    continue
    }

    for _, fi := range dirs {
    fi.Name()
    if fi.IsDir() {
    u.Ge.Static(param.Index+fi.Name(), param.Path+fi.Name())
    // fmt.Println("路由目录:", param.Index+fi.Name(), param.Path+fi.Name())
    } else {
    u.Ge.StaticFile(param.Index+fi.Name(), param.Path+fi.Name())
    // fmt.Println("路由文件:", param.Index+fi.Name(), param.Path+fi.Name())
    }
    }
    u.Ge.StaticFile(param.Index, param.Path+"index.html")
    }
    }
  2. Live2D的资源,也可采用同样方式

    1
    2
    # 通过配置表,可以使gin指向不同的静态资源
    { index: /live2d_api/model/, path: ../live2d_api/model/ }

Go重写php接口

原live2d-widget给出的示例接口,是通过php实现。需在站点部署php、php-fpm等工具,麻烦还经常抽风。遂放弃使用php自行重写

这里选择直接套用现成的开源代码live2d_api_go,并做如部分修改,达到原php接口的效果:

  1. 将net/http库,改为gin库

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // old
    mux := http.NewServeMux()
    mux.Handle("/live2Dmodel/", http.StripPrefix("/live2Dmodel/", fs))
    mux.HandleFunc("/get/", GetModel)
    mux.HandleFunc("/switch/", SwitchModel)
    mux.HandleFunc("/rand_textures/", RandomTextures)

    // new
    live2dRouter := u.Ge.Group("/live2d_api")
    {
    live2dRouter.GET("/get/", u.getModel)
    live2dRouter.GET("/switch/", u.switchModel)
    live2dRouter.GET("/rand_textures/", u.randomTextures)
    }
  2. 修改get方法参数获取方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    // old
    func GetModel(w http.ResponseWriter, req *http.Request) {
    ids, ok := req.URL.Query()["id"]
    var id string
    if !ok || len(ids) < 1 {
    //log.Printf("err")
    //error handling
    w.Write([]byte("error"))
    return
    } else {
    id = ids[0]
    }
    // --- skip ---
    }

    // new
    func (u *Live2D) getModel(c *gin.Context) {
    id := c.Query("id")
    if len(id) < 1 {
    // c.String(http.StatusNotFound, "error")
    // return
    id = "0-0"
    }
    // --- skip ---
    }
  3. 调整rand_textures接口。原接口仅根据model_list.json文件,来返回可以使用的皮肤。
    但部分模型未将皮肤信息写入model_list.json中,而是需要读取模型中的textures.cache文件,并替换index.json中的textures字段,来实现换装

  • 修改/rand_textures/接口,使其可以随机出textures.cache中的皮肤

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    // old
    func RandomTextures(w http.ResponseWriter, req *http.Request) {
    // --- skip ---
    switch modelList.Models[modelId].(type) {
    case string:
    resp.Textures.ID = textureId
    case []interface{}:
    length := len(modelList.Models[modelId].([]interface{}))
    resp.Textures.ID = rand.Intn(length)
    for resp.Textures.ID == textureId {
    resp.Textures.ID = rand.Intn(length)
    }

    }
    // --- skip ---
    }

    // new
    func (u *Live2D) randomTextures(c *gin.Context) {
    // --- skip ---
    switch modelList.Models[modelId].(type) {
    case string:
    resp.Textures.ID = textureId
    // 这里判断一下textures中是否有换装资源
    resp.Textures.ID = getTexturesID(modelList.Models[modelId].(string), textureId)
    case []interface{}:
    length := len(modelList.Models[modelId].([]interface{}))
    resp.Textures.ID = rand.Intn(length)
    for resp.Textures.ID == textureId {
    resp.Textures.ID = rand.Intn(length)
    }

    }
    // --- skip ---
    }

    // 从textures.cache中获取id
    func getTexturesID(path string, id int) int {
    cacheFile, err := os.Open(live2d_resources + "model/" + path + "/textures.cache")
    if err != nil {
    return id
    }
    defer cacheFile.Close()
    byteValue, _ := io.ReadAll(cacheFile)
    var list []interface{}
    json.Unmarshal(byteValue, &list)

    length := len(list)
    newId := rand.Intn(length)
    for newId == id {
    newId = rand.Intn(length)
    }
    return newId
    }
  • 修改/get/接口,使其返回的indox.json中的textures字段可变

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    // old
    func GetModel(w http.ResponseWriter, req *http.Request) {
    // --- skip ---
    var path string
    switch modelList.Models[modelId].(type) {
    case string:
    path = modelList.Models[modelId].(string)
    case []interface{}:
    path = modelList.Models[modelId].([]interface{})[textureId].(string)

    }

    model := GetModelData(path)
    // --- skip ---
    }

    // new
    func (u *Live2D) getModel(c *gin.Context) {
    // --- skip ---
    var path string
    switch modelList.Models[modelId].(type) {
    case string:
    path = modelList.Models[modelId].(string)
    case []interface{}:
    path = modelList.Models[modelId].([]interface{})[textureId].(string)
    textureId = -1
    }

    model := getModelData(path, textureId)
    // --- skip ---
    }

    // 获取index.json
    func getModelData(path string, texId int) ModelData {
    // Open our jsonFile
    jsonFile, err := os.Open(live2d_resources + "model/" + path + "/index.json")
    // if we os.Open returns an error then handle it
    if err != nil {
    fmt.Println(err)
    }
    // defer the closing of our jsonFile so that we can parse it later on
    defer jsonFile.Close()
    byteValue, _ := io.ReadAll(jsonFile)
    var model ModelData
    json.Unmarshal(byteValue, &model)

    if texId > -1 {
    getTextures(&model, path, texId)
    }

    changeDataPathInModel(&model, path)
    return model
    }

    // 获取textures.cache中的值
    func getTextures(data *ModelData, path string, id int) {
    cacheFile, err := os.Open(live2d_resources + "model/" + path + "/textures.cache")
    if err != nil {
    return
    }
    defer cacheFile.Close()
    byteValue, _ := io.ReadAll(cacheFile)
    var list []interface{}
    json.Unmarshal(byteValue, &list)

    switch list[id].(type) {
    case string:
    data.Textures = []string{list[id].(string)}
    case []interface{}:
    data.Textures = []string{}
    for _, s := range list[id].([]interface{}) {
    data.Textures = append(data.Textures, s.(string))
    }
    }
    }