models

package
v0.1.2-alpha Latest Latest
Warning

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

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

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsCloudflareChallengePage

func IsCloudflareChallengePage(body string) bool

IsCloudflareChallengePage detects Cloudflare anti-bot/interstitial challenge pages from HTML content. These pages are often returned with HTTP 200.

func ResolveSearchQueryForScraper

func ResolveSearchQueryForScraper(scraper Scraper, input string) (string, bool)

ResolveSearchQueryForScraper resolves an input query using a scraper's optional ScraperQueryResolver hook.

Types

type Actress

type Actress struct {
	ID           uint   `json:"id" gorm:"primaryKey"`
	DMMID        int    `json:"dmm_id" gorm:"uniqueIndex"` // DMM actress ID for unique identification
	FirstName    string `json:"first_name"`
	LastName     string `json:"last_name"`
	JapaneseName string `json:"japanese_name" gorm:"index"`
	ThumbURL     string `json:"thumb_url"`
	Aliases      string `json:"aliases"` // Pipe-separated

	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

Actress represents a JAV actress

func (*Actress) FullName

func (a *Actress) FullName() string

FullName returns the actress's full English name

func (Actress) TableName

func (Actress) TableName() string

TableName specifies the table name for Actress

type ActressAlias

type ActressAlias struct {
	ID            uint      `json:"id" gorm:"primaryKey"`
	AliasName     string    `json:"alias_name" gorm:"uniqueIndex;not null"` // The alternate name (e.g., "Yui Hatano")
	CanonicalName string    `json:"canonical_name" gorm:"index;not null"`   // The canonical/preferred name (e.g., "Hatano Yui")
	CreatedAt     time.Time `json:"created_at"`
	UpdatedAt     time.Time `json:"updated_at"`
}

ActressAlias represents an alternate name mapping for an actress This allows users to consolidate multiple actress names into a canonical one

type ActressInfo

type ActressInfo struct {
	DMMID        int    `json:"dmm_id"` // DMM actress ID for unique identification
	FirstName    string `json:"first_name"`
	LastName     string `json:"last_name"`
	JapaneseName string `json:"japanese_name"`
	ThumbURL     string `json:"thumb_url"`
}

ActressInfo represents actress information from a scraper

func (*ActressInfo) FullName

func (a *ActressInfo) FullName() string

FullName returns the actress's full name

type ContentIDMapping

type ContentIDMapping struct {
	ID        uint      `gorm:"primarykey" json:"id"`
	SearchID  string    `gorm:"uniqueIndex;not null" json:"search_id"` // e.g., "MDB-087"
	ContentID string    `gorm:"not null" json:"content_id"`            // e.g., "61mdb087"
	Source    string    `gorm:"not null" json:"source"`                // e.g., "dmm"
	CreatedAt time.Time `json:"created_at"`
}

ContentIDMapping stores the mapping between search IDs and actual DMM content IDs This is used to cache the resolution of display IDs (like "MDB-087") to actual content IDs (like "61mdb087") that DMM uses internally

type Genre

type Genre struct {
	ID   uint   `json:"id" gorm:"primaryKey"`
	Name string `json:"name" gorm:"uniqueIndex"`
}

Genre represents a category/tag

func (Genre) TableName

func (Genre) TableName() string

TableName specifies the table name for Genre

type GenreReplacement

type GenreReplacement struct {
	ID          uint      `json:"id" gorm:"primaryKey"`
	Original    string    `json:"original" gorm:"uniqueIndex;not null"`
	Replacement string    `json:"replacement" gorm:"not null"`
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`
}

GenreReplacement represents a user-defined genre name mapping

type History

type History struct {
	ID           uint      `json:"id" gorm:"primaryKey"`
	MovieID      string    `json:"movie_id" gorm:"index"`          // Foreign key to movies.content_id (nullable for historical records)
	Operation    string    `json:"operation"`                      // "scrape", "organize", "download", "nfo"
	OriginalPath string    `json:"original_path"`                  // Source file path
	NewPath      string    `json:"new_path"`                       // Destination file path
	Status       string    `json:"status"`                         // "success", "failed", "reverted"
	ErrorMessage string    `json:"error_message" gorm:"type:text"` // Error details if failed
	Metadata     string    `json:"metadata" gorm:"type:json"`      // Additional metadata (JSON)
	DryRun       bool      `json:"dry_run"`                        // Whether this was a dry run
	CreatedAt    time.Time `json:"created_at" gorm:"index"`
}

History represents a log of file organization operations

func (History) TableName

func (History) TableName() string

TableName specifies the table name for History

type Movie

type Movie struct {
	ContentID        string     `json:"content_id" gorm:"primaryKey"`
	ID               string     `json:"id" gorm:"index"`
	DisplayName      string     `json:"display_name"`
	Title            string     `json:"title"`
	OriginalTitle    string     `json:"original_title"` // Japanese/original language title
	Description      string     `json:"description" gorm:"type:text"`
	ReleaseDate      *time.Time `json:"release_date"`
	ReleaseYear      int        `json:"release_year"`
	Runtime          int        `json:"runtime"` // in minutes
	Director         string     `json:"director"`
	Maker            string     `json:"maker"`  // Studio/maker
	Label            string     `json:"label"`  // Sub-label
	Series           string     `json:"series"` // Series name
	RatingScore      float64    `json:"rating_score" gorm:"column:rating_score"`
	RatingVotes      int        `json:"rating_votes" gorm:"column:rating_votes"`
	PosterURL        string     `json:"poster_url"`         // Portrait/box art image
	CoverURL         string     `json:"cover_url"`          // Landscape/fanart image
	CroppedPosterURL string     `json:"cropped_poster_url"` // URL to the cropped poster (persisted)
	ShouldCropPoster bool       `json:"should_crop_poster"` // Whether poster needs cropping from cover
	TrailerURL       string     `json:"trailer_url"`
	OriginalFileName string     `json:"original_filename"`

	// Relationships
	Actresses   []Actress `` /* 139-byte string literal not displayed */
	Genres      []Genre   `` /* 131-byte string literal not displayed */
	Screenshots []string  `json:"screenshot_urls" gorm:"serializer:json"`

	// Translations
	Translations []MovieTranslation `json:"translations" gorm:"foreignKey:MovieID;references:ContentID"`

	// Metadata
	SourceName string    `json:"source_name"` // Primary source
	SourceURL  string    `json:"source_url"`
	CreatedAt  time.Time `json:"created_at"`
	UpdatedAt  time.Time `json:"updated_at"`
}

Movie represents the aggregated metadata for a JAV movie

func (Movie) TableName

func (Movie) TableName() string

TableName specifies the table name for Movie

type MovieTag

type MovieTag struct {
	ID        uint      `json:"id" gorm:"primaryKey"`
	MovieID   string    `json:"movie_id" gorm:"index:idx_movie_tag,unique;not null;size:50"` // Foreign key to movies.content_id (CASCADE handled in Delete)
	Tag       string    `json:"tag" gorm:"index:idx_movie_tag,unique;not null;size:100"`     // Tag name (case-sensitive)
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

MovieTag represents a custom user-defined tag for a specific movie Tags are used for personal organization and appear in NFO files

func (MovieTag) TableName

func (MovieTag) TableName() string

TableName specifies the table name for MovieTag

type MovieTranslation

type MovieTranslation struct {
	ID            uint      `json:"id" gorm:"primaryKey"`
	MovieID       string    `json:"movie_id" gorm:"index:idx_movie_language,unique"`
	Language      string    `json:"language" gorm:"index:idx_movie_language,unique;size:5"` // ISO 639-1: en, ja, zh, etc.
	Title         string    `json:"title"`
	OriginalTitle string    `json:"original_title"` // Japanese/original language title
	Description   string    `json:"description" gorm:"type:text"`
	Director      string    `json:"director"`
	Maker         string    `json:"maker"`
	Label         string    `json:"label"`
	Series        string    `json:"series"`
	SourceName    string    `json:"source_name"` // Which scraper provided this translation
	CreatedAt     time.Time `json:"created_at"`
	UpdatedAt     time.Time `json:"updated_at"`
}

MovieTranslation represents a movie's metadata in a specific language

func (MovieTranslation) TableName

func (MovieTranslation) TableName() string

TableName specifies the table name for MovieTranslation

type Rating

type Rating struct {
	Score float64 `json:"score"`
	Votes int     `json:"votes"`
}

Rating represents rating information from scrapers

type Scraper

type Scraper interface {
	// Name returns the scraper's identifier (e.g., "r18dev", "dmm")
	Name() string

	// Search attempts to find and scrape metadata for the given movie ID
	Search(id string) (*ScraperResult, error)

	// GetURL attempts to find the URL for a given movie ID
	GetURL(id string) (string, error)

	// IsEnabled returns whether this scraper is enabled in configuration
	IsEnabled() bool
}

Scraper defines the interface that all scrapers must implement

type ScraperDownloadProxyResolver

type ScraperDownloadProxyResolver interface {
	ResolveDownloadProxyForHost(host string) (downloadOverride *config.ProxyConfig, scraperProxy *config.ProxyConfig, handled bool)
}

ScraperDownloadProxyResolver is an optional hook for scrapers to control media download proxy routing for scraper-specific media/CDN hosts.

Implementations should return handled=false for unrelated hosts. When handled=true, downloader applies the same proxy precedence rules used by scraper download_proxy/proxy/global settings.

type ScraperError

type ScraperError struct {
	Scraper    string
	Kind       ScraperErrorKind
	StatusCode int
	Message    string
	Temporary  bool
	Retryable  bool
	Cause      error
}

ScraperError is a typed scraper failure that worker/UI layers can classify without brittle string parsing.

func AsScraperError

func AsScraperError(err error) (*ScraperError, bool)

AsScraperError extracts a ScraperError from any wrapped error chain.

func NewScraperChallengeError

func NewScraperChallengeError(scraper, message string) *ScraperError

NewScraperChallengeError builds a typed blocked error for anti-bot challenge pages (for example Cloudflare challenge interstitials served with HTTP 200).

func NewScraperNotFoundError

func NewScraperNotFoundError(scraper, message string) *ScraperError

NewScraperNotFoundError builds a typed "not found" scraper error.

func NewScraperStatusError

func NewScraperStatusError(scraper string, statusCode int, message string) *ScraperError

NewScraperStatusError builds a typed scraper error from an HTTP status code.

func (*ScraperError) Error

func (e *ScraperError) Error() string

func (*ScraperError) Unwrap

func (e *ScraperError) Unwrap() error

type ScraperErrorKind

type ScraperErrorKind string

ScraperErrorKind classifies scraper failures in a structured way.

const (
	ScraperErrorKindUnknown     ScraperErrorKind = "unknown"
	ScraperErrorKindNotFound    ScraperErrorKind = "not_found"
	ScraperErrorKindUnavailable ScraperErrorKind = "unavailable"
	ScraperErrorKindRateLimited ScraperErrorKind = "rate_limited"
	ScraperErrorKindBlocked     ScraperErrorKind = "blocked"
)

type ScraperQueryResolver

type ScraperQueryResolver interface {
	ResolveSearchQuery(input string) (string, bool)
}

ScraperQueryResolver is an optional hook for scrapers to declare and normalize identifier formats they can handle (e.g., non-standard filename IDs).

Implementations should return (normalizedQuery, true) when input matches a scraper-specific pattern, or ("", false) when it does not apply.

type ScraperRegistry

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

ScraperRegistry manages available scrapers

func NewScraperRegistry

func NewScraperRegistry() *ScraperRegistry

NewScraperRegistry creates a new scraper registry

func (*ScraperRegistry) Get

func (r *ScraperRegistry) Get(name string) (Scraper, bool)

Get retrieves a scraper by name

func (*ScraperRegistry) GetAll

func (r *ScraperRegistry) GetAll() []Scraper

GetAll returns all registered scrapers

func (*ScraperRegistry) GetByPriority

func (r *ScraperRegistry) GetByPriority(priority []string) []Scraper

GetByPriority returns enabled scrapers in the specified priority order If priority list is empty or nil, returns all enabled scrapers Only returns scrapers that are both in the priority list AND enabled

func (*ScraperRegistry) GetByPriorityForInput

func (r *ScraperRegistry) GetByPriorityForInput(priority []string, input string) []Scraper

GetByPriorityForInput returns enabled scrapers in priority order, but moves scrapers with matching query resolvers to the front for the provided input.

If no scraper resolver matches, the original GetByPriority ordering is returned unchanged.

func (*ScraperRegistry) GetEnabled

func (r *ScraperRegistry) GetEnabled() []Scraper

GetEnabled returns all enabled scrapers

func (*ScraperRegistry) Register

func (r *ScraperRegistry) Register(scraper Scraper)

Register adds a scraper to the registry

type ScraperResult

type ScraperResult struct {
	Source           string        `json:"source"`
	SourceURL        string        `json:"source_url"`
	Language         string        `json:"language"` // ISO 639-1 code: en, ja, zh, etc.
	ID               string        `json:"id"`
	ContentID        string        `json:"content_id"`
	Title            string        `json:"title"`
	OriginalTitle    string        `json:"original_title"` // Japanese/original language title
	Description      string        `json:"description"`
	ReleaseDate      *time.Time    `json:"release_date"`
	Runtime          int           `json:"runtime"`
	Director         string        `json:"director"`
	Maker            string        `json:"maker"`
	Label            string        `json:"label"`
	Series           string        `json:"series"`
	Rating           *Rating       `json:"rating"`
	Actresses        []ActressInfo `json:"actresses"`
	Genres           []string      `json:"genres"`
	PosterURL        string        `json:"poster_url"`         // Portrait/box art image
	CoverURL         string        `json:"cover_url"`          // Landscape/fanart image
	ShouldCropPoster bool          `json:"should_crop_poster"` // Whether poster needs cropping from cover
	ScreenshotURL    []string      `json:"screenshot_urls"`
	TrailerURL       string        `json:"trailer_url"`
}

ScraperResult represents the raw data returned by a scraper

Jump to

Keyboard shortcuts

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