Goondex/internal/db/movie_store.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

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
}