lens @ ff6b55491dec7ad1973dda37a35e2d9f47f75bcb

  1package sql
  2
  3import (
  4	"context"
  5	"time"
  6
  7	"gorm.io/gorm"
  8
  9	"git.sr.ht/~gabrielgio/img/pkg/database/repository"
 10	"git.sr.ht/~gabrielgio/img/pkg/list"
 11)
 12
 13type (
 14	Media struct {
 15		gorm.Model
 16		Name     string `gorm:"not null"`
 17		Path     string `gorm:"not null;unique"`
 18		PathHash string `gorm:"not null;unique"`
 19		MIMEType string `gorm:"not null"`
 20	}
 21
 22	MediaEXIF struct {
 23		gorm.Model
 24		Width           *float64
 25		Height          *float64
 26		MediaID         uint
 27		Media           Media
 28		Description     *string
 29		Camera          *string
 30		Maker           *string
 31		Lens            *string
 32		DateShot        *time.Time
 33		Exposure        *float64
 34		Aperture        *float64
 35		Iso             *int64
 36		FocalLength     *float64
 37		Flash           *int64
 38		Orientation     *int64
 39		ExposureProgram *int64
 40		GPSLatitude     *float64
 41		GPSLongitude    *float64
 42	}
 43
 44	MediaThumbnail struct {
 45		gorm.Model
 46		Path    string
 47		MediaID uint
 48		Media   Media
 49	}
 50
 51	MediaRepository struct {
 52		db *gorm.DB
 53	}
 54)
 55
 56var _ repository.MediaRepository = &MediaRepository{}
 57
 58func (self *Media) ToModel() *repository.Media {
 59	return &repository.Media{
 60		ID:       self.ID,
 61		Path:     self.Path,
 62		PathHash: self.PathHash,
 63		Name:     self.Name,
 64		MIMEType: self.MIMEType,
 65	}
 66}
 67
 68func (m *MediaEXIF) ToModel() *repository.MediaEXIF {
 69	return &repository.MediaEXIF{
 70		Height:          m.Height,
 71		Width:           m.Width,
 72		Description:     m.Description,
 73		Camera:          m.Camera,
 74		Maker:           m.Maker,
 75		Lens:            m.Lens,
 76		DateShot:        m.DateShot,
 77		Exposure:        m.Exposure,
 78		Aperture:        m.Aperture,
 79		Iso:             m.Iso,
 80		FocalLength:     m.FocalLength,
 81		Flash:           m.Flash,
 82		Orientation:     m.Orientation,
 83		ExposureProgram: m.ExposureProgram,
 84		GPSLatitude:     m.GPSLatitude,
 85		GPSLongitude:    m.GPSLongitude,
 86	}
 87}
 88
 89func (m *MediaThumbnail) ToModel() *repository.MediaThumbnail {
 90	return &repository.MediaThumbnail{
 91		Path: m.Path,
 92	}
 93}
 94
 95func NewMediaRepository(db *gorm.DB) *MediaRepository {
 96	return &MediaRepository{
 97		db: db,
 98	}
 99}
100
101func (self *MediaRepository) Create(ctx context.Context, createMedia *repository.CreateMedia) error {
102	media := &Media{
103		Name:     createMedia.Name,
104		Path:     createMedia.Path,
105		PathHash: createMedia.PathHash,
106		MIMEType: createMedia.MIMEType,
107	}
108
109	result := self.db.
110		WithContext(ctx).
111		Create(media)
112	if result.Error != nil {
113		return result.Error
114	}
115
116	return nil
117}
118
119func (self *MediaRepository) Exists(ctx context.Context, path string) (bool, error) {
120	var exists bool
121	result := self.db.
122		WithContext(ctx).
123		Model(&Media{}).
124		Select("count(id) > 0").
125		Where("path_hash = ?", path).
126		Find(&exists)
127
128	if result.Error != nil {
129		return false, result.Error
130	}
131
132	return exists, nil
133}
134
135func (self *MediaRepository) List(ctx context.Context, pagination *repository.Pagination) ([]*repository.Media, error) {
136	medias := make([]*Media, 0)
137	result := self.db.
138		WithContext(ctx).
139		Model(&Media{}).
140		Offset(pagination.Page * pagination.Size).
141		Limit(pagination.Size).
142		Where("path like '" + pagination.Path + "%'").
143		Order("created_at DESC").
144		Find(&medias)
145
146	if result.Error != nil {
147		return nil, result.Error
148	}
149
150	m := list.Map(medias, func(s *Media) *repository.Media {
151		return s.ToModel()
152	})
153
154	return m, nil
155}
156
157func (self *MediaRepository) Get(ctx context.Context, pathHash string) (*repository.Media, error) {
158	m := &Media{}
159	result := self.db.
160		WithContext(ctx).
161		Model(&Media{}).
162		Where("path_hash = ?", pathHash).
163		Limit(1).
164		Take(m)
165
166	if result.Error != nil {
167		return nil, result.Error
168	}
169
170	return m.ToModel(), nil
171}
172
173func (self *MediaRepository) GetPath(ctx context.Context, pathHash string) (string, error) {
174	var path string
175	result := self.db.
176		WithContext(ctx).
177		Model(&Media{}).
178		Select("path").
179		Where("path_hash = ?", pathHash).
180		Limit(1).
181		Find(&path)
182
183	if result.Error != nil {
184		return "", result.Error
185	}
186
187	return path, nil
188}
189
190func (self *MediaRepository) GetThumbnailPath(ctx context.Context, pathHash string) (string, error) {
191	var path string
192	result := self.db.
193		WithContext(ctx).
194		Model(&Media{}).
195		Select("media_thumbnails.path").
196		Joins("left join media_thumbnails on media.id = media_thumbnails.media_id").
197		Where("media.path_hash = ?", pathHash).
198		Limit(1).
199		Find(&path)
200
201	if result.Error != nil {
202		return "", result.Error
203	}
204
205	return path, nil
206}
207
208func (m *MediaRepository) GetEXIF(ctx context.Context, mediaID uint) (*repository.MediaEXIF, error) {
209	exif := &MediaEXIF{}
210	result := m.db.
211		WithContext(ctx).
212		Model(&Media{}).
213		Where("media_id = ?", mediaID).
214		Limit(1).
215		Take(m)
216
217	if result.Error != nil {
218		return nil, result.Error
219	}
220
221	return exif.ToModel(), nil
222}
223
224func (s *MediaRepository) CreateEXIF(ctx context.Context, id uint, info *repository.MediaEXIF) error {
225	media := &MediaEXIF{
226		MediaID:         id,
227		Width:           info.Width,
228		Height:          info.Height,
229		Description:     info.Description,
230		Camera:          info.Camera,
231		Maker:           info.Maker,
232		Lens:            info.Lens,
233		DateShot:        info.DateShot,
234		Exposure:        info.Exposure,
235		Aperture:        info.Aperture,
236		Iso:             info.Iso,
237		FocalLength:     info.FocalLength,
238		Flash:           info.Flash,
239		Orientation:     info.Orientation,
240		ExposureProgram: info.ExposureProgram,
241		GPSLatitude:     info.GPSLatitude,
242		GPSLongitude:    info.GPSLongitude,
243	}
244
245	result := s.db.
246		WithContext(ctx).
247		Create(media)
248	if result.Error != nil {
249		return result.Error
250	}
251
252	return nil
253}
254
255func (r *MediaRepository) ListEmptyEXIF(ctx context.Context, pagination *repository.Pagination) ([]*repository.Media, error) {
256	medias := make([]*Media, 0)
257	result := r.db.
258		WithContext(ctx).
259		Model(&Media{}).
260		Joins("left join media_exifs on media.id = media_exifs.media_id").
261		Where("media_exifs.media_id IS NULL AND media.path like '" + pagination.Path + "%'").
262		Offset(pagination.Page * pagination.Size).
263		Limit(pagination.Size).
264		Order("media.created_at DESC").
265		Find(&medias)
266
267	if result.Error != nil {
268		return nil, result.Error
269	}
270
271	m := list.Map(medias, func(s *Media) *repository.Media {
272		return s.ToModel()
273	})
274
275	return m, nil
276}
277
278func (r *MediaRepository) ListEmptyThumbnail(ctx context.Context, pagination *repository.Pagination) ([]*repository.Media, error) {
279	medias := make([]*Media, 0)
280	result := r.db.
281		WithContext(ctx).
282		Model(&Media{}).
283		Joins("left join media_thumbnails on media.id = media_thumbnails.media_id").
284		Where("media_thumbnails.media_id IS NULL AND media.path like '" + pagination.Path + "%'").
285		Offset(pagination.Page * pagination.Size).
286		Limit(pagination.Size).
287		Order("media.created_at DESC").
288		Find(&medias)
289
290	if result.Error != nil {
291		return nil, result.Error
292	}
293
294	m := list.Map(medias, func(s *Media) *repository.Media {
295		return s.ToModel()
296	})
297
298	return m, nil
299}
300
301func (m *MediaRepository) GetThumbnail(ctx context.Context, mediaID uint) (*repository.MediaThumbnail, error) {
302	thumbnail := &MediaThumbnail{}
303	result := m.db.
304		WithContext(ctx).
305		Model(&Media{}).
306		Where("media_id = ?", mediaID).
307		Limit(1).
308		Take(m)
309
310	if result.Error != nil {
311		return nil, result.Error
312	}
313
314	return thumbnail.ToModel(), nil
315}
316
317func (m *MediaRepository) CreateThumbnail(ctx context.Context, mediaID uint, thumbnail *repository.MediaThumbnail) error {
318	media := &MediaThumbnail{
319		MediaID: mediaID,
320		Path:    thumbnail.Path,
321	}
322
323	result := m.db.
324		WithContext(ctx).
325		Create(media)
326	if result.Error != nil {
327		return result.Error
328	}
329
330	return nil
331}