- Tags: a, b, c"
+ // 标题的结束边界优先认 " - Tags:"(标签段);没有 Tags 段时认行尾。
+ // ⚠️ 别用 `Gallery:\s*([^:]+):` —— 那个会把 " - Tags" 一起吞进标题。
+ reTitleFromDesc = regexp.MustCompile(`(?is)Gallery:\s*(.*?)(?:\s*-\s*Tags\s*:|\s*$|[.:]\s+\d+\s+images\b)`)
reRestricted = regexp.MustCompile(`(?i)Login Required|restricted|Sad Panda`)
// #taglist 的每一行:
artist: | |
@@ -545,7 +549,8 @@ func (c *Client) FetchGalleryPage(ctx context.Context, gid, token string, onProg
}
if m := reMetaDesc.FindStringSubmatch(html); m != nil {
if nm := reTitleFromDesc.FindStringSubmatch(DecodeEntities(m[1])); nm != nil {
- title = strings.TrimSpace(nm[1])
+ // 保险:再剥一次尾部 " - Tags"(有些页面 desc 里 Tags 前无空格)
+ title = strings.TrimSpace(reTitleSuffix.ReplaceAllString(nm[1], ""))
}
}
if m := reTotal.FindStringSubmatch(html); m != nil {
diff --git a/internal/eh/title_test.go b/internal/eh/title_test.go
new file mode 100644
index 0000000..782d96a
--- /dev/null
+++ b/internal/eh/title_test.go
@@ -0,0 +1,72 @@
+package eh
+
+// 回归测试:文件名尾巴多出 " - Tags"(2026-10 线上问题)
+//
+// 现象:下载下来的目录/压缩包名全是 "... - Tags",原本标题没有这个。
+//
+// 根因:meta description 的真实格式是
+// "Free Hentai Misc Gallery:
- Tags: a, b, c"
+// 旧正则 `Gallery:\s*([^:]+):` 里的 `[^:]+` 是贪婪的,它一路吃到
+// 标签段自己的那个冒号为止,于是把 " - Tags" 也当成标题的一部分。
+//
+// 正确做法:标题的结束边界就是 " - Tags:",必须显式认这个分隔符。
+
+import "testing"
+
+func TestTitleFromDescStripsTags(t *testing.T) {
+ tests := []struct {
+ name string
+ desc string
+ want string
+ }{
+ {
+ name: "带 Tags 段(线上真实样本)",
+ desc: "Free Hentai Misc Gallery: Hinata l Boruto (CriticalHAI) [AI Generated] - Tags: boruto, hinata hyuga, sole female, ai generated",
+ want: "Hinata l Boruto (CriticalHAI) [AI Generated]",
+ },
+ {
+ name: "无 Tags 段",
+ desc: "Free Hentai Doujinshi Gallery: [Some Circle] Flagged Gallery",
+ want: "[Some Circle] Flagged Gallery",
+ },
+ {
+ name: "Tags 前多空格",
+ desc: "Free Hentai Manga Gallery: Some Title - Tags: a, b",
+ want: "Some Title",
+ },
+ {
+ name: "标题本身含冒号(竖线译名等)",
+ desc: "Free Hentai Misc Gallery: [A] Orig | 译名 - Tags: x",
+ want: "[A] Orig | 译名",
+ },
+ {
+ name: "标题本身以 Tags 结尾也不能被剥",
+ desc: "Free Hentai Misc Gallery: Study About Tags - Tags: a",
+ want: "Study About Tags",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ m := reTitleFromDesc.FindStringSubmatch(tt.desc)
+ if m == nil {
+ t.Fatalf("没匹配到标题: %q", tt.desc)
+ }
+ got := reTitleSuffix.ReplaceAllString(m[1], "")
+ if got != tt.want {
+ t.Errorf("标题不对:\n got %q\nwant %q", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestTitleTagStripsTagsSuffix(t *testing.T) {
+ // 一般是 "... - E-Hentai Galleries",但也可能直接是 "... - Tags"
+ m := reTitleTag.FindStringSubmatch("Some Gallery - Tags")
+ if m == nil {
+ t.Fatal("没取到 ")
+ }
+ if got := reTitleSuffix.ReplaceAllString(m[1], ""); got != "Some Gallery" {
+ t.Errorf("title 去 Tags 失败: %q", got)
+ }
+}
\ No newline at end of file
diff --git a/remove-tags-suffix.bat b/remove-tags-suffix.bat
new file mode 100644
index 0000000..4f89e52
--- /dev/null
+++ b/remove-tags-suffix.bat
@@ -0,0 +1,4 @@
+@echo off
+chcp 65001 >nul
+powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0remove-tags-suffix.ps1"
+pause
\ No newline at end of file
diff --git a/remove-tags-suffix.ps1 b/remove-tags-suffix.ps1
new file mode 100644
index 0000000..fca522c
--- /dev/null
+++ b/remove-tags-suffix.ps1
@@ -0,0 +1,66 @@
+<#
+ 去掉压缩包文件名结尾的 " - Tags"
+ 递归遍历「脚本所在目录」下所有层级。
+ 支持扩展名: .zip .rar .7z .cbz .cbr .cb7 .tar .gz
+#>
+
+$ErrorActionPreference = 'Stop'
+[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
+
+$root = $PSScriptRoot
+$suffix = ' - Tags'
+$arcExts = @('.zip', '.rar', '.7z', '.cbz', '.cbr', '.cb7', '.tar', '.gz')
+
+Write-Host "正在递归扫描: $root" -ForegroundColor Cyan
+Write-Host ''
+
+$found = 0
+$done = 0
+$fail = 0
+
+$files = Get-ChildItem -LiteralPath $root -Recurse -File -Force -ErrorAction SilentlyContinue |
+ Where-Object { $arcExts -contains $_.Extension.ToLower() }
+
+foreach ($f in $files) {
+ $name = $f.Name
+ $base = [System.IO.Path]::GetFileNameWithoutExtension($name)
+ $ext = $f.Extension
+
+ # ⚠️ 必须对「去掉扩展名后的名字」判断。
+ # 直接拿 "xxx - Tags.zip" 去 EndsWith(" - Tags") 永远为 False。
+ if (-not $base.ToLower().EndsWith($suffix.ToLower())) { continue }
+
+ $newBase = $base.Substring(0, $base.Length - $suffix.Length).TrimEnd(' ', '.')
+
+ # 防止改名后变成空名 / 非法名
+ if ([string]::IsNullOrWhiteSpace($newBase)) {
+ Write-Host " [跳过] 去掉后缀后名字为空: $name" -ForegroundColor Yellow
+ continue
+ }
+ if ($newBase -match '[\\/:*?"<>|]') {
+ Write-Host " [跳过] 新名字含非法字符: $newBase" -ForegroundColor Yellow
+ continue
+ }
+
+ $newName = $newBase + $ext
+ if ($newName -eq $name) { continue }
+
+ $found++
+ Write-Host " [找到] $name"
+ Write-Host " -> $newName" -ForegroundColor DarkGray
+
+ $target = Join-Path $f.DirectoryName $newName
+ try {
+ Rename-Item -LiteralPath $f.FullName -NewName $newName -ErrorAction Stop
+ Write-Host " [完成]" -ForegroundColor Green
+ $done++
+ } catch {
+ Write-Host " [失败] $($_.Exception.Message)" -ForegroundColor Red
+ $fail++
+ }
+}
+
+Write-Host ''
+Write-Host '============================================' -ForegroundColor Cyan
+Write-Host "完成:命中 $found 个,成功 $done 个,失败 $fail 个" -ForegroundColor Cyan
+Write-Host '============================================' -ForegroundColor Cyan
\ No newline at end of file