diff --git a/internal/service/site_adapter_nexusphp.go b/internal/service/site_adapter_nexusphp.go index af630fa..dd403c3 100644 --- a/internal/service/site_adapter_nexusphp.go +++ b/internal/service/site_adapter_nexusphp.go @@ -217,14 +217,14 @@ func nexusPHPTorrentRows(pageHTML string) []string { } func firstNexusPHPLink(row, path string) *nexusPHPLink { - pattern := regexp.MustCompile(`(?is)]*href\s*=\s*["']([^"']*` + regexp.QuoteMeta(path) + `[^"']*)["'][^>]*)>(.*?)`) + pattern := regexp.MustCompile(`(?is)]*href\s*=\s*["']([^"']*)["'][^>]*)>(.*?)`) for _, match := range pattern.FindAllStringSubmatch(row, -1) { if len(match) < 4 { continue } href := html.UnescapeString(strings.TrimSpace(match[2])) parsed, err := url.Parse(href) - if err != nil { + if err != nil || !nexusPHPLinkPathMatches(parsed, path) { continue } return &nexusPHPLink{ @@ -237,6 +237,22 @@ func firstNexusPHPLink(row, path string) *nexusPHPLink { return nil } +func nexusPHPLinkPathMatches(parsed *url.URL, want string) bool { + if parsed == nil { + return false + } + path := strings.TrimSpace(parsed.Path) + if path == "" { + path = strings.TrimSpace(parsed.Opaque) + } + path = strings.Trim(strings.ToLower(path), "/") + want = strings.Trim(strings.ToLower(strings.TrimSpace(want)), "/") + if path == "" || want == "" { + return false + } + return path == want || strings.HasSuffix(path, "/"+want) +} + func nexusPHPTitleFromLink(link nexusPHPLink) string { for _, attr := range []string{"title", "data-title"} { if value := htmlAttr(link.attrs, attr); value != "" { diff --git a/internal/service/site_adapter_test.go b/internal/service/site_adapter_test.go index 6b4a03d..1152c30 100644 --- a/internal/service/site_adapter_test.go +++ b/internal/service/site_adapter_test.go @@ -247,6 +247,24 @@ func TestParseNexusPHPHTMLModernRows(t *testing.T) { } } +func TestParseNexusPHPHTMLIgnoresUserDetailsLinks(t *testing.T) { + page := ` + + + +
shukBeta15.5 GiB
问心下载1.5 GiB
` + result, err := parseNexusPHPHTML(page, "Nexus", "https://pt.example") + if err != nil { + t.Fatal(err) + } + if len(result.Items) != 1 { + t.Fatalf("items = %#v, want only real torrent details row", result.Items) + } + if result.Items[0].ID != "789" || result.Items[0].Title != "问心 S01 1080p" { + t.Fatalf("parsed item = %#v", result.Items[0]) + } +} + func TestMTeamAPIRateLimits(t *testing.T) { search := mteamAPIRateLimits(mteamAPIEndpointSearch) if len(search) != 1 || search[0].Limit != 1500 || search[0].Window != 24*time.Hour { diff --git a/web/src/pages/LibrariesPageSections.tsx b/web/src/pages/LibrariesPageSections.tsx index c6936f6..b97967c 100644 --- a/web/src/pages/LibrariesPageSections.tsx +++ b/web/src/pages/LibrariesPageSections.tsx @@ -7,6 +7,7 @@ import { imageURL } from '../api/client' import { EpisodeArtworkToggle } from '../components/EpisodeArtworkToggle' import { MediaCard } from '../components/MediaCard' import { artworkScore, seriesCardLink, type SeriesCard } from '../utils/groupSeries' +import { libraryDisplayPath } from './libraryDisplayModel' import { mediaTime, type LibraryPreview } from './librariesPageModel' const TYPE_ICONS: Record = { @@ -127,6 +128,7 @@ export function LibrariesContent({ previews }: { previews: LibraryPreview[] }) { function LibraryEntryCard({ preview }: { preview: LibraryPreview }) { const library = preview.library const artwork = libraryArtworkItems(preview.cards) + const displayPath = libraryDisplayPath(library.path) return ( {library.name} -

{library.path}

+

+ {displayPath} +

{preview.total.toLocaleString()} 个条目 @@ -174,6 +178,7 @@ function LibraryEntryCard({ preview }: { preview: LibraryPreview }) { function LibraryShelf({ preview }: { preview: LibraryPreview }) { const library = preview.library const cards = preview.cards.slice(0, 10) + const displayPath = libraryDisplayPath(library.path) return (
@@ -185,7 +190,7 @@ function LibraryShelf({ preview }: { preview: LibraryPreview }) {

{library.name}

- {library.path} · {preview.total.toLocaleString()} 个条目 · 最新 {cards.length} 部 + {displayPath} · {preview.total.toLocaleString()} 个条目 · 最新 {cards.length} 部

diff --git a/web/src/pages/LibraryPageHeader.tsx b/web/src/pages/LibraryPageHeader.tsx index 765f200..3784267 100644 --- a/web/src/pages/LibraryPageHeader.tsx +++ b/web/src/pages/LibraryPageHeader.tsx @@ -1,5 +1,6 @@ import type { Library } from '../types' import { EpisodeArtworkToggle } from '../components/EpisodeArtworkToggle' +import { libraryDisplayPath } from './libraryDisplayModel' type LibraryPageHeaderProps = { library: Library | null @@ -32,6 +33,8 @@ export function LibraryPageHeader({ onScrape, onRepairRescrape, }: LibraryPageHeaderProps) { + const displayPath = library ? libraryDisplayPath(library.path) : '' + return (
@@ -39,7 +42,7 @@ export function LibraryPageHeader({ {library?.name ?? '媒体库'} ({itemCount}) - {library &&

{library.type} · {library.path}

} + {library &&

{library.type} · {displayPath}

} {loadingAllText &&

{loadingAllText}

} {scanProgress &&

{scanProgress}

}
diff --git a/web/src/pages/libraryDisplayModel.ts b/web/src/pages/libraryDisplayModel.ts new file mode 100644 index 0000000..8b8df46 --- /dev/null +++ b/web/src/pages/libraryDisplayModel.ts @@ -0,0 +1,8 @@ +import { cloudLibraryLabel, cloudLibraryProvider, TYPE_LABEL } from './storageConfigModel' + +export function libraryDisplayPath(path: string): string { + const provider = cloudLibraryProvider(path) + if (!provider) return path + const providerLabel = TYPE_LABEL[provider] || provider + return `${providerLabel} / ${cloudLibraryLabel(path)}` +}