- 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>
202 lines
5.7 KiB
Go
202 lines
5.7 KiB
Go
package db
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"time"
|
|
|
|
"git.leaktechnologies.dev/stu/Goondex/internal/model"
|
|
)
|
|
|
|
// StudioStore handles CRUD operations for studios
|
|
type StudioStore struct {
|
|
db *DB
|
|
}
|
|
|
|
// NewStudioStore creates a new studio store
|
|
func NewStudioStore(db *DB) *StudioStore {
|
|
return &StudioStore{db: db}
|
|
}
|
|
|
|
// Create inserts a new studio
|
|
func (s *StudioStore) Create(studio *model.Studio) error {
|
|
now := time.Now()
|
|
studio.CreatedAt = now
|
|
studio.UpdatedAt = now
|
|
|
|
result, err := s.db.conn.Exec(`
|
|
INSERT INTO studios (name, parent_id, image_path, image_url, description, source, source_id, created_at, updated_at)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
`, studio.Name, studio.ParentID, studio.ImagePath, studio.ImageURL, studio.Description, studio.Source, studio.SourceID, studio.CreatedAt.Format(time.RFC3339), studio.UpdatedAt.Format(time.RFC3339))
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create studio: %w", err)
|
|
}
|
|
|
|
id, err := result.LastInsertId()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get last insert id: %w", err)
|
|
}
|
|
|
|
studio.ID = id
|
|
return nil
|
|
}
|
|
|
|
// GetByID retrieves a studio by ID
|
|
func (s *StudioStore) GetByID(id int64) (*model.Studio, error) {
|
|
studio := &model.Studio{}
|
|
var createdAt, updatedAt string
|
|
|
|
err := s.db.conn.QueryRow(`
|
|
SELECT id, name, COALESCE(parent_id, 0), COALESCE(image_path, ''), COALESCE(image_url, ''), COALESCE(description, ''), COALESCE(source, ''), COALESCE(source_id, ''), created_at, updated_at
|
|
FROM studios WHERE id = ?
|
|
`, id).Scan(&studio.ID, &studio.Name, &studio.ParentID, &studio.ImagePath, &studio.ImageURL, &studio.Description, &studio.Source, &studio.SourceID, &createdAt, &updatedAt)
|
|
|
|
if err == sql.ErrNoRows {
|
|
return nil, fmt.Errorf("studio not found")
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get studio: %w", err)
|
|
}
|
|
|
|
studio.CreatedAt, _ = time.Parse(time.RFC3339, createdAt)
|
|
studio.UpdatedAt, _ = time.Parse(time.RFC3339, updatedAt)
|
|
|
|
return studio, nil
|
|
}
|
|
|
|
// Search searches for studios by name
|
|
func (s *StudioStore) Search(query string) ([]model.Studio, error) {
|
|
rows, err := s.db.conn.Query(`
|
|
SELECT id, name, COALESCE(parent_id, 0), COALESCE(image_path, ''), COALESCE(image_url, ''), COALESCE(description, ''), COALESCE(source, ''), COALESCE(source_id, ''), created_at, updated_at
|
|
FROM studios
|
|
WHERE name LIKE ?
|
|
ORDER BY name
|
|
`, "%"+query+"%")
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to search studios: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var studios []model.Studio
|
|
for rows.Next() {
|
|
var studio model.Studio
|
|
var createdAt, updatedAt string
|
|
|
|
err := rows.Scan(&studio.ID, &studio.Name, &studio.ParentID, &studio.ImagePath, &studio.ImageURL, &studio.Description, &studio.Source, &studio.SourceID, &createdAt, &updatedAt)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to scan studio: %w", err)
|
|
}
|
|
|
|
studio.CreatedAt, _ = time.Parse(time.RFC3339, createdAt)
|
|
studio.UpdatedAt, _ = time.Parse(time.RFC3339, updatedAt)
|
|
|
|
studios = append(studios, studio)
|
|
}
|
|
|
|
return studios, nil
|
|
}
|
|
|
|
// Update updates an existing studio
|
|
func (s *StudioStore) Update(studio *model.Studio) error {
|
|
studio.UpdatedAt = time.Now()
|
|
|
|
result, err := s.db.conn.Exec(`
|
|
UPDATE studios
|
|
SET name = ?, parent_id = ?, image_path = ?, image_url = ?, description = ?, source = ?, source_id = ?, updated_at = ?
|
|
WHERE id = ?
|
|
`, studio.Name, studio.ParentID, studio.ImagePath, studio.ImageURL, studio.Description, studio.Source, studio.SourceID, studio.UpdatedAt.Format(time.RFC3339), studio.ID)
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed to update studio: %w", err)
|
|
}
|
|
|
|
rows, err := result.RowsAffected()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get rows affected: %w", err)
|
|
}
|
|
|
|
if rows == 0 {
|
|
return fmt.Errorf("studio not found")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetSceneCount returns the number of scenes associated with a studio
|
|
func (s *StudioStore) GetSceneCount(studioID int64) (int, error) {
|
|
var count int
|
|
err := s.db.conn.QueryRow(`
|
|
SELECT COUNT(*) FROM scenes WHERE studio_id = ?
|
|
`, studioID).Scan(&count)
|
|
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to count scenes: %w", err)
|
|
}
|
|
|
|
return count, nil
|
|
}
|
|
|
|
// Delete deletes a studio by ID
|
|
func (s *StudioStore) Delete(id int64) error {
|
|
result, err := s.db.conn.Exec("DELETE FROM studios WHERE id = ?", id)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to delete studio: %w", err)
|
|
}
|
|
|
|
rows, err := result.RowsAffected()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get rows affected: %w", err)
|
|
}
|
|
|
|
if rows == 0 {
|
|
return fmt.Errorf("studio not found")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Upsert inserts or updates a studio based on source_id
|
|
func (s *StudioStore) Upsert(st *model.Studio) error {
|
|
// Try to find existing studio by source_id
|
|
existing, err := s.GetBySourceID(st.Source, st.SourceID)
|
|
if err == nil && existing != nil {
|
|
// Update existing
|
|
st.ID = existing.ID
|
|
return s.Update(st)
|
|
}
|
|
// Create new
|
|
return s.Create(st)
|
|
}
|
|
|
|
// GetBySourceID retrieves a studio by its source and source_id
|
|
func (s *StudioStore) GetBySourceID(source, sourceID string) (*model.Studio, error) {
|
|
var st model.Studio
|
|
var createdAt, updatedAt string
|
|
|
|
err := s.db.conn.QueryRow(`
|
|
SELECT id, name, COALESCE(parent_id, 0), COALESCE(image_path, ''), COALESCE(image_url, ''),
|
|
COALESCE(description, ''), COALESCE(source, ''), COALESCE(source_id, ''),
|
|
created_at, updated_at
|
|
FROM studios
|
|
WHERE source = ? AND source_id = ?
|
|
`, source, sourceID).Scan(
|
|
&st.ID, &st.Name, &st.ParentID, &st.ImagePath, &st.ImageURL,
|
|
&st.Description, &st.Source, &st.SourceID,
|
|
&createdAt, &updatedAt,
|
|
)
|
|
|
|
if err == sql.ErrNoRows {
|
|
return nil, nil
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get studio: %w", err)
|
|
}
|
|
|
|
st.CreatedAt, _ = time.Parse(time.RFC3339, createdAt)
|
|
st.UpdatedAt, _ = time.Parse(time.RFC3339, updatedAt)
|
|
|
|
return &st, nil
|
|
}
|