mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-05 13:06:36 +08:00
优化
This commit is contained in:
@@ -248,10 +248,16 @@ func (a *AnalyzeRule) putRule(putMap map[string]string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AnalyzeRule) makeDeps() *RuleDeps {
|
||||
// makeDeps 构造 MakeUpRule 展开内嵌 {{...}} 需要的执行环境。
|
||||
//
|
||||
// content 是当前正在解析的元素。legado 的内嵌规则({{$.x}} / {{@x}})在**当前元素**
|
||||
// 上求值:书源普遍用 `{{$.status}},{{$.score}}` 这种模板拼 kind、用
|
||||
// `{{$.source}} {{$.last_chapter_title}}` 拼最新章节。若拿整份响应去求值,
|
||||
// 这些模板会全部取空——表现为 kind=",,,"、"最新"为空(聚合源尤其明显)。
|
||||
func (a *AnalyzeRule) makeDeps(content any) *RuleDeps {
|
||||
return &RuleDeps{
|
||||
JS: a.evalJS,
|
||||
Rule: func(rule string) (string, error) { return a.GetString(rule, nil, false) },
|
||||
Rule: func(rule string) (string, error) { return a.GetString(rule, content, false) },
|
||||
Get: a.Get,
|
||||
}
|
||||
}
|
||||
@@ -279,7 +285,7 @@ func (a *AnalyzeRule) getStringListRules(ruleList []*SourceRule, mContent any, i
|
||||
if err := a.putRule(sourceRule.putMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resolved, err := sourceRule.MakeUpRule(result, a.makeDeps())
|
||||
resolved, err := sourceRule.MakeUpRule(result, a.makeDeps(result))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -378,7 +384,7 @@ func (a *AnalyzeRule) getStringRules(ruleList []*SourceRule, mContent any, isUrl
|
||||
if err := a.putRule(sourceRule.putMap); err != nil {
|
||||
return "", err
|
||||
}
|
||||
resolved, err := sourceRule.MakeUpRule(result, a.makeDeps())
|
||||
resolved, err := sourceRule.MakeUpRule(result, a.makeDeps(result))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -423,7 +429,14 @@ func (a *AnalyzeRule) getStringRules(ruleList []*SourceRule, mContent any, isUrl
|
||||
}
|
||||
if isUrl {
|
||||
if strings.TrimSpace(str) == "" {
|
||||
return a.baseUrl, nil
|
||||
// 对应 legado:取值为空时回退 baseUrl,让相对地址还能解析。
|
||||
// 但 baseUrl 未必是地址:聚合类书源(如「光遇聚合」)的搜索请求地址
|
||||
// 本身就是 data:;base64,... 参数信封,直接回退会把信封当成封面/书址
|
||||
// 返回,前端 <img> 只能显示破图。故仅在 baseUrl 是 http(s) 地址时回退。
|
||||
if base := strings.TrimSpace(a.baseUrl); isAbsURL(base) {
|
||||
return base, nil
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
return a.absolutize(str), nil
|
||||
}
|
||||
@@ -444,7 +457,7 @@ func (a *AnalyzeRule) GetElement(ruleStr string) (any, error) {
|
||||
if err := a.putRule(sourceRule.putMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resolved, err := sourceRule.MakeUpRule(result, a.makeDeps())
|
||||
resolved, err := sourceRule.MakeUpRule(result, a.makeDeps(result))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package rule
|
||||
|
||||
import "testing"
|
||||
|
||||
// 聚合类书源(如「光遇聚合」)的搜索请求地址本身就是 data:;base64,... 参数信封。
|
||||
// 规则取值为空时 legado 语义会回退 baseUrl;若此时的 baseUrl 不是 http(s) 地址,
|
||||
// 回退结果会把封面/书址污染成非图片文本(前端 <img> 直接破图)。
|
||||
func TestGetStringURLFallbackSkipsNonHTTPBaseURL(t *testing.T) {
|
||||
const payload = `{"data":[{"book_name":"全球高武","author":"老鹰吃小鸡"}]}`
|
||||
const envelope = "data:;base64,eyJrZXkiOiLlhajnkIPpq5jmraYiLCJ0YWIiOiLlsI/or7QiLCJzb3VyY2VzS2V5Ijoi5YWo6YOoIiwicGFnZSI6MSwiZGlzYWJsZWRfc291cmNlcyI6IjAifQ=="
|
||||
|
||||
elementOf := func(t *testing.T, content, baseURL string) (*AnalyzeRule, any) {
|
||||
t.Helper()
|
||||
a := NewAnalyzeRule()
|
||||
a.SetContent(content, baseURL)
|
||||
els, err := a.GetElements("$.data")
|
||||
if err != nil {
|
||||
t.Fatalf("GetElements: %v", err)
|
||||
}
|
||||
if len(els) != 1 {
|
||||
t.Fatalf("GetElements: want 1 element, got %d", len(els))
|
||||
}
|
||||
return a, els[0]
|
||||
}
|
||||
|
||||
t.Run("空取值不会把 data: 信封当地址返回", func(t *testing.T) {
|
||||
a, el := elementOf(t, payload, envelope)
|
||||
got, err := a.GetString("$.thumb_url", el, true)
|
||||
if err != nil {
|
||||
t.Fatalf("GetString: %v", err)
|
||||
}
|
||||
if got != "" {
|
||||
t.Fatalf("封面应为空,实际 = %q", got)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("baseUrl 是 http(s) 时仍按 legado 语义回退", func(t *testing.T) {
|
||||
const base = "https://www.example.com/search?q=x"
|
||||
a, el := elementOf(t, payload, base)
|
||||
got, err := a.GetString("$.thumb_url", el, true)
|
||||
if err != nil {
|
||||
t.Fatalf("GetString: %v", err)
|
||||
}
|
||||
if got != base {
|
||||
t.Fatalf("want base fallback %q, got %q", base, got)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("规则有取值时原样返回", func(t *testing.T) {
|
||||
const withCover = `{"data":[{"book_name":"书","thumb_url":"https://img.example.com/c.jpg"}]}`
|
||||
a, el := elementOf(t, withCover, envelope)
|
||||
got, err := a.GetString("$.thumb_url", el, true)
|
||||
if err != nil {
|
||||
t.Fatalf("GetString: %v", err)
|
||||
}
|
||||
if got != "https://img.example.com/c.jpg" {
|
||||
t.Fatalf("封面 = %q", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package rule
|
||||
|
||||
import "testing"
|
||||
|
||||
// 书源普遍用 legado 的内嵌模板拼字段(聚合源的 kind / 最新章节就是这种写法):
|
||||
//
|
||||
// kind = "{{$.status}},{{$.score}},{{$.tags}}"
|
||||
// lastChapter = "{{$.source}} {{$.last_chapter_title}}"
|
||||
//
|
||||
// 模板必须在**当前元素**上求值。修复前这里拿整份响应求值,模板一律取空,
|
||||
// 表现为 kind=",,,"、最新章节为空——搜索结果和换源列表里所有条目长得一模一样。
|
||||
func TestInnerRuleTemplateEvaluatesAgainstElement(t *testing.T) {
|
||||
const body = `{"data":[{"book_name":"全球高武","status":"连载","score":"9.2","tags":"都市","source":"svip_酷我","last_chapter_title":"第694章"}]}`
|
||||
a := NewAnalyzeRule()
|
||||
a.SetContent(body, "")
|
||||
els, err := a.GetElements("$.data")
|
||||
if err != nil {
|
||||
t.Fatalf("GetElements: %v", err)
|
||||
}
|
||||
if len(els) != 1 {
|
||||
t.Fatalf("want 1 element, got %d", len(els))
|
||||
}
|
||||
el := els[0]
|
||||
|
||||
got, err := a.GetString("{{$.status}},{{$.score}},{{$.tags}}", el, false)
|
||||
if err != nil {
|
||||
t.Fatalf("GetString: %v", err)
|
||||
}
|
||||
if got != "连载,9.2,都市" {
|
||||
t.Fatalf("kind 模板 = %q, want 连载,9.2,都市", got)
|
||||
}
|
||||
|
||||
got, err = a.GetString("{{$.source}} {{$.last_chapter_title}}", el, false)
|
||||
if err != nil {
|
||||
t.Fatalf("GetString: %v", err)
|
||||
}
|
||||
if got != "svip_酷我 第694章" {
|
||||
t.Fatalf("最新章节模板 = %q, want svip_酷我 第694章", got)
|
||||
}
|
||||
|
||||
// 列表规则(GetStringList)走的是同一套 MakeUpRule,必须同样生效
|
||||
list, err := a.GetStringList("{{$.status}},{{$.score}}", el, false)
|
||||
if err != nil {
|
||||
t.Fatalf("GetStringList: %v", err)
|
||||
}
|
||||
if len(list) != 1 || list[0] != "连载,9.2" {
|
||||
t.Fatalf("列表模板 = %v, want [连载,9.2]", list)
|
||||
}
|
||||
|
||||
// 模板里取不存在的字段应得到空串,而不是报错
|
||||
got, err = a.GetString("[{{$.not_exist}}]", el, false)
|
||||
if err != nil {
|
||||
t.Fatalf("GetString: %v", err)
|
||||
}
|
||||
if got != "[]" {
|
||||
t.Fatalf("缺失字段 = %q, want []", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user