- 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>
188 lines
4.4 KiB
Go
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)
|
|
}
|