diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-03 19:17:04 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-03 19:17:04 +0300 |
| commit | 30c2b0fe8232cc748ab2bded6ab4d76febe32425 (patch) | |
| tree | 985578b326a1ff8fe7af395bf08471857376b8fc /internal/model | |
| parent | 687b86cdeef2192272f0945ddd496322a9113f9e (diff) | |
fix(admin): fix rescan goroutine lifecycle and race on shared ScanProgress
- Protect adminService scan state (cancel func + progress pointer) with sync.Mutex.
- Allocate fresh ScanProgress per trigger and pass it to the scanner,
eliminating races on the previously shared progress struct.
- Cancel previous scan context before starting a new one.
- Add tests for cancellation, fresh progress per scan, concurrent triggers,
and empty progress when never started.
- Fix race-prone tests by polling Running==true before waiting for completion.
Diffstat (limited to 'internal/model')
| -rw-r--r-- | internal/model/media.go | 61 |
1 files changed, 40 insertions, 21 deletions
diff --git a/internal/model/media.go b/internal/model/media.go index b1b4ef3..74492c1 100644 --- a/internal/model/media.go +++ b/internal/model/media.go @@ -9,6 +9,7 @@ type MediaType string const ( MediaTypeVideo MediaType = "video" MediaTypeAudio MediaType = "audio" + MediaTypeImage MediaType = "image" ) // Role defines the level of access a user has to a set. @@ -46,23 +47,32 @@ type SetPermission struct { CreatedAt time.Time `json:"created_at"` } -// Media represents a single audio or video file within a set. +// Media represents a single media file within a set. type Media struct { - ID int64 `json:"id"` - SetID int64 `json:"set_id"` - RelPath string `json:"rel_path"` - FileName string `json:"file_name"` - AbsPath string `json:"abs_path"` - Type MediaType `json:"type"` - Duration float64 `json:"duration"` - Codec string `json:"codec"` - Resolution string `json:"resolution"` - Bitrate int `json:"bitrate"` - FileSizeBytes int64 `json:"file_size_bytes"` - ThumbnailPath string `json:"thumbnail_path"` - PlayCount int `json:"play_count"` - DeletedAt *time.Time `json:"deleted_at"` - CreatedAt time.Time `json:"created_at"` + ID int64 `json:"id"` + SetID int64 `json:"set_id"` + RelPath string `json:"rel_path"` + FileName string `json:"file_name"` + AbsPath string `json:"abs_path"` + Type MediaType `json:"type"` + Duration float64 `json:"duration"` + Codec string `json:"codec"` + Resolution string `json:"resolution"` + Bitrate int `json:"bitrate"` + FileSizeBytes int64 `json:"file_size_bytes"` + Width int `json:"width"` + Height int `json:"height"` + EXIFCamera string `json:"exif_camera"` + EXIFLens string `json:"exif_lens"` + EXIFDate string `json:"exif_date"` + EXIFISO string `json:"exif_iso"` + EXIFFNumber string `json:"exif_f_number"` + EXIFExposure string `json:"exif_exposure"` + EXIFFocalLength string `json:"exif_focal_length"` + ThumbnailPath string `json:"thumbnail_path"` + PlayCount int `json:"play_count"` + DeletedAt *time.Time `json:"deleted_at"` + CreatedAt time.Time `json:"created_at"` } // Tag is a label that can be attached to media items. @@ -133,9 +143,18 @@ type MediaTag struct { // Metadata holds extracted file properties from ffprobe and os.Stat. type Metadata struct { - Duration float64 `json:"duration"` - Codec string `json:"codec"` - Resolution string `json:"resolution"` - Bitrate int `json:"bitrate"` - FileSizeBytes int64 `json:"file_size_bytes"` + Duration float64 `json:"duration"` + Codec string `json:"codec"` + Resolution string `json:"resolution"` + Bitrate int `json:"bitrate"` + FileSizeBytes int64 `json:"file_size_bytes"` + Width int `json:"width"` + Height int `json:"height"` + EXIFCamera string `json:"exif_camera"` + EXIFLens string `json:"exif_lens"` + EXIFDate string `json:"exif_date"` + EXIFISO string `json:"exif_iso"` + EXIFFNumber string `json:"exif_f_number"` + EXIFExposure string `json:"exif_exposure"` + EXIFFocalLength string `json:"exif_focal_length"` } |
