media.go
⎇
Raw
1package repository
2
3import (
4 "database/sql"
5 "strings"
6 "vidarchive/internal/models"
7)
8
9type MediaRepository struct {
10 db *sql.DB
11}
12
13func NewMediaRepository(db *sql.DB) *MediaRepository {
14 return &MediaRepository{db: db}
15}
16
17func (r *MediaRepository) Create(m *models.Media) error {
18 result, err := r.db.Exec(
19 `INSERT INTO media (url, title, description, duration, filepath, relative_path, is_audio, has_embedded_thumbnail, info_json_path, playlist_id, playlist_index)
20 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
21 m.URL, m.Title, m.Description, m.Duration, m.Filepath, m.RelativePath,
22 boolToInt(m.IsAudio), boolToInt(m.HasEmbeddedThumbnail), m.InfoJSONPath,
23 m.PlaylistID, m.PlaylistIndex,
24 )
25 if err != nil {
26 return err
27 }
28 m.ID, _ = result.LastInsertId()
29 return nil
30}
31
32func (r *MediaRepository) GetByID(id int64) (*models.Media, error) {
33 row := r.db.QueryRow(
34 `SELECT id, url, title, description, duration, filepath, relative_path, is_audio, has_embedded_thumbnail, info_json_path, playlist_id, playlist_index, created_at
35 FROM media WHERE id = ?`, id,
36 )
37 return scanMedia(row)
38}
39
40func (r *MediaRepository) GetAll(path string, sortBy, filter string) ([]*models.Media, error) {
41 query := `SELECT id, url, title, description, duration, filepath, relative_path, is_audio, has_embedded_thumbnail, info_json_path, playlist_id, playlist_index, created_at
42 FROM media WHERE relative_path LIKE ?`
43 args := []interface{}{path + "%"}
44
45 // For non-root paths, restrict to immediate items (not nested inside subfolders).
46 // For root path, only show media not inside any folder.
47 if path != "" {
48 query += ` AND relative_path NOT LIKE ?`
49 args = append(args, path+"%/%")
50 } else {
51 query += ` AND relative_path NOT LIKE ?`
52 args = append(args, "%/%")
53 }
54
55 if filter != "" {
56 query += ` AND (title LIKE ? OR description LIKE ?)`
57 args = append(args, "%"+filter+"%", "%"+filter+"%")
58 }
59
60 switch sortBy {
61 case "title":
62 query += ` ORDER BY title`
63 case "date":
64 query += ` ORDER BY created_at DESC`
65 case "duration":
66 query += ` ORDER BY duration DESC`
67 default:
68 query += ` ORDER BY created_at DESC`
69 }
70
71 rows, err := r.db.Query(query, args...)
72 if err != nil {
73 return nil, err
74 }
75 defer rows.Close()
76
77 var media []*models.Media
78 for rows.Next() {
79 m, err := scanMedia(rows)
80 if err != nil {
81 return nil, err
82 }
83 media = append(media, m)
84 }
85 return media, rows.Err()
86}
87
88func (r *MediaRepository) GetByRelativePath(relPath string) (*models.Media, error) {
89 row := r.db.QueryRow(
90 `SELECT id, url, title, description, duration, filepath, relative_path, is_audio, has_embedded_thumbnail, info_json_path, playlist_id, playlist_index, created_at
91 FROM media WHERE relative_path = ?`, relPath,
92 )
93 return scanMedia(row)
94}
95
96func (r *MediaRepository) Delete(id int64) error {
97 _, err := r.db.Exec(`DELETE FROM media WHERE id = ?`, id)
98 return err
99}
100
101func (r *MediaRepository) GetFolders(parentPath string) ([]string, error) {
102 rows, err := r.db.Query(
103 `SELECT relative_path FROM media WHERE relative_path LIKE ?`,
104 parentPath+"%",
105 )
106 if err != nil {
107 return nil, err
108 }
109 defer rows.Close()
110
111 folders := make(map[string]bool)
112 for rows.Next() {
113 var relPath string
114 if err := rows.Scan(&relPath); err != nil {
115 return nil, err
116 }
117 // Get the folder part after parentPath
118 remaining := strings.TrimPrefix(relPath, parentPath)
119 remaining = strings.TrimPrefix(remaining, "/")
120 if idx := strings.Index(remaining, "/"); idx >= 0 {
121 folder := remaining[:idx]
122 if folder != "" {
123 folders[folder] = true
124 }
125 }
126 }
127
128 var result []string
129 for f := range folders {
130 result = append(result, f)
131 }
132 return result, rows.Err()
133}
134
135func scanMedia(row interface{ Scan(...interface{}) error }) (*models.Media, error) {
136 var m models.Media
137 var isAudio, hasThumb int
138 err := row.Scan(
139 &m.ID, &m.URL, &m.Title, &m.Description, &m.Duration, &m.Filepath,
140 &m.RelativePath, &isAudio, &hasThumb, &m.InfoJSONPath,
141 &m.PlaylistID, &m.PlaylistIndex, &m.CreatedAt,
142 )
143 if err != nil {
144 return nil, err
145 }
146 m.IsAudio = isAudio == 1
147 m.HasEmbeddedThumbnail = hasThumb == 1
148 return &m, nil
149}
150