lens @ c2d666b43477ea7042b574ad940c508216cb0e83

  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		Order("created_at DESC").
143		Find(&medias)
144
145	if result.Error != nil {
146		return nil, result.Error
147	}
148
149	m := list.Map(medias, func(s *Media) *repository.Media {
150		return s.ToModel()
151	})
152
153	return m, nil
154}
155
156func (self *MediaRepository) Get(ctx context.Context, pathHash string) (*repository.Media, error) {
157	m := &Media{}
158	result := self.db.
159		WithContext(ctx).
160		Model(&Media{}).
161		Where("path_hash = ?", pathHash).
162		Limit(1).
163		Take(m)
164
165	if result.Error != nil {
166		return nil, result.Error
167	}
168
169	return m.ToModel(), nil
170}
171
172func (self *MediaRepository) GetPath(ctx context.Context, pathHash string) (string, error) {
173	var path string
174	result := self.db.
175		WithContext(ctx).
176		Model(&Media{}).
177		Select("path").
178		Where("path_hash = ?", pathHash).
179		Limit(1).
180		Find(&path)
181
182	if result.Error != nil {
183		return "", result.Error
184	}
185
186	return path, nil
187}
188
189func (self *MediaRepository) GetThumbnailPath(ctx context.Context, pathHash string) (string, error) {
190	var path string
191	result := self.db.
192		WithContext(ctx).
193		Model(&Media{}).
194		Select("media_thumbnails.path").
195		Joins("left join media_thumbnails on media.id = media_thumbnails.media_id").
196		Where("media.path_hash = ?", pathHash).
197		Limit(1).
198		Find(&path)
199
200	if result.Error != nil {
201		return "", result.Error
202	}
203
204	return path, nil
205}
206
207func (m *MediaRepository) GetEXIF(ctx context.Context, mediaID uint) (*repository.MediaEXIF, error) {
208	exif := &MediaEXIF{}
209	result := m.db.
210		WithContext(ctx).
211		Model(&Media{}).
212		Where("media_id = ?", mediaID).
213		Limit(1).
214		Take(m)
215
216	if result.Error != nil {
217		return nil, result.Error
218	}
219
220	return exif.ToModel(), nil
221}
222
223func (s *MediaRepository) CreateEXIF(ctx context.Context, id uint, info *repository.MediaEXIF) error {
224	media := &MediaEXIF{
225		MediaID:         id,
226		Width:           info.Width,
227		Height:          info.Height,
228		Description:     info.Description,
229		Camera:          info.Camera,
230		Maker:           info.Maker,
231		Lens:            info.Lens,
232		DateShot:        info.DateShot,
233		Exposure:        info.Exposure,
234		Aperture:        info.Aperture,
235		Iso:             info.Iso,
236		FocalLength:     info.FocalLength,
237		Flash:           info.Flash,
238		Orientation:     info.Orientation,
239		ExposureProgram: info.ExposureProgram,
240		GPSLatitude:     info.GPSLatitude,
241		GPSLongitude:    info.GPSLongitude,
242	}
243
244	result := s.db.
245		WithContext(ctx).
246		Create(media)
247	if result.Error != nil {
248		return result.Error
249	}
250
251	return nil
252}
253
254func (r *MediaRepository) ListEmptyEXIF(ctx context.Context, pagination *repository.Pagination) ([]*repository.Media, error) {
255	medias := make([]*Media, 0)
256	result := r.db.
257		WithContext(ctx).
258		Model(&Media{}).
259		Joins("left join media_exifs on media.id = media_exifs.media_id").
260		Where("media_exifs.media_id IS NULL").
261		Offset(pagination.Page * pagination.Size).
262		Limit(pagination.Size).
263		Order("media.created_at DESC").
264		Find(&medias)
265
266	if result.Error != nil {
267		return nil, result.Error
268	}
269
270	m := list.Map(medias, func(s *Media) *repository.Media {
271		return s.ToModel()
272	})
273
274	return m, nil
275}
276
277func (r *MediaRepository) ListEmptyThumbnail(ctx context.Context, pagination *repository.Pagination) ([]*repository.Media, error) {
278	medias := make([]*Media, 0)
279	result := r.db.
280		WithContext(ctx).
281		Model(&Media{}).
282		Joins("left join media_thumbnails on media.id = media_thumbnails.media_id").
283		Where("media_thumbnails.media_id IS NULL").
284		Offset(pagination.Page * pagination.Size).
285		Limit(pagination.Size).
286		Order("media.created_at DESC").
287		Find(&medias)
288
289	if result.Error != nil {
290		return nil, result.Error
291	}
292
293	m := list.Map(medias, func(s *Media) *repository.Media {
294		return s.ToModel()
295	})
296
297	return m, nil
298}
299
300func (m *MediaRepository) GetThumbnail(ctx context.Context, mediaID uint) (*repository.MediaThumbnail, error) {
301	thumbnail := &MediaThumbnail{}
302	result := m.db.
303		WithContext(ctx).
304		Model(&Media{}).
305		Where("media_id = ?", mediaID).
306		Limit(1).
307		Take(m)
308
309	if result.Error != nil {
310		return nil, result.Error
311	}
312
313	return thumbnail.ToModel(), nil
314}
315
316func (m *MediaRepository) CreateThumbnail(ctx context.Context, mediaID uint, thumbnail *repository.MediaThumbnail) error {
317	media := &MediaThumbnail{
318		MediaID: mediaID,
319		Path:    thumbnail.Path,
320	}
321
322	result := m.db.
323		WithContext(ctx).
324		Create(media)
325	if result.Error != nil {
326		return result.Error
327	}
328
329	return nil
330}