package media import ( "context" "database/sql" "time" ) type Row struct { ID int64 ItemID int64 MediaType string // 'original' (user uploads), 'image' (fetched from URLs) ContentType string // MIME type Data []byte Width *int Height *int SourceURL *string // Original URL the media was fetched from CreatedAt time.Time } type CreateParams struct { ItemID int64 MediaType string ContentType string Data []byte Width *int Height *int SourceURL *string } // QCreate creates a new media record. func QCreate(ctx context.Context, db *sql.DB, p CreateParams) (Row, error) { query := ` INSERT INTO media (item_id, media_type, content_type, data, width, height, source_url) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id, item_id, media_type, content_type, data, width, height, source_url, created_at ` var row Row err := db.QueryRowContext(ctx, query, p.ItemID, p.MediaType, p.ContentType, p.Data, p.Width, p.Height, p.SourceURL, ).Scan( &row.ID, &row.ItemID, &row.MediaType, &row.ContentType, &row.Data, &row.Width, &row.Height, &row.SourceURL, &row.CreatedAt, ) return row, err } // QFindByID finds a media record by ID. func QFindByID(ctx context.Context, db *sql.DB, id int64) (*Row, error) { query := ` SELECT id, item_id, media_type, content_type, data, width, height, source_url, created_at FROM media WHERE id = $1 ` var row Row err := db.QueryRowContext(ctx, query, id).Scan( &row.ID, &row.ItemID, &row.MediaType, &row.ContentType, &row.Data, &row.Width, &row.Height, &row.SourceURL, &row.CreatedAt, ) if err == sql.ErrNoRows { return nil, nil } if err != nil { return nil, err } return &row, nil } // QFindByItemID finds all media for an item, ordered by ID. func QFindByItemID(ctx context.Context, db *sql.DB, itemID int64) ([]Row, error) { query := ` SELECT id, item_id, media_type, content_type, data, width, height, source_url, created_at FROM media WHERE item_id = $1 ORDER BY id ASC ` rows, err := db.QueryContext(ctx, query, itemID) if err != nil { return nil, err } defer rows.Close() var media []Row for rows.Next() { var row Row if err := rows.Scan( &row.ID, &row.ItemID, &row.MediaType, &row.ContentType, &row.Data, &row.Width, &row.Height, &row.SourceURL, &row.CreatedAt, ); err != nil { return nil, err } media = append(media, row) } return media, rows.Err() } // QFindThumbnailByItemID finds the first image for an item (used as thumbnail). func QFindThumbnailByItemID(ctx context.Context, db *sql.DB, itemID int64) (*Row, error) { query := ` SELECT id, item_id, media_type, content_type, data, width, height, source_url, created_at FROM media WHERE item_id = $1 AND media_type = 'image' ORDER BY id ASC LIMIT 1 ` var row Row err := db.QueryRowContext(ctx, query, itemID).Scan( &row.ID, &row.ItemID, &row.MediaType, &row.ContentType, &row.Data, &row.Width, &row.Height, &row.SourceURL, &row.CreatedAt, ) if err == sql.ErrNoRows { return nil, nil } if err != nil { return nil, err } return &row, nil } // QFindOriginalByItemID finds the original media for an item. func QFindOriginalByItemID(ctx context.Context, db *sql.DB, itemID int64) (*Row, error) { query := ` SELECT id, item_id, media_type, content_type, data, width, height, source_url, created_at FROM media WHERE item_id = $1 AND media_type = 'original' LIMIT 1 ` var row Row err := db.QueryRowContext(ctx, query, itemID).Scan( &row.ID, &row.ItemID, &row.MediaType, &row.ContentType, &row.Data, &row.Width, &row.Height, &row.SourceURL, &row.CreatedAt, ) if err == sql.ErrNoRows { return nil, nil } if err != nil { return nil, err } return &row, nil } // QDelete deletes a media record by ID. func QDelete(ctx context.Context, db *sql.DB, id int64) error { query := `DELETE FROM media WHERE id = $1` _, err := db.ExecContext(ctx, query, id) return err } // QDeleteByItemID deletes all media for an item. func QDeleteByItemID(ctx context.Context, db *sql.DB, itemID int64) error { query := `DELETE FROM media WHERE item_id = $1` _, err := db.ExecContext(ctx, query, itemID) return err }