- Implement full web interface with Go html/template server - Add GX component library (buttons, dialogs, tables, forms, etc.) - Create scene/performer/studio/movie detail and listing pages - Add Adult Empire scraper for additional metadata sources - Implement movie support with database schema - Add import and sync services for data management - Include comprehensive API and frontend documentation - Add custom color scheme and responsive layout 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
259 lines
7.3 KiB
Go
259 lines
7.3 KiB
Go
package tpdb
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/url"
|
|
|
|
"git.leaktechnologies.dev/stu/Goondex/internal/model"
|
|
)
|
|
|
|
// Scraper implements the scraper.Scraper interface for TPDB
|
|
type Scraper struct {
|
|
client *Client
|
|
}
|
|
|
|
// NewScraper creates a new TPDB scraper
|
|
func NewScraper(baseURL, apiKey string) *Scraper {
|
|
return &Scraper{
|
|
client: NewClient(baseURL, apiKey),
|
|
}
|
|
}
|
|
|
|
// Name returns the scraper's unique identifier
|
|
func (s *Scraper) Name() string {
|
|
return "tpdb"
|
|
}
|
|
|
|
// SearchPerformers searches for performers by query string
|
|
func (s *Scraper) SearchPerformers(ctx context.Context, query string) ([]model.Performer, error) {
|
|
params := url.Values{}
|
|
params.Set("q", query)
|
|
|
|
body, err := s.client.get(ctx, "/performers", params)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to search performers: %w", err)
|
|
}
|
|
|
|
var apiResp APIResponse
|
|
if err := json.Unmarshal(body, &apiResp); err != nil {
|
|
return nil, fmt.Errorf("failed to parse response: %w", err)
|
|
}
|
|
|
|
var tpdbPerformers []PerformerResponse
|
|
if err := json.Unmarshal(apiResp.Data, &tpdbPerformers); err != nil {
|
|
return nil, fmt.Errorf("failed to parse performers: %w", err)
|
|
}
|
|
|
|
performers := make([]model.Performer, 0, len(tpdbPerformers))
|
|
for _, p := range tpdbPerformers {
|
|
performers = append(performers, mapPerformer(p))
|
|
}
|
|
|
|
return performers, nil
|
|
}
|
|
|
|
// SearchStudios searches for studios by query string
|
|
func (s *Scraper) SearchStudios(ctx context.Context, query string) ([]model.Studio, error) {
|
|
params := url.Values{}
|
|
params.Set("q", query)
|
|
|
|
body, err := s.client.get(ctx, "/sites", params)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to search studios: %w", err)
|
|
}
|
|
|
|
var apiResp APIResponse
|
|
if err := json.Unmarshal(body, &apiResp); err != nil {
|
|
return nil, fmt.Errorf("failed to parse response: %w", err)
|
|
}
|
|
|
|
var tpdbStudios []StudioResponse
|
|
if err := json.Unmarshal(apiResp.Data, &tpdbStudios); err != nil {
|
|
return nil, fmt.Errorf("failed to parse studios: %w", err)
|
|
}
|
|
|
|
studios := make([]model.Studio, 0, len(tpdbStudios))
|
|
for _, st := range tpdbStudios {
|
|
studios = append(studios, mapStudio(st))
|
|
}
|
|
|
|
return studios, nil
|
|
}
|
|
|
|
// SearchScenes searches for scenes by query string
|
|
func (s *Scraper) SearchScenes(ctx context.Context, query string) ([]model.Scene, error) {
|
|
params := url.Values{}
|
|
params.Set("q", query)
|
|
|
|
body, err := s.client.get(ctx, "/scenes", params)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to search scenes: %w", err)
|
|
}
|
|
|
|
var apiResp APIResponse
|
|
if err := json.Unmarshal(body, &apiResp); err != nil {
|
|
return nil, fmt.Errorf("failed to parse response: %w", err)
|
|
}
|
|
|
|
var tpdbScenes []SceneResponse
|
|
if err := json.Unmarshal(apiResp.Data, &tpdbScenes); err != nil {
|
|
return nil, fmt.Errorf("failed to parse scenes: %w", err)
|
|
}
|
|
|
|
scenes := make([]model.Scene, 0, len(tpdbScenes))
|
|
for _, sc := range tpdbScenes {
|
|
scenes = append(scenes, mapScene(sc))
|
|
}
|
|
|
|
return scenes, nil
|
|
}
|
|
|
|
// GetSceneByID retrieves a scene by its remote ID
|
|
func (s *Scraper) GetSceneByID(ctx context.Context, remoteID string) (*model.Scene, error) {
|
|
body, err := s.client.get(ctx, "/scenes/"+remoteID, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get scene: %w", err)
|
|
}
|
|
|
|
var apiResp APIResponse
|
|
if err := json.Unmarshal(body, &apiResp); err != nil {
|
|
return nil, fmt.Errorf("failed to parse response: %w", err)
|
|
}
|
|
|
|
var tpdbScene SceneResponse
|
|
if err := json.Unmarshal(apiResp.Data, &tpdbScene); err != nil {
|
|
return nil, fmt.Errorf("failed to parse scene: %w", err)
|
|
}
|
|
|
|
scene := mapScene(tpdbScene)
|
|
return &scene, nil
|
|
}
|
|
|
|
// GetPerformerByID retrieves a performer by its remote ID
|
|
func (s *Scraper) GetPerformerByID(ctx context.Context, remoteID string) (*model.Performer, error) {
|
|
body, err := s.client.get(ctx, "/performers/"+remoteID, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get performer: %w", err)
|
|
}
|
|
|
|
var apiResp APIResponse
|
|
if err := json.Unmarshal(body, &apiResp); err != nil {
|
|
return nil, fmt.Errorf("failed to parse response: %w", err)
|
|
}
|
|
|
|
var tpdbPerformer PerformerResponse
|
|
if err := json.Unmarshal(apiResp.Data, &tpdbPerformer); err != nil {
|
|
return nil, fmt.Errorf("failed to parse performer: %w", err)
|
|
}
|
|
|
|
performer := mapPerformer(tpdbPerformer)
|
|
return &performer, nil
|
|
}
|
|
|
|
// GetStudioByID retrieves a studio by its remote ID
|
|
func (s *Scraper) GetStudioByID(ctx context.Context, remoteID string) (*model.Studio, error) {
|
|
body, err := s.client.get(ctx, "/sites/"+remoteID, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get studio: %w", err)
|
|
}
|
|
|
|
var apiResp APIResponse
|
|
if err := json.Unmarshal(body, &apiResp); err != nil {
|
|
return nil, fmt.Errorf("failed to parse response: %w", err)
|
|
}
|
|
|
|
var tpdbStudio StudioResponse
|
|
if err := json.Unmarshal(apiResp.Data, &tpdbStudio); err != nil {
|
|
return nil, fmt.Errorf("failed to parse studio: %w", err)
|
|
}
|
|
|
|
studio := mapStudio(tpdbStudio)
|
|
return &studio, nil
|
|
}
|
|
|
|
// ListPerformers fetches all performers with pagination
|
|
func (s *Scraper) ListPerformers(ctx context.Context, page int) ([]model.Performer, *MetaData, error) {
|
|
params := url.Values{}
|
|
params.Set("page", fmt.Sprintf("%d", page))
|
|
|
|
body, err := s.client.get(ctx, "/performers", params)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to list performers: %w", err)
|
|
}
|
|
|
|
var apiResp APIResponse
|
|
if err := json.Unmarshal(body, &apiResp); err != nil {
|
|
return nil, nil, fmt.Errorf("failed to parse response: %w", err)
|
|
}
|
|
|
|
var tpdbPerformers []PerformerResponse
|
|
if err := json.Unmarshal(apiResp.Data, &tpdbPerformers); err != nil {
|
|
return nil, nil, fmt.Errorf("failed to parse performers: %w", err)
|
|
}
|
|
|
|
performers := make([]model.Performer, 0, len(tpdbPerformers))
|
|
for _, p := range tpdbPerformers {
|
|
performers = append(performers, mapPerformer(p))
|
|
}
|
|
|
|
return performers, apiResp.Meta, nil
|
|
}
|
|
|
|
// ListStudios fetches all studios with pagination
|
|
func (s *Scraper) ListStudios(ctx context.Context, page int) ([]model.Studio, *MetaData, error) {
|
|
params := url.Values{}
|
|
params.Set("page", fmt.Sprintf("%d", page))
|
|
|
|
body, err := s.client.get(ctx, "/sites", params)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to list studios: %w", err)
|
|
}
|
|
|
|
var apiResp APIResponse
|
|
if err := json.Unmarshal(body, &apiResp); err != nil {
|
|
return nil, nil, fmt.Errorf("failed to parse response: %w", err)
|
|
}
|
|
|
|
var tpdbStudios []StudioResponse
|
|
if err := json.Unmarshal(apiResp.Data, &tpdbStudios); err != nil {
|
|
return nil, nil, fmt.Errorf("failed to parse studios: %w", err)
|
|
}
|
|
|
|
studios := make([]model.Studio, 0, len(tpdbStudios))
|
|
for _, st := range tpdbStudios {
|
|
studios = append(studios, mapStudio(st))
|
|
}
|
|
|
|
return studios, apiResp.Meta, nil
|
|
}
|
|
|
|
// ListScenes fetches all scenes with pagination
|
|
func (s *Scraper) ListScenes(ctx context.Context, page int) ([]model.Scene, *MetaData, error) {
|
|
params := url.Values{}
|
|
params.Set("page", fmt.Sprintf("%d", page))
|
|
|
|
body, err := s.client.get(ctx, "/scenes", params)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to list scenes: %w", err)
|
|
}
|
|
|
|
var apiResp APIResponse
|
|
if err := json.Unmarshal(body, &apiResp); err != nil {
|
|
return nil, nil, fmt.Errorf("failed to parse response: %w", err)
|
|
}
|
|
|
|
var tpdbScenes []SceneResponse
|
|
if err := json.Unmarshal(apiResp.Data, &tpdbScenes); err != nil {
|
|
return nil, nil, fmt.Errorf("failed to parse scenes: %w", err)
|
|
}
|
|
|
|
scenes := make([]model.Scene, 0, len(tpdbScenes))
|
|
for _, sc := range tpdbScenes {
|
|
scenes = append(scenes, mapScene(sc))
|
|
}
|
|
|
|
return scenes, apiResp.Meta, nil
|
|
}
|