v2

package
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Jan 17, 2026 License: MIT Imports: 38 Imported by: 0

Documentation

Overview

Package v2 provides caching utilities for performance optimization

Package v2 provides circuit breaker pattern for fault tolerance

Package v2 provides HTTP client utilities based on github.com/sunerpy/requests

Package v2 provides level management for PT sites

Package v2 provides metrics collection for performance monitoring

Package v2 provides migration utilities for backward compatibility

Package v2 provides site parsers for NexusPHP-based sites These parsers are migrated from internal/site_compat.go

Package v2 provides site registry for managing site metadata and creation

Package v2 provides a generic site architecture using the Driver pattern. This package implements type-safe site drivers for different PT site architectures including NexusPHP, Unit3D, Gazelle, and mTorrent (M-Team).

Package v2 provides database-backed user info repository

Package v2 provides input validation utilities

Index

Constants

View Source
const (
	KB int64 = 1024
	MB int64 = 1024 * KB
	GB int64 = 1024 * MB
	TB int64 = 1024 * GB
)

Size constants

View Source
const (
	// MinVipLevelID is the minimum level ID for VIP users
	MinVipLevelID = 100
	// MinManagerLevelID is the minimum level ID for manager/staff users
	MinManagerLevelID = 200
)

Level ID constants for special user groups

Variables

View Source
var (
	ErrNoFreeTorrents        = errors.New("no free torrents found")
	ErrArchiveCreationFailed = errors.New("failed to create archive")
	ErrTorrentDownloadFailed = errors.New("failed to download torrent")
)

Batch download errors

View Source
var (
	// ErrCircuitOpen is returned when the circuit is open
	ErrCircuitOpen = errors.New("circuit breaker is open")
	// ErrTooManyRequests is returned when too many requests in half-open state
	ErrTooManyRequests = errors.New("too many requests in half-open state")
)
View Source
var (
	ErrAllURLsFailed    = errors.New("all URLs failed")
	ErrNoURLsConfigured = errors.New("no URLs configured for site")
)

Failover errors

View Source
var (
	ErrInvalidTorrent  = errors.New("invalid torrent file")
	ErrInvalidMagnet   = errors.New("invalid magnet link")
	ErrTorrentNotFound = errors.New("torrent not found")
	ErrInvalidInfoHash = errors.New("invalid info hash")
)

Common errors for torrent operations

View Source
var (
	ErrSiteNotFound       = errors.New("site not found")
	ErrInvalidCredentials = errors.New("invalid credentials")
	ErrSessionExpired     = errors.New("session expired")
	ErrAuthFailed         = errors.New("authentication failed: please check cookie or 2FA settings")
	Err2FARequired        = ErrAuthFailed // Alias for backward compatibility
	ErrRateLimited        = errors.New("rate limited")
	ErrParseError         = errors.New("failed to parse response")
	ErrNetworkError       = errors.New("network error")
	ErrNotImplemented     = errors.New("not implemented")
)

Common errors for site operations

View Source
var (
	ErrBonusInfoUnavailable    = errors.New("bonus info unavailable")
	ErrMessageCountUnavailable = errors.New("message count unavailable")
	ErrPeerStatsUnavailable    = errors.New("peer statistics unavailable")
)

Extended info errors

View Source
var (
	ErrEmptyInput       = errors.New("input cannot be empty")
	ErrInputTooLong     = errors.New("input exceeds maximum length")
	ErrInvalidURL       = errors.New("invalid URL format")
	ErrInvalidEmail     = errors.New("invalid email format")
	ErrInvalidHash      = errors.New("invalid hash format")
	ErrInvalidCharacter = errors.New("input contains invalid characters")
	ErrFileTooLarge     = errors.New("file exceeds maximum size")
)

Validation errors

View Source
var CSTLocation = utils.CSTLocation

CSTLocation is the China Standard Time timezone (UTC+8). Re-exported from utils for convenience within this package.

View Source
var DebugUserInfo = false

DebugUserInfo enables debug output for user info parsing Set to true to see detailed parsing information

View Source
var DefaultMetrics = NewMetricsRegistry()

DefaultMetrics is the default metrics registry

View Source
var DefaultSiteMetrics = NewSiteMetrics(DefaultMetrics)

DefaultSiteMetrics is the default site metrics instance

View Source
var DefaultSiteURLs = map[SiteName][]string{
	SiteNameMTeam: {
		"https://api.m-team.cc",
		"https://kp.m-team.cc",
		"https://pt.m-team.cc",
	},
	SiteNameHDSky: {
		"https://hdsky.me",
	},
	SiteNameSpringSunday: {
		"https://springsunday.net",
	},
	SiteNameCHDBits: {
		"https://chdbits.co",
	},
	SiteNameTTG: {
		"https://totheglory.im",
	},
	SiteNameOurBits: {
		"https://ourbits.club",
	},
	SiteNamePterClub: {
		"https://pterclub.com",
	},
	SiteNameAudiences: {
		"https://audiences.me",
	},
}

DefaultSiteURLs contains the default base URLs for each site Sites can have multiple URLs for failover purposes

DefaultValidator is the default validator instance

View Source
var FreeDiscountLevels = []DiscountLevel{
	DiscountFree,
	Discount2xFree,
}

FreeDiscountLevels contains all discount levels that result in free download

View Source
var InfoHashFromMagnet = ExtractMagnetHash

InfoHashFromMagnet is an alias for ExtractMagnetHash

View Source
var ParserRegistry = map[SiteName]func() NexusPHPDetailParser{
	SiteNameHDSky:        func() NexusPHPDetailParser { return NewHDSkyParser() },
	SiteNameSpringSunday: func() NexusPHPDetailParser { return NewSpringSundayParser() },
}

ParserRegistry maps site names to their parsers

SiteKindMap maps site names to their architecture kinds

View Source
var TorrentHashRegex = regexp.MustCompile(`^[a-fA-F0-9]{40}$`)

TorrentHashRegex matches a 40-character hex info hash

Functions

func AddAtPausedToAutoStart

func AddAtPausedToAutoStart(addAtPaused bool) bool

AddAtPausedToAutoStart converts the new AddAtPaused field back to the old autoStart field This is the inverse of AutoStartToAddAtPaused

func ApplyFilters

func ApplyFilters(value any, filters []Filter) any

ApplyFilters applies a chain of filters to a value

func AutoStartToAddAtPaused

func AutoStartToAddAtPaused(autoStart bool) bool

AutoStartToAddAtPaused converts the old autoStart field to the new AddAtPaused field The mapping is: autoStart=true → AddAtPaused=false (start immediately)

autoStart=false → AddAtPaused=true (add paused)
func BuildMagnetLink(infoHash, name string, trackers []string) string

BuildMagnetLink builds a magnet link from an info hash and optional name

func CalculateEffectiveDownload

func CalculateEffectiveDownload(sizeBytes int64, discountLevel DiscountLevel) int64

CalculateEffectiveDownload calculates the effective download size based on discount

func CalculateEffectiveUpload

func CalculateEffectiveUpload(sizeBytes int64, discountLevel DiscountLevel) int64

CalculateEffectiveUpload calculates the effective upload size based on discount

func CalculateRatioImpact

func CalculateRatioImpact(currentUploaded, currentDownloaded, torrentSize int64, discountLevel DiscountLevel, expectedUploadRatio float64) float64

CalculateRatioImpact calculates the ratio impact of downloading a torrent Returns the change in ratio (positive = ratio increases, negative = ratio decreases)

func CanDownloadInTimeSimple

func CanDownloadInTimeSimple(sizeBytes, downloadSpeedBps int64, discountLevel DiscountLevel, discountEndTime time.Time) bool

CanDownloadInTimeSimple is a simplified version that returns just a boolean

func CompareDiscounts

func CompareDiscounts(a, b DiscountLevel) int

CompareDiscounts compares two discount levels Returns: -1 if a < b, 0 if a == b, 1 if a > b

func ComputeTorrentHash

func ComputeTorrentHash(data []byte) (string, error)

ComputeTorrentHash computes the info hash from torrent file bytes

func ComputeTorrentHashFromFile

func ComputeTorrentHashFromFile(path string) (string, error)

ComputeTorrentHashFromFile computes the info hash from a torrent file path

func DiscountPriority

func DiscountPriority(level DiscountLevel) int

DiscountPriority returns a priority value for sorting torrents by discount Higher priority = better discount

func EstimateDownloadTime

func EstimateDownloadTime(sizeBytes, downloadSpeedBps int64) time.Duration

EstimateDownloadTime estimates the download time for a given size and speed

func ExtractMagnetHash

func ExtractMagnetHash(magnetURI string) (string, error)

ExtractMagnetHash extracts the info hash from a magnet link

func FormatBytes

func FormatBytes(bytes int64) string

FormatBytes formats bytes to human readable string

func GetAllSiteCategoryConfigs

func GetAllSiteCategoryConfigs() map[string]SiteCategoryConfig

GetAllSiteCategoryConfigs 获取所有站点的分类配置

func GetSiteNextLevelUnmet

func GetSiteNextLevelUnmet(info *UserInfo, requirements []SiteLevelRequirement) map[string]any

GetSiteNextLevelUnmet returns unmet requirements for next level

func GetSiteURLsForKind

func GetSiteURLsForKind(kind SiteKind) map[SiteName][]string

GetSiteURLsForKind returns URLs for sites of a specific kind This is a convenience function for getting URLs by site architecture type

func GuessUserLevelID

func GuessUserLevelID(info *UserInfo, requirements []SiteLevelRequirement) int

GuessUserLevelID determines user level from info and requirements

func HandleQBittorrentAuthWithRequests

func HandleQBittorrentAuthWithRequests(ctx context.Context, baseURL, username, password string) (string, error)

HandleQBittorrentAuthWithRequests handles QBittorrent 403 responses using requests library

func HandleTransmissionSession

func HandleTransmissionSession(resp *http.Response) string

HandleTransmissionSession handles Transmission 409 responses by extracting session ID

func IsBetterDiscount

func IsBetterDiscount(a, b DiscountLevel) bool

IsBetterDiscount returns true if a is a better discount than b

func IsFreeTorrent

func IsFreeTorrent(level DiscountLevel) bool

IsFreeTorrent checks if the given discount level results in free download

func IsTorrentHash

func IsTorrentHash(s string) bool

IsTorrentHash checks if a string is a valid torrent info hash

func NormalizeInfoHash

func NormalizeInfoHash(hash string) string

NormalizeInfoHash normalizes an info hash to lowercase

func ParseTimeInCST added in v0.3.0

func ParseTimeInCST(layout, value string) (time.Time, error)

ParseTimeInCST parses a time string in CST timezone. Re-exported from utils for convenience within this package.

func RegisterFilter

func RegisterFilter(name string, fn FilterFunc)

RegisterFilter adds a custom filter

func RegisterSiteDefinition

func RegisterSiteDefinition(def *SiteDefinition)

RegisterSiteDefinition is a convenience function for init() registration

func SanitizeHTML

func SanitizeHTML(input string) string

SanitizeHTML sanitizes HTML content by escaping dangerous tags

func SanitizeSearchKeyword

func SanitizeSearchKeyword(keyword string) string

SanitizeSearchKeyword sanitizes a search keyword

func StripHTMLTags

func StripHTMLTags(input string) string

StripHTMLTags removes HTML tags from a string

func ValidateAPIKey

func ValidateAPIKey(apiKey string) error

ValidateAPIKey validates an API key

func ValidateCookie

func ValidateCookie(cookie string) error

ValidateCookie validates a cookie string

func ValidateInfoHash

func ValidateInfoHash(hash string) bool

ValidateInfoHash validates an info hash string

func ValidateTorrentFile

func ValidateTorrentFile(data []byte) error

ValidateTorrentFile validates torrent file content

Types

type AggregatedStats

type AggregatedStats struct {
	// TotalUploaded is the sum of all uploaded bytes
	TotalUploaded int64 `json:"totalUploaded"`
	// TotalDownloaded is the sum of all downloaded bytes
	TotalDownloaded int64 `json:"totalDownloaded"`
	// AverageRatio is the average ratio across all sites
	AverageRatio float64 `json:"averageRatio"`
	// TotalSeeding is the sum of all seeding torrents
	TotalSeeding int `json:"totalSeeding"`
	// TotalLeeching is the sum of all leeching torrents
	TotalLeeching int `json:"totalLeeching"`
	// TotalBonus is the sum of all bonus points
	TotalBonus float64 `json:"totalBonus"`
	// SiteCount is the number of sites included
	SiteCount int `json:"siteCount"`
	// LastUpdate is when this was last calculated (Unix seconds)
	LastUpdate int64 `json:"lastUpdate"`
	// PerSiteStats contains individual site statistics
	PerSiteStats []UserInfo `json:"perSiteStats"`
	// TotalBonusPerHour is the sum of all bonus per hour
	TotalBonusPerHour float64 `json:"totalBonusPerHour,omitempty"`
	// TotalSeedingBonus is the sum of all seeding bonus points
	TotalSeedingBonus float64 `json:"totalSeedingBonus,omitempty"`
	// TotalUnreadMessages is the sum of all unread messages
	TotalUnreadMessages int `json:"totalUnreadMessages,omitempty"`
	// TotalSeederSize is the sum of all seeding sizes
	TotalSeederSize int64 `json:"totalSeederSize,omitempty"`
	// TotalLeecherSize is the sum of all leeching sizes
	TotalLeecherSize int64 `json:"totalLeecherSize,omitempty"`
}

AggregatedStats represents aggregated statistics across all sites

type AlternativeRequirement

type AlternativeRequirement struct {
	SeedingBonus float64 `json:"seedingBonus,omitempty"`
	Uploads      int     `json:"uploads,omitempty"`
	Bonus        float64 `json:"bonus,omitempty"`
	Downloaded   string  `json:"downloaded,omitempty"`
	Ratio        float64 `json:"ratio,omitempty"`
}

AlternativeRequirement for OR-based requirements

type BaseSite

type BaseSite[Req any, Res any] struct {
	// contains filtered or unexported fields
}

BaseSite wraps a Driver and provides common functionality like rate limiting and logging. It implements the Site interface by delegating to the underlying Driver.

func NewBaseSite

func NewBaseSite[Req, Res any](driver Driver[Req, Res], config BaseSiteConfig) *BaseSite[Req, Res]

NewBaseSite creates a new BaseSite with the given driver and configuration

func (*BaseSite[Req, Res]) Close

func (b *BaseSite[Req, Res]) Close() error

Close releases any resources held by the site

func (*BaseSite[Req, Res]) Download

func (b *BaseSite[Req, Res]) Download(ctx context.Context, torrentID string) ([]byte, error)

Download downloads a torrent file by ID

func (*BaseSite[Req, Res]) GetDriver

func (b *BaseSite[Req, Res]) GetDriver() Driver[Req, Res]

GetDriver returns the underlying driver (for testing purposes)

func (*BaseSite[Req, Res]) GetRateLimiter

func (b *BaseSite[Req, Res]) GetRateLimiter() *rate.Limiter

GetRateLimiter returns the rate limiter (for testing purposes)

func (*BaseSite[Req, Res]) GetUserInfo

func (b *BaseSite[Req, Res]) GetUserInfo(ctx context.Context) (UserInfo, error)

GetUserInfo fetches the current user's information

func (*BaseSite[Req, Res]) ID

func (b *BaseSite[Req, Res]) ID() string

ID returns the unique site identifier

func (*BaseSite[Req, Res]) IsLoggedIn

func (b *BaseSite[Req, Res]) IsLoggedIn() bool

IsLoggedIn returns whether the site is currently logged in

func (*BaseSite[Req, Res]) Kind

func (b *BaseSite[Req, Res]) Kind() SiteKind

Kind returns the site architecture type

func (*BaseSite[Req, Res]) Login

func (b *BaseSite[Req, Res]) Login(ctx context.Context, creds Credentials) error

Login authenticates with the site

func (*BaseSite[Req, Res]) Name

func (b *BaseSite[Req, Res]) Name() string

Name returns the human-readable site name

func (*BaseSite[Req, Res]) Search

func (b *BaseSite[Req, Res]) Search(ctx context.Context, query SearchQuery) ([]TorrentItem, error)

Search searches for torrents on the site

type BaseSiteConfig

type BaseSiteConfig struct {
	ID        string
	Name      string
	Kind      SiteKind
	RateLimit float64 // Requests per second
	RateBurst int     // Maximum burst size
	Logger    *zap.Logger
}

BaseSiteConfig holds configuration for creating a BaseSite

type BatchDownloadService

type BatchDownloadService struct {
	// contains filtered or unexported fields
}

BatchDownloadService provides batch download functionality for free torrents

func NewBatchDownloadService

func NewBatchDownloadService(site Site, logger *zap.Logger) *BatchDownloadService

NewBatchDownloadService creates a new batch download service

func (*BatchDownloadService) DownloadFreeTorrents

func (s *BatchDownloadService) DownloadFreeTorrents(
	ctx context.Context,
	archiveType string,
	outputDir string,
) (*FreeTorrentBatchResult, error)

DownloadFreeTorrents downloads all free torrents and packages them into an archive

func (*BatchDownloadService) FetchFreeTorrents

func (s *BatchDownloadService) FetchFreeTorrents(ctx context.Context) ([]TorrentItem, error)

FetchFreeTorrents fetches all free torrents from the site This method only filters by free status, no other filter rules are applied

type CacheEntry

type CacheEntry struct {
	Key       string
	Value     any
	ExpiresAt time.Time
}

CacheEntry represents a cached item with TTL

func (*CacheEntry) IsExpired

func (e *CacheEntry) IsExpired() bool

IsExpired checks if the cache entry has expired

type CachedSearchOrchestrator

type CachedSearchOrchestrator struct {
	*SearchOrchestrator
	// contains filtered or unexported fields
}

CachedSearchOrchestrator wraps SearchOrchestrator with caching

func NewCachedSearchOrchestrator

func NewCachedSearchOrchestrator(orchestrator *SearchOrchestrator, cacheConfig SearchCacheConfig) *CachedSearchOrchestrator

NewCachedSearchOrchestrator creates a new CachedSearchOrchestrator

func (*CachedSearchOrchestrator) CacheSize

func (c *CachedSearchOrchestrator) CacheSize() int

CacheSize returns the number of cached entries

func (*CachedSearchOrchestrator) CleanupCache

func (c *CachedSearchOrchestrator) CleanupCache() int

CleanupCache removes expired entries

func (*CachedSearchOrchestrator) ClearCache

func (c *CachedSearchOrchestrator) ClearCache()

ClearCache clears the search cache

func (*CachedSearchOrchestrator) Search

Search performs a cached search

type CategoryDefinition

type CategoryDefinition struct {
	Name    string           `json:"name"`
	Key     string           `json:"key"`
	Options []CategoryOption `json:"options"`
	Notes   string           `json:"notes,omitempty"`
}

CategoryDefinition 分类定义

type CategoryOption

type CategoryOption struct {
	Value any    `json:"value"`
	Name  string `json:"name"`
	Type  string `json:"type,omitempty"` // e.g., "normal", "adult" for mteam
}

CategoryOption 分类选项

type CircuitBreaker

type CircuitBreaker struct {
	// contains filtered or unexported fields
}

CircuitBreaker implements the circuit breaker pattern

func NewCircuitBreaker

func NewCircuitBreaker(name string, config CircuitBreakerConfig) *CircuitBreaker

NewCircuitBreaker creates a new circuit breaker

func (*CircuitBreaker) Execute

func (cb *CircuitBreaker) Execute(fn func() error) error

Execute runs the given function if the circuit allows it

func (*CircuitBreaker) Name

func (cb *CircuitBreaker) Name() string

Name returns the circuit breaker name

func (*CircuitBreaker) Reset

func (cb *CircuitBreaker) Reset()

Reset resets the circuit breaker to closed state

func (*CircuitBreaker) State

func (cb *CircuitBreaker) State() CircuitState

State returns the current circuit state

func (*CircuitBreaker) Stats

func (cb *CircuitBreaker) Stats() CircuitBreakerStats

Stats returns circuit breaker statistics

type CircuitBreakerConfig

type CircuitBreakerConfig struct {
	// FailureThreshold is the number of failures before opening the circuit
	FailureThreshold int
	// SuccessThreshold is the number of successes in half-open to close the circuit
	SuccessThreshold int
	// Timeout is how long to wait before transitioning from open to half-open
	Timeout time.Duration
	// MaxHalfOpenRequests is the max concurrent requests in half-open state
	MaxHalfOpenRequests int
}

CircuitBreakerConfig configures the circuit breaker

func DefaultCircuitBreakerConfig

func DefaultCircuitBreakerConfig() CircuitBreakerConfig

DefaultCircuitBreakerConfig returns default configuration

type CircuitBreakerRegistry

type CircuitBreakerRegistry struct {
	// contains filtered or unexported fields
}

CircuitBreakerRegistry manages multiple circuit breakers

func NewCircuitBreakerRegistry

func NewCircuitBreakerRegistry(config CircuitBreakerConfig) *CircuitBreakerRegistry

NewCircuitBreakerRegistry creates a new registry

func (*CircuitBreakerRegistry) Get

Get returns or creates a circuit breaker for the given name

func (*CircuitBreakerRegistry) GetAll

func (r *CircuitBreakerRegistry) GetAll() []*CircuitBreaker

GetAll returns all circuit breakers

func (*CircuitBreakerRegistry) Reset

func (r *CircuitBreakerRegistry) Reset()

Reset resets all circuit breakers

func (*CircuitBreakerRegistry) Stats

Stats returns statistics for all circuit breakers

type CircuitBreakerStats

type CircuitBreakerStats struct {
	Name            string    `json:"name"`
	State           string    `json:"state"`
	Failures        int       `json:"failures"`
	Successes       int       `json:"successes"`
	LastFailureTime time.Time `json:"lastFailureTime,omitempty"`
}

CircuitBreakerStats contains circuit breaker statistics

type CircuitState

type CircuitState int

CircuitState represents the state of a circuit breaker

const (
	// CircuitClosed allows requests to pass through
	CircuitClosed CircuitState = iota
	// CircuitOpen blocks all requests
	CircuitOpen
	// CircuitHalfOpen allows limited requests for testing recovery
	CircuitHalfOpen
)

func (CircuitState) String

func (s CircuitState) String() string

String returns the string representation of the circuit state

type ConfigMigrator

type ConfigMigrator struct {
	// contains filtered or unexported fields
}

ConfigMigrator handles migration from old configuration formats to new ones

func NewConfigMigrator

func NewConfigMigrator(logger *zap.Logger) *ConfigMigrator

NewConfigMigrator creates a new ConfigMigrator

func (*ConfigMigrator) CheckForDeprecatedFields

func (m *ConfigMigrator) CheckForDeprecatedFields(configJSON []byte) []DeprecationWarning

CheckForDeprecatedFields checks a config JSON for deprecated fields and logs warnings

func (*ConfigMigrator) DetectDownloaderConfigVersion

func (m *ConfigMigrator) DetectDownloaderConfigVersion(configJSON []byte) string

DetectConfigVersion attempts to detect if a config is in old or new format Returns "old" for old format, "new" for new format, or "unknown"

func (*ConfigMigrator) MigrateDownloaderConfig

func (m *ConfigMigrator) MigrateDownloaderConfig(old OldDownloaderConfig) NewDownloaderConfig

MigrateDownloaderConfig converts an old downloader config to the new format

func (*ConfigMigrator) MigrateDownloaderConfigIfNeeded

func (m *ConfigMigrator) MigrateDownloaderConfigIfNeeded(configJSON []byte) ([]byte, bool, error)

MigrateDownloaderConfigIfNeeded migrates config only if it's in old format

func (*ConfigMigrator) MigrateDownloaderConfigJSON

func (m *ConfigMigrator) MigrateDownloaderConfigJSON(oldJSON []byte) ([]byte, error)

MigrateDownloaderConfigJSON converts old downloader config JSON to new format

func (*ConfigMigrator) MigrateSiteConfig

func (m *ConfigMigrator) MigrateSiteConfig(old OldSiteConfig) SiteConfig

MigrateSiteConfig converts an old site config to the new SiteConfig format

func (*ConfigMigrator) MigrateSiteConfigJSON

func (m *ConfigMigrator) MigrateSiteConfigJSON(oldJSON []byte) ([]byte, error)

MigrateSiteConfigJSON converts old site config JSON to new format

type Counter

type Counter struct {
	// contains filtered or unexported fields
}

Counter is a monotonically increasing counter

func NewCounter

func NewCounter() *Counter

NewCounter creates a new counter

func (*Counter) Add

func (c *Counter) Add(delta int64)

Add adds the given value to the counter

func (*Counter) Inc

func (c *Counter) Inc()

Inc increments the counter by 1

func (*Counter) Value

func (c *Counter) Value() int64

Value returns the current counter value

type Credentials

type Credentials struct {
	Username string `json:"username,omitempty"`
	Password string `json:"password,omitempty"`
	Cookie   string `json:"cookie,omitempty"`
	APIKey   string `json:"apiKey,omitempty"`
}

Credentials holds authentication information for a site

type DBUserInfoRepo

type DBUserInfoRepo struct {
	// contains filtered or unexported fields
}

DBUserInfoRepo is a database-backed implementation of UserInfoRepo

func NewDBUserInfoRepo

func NewDBUserInfoRepo(db *gorm.DB) (*DBUserInfoRepo, error)

NewDBUserInfoRepo creates a new database-backed user info repository

func (*DBUserInfoRepo) Count

func (r *DBUserInfoRepo) Count(ctx context.Context) (int64, error)

Count returns the number of stored user info entries

func (*DBUserInfoRepo) Delete

func (r *DBUserInfoRepo) Delete(ctx context.Context, site string) error

Delete removes user info for a site

func (*DBUserInfoRepo) DeleteAll

func (r *DBUserInfoRepo) DeleteAll(ctx context.Context) error

DeleteAll removes all user info records

func (*DBUserInfoRepo) Get

func (r *DBUserInfoRepo) Get(ctx context.Context, site string) (UserInfo, error)

Get retrieves user info for a specific site

func (*DBUserInfoRepo) GetAggregated

func (r *DBUserInfoRepo) GetAggregated(ctx context.Context) (AggregatedStats, error)

GetAggregated calculates aggregated statistics

func (*DBUserInfoRepo) ListAll

func (r *DBUserInfoRepo) ListAll(ctx context.Context) ([]UserInfo, error)

ListAll retrieves all stored user info

func (*DBUserInfoRepo) ListBySites

func (r *DBUserInfoRepo) ListBySites(ctx context.Context, sites []string) ([]UserInfo, error)

ListBySites retrieves user info for specific sites

func (*DBUserInfoRepo) Save

func (r *DBUserInfoRepo) Save(ctx context.Context, info UserInfo) error

Save stores user info for a site (upsert)

type Deduper

type Deduper struct{}

Deduper removes duplicate torrents based on InfoHash

func NewDeduper

func NewDeduper() *Deduper

NewDeduper creates a new Deduper

func (*Deduper) Deduplicate

func (d *Deduper) Deduplicate(items []TorrentItem) []TorrentItem

Deduplicate removes duplicate torrents, keeping the best version of each

func (*Deduper) DeduplicateByTitle

func (d *Deduper) DeduplicateByTitle(items []TorrentItem, normalizer *Normalizer) []TorrentItem

DeduplicateByTitle removes duplicates based on normalized title This is useful when InfoHash is not available

type DeprecationWarning

type DeprecationWarning struct {
	Field       string `json:"field"`
	Message     string `json:"message"`
	Replacement string `json:"replacement"`
	Version     string `json:"version"` // Version when it will be removed
}

DeprecationWarning represents a deprecation warning message

func GetDeprecationWarnings

func GetDeprecationWarnings() []DeprecationWarning

GetDeprecationWarnings returns a list of deprecation warnings for old config fields

type DiscountLevel

type DiscountLevel string

DiscountLevel represents the discount level of a torrent

const (
	// DiscountNone represents no discount (normal download/upload counting)
	DiscountNone DiscountLevel = "NONE"
	// DiscountFree represents free download (0% download counting)
	DiscountFree DiscountLevel = "FREE"
	// Discount2xFree represents 2x free (0% download, 2x upload counting)
	Discount2xFree DiscountLevel = "2XFREE"
	// DiscountPercent50 represents 50% download counting
	DiscountPercent50 DiscountLevel = "PERCENT_50"
	// DiscountPercent30 represents 30% download counting
	DiscountPercent30 DiscountLevel = "PERCENT_30"
	// DiscountPercent70 represents 70% download counting
	DiscountPercent70 DiscountLevel = "PERCENT_70"
	// Discount2xUp represents 2x upload counting
	Discount2xUp DiscountLevel = "2XUP"
	// Discount2x50 represents 2x upload and 50% download counting
	Discount2x50 DiscountLevel = "2X50"
)

func SuggestBestDiscount

func SuggestBestDiscount(currentDiscount DiscountLevel, discountEndTime time.Time, minTimeNeeded time.Duration) DiscountLevel

SuggestBestDiscount suggests the best discount level to wait for based on current discount and time constraints

func (DiscountLevel) GetDownloadRatio

func (d DiscountLevel) GetDownloadRatio() float64

GetDownloadRatio returns the download counting ratio (0.0 = free, 1.0 = normal)

func (DiscountLevel) GetUploadRatio

func (d DiscountLevel) GetUploadRatio() float64

GetUploadRatio returns the upload counting ratio (1.0 = normal, 2.0 = double)

type DownloadFeasibility

type DownloadFeasibility struct {
	// CanComplete indicates if the download can complete before discount ends
	CanComplete bool `json:"canComplete"`
	// EstimatedTime is the estimated download time
	EstimatedTime time.Duration `json:"estimatedTime"`
	// TimeRemaining is the time remaining before discount ends
	TimeRemaining time.Duration `json:"timeRemaining"`
	// EffectiveSize is the size that counts towards download quota
	EffectiveSize int64 `json:"effectiveSize"`
	// Margin is the time margin (positive = can complete with time to spare)
	Margin time.Duration `json:"margin"`
}

DownloadFeasibility represents the result of download feasibility calculation

func CanDownloadInTime

func CanDownloadInTime(sizeBytes, downloadSpeedBps int64, discountLevel DiscountLevel, discountEndTime time.Time) DownloadFeasibility

CanDownloadInTime calculates if a torrent can be downloaded before the discount expires Parameters:

  • sizeBytes: total torrent size in bytes
  • downloadSpeedBps: download speed in bytes per second
  • discountLevel: current discount level
  • discountEndTime: when the discount expires (zero time means permanent)

Returns DownloadFeasibility with detailed information

type Driver

type Driver[Req any, Res any] interface {
	// PrepareSearch converts a standard SearchQuery to site-specific request
	PrepareSearch(query SearchQuery) (Req, error)
	// Execute performs the actual network call
	Execute(ctx context.Context, req Req) (Res, error)
	// ParseSearch converts the raw response into standard TorrentItem list
	ParseSearch(res Res) ([]TorrentItem, error)
	// GetUserInfo fetches complete user information (including extended info if supported)
	// This method is responsible for making all necessary API calls
	GetUserInfo(ctx context.Context) (UserInfo, error)
	// PrepareDownload prepares request for downloading a torrent
	PrepareDownload(torrentID string) (Req, error)
	// ParseDownload extracts torrent file data from response
	ParseDownload(res Res) ([]byte, error)
}

Driver defines how to interact with a specific type of site architecture. Req is the type of request payload (e.g., url.Values for forms, struct for JSON). Res is the raw response type (e.g., *goquery.Document, JSON struct).

type FailoverHTTPClient

type FailoverHTTPClient struct {
	// contains filtered or unexported fields
}

FailoverHTTPClient is a generic HTTP client with automatic URL failover All site drivers can use this client for making HTTP requests Uses requests library instead of net/http directly

func NewFailoverHTTPClient

func NewFailoverHTTPClient(config URLFailoverConfig, opts ...FailoverOption) *FailoverHTTPClient

NewFailoverHTTPClient creates a new failover HTTP client

func (*FailoverHTTPClient) Do

func (c *FailoverHTTPClient) Do(ctx context.Context, method, path string, body []byte, headers map[string]string) (*HTTPResponse, error)

Do performs a custom request with automatic URL failover Note: The request URL should be a path (e.g., "/api/search"), not a full URL

func (*FailoverHTTPClient) Get

func (c *FailoverHTTPClient) Get(ctx context.Context, path string, headers map[string]string) (*HTTPResponse, error)

Get performs a GET request with automatic URL failover

func (*FailoverHTTPClient) GetCurrentBaseURL

func (c *FailoverHTTPClient) GetCurrentBaseURL() string

GetCurrentBaseURL returns the currently active base URL

func (*FailoverHTTPClient) Post

func (c *FailoverHTTPClient) Post(ctx context.Context, path string, body []byte, headers map[string]string) (*HTTPResponse, error)

Post performs a POST request with automatic URL failover

type FailoverOption

type FailoverOption func(*FailoverHTTPClient)

FailoverOption is a functional option for FailoverHTTPClient

func WithLogger

func WithLogger(logger *zap.Logger) FailoverOption

WithLogger sets the logger

func WithUserAgent

func WithUserAgent(ua string) FailoverOption

WithUserAgent sets the User-Agent header

type FieldSelector

type FieldSelector struct {
	// Selector is CSS selector(s) for HTML or JSON path for API
	Selector []string `json:"selector,omitempty"`
	// Text is the default value if selector doesn't match
	Text string `json:"text,omitempty"`
	// Attr is the attribute to extract (for HTML elements)
	Attr string `json:"attr,omitempty"`
	// Filters to apply to extracted value
	Filters []Filter `json:"filters,omitempty"`
	// ElementProcess is a custom processing function name
	ElementProcess string `json:"elementProcess,omitempty"`
	// SwitchFilters for different selectors
	SwitchFilters map[string][]Filter `json:"switchFilters,omitempty"`
}

FieldSelector defines how to extract a field value

type Filter

type Filter struct {
	// Name is the filter function name
	Name string `json:"name"`
	// Args are optional arguments
	Args []any `json:"args,omitempty"`
}

Filter defines a value transformation

type FilterFunc

type FilterFunc func(value any, args ...any) any

FilterFunc is a function that transforms a value

func GetFilter

func GetFilter(name string) (FilterFunc, bool)

GetFilter returns a filter by name

type FlexInt

type FlexInt int

FlexInt handles fields that can be either string or int in JSON

func (FlexInt) Int

func (fi FlexInt) Int() int

Int returns the int value

func (*FlexInt) UnmarshalJSON

func (fi *FlexInt) UnmarshalJSON(data []byte) error

UnmarshalJSON implements custom JSON unmarshaling for FlexInt

type FlexibleCode

type FlexibleCode string

FlexibleCode handles M-Team API code field which can be either string or number

func (FlexibleCode) IsSuccess

func (fc FlexibleCode) IsSuccess() bool

IsSuccess checks if the code represents success (typically "0" or "SUCCESS")

func (FlexibleCode) String

func (fc FlexibleCode) String() string

String returns the string representation of the code

func (*FlexibleCode) UnmarshalJSON

func (fc *FlexibleCode) UnmarshalJSON(data []byte) error

UnmarshalJSON implements custom JSON unmarshaling for FlexibleCode

type FreeTorrentBatchResult

type FreeTorrentBatchResult struct {
	// ArchivePath is the path to the created archive file
	ArchivePath string `json:"archivePath"`
	// ArchiveType is the type of archive (tar.gz or zip)
	ArchiveType string `json:"archiveType"`
	// TorrentCount is the number of torrents in the archive
	TorrentCount int `json:"torrentCount"`
	// TotalSize is the total size of all torrents (bytes)
	TotalSize int64 `json:"totalSize"`
	// Manifest contains metadata for all torrents
	Manifest []TorrentManifest `json:"manifest"`
}

FreeTorrentBatchResult represents the result of a batch download operation

type Gauge

type Gauge struct {
	// contains filtered or unexported fields
}

Gauge is a value that can go up and down

func NewGauge

func NewGauge() *Gauge

NewGauge creates a new gauge

func (*Gauge) Add

func (g *Gauge) Add(delta int64)

Add adds the given value to the gauge

func (*Gauge) Dec

func (g *Gauge) Dec()

Dec decrements the gauge by 1

func (*Gauge) Inc

func (g *Gauge) Inc()

Inc increments the gauge by 1

func (*Gauge) Set

func (g *Gauge) Set(value int64)

Set sets the gauge value

func (*Gauge) Value

func (g *Gauge) Value() int64

Value returns the current gauge value

type GazelleDriver

type GazelleDriver struct {
	BaseURL string
	APIKey  string
	Cookie  string
	// contains filtered or unexported fields
}

GazelleDriver implements the Driver interface for Gazelle sites

func NewGazelleDriver

func NewGazelleDriver(config GazelleDriverConfig) *GazelleDriver

NewGazelleDriver creates a new Gazelle driver

func (*GazelleDriver) Execute

Execute performs the HTTP request

func (*GazelleDriver) GetUserInfo

func (d *GazelleDriver) GetUserInfo(ctx context.Context) (UserInfo, error)

GetUserInfo fetches complete user information

func (*GazelleDriver) ParseDownload

func (d *GazelleDriver) ParseDownload(res GazelleResponse) ([]byte, error)

ParseDownload extracts torrent file data from the response

func (*GazelleDriver) ParseSearch

func (d *GazelleDriver) ParseSearch(res GazelleResponse) ([]TorrentItem, error)

ParseSearch extracts torrent items from the response

func (*GazelleDriver) ParseUserInfo

func (d *GazelleDriver) ParseUserInfo(res GazelleResponse) (UserInfo, error)

ParseUserInfo extracts user info from the response

func (*GazelleDriver) PrepareDownload

func (d *GazelleDriver) PrepareDownload(torrentID string) (GazelleRequest, error)

PrepareDownload prepares a request for downloading a torrent

func (*GazelleDriver) PrepareSearch

func (d *GazelleDriver) PrepareSearch(query SearchQuery) (GazelleRequest, error)

PrepareSearch converts a SearchQuery to a Gazelle request

func (*GazelleDriver) PrepareUserInfo

func (d *GazelleDriver) PrepareUserInfo() (GazelleRequest, error)

PrepareUserInfo prepares a request for user info

type GazelleDriverConfig

type GazelleDriverConfig struct {
	BaseURL    string
	APIKey     string
	Cookie     string
	HTTPClient *SiteHTTPClient // Use SiteHTTPClient instead of *http.Client
	UserAgent  string
}

GazelleDriverConfig holds configuration for creating a Gazelle driver

type GazelleOptions

type GazelleOptions struct {
	APIKey string `json:"apiKey,omitempty"`
	Cookie string `json:"cookie,omitempty"`
}

GazelleOptions holds Gazelle-specific configuration

type GazelleRequest

type GazelleRequest struct {
	// Action is the API action
	Action string
	// Params are the query parameters
	Params url.Values
}

GazelleRequest represents a request to a Gazelle site

type GazelleResponse

type GazelleResponse struct {
	// Status is the response status ("success" or "failure")
	Status string `json:"status"`
	// Error is the error message (if status is "failure")
	Error string `json:"error,omitempty"`
	// Response is the response data
	Response json.RawMessage `json:"response"`
	// RawBody is the raw response body
	RawBody []byte `json:"-"`
	// StatusCode is the HTTP status code
	StatusCode int `json:"-"`
}

GazelleResponse represents a response from Gazelle API

type GazelleSearchResponse

type GazelleSearchResponse struct {
	CurrentPage int                   `json:"currentPage"`
	Pages       int                   `json:"pages"`
	Results     []GazelleTorrentGroup `json:"results"`
}

GazelleSearchResponse represents search results from Gazelle API

type GazelleTorrent

type GazelleTorrent struct {
	TorrentID int `json:"torrentId"`
	EditionID int `json:"editionId,omitempty"`
	Artists   []struct {
		ID   int    `json:"id"`
		Name string `json:"name"`
	} `json:"artists,omitempty"`
	Remastered     bool   `json:"remastered"`
	RemasterYear   int    `json:"remasterYear,omitempty"`
	RemasterTitle  string `json:"remasterTitle,omitempty"`
	Media          string `json:"media"`
	Encoding       string `json:"encoding"`
	Format         string `json:"format"`
	HasLog         bool   `json:"hasLog"`
	LogScore       int    `json:"logScore"`
	HasCue         bool   `json:"hasCue"`
	Scene          bool   `json:"scene"`
	VanityHouse    bool   `json:"vanityHouse"`
	FileCount      int    `json:"fileCount"`
	Time           string `json:"time"`
	Size           int64  `json:"size"`
	Snatches       int    `json:"snatches"`
	Seeders        int    `json:"seeders"`
	Leechers       int    `json:"leechers"`
	IsFreeleech    bool   `json:"isFreeleech"`
	IsNeutralLeech bool   `json:"isNeutralLeech"`
	IsPersonalFL   bool   `json:"isPersonalFreeleech"`
	CanUseToken    bool   `json:"canUseToken"`
}

GazelleTorrent represents a torrent in Gazelle

type GazelleTorrentGroup

type GazelleTorrentGroup struct {
	GroupID   int              `json:"groupId"`
	GroupName string           `json:"groupName"`
	Artist    string           `json:"artist,omitempty"`
	Tags      []string         `json:"tags"`
	Torrents  []GazelleTorrent `json:"torrents"`
}

GazelleTorrentGroup represents a torrent group in Gazelle

type GazelleUserResponse

type GazelleUserResponse struct {
	Username string `json:"username"`
	ID       int    `json:"id"`
	Stats    struct {
		Uploaded   int64   `json:"uploaded"`
		Downloaded int64   `json:"downloaded"`
		Ratio      float64 `json:"ratio"`
		Buffer     int64   `json:"buffer"`
	} `json:"stats"`
	Ranks struct {
		Class string `json:"class"`
	} `json:"ranks"`
	Personal struct {
		Bonus float64 `json:"bonus"`
	} `json:"personal"`
	Community struct {
		Seeding  int `json:"seeding"`
		Leeching int `json:"leeching"`
	} `json:"community"`
}

GazelleUserResponse represents user info from Gazelle API

type HDSkyParser

type HDSkyParser struct {
	Config NexusPHPParserConfig
}

HDSkyParser implements NexusPHPDetailParser for HDSky site

func NewHDSkyParser

func NewHDSkyParser(options ...NexusPHPParserOption) *HDSkyParser

NewHDSkyParser creates a new HDSkyParser

func (*HDSkyParser) ParseAll

func (p *HDSkyParser) ParseAll(doc *goquery.Selection) *TorrentDetailInfo

ParseAll parses all information from HDSky page

func (*HDSkyParser) ParseDiscount

func (p *HDSkyParser) ParseDiscount(doc *goquery.Selection) (DiscountLevel, time.Time)

ParseDiscount parses discount type and end time from HDSky page

func (*HDSkyParser) ParseHR

func (p *HDSkyParser) ParseHR(doc *goquery.Selection) bool

ParseHR parses HR status from HDSky page

func (*HDSkyParser) ParseSizeMB

func (p *HDSkyParser) ParseSizeMB(doc *goquery.Selection) float64

ParseSizeMB parses torrent size in MB from HDSky page

func (*HDSkyParser) ParseTitleAndID

func (p *HDSkyParser) ParseTitleAndID(doc *goquery.Selection) (title, torrentID string)

ParseTitleAndID parses title and torrent ID from HDSky page

type HTTPClientConfig

type HTTPClientConfig struct {
	// Timeout is the request timeout
	Timeout time.Duration
	// MaxIdleConns is the maximum number of idle connections
	MaxIdleConns int
	// MaxIdleConnsPerHost is the maximum idle connections per host
	MaxIdleConnsPerHost int
	// IdleConnTimeout is the idle connection timeout
	IdleConnTimeout time.Duration
	// DisableKeepAlives disables HTTP keep-alives
	DisableKeepAlives bool
}

HTTPClientConfig holds configuration for HTTP clients

func DefaultHTTPClientConfig

func DefaultHTTPClientConfig() HTTPClientConfig

DefaultHTTPClientConfig returns default HTTP client configuration

type HTTPClientPool

type HTTPClientPool struct {
	// contains filtered or unexported fields
}

HTTPClientPool manages HTTP sessions for different sites using requests library

func NewHTTPClientPool

func NewHTTPClientPool(config HTTPClientConfig, logger *zap.Logger) *HTTPClientPool

NewHTTPClientPool creates a new HTTP client pool

func (*HTTPClientPool) CloseAll

func (p *HTTPClientPool) CloseAll()

CloseAll closes all clients

func (*HTTPClientPool) CloseClient

func (p *HTTPClientPool) CloseClient(siteID string)

CloseClient closes and removes the client for a site

func (*HTTPClientPool) GetClient

func (p *HTTPClientPool) GetClient(siteID string) *http.Client

GetClient returns an HTTP client for the given site ID (backward compatibility)

func (*HTTPClientPool) GetSession

func (p *HTTPClientPool) GetSession(siteID string) requests.Session

GetSession returns a requests.Session for the given site ID

type HTTPResponse

type HTTPResponse struct {
	StatusCode int
	Body       []byte
	Headers    http.Header
}

HTTPResponse wraps the response from requests library

func (*HTTPResponse) IsError

func (r *HTTPResponse) IsError() bool

IsError returns true if status code is 4xx or 5xx

func (*HTTPResponse) IsSuccess

func (r *HTTPResponse) IsSuccess() bool

IsSuccess returns true if status code is 2xx

type Histogram

type Histogram struct {
	// contains filtered or unexported fields
}

Histogram tracks the distribution of values

func NewHistogram

func NewHistogram() *Histogram

NewHistogram creates a new histogram

func (*Histogram) Observe

func (h *Histogram) Observe(value float64)

Observe records a value

func (*Histogram) Stats

func (h *Histogram) Stats() HistogramStats

Stats returns histogram statistics

type HistogramStats

type HistogramStats struct {
	Count int64   `json:"count"`
	Sum   float64 `json:"sum"`
	Min   float64 `json:"min"`
	Max   float64 `json:"max"`
	Avg   float64 `json:"avg"`
}

HistogramStats contains histogram statistics

type InMemoryUserInfoRepo

type InMemoryUserInfoRepo struct {
	// contains filtered or unexported fields
}

InMemoryUserInfoRepo is an in-memory implementation of UserInfoRepo

func NewInMemoryUserInfoRepo

func NewInMemoryUserInfoRepo() *InMemoryUserInfoRepo

NewInMemoryUserInfoRepo creates a new in-memory user info repository

func (*InMemoryUserInfoRepo) Clear

func (r *InMemoryUserInfoRepo) Clear()

Clear removes all stored user info

func (*InMemoryUserInfoRepo) Count

func (r *InMemoryUserInfoRepo) Count() int

Count returns the number of stored user info entries

func (*InMemoryUserInfoRepo) Delete

func (r *InMemoryUserInfoRepo) Delete(ctx context.Context, site string) error

Delete removes user info for a site

func (*InMemoryUserInfoRepo) Get

Get retrieves user info for a specific site

func (*InMemoryUserInfoRepo) GetAggregated

func (r *InMemoryUserInfoRepo) GetAggregated(ctx context.Context) (AggregatedStats, error)

GetAggregated calculates aggregated statistics

func (*InMemoryUserInfoRepo) ListAll

func (r *InMemoryUserInfoRepo) ListAll(ctx context.Context) ([]UserInfo, error)

ListAll retrieves all stored user info

func (*InMemoryUserInfoRepo) ListBySites

func (r *InMemoryUserInfoRepo) ListBySites(ctx context.Context, sites []string) ([]UserInfo, error)

ListBySites retrieves user info for specific sites

func (*InMemoryUserInfoRepo) Save

func (r *InMemoryUserInfoRepo) Save(ctx context.Context, info UserInfo) error

Save stores user info for a site

type L2Cache

type L2Cache interface {
	Get(key string) ([]byte, error)
	Set(key string, value []byte, ttl time.Duration) error
	Delete(key string) error
}

L2Cache defines the interface for L2 cache (e.g., Redis)

type LRUCache

type LRUCache struct {
	// contains filtered or unexported fields
}

LRUCache is a thread-safe LRU cache with TTL support

func NewLRUCache

func NewLRUCache(capacity int, ttl time.Duration) *LRUCache

NewLRUCache creates a new LRU cache with the specified capacity and TTL

func (*LRUCache) Cleanup

func (c *LRUCache) Cleanup() int

Cleanup removes all expired entries

func (*LRUCache) Clear

func (c *LRUCache) Clear()

Clear removes all entries from the cache

func (*LRUCache) Delete

func (c *LRUCache) Delete(key string)

Delete removes a value from the cache

func (*LRUCache) Get

func (c *LRUCache) Get(key string) (any, bool)

Get retrieves a value from the cache

func (*LRUCache) Len

func (c *LRUCache) Len() int

Len returns the number of items in the cache

func (*LRUCache) Set

func (c *LRUCache) Set(key string, value any)

Set adds or updates a value in the cache

func (*LRUCache) SetWithTTL

func (c *LRUCache) SetWithTTL(key string, value any, ttl time.Duration)

SetWithTTL adds or updates a value with a custom TTL

type LevelGroupType

type LevelGroupType string

LevelGroupType represents the type of user level group

const (
	// LevelGroupUser represents regular user levels
	LevelGroupUser LevelGroupType = "user"
	// LevelGroupVIP represents VIP/donor levels
	LevelGroupVIP LevelGroupType = "vip"
	// LevelGroupManager represents staff/manager levels
	LevelGroupManager LevelGroupType = "manager"
)

type LevelManager

type LevelManager struct {
	// contains filtered or unexported fields
}

LevelManager manages user levels and calculates progress

func NewLevelManager

func NewLevelManager() *LevelManager

NewLevelManager creates a new level manager with default configurations

func (*LevelManager) CalculateNextLevel

func (lm *LevelManager) CalculateNextLevel(kind SiteKind, upload int64, ratio float64, days int) *LevelProgress

CalculateNextLevel calculates progress towards the next level

func (*LevelManager) GetCurrentLevel

func (lm *LevelManager) GetCurrentLevel(kind SiteKind, upload int64, ratio float64, days int) string

GetCurrentLevel determines the user's current level based on their stats

func (*LevelManager) GetLevels

func (lm *LevelManager) GetLevels(kind SiteKind) []LevelRequirement

GetLevels returns the level requirements for a site kind

func (*LevelManager) SetLevels

func (lm *LevelManager) SetLevels(kind SiteKind, levels []LevelRequirement)

SetLevels sets custom level requirements for a site kind

type LevelProgress

type LevelProgress struct {
	// CurrentLevel is the user's current level/rank
	CurrentLevel string `json:"currentLevel"`
	// NextLevel is the next level to achieve
	NextLevel string `json:"nextLevel"`
	// UploadNeeded is the additional upload needed (bytes)
	UploadNeeded int64 `json:"uploadNeeded,omitempty"`
	// DownloadNeeded is the additional download needed (bytes)
	DownloadNeeded int64 `json:"downloadNeeded,omitempty"`
	// RatioNeeded is the additional ratio needed
	RatioNeeded float64 `json:"ratioNeeded,omitempty"`
	// TimeNeeded is the additional time needed
	TimeNeeded time.Duration `json:"timeNeeded,omitempty"`
	// ProgressPercent is the overall progress percentage (0-100)
	ProgressPercent float64 `json:"progressPercent"`
}

LevelProgress represents progress towards the next user level

type LevelRequirement

type LevelRequirement struct {
	// Level is the level name (e.g., "Power User", "Elite", "Master")
	Level string
	// MinUpload is the minimum upload amount in bytes
	MinUpload int64
	// MinRatio is the minimum ratio required
	MinRatio float64
	// MinDays is the minimum account age in days
	MinDays int
	// MinSeedingTorrents is the minimum number of seeding torrents
	MinSeedingTorrents int
	// MinSeedingTime is the minimum total seeding time in hours
	MinSeedingTime int
	// Order is the level order (higher = better)
	Order int
}

LevelRequirement defines the requirements for a user level

type MTorrentBonusResponse

type MTorrentBonusResponse struct {
	Code    FlexibleCode `json:"code"`
	Message string       `json:"message"`
	Data    struct {
		FormulaParams struct {
			FinalBs json.Number `json:"finalBs"` // Bonus per hour (时魔) - can be string or number
		} `json:"formulaParams"`
	} `json:"data"`
}

MTorrentBonusResponse represents the response from /api/tracker/mybonus

type MTorrentDriver

type MTorrentDriver struct {
	BaseURL string // API URL (e.g., https://api.m-team.cc)
	WebURL  string // Web URL for detail pages (e.g., https://kp.m-team.cc)
	APIKey  string
	// contains filtered or unexported fields
}

MTorrentDriver implements the Driver interface for M-Team sites

func NewMTorrentDriver

func NewMTorrentDriver(config MTorrentDriverConfig) *MTorrentDriver

NewMTorrentDriver creates a new M-Team driver

func NewMTorrentDriverWithFailover

func NewMTorrentDriverWithFailover(apiKey string) *MTorrentDriver

NewMTorrentDriverWithFailover creates a new M-Team driver with failover enabled

func (*MTorrentDriver) Execute

Execute performs the HTTP request

func (*MTorrentDriver) GetBonusPerHour

func (d *MTorrentDriver) GetBonusPerHour(ctx context.Context) (float64, error)

GetBonusPerHour fetches the bonus per hour (时魔) for the user

func (*MTorrentDriver) GetPeerStatistics

func (d *MTorrentDriver) GetPeerStatistics(ctx context.Context) (*PeerStatistics, error)

GetPeerStatistics fetches the peer statistics for the user

func (*MTorrentDriver) GetSiteDefinition

func (d *MTorrentDriver) GetSiteDefinition() *SiteDefinition

GetSiteDefinition returns the site definition

func (*MTorrentDriver) GetUnreadMessageCount

func (d *MTorrentDriver) GetUnreadMessageCount(ctx context.Context) (int, int, error)

GetUnreadMessageCount fetches the unread message count for the user Returns (unread, total, error)

func (*MTorrentDriver) GetUserInfo

func (d *MTorrentDriver) GetUserInfo(ctx context.Context) (UserInfo, error)

GetUserInfo fetches complete user information including extended stats Uses parallel requests for independent API calls to improve performance

func (*MTorrentDriver) ParseBonusPerHour

func (d *MTorrentDriver) ParseBonusPerHour(res MTorrentResponse) (float64, error)

ParseBonusPerHour extracts bonus per hour from the response

func (*MTorrentDriver) ParseDownload

func (d *MTorrentDriver) ParseDownload(res MTorrentResponse) ([]byte, error)

ParseDownload extracts torrent file data from the response

func (*MTorrentDriver) ParsePeerStatistics

func (d *MTorrentDriver) ParsePeerStatistics(res MTorrentResponse) (*PeerStatistics, error)

ParsePeerStatistics extracts peer statistics from the response

func (*MTorrentDriver) ParseSearch

func (d *MTorrentDriver) ParseSearch(res MTorrentResponse) ([]TorrentItem, error)

ParseSearch extracts torrent items from the response

func (*MTorrentDriver) ParseUnreadMessageCount

func (d *MTorrentDriver) ParseUnreadMessageCount(res MTorrentResponse) (int, int, error)

ParseUnreadMessageCount extracts unread message count from the response

func (*MTorrentDriver) ParseUserInfo

func (d *MTorrentDriver) ParseUserInfo(res MTorrentResponse) (UserInfo, error)

ParseUserInfo extracts user info from the response

func (*MTorrentDriver) PrepareDownload

func (d *MTorrentDriver) PrepareDownload(torrentID string) (MTorrentRequest, error)

PrepareDownload prepares a request for downloading a torrent

func (*MTorrentDriver) PrepareGetBonusPerHour

func (d *MTorrentDriver) PrepareGetBonusPerHour() (MTorrentRequest, error)

PrepareGetBonusPerHour prepares a request for fetching bonus per hour

func (*MTorrentDriver) PrepareGetPeerStatistics

func (d *MTorrentDriver) PrepareGetPeerStatistics() (MTorrentRequest, error)

PrepareGetPeerStatistics prepares a request for fetching peer statistics

func (*MTorrentDriver) PrepareGetUnreadMessageCount

func (d *MTorrentDriver) PrepareGetUnreadMessageCount() (MTorrentRequest, error)

PrepareGetUnreadMessageCount prepares a request for fetching unread message count

func (*MTorrentDriver) PrepareSearch

func (d *MTorrentDriver) PrepareSearch(query SearchQuery) (MTorrentRequest, error)

PrepareSearch converts a SearchQuery to an M-Team request

func (*MTorrentDriver) PrepareUserInfo

func (d *MTorrentDriver) PrepareUserInfo() (MTorrentRequest, error)

PrepareUserInfo prepares a request for user info

func (*MTorrentDriver) SetSiteDefinition

func (d *MTorrentDriver) SetSiteDefinition(def *SiteDefinition)

SetSiteDefinition sets the site definition for custom parsing

type MTorrentDriverConfig

type MTorrentDriverConfig struct {
	BaseURL     string
	WebURL      string // Optional: Web URL for detail pages, defaults to "https://kp.m-team.cc"
	APIKey      string
	HTTPClient  *SiteHTTPClient // Use SiteHTTPClient instead of *http.Client
	UserAgent   string
	UseFailover bool // Enable multi-URL failover
}

MTorrentDriverConfig holds configuration for creating an M-Team driver

type MTorrentMemberCount

type MTorrentMemberCount struct {
	Uploaded   string `json:"uploaded"`
	Downloaded string `json:"downloaded"`
	Bonus      string `json:"bonus"`
	ShareRate  string `json:"shareRate"`
	Seedtime   string `json:"seedtime"`
	Leechtime  string `json:"leechtime"`
}

MTorrentMemberCount contains upload/download stats

type MTorrentMemberStatus

type MTorrentMemberStatus struct {
	LastLogin  string `json:"lastLogin"`
	LastBrowse string `json:"lastBrowse"`
	Vip        bool   `json:"vip"`
}

MTorrentMemberStatus contains user status info

type MTorrentMessageStatResponse

type MTorrentMessageStatResponse struct {
	Code    FlexibleCode `json:"code"`
	Message string       `json:"message"`
	Data    struct {
		Count  string `json:"count"`  // Total message count
		UnMake string `json:"unMake"` // Unread message count (string in API response)
	} `json:"data"`
}

MTorrentMessageStatResponse represents the response from /api/msg/notify/statistic

type MTorrentOptions

type MTorrentOptions struct {
	APIKey string `json:"apiKey"`
}

MTorrentOptions holds M-Team-specific configuration

type MTorrentPeerStatResponse

type MTorrentPeerStatResponse struct {
	Code    FlexibleCode `json:"code"`
	Message string       `json:"message"`
	Data    struct {
		UID          string `json:"uid"`
		SeederCount  string `json:"seederCount"`
		SeederSize   string `json:"seederSize"`
		LeecherCount string `json:"leecherCount"`
		LeecherSize  string `json:"leecherSize"`
		UploadCount  string `json:"uploadCount"`
	} `json:"data"`
}

MTorrentPeerStatResponse represents the response from /api/tracker/myPeerStatistics

type MTorrentRequest

type MTorrentRequest struct {
	// Endpoint is the API endpoint path
	Endpoint string
	// Method is the HTTP method
	Method string
	// Body is the request body (will be JSON encoded or form-urlencoded based on ContentType)
	Body any
	// ContentType specifies the request content type (default: "application/json")
	// Use "application/x-www-form-urlencoded" for form data
	ContentType string
}

MTorrentRequest represents a request to M-Team API

type MTorrentResponse

type MTorrentResponse struct {
	// Code is the response code ("0" for success)
	Code FlexibleCode `json:"code"`
	// Message is the response message
	Message string `json:"message"`
	// Data is the response data
	Data json.RawMessage `json:"data"`
	// RawBody is the raw response body (for downloads)
	RawBody []byte `json:"-"`
	// StatusCode is the HTTP status code
	StatusCode int `json:"-"`
}

MTorrentResponse represents a response from M-Team API

type MTorrentSearchData

type MTorrentSearchData struct {
	Data  []MTorrentTorrent `json:"data"`
	Total FlexInt           `json:"total"`
}

MTorrentSearchData is the search response data

type MTorrentSearchRequest

type MTorrentSearchRequest struct {
	Mode       string   `json:"mode"`
	Categories []string `json:"categories,omitempty"`
	Keyword    string   `json:"keyword,omitempty"`
	PageNumber int      `json:"pageNumber"`
	PageSize   int      `json:"pageSize"`
}

MTorrentSearchRequest is the search request body

type MTorrentTorrent

type MTorrentTorrent struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	SmallDescr  string `json:"smallDescr"`
	Size        string `json:"size"`
	CreatedDate string `json:"createdDate"`
	Status      struct {
		Seeders         FlexInt `json:"seeders"`
		Leechers        FlexInt `json:"leechers"`
		TimesCompleted  FlexInt `json:"timesCompleted"`
		Discount        string  `json:"discount"`
		DiscountEndTime string  `json:"discountEndTime,omitempty"`
	} `json:"status"`
	Category string `json:"category"`
}

MTorrentTorrent represents a torrent in M-Team API response

type MTorrentUserInfo

type MTorrentUserInfo struct {
	ID           string               `json:"id"`
	Username     string               `json:"username"`
	CreatedDate  string               `json:"createdDate"`
	Role         string               `json:"role"`
	MemberCount  MTorrentMemberCount  `json:"memberCount"`
	MemberStatus MTorrentMemberStatus `json:"memberStatus"`
}

MTorrentUserInfo represents user info from M-Team API

type MetricType

type MetricType string

MetricType represents the type of metric

const (
	MetricTypeCounter   MetricType = "counter"
	MetricTypeGauge     MetricType = "gauge"
	MetricTypeHistogram MetricType = "histogram"
)

type MetricsRegistry

type MetricsRegistry struct {
	// contains filtered or unexported fields
}

MetricsRegistry manages all metrics

func NewMetricsRegistry

func NewMetricsRegistry() *MetricsRegistry

NewMetricsRegistry creates a new metrics registry

func (*MetricsRegistry) Counter

func (r *MetricsRegistry) Counter(name string) *Counter

Counter returns or creates a counter with the given name

func (*MetricsRegistry) Gauge

func (r *MetricsRegistry) Gauge(name string) *Gauge

Gauge returns or creates a gauge with the given name

func (*MetricsRegistry) Histogram

func (r *MetricsRegistry) Histogram(name string) *Histogram

Histogram returns or creates a histogram with the given name

func (*MetricsRegistry) Snapshot

func (r *MetricsRegistry) Snapshot() MetricsSnapshot

Snapshot returns a snapshot of all metrics

func (*MetricsRegistry) Timer

func (r *MetricsRegistry) Timer(name string) *Timer

Timer creates a new timer for the given histogram

type MetricsSnapshot

type MetricsSnapshot struct {
	Counters   map[string]int64          `json:"counters"`
	Gauges     map[string]int64          `json:"gauges"`
	Histograms map[string]HistogramStats `json:"histograms"`
	Timestamp  time.Time                 `json:"timestamp"`
}

MetricsSnapshot contains a point-in-time snapshot of all metrics

type MigrationStatus

type MigrationStatus struct {
	SiteID            string `json:"siteId"`
	SiteName          string `json:"siteName"`
	OldImplementation bool   `json:"oldImplementation"`
	NewImplementation bool   `json:"newImplementation"`
	MigrationReady    bool   `json:"migrationReady"`
	Notes             string `json:"notes,omitempty"`
}

MigrationStatus represents the migration status of a site

type MultiSiteSearchQuery

type MultiSiteSearchQuery struct {
	SearchQuery
	// Sites specifies which sites to search (empty means all)
	Sites []string `json:"sites,omitempty"`
	// Timeout is the maximum time to wait for all searches
	Timeout time.Duration `json:"timeout,omitempty"`
	// MinSeeders filters results by minimum seeders
	MinSeeders int `json:"minSeeders,omitempty"`
	// MaxSizeBytes filters results by maximum size
	MaxSizeBytes int64 `json:"maxSizeBytes,omitempty"`
	// MinSizeBytes filters results by minimum size
	MinSizeBytes int64 `json:"minSizeBytes,omitempty"`
}

MultiSiteSearchQuery extends SearchQuery with multi-site options

type MultiSiteSearchResult

type MultiSiteSearchResult struct {
	// Items contains deduplicated and ranked torrent items
	Items []TorrentItem `json:"items"`
	// TotalResults is the total number of results before deduplication
	TotalResults int `json:"totalResults"`
	// SiteResults contains per-site result counts
	SiteResults map[string]int `json:"siteResults"`
	// Errors contains any errors that occurred during search
	Errors []SearchError `json:"errors,omitempty"`
	// Duration is how long the search took
	Duration time.Duration `json:"duration"`
}

MultiSiteSearchResult contains results from a multi-site search

type NewDownloaderConfig

type NewDownloaderConfig struct {
	Type        string `json:"type"`        // "qbittorrent", "transmission"
	Name        string `json:"name"`        // Downloader name
	URL         string `json:"url"`         // Downloader URL
	Username    string `json:"username"`    // Username for authentication
	Password    string `json:"password"`    // Password for authentication
	AddAtPaused bool   `json:"addAtPaused"` // New field: true = add paused
}

NewDownloaderConfig represents the new downloader configuration format with AddAtPaused field for controlling torrent start behavior

type NexusPHPDetailParser

type NexusPHPDetailParser interface {
	// ParseTitleAndID parses title and torrent ID from the page
	ParseTitleAndID(doc *goquery.Selection) (title, torrentID string)
	// ParseDiscount parses discount type and end time
	ParseDiscount(doc *goquery.Selection) (DiscountLevel, time.Time)
	// ParseHR parses HR (Hit and Run) status
	ParseHR(doc *goquery.Selection) bool
	// ParseSizeMB parses torrent size in MB
	ParseSizeMB(doc *goquery.Selection) float64
	// ParseAll parses all information from the page
	ParseAll(doc *goquery.Selection) *TorrentDetailInfo
}

NexusPHPDetailParser interface for parsing torrent detail pages

func GetParser

func GetParser(siteName SiteName) NexusPHPDetailParser

GetParser returns a parser for the given site name

type NexusPHPDriver

type NexusPHPDriver struct {
	BaseURL   string
	Cookie    string
	Selectors SiteSelectors
	// contains filtered or unexported fields
}

NexusPHPDriver implements the Driver interface for NexusPHP sites

func NewNexusPHPDriver

func NewNexusPHPDriver(config NexusPHPDriverConfig) *NexusPHPDriver

NewNexusPHPDriver creates a new NexusPHP driver

func NewNexusPHPDriverWithFailover

func NewNexusPHPDriverWithFailover(siteName SiteName, cookie string) *NexusPHPDriver

NewNexusPHPDriverWithFailover creates a new NexusPHP driver with failover enabled

func (*NexusPHPDriver) Execute

Execute performs the HTTP request

func (*NexusPHPDriver) ExtractFieldValuePublic

func (d *NexusPHPDriver) ExtractFieldValuePublic(doc *goquery.Document, selector FieldSelector) string

ExtractFieldValuePublic is a public wrapper for extractFieldValue for testing purposes

func (*NexusPHPDriver) FetchSeedingStatus

func (d *NexusPHPDriver) FetchSeedingStatus(ctx context.Context, userID string) (seeding int, seedingSize int64, err error)

FetchSeedingStatus fetches the seeding status (count and size) for a user This method requests /getusertorrentlistajax.php and parses the response

func (*NexusPHPDriver) GetSiteDefinition

func (d *NexusPHPDriver) GetSiteDefinition() *SiteDefinition

GetSiteDefinition returns the site definition

func (*NexusPHPDriver) GetUserInfo

func (d *NexusPHPDriver) GetUserInfo(ctx context.Context) (UserInfo, error)

GetUserInfo fetches complete user information For NexusPHP sites, this involves two steps: 1. Fetch /index.php to get user ID and basic info from info_block 2. Fetch /userdetails.php?id=xxx to get detailed info

func (*NexusPHPDriver) ParseDetail

func (d *NexusPHPDriver) ParseDetail(res NexusPHPResponse) (TorrentDetail, error)

ParseDetail extracts download URL and other info from detail page

func (*NexusPHPDriver) ParseDownload

func (d *NexusPHPDriver) ParseDownload(res NexusPHPResponse) ([]byte, error)

ParseDownload extracts torrent file data from the response For NexusPHP, the response is a detail page - we need to extract the download URL and fetch the torrent

func (*NexusPHPDriver) ParseSearch

func (d *NexusPHPDriver) ParseSearch(res NexusPHPResponse) ([]TorrentItem, error)

ParseSearch extracts torrent items from the response

func (*NexusPHPDriver) ParseSeedingStatus

func (d *NexusPHPDriver) ParseSeedingStatus(res NexusPHPResponse) (seeding int, seedingSize int64, err error)

ParseSeedingStatus parses the seeding status from the AJAX response Implements two parsing strategies based on NexusPHP.ts: 1. Direct parsing: Look for summary text like "10 | 100 GB" or "<b>94</b>条记录,共计<b>2.756 TB</b>" 2. Table accumulation: Sum up sizes from individual torrent rows

func (*NexusPHPDriver) ParseUserDetails

func (d *NexusPHPDriver) ParseUserDetails(res NexusPHPResponse) (UserInfo, error)

ParseUserDetails extracts detailed user info from userdetails.php page

func (*NexusPHPDriver) ParseUserInfo

func (d *NexusPHPDriver) ParseUserInfo(res NexusPHPResponse) (UserInfo, error)

ParseUserInfo extracts user info from the response

func (*NexusPHPDriver) PrepareDetail

func (d *NexusPHPDriver) PrepareDetail(torrentID string) (NexusPHPRequest, error)

PrepareDetail prepares a request for torrent detail page

func (*NexusPHPDriver) PrepareDownload

func (d *NexusPHPDriver) PrepareDownload(torrentID string) (NexusPHPRequest, error)

PrepareDownload prepares a request for downloading a torrent For NexusPHP sites, we first need to visit the detail page to get the download URL with passkey

func (*NexusPHPDriver) PrepareSearch

func (d *NexusPHPDriver) PrepareSearch(query SearchQuery) (NexusPHPRequest, error)

PrepareSearch converts a SearchQuery to a NexusPHP request

func (*NexusPHPDriver) PrepareUserDetails

func (d *NexusPHPDriver) PrepareUserDetails(userID string) (NexusPHPRequest, error)

PrepareUserDetails prepares a request for user details page

func (*NexusPHPDriver) PrepareUserInfo

func (d *NexusPHPDriver) PrepareUserInfo() (NexusPHPRequest, error)

PrepareUserInfo prepares a request for user info (index page to get user ID)

func (*NexusPHPDriver) PrepareUserSeedingPage

func (d *NexusPHPDriver) PrepareUserSeedingPage(userID, listType string) (NexusPHPRequest, error)

PrepareUserSeedingPage prepares a request for user seeding page via AJAX This is used to fetch seeding size information from /getusertorrentlistajax.php

func (*NexusPHPDriver) SetSiteDefinition

func (d *NexusPHPDriver) SetSiteDefinition(def *SiteDefinition)

SetSiteDefinition sets the site definition for custom parsing

type NexusPHPDriverConfig

type NexusPHPDriverConfig struct {
	BaseURL     string
	Cookie      string
	Selectors   *SiteSelectors
	HTTPClient  *SiteHTTPClient // Use SiteHTTPClient instead of *http.Client
	UserAgent   string
	UseFailover bool     // Enable multi-URL failover
	SiteName    SiteName // Site name for failover URL lookup
}

NexusPHPDriverConfig holds configuration for creating a NexusPHP driver

type NexusPHPOptions

type NexusPHPOptions struct {
	Cookie    string         `json:"cookie"`
	Selectors *SiteSelectors `json:"selectors,omitempty"`
}

NexusPHPOptions holds NexusPHP-specific configuration

type NexusPHPParserConfig

type NexusPHPParserConfig struct {
	TimeLayout string
}

NexusPHPParserConfig defines parser configuration

func DefaultNexusPHPParserConfig

func DefaultNexusPHPParserConfig() NexusPHPParserConfig

DefaultNexusPHPParserConfig returns default parser configuration

type NexusPHPParserOption

type NexusPHPParserOption func(*NexusPHPParserConfig)

NexusPHPParserOption is a function that modifies NexusPHPParserConfig

func WithParserTimeLayout

func WithParserTimeLayout(layout string) NexusPHPParserOption

WithParserTimeLayout sets the time layout for parsing

type NexusPHPRequest

type NexusPHPRequest struct {
	// Path is the URL path (e.g., "/torrents.php")
	Path string
	// Params are the query parameters
	Params url.Values
	// Method is the HTTP method (default: GET)
	Method string
}

NexusPHPRequest represents a request to a NexusPHP site

type NexusPHPResponse

type NexusPHPResponse struct {
	// Document is the parsed HTML document
	Document *goquery.Document
	// RawBody is the raw response body (for downloads)
	RawBody []byte
	// StatusCode is the HTTP status code
	StatusCode int
}

NexusPHPResponse wraps a goquery document for parsing

type Normalizer

type Normalizer struct {
	// contains filtered or unexported fields
}

Normalizer standardizes torrent titles and tags

func NewNormalizer

func NewNormalizer() *Normalizer

NewNormalizer creates a new Normalizer

func (*Normalizer) ExtractEncoding

func (n *Normalizer) ExtractEncoding(title string) string

ExtractEncoding extracts the encoding from a title

func (*Normalizer) ExtractFormat

func (n *Normalizer) ExtractFormat(title string) string

ExtractFormat extracts the format from a title

func (*Normalizer) ExtractResolution

func (n *Normalizer) ExtractResolution(title string) string

ExtractResolution extracts the resolution from a title

func (*Normalizer) NormalizeTags

func (n *Normalizer) NormalizeTags(tags []string) []string

NormalizeTags standardizes a list of tags

func (*Normalizer) NormalizeTitle

func (n *Normalizer) NormalizeTitle(title string) string

NormalizeTitle standardizes a torrent title

type OldDownloaderConfig

type OldDownloaderConfig struct {
	Type      string `json:"type"`      // "qbittorrent", "transmission"
	Name      string `json:"name"`      // Downloader name
	URL       string `json:"url"`       // Downloader URL
	Username  string `json:"username"`  // Username for authentication
	Password  string `json:"password"`  // Password for authentication
	AutoStart bool   `json:"autoStart"` // Old field: true = start immediately
}

OldDownloaderConfig represents the old downloader configuration format with autoStart field that needs to be converted to AddAtPaused

type OldSiteConfig

type OldSiteConfig struct {
	Name       string `json:"name"`
	Type       string `json:"type"` // "nexusphp", "mteam", etc.
	URL        string `json:"url"`
	Cookie     string `json:"cookie,omitempty"`
	APIKey     string `json:"apiKey,omitempty"`
	RateLimit  int    `json:"rateLimit,omitempty"`
	Selectors  any    `json:"selectors,omitempty"`
	AuthMethod string `json:"authMethod,omitempty"` // Old field name
}

OldSiteConfig represents the old site configuration format

type ParsedTorrent

type ParsedTorrent struct {
	// Name is the torrent name
	Name string `json:"name"`
	// InfoHash is the 40-character hex info hash
	InfoHash string `json:"infoHash"`
	// Size is the total size in bytes
	Size int64 `json:"size"`
	// Files is the list of files in the torrent
	Files []TorrentFile `json:"files,omitempty"`
	// PieceLength is the piece size in bytes
	PieceLength int64 `json:"pieceLength"`
	// Comment is the torrent comment
	Comment string `json:"comment,omitempty"`
	// CreatedBy is the creator of the torrent
	CreatedBy string `json:"createdBy,omitempty"`
	// CreationDate is when the torrent was created
	CreationDate time.Time `json:"creationDate,omitempty"`
	// Announce is the primary tracker URL
	Announce string `json:"announce,omitempty"`
	// AnnounceList is the list of tracker URLs
	AnnounceList [][]string `json:"announceList,omitempty"`
	// RawMetadata is the raw torrent file bytes
	RawMetadata []byte `json:"-"`
}

ParsedTorrent represents parsed torrent metadata

func GetRemoteTorrent

func GetRemoteTorrent(torrentURL, cookie string) (*ParsedTorrent, error)

GetRemoteTorrent fetches a torrent file from a URL

func GetRemoteTorrentWithClient

func GetRemoteTorrentWithClient(torrentURL, cookie string, client *http.Client) (*ParsedTorrent, error)

GetRemoteTorrentWithClient fetches a torrent file from a URL with a custom HTTP client Deprecated: Use GetRemoteTorrentWithRequests instead

func GetRemoteTorrentWithRequests

func GetRemoteTorrentWithRequests(torrentURL, cookie string) (*ParsedTorrent, error)

GetRemoteTorrentWithRequests fetches a torrent file from a URL using requests library

func ParseTorrent

func ParseTorrent(data []byte) (*ParsedTorrent, error)

ParseTorrent parses torrent file bytes into ParsedTorrent

func ParseTorrentFromFile

func ParseTorrentFromFile(path string) (*ParsedTorrent, error)

ParseTorrentFromFile parses a torrent file from path

type PeerStatistics

type PeerStatistics struct {
	// SeederCount is the number of torrents being seeded
	SeederCount int `json:"seederCount"`
	// SeederSize is the total size of seeding torrents (bytes)
	SeederSize int64 `json:"seederSize"`
	// LeecherCount is the number of torrents being downloaded
	LeecherCount int `json:"leecherCount"`
	// LeecherSize is the total size of leeching torrents (bytes)
	LeecherSize int64 `json:"leecherSize"`
}

PeerStatistics represents user's seeding/leeching statistics

type Ranker

type Ranker struct {
	// contains filtered or unexported fields
}

Ranker scores and ranks torrent search results

func NewRanker

func NewRanker(config RankerConfig) *Ranker

NewRanker creates a new Ranker with the given configuration

func (*Ranker) GetSiteReliability

func (r *Ranker) GetSiteReliability(siteID string) float64

GetSiteReliability returns the reliability score for a site

func (*Ranker) Rank

func (r *Ranker) Rank(items []TorrentItem) []TorrentItem

Rank sorts torrents by score in descending order

func (*Ranker) Score

func (r *Ranker) Score(item TorrentItem) float64

Score calculates a score for a torrent item

func (*Ranker) SetSiteReliability

func (r *Ranker) SetSiteReliability(siteID string, reliability float64)

SetSiteReliability sets the reliability score for a site

type RankerConfig

type RankerConfig struct {
	// SeederWeight is the weight for seeders in scoring (default: 1.0)
	SeederWeight float64 `json:"seederWeight,omitempty"`
	// LeecherWeight is the weight for leechers in scoring (default: 0.5)
	LeecherWeight float64 `json:"leecherWeight,omitempty"`
	// FreeBonus is the bonus score for free torrents (default: 100)
	FreeBonus float64 `json:"freeBonus,omitempty"`
	// SizeWeight is the weight for size in scoring (default: 0.1)
	SizeWeight float64 `json:"sizeWeight,omitempty"`
	// RecencyWeight is the weight for recency in scoring (default: 0.2)
	RecencyWeight float64 `json:"recencyWeight,omitempty"`
	// SiteReliability maps site IDs to reliability scores (0-1)
	SiteReliability map[string]float64 `json:"siteReliability,omitempty"`
}

RankerConfig holds configuration for the Ranker

type RequestConfig

type RequestConfig struct {
	// URL is the request path (e.g., "/index.php")
	URL string `json:"url"`
	// Method is the HTTP method (default: GET)
	Method string `json:"method,omitempty"`
	// Params are query parameters
	Params map[string]string `json:"params,omitempty"`
	// Data is the request body for POST requests
	Data map[string]any `json:"data,omitempty"`
	// ResponseType is "document" for HTML or "json" for JSON
	ResponseType string `json:"responseType,omitempty"`
	// Headers are additional HTTP headers
	Headers map[string]string `json:"headers,omitempty"`
}

RequestConfig for HTTP requests

type RequestsClient

type RequestsClient struct {
	// contains filtered or unexported fields
}

RequestsClient provides a high-level HTTP client using requests library

func NewRequestsClient

func NewRequestsClient(config HTTPClientConfig, retryConfig RetryConfig, logger *zap.Logger) *RequestsClient

NewRequestsClient creates a new requests-based HTTP client

func (*RequestsClient) Close

func (c *RequestsClient) Close() error

Close closes the underlying session

func (*RequestsClient) Get

Get performs a GET request with retry logic

func (*RequestsClient) Post

func (c *RequestsClient) Post(ctx context.Context, url string, body any, opts ...requests.RequestOption) (*RequestsResponse, error)

Post performs a POST request with retry logic

type RequestsResponse

type RequestsResponse struct {
	// contains filtered or unexported fields
}

RequestsResponse wraps the response from requests library

func (*RequestsResponse) Bytes

func (r *RequestsResponse) Bytes() []byte

Bytes returns the response body as bytes

func (*RequestsResponse) Headers

func (r *RequestsResponse) Headers() http.Header

Headers returns the response headers

func (*RequestsResponse) IsError

func (r *RequestsResponse) IsError() bool

IsError returns true if status code is 4xx or 5xx

func (*RequestsResponse) IsSuccess

func (r *RequestsResponse) IsSuccess() bool

IsSuccess returns true if status code is 2xx

func (*RequestsResponse) StatusCode

func (r *RequestsResponse) StatusCode() int

StatusCode returns the HTTP status code

func (*RequestsResponse) Text

func (r *RequestsResponse) Text() string

Text returns the response body as string

type RetryConfig

type RetryConfig struct {
	// MaxRetries is the maximum number of retries
	MaxRetries int
	// InitialBackoff is the initial backoff duration
	InitialBackoff time.Duration
	// MaxBackoff is the maximum backoff duration
	MaxBackoff time.Duration
	// BackoffMultiplier is the backoff multiplier
	BackoffMultiplier float64
	// Jitter adds randomness to backoff
	Jitter bool
	// RetryableStatusCodes are HTTP status codes that should trigger a retry
	RetryableStatusCodes []int
}

RetryConfig holds configuration for retry logic

func DefaultRetryConfig

func DefaultRetryConfig() RetryConfig

DefaultRetryConfig returns default retry configuration

type RetryableHTTPClient

type RetryableHTTPClient struct {
	// contains filtered or unexported fields
}

RetryableHTTPClient wraps an HTTP client with retry logic

func NewRetryableHTTPClient

func NewRetryableHTTPClient(client *http.Client, config RetryConfig, logger *zap.Logger) *RetryableHTTPClient

NewRetryableHTTPClient creates a new retryable HTTP client

func (*RetryableHTTPClient) Do

Do executes an HTTP request with retry logic

type SearchCache

type SearchCache struct {
	// contains filtered or unexported fields
}

SearchCache provides caching for search results

func NewSearchCache

func NewSearchCache(config SearchCacheConfig) *SearchCache

NewSearchCache creates a new SearchCache

func (*SearchCache) Cleanup

func (c *SearchCache) Cleanup() int

Cleanup removes expired entries from the cache

func (*SearchCache) Clear

func (c *SearchCache) Clear()

Clear removes all entries from the cache

func (*SearchCache) Delete

func (c *SearchCache) Delete(query MultiSiteSearchQuery)

Delete removes a specific entry from the cache

func (*SearchCache) Get

Get retrieves a cached search result

func (*SearchCache) Set

Set stores a search result in the cache

func (*SearchCache) Size

func (c *SearchCache) Size() int

Size returns the number of entries in the cache

type SearchCacheConfig

type SearchCacheConfig struct {
	// TTL is the time-to-live for cache entries (default: 5 minutes)
	TTL time.Duration `json:"ttl,omitempty"`
	// MaxSize is the maximum number of entries (default: 1000)
	MaxSize int `json:"maxSize,omitempty"`
}

SearchCacheConfig holds configuration for SearchCache

type SearchError

type SearchError struct {
	Site  string `json:"site"`
	Error string `json:"error"`
}

SearchError represents an error from a specific site

type SearchOrchestrator

type SearchOrchestrator struct {
	// contains filtered or unexported fields
}

SearchOrchestrator coordinates concurrent searches across multiple sites

func NewSearchOrchestrator

func NewSearchOrchestrator(config SearchOrchestratorConfig) *SearchOrchestrator

NewSearchOrchestrator creates a new SearchOrchestrator

func (*SearchOrchestrator) GetSite

func (o *SearchOrchestrator) GetSite(siteID string) Site

GetSite returns a registered site by ID, or nil if not found

func (*SearchOrchestrator) ListSites

func (o *SearchOrchestrator) ListSites() []string

ListSites returns all registered site IDs

func (*SearchOrchestrator) RegisterSite

func (o *SearchOrchestrator) RegisterSite(site Site)

RegisterSite adds a site to the orchestrator

func (*SearchOrchestrator) Search

Search performs a concurrent search across multiple sites

func (*SearchOrchestrator) UnregisterSite

func (o *SearchOrchestrator) UnregisterSite(siteID string)

UnregisterSite removes a site from the orchestrator

type SearchOrchestratorConfig

type SearchOrchestratorConfig struct {
	Logger *zap.Logger
}

SearchOrchestratorConfig holds configuration for SearchOrchestrator

type SearchQuery

type SearchQuery struct {
	// Keyword is the search term
	Keyword string `json:"keyword"`
	// Category filters by torrent category
	Category string `json:"category,omitempty"`
	// FreeOnly filters to only show free torrents
	FreeOnly bool `json:"freeOnly,omitempty"`
	// Page is the page number (1-indexed)
	Page int `json:"page,omitempty"`
	// PageSize is the number of results per page
	PageSize int `json:"pageSize,omitempty"`
	// SortBy specifies the sort field
	SortBy string `json:"sortBy,omitempty"`
	// OrderDesc specifies descending order when true
	OrderDesc bool `json:"orderDesc,omitempty"`
}

SearchQuery represents a search request to a PT site

func (*SearchQuery) Validate

func (q *SearchQuery) Validate() error

Validate validates the search query

type Session

type Session struct {
	SiteID     string
	Cookie     string
	APIKey     string
	SessionID  string // For Transmission
	ExpiresAt  time.Time
	LastUsed   time.Time
	LoginCount int
	// contains filtered or unexported fields
}

Session represents an authentication session

type SessionManager

type SessionManager struct {
	// contains filtered or unexported fields
}

SessionManager manages authentication sessions for sites

func NewSessionManager

func NewSessionManager(logger *zap.Logger) *SessionManager

NewSessionManager creates a new session manager

func (*SessionManager) GetSession

func (m *SessionManager) GetSession(siteID string) (*Session, bool)

GetSession returns the session for a site

func (*SessionManager) IncrementLoginCount

func (m *SessionManager) IncrementLoginCount(siteID string) int

IncrementLoginCount increments the login count for a session

func (*SessionManager) InvalidateSession

func (m *SessionManager) InvalidateSession(siteID string)

InvalidateSession marks a session as expired

func (*SessionManager) IsSessionValid

func (m *SessionManager) IsSessionValid(siteID string) bool

IsSessionValid checks if a session is still valid

func (*SessionManager) RemoveSession

func (m *SessionManager) RemoveSession(siteID string)

RemoveSession removes a session

func (*SessionManager) ResetLoginCount

func (m *SessionManager) ResetLoginCount(siteID string)

ResetLoginCount resets the login count for a session

func (*SessionManager) SetSession

func (m *SessionManager) SetSession(siteID string, session *Session)

SetSession sets the session for a site

func (*SessionManager) UpdateSessionID

func (m *SessionManager) UpdateSessionID(siteID, sessionID string)

UpdateSessionID updates the session ID (for Transmission)

type Site

type Site interface {
	// ID returns the unique site identifier
	ID() string
	// Name returns the human-readable site name
	Name() string
	// Kind returns the site architecture type
	Kind() SiteKind
	// Login authenticates with the site
	Login(ctx context.Context, creds Credentials) error
	// Search searches for torrents
	Search(ctx context.Context, query SearchQuery) ([]TorrentItem, error)
	// GetUserInfo fetches the current user's information
	GetUserInfo(ctx context.Context) (UserInfo, error)
	// Download downloads a torrent file by ID
	Download(ctx context.Context, torrentID string) ([]byte, error)
	// Close releases any resources held by the site
	Close() error
}

Site is the core interface for interacting with a PT site

type SiteAdapter

type SiteAdapter struct {
	// OldSite is the old site implementation (if any)
	OldSite any
	// NewSite is the new v2 site implementation
	NewSite Site
	// UseNew indicates whether to use the new implementation
	UseNew bool
}

SiteAdapter provides an adapter layer to use old site implementations with new interface This allows gradual migration of site implementations

func NewSiteAdapter

func NewSiteAdapter(oldSite any, newSite Site, useNew bool) *SiteAdapter

NewSiteAdapter creates a new SiteAdapter If newSite is provided and useNew is true, it will be used Otherwise, oldSite will be used (for backward compatibility)

func (*SiteAdapter) GetSite

func (a *SiteAdapter) GetSite() any

GetSite returns the appropriate site implementation based on configuration

func (*SiteAdapter) IsUsingNewImplementation

func (a *SiteAdapter) IsUsingNewImplementation() bool

IsUsingNewImplementation returns true if using the new v2 implementation

type SiteCategoryConfig

type SiteCategoryConfig struct {
	SiteID     string               `json:"siteId"`
	SiteName   string               `json:"siteName"`
	Categories []CategoryDefinition `json:"categories"`
}

SiteCategoryConfig 站点分类配置

func GetSiteCategoryConfig

func GetSiteCategoryConfig(siteID string) *SiteCategoryConfig

GetSiteCategoryConfig 获取站点分类配置

type SiteConfig

type SiteConfig struct {
	// Type is the site type (nexusphp, unit3d, gazelle, mtorrent)
	Type string `json:"type"`
	// ID is the unique site identifier
	ID string `json:"id"`
	// Name is the human-readable site name
	Name string `json:"name"`
	// BaseURL is the site's base URL
	BaseURL string `json:"baseUrl"`
	// Options contains type-specific configuration
	Options json.RawMessage `json:"options"`
	// RateLimit is the requests per second limit (optional)
	RateLimit float64 `json:"rateLimit,omitempty"`
	// RateBurst is the maximum burst size (optional)
	RateBurst int `json:"rateBurst,omitempty"`
}

SiteConfig holds configuration for creating a site

type SiteCredentials

type SiteCredentials struct {
	Cookie string
	APIKey string
}

SiteCredentials holds authentication credentials for a site

type SiteDefinition

type SiteDefinition struct {
	// ID is the unique site identifier (e.g., "hdsky")
	ID string `json:"id"`
	// Name is the human-readable site name (e.g., "HDSky")
	Name string `json:"name"`
	// Aka contains alternative names for the site
	Aka []string `json:"aka,omitempty"`
	// Description is a brief description of the site
	Description string `json:"description,omitempty"`
	// Schema is the base schema type (e.g., "NexusPHP", "mTorrent")
	Schema string `json:"schema"`

	// URLs are the primary site URLs
	URLs []string `json:"urls"`
	// LegacyURLs are alternative/legacy URLs
	LegacyURLs []string `json:"legacyUrls,omitempty"`
	// FaviconURL is the site's favicon URL for caching
	FaviconURL string `json:"faviconUrl,omitempty"`

	// TimezoneOffset is the site's timezone (e.g., "+0800")
	TimezoneOffset string `json:"timezoneOffset,omitempty"`

	// UserInfo contains user info fetching configuration
	UserInfo *UserInfoConfig `json:"userInfo,omitempty"`

	// LevelRequirements defines user level requirements
	LevelRequirements []SiteLevelRequirement `json:"levelRequirements,omitempty"`

	// Selectors contains custom CSS selectors (merged with schema defaults)
	Selectors *SiteSelectors `json:"selectors,omitempty"`
}

SiteDefinition contains site-specific metadata and configuration

type SiteDefinitionRegistry

type SiteDefinitionRegistry struct {
	// contains filtered or unexported fields
}

SiteDefinitionRegistry manages site definitions

func GetDefinitionRegistry

func GetDefinitionRegistry() *SiteDefinitionRegistry

GetDefinitionRegistry returns the global site definition registry

func (*SiteDefinitionRegistry) Get

func (r *SiteDefinitionRegistry) Get(siteID string) (*SiteDefinition, bool)

Get retrieves a site definition by ID

func (*SiteDefinitionRegistry) GetAll

func (r *SiteDefinitionRegistry) GetAll() []*SiteDefinition

GetAll returns all registered site definitions

func (*SiteDefinitionRegistry) GetOrDefault

func (r *SiteDefinitionRegistry) GetOrDefault(siteID string) *SiteDefinition

GetOrDefault returns site definition or nil if not found

func (*SiteDefinitionRegistry) List

func (r *SiteDefinitionRegistry) List() []string

List returns all registered site IDs

func (*SiteDefinitionRegistry) Register

func (r *SiteDefinitionRegistry) Register(def *SiteDefinition)

Register adds a site definition to the registry

type SiteFactory

type SiteFactory struct {
	// contains filtered or unexported fields
}

SiteFactory creates Site instances from configuration

func NewSiteFactory

func NewSiteFactory(logger *zap.Logger) *SiteFactory

NewSiteFactory creates a new SiteFactory

func (*SiteFactory) CreateSite

func (f *SiteFactory) CreateSite(config SiteConfig) (Site, error)

CreateSite creates a Site from configuration

func (*SiteFactory) CreateSiteFromJSON

func (f *SiteFactory) CreateSiteFromJSON(jsonData []byte) (Site, error)

CreateSiteFromJSON creates a Site from JSON configuration

func (*SiteFactory) CreateSitesFromJSON

func (f *SiteFactory) CreateSitesFromJSON(jsonData []byte) ([]Site, error)

CreateSitesFromJSON creates multiple Sites from JSON array configuration

type SiteHTTPClient

type SiteHTTPClient struct {
	// contains filtered or unexported fields
}

SiteHTTPClient provides a unified HTTP client interface for site drivers using the requests library instead of net/http directly

func NewSiteHTTPClient

func NewSiteHTTPClient(config SiteHTTPClientConfig) *SiteHTTPClient

NewSiteHTTPClient creates a new SiteHTTPClient

func (*SiteHTTPClient) Close

func (c *SiteHTTPClient) Close() error

Close closes the underlying session

func (*SiteHTTPClient) DoRequest

func (c *SiteHTTPClient) DoRequest(ctx context.Context, method, url string, body io.Reader, headers map[string]string) (*HTTPResponse, error)

DoRequest performs an HTTP request using the requests library

func (*SiteHTTPClient) Get

func (c *SiteHTTPClient) Get(ctx context.Context, url string, headers map[string]string) (*HTTPResponse, error)

Get performs a GET request

func (*SiteHTTPClient) Post

func (c *SiteHTTPClient) Post(ctx context.Context, url string, body []byte, headers map[string]string) (*HTTPResponse, error)

Post performs a POST request with body

func (*SiteHTTPClient) PostJSON

func (c *SiteHTTPClient) PostJSON(ctx context.Context, url string, body []byte, headers map[string]string) (*HTTPResponse, error)

PostJSON performs a POST request with JSON body

type SiteHTTPClientConfig

type SiteHTTPClientConfig struct {
	Timeout           time.Duration
	MaxIdleConns      int
	IdleConnTimeout   time.Duration
	DisableKeepAlives bool
	UserAgent         string
	Logger            *zap.Logger
}

SiteHTTPClientConfig holds configuration for SiteHTTPClient

func DefaultSiteHTTPClientConfig

func DefaultSiteHTTPClientConfig() SiteHTTPClientConfig

DefaultSiteHTTPClientConfig returns default configuration

type SiteKind

type SiteKind string

SiteKind represents the type of PT site architecture

const (
	// SiteNexusPHP represents NexusPHP-based sites (e.g., HDSky, CHDBits)
	SiteNexusPHP SiteKind = "nexusphp"
	// SiteUnit3D represents Unit3D-based sites
	SiteUnit3D SiteKind = "unit3d"
	// SiteGazelle represents Gazelle-based sites (e.g., What.CD clones)
	SiteGazelle SiteKind = "gazelle"
	// SiteMTorrent represents M-Team's custom API
	SiteMTorrent SiteKind = "mtorrent"
)

func GetSiteKind

func GetSiteKind(siteName SiteName) SiteKind

GetSiteKind returns the architecture kind for a site

type SiteLevelProgressInfo

type SiteLevelProgressInfo struct {
	// CurrentLevel is the current level requirement
	CurrentLevel *SiteLevelRequirement `json:"currentLevel,omitempty"`
	// NextLevel is the next level requirement
	NextLevel *SiteLevelRequirement `json:"nextLevel,omitempty"`
	// UnmetRequirements contains what's still needed
	UnmetRequirements map[string]any `json:"unmetRequirements,omitempty"`
	// ProgressPercent is the overall progress percentage (0-100)
	ProgressPercent float64 `json:"progressPercent"`
}

SiteLevelProgressInfo represents progress towards the next user level

func CalculateSiteLevelProgress

func CalculateSiteLevelProgress(info *UserInfo, requirements []SiteLevelRequirement) *SiteLevelProgressInfo

CalculateSiteLevelProgress calculates progress to next level

type SiteLevelRequirement

type SiteLevelRequirement struct {
	// ID is the numeric level identifier
	ID int `json:"id"`
	// Name is the level name (e.g., "Power User")
	Name string `json:"name"`
	// NameAka contains alternative names for matching
	NameAka []string `json:"nameAka,omitempty"`
	// GroupType: "user", "vip", "manager"
	GroupType LevelGroupType `json:"groupType,omitempty"`

	// Requirements (all optional)
	// Interval is ISO 8601 duration (e.g., "P5W" for 5 weeks)
	Interval string `json:"interval,omitempty"`
	// Downloaded is size string (e.g., "200GB")
	Downloaded string `json:"downloaded,omitempty"`
	// Uploaded is size string
	Uploaded string `json:"uploaded,omitempty"`
	// Ratio is minimum ratio
	Ratio float64 `json:"ratio,omitempty"`
	// Bonus is minimum bonus points
	Bonus float64 `json:"bonus,omitempty"`

	// Extended requirements
	// SeedingBonus is seeding bonus points requirement
	SeedingBonus float64 `json:"seedingBonus,omitempty"`
	// Uploads is number of uploads requirement
	Uploads int `json:"uploads,omitempty"`
	// Seeding is number of seeding torrents requirement
	Seeding int `json:"seeding,omitempty"`
	// SeedingSize is total seeding size requirement
	SeedingSize string `json:"seedingSize,omitempty"`

	// Alternative contains OR-based alternative requirements
	Alternative []AlternativeRequirement `json:"alternative,omitempty"`

	// Privilege describes the privileges granted at this level
	Privilege string `json:"privilege,omitempty"`
}

SiteLevelRequirement defines requirements for a user level (site-specific) This is different from LevelRequirement in level_manager.go which is for generic level management

type SiteMeta

type SiteMeta struct {
	// ID is the unique site identifier (e.g., "mteam", "hdsky")
	ID string
	// Name is the human-readable site name
	Name string
	// Kind is the site architecture type
	Kind SiteKind
	// DefaultBaseURL is the default base URL for the site
	DefaultBaseURL string
	// AuthMethod is the authentication method (cookie, api_key)
	AuthMethod string
	// RateLimit is the default rate limit (requests per second)
	RateLimit float64
	// RateBurst is the default rate burst
	RateBurst int
}

SiteMeta contains metadata for a site type

type SiteMetrics

type SiteMetrics struct {
	// contains filtered or unexported fields
}

SiteMetrics provides site-specific metrics

func NewSiteMetrics

func NewSiteMetrics(registry *MetricsRegistry) *SiteMetrics

NewSiteMetrics creates a new site metrics instance

func (*SiteMetrics) RecordCacheHit

func (m *SiteMetrics) RecordCacheHit(cacheType string)

RecordCacheHit records a cache hit

func (*SiteMetrics) RecordCacheMiss

func (m *SiteMetrics) RecordCacheMiss(cacheType string)

RecordCacheMiss records a cache miss

func (*SiteMetrics) RecordDownloaderRequest

func (m *SiteMetrics) RecordDownloaderRequest(downloader string, success bool, duration time.Duration)

RecordDownloaderRequest records a downloader request

func (*SiteMetrics) RecordError

func (m *SiteMetrics) RecordError(errorType string)

RecordError records an error by type

func (*SiteMetrics) RecordRequest

func (m *SiteMetrics) RecordRequest(site string, success bool, duration time.Duration)

RecordRequest records a site request

func (*SiteMetrics) SetActiveDownloaders

func (m *SiteMetrics) SetActiveDownloaders(count int)

SetActiveDownloaders sets the number of active downloaders

func (*SiteMetrics) SetActiveSites

func (m *SiteMetrics) SetActiveSites(count int)

SetActiveSites sets the number of active sites

func (*SiteMetrics) Snapshot

func (m *SiteMetrics) Snapshot() MetricsSnapshot

Snapshot returns a snapshot of all metrics

type SiteMigrationGuide

type SiteMigrationGuide struct {
	SiteType        string   `json:"siteType"`
	OldFormat       string   `json:"oldFormat"`
	NewFormat       string   `json:"newFormat"`
	Steps           []string `json:"steps"`
	Notes           []string `json:"notes,omitempty"`
	BreakingChanges []string `json:"breakingChanges,omitempty"`
}

SiteMigrationGuide provides migration guidance for each site type

func GetSiteMigrationGuides

func GetSiteMigrationGuides() []SiteMigrationGuide

GetSiteMigrationGuides returns migration guides for all site types

type SiteMigrationManager

type SiteMigrationManager struct {
	// contains filtered or unexported fields
}

SiteMigrationManager manages the migration of sites from old to new implementation

func NewSiteMigrationManager

func NewSiteMigrationManager(logger *zap.Logger) *SiteMigrationManager

NewSiteMigrationManager creates a new SiteMigrationManager

func (*SiteMigrationManager) GetMigrationStatus

func (m *SiteMigrationManager) GetMigrationStatus() []MigrationStatus

GetMigrationStatus returns the migration status of all registered sites

func (*SiteMigrationManager) GetNewSite

func (m *SiteMigrationManager) GetNewSite(siteID string) Site

GetNewSite returns the new v2 site implementation if available

func (*SiteMigrationManager) GetSite

func (m *SiteMigrationManager) GetSite(siteID string) any

GetSite returns the site implementation for the given ID

func (*SiteMigrationManager) MigrateToNew

func (m *SiteMigrationManager) MigrateToNew(siteID string) error

MigrateToNew switches a site to use the new implementation

func (*SiteMigrationManager) RegisterSite

func (m *SiteMigrationManager) RegisterSite(siteID string, oldSite any, newSite Site, useNew bool)

RegisterSite registers a site with both old and new implementations

func (*SiteMigrationManager) RollbackToOld

func (m *SiteMigrationManager) RollbackToOld(siteID string) error

RollbackToOld switches a site back to the old implementation

type SiteName

type SiteName string

SiteName represents a PT site identifier

const (
	SiteNameMTeam        SiteName = "mteam"
	SiteNameHDSky        SiteName = "hdsky"
	SiteNameSpringSunday SiteName = "springsunday"
	SiteNameCHDBits      SiteName = "chdbits"
	SiteNameTTG          SiteName = "ttg"
	SiteNameOurBits      SiteName = "ourbits"
	SiteNamePterClub     SiteName = "pterclub"
	SiteNameAudiences    SiteName = "audiences"
)

Site name constants

func (SiteName) String

func (s SiteName) String() string

String returns the string representation of SiteName

type SiteRegistry

type SiteRegistry struct {
	// contains filtered or unexported fields
}

SiteRegistry manages site metadata and creation

func NewSiteRegistry

func NewSiteRegistry(logger *zap.Logger) *SiteRegistry

NewSiteRegistry creates a new SiteRegistry with default site metadata

func (*SiteRegistry) CreateSite

func (r *SiteRegistry) CreateSite(siteID string, creds SiteCredentials, customBaseURL string) (Site, error)

CreateSite creates a Site instance from registry metadata and credentials

func (*SiteRegistry) Get

func (r *SiteRegistry) Get(id string) (SiteMeta, bool)

Get returns site metadata by ID

func (*SiteRegistry) GetDefaultBaseURL

func (r *SiteRegistry) GetDefaultBaseURL(siteID string) (string, bool)

GetDefaultBaseURL returns the default base URL for a given site ID

func (*SiteRegistry) GetSiteKind

func (r *SiteRegistry) GetSiteKind(siteID string) (SiteKind, bool)

GetSiteKind returns the site kind for a given site ID

func (*SiteRegistry) List

func (r *SiteRegistry) List() []string

List returns all registered site IDs

func (*SiteRegistry) Register

func (r *SiteRegistry) Register(meta SiteMeta)

Register adds or updates site metadata

type SiteSelectors

type SiteSelectors struct {
	// TableRows selects torrent rows in the search results
	TableRows string `json:"tableRows"`
	// Title selects the torrent title
	Title string `json:"title"`
	// TitleLink selects the link containing torrent ID
	TitleLink string `json:"titleLink"`
	// Size selects the torrent size
	Size string `json:"size"`
	// Seeders selects the seeder count
	Seeders string `json:"seeders"`
	// Leechers selects the leecher count
	Leechers string `json:"leechers"`
	// Snatched selects the snatch count
	Snatched string `json:"snatched"`
	// DiscountIcon selects the discount icon element
	DiscountIcon string `json:"discountIcon"`
	// DiscountEndTime selects the discount end time
	DiscountEndTime string `json:"discountEndTime"`
	// DownloadLink selects the download link
	DownloadLink string `json:"downloadLink"`
	// Category selects the category
	Category string `json:"category"`
	// UploadTime selects the upload time
	UploadTime string `json:"uploadTime"`
	// HRIcon selects the H&R icon
	HRIcon string `json:"hrIcon"`
	// Subtitle selects the subtitle in search results
	Subtitle string `json:"subtitle"`
	// UserInfo selectors for user page
	UserInfoUsername   string `json:"userInfoUsername"`
	UserInfoUploaded   string `json:"userInfoUploaded"`
	UserInfoDownloaded string `json:"userInfoDownloaded"`
	UserInfoRatio      string `json:"userInfoRatio"`
	UserInfoBonus      string `json:"userInfoBonus"`
	UserInfoRank       string `json:"userInfoRank"`
	// Detail page selectors
	// DetailDownloadLink selects the download link from details page
	DetailDownloadLink string `json:"detailDownloadLink"`
	// DetailSubtitle selects the subtitle from details page
	DetailSubtitle string `json:"detailSubtitle"`
}

SiteSelectors defines CSS selectors for parsing NexusPHP pages

func DefaultNexusPHPSelectors

func DefaultNexusPHPSelectors() SiteSelectors

DefaultNexusPHPSelectors returns default selectors for standard NexusPHP sites

type SiteURLRegistry

type SiteURLRegistry struct {
	// contains filtered or unexported fields
}

SiteURLRegistry manages site URL configurations It provides a centralized way to register and retrieve site URLs

func GetGlobalRegistry

func GetGlobalRegistry() *SiteURLRegistry

GetGlobalRegistry returns the global site URL registry

func NewSiteURLRegistry

func NewSiteURLRegistry(logger *zap.Logger) *SiteURLRegistry

NewSiteURLRegistry creates a new site URL registry

func (*SiteURLRegistry) GetFailoverClient

func (r *SiteURLRegistry) GetFailoverClient(siteName SiteName, opts ...FailoverOption) (*FailoverHTTPClient, error)

GetFailoverClient creates a FailoverHTTPClient for the specified site

func (*SiteURLRegistry) GetFailoverConfig

func (r *SiteURLRegistry) GetFailoverConfig(siteName SiteName) (URLFailoverConfig, error)

GetFailoverConfig returns a failover config for the specified site

func (*SiteURLRegistry) GetURLs

func (r *SiteURLRegistry) GetURLs(siteName SiteName) []string

GetURLs returns the URL list for a site Returns nil if the site is not registered

func (*SiteURLRegistry) HasSite

func (r *SiteURLRegistry) HasSite(siteName SiteName) bool

HasSite checks if a site is registered

func (*SiteURLRegistry) ListSites

func (r *SiteURLRegistry) ListSites() []SiteName

ListSites returns all registered site names

func (*SiteURLRegistry) RegisterURLs

func (r *SiteURLRegistry) RegisterURLs(siteName SiteName, urls []string)

RegisterURLs registers or updates URLs for a site

type SpringSundayParser

type SpringSundayParser struct {
	Config NexusPHPParserConfig
}

SpringSundayParser implements NexusPHPDetailParser for SpringSunday site

func NewSpringSundayParser

func NewSpringSundayParser(options ...NexusPHPParserOption) *SpringSundayParser

NewSpringSundayParser creates a new SpringSundayParser

func (*SpringSundayParser) ParseAll

ParseAll parses all information from SpringSunday page

func (*SpringSundayParser) ParseDiscount

func (p *SpringSundayParser) ParseDiscount(doc *goquery.Selection) (DiscountLevel, time.Time)

ParseDiscount parses discount type and end time from SpringSunday page

func (*SpringSundayParser) ParseHR

func (p *SpringSundayParser) ParseHR(doc *goquery.Selection) bool

ParseHR parses HR status from SpringSunday page

func (*SpringSundayParser) ParseSizeMB

func (p *SpringSundayParser) ParseSizeMB(doc *goquery.Selection) float64

ParseSizeMB parses torrent size in MB from SpringSunday page

func (*SpringSundayParser) ParseTitleAndID

func (p *SpringSundayParser) ParseTitleAndID(doc *goquery.Selection) (title, torrentID string)

ParseTitleAndID parses title and torrent ID from SpringSunday page

type SyncError

type SyncError struct {
	Site  string
	Error error
}

SyncError represents a sync error for a specific site

type Timer

type Timer struct {
	// contains filtered or unexported fields
}

Timer measures elapsed time

func NewTimer

func NewTimer(h *Histogram) *Timer

NewTimer creates a new timer

func (*Timer) ObserveDuration

func (t *Timer) ObserveDuration() time.Duration

ObserveDuration records the elapsed time since the timer was created

type TorrentDetail

type TorrentDetail struct {
	// DownloadURL is the direct download URL with passkey
	DownloadURL string `json:"downloadUrl"`
	// Subtitle is the torrent subtitle
	Subtitle string `json:"subtitle"`
	// InfoHash is the torrent info hash
	InfoHash string `json:"infoHash,omitempty"`
}

TorrentDetail contains detailed information from a torrent detail page

type TorrentDetailInfo

type TorrentDetailInfo struct {
	TorrentID     string
	Title         string
	SizeMB        float64
	DiscountLevel DiscountLevel
	DiscountEnd   time.Time
	HasHR         bool
}

TorrentDetailInfo represents parsed torrent detail information

type TorrentFile

type TorrentFile struct {
	Path   string `json:"path"`
	Length int64  `json:"length"`
}

TorrentFile represents a file in a torrent

type TorrentItem

type TorrentItem struct {
	// ID is the site-specific torrent identifier
	ID string `json:"id"`
	// URL is the torrent detail page URL
	URL string `json:"url,omitempty"`
	// Title is the torrent title
	Title string `json:"title"`
	// Subtitle is the torrent subtitle (副标题)
	Subtitle string `json:"subtitle,omitempty"`
	// InfoHash is the torrent info hash (if available)
	InfoHash string `json:"infoHash,omitempty"`
	// Magnet is the magnet link (if available)
	Magnet string `json:"magnet,omitempty"`
	// SizeBytes is the torrent size in bytes
	SizeBytes int64 `json:"sizeBytes"`
	// Seeders is the number of seeders
	Seeders int `json:"seeders"`
	// Leechers is the number of leechers
	Leechers int `json:"leechers"`
	// Snatched is the number of completed downloads
	Snatched int `json:"snatched,omitempty"`
	// UploadedAt is the upload timestamp (Unix seconds)
	UploadedAt int64 `json:"uploadedAt,omitempty"`
	// Tags are the torrent tags/labels
	Tags []string `json:"tags,omitempty"`
	// SourceSite is the site this torrent came from
	SourceSite string `json:"sourceSite"`
	// DiscountLevel is the current discount level
	DiscountLevel DiscountLevel `json:"discountLevel"`
	// DiscountEndTime is when the discount expires
	DiscountEndTime time.Time `json:"discountEndTime,omitempty"`
	// HasHR indicates if the torrent has H&R requirements
	HasHR bool `json:"hasHR,omitempty"`
	// DownloadURL is the direct download URL
	DownloadURL string `json:"downloadUrl,omitempty"`
	// Category is the torrent category
	Category string `json:"category,omitempty"`
}

TorrentItem represents a torrent search result

func (*TorrentItem) CanbeFinished

func (t *TorrentItem) CanbeFinished(enabled bool, speedLimit, sizeLimitGB int) bool

CanbeFinished checks if the torrent can be downloaded within the free period enabled: whether download limit is enabled speedLimit: download speed limit in MB/s sizeLimitGB: maximum torrent size in GB

func (*TorrentItem) GetFreeEndTime

func (t *TorrentItem) GetFreeEndTime() *time.Time

GetFreeEndTime returns the discount end time

func (*TorrentItem) GetFreeLevel

func (t *TorrentItem) GetFreeLevel() string

GetFreeLevel returns the discount level as string

func (*TorrentItem) GetName

func (t *TorrentItem) GetName() string

GetName returns the torrent title

func (*TorrentItem) GetSubTitle

func (t *TorrentItem) GetSubTitle() string

GetSubTitle returns the torrent tags as subtitle

func (*TorrentItem) IsDiscountActive

func (t *TorrentItem) IsDiscountActive() bool

IsDiscountActive returns true if the discount is still active

func (*TorrentItem) IsFree

func (t *TorrentItem) IsFree() bool

IsFree returns true if the torrent is currently free

type TorrentManifest

type TorrentManifest struct {
	ID            string        `json:"id"`
	Title         string        `json:"title"`
	SizeBytes     int64         `json:"sizeBytes"`
	DiscountLevel DiscountLevel `json:"discountLevel"`
	DownloadURL   string        `json:"downloadUrl"`
	Category      string        `json:"category,omitempty"`
	Seeders       int           `json:"seeders,omitempty"`
	Leechers      int           `json:"leechers,omitempty"`
}

TorrentManifest represents metadata for a torrent in the batch download

type TwoLevelCache

type TwoLevelCache struct {
	// contains filtered or unexported fields
}

TwoLevelCache provides L1 (in-memory) and optional L2 (external) caching

func NewTwoLevelCache

func NewTwoLevelCache(config TwoLevelCacheConfig) *TwoLevelCache

NewTwoLevelCache creates a new two-level cache

func (*TwoLevelCache) Clear

func (c *TwoLevelCache) Clear()

Clear removes all entries from L1 (L2 is not cleared)

func (*TwoLevelCache) Delete

func (c *TwoLevelCache) Delete(key string) error

Delete removes a value from both L1 and L2

func (*TwoLevelCache) Get

func (c *TwoLevelCache) Get(key string, unmarshal func([]byte) (any, error)) (any, bool)

Get retrieves a value, checking L1 first, then L2

func (*TwoLevelCache) Set

func (c *TwoLevelCache) Set(key string, value any, marshal func(any) ([]byte, error)) error

Set stores a value in both L1 and L2

type TwoLevelCacheConfig

type TwoLevelCacheConfig struct {
	L1Capacity int           // L1 cache capacity (default: 1000)
	L1TTL      time.Duration // L1 TTL (default: 5 minutes)
	L2TTL      time.Duration // L2 TTL (default: 1 hour)
	L2Cache    L2Cache       // Optional L2 cache implementation
}

TwoLevelCacheConfig configures the two-level cache

type URLFailoverConfig

type URLFailoverConfig struct {
	// BaseURLs is the list of base URLs to try in order
	BaseURLs []string
	// RetryDelay is the delay between retries on the same URL
	RetryDelay time.Duration
	// MaxRetries is the maximum number of retries per URL (0 = no retry, just try once)
	MaxRetries int
	// Timeout is the timeout for each request
	Timeout time.Duration
}

URLFailoverConfig configures multi-URL failover behavior

func DefaultFailoverConfig

func DefaultFailoverConfig(baseURLs []string) URLFailoverConfig

DefaultFailoverConfig returns a default failover configuration

type URLFailoverManager

type URLFailoverManager struct {
	// contains filtered or unexported fields
}

URLFailoverManager manages multi-URL failover for site requests

func NewURLFailoverManager

func NewURLFailoverManager(config URLFailoverConfig, logger *zap.Logger) *URLFailoverManager

NewURLFailoverManager creates a new URL failover manager

func (*URLFailoverManager) ExecuteWithFailover

func (m *URLFailoverManager) ExecuteWithFailover(
	ctx context.Context,
	execFunc func(baseURL string) error,
) error

ExecuteWithFailover executes a function with automatic URL failover The execFunc receives the base URL and should return an error if the request fails Returns the error from the last attempted URL if all URLs fail

func (*URLFailoverManager) GetAllURLs

func (m *URLFailoverManager) GetAllURLs() []string

GetAllURLs returns all configured base URLs

func (*URLFailoverManager) GetCurrentURL

func (m *URLFailoverManager) GetCurrentURL() string

GetCurrentURL returns the currently active base URL

type Unit3DDriver

type Unit3DDriver struct {
	BaseURL string
	APIKey  string
	// contains filtered or unexported fields
}

Unit3DDriver implements the Driver interface for Unit3D sites

func NewUnit3DDriver

func NewUnit3DDriver(config Unit3DDriverConfig) *Unit3DDriver

NewUnit3DDriver creates a new Unit3D driver

func (*Unit3DDriver) Execute

Execute performs the HTTP request

func (*Unit3DDriver) GetUserInfo

func (d *Unit3DDriver) GetUserInfo(ctx context.Context) (UserInfo, error)

GetUserInfo fetches complete user information

func (*Unit3DDriver) ParseDownload

func (d *Unit3DDriver) ParseDownload(res Unit3DResponse) ([]byte, error)

ParseDownload extracts torrent file data from the response

func (*Unit3DDriver) ParseSearch

func (d *Unit3DDriver) ParseSearch(res Unit3DResponse) ([]TorrentItem, error)

ParseSearch extracts torrent items from the response

func (*Unit3DDriver) ParseUserInfo

func (d *Unit3DDriver) ParseUserInfo(res Unit3DResponse) (UserInfo, error)

ParseUserInfo extracts user info from the response

func (*Unit3DDriver) PrepareDownload

func (d *Unit3DDriver) PrepareDownload(torrentID string) (Unit3DRequest, error)

PrepareDownload prepares a request for downloading a torrent

func (*Unit3DDriver) PrepareSearch

func (d *Unit3DDriver) PrepareSearch(query SearchQuery) (Unit3DRequest, error)

PrepareSearch converts a SearchQuery to a Unit3D request

func (*Unit3DDriver) PrepareUserInfo

func (d *Unit3DDriver) PrepareUserInfo() (Unit3DRequest, error)

PrepareUserInfo prepares a request for user info

type Unit3DDriverConfig

type Unit3DDriverConfig struct {
	BaseURL    string
	APIKey     string
	HTTPClient *SiteHTTPClient // Use SiteHTTPClient instead of *http.Client
	UserAgent  string
}

Unit3DDriverConfig holds configuration for creating a Unit3D driver

type Unit3DOptions

type Unit3DOptions struct {
	APIKey string `json:"apiKey"`
}

Unit3DOptions holds Unit3D-specific configuration

type Unit3DRequest

type Unit3DRequest struct {
	// Endpoint is the API endpoint path
	Endpoint string
	// Method is the HTTP method
	Method string
	// Params are the query parameters
	Params url.Values
}

Unit3DRequest represents a request to a Unit3D site

type Unit3DResponse

type Unit3DResponse struct {
	// Data is the response data
	Data json.RawMessage `json:"data"`
	// Links contains pagination links
	Links json.RawMessage `json:"links,omitempty"`
	// Meta contains pagination metadata
	Meta json.RawMessage `json:"meta,omitempty"`
	// RawBody is the raw response body
	RawBody []byte `json:"-"`
	// StatusCode is the HTTP status code
	StatusCode int `json:"-"`
}

Unit3DResponse represents a response from Unit3D API

type Unit3DTorrent

type Unit3DTorrent struct {
	ID             int    `json:"id"`
	Name           string `json:"name"`
	InfoHash       string `json:"info_hash"`
	Size           int64  `json:"size"`
	Seeders        int    `json:"seeders"`
	Leechers       int    `json:"leechers"`
	TimesCompleted int    `json:"times_completed"`
	Category       struct {
		ID   int    `json:"id"`
		Name string `json:"name"`
	} `json:"category"`
	Type struct {
		ID   int    `json:"id"`
		Name string `json:"name"`
	} `json:"type"`
	Freeleech     string `json:"freeleech"`
	FreeleechEnds string `json:"freeleech_ends,omitempty"`
	DoubleUpload  bool   `json:"double_upload"`
	Featured      bool   `json:"featured"`
	CreatedAt     string `json:"created_at"`
	DownloadLink  string `json:"download_link,omitempty"`
}

Unit3DTorrent represents a torrent in Unit3D API response

type Unit3DUserProfile

type Unit3DUserProfile struct {
	ID         int     `json:"id"`
	Username   string  `json:"username"`
	Uploaded   int64   `json:"uploaded"`
	Downloaded int64   `json:"downloaded"`
	Ratio      float64 `json:"ratio"`
	Buffer     int64   `json:"buffer"`
	Seedbonus  float64 `json:"seedbonus"`
	Seeding    int     `json:"seeding"`
	Leeching   int     `json:"leeching"`
	Group      struct {
		Name string `json:"name"`
	} `json:"group"`
	CreatedAt string `json:"created_at"`
}

Unit3DUserProfile represents user profile from Unit3D API

type UserInfo

type UserInfo struct {
	// Site is the site identifier
	Site string `json:"site"`
	// Username is the user's username
	Username string `json:"username"`
	// UserID is the site-specific user ID
	UserID string `json:"userId"`
	// Uploaded is the total uploaded bytes
	Uploaded int64 `json:"uploaded"`
	// Downloaded is the total downloaded bytes
	Downloaded int64 `json:"downloaded"`
	// Ratio is the upload/download ratio
	Ratio float64 `json:"ratio"`
	// Bonus is the bonus points
	Bonus float64 `json:"bonus"`
	// Seeding is the number of torrents being seeded
	Seeding int `json:"seeding"`
	// Leeching is the number of torrents being downloaded
	Leeching int `json:"leeching"`
	// Rank is the user's rank/class
	Rank string `json:"rank"`
	// JoinDate is when the user joined (Unix seconds)
	JoinDate int64 `json:"joinDate,omitempty"`
	// LastAccess is the last access time (Unix seconds)
	LastAccess int64 `json:"lastAccess,omitempty"`
	// LastUpdate is when this info was last updated (Unix seconds)
	LastUpdate int64 `json:"lastUpdate"`
	// NextLevel contains level progression info (optional)
	NextLevel *LevelProgress `json:"nextLevel,omitempty"`

	// Extended fields (populated by sites that support them)
	// LevelName is the user's level/rank name
	LevelName string `json:"levelName,omitempty"`
	// LevelID is the numeric level ID
	LevelID int `json:"levelId,omitempty"`
	// BonusPerHour is the bonus points earned per hour (时魔)
	BonusPerHour float64 `json:"bonusPerHour,omitempty"`
	// SeedingBonus is the seeding bonus points (做种积分)
	SeedingBonus float64 `json:"seedingBonus,omitempty"`
	// SeedingBonusPerHour is the seeding bonus per hour
	SeedingBonusPerHour float64 `json:"seedingBonusPerHour,omitempty"`
	// UnreadMessageCount is the number of unread messages
	UnreadMessageCount int `json:"unreadMessageCount,omitempty"`
	// TotalMessageCount is the total number of messages
	TotalMessageCount int `json:"totalMessageCount,omitempty"`
	// SeederCount is the number of torrents being seeded (from peer statistics)
	SeederCount int `json:"seederCount,omitempty"`
	// SeederSize is the total size of seeding torrents (bytes)
	SeederSize int64 `json:"seederSize,omitempty"`
	// LeecherCount is the number of torrents being downloaded
	LeecherCount int `json:"leecherCount,omitempty"`
	// LeecherSize is the total size of leeching torrents (bytes)
	LeecherSize int64 `json:"leecherSize,omitempty"`
	// HnRUnsatisfied is the number of unsatisfied H&R
	HnRUnsatisfied int `json:"hnrUnsatisfied,omitempty"`
	// HnRPreWarning is the number of H&R pre-warnings
	HnRPreWarning int `json:"hnrPreWarning,omitempty"`
	// TrueUploaded is the true uploaded bytes (some sites track separately)
	TrueUploaded int64 `json:"trueUploaded,omitempty"`
	// TrueDownloaded is the true downloaded bytes
	TrueDownloaded int64 `json:"trueDownloaded,omitempty"`
	// Uploads is the number of torrents uploaded by user
	Uploads int `json:"uploads,omitempty"`
}

UserInfo represents user information from a PT site

type UserInfoConfig

type UserInfoConfig struct {
	// PickLast specifies fields that should retain last known value
	PickLast []string `json:"pickLast,omitempty"`

	// RequestDelay between multi-step requests (milliseconds)
	RequestDelay int `json:"requestDelay,omitempty"`

	// Process defines multi-step user info fetching
	Process []UserInfoProcess `json:"process,omitempty"`

	// Selectors for parsing user info fields
	Selectors map[string]FieldSelector `json:"selectors,omitempty"`
}

UserInfoConfig defines how to fetch and parse user info

type UserInfoProcess

type UserInfoProcess struct {
	// RequestConfig for this step
	RequestConfig RequestConfig `json:"requestConfig"`

	// Assertion for parameter passing (e.g., {"id": "params.id"})
	Assertion map[string]string `json:"assertion,omitempty"`

	// Fields to extract in this step
	Fields []string `json:"fields"`
}

UserInfoProcess defines a single step in user info fetching

type UserInfoRecord

type UserInfoRecord struct {
	ID         uint      `gorm:"primaryKey" json:"id"`
	Site       string    `gorm:"uniqueIndex;size:64;not null" json:"site"`
	Username   string    `gorm:"size:128" json:"username"`
	UserID     string    `gorm:"size:64" json:"userId"`
	Rank       string    `gorm:"size:64" json:"rank"`
	Uploaded   int64     `json:"uploaded"`
	Downloaded int64     `json:"downloaded"`
	Ratio      float64   `json:"ratio"`
	Seeding    int       `json:"seeding"`
	Leeching   int       `json:"leeching"`
	Bonus      float64   `json:"bonus"`
	JoinDate   int64     `json:"joinDate"`
	LastAccess int64     `json:"lastAccess"`
	LastUpdate int64     `json:"lastUpdate"`
	CreatedAt  time.Time `json:"createdAt"`
	UpdatedAt  time.Time `json:"updatedAt"`
	// Extended fields
	LevelName           string  `gorm:"size:64" json:"levelName"`
	LevelID             int     `json:"levelId"`
	BonusPerHour        float64 `json:"bonusPerHour"`
	SeedingBonus        float64 `json:"seedingBonus"`
	SeedingBonusPerHour float64 `json:"seedingBonusPerHour"`
	UnreadMessageCount  int     `json:"unreadMessageCount"`
	TotalMessageCount   int     `json:"totalMessageCount"`
	SeederCount         int     `json:"seederCount"`
	SeederSize          int64   `json:"seederSize"`
	LeecherCount        int     `json:"leecherCount"`
	LeecherSize         int64   `json:"leecherSize"`
	HnRUnsatisfied      int     `json:"hnrUnsatisfied"`
	HnRPreWarning       int     `json:"hnrPreWarning"`
	TrueUploaded        int64   `json:"trueUploaded"`
	TrueDownloaded      int64   `json:"trueDownloaded"`
	Uploads             int     `json:"uploads"`
}

UserInfoRecord represents the database model for user info

func FromUserInfo

func FromUserInfo(info UserInfo) UserInfoRecord

FromUserInfo creates a UserInfoRecord from UserInfo

func (UserInfoRecord) TableName

func (UserInfoRecord) TableName() string

TableName returns the table name for UserInfoRecord

func (*UserInfoRecord) ToUserInfo

func (r *UserInfoRecord) ToUserInfo() UserInfo

ToUserInfo converts UserInfoRecord to UserInfo

type UserInfoRepo

type UserInfoRepo interface {
	// Save stores user info for a site
	Save(ctx context.Context, info UserInfo) error
	// Get retrieves user info for a specific site
	Get(ctx context.Context, site string) (UserInfo, error)
	// ListAll retrieves all stored user info
	ListAll(ctx context.Context) ([]UserInfo, error)
	// ListBySites retrieves user info for specific sites
	ListBySites(ctx context.Context, sites []string) ([]UserInfo, error)
	// Delete removes user info for a site
	Delete(ctx context.Context, site string) error
	// GetAggregated calculates aggregated statistics
	GetAggregated(ctx context.Context) (AggregatedStats, error)
}

UserInfoRepo defines the interface for storing and retrieving user information

type UserInfoService

type UserInfoService struct {
	// contains filtered or unexported fields
}

UserInfoService provides user information management across multiple sites

func NewUserInfoService

func NewUserInfoService(config UserInfoServiceConfig) *UserInfoService

NewUserInfoService creates a new UserInfoService

func (*UserInfoService) ClearCache

func (s *UserInfoService) ClearCache()

ClearCache clears the user info cache

func (*UserInfoService) DeleteUserInfo

func (s *UserInfoService) DeleteUserInfo(ctx context.Context, siteID string) error

DeleteUserInfo removes user info for a site

func (*UserInfoService) FetchAndSave

func (s *UserInfoService) FetchAndSave(ctx context.Context, siteID string) (UserInfo, error)

FetchAndSave fetches user info from a site and saves it

func (*UserInfoService) FetchAndSaveAll

func (s *UserInfoService) FetchAndSaveAll(ctx context.Context) ([]UserInfo, []error)

FetchAndSaveAll fetches user info from all registered sites

func (*UserInfoService) FetchAndSaveAllWithConcurrency

func (s *UserInfoService) FetchAndSaveAllWithConcurrency(
	ctx context.Context,
	maxConcurrent int,
	timeout time.Duration,
) ([]UserInfo, []SyncError)

FetchAndSaveAllWithConcurrency fetches user info from all registered sites with concurrency control maxConcurrent: maximum number of concurrent requests timeout: timeout for each site request

func (*UserInfoService) GetAggregated

func (s *UserInfoService) GetAggregated(ctx context.Context) (AggregatedStats, error)

GetAggregated returns aggregated statistics across all sites

func (*UserInfoService) GetAllUserInfo

func (s *UserInfoService) GetAllUserInfo(ctx context.Context) ([]UserInfo, error)

GetAllUserInfo retrieves all stored user info

func (*UserInfoService) GetSite

func (s *UserInfoService) GetSite(siteID string) (Site, bool)

GetSite returns a registered site by ID

func (*UserInfoService) GetUserInfo

func (s *UserInfoService) GetUserInfo(ctx context.Context, siteID string) (UserInfo, error)

GetUserInfo retrieves user info for a site (from cache or repository)

func (*UserInfoService) ListSites

func (s *UserInfoService) ListSites() []string

ListSites returns all registered site IDs

func (*UserInfoService) RegisterSite

func (s *UserInfoService) RegisterSite(site Site)

RegisterSite registers a site for user info fetching

func (*UserInfoService) UnregisterSite

func (s *UserInfoService) UnregisterSite(siteID string)

UnregisterSite removes a site from the service

type UserInfoServiceConfig

type UserInfoServiceConfig struct {
	Repo     UserInfoRepo
	CacheTTL time.Duration
	Logger   *zap.Logger
}

UserInfoServiceConfig holds configuration for UserInfoService

type ValidationConfig

type ValidationConfig struct {
	MaxStringLength  int   // Maximum string length (default: 10000)
	MaxURLLength     int   // Maximum URL length (default: 2048)
	MaxFileSizeBytes int64 // Maximum file size in bytes (default: 100MB)
	AllowHTML        bool  // Whether to allow HTML content
	StripHTML        bool  // Whether to strip HTML tags
}

ValidationConfig configures validation limits

func DefaultValidationConfig

func DefaultValidationConfig() ValidationConfig

DefaultValidationConfig returns default validation configuration

type Validator

type Validator struct {
	// contains filtered or unexported fields
}

Validator provides input validation methods

func NewValidator

func NewValidator(config ValidationConfig) *Validator

NewValidator creates a new validator with the given configuration

func (*Validator) ValidateFileSize

func (v *Validator) ValidateFileSize(size int64) error

ValidateFileSize validates a file size

func (*Validator) ValidateInfoHash

func (v *Validator) ValidateInfoHash(input string) (string, error)

ValidateInfoHash validates a torrent info hash

func (*Validator) ValidateSearchQuery

func (v *Validator) ValidateSearchQuery(query SearchQuery) error

ValidateSearchQuery validates a search query

func (*Validator) ValidateString

func (v *Validator) ValidateString(input string) (string, error)

ValidateString validates a string input

func (*Validator) ValidateURL

func (v *Validator) ValidateURL(input string) (string, error)

ValidateURL validates a URL input

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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