Files
silo-server/internal/metadata/skipped_root_repo.go
T

154 lines
4.4 KiB
Go

package metadata
import (
"context"
"fmt"
"path/filepath"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/Silo-Server/silo-server/internal/models"
)
// SkippedRootRepository persists media roots that were skipped during scans.
type SkippedRootRepository struct {
pool *pgxpool.Pool
}
// NewSkippedRootRepository creates a new SkippedRootRepository.
func NewSkippedRootRepository(pool *pgxpool.Pool) *SkippedRootRepository {
return &SkippedRootRepository{pool: pool}
}
const skippedRootColumns = `media_folder_id, root_path, reason, sample_file_path, file_count, first_seen_at, last_seen_at`
func scanSkippedRoot(row pgx.Row) (*models.SkippedMediaRoot, error) {
var root models.SkippedMediaRoot
if err := row.Scan(
&root.MediaFolderID,
&root.RootPath,
&root.Reason,
&root.SampleFilePath,
&root.FileCount,
&root.FirstSeenAt,
&root.LastSeenAt,
); err != nil {
return nil, fmt.Errorf("scanning skipped media root: %w", err)
}
return &root, nil
}
func scanSkippedRoots(rows pgx.Rows) ([]*models.SkippedMediaRoot, error) {
var roots []*models.SkippedMediaRoot
for rows.Next() {
var root models.SkippedMediaRoot
if err := rows.Scan(
&root.MediaFolderID,
&root.RootPath,
&root.Reason,
&root.SampleFilePath,
&root.FileCount,
&root.FirstSeenAt,
&root.LastSeenAt,
); err != nil {
return nil, fmt.Errorf("scanning skipped media root row: %w", err)
}
roots = append(roots, &root)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("iterating skipped media root rows: %w", err)
}
if roots == nil {
roots = []*models.SkippedMediaRoot{}
}
return roots, nil
}
// Upsert inserts or updates a skipped media root keyed by folder and root path.
func (r *SkippedRootRepository) Upsert(ctx context.Context, root models.SkippedMediaRoot) error {
query := `
INSERT INTO skipped_media_roots (
media_folder_id, root_path, reason, sample_file_path, file_count, first_seen_at, last_seen_at
)
VALUES ($1, $2, $3, $4, $5, NOW(), NOW())
ON CONFLICT (media_folder_id, root_path) DO UPDATE
SET reason = EXCLUDED.reason,
sample_file_path = EXCLUDED.sample_file_path,
file_count = EXCLUDED.file_count,
last_seen_at = NOW()
`
_, err := r.pool.Exec(ctx, query,
root.MediaFolderID,
root.RootPath,
root.Reason,
root.SampleFilePath,
root.FileCount,
)
if err != nil {
return fmt.Errorf("upserting skipped media root: %w", err)
}
return nil
}
// Delete removes one skipped media root.
func (r *SkippedRootRepository) Delete(ctx context.Context, folderID int, rootPath string) error {
_, err := r.pool.Exec(ctx,
`DELETE FROM skipped_media_roots WHERE media_folder_id = $1 AND root_path = $2`,
folderID, rootPath,
)
if err != nil {
return fmt.Errorf("deleting skipped media root: %w", err)
}
return nil
}
// DeleteMissingInScope removes skipped roots under scopePath that were not seen.
func (r *SkippedRootRepository) DeleteMissingInScope(ctx context.Context, folderID int, scopePath string, seenRoots []string) error {
if seenRoots == nil {
seenRoots = []string{}
}
scopePath = filepath.Clean(scopePath)
_, err := r.pool.Exec(ctx, `
DELETE FROM skipped_media_roots
WHERE media_folder_id = $1
AND (root_path = $2 OR strpos(root_path, $2 || '/') = 1)
AND NOT (root_path = ANY($3))
`, folderID, scopePath, seenRoots)
if err != nil {
return fmt.Errorf("deleting missing skipped media roots in scope: %w", err)
}
return nil
}
// ListByFolder returns skipped roots for a folder ordered by most recent sighting.
func (r *SkippedRootRepository) ListByFolder(ctx context.Context, folderID int) ([]*models.SkippedMediaRoot, error) {
rows, err := r.pool.Query(ctx, `
SELECT `+skippedRootColumns+`
FROM skipped_media_roots
WHERE media_folder_id = $1
ORDER BY last_seen_at DESC, root_path ASC
`, folderID)
if err != nil {
return nil, fmt.Errorf("listing skipped media roots by folder: %w", err)
}
defer rows.Close()
return scanSkippedRoots(rows)
}
// ListAll returns all skipped roots ordered by most recent sighting.
func (r *SkippedRootRepository) ListAll(ctx context.Context) ([]*models.SkippedMediaRoot, error) {
rows, err := r.pool.Query(ctx, `
SELECT `+skippedRootColumns+`
FROM skipped_media_roots
ORDER BY last_seen_at DESC, media_folder_id ASC, root_path ASC
`)
if err != nil {
return nil, fmt.Errorf("listing skipped media roots: %w", err)
}
defer rows.Close()
return scanSkippedRoots(rows)
}