From 1d7d261490c4dc3ad4b1a5f2bb44eeb0cbbc6af9 Mon Sep 17 00:00:00 2001 From: jack Date: Wed, 21 Oct 2026 22:18:45 +0800 Subject: [PATCH] =?UTF-8?q?fix(eh):=20=E6=A0=87=E9=A2=98=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E5=B8=A6=20"=20-=20Tags"=EF=BC=8C=E5=B9=B6=E9=99=84=E6=89=B9?= =?UTF-8?q?=E9=87=8F=E6=94=B9=E5=90=8D=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit meta description 的真实格式是 "Free Hentai Misc Gallery: - Tags: a, b, c" 旧正则 `Gallery:\s*([^:]+):` 里的 [^:]+ 是贪婪的, 一路吃到标签段自己的冒号为止,把 " - Tags" 也吞进了标题, 导致下载目录/文件名结尾凭空多出 " - Tags"。 改为显式认 " - Tags:" 这个分隔符(Tags 段可选,无标签时认行尾), reTitleSuffix 同时认 Tags 结尾,提取后再兜底剥一次。 线上实测(g/4239272):标题由 "Hinata l Boruto (CriticalHAI) [AI Generated] - Tags" 变为 "Hinata l Boruto (CriticalHAI) [AI Generated]" 附带 remove-tags-suffix.bat/.ps1:递归去掉已有压缩包文件名结尾的 " - Tags",覆盖 zip/rar/7z/cbz/cbr/cb7/tar/gz,幂等。 --- internal/eh/eh.go | 11 ++++-- internal/eh/title_test.go | 72 +++++++++++++++++++++++++++++++++++++++ remove-tags-suffix.bat | 4 +++ remove-tags-suffix.ps1 | 66 +++++++++++++++++++++++++++++++++++ 4 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 internal/eh/title_test.go create mode 100644 remove-tags-suffix.bat create mode 100644 remove-tags-suffix.ps1 diff --git a/internal/eh/eh.go b/internal/eh/eh.go index 3de3bca..d77a4ee 100644 --- a/internal/eh/eh.go +++ b/internal/eh/eh.go @@ -156,7 +156,7 @@ type Gallery struct { var ( reTitleTag = regexp.MustCompile(`(?is)<title>(.*?)`) reMetaDesc = regexp.MustCompile(`(?i)
]*>\s* - 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:
Noxe... @@ -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) { + // <title> 一般是 "... - E-Hentai Galleries",但也可能直接是 "... - Tags" + m := reTitleTag.FindStringSubmatch("<title>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