definitions

package
v0.19.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 5, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var AGSVPTDefinition = &v2.SiteDefinition{
	ID:             "agsvpt",
	Name:           "AGSVPT",
	Aka:            []string{"末日种子库", "AGSV"},
	Description:    "Arctic Global Seed Vault",
	Schema:         v2.SchemaNexusPHP,
	URLs:           []string{"https://www.agsvpt.com/"},
	FaviconURL:     "https://www.agsvpt.com/favicon.ico",
	TimezoneOffset: "+0800",
	RateLimit:      0.5,
	RateBurst:      2,
	HREnabled:      true,
	UserInfo: &v2.UserInfoConfig{
		PickLast:     []string{"id"},
		RequestDelay: 500,
		Process: []v2.UserInfoProcess{
			{
				RequestConfig: v2.RequestConfig{
					URL:          "/index.php",
					ResponseType: "document",
				},
				Fields: []string{"id", "name", "seeding", "leeching"},
			},
			{
				RequestConfig: v2.RequestConfig{
					URL:          "/userdetails.php",
					ResponseType: "document",
				},
				Assertion: map[string]string{"id": "params.id"},
				Fields: []string{
					"name", "uploaded", "downloaded", "ratio", "levelName",
					"bonus", "joinTime", "messageCount",
				},
			},
			{
				RequestConfig: v2.RequestConfig{
					URL:          "/mybonus.php",
					ResponseType: "document",
				},
				Fields: []string{"bonusPerHour"},
			},
		},
		Selectors: map[string]v2.FieldSelector{
			"id": {
				Selector: []string{
					"a[href*='userdetails.php'][class*='Name']",
					"a.User_Name[href*='userdetails.php']",
					"#info_block a[href*='userdetails.php']",
					"a[href*='userdetails.php']",
				},
				Attr:    "href",
				Filters: []v2.Filter{{Name: "querystring", Args: []any{"id"}}},
			},
			"name": {
				Selector: []string{
					"a[href*='userdetails.php'][class*='Name']",
					"a.User_Name[href*='userdetails.php']",
					"#info_block a[href*='userdetails.php']",
				},
			},
			"uploaded": {
				Selector: []string{
					"td.rowhead:contains('上传量') + td",
					"td.rowhead:contains('上傳量') + td",
					"td.rowhead:contains('Uploaded') + td",
				},
				Filters: []v2.Filter{{Name: "parseSize"}},
			},
			"downloaded": {
				Selector: []string{
					"td.rowhead:contains('下载量') + td",
					"td.rowhead:contains('下載量') + td",
					"td.rowhead:contains('Downloaded') + td",
				},
				Filters: []v2.Filter{{Name: "parseSize"}},
			},
			"ratio": {
				Selector: []string{
					"td.rowhead:contains('分享率') + td font",
					"td.rowhead:contains('分享率') + td > font",
					"td.rowhead:contains('分享率') + td",
					"td.rowhead:contains('Ratio') + td font",
					"td.rowhead:contains('Ratio') + td",
				},
				Filters: []v2.Filter{{Name: "parseNumber"}},
			},
			"levelName": {
				Selector: []string{
					"td.rowhead:contains('等级') + td > img",
					"td.rowhead:contains('等級') + td > img",
					"td.rowhead:contains('Class') + td > img",
					"td.rowhead:contains('等级') + td img",
				},
				Attr: "title",
			},
			"bonus": {
				Selector: []string{
					"td.rowhead:contains('魔力值') + td",
					"td.rowhead:contains('冰晶') + td",
					"td.rowhead:contains('Bonus') + td",
				},
				Filters: []v2.Filter{{Name: "parseNumber"}},
			},
			"bonusPerHour": {
				Selector: []string{
					"#outer td[rowspan]",
					"div:contains('你当前每小时能获取')",
				},
				Filters: []v2.Filter{{Name: "parseNumber"}},
			},
			"messageCount": {
				Text:     "0",
				Selector: []string{"td[style*='background: red'] a[href*='messages.php']"},
				Filters:  []v2.Filter{{Name: "parseNumber"}},
			},
			"joinTime": {
				Selector: []string{
					"td.rowhead:contains('加入日期') + td",
					"td.rowhead:contains('Join') + td",
				},
				Filters: []v2.Filter{
					{Name: "split", Args: []any{" (", 0}},
					{Name: "parseTime"},
				},
			},
			"seeding": {
				Selector: []string{"#info_block"},
				Attr:     "html",
				Filters: []v2.Filter{
					{Name: "regex", Args: []any{`class="arrowup"[^>]*/>(\d+)`}},
					{Name: "parseNumber"},
				},
			},
			"leeching": {
				Selector: []string{"#info_block"},
				Attr:     "html",
				Filters: []v2.Filter{
					{Name: "regex", Args: []any{`class="arrowdown"[^>]*/>(\d+)`}},
					{Name: "parseNumber"},
				},
			},
		},
	},
	Selectors: &v2.SiteSelectors{
		TableRows:          "table.torrents > tbody > tr:has(table.torrentname), table.torrents > tr:has(table.torrentname)",
		Title:              "table.torrentname a[href*='details.php']",
		TitleLink:          "table.torrentname a[href*='details.php']",
		Subtitle:           "table.torrentname td.embedded > span:not(.optiontag)",
		Size:               "td.rowfollow:nth-child(5)",
		Seeders:            "td.rowfollow:nth-child(6)",
		Leechers:           "td.rowfollow:nth-child(7)",
		Snatched:           "td.rowfollow:nth-child(8)",
		DiscountIcon:       "img.pro_free, img.pro_free2up, img.pro_50pctdown, img.pro_30pctdown, img.pro_2up, img.pro_50pctdown2up",
		Category:           "td.rowfollow:nth-child(1) img[alt]",
		UploadTime:         "td.rowfollow:nth-child(4) span[title]",
		DetailDownloadLink: "td.rowhead:contains('下载') + td a[href*='download.php']",
		DetailSubtitle:     "td.rowhead:contains('副标题') + td",
	},
	DetailParser: &v2.DetailParserConfig{
		TimeLayout: "2006-01-02 15:04:05",
		DiscountMapping: map[string]v2.DiscountLevel{
			"free":          v2.DiscountFree,
			"twoup":         v2.Discount2xUp,
			"twoupfree":     v2.Discount2xFree,
			"thirtypercent": v2.DiscountPercent30,
			"halfdown":      v2.DiscountPercent50,
			"twouphalfdown": v2.Discount2x50,
		},
		HRKeywords:       []string{"hitandrun", "hit_run.gif", "Hit and Run", "Hit & Run"},
		TitleSelector:    "h1",
		DiscountSelector: "h1 font.free, h1 font[class]",
		EndTimeSelector:  "h1 span[title]",
		SizeSelector:     "td.rowfollow:contains('大小')",
		SizeRegex:        `大小[::]\s*([\d.]+)\s*(GB|MB|KB|TB)`,
	},
	LevelRequirements: agsvptLevelRequirements,
}
View Source
var HDDolbyDefinition = &v2.SiteDefinition{
	ID:              "hddolby",
	Name:            "HD Dolby",
	Aka:             []string{"高清杜比"},
	Description:     "高清杜比 - 需要同时配置 Cookie 和 RSS Key。RSS Key 用于搜索和下载,Cookie 用于获取时魔等信息",
	Schema:          v2.SchemaHDDolby,
	AuthMethod:      v2.AuthMethodCookieAndAPIKey,
	URLs:            []string{"https://www.hddolby.com/"},
	LegacyURLs:      []string{"https://hddolby.com/"},
	FaviconURL:      "https://www.hddolby.com/favicon.ico",
	Unavailable:     false,
	TimezoneOffset:  "+0800",
	RateLimit:       0.5,
	RateBurst:       3,
	RateWindow:      time.Hour,
	RateWindowLimit: 50,
	LevelRequirements: []v2.SiteLevelRequirement{
		{
			ID:        1,
			Name:      "User",
			Privilege: "新用户的默认级别。",
		},
		{
			ID:           2,
			Name:         "Power User",
			Interval:     "P2W",
			Downloaded:   "120GB",
			Ratio:        2.0,
			SeedingBonus: 60000,
			Privilege:    "得到0个邀请名额;可以直接发布种子;可以查看NFO文档;可以查看用户列表;可以请求续种;可以查看排行榜;可以查看其它用户的种子历史;可以删除自己上传的字幕。",
		},
		{
			ID:           3,
			Name:         "(连长)Elite User",
			NameAka:      []string{"Elite User"},
			Interval:     "P4W",
			Downloaded:   "256GB",
			Ratio:        2.5,
			SeedingBonus: 120000,
			Privilege:    "Elite User及以上用户封存账号后不会被删除。",
		},
		{
			ID:           4,
			Name:         "Crazy User",
			Interval:     "P8W",
			Downloaded:   "512GB",
			Ratio:        3.0,
			SeedingBonus: 240000,
			Privilege:    "得到0个邀请名额;可以在做种/下载/发布的时候选择匿名模式。",
		},
		{
			ID:           5,
			Name:         "Insane User",
			Interval:     "P12W",
			Downloaded:   "768GB",
			Ratio:        3.5,
			SeedingBonus: 360000,
			Privilege:    "无",
		},
		{
			ID:           6,
			Name:         "Veteran User",
			Interval:     "P20W",
			Downloaded:   "1TB",
			Ratio:        4.0,
			SeedingBonus: 600000,
			Privilege:    "可以查看其它用户的评论、帖子历史。",
		},
		{
			ID:           7,
			Name:         "Extreme User",
			Interval:     "P28W",
			Downloaded:   "2TB",
			Ratio:        4.5,
			SeedingBonus: 720000,
			Privilege:    "Extreme User及以上用户会永远保留账号。",
		},
		{
			ID:           8,
			Name:         "Ultimate User",
			Interval:     "P40W",
			Downloaded:   "4TB",
			Ratio:        5.0,
			SeedingBonus: 1200000,
			Privilege:    "无",
		},
		{
			ID:           9,
			Name:         "Nexus Master",
			Interval:     "P56W",
			Downloaded:   "8TB",
			Ratio:        5.5,
			SeedingBonus: 1680000,
			Privilege:    "无",
		},
	},
}
View Source
var HDSkyDefinition = &v2.SiteDefinition{
	ID:             "hdsky",
	Name:           "HDSky",
	Aka:            []string{"HDS", "天空"},
	Description:    "高清发烧友后花园PT",
	Schema:         v2.SchemaNexusPHP,
	URLs:           []string{"https://hdsky.me/"},
	FaviconURL:     "https://hdsky.me/favicon.ico",
	TimezoneOffset: "+0800",
	RateLimit:      0.5,
	RateBurst:      2,
	UserInfo: &v2.UserInfoConfig{
		PickLast:     []string{"id"},
		RequestDelay: 500,
		Process: []v2.UserInfoProcess{
			{
				RequestConfig: v2.RequestConfig{
					URL:          "/index.php",
					ResponseType: "document",
				},
				Fields: []string{"id", "name", "seeding", "leeching"},
			},
			{
				RequestConfig: v2.RequestConfig{
					URL:          "/userdetails.php",
					ResponseType: "document",
				},
				Assertion: map[string]string{"id": "params.id"},
				Fields: []string{
					"name", "uploaded", "downloaded", "ratio", "levelName",
					"bonus", "seedingBonus", "joinTime",
					"hnrUnsatisfied", "hnrPreWarning", "messageCount",
				},
			},
			{
				RequestConfig: v2.RequestConfig{
					URL:          "/mybonus.php",
					ResponseType: "document",
				},
				Fields: []string{"bonusPerHour"},
			},
		},
		Selectors: map[string]v2.FieldSelector{
			"id": {
				Selector: []string{
					"a[href*='userdetails.php'][class*='Name']",
					"a.User_Name[href*='userdetails.php']",
					"#info_block a[href*='userdetails.php']",
					"a[href*='userdetails.php']",
				},
				Attr:    "href",
				Filters: []v2.Filter{{Name: "querystring", Args: []any{"id"}}},
			},
			"name": {
				Selector: []string{
					"a[href*='userdetails.php'][class*='Name']",
					"a.User_Name[href*='userdetails.php']",
					"#info_block a[href*='userdetails.php']",
				},
			},
			"uploaded": {
				Selector: []string{

					"td.rowhead:contains('传输') + td",
					"td.rowhead:contains('傳輸') + td",
					"td.rowhead:contains('Transfer') + td",

					"td.rowhead:contains('上传量') + td",
					"td.rowhead:contains('上傳量') + td",
					"td.rowhead:contains('Uploaded') + td",
				},
				Attr: "html",
				Filters: []v2.Filter{

					{Name: "regex", Args: []any{`上[传傳][量]?</strong>[::\s]*\s*([\d.,]+\s*[KMGTP]?i?B)`}},
					{Name: "parseSize"},
				},
			},
			"downloaded": {
				Selector: []string{

					"td.rowhead:contains('传输') + td",
					"td.rowhead:contains('傳輸') + td",
					"td.rowhead:contains('Transfer') + td",

					"td.rowhead:contains('下载量') + td",
					"td.rowhead:contains('下載量') + td",
					"td.rowhead:contains('Downloaded') + td",
				},
				Attr: "html",
				Filters: []v2.Filter{

					{Name: "regex", Args: []any{`下[载載][量]?</strong>[::\s]*\s*([\d.,]+\s*[KMGTP]?i?B)`}},
					{Name: "parseSize"},
				},
			},
			"ratio": {
				Selector: []string{

					"td.rowhead:contains('传输') + td",
					"td.rowhead:contains('傳輸') + td",
					"td.rowhead:contains('Transfer') + td",

					"td.rowhead:contains('分享率') + td font",
					"td.rowhead:contains('分享率') + td > font",
					"td.rowhead:contains('分享率') + td",
					"td.rowhead:contains('Ratio') + td font",
					"td.rowhead:contains('Ratio') + td",
				},
				Attr: "html",
				Filters: []v2.Filter{

					{Name: "regex", Args: []any{`分享率</strong>[::\s]*\s*(?:<font[^>]*>)?([\d.,]+|∞|Inf)`}},
					{Name: "parseNumber"},
				},
			},
			"levelName": {
				Selector: []string{
					"td.rowhead:contains('等级') + td > img",
					"td.rowhead:contains('等級') + td > img",
					"td.rowhead:contains('Class') + td > img",

					"td.rowhead:contains('等级') + td img",
					"td.rowhead:contains('等級') + td img",
					"td.rowhead:contains('Class') + td img",
				},
				Attr: "title",
			},
			"bonus": {
				Selector: []string{
					"td.rowhead:contains('魔力值') + td",
					"td.rowhead:contains('魔力') + td",
					"td.rowhead:contains('Bonus') + td",
				},
				Filters: []v2.Filter{{Name: "parseNumber"}},
			},
			"seedingBonus": {
				Selector: []string{
					"td.rowhead:contains('做种积分') + td",
					"td.rowhead:contains('做種積分') + td",
				},
				Filters: []v2.Filter{{Name: "parseNumber"}},
			},
			"bonusPerHour": {
				Selector: []string{
					"#outer td[rowspan]",
					"div:contains('你当前每小时能获取')",
				},
				Filters: []v2.Filter{{Name: "parseNumber"}},
			},
			"messageCount": {
				Text:     "0",
				Selector: []string{"td[style*='background: red'] a[href*='messages.php']"},
				Filters:  []v2.Filter{{Name: "parseNumber"}},
			},
			"joinTime": {
				Selector: []string{
					"td.rowhead:contains('加入日期') + td",
					"td.rowhead:contains('Join') + td",
				},
				Filters: []v2.Filter{
					{Name: "split", Args: []any{" (", 0}},
					{Name: "parseTime"},
				},
			},
			"seeding": {
				Selector: []string{

					"#info_block",
				},
				Attr: "html",
				Filters: []v2.Filter{
					{Name: "regex", Args: []any{`class="arrowup"[^>]*/>(\d+)`}},
					{Name: "parseNumber"},
				},
			},
			"leeching": {
				Selector: []string{

					"#info_block",
				},
				Attr: "html",
				Filters: []v2.Filter{
					{Name: "regex", Args: []any{`class="arrowdown"[^>]*/>(\d+)`}},
					{Name: "parseNumber"},
				},
			},
			"hnrUnsatisfied": {
				Text:     "0",
				Selector: []string{"#info_block a[href*='myhr.php']"},
				Filters: []v2.Filter{
					{Name: "regex", Args: []any{`\d+\s*/\s*(\d+)`}},
					{Name: "parseNumber"},
				},
			},
			"hnrPreWarning": {
				Text:     "0",
				Selector: []string{"#info_block a[href*='myhr.php']"},
				Filters: []v2.Filter{
					{Name: "regex", Args: []any{`^(\d+)`}},
					{Name: "parseNumber"},
				},
			},
		},
	},

	Selectors: &v2.SiteSelectors{
		TableRows:          "table.torrents > tbody > tr:has(table.torrentname), table.torrents > tr:has(table.torrentname)",
		Title:              "table.torrentname a[href*='details.php']",
		TitleLink:          "table.torrentname a[href*='details.php']",
		Subtitle:           "table.torrentname td.embedded > span:not(.optiontag)",
		Size:               "td.rowfollow:nth-child(5)",
		Seeders:            "td.rowfollow:nth-child(6)",
		Leechers:           "td.rowfollow:nth-child(7)",
		Snatched:           "td.rowfollow:nth-child(8)",
		DiscountIcon:       "img.pro_free, img.pro_free2up, img.pro_50pctdown, img.pro_30pctdown, img.pro_2up, img.pro_50pctdown2up",
		Category:           "td.rowfollow:nth-child(1) img[alt]",
		UploadTime:         "td.rowfollow:nth-child(4) span[title]",
		DetailDownloadLink: "td.rowhead:contains('下载链接') + td a[href*='download.php']",
		DetailSubtitle:     "td.rowhead:contains('副标题') + td",
	},
	LevelRequirements: hdSkyNewLevelRequirements,
}

HDSkyDefinition is the site definition for HDSky

View Source
var HDSkyNewRequirementsDate = time.Date(2025, 3, 1, 0, 0, 0, 0, time.FixedZone("CST", 8*3600))

HDSkyNewRequirementsDate is the date after which new level requirements apply

View Source
var MTeamDefinition = &v2.SiteDefinition{
	ID:          "mteam",
	Name:        "M-Team - TP",
	Aka:         []string{"MTeam", "馒头"},
	Description: "综合性网站,有分享率要求",
	Schema:      v2.SchemaMTorrent,
	RateLimit:   1.0,
	RateBurst:   3,
	URLs: []string{
		"https://api.m-team.cc",
		"https://kp.m-team.cc",
		"https://zp.m-team.io",
		"https://xp.m-team.cc",
		"https://ap.m-team.cc",
		"https://next.m-team.cc",
		"https://ob.m-team.cc",
	},
	LegacyURLs:     []string{"https://xp.m-team.io/", "https://pt.m-team.cc/", "https://tp.m-team.cc/"},
	FaviconURL:     "https://kp.m-team.cc/favicon.ico",
	TimezoneOffset: "+0800",
	UserInfo: &v2.UserInfoConfig{
		PickLast:     []string{"id", "name", "joinTime"},
		RequestDelay: 300,
		Process: []v2.UserInfoProcess{
			{
				RequestConfig: v2.RequestConfig{
					URL:          "/api/member/profile",
					Method:       "POST",
					ResponseType: "json",
				},
				Fields: []string{
					"id", "name", "joinTime", "uploaded", "downloaded",
					"levelName", "levelId", "bonus", "lastAccessAt",
				},
			},
			{
				RequestConfig: v2.RequestConfig{
					URL:          "/api/tracker/myPeerStatistics",
					Method:       "POST",
					ResponseType: "json",
				},
				Fields: []string{"seeding", "seedingSize", "uploads"},
			},
			{
				RequestConfig: v2.RequestConfig{
					URL:          "/api/tracker/mybonus",
					Method:       "POST",
					ResponseType: "json",
				},
				Fields: []string{"bonusPerHour"},
			},
			{
				RequestConfig: v2.RequestConfig{
					URL:          "/api/msg/notify/statistic",
					Method:       "POST",
					ResponseType: "json",
				},
				Fields: []string{"messageCount"},
			},
		},
		Selectors: map[string]v2.FieldSelector{
			"id": {
				Selector: []string{"data.id"},
				Filters:  []v2.Filter{{Name: "parseNumber"}},
			},
			"name": {
				Selector: []string{"data.username"},
			},
			"joinTime": {
				Selector: []string{"data.createdDate"},
				Filters:  []v2.Filter{{Name: "parseTime"}},
			},
			"uploaded": {
				Selector: []string{"data.memberCount.uploaded"},
				Filters:  []v2.Filter{{Name: "parseNumber"}},
			},
			"downloaded": {
				Selector: []string{"data.memberCount.downloaded"},
				Filters:  []v2.Filter{{Name: "parseNumber"}},
			},
			"levelName": {
				Selector: []string{"data.role"},
			},
			"levelId": {
				Selector: []string{"data.role"},
				Filters:  []v2.Filter{{Name: "parseNumber"}},
			},
			"bonus": {
				Selector: []string{"data.memberCount.bonus"},
				Filters:  []v2.Filter{{Name: "parseNumber"}},
			},
			"lastAccessAt": {
				Selector: []string{"data.memberStatus.lastBrowse"},
				Filters:  []v2.Filter{{Name: "parseTime"}},
			},
			"seeding": {
				Selector: []string{"data.seederCount"},
				Filters:  []v2.Filter{{Name: "parseNumber"}},
			},
			"seedingSize": {
				Selector: []string{"data.seederSize"},
				Filters:  []v2.Filter{{Name: "parseNumber"}},
			},
			"uploads": {
				Selector: []string{"data.uploadCount"},
				Filters:  []v2.Filter{{Name: "parseNumber"}},
			},
			"bonusPerHour": {
				Selector: []string{"data.formulaParams.finalBs"},
				Filters:  []v2.Filter{{Name: "parseNumber"}},
			},
			"messageCount": {
				Selector: []string{"data.unMake"},
				Filters:  []v2.Filter{{Name: "parseNumber"}},
			},
		},
	},
	LevelRequirements: []v2.SiteLevelRequirement{
		{
			ID:   1,
			Name: "User",
		},
		{
			ID:         2,
			Name:       "Power User",
			Interval:   "P4W",
			Downloaded: "200GB",
			Ratio:      2,
			Privilege:  "魔力值加成:+1%;可以使用匿名發表候選種子;可以上傳字幕",
		},
		{
			ID:         3,
			Name:       "Elite User",
			Interval:   "P8W",
			Downloaded: "400GB",
			Ratio:      3,
			Privilege:  "魔力值加成:+2%;可以發送邀請;可以管理自己上傳的字幕;可以檢視別人的下載紀錄;可以使用個性條",
		},
		{
			ID:         4,
			Name:       "Crazy User",
			Interval:   "P12W",
			Downloaded: "500GB",
			Ratio:      4,
			Privilege:  "魔力值加成:+3%",
		},
		{
			ID:         5,
			Name:       "Insane User",
			Interval:   "P16W",
			Downloaded: "800GB",
			Ratio:      5,
			Privilege:  "魔力值加成:+4%;可以檢視排行榜",
		},
		{
			ID:         6,
			Name:       "Veteran User",
			Interval:   "P20W",
			Downloaded: "1000GB",
			Ratio:      6,
			Privilege:  "魔力值加成:+5%;封存帳號後不會被刪除帳號",
		},
		{
			ID:         7,
			Name:       "Extreme User",
			Interval:   "P24W",
			Downloaded: "2000GB",
			Ratio:      7,
			Privilege:  "魔力值加成:+6%;永遠保留",
		},
		{
			ID:         8,
			Name:       "Ultimate User",
			Interval:   "P28W",
			Downloaded: "2500GB",
			Ratio:      8,
			Privilege:  "魔力值加成:+7%",
		},
		{
			ID:         9,
			Name:       "Nexus Master",
			Interval:   "P32W",
			Downloaded: "3000GB",
			Ratio:      9,
			Privilege:  "魔力值加成:+8%",
		},
		{
			ID:        100,
			Name:      "VIP",
			GroupType: v2.LevelGroupVIP,
		},
	},
}

MTeamDefinition is the site definition for M-Team

View Source
var MTeamLevelIDMap = map[string]string{
	"1":  "User",
	"2":  "Power User",
	"3":  "Elite User",
	"4":  "Crazy User",
	"5":  "Insane User",
	"6":  "Veteran User",
	"7":  "Extreme User",
	"8":  "Ultimate User",
	"9":  "Nexus Master",
	"10": "VIP",
}

MTeamLevelIDMap maps API role ID to level name

View Source
var MooKoDefinition = &v2.SiteDefinition{
	ID:             "mooko",
	Name:           "MooKo",
	Aka:            []string{"MK"},
	Description:    "MooKo is a CHINESE Private Torrent Tracker for MOVIES / TV / GENERAL",
	Schema:         v2.SchemaGazelle,
	URLs:           []string{"https://mooko.org/"},
	FaviconURL:     "https://mooko.org/favicon.ico",
	TimezoneOffset: "+0800",
	RateLimit:      0.5,
	RateBurst:      2,
	HREnabled:      true,

	HRCalcSeedTime: v2.NewSizeTieredHRCalc(
		[]v2.HRSeedTimeRule{
			{MinSizeGB: 0, MaxSizeGB: 10, SeedTimeH: 36},
			{MinSizeGB: 10, MaxSizeGB: 20, SeedTimeH: 72},
			{MinSizeGB: 20, MaxSizeGB: 50, SeedTimeH: 120},
			{MinSizeGB: 50, MaxSizeGB: 200, SeedTimeH: 168},
			{MinSizeGB: 200, MaxSizeGB: 0, SeedTimeH: 336},
		},
		168,
	),
}
View Source
var NovaHDDefinition = &v2.SiteDefinition{
	ID:             "novahd",
	Name:           "NovaHD",
	Aka:            []string{"Nova"},
	Description:    "NovaHD PT站点",
	Schema:         v2.SchemaNexusPHP,
	URLs:           []string{"https://pt.novahd.top/"},
	FaviconURL:     "https://pt.novahd.top/favicon.ico",
	TimezoneOffset: "+0800",
	RateLimit:      0.5,
	RateBurst:      2,
	UserInfo: &v2.UserInfoConfig{
		PickLast:     []string{"id"},
		RequestDelay: 500,
		Process: []v2.UserInfoProcess{
			{
				RequestConfig: v2.RequestConfig{
					URL:          "/index.php",
					ResponseType: "document",
				},
				Fields: []string{"id", "name", "seeding", "leeching"},
			},
			{
				RequestConfig: v2.RequestConfig{
					URL:          "/userdetails.php",
					ResponseType: "document",
				},
				Assertion: map[string]string{"id": "params.id"},
				Fields: []string{
					"name", "uploaded", "downloaded", "ratio", "levelName",
					"bonus", "joinTime", "messageCount",
				},
			},
			{
				RequestConfig: v2.RequestConfig{
					URL:          "/mybonus.php",
					ResponseType: "document",
				},
				Fields: []string{"bonusPerHour"},
			},
		},
		Selectors: map[string]v2.FieldSelector{
			"id": {
				Selector: []string{
					"a[href*='userdetails.php'][class*='Name']",
					"a.User_Name[href*='userdetails.php']",
					"#info_block a[href*='userdetails.php']",
					"a[href*='userdetails.php']",
				},
				Attr:    "href",
				Filters: []v2.Filter{{Name: "querystring", Args: []any{"id"}}},
			},
			"name": {
				Selector: []string{
					"a[href*='userdetails.php'][class*='Name']",
					"a.User_Name[href*='userdetails.php']",
					"#info_block a[href*='userdetails.php']",
				},
			},
			"uploaded": {
				Selector: []string{
					"td.rowhead:contains('上传量') + td",
					"td.rowhead:contains('上傳量') + td",
					"td.rowhead:contains('Uploaded') + td",
				},
				Filters: []v2.Filter{
					{Name: "parseSize"},
				},
			},
			"downloaded": {
				Selector: []string{
					"td.rowhead:contains('下载量') + td",
					"td.rowhead:contains('下載量') + td",
					"td.rowhead:contains('Downloaded') + td",
				},
				Filters: []v2.Filter{
					{Name: "parseSize"},
				},
			},
			"ratio": {
				Selector: []string{
					"td.rowhead:contains('分享率') + td font",
					"td.rowhead:contains('分享率') + td > font",
					"td.rowhead:contains('分享率') + td",
					"td.rowhead:contains('Ratio') + td font",
					"td.rowhead:contains('Ratio') + td",
				},
				Filters: []v2.Filter{
					{Name: "parseNumber"},
				},
			},
			"levelName": {
				Selector: []string{
					"td.rowhead:contains('等级') + td > img",
					"td.rowhead:contains('等級') + td > img",
					"td.rowhead:contains('Class') + td > img",
					"td.rowhead:contains('等级') + td img",
				},
				Attr: "title",
			},
			"bonus": {
				Selector: []string{
					"td.rowhead:contains('魔力值') + td",
					"td.rowhead:contains('魔力') + td",
					"td.rowhead:contains('Bonus') + td",
				},
				Filters: []v2.Filter{{Name: "parseNumber"}},
			},
			"bonusPerHour": {
				Selector: []string{
					"#outer td[rowspan]",
					"div:contains('你当前每小时能获取')",
				},
				Filters: []v2.Filter{{Name: "parseNumber"}},
			},
			"messageCount": {
				Text:     "0",
				Selector: []string{"td[style*='background: red'] a[href*='messages.php']"},
				Filters:  []v2.Filter{{Name: "parseNumber"}},
			},
			"joinTime": {
				Selector: []string{
					"td.rowhead:contains('加入日期') + td",
					"td.rowhead:contains('Join') + td",
				},
				Filters: []v2.Filter{
					{Name: "split", Args: []any{" (", 0}},
					{Name: "parseTime"},
				},
			},
			"seeding": {
				Selector: []string{
					"#info_block",
				},
				Attr: "html",
				Filters: []v2.Filter{
					{Name: "regex", Args: []any{`class="arrowup"[^>]*/>(\d+)`}},
					{Name: "parseNumber"},
				},
			},
			"leeching": {
				Selector: []string{
					"#info_block",
				},
				Attr: "html",
				Filters: []v2.Filter{
					{Name: "regex", Args: []any{`class="arrowdown"[^>]*/>(\d+)`}},
					{Name: "parseNumber"},
				},
			},
		},
	},

	Selectors: &v2.SiteSelectors{
		TableRows:          "table.torrents > tbody > tr:has(table.torrentname), table.torrents > tr:has(table.torrentname)",
		Title:              "table.torrentname a[href*='details.php']",
		TitleLink:          "table.torrentname a[href*='details.php']",
		Subtitle:           "table.torrentname td.embedded > span:not(.optiontag)",
		Size:               "td.rowfollow:nth-child(5)",
		Seeders:            "td.rowfollow:nth-child(6)",
		Leechers:           "td.rowfollow:nth-child(7)",
		Snatched:           "td.rowfollow:nth-child(8)",
		DiscountIcon:       "img.pro_free, img.pro_free2up, img.pro_50pctdown, img.pro_30pctdown, img.pro_2up, img.pro_50pctdown2up",
		Category:           "td.rowfollow:nth-child(1) img[alt]",
		UploadTime:         "td.rowfollow:nth-child(4) span[title]",
		DetailDownloadLink: "td.rowhead:contains('下载') + td a[href*='download.php']",
		DetailSubtitle:     "td.rowhead:contains('副标题') + td",
	},

	DetailParser: &v2.DetailParserConfig{
		TimeLayout: "2006-01-02 15:04:05",
		DiscountMapping: map[string]v2.DiscountLevel{
			"free":          v2.DiscountFree,
			"twoup":         v2.Discount2xUp,
			"twoupfree":     v2.Discount2xFree,
			"thirtypercent": v2.DiscountPercent30,
			"halfdown":      v2.DiscountPercent50,
			"twouphalfdown": v2.Discount2x50,
		},
		HRKeywords:       []string{"hitandrun", "hit_run.gif", "Hit and Run", "Hit & Run"},
		TitleSelector:    "h1",
		DiscountSelector: "h1 font.free, h1 font[class]",
		EndTimeSelector:  "h1 span[title]",
		SizeSelector:     "td.rowfollow:contains('大小')",
		SizeRegex:        `大小[::]\s*([\d.]+)\s*(GB|MB|KB|TB)`,
	},
	LevelRequirements: novaHDLevelRequirements,
}

NovaHDDefinition is the site definition for NovaHD

View Source
var RousiProDefinition = &v2.SiteDefinition{
	ID:          "rousipro",
	Name:        "Rousi Pro",
	Aka:         []string{"Rousi", "肉丝"},
	Description: "综合性PT站点,使用Bearer Token认证",
	Schema:      v2.SchemaRousi,
	AuthMethod:  v2.AuthMethodPasskey,
	RateLimit:   2.0,
	RateBurst:   5,
	URLs: []string{
		"https://rousi.pro",
	},
	FaviconURL:     "https://rousi.pro/favicon.ico",
	TimezoneOffset: "+0800",
	CreateDriver:   createRousiDriver,
}
View Source
var SpringSundayDefinition = &v2.SiteDefinition{
	ID:             "springsunday",
	Name:           "SpringSunday",
	Aka:            []string{"SSD"},
	Description:    "Classic Movie Compression Team",
	Schema:         v2.SchemaNexusPHP,
	RateLimit:      0.5,
	RateBurst:      2,
	URLs:           []string{"https://springsunday.net/"},
	LegacyURLs:     []string{"https://hdcmct.org/"},
	FaviconURL:     "https://springsunday.net/favicon.ico",
	TimezoneOffset: "+0800",

	Selectors: &v2.SiteSelectors{

		TableRows: "table.torrents > tbody > tr:has(table.torrentname), table.torrents > tr:has(table.torrentname)",

		Title:     "div.torrent-title > a[href*='details.php']",
		TitleLink: "div.torrent-title > a[href*='details.php']",

		Subtitle: "div.torrent-smalldescr > span[title]:last-of-type",

		Size: "td.rowfollow:nth-child(5)",

		Seeders: "td.rowfollow:nth-child(6)",

		Leechers: "td.rowfollow:nth-child(7)",

		Snatched: "td.rowfollow:nth-child(8)",

		DiscountIcon: "span.torrent-pro-free, span.torrent-pro-2up, span.torrent-pro-50pctdown, span.torrent-pro-30pctdown, span.torrent-pro-2xfree",

		DiscountEndTime: "div.torrent-title span[style*='DimGray'] span[title]",

		DownloadLink: "a[href*='download.php']",

		Category: "td.rowfollow:nth-child(1) img[alt]",

		UploadTime: "td.rowfollow.nowrap span[title]",

		DetailSubtitle: "td.rowhead:contains('副标题') + td.rowfollow",
	},
	UserInfo: &v2.UserInfoConfig{
		PickLast:     []string{"id"},
		RequestDelay: 500,
		Process: []v2.UserInfoProcess{
			{
				RequestConfig: v2.RequestConfig{
					URL:          "/index.php",
					ResponseType: "document",
				},
				Fields: []string{"id", "name", "seeding", "leeching", "bonus"},
			},
			{
				RequestConfig: v2.RequestConfig{
					URL:          "/userdetails.php",
					ResponseType: "document",
				},
				Assertion: map[string]string{"id": "params.id"},
				Fields: []string{
					"name", "uploaded", "downloaded", "ratio", "levelName",
					"seedingBonus", "joinTime", "messageCount",
				},
			},
			{
				RequestConfig: v2.RequestConfig{
					URL:          "/mybonus.php",
					ResponseType: "document",
				},
				Fields: []string{"bonusPerHour"},
			},
		},
		Selectors: map[string]v2.FieldSelector{

			"id": {
				Selector: []string{
					"#info_block a[href*='userdetails.php']",
					"a[href*='userdetails.php']",
				},
				Attr:    "href",
				Filters: []v2.Filter{{Name: "querystring", Args: []any{"id"}}},
			},

			"name": {
				Selector: []string{
					"#info_block a[href*='userdetails.php'] span",
					"#info_block a[href*='userdetails.php']",
				},
			},

			"uploaded": {
				Selector: []string{
					"td.rowhead:contains('传输') + td",
				},
				Attr: "html",
				Filters: []v2.Filter{
					{Name: "regex", Args: []any{`<strong>上传量</strong>[::\s]*([\d.,]+\s*[KMGTP]?i?B)`}},
					{Name: "parseSize"},
				},
			},

			"downloaded": {
				Selector: []string{
					"td.rowhead:contains('传输') + td",
				},
				Attr: "html",
				Filters: []v2.Filter{
					{Name: "regex", Args: []any{`<strong>下载量</strong>[::\s]*([\d.,]+\s*[KMGTP]?i?B)`}},
					{Name: "parseSize"},
				},
			},

			"ratio": {
				Selector: []string{
					"td.rowhead:contains('传输') + td",
				},
				Attr: "html",
				Filters: []v2.Filter{
					{Name: "regex", Args: []any{`<strong>分享率</strong>[::\s]*(?:<font[^>]*>)?([\d.,]+|∞|Inf)`}},
					{Name: "parseNumber"},
				},
			},

			"levelName": {
				Selector: []string{
					"td.rowhead:contains('等级') + td img",
				},
				Attr: "title",
			},

			"bonus": {
				Selector: []string{
					"#info_block a[href*='mybonus.php']",
				},
				Attr: "title",
				Filters: []v2.Filter{
					{Name: "regex", Args: []any{`茉莉[::\s]*([\d.,]+)`}},
					{Name: "parseNumber"},
				},
			},

			"bonusPerHour": {
				Selector: []string{
					"h3:contains('当前每小时') + table tbody",
					"table:has(th:contains('每小时茉莉')) tbody",
				},
				Attr: "html",
				Filters: []v2.Filter{

					{Name: "regex", Args: []any{`我的数据</b></td>(?:<td[^>]*>[\d.,]*</td>){9}<td[^>]*>([\d.,]+)</td>`}},
					{Name: "parseNumber"},
				},
			},

			"seeding": {
				Selector: []string{
					"#info_block",
				},
				Attr: "html",
				Filters: []v2.Filter{
					{Name: "regex", Args: []any{`class="arrowup"[^>]*/>\s*(\d+)`}},
					{Name: "parseNumber"},
				},
			},

			"leeching": {
				Selector: []string{
					"#info_block",
				},
				Attr: "html",
				Filters: []v2.Filter{
					{Name: "regex", Args: []any{`class="arrowdown"[^>]*/>\s*(\d+)`}},
					{Name: "parseNumber"},
				},
			},

			"seedingBonus": {
				Selector: []string{
					"td.rowhead:contains('积分') + td",
				},
				Attr: "html",
				Filters: []v2.Filter{
					{Name: "regex", Args: []any{`做种积分[::</b>\s]*([\d.,]+)`}},
					{Name: "parseNumber"},
				},
			},

			"joinTime": {
				Selector: []string{
					"td.rowhead:contains('加入日期') + td",
				},
				Filters: []v2.Filter{
					{Name: "regex", Args: []any{`^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})`}},
					{Name: "parseTime"},
				},
			},

			"messageCount": {
				Text: "0",
				Selector: []string{

					"body",
				},
				Attr: "html",
				Filters: []v2.Filter{

					{Name: "sumRegexMatches", Args: []any{`你有(\d+)条新`}},
				},
			},
		},
	},
	LevelRequirements: []v2.SiteLevelRequirement{
		{
			ID:        1,
			Name:      "新人(User)",
			Privilege: "新用户的默认级别。可以发种,可以请求续种;可以在做种/下载/发布的时候选择匿名模式;可以上传字幕或删除自己上传的字幕;可以更新过期的外部信息。",
		},
		{
			ID:         2,
			Name:       "精英(Elite)",
			NameAka:    []string{"Elite", "精英"},
			Downloaded: "500GB",
			Ratio:      1.2,
			Alternative: []v2.AlternativeRequirement{
				{SeedingBonus: 100000, Uploads: 1},
				{SeedingBonus: 150000},
			},
			Privilege: "可以在做种/下载/发布的时候选择匿名模式;可以查看用户列表;可以查看排行榜;可以浏览论坛邀请区;自助申请保种员;等级加成 0.05。",
		},
		{
			ID:         3,
			Name:       "大师(Master)",
			NameAka:    []string{"Master", "大师"},
			Downloaded: "1TB",
			Ratio:      1.2,
			Alternative: []v2.AlternativeRequirement{
				{SeedingBonus: 500000, Uploads: 100},
				{SeedingBonus: 1000000},
			},
			Privilege: "可以访问高级用户论坛,等级加成 0.15。",
		},
		{
			ID:         4,
			Name:       "神仙(God)",
			NameAka:    []string{"God", "神仙"},
			Downloaded: "3TB",
			Ratio:      2,
			Alternative: []v2.AlternativeRequirement{
				{SeedingBonus: 1200000, Uploads: 300},
				{SeedingBonus: 2400000},
			},
			Privilege: "彩色 ID 特权;可以查看普通日志;等级加成 0.25。",
		},
		{
			ID:        5,
			Name:      "神王(Immortal)",
			NameAka:   []string{"Immortal", "神王"},
			Privilege: "成为当月神王时奖励当时邀请茉莉价格的一半茉莉,最酷炫的动态彩色 ID 特权;常规时期可以购买及发送邀请;等级加成0.35。",
		},
		{
			ID:        100,
			Name:      "贵宾(VIP)",
			NameAka:   []string{"VIP", "贵宾"},
			GroupType: v2.LevelGroupVIP,
			Privilege: "贵宾(VIP)的权限和神王(Immortal)相同。贵宾(VIP)及其以上等级免除自动降级。",
		},
	},
}

SpringSundayDefinition is the site definition for SpringSunday

View Source
var XingYunGeDefinition = &v2.SiteDefinition{
	ID:             "xingyunge",
	Name:           "XingYunGe",
	Aka:            []string{"星陨阁"},
	Description:    "三十年河东,三十年河西!莫欺少年穷!",
	Schema:         v2.SchemaNexusPHP,
	URLs:           []string{"https://pt.xingyungept.org/"},
	LegacyURLs:     []string{"https://xingyunge.top/"},
	FaviconURL:     "https://pt.xingyungept.org/favicon.ico",
	TimezoneOffset: "+0800",
	RateLimit:      0.5,
	RateBurst:      2,
	UserInfo: &v2.UserInfoConfig{
		PickLast:     []string{"id"},
		RequestDelay: 500,
		Process: []v2.UserInfoProcess{
			{
				RequestConfig: v2.RequestConfig{
					URL:          "/index.php",
					ResponseType: "document",
				},
				Fields: []string{"id", "name", "seeding", "leeching", "bonusIndex"},
			},
			{
				RequestConfig: v2.RequestConfig{
					URL:          "/userdetails.php",
					ResponseType: "document",
				},
				Assertion: map[string]string{"id": "params.id"},
				Fields: []string{
					"name", "uploaded", "downloaded", "ratio", "levelName",
					"bonus", "joinTime", "messageCount",
				},
			},
			{
				RequestConfig: v2.RequestConfig{
					URL:          "/mybonus.php",
					ResponseType: "document",
				},
				Fields: []string{"bonusPerHour"},
			},
		},
		Selectors: map[string]v2.FieldSelector{
			"id": {
				Selector: []string{
					"a[href*='userdetails.php'][class*='Name']",
					"a.User_Name[href*='userdetails.php']",
					"#info_block a[href*='userdetails.php']",
					"a[href*='userdetails.php']",
				},
				Attr:    "href",
				Filters: []v2.Filter{{Name: "querystring", Args: []any{"id"}}},
			},
			"name": {
				Selector: []string{
					"a[href*='userdetails.php'][class*='Name']",
					"a.User_Name[href*='userdetails.php']",
					"#info_block a[href*='userdetails.php']",
				},
			},
			"uploaded": {
				Selector: []string{
					"td.rowhead:contains('上传量') + td",
					"td.rowhead:contains('上傳量') + td",
					"td.rowhead:contains('Uploaded') + td",
				},
				Filters: []v2.Filter{{Name: "parseSize"}},
			},
			"downloaded": {
				Selector: []string{
					"td.rowhead:contains('下载量') + td",
					"td.rowhead:contains('下載量') + td",
					"td.rowhead:contains('Downloaded') + td",
				},
				Filters: []v2.Filter{{Name: "parseSize"}},
			},
			"ratio": {
				Selector: []string{
					"td.rowhead:contains('分享率') + td font",
					"td.rowhead:contains('分享率') + td > font",
					"td.rowhead:contains('分享率') + td",
					"td.rowhead:contains('Ratio') + td font",
					"td.rowhead:contains('Ratio') + td",
				},
				Filters: []v2.Filter{{Name: "parseNumber"}},
			},
			"levelName": {
				Selector: []string{
					"td.rowhead:contains('等级') + td > img",
					"td.rowhead:contains('等級') + td > img",
					"td.rowhead:contains('Class') + td > img",
					"td.rowhead:contains('等级') + td img",
				},
				Attr: "title",
			},
			"bonusIndex": {
				Selector: []string{"#info_block"},
				Attr:     "html",
				Filters: []v2.Filter{
					{Name: "regex", Args: []any{`星焱\s*</font>\[<a[^>]*>使用</a>\][::\s]*([\d.,]+)`}},
					{Name: "parseNumber"},
				},
			},
			"bonus": {
				Selector: []string{
					"td.rowhead:contains('魔力值') + td",
					"td.rowhead:contains('星焱') + td",
					"td.rowhead:contains('Bonus') + td",
				},
				Filters: []v2.Filter{{Name: "parseNumber"}},
			},
			"bonusPerHour": {
				Selector: []string{
					"#outer td[rowspan]",
					"div:contains('你当前每小时能获取')",
				},
				Filters: []v2.Filter{{Name: "parseNumber"}},
			},
			"messageCount": {
				Text:     "0",
				Selector: []string{"td[style*='background: red'] a[href*='messages.php']"},
				Filters:  []v2.Filter{{Name: "parseNumber"}},
			},
			"joinTime": {
				Selector: []string{
					"td.rowhead:contains('加入日期') + td",
					"td.rowhead:contains('Join') + td",
				},
				Filters: []v2.Filter{
					{Name: "split", Args: []any{" (", 0}},
					{Name: "parseTime"},
				},
			},
			"seeding": {
				Selector: []string{"#info_block"},
				Attr:     "html",
				Filters: []v2.Filter{
					{Name: "regex", Args: []any{`class="arrowup"[^>]*/>(\d+)`}},
					{Name: "parseNumber"},
				},
			},
			"leeching": {
				Selector: []string{"#info_block"},
				Attr:     "html",
				Filters: []v2.Filter{
					{Name: "regex", Args: []any{`class="arrowdown"[^>]*/>(\d+)`}},
					{Name: "parseNumber"},
				},
			},
		},
	},
	Selectors: &v2.SiteSelectors{
		TableRows:          "table.torrents > tbody > tr:has(table.torrentname), table.torrents > tr:has(table.torrentname)",
		Title:              "table.torrentname a[href*='details.php']",
		TitleLink:          "table.torrentname a[href*='details.php']",
		Subtitle:           "table.torrentname td.embedded > span:not(.optiontag)",
		Size:               "td.rowfollow:nth-child(5)",
		Seeders:            "td.rowfollow:nth-child(6)",
		Leechers:           "td.rowfollow:nth-child(7)",
		Snatched:           "td.rowfollow:nth-child(8)",
		DiscountIcon:       "img.pro_free, img.pro_free2up, img.pro_50pctdown, img.pro_30pctdown, img.pro_2up, img.pro_50pctdown2up",
		Category:           "td.rowfollow:nth-child(1) img[alt]",
		UploadTime:         "td.rowfollow:nth-child(4) span[title]",
		DetailDownloadLink: "td.rowhead:contains('下载') + td a[href*='download.php']",
		DetailSubtitle:     "td.rowhead:contains('副标题') + td",
	},
	DetailParser: &v2.DetailParserConfig{
		TimeLayout: "2006-01-02 15:04:05",
		DiscountMapping: map[string]v2.DiscountLevel{
			"free":          v2.DiscountFree,
			"twoup":         v2.Discount2xUp,
			"twoupfree":     v2.Discount2xFree,
			"thirtypercent": v2.DiscountPercent30,
			"halfdown":      v2.DiscountPercent50,
			"twouphalfdown": v2.Discount2x50,
		},
		HRKeywords:       []string{"hitandrun", "hit_run.gif", "Hit and Run", "Hit & Run"},
		TitleSelector:    "h1",
		DiscountSelector: "h1 font.free, h1 font[class]",
		EndTimeSelector:  "h1 span[title]",
		SizeSelector:     "td.rowfollow:contains('大小')",
		SizeRegex:        `大小[::]\s*([\d.]+)\s*(GB|MB|KB|TB)`,
	},
	LevelRequirements: xingYunGeLevelRequirements,
}

Functions

func GetHDSkyLevelRequirements

func GetHDSkyLevelRequirements(joinTime int64) []v2.SiteLevelRequirement

GetHDSkyLevelRequirements returns appropriate level requirements based on join date

func GetMTeamLevelName

func GetMTeamLevelName(roleID string) string

GetMTeamLevelName returns level name from role ID

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL