Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -307,8 +307,8 @@ chains:
| 后缀 | 举例 | 说明 |
|---|---|---|
| `-CD{Number}` | `-CD1` | 多 CD 场景下指定当前影片对应 CD ID(从 1 开始) |
| `-C` | `-` | 添加“字幕”分类并为封面添加水印 |
| `-4K` | `-` | 添加“4K”分类并为封面添加水印 |
| `-C` | `-` | 标记为含字幕轨版本,添加相应分类并为封面附加水印 |
| `-4K` | `-` | 添加“4K”分类并为封面附加水印 |
| `-8K` | `-` | 添加 8K 水印 |
| `-VR` | `-` | 添加 VR 水印 |

Expand Down
31 changes: 24 additions & 7 deletions cmd/yamdc/ruleset_test_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,27 @@ type rulesetCaseItem struct {
}

type rulesetCaseOutput struct {
Number string `json:"number"`
Uncensor *bool `json:"uncensor"`
Suffixes []string `json:"suffix-set"`
Category string `json:"category"`
Status string `json:"status"`
Number string `json:"number"`
Unrated *bool `json:"unrated,omitempty"`
// UncensorDeprecated 仅为兼容既有 case 文件 (例如外部仓库 yamdc-script/
// cases/default.json 里大量 `"uncensor": true/false` 的断言) 保留。
// 解析期若 Unrated 未显式给出而此字段非 nil, 会把值提升到 Unrated,
// 下游 assertRulesetCaseOutput 统一按 Unrated 比对。
UncensorDeprecated *bool `json:"uncensor,omitempty"`
Suffixes []string `json:"suffix-set"`
Category string `json:"category"`
Status string `json:"status"`
}

// normalizeRulesetCaseOutput 把老 case 文件里的 `uncensor:` 字段抬升到
// `unrated:`, 保证下游只认一种形态。这个迁移是幂等的, 重复调用安全。
func normalizeRulesetCaseOutput(out *rulesetCaseOutput) {
if out == nil {
return
}
if out.Unrated == nil && out.UncensorDeprecated != nil {
out.Unrated = out.UncensorDeprecated
}
}

// 与 newPluginTestCmd 在 cobra 样板 (flag 声明 / RunE 桥接) 上结构相似,
Expand Down Expand Up @@ -135,9 +151,9 @@ func assertRulesetCaseOutput(name string, item *rulesetCaseItem, res *movieidcle
"expected number=%s but got %s", expected, res.NumberID)}
}
}
if item.Output.Uncensor != nil && res.Uncensor != *item.Output.Uncensor {
if item.Output.Unrated != nil && res.Unrated != *item.Output.Unrated {
return &bundleVerifyCaseItem{Name: name, Pass: false, Errmsg: fmt.Sprintf(
"expected uncensor=%t but got %t", *item.Output.Uncensor, res.Uncensor)}
"expected unrated=%t but got %t", *item.Output.Unrated, res.Unrated)}
}
if expected := strings.TrimSpace(item.Output.Category); expected != "" {
if !strings.EqualFold(res.Category, expected) {
Expand Down Expand Up @@ -170,6 +186,7 @@ func verifyRulesetCase(cleaner movieidcleaner.Cleaner, index int, item *rulesetC
if item == nil {
return &bundleVerifyCaseItem{Name: name, Pass: false, Errmsg: "case is null"}
}
normalizeRulesetCaseOutput(&item.Output)
input := strings.TrimSpace(item.Input)
if input == "" {
return &bundleVerifyCaseItem{Name: name, Pass: false, Errmsg: "input is required"}
Expand Down
31 changes: 31 additions & 0 deletions cmd/yamdc/ruleset_test_cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,34 @@ func TestLoadRulesetCaseFileFromDirRequiresJSON(t *testing.T) {
require.Error(t, err)
require.Contains(t, err.Error(), "no json case files found")
}

// TestLoadRulesetCaseFileLegacyField verifies that existing case files using
// the deprecated `uncensor:` field are still read correctly after normalization.
func TestLoadRulesetCaseFileLegacyField(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "default.json")
require.NoError(t, os.WriteFile(path, []byte(`{
"cases": [
{"name":"legacy","input":"foo","output":{"number":"FOO-1","uncensor":true}},
{"name":"modern","input":"bar","output":{"number":"BAR-1","unrated":false}},
{"name":"mixed","input":"baz","output":{"number":"BAZ-1","uncensor":true,"unrated":false}}
]
}`), 0o600))

out, err := loadRulesetCaseFile(path)
require.NoError(t, err)
require.Len(t, out.Cases, 3)

normalizeRulesetCaseOutput(&out.Cases[0].Output)
require.NotNil(t, out.Cases[0].Output.Unrated)
require.True(t, *out.Cases[0].Output.Unrated)

normalizeRulesetCaseOutput(&out.Cases[1].Output)
require.NotNil(t, out.Cases[1].Output.Unrated)
require.False(t, *out.Cases[1].Output.Unrated)

normalizeRulesetCaseOutput(&out.Cases[2].Output)
require.NotNil(t, out.Cases[2].Output.Unrated)
require.False(t, *out.Cases[2].Output.Unrated,
"explicit unrated field should win over legacy uncensor")
}
12 changes: 6 additions & 6 deletions docs/004-movieid-ruleset/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ internal/movieidcleaner/model.go
2. `NumberID`
3. `Suffixes`
4. `Category`
5. `Uncensor`
5. `Unrated`
6. `CategoryMatched`
7. `UncensorMatched`
7. `UnratedMatched`
8. `Confidence`
9. `Status`
10. `RuleHits`
Expand Down Expand Up @@ -160,7 +160,7 @@ internal/movieidcleaner/model.go
matchers:
- name: source_a
category: SOURCE_A
uncensor: true
unrated: true
pattern: '(?i)SRCA[-_\\s]?([0-9]{3,})'
normalize_template: 'SRCA-$1'
score: 100
Expand All @@ -170,7 +170,7 @@ internal/movieidcleaner/model.go
`Normalized = SRCA-123456`
`NumberID = SRCA-123456`
`Category = SOURCE_A`
`Uncensor = true`
`Unrated = true`
7. `PostProcessors`
1. 负责在主匹配完成后做统一后处理。
2. 适合处理后缀排序、连接符归一化等全局一致性问题。
Expand Down Expand Up @@ -411,7 +411,7 @@ func MergeRuleSets(base *RuleSet, override *RuleSet) (*RuleSet, error)
2. 通过 `normalize_template` 生成规范化输出。
3. 通过 `score` 控制候选优先级。
4. 通过 `category` 生成分类信息。
5. 通过 `uncensor` 生成附加布尔标记。
5. 通过 `unrated` 生成附加布尔标记。
6. 通过 `require_boundary` 与 `prefixes` 控制匹配边界和适用前缀。

示例:
Expand Down Expand Up @@ -444,7 +444,7 @@ XFILM123Y
`MatcherRule` 当前已支持:

1. `category`
2. `uncensor`
2. `unrated`
3. `pattern`
4. `normalize_template`
5. `score`
Expand Down
2 changes: 1 addition & 1 deletion docs/004-movieid-ruleset/example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
2. `advanced-ruleset/`
- 较复杂规则集示例
- 展示 `rewrite_rules`、`suffix_rules`、`noise_rules`、`matchers`、`post_processors`
- 展示 `require_boundary`、`score`、`category`、`uncensor`
- 展示 `require_boundary`、`score`、`category`、`unrated`

3. `override-bundle/`
- 演示规则包结构
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ matchers:
# 示例一:来源 A 影片族,直接推导 category 和附加标记
- name: source_a
category: SOURCE_A
uncensor: true
unrated: true
pattern: '(?i)SRCA[-_\s]?([0-9]{3,})'
normalize_template: 'SRCA-$1'
score: 120
Expand All @@ -19,7 +19,7 @@ matchers:
# 示例三:按前缀限制适用范围,避免宽泛规则误中
- name: source_b_archive
category: SOURCE_B
uncensor: true
unrated: true
pattern: '(?i)([0-9]{6})[-_\s]?([0-9]{3})'
normalize_template: '$1-$2'
score: 90
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ matchers:
# override 层按规则名覆盖系统规则,提高优先级并收紧 pattern
- name: source_a
category: SOURCE_A
uncensor: true
unrated: true
pattern: '(?i)SRCA[-_\s]?([0-9]{5,})'
normalize_template: 'SRCA-$1'
score: 140
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
matchers:
- name: source_a
category: SOURCE_A
uncensor: true
unrated: true
pattern: '(?i)SRCA[-_\s]?([0-9]{3,})'
normalize_template: 'SRCA-$1'
score: 100
Expand Down
44 changes: 22 additions & 22 deletions docs/007-watermark-tag-driven-refactor/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,17 @@ func (h *watermark) Handle(ctx context.Context, fc *model.FileContext) error {
if fc.Number.GetIsVR() {
tags = append(tags, image.WMVR)
}
if fc.Number.GetExternalFieldUncensor() {
tags = append(tags, image.WMUncensored)
if fc.Number.GetExternalFieldUnrated() {
tags = append(tags, image.WMUnrated)
}
if fc.Number.GetIsChineseSubtitle() {
tags = append(tags, image.WMChineseSubtitle)
}
if fc.Number.GetIsLeak() {
tags = append(tags, image.WMLeak)
if fc.Number.GetIsSpecialEdition() {
tags = append(tags, image.WMSpecialEdition)
}
if fc.Number.GetIsHack() {
tags = append(tags, image.WMHack)
if fc.Number.GetIsRestored() {
tags = append(tags, image.WMRestored)
}
...
}
Expand All @@ -56,11 +56,11 @@ func (h *watermark) Handle(ctx context.Context, fc *model.FileContext) error {

```15:23:internal/number/constant.go
const (
defaultTagUncensored = "未审查"
defaultTagUnrated = "未审查"
defaultTagChineseSubtitle = "字幕版"
defaultTag4K = "4K"
defaultTagLeak = "特别版"
defaultTagHack = "修复版"
defaultTagSpecialEdition = "特别版"
defaultTagRestored = "修复版"
defaultTag8K = "8K"
defaultTagVR = "VR"
)
Expand Down Expand Up @@ -130,27 +130,27 @@ var sysHandler = []string{
package tag

const (
Uncensored = "未审查"
Unrated = "未审查"
ChineseSubtitle = "字幕版"
K4 = "4K"
K8 = "8K"
VR = "VR"
Leak = "特别版"
Hack = "修复版"
SpecialEdition = "特别版"
Restored = "修复版"
)
```

命名纠结点:Go identifier 不能以数字开头,`4K` → `K4` 可以接受;也可以用 `Res4K` / `Tag4K` 风格。**倾向 `Res4K` / `Res8K` + `VR` / `ChineseSubtitle` / `Uncensored` / `Leak` / `Hack`**,语义更自然:
命名纠结点:Go identifier 不能以数字开头,`4K` → `K4` 可以接受;也可以用 `Res4K` / `Tag4K` 风格。**倾向 `Res4K` / `Res8K` + `VR` / `ChineseSubtitle` / `Unrated` / `SpecialEdition` / `Restored`**,语义更自然:

```go
const (
Uncensored = "未审查"
Unrated = "未审查"
ChineseSubtitle = "字幕版"
Res4K = "4K"
Res8K = "8K"
VR = "VR"
Leak = "特别版"
Hack = "修复版"
SpecialEdition = "特别版"
Restored = "修复版"
)
```

Expand All @@ -169,8 +169,8 @@ import "github.com/xxxsen/yamdc/internal/tag"

func (n *Number) GenerateTags() []string {
rs := make([]string, 0, 5)
if n.GetExternalFieldUncensor() {
rs = append(rs, tag.Uncensored)
if n.GetExternalFieldUnrated() {
rs = append(rs, tag.Unrated)
}
if n.GetIsChineseSubtitle() {
rs = append(rs, tag.ChineseSubtitle)
Expand Down Expand Up @@ -227,10 +227,10 @@ var defaultWatermarkRules = []watermarkRule{
{tag.Res4K, image.WM4K},
{tag.Res8K, image.WM8K},
{tag.VR, image.WMVR},
{tag.Uncensored, image.WMUncensored},
{tag.Unrated, image.WMUnrated},
{tag.ChineseSubtitle, image.WMChineseSubtitle},
{tag.Leak, image.WMLeak},
{tag.Hack, image.WMHack},
{tag.SpecialEdition, image.WMSpecialEdition},
{tag.Restored, image.WMRestored},
}

type watermark struct {
Expand Down Expand Up @@ -489,7 +489,7 @@ var sysHandler = []string{

- `TestWatermarkHandlerNilPoster` / `TestWatermarkHandlerEmptyPosterKey`: 不依赖 Number 字段,保留。
- `TestWatermarkHandlerNoTags`: 逻辑不变,保留(只是"没 tag 就跳过"从 Number 空转成 Genres 空)。
- `TestWatermarkHandlerStorageError`: 改成设置 `fc.Meta.Genres = []string{tag.Uncensored}`,不再 `SetExternalFieldUncensor`。
- `TestWatermarkHandlerStorageError`: 改成设置 `fc.Meta.Genres = []string{tag.Unrated}`,不再 `SetExternalFieldUnrated`。
- `TestWatermarkHandlerWithValidImage`: 同上,用 `Genres` 替代 `number.Parse("ABC-123-C")`。
- `TestWatermarkHandlerAllTagTypes`: 改成 table-driven,每行直接给一个 tag 字符串。

Expand Down
4 changes: 2 additions & 2 deletions internal/capture/capture.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ func (c *Capture) resolveFileInfo(fc *model.FileContext, file, preferredNumber s
return fmt.Errorf("parse number failed, err:%w", err)
}
if cleaned != nil {
if cleaned.UncensorMatched {
info.SetExternalFieldUncensor(cleaned.Uncensor)
if cleaned.UnratedMatched {
info.SetExternalFieldUnrated(cleaned.Unrated)
}
if cleaned.CategoryMatched {
info.SetExternalFieldCategory(cleaned.Category)
Expand Down
18 changes: 9 additions & 9 deletions internal/capture/capture_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ type staticCleaner struct {
normalized string
category string
categoryMatched bool
uncensor bool
uncensorMatched bool
unrated bool
unratedMatched bool
}

func (c *staticCleaner) Clean(input string) (*movieidcleaner.Result, error) {
Expand All @@ -59,8 +59,8 @@ func (c *staticCleaner) Clean(input string) (*movieidcleaner.Result, error) {
Normalized: c.normalized,
Category: c.category,
CategoryMatched: c.categoryMatched,
Uncensor: c.uncensor,
UncensorMatched: c.uncensorMatched,
Unrated: c.unrated,
UnratedMatched: c.unratedMatched,
Status: movieidcleaner.StatusSuccess,
Confidence: movieidcleaner.ConfidenceHigh,
}, nil
Expand Down Expand Up @@ -249,14 +249,14 @@ func TestResolveFileContext(t *testing.T) {
name: "uses cleaner derived fields for preferred number",
cleaner: &staticCleaner{
normalized: "ABC-123",
category: "HEYZO",
category: "DEMO",
categoryMatched: true,
uncensor: true,
uncensorMatched: true,
unrated: true,
unratedMatched: true,
},
file: "ignored.mp4",
preferredNumber: "HEYZO-0040",
wantNumber: "HEYZO-0040",
preferredNumber: "DEMO-0040",
wantNumber: "DEMO-0040",
},
{
name: "cleaner returns error",
Expand Down
12 changes: 6 additions & 6 deletions internal/image/watermark.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,24 @@ type Watermark int

const (
WMChineseSubtitle Watermark = 1
WMUncensored Watermark = 2
WMUnrated Watermark = 2
WM4K Watermark = 3
WMLeak Watermark = 4
WMSpecialEdition Watermark = 4
WM8K Watermark = 5
WMVR Watermark = 6
WMHack Watermark = 7
WMRestored Watermark = 7
)

var resMap = make(map[Watermark][]byte)

func registerResource() {
resMap[WMChineseSubtitle] = resource.ResIMGSubtitle
resMap[WM4K] = resource.ResIMG4K
resMap[WMUncensored] = resource.ResIMGUncensored
resMap[WMLeak] = resource.ResIMGLeak
resMap[WMUnrated] = resource.ResIMGUnrated
resMap[WMSpecialEdition] = resource.ResIMGSpecialEdition
resMap[WM8K] = resource.ResIMG8K
resMap[WMVR] = resource.ResIMGVR
resMap[WMHack] = resource.ResIMGHack
resMap[WMRestored] = resource.ResIMGRestored
}

func init() {
Expand Down
Loading
Loading