- 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>
212 lines
6.0 KiB
Go
212 lines
6.0 KiB
Go
package db
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"time"
|
|
|
|
"git.leaktechnologies.dev/stu/Goondex/internal/model"
|
|
)
|
|
|
|
// MovieStore provides database operations for movies
|
|
type MovieStore struct {
|
|
db *DB
|
|
}
|
|
|
|
// NewMovieStore creates a new MovieStore
|
|
func NewMovieStore(db *DB) *MovieStore {
|
|
return &MovieStore{db: db}
|
|
}
|
|
|
|
// Create inserts a new movie into the database
|
|
func (s *MovieStore) Create(movie *model.Movie) error {
|
|
now := time.Now()
|
|
movie.CreatedAt = now
|
|
movie.UpdatedAt = now
|
|
|
|
result, err := s.db.conn.Exec(`
|
|
INSERT INTO movies (
|
|
title, date, studio_id, description, director, duration,
|
|
image_path, image_url, back_image_url, url, source, source_id,
|
|
created_at, updated_at
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
`,
|
|
movie.Title, movie.Date, movie.StudioID, movie.Description, movie.Director, movie.Duration,
|
|
movie.ImagePath, movie.ImageURL, movie.BackImageURL, movie.URL, movie.Source, movie.SourceID,
|
|
now, now,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create movie: %w", err)
|
|
}
|
|
|
|
id, err := result.LastInsertId()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get movie ID: %w", err)
|
|
}
|
|
|
|
movie.ID = id
|
|
return nil
|
|
}
|
|
|
|
// GetByID retrieves a movie by its ID
|
|
func (s *MovieStore) GetByID(id int64) (*model.Movie, error) {
|
|
var movie model.Movie
|
|
var studioID sql.NullInt64
|
|
|
|
err := s.db.conn.QueryRow(`
|
|
SELECT id, title, COALESCE(date, ''), COALESCE(studio_id, 0), COALESCE(description, ''),
|
|
COALESCE(director, ''), COALESCE(duration, 0),
|
|
COALESCE(image_path, ''), COALESCE(image_url, ''), COALESCE(back_image_url, ''),
|
|
COALESCE(url, ''), COALESCE(source, ''), COALESCE(source_id, ''),
|
|
created_at, updated_at
|
|
FROM movies
|
|
WHERE id = ?
|
|
`, id).Scan(
|
|
&movie.ID, &movie.Title, &movie.Date, &studioID, &movie.Description,
|
|
&movie.Director, &movie.Duration,
|
|
&movie.ImagePath, &movie.ImageURL, &movie.BackImageURL,
|
|
&movie.URL, &movie.Source, &movie.SourceID,
|
|
&movie.CreatedAt, &movie.UpdatedAt,
|
|
)
|
|
|
|
if err == sql.ErrNoRows {
|
|
return nil, fmt.Errorf("movie not found")
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get movie: %w", err)
|
|
}
|
|
|
|
if studioID.Valid && studioID.Int64 > 0 {
|
|
movie.StudioID = &studioID.Int64
|
|
}
|
|
|
|
return &movie, nil
|
|
}
|
|
|
|
// Search searches for movies by title
|
|
func (s *MovieStore) Search(query string) ([]model.Movie, error) {
|
|
rows, err := s.db.conn.Query(`
|
|
SELECT id, title, COALESCE(date, ''), COALESCE(studio_id, 0), COALESCE(description, ''),
|
|
COALESCE(director, ''), COALESCE(duration, 0),
|
|
COALESCE(image_path, ''), COALESCE(image_url, ''), COALESCE(back_image_url, ''),
|
|
COALESCE(url, ''), COALESCE(source, ''), COALESCE(source_id, ''),
|
|
created_at, updated_at
|
|
FROM movies
|
|
WHERE title LIKE ?
|
|
ORDER BY date DESC, title ASC
|
|
LIMIT 100
|
|
`, "%"+query+"%")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to search movies: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var movies []model.Movie
|
|
for rows.Next() {
|
|
var movie model.Movie
|
|
var studioID sql.NullInt64
|
|
|
|
if err := rows.Scan(
|
|
&movie.ID, &movie.Title, &movie.Date, &studioID, &movie.Description,
|
|
&movie.Director, &movie.Duration,
|
|
&movie.ImagePath, &movie.ImageURL, &movie.BackImageURL,
|
|
&movie.URL, &movie.Source, &movie.SourceID,
|
|
&movie.CreatedAt, &movie.UpdatedAt,
|
|
); err != nil {
|
|
return nil, fmt.Errorf("failed to scan movie: %w", err)
|
|
}
|
|
|
|
if studioID.Valid && studioID.Int64 > 0 {
|
|
movie.StudioID = &studioID.Int64
|
|
}
|
|
|
|
movies = append(movies, movie)
|
|
}
|
|
|
|
return movies, nil
|
|
}
|
|
|
|
// AddScene links a scene to a movie
|
|
func (s *MovieStore) AddScene(movieID, sceneID int64, sceneNumber int) error {
|
|
_, err := s.db.conn.Exec(`
|
|
INSERT OR IGNORE INTO movie_scenes (movie_id, scene_id, scene_number)
|
|
VALUES (?, ?, ?)
|
|
`, movieID, sceneID, sceneNumber)
|
|
return err
|
|
}
|
|
|
|
// GetScenes returns all scenes for a movie
|
|
func (s *MovieStore) GetScenes(movieID int64) ([]model.Scene, error) {
|
|
rows, err := s.db.conn.Query(`
|
|
SELECT s.id, s.title, COALESCE(s.code, ''), COALESCE(s.date, ''), COALESCE(s.studio_id, 0),
|
|
COALESCE(s.description, ''), COALESCE(s.image_path, ''), COALESCE(s.image_url, ''),
|
|
COALESCE(s.director, ''), COALESCE(s.url, ''), COALESCE(s.source, ''), COALESCE(s.source_id, ''),
|
|
s.created_at, s.updated_at, COALESCE(ms.scene_number, 0)
|
|
FROM scenes s
|
|
INNER JOIN movie_scenes ms ON s.id = ms.scene_id
|
|
WHERE ms.movie_id = ?
|
|
ORDER BY ms.scene_number ASC, s.title ASC
|
|
`, movieID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get scenes: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var scenes []model.Scene
|
|
for rows.Next() {
|
|
var scene model.Scene
|
|
var studioID sql.NullInt64
|
|
var sceneNumber int
|
|
|
|
if err := rows.Scan(
|
|
&scene.ID, &scene.Title, &scene.Code, &scene.Date, &studioID,
|
|
&scene.Description, &scene.ImagePath, &scene.ImageURL,
|
|
&scene.Director, &scene.URL, &scene.Source, &scene.SourceID,
|
|
&scene.CreatedAt, &scene.UpdatedAt, &sceneNumber,
|
|
); err != nil {
|
|
return nil, fmt.Errorf("failed to scan scene: %w", err)
|
|
}
|
|
|
|
if studioID.Valid && studioID.Int64 > 0 {
|
|
scene.StudioID = &studioID.Int64
|
|
}
|
|
|
|
scenes = append(scenes, scene)
|
|
}
|
|
|
|
return scenes, nil
|
|
}
|
|
|
|
// GetSceneCount returns the number of scenes in a movie
|
|
func (s *MovieStore) GetSceneCount(movieID int64) (int, error) {
|
|
var count int
|
|
err := s.db.conn.QueryRow(`
|
|
SELECT COUNT(*) FROM movie_scenes WHERE movie_id = ?
|
|
`, movieID).Scan(&count)
|
|
return count, err
|
|
}
|
|
|
|
// Update updates an existing movie
|
|
func (s *MovieStore) Update(movie *model.Movie) error {
|
|
movie.UpdatedAt = time.Now()
|
|
|
|
_, err := s.db.conn.Exec(`
|
|
UPDATE movies SET
|
|
title = ?, date = ?, studio_id = ?, description = ?, director = ?, duration = ?,
|
|
image_path = ?, image_url = ?, back_image_url = ?, url = ?, source = ?, source_id = ?,
|
|
updated_at = ?
|
|
WHERE id = ?
|
|
`,
|
|
movie.Title, movie.Date, movie.StudioID, movie.Description, movie.Director, movie.Duration,
|
|
movie.ImagePath, movie.ImageURL, movie.BackImageURL, movie.URL, movie.Source, movie.SourceID,
|
|
movie.UpdatedAt, movie.ID,
|
|
)
|
|
return err
|
|
}
|
|
|
|
// Delete removes a movie from the database
|
|
func (s *MovieStore) Delete(id int64) error {
|
|
_, err := s.db.conn.Exec("DELETE FROM movies WHERE id = ?", id)
|
|
return err
|
|
}
|