Goondex/internal/scraper/adultemp/client.go
Stu Leak 16fb407a3c v0.1.0-dev4: Add web frontend with UI component library
- 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>
2025-11-17 10:47:30 -05:00

188 lines
4.4 KiB
Go

package adultemp
import (
"context"
"fmt"
"io"
"net/http"
"net/http/cookiejar"
"net/url"
"time"
"golang.org/x/net/publicsuffix"
)
// Client handles HTTP requests to Adult Empire
type Client struct {
httpClient *http.Client
baseURL string
userAgent string
}
// NewClient creates a new Adult Empire client
func NewClient() (*Client, error) {
// Create cookie jar for session management
jar, err := cookiejar.New(&cookiejar.Options{
PublicSuffixList: publicsuffix.List,
})
if err != nil {
return nil, fmt.Errorf("failed to create cookie jar: %w", err)
}
client := &http.Client{
Jar: jar,
Timeout: 30 * time.Second,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
// Allow up to 10 redirects
if len(via) >= 10 {
return fmt.Errorf("stopped after 10 redirects")
}
return nil
},
}
c := &Client{
httpClient: client,
baseURL: "https://www.adultempire.com",
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
}
// Set age confirmation cookie by default
if err := c.setAgeConfirmation(); err != nil {
return nil, fmt.Errorf("failed to set age confirmation: %w", err)
}
return c, nil
}
// setAgeConfirmation sets the age confirmation cookie required to view Adult Empire content
func (c *Client) setAgeConfirmation() error {
u, err := url.Parse(c.baseURL)
if err != nil {
return err
}
cookies := []*http.Cookie{
{
Name: "ageConfirmed",
Value: "1",
Domain: ".adultempire.com",
Path: "/",
},
}
c.httpClient.Jar.SetCookies(u, cookies)
return nil
}
// SetAuthToken sets the authentication token for Adult Empire
// etoken is the session cookie from an authenticated Adult Empire session
func (c *Client) SetAuthToken(etoken string) error {
u, err := url.Parse(c.baseURL)
if err != nil {
return err
}
// Set the etoken cookie
cookies := []*http.Cookie{
{
Name: "etoken",
Value: etoken,
Domain: ".adultempire.com",
Path: "/",
},
{
Name: "ageConfirmed",
Value: "1",
Domain: ".adultempire.com",
Path: "/",
},
}
c.httpClient.Jar.SetCookies(u, cookies)
return nil
}
// Get performs a GET request to the specified path
func (c *Client) Get(ctx context.Context, path string) ([]byte, error) {
fullURL := c.baseURL + path
req, err := http.NewRequestWithContext(ctx, "GET", fullURL, nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("User-Agent", c.userAgent)
req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
req.Header.Set("Accept-Language", "en-US,en;q=0.5")
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}
return body, nil
}
// SearchScenes searches for scenes by query
func (c *Client) SearchScenes(ctx context.Context, query string) ([]byte, error) {
// Adult Empire search URL format
searchPath := fmt.Sprintf("/dvd/search?q=%s", url.QueryEscape(query))
return c.Get(ctx, searchPath)
}
// SearchPerformers searches for performers by name
func (c *Client) SearchPerformers(ctx context.Context, name string) ([]byte, error) {
// Adult Empire performer search
searchPath := fmt.Sprintf("/performer/search?q=%s", url.QueryEscape(name))
return c.Get(ctx, searchPath)
}
// GetSceneByURL fetches a scene page by its URL
func (c *Client) GetSceneByURL(ctx context.Context, sceneURL string) ([]byte, error) {
// Parse the URL to get just the path
u, err := url.Parse(sceneURL)
if err != nil {
return nil, fmt.Errorf("invalid URL: %w", err)
}
// If it's a full URL, use the path; otherwise use as-is
path := sceneURL
if u.Host != "" {
path = u.Path
if u.RawQuery != "" {
path += "?" + u.RawQuery
}
}
return c.Get(ctx, path)
}
// GetPerformerByURL fetches a performer page by its URL
func (c *Client) GetPerformerByURL(ctx context.Context, performerURL string) ([]byte, error) {
u, err := url.Parse(performerURL)
if err != nil {
return nil, fmt.Errorf("invalid URL: %w", err)
}
path := performerURL
if u.Host != "" {
path = u.Path
if u.RawQuery != "" {
path += "?" + u.RawQuery
}
}
return c.Get(ctx, path)
}