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 if filter != "" {
46 query += ` AND (title LIKE ? OR description LIKE ?)`
47 args = append(args, "%"+filter+"%", "%"+filter+"%")
48 }
49
50 switch sortBy {
51 case "title":
52 query += ` ORDER BY title`
53 case "date":
54 query += ` ORDER BY created_at DESC`
55 case "duration":
56 query += ` ORDER BY duration DESC`
57 default:
58 query += ` ORDER BY created_at DESC`
59 }
60
61 rows, err := r.db.Query(query, args...)
62 if err != nil {
63 return nil, err
64 }
65 defer rows.Close()
66
67 var media []*models.Media
68 for rows.Next() {
69 m, err := scanMedia(rows)
70 if err != nil {
71 return nil, err
72 }
73 media = append(media, m)
74 }
75 return media, rows.Err()
76}
77
78func (r *MediaRepository) GetByRelativePath(relPath string) (*models.Media, error) {
79 row := r.db.QueryRow(
80 `SELECT id, url, title, description, duration, filepath, relative_path, is_audio, has_embedded_thumbnail, info_json_path, playlist_id, playlist_index, created_at
81 FROM media WHERE relative_path = ?`, relPath,
82 )
83 return scanMedia(row)
84}
85
86func (r *MediaRepository) Delete(id int64) error {
87 _, err := r.db.Exec(`DELETE FROM media WHERE id = ?`, id)
88 return err
89}
90
91func (r *MediaRepository) GetFolders(parentPath string) ([]string, error) {
92 rows, err := r.db.Query(
93 `SELECT relative_path FROM media WHERE relative_path LIKE ?`,
94 parentPath+"%",
95 )
96 if err != nil {
97 return nil, err
98 }
99 defer rows.Close()
100
101 folders := make(map[string]bool)
102 for rows.Next() {
103 var relPath string
104 if err := rows.Scan(&relPath); err != nil {
105 return nil, err
106 }
107 // Get the folder part after parentPath
108 remaining := strings.TrimPrefix(relPath, parentPath)
109 remaining = strings.TrimPrefix(remaining, "/")
110 if idx := strings.Index(remaining, "/"); idx >= 0 {
111 folder := remaining[:idx]
112 if folder != "" {
113 folders[parentPath+"/"+folder] = true
114 }
115 }
116 }
117
118 var result []string
119 for f := range folders {
120 result = append(result, f)
121 }
122 return result, rows.Err()
123}
124
125func scanMedia(row interface{ Scan(...interface{}) error }) (*models.Media, error) {
126 var m models.Media
127 var isAudio, hasThumb int
128 err := row.Scan(
129 &m.ID, &m.URL, &m.Title, &m.Description, &m.Duration, &m.Filepath,
130 &m.RelativePath, &isAudio, &hasThumb, &m.InfoJSONPath,
131 &m.PlaylistID, &m.PlaylistIndex, &m.CreatedAt,
132 )
133 if err != nil {
134 return nil, err
135 }
136 m.IsAudio = isAudio == 1
137 m.HasEmbeddedThumbnail = hasThumb == 1
138 return &m, nil
139}
140