181 lines
4.8 KiB
Go
181 lines
4.8 KiB
Go
package seoser
|
|
|
|
import (
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// IsIndexablePath 是否为需要收录的公开页。
|
|
// 采用白名单策略:新增页面只有明确归类后才允许进入索引,避免搜索、账户、
|
|
// 排序变体和工具页因遗漏配置被搜索引擎收录。
|
|
func IsIndexablePath(raw string) bool {
|
|
path := normalizedPath(raw)
|
|
if path == "/" {
|
|
return true
|
|
}
|
|
switch path {
|
|
case "/about", "/help", "/terms", "/privacy", "/2257", "/dmca":
|
|
return true
|
|
}
|
|
|
|
segments := splitPath(path)
|
|
if len(segments) == 0 {
|
|
return false
|
|
}
|
|
switch segments[0] {
|
|
case "list":
|
|
return isIndexablePlayletList(segments)
|
|
case "series":
|
|
return isIndexableSeriesList(segments)
|
|
case "topic":
|
|
return isIndexableSimplePagination(segments, "topic")
|
|
case "topic-detail", "tags":
|
|
return isIndexableNamedCollection(segments)
|
|
case "rank", "popular-recommend":
|
|
return isIndexableRanking(segments)
|
|
case "more-list", "more-series-list":
|
|
return isIndexableCuratedCollection(segments)
|
|
case "recently-added":
|
|
return isIndexableSimplePagination(segments, "recently-added")
|
|
case "play":
|
|
return isIndexablePlayletPlayback(segments)
|
|
case "series-play":
|
|
return len(segments) == 2 && segments[1] != ""
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// IsPrivatePath 标记不应跟踪链接的账户、认证和交易页面。
|
|
func IsPrivatePath(raw string) bool {
|
|
path := normalizedPath(raw)
|
|
for _, prefix := range []string{
|
|
"/login", "/register", "/profile", "/history", "/favorites",
|
|
"/purchased", "/setting", "/settings", "/recharge", "/feedback",
|
|
} {
|
|
if path == prefix || strings.HasPrefix(path, prefix+"/") {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func normalizedPath(raw string) string {
|
|
path := strings.TrimSpace(raw)
|
|
if u, err := url.Parse(path); err == nil && u.Path != "" {
|
|
path = u.Path
|
|
}
|
|
path = "/" + strings.Trim(strings.TrimSpace(path), "/")
|
|
if path == "/" {
|
|
return path
|
|
}
|
|
return strings.TrimSuffix(path, "/")
|
|
}
|
|
|
|
func splitPath(path string) []string {
|
|
path = strings.Trim(path, "/")
|
|
if path == "" {
|
|
return nil
|
|
}
|
|
return strings.Split(path, "/")
|
|
}
|
|
|
|
func positiveInt(raw string) bool {
|
|
n, err := strconv.Atoi(raw)
|
|
return err == nil && n > 0
|
|
}
|
|
|
|
func pageNumber(raw string) bool {
|
|
n, err := strconv.Atoi(raw)
|
|
return err == nil && n > 1
|
|
}
|
|
|
|
func isIndexableSimplePagination(segments []string, root string) bool {
|
|
if len(segments) == 1 && segments[0] == root {
|
|
return true
|
|
}
|
|
return len(segments) == 3 && segments[0] == root && segments[1] == "page" && pageNumber(segments[2])
|
|
}
|
|
|
|
func isIndexablePlayletList(segments []string) bool {
|
|
if len(segments) == 1 {
|
|
return true
|
|
}
|
|
if len(segments) == 2 {
|
|
return segments[1] != "" && segments[1] != "all"
|
|
}
|
|
base, hasPage := withoutPageSuffix(segments)
|
|
if len(base) != 4 || base[1] == "" {
|
|
return false
|
|
}
|
|
updateStatus, updateErr := strconv.Atoi(base[2])
|
|
sortType, sortErr := strconv.Atoi(base[3])
|
|
if updateErr != nil || sortErr != nil || updateStatus < 0 || updateStatus > 2 || sortType != 1 {
|
|
return false
|
|
}
|
|
// 默认第一页已有更短规范地址 /list 或 /list/:theme。
|
|
return hasPage || updateStatus != 0
|
|
}
|
|
|
|
func isIndexableSeriesList(segments []string) bool {
|
|
if len(segments) == 1 {
|
|
return true
|
|
}
|
|
base, hasPage := withoutPageSuffix(segments)
|
|
if len(base) != 4 {
|
|
return false
|
|
}
|
|
media, topic, sortType := base[1], base[2], base[3]
|
|
if media != "all" && media != "film" && media != "anime" {
|
|
return false
|
|
}
|
|
if topic != "all" && !positiveInt(topic) {
|
|
return false
|
|
}
|
|
if sortType != "1" {
|
|
return false
|
|
}
|
|
// 默认第一页已有更短规范地址 /series。
|
|
return hasPage || media != "all" || topic != "all"
|
|
}
|
|
|
|
func isIndexableNamedCollection(segments []string) bool {
|
|
if len(segments) == 2 && segments[1] != "" {
|
|
return true
|
|
}
|
|
return len(segments) == 4 && segments[1] != "" && segments[2] == "page" && pageNumber(segments[3])
|
|
}
|
|
|
|
func isIndexableRanking(segments []string) bool {
|
|
if len(segments) == 1 {
|
|
return true
|
|
}
|
|
if len(segments) == 2 {
|
|
return segments[1] == "2" || segments[1] == "3"
|
|
}
|
|
if len(segments) == 3 {
|
|
return segments[1] == "page" && pageNumber(segments[2])
|
|
}
|
|
return len(segments) == 4 && (segments[1] == "2" || segments[1] == "3") && segments[2] == "page" && pageNumber(segments[3])
|
|
}
|
|
|
|
func isIndexableCuratedCollection(segments []string) bool {
|
|
if len(segments) == 2 {
|
|
return segments[1] != ""
|
|
}
|
|
return len(segments) == 4 && segments[1] != "" && segments[2] == "page" && pageNumber(segments[3])
|
|
}
|
|
|
|
func isIndexablePlayletPlayback(segments []string) bool {
|
|
// 规范入口是 /play/{slug};各集 /play/{slug}/{ep} 近重复且多为薄播放页,不收录。
|
|
return len(segments) == 2 && segments[1] != ""
|
|
}
|
|
|
|
func withoutPageSuffix(segments []string) ([]string, bool) {
|
|
if len(segments) < 3 || segments[len(segments)-2] != "page" || !pageNumber(segments[len(segments)-1]) {
|
|
return segments, false
|
|
}
|
|
return segments[:len(segments)-2], true
|
|
}
|