diff --git a/assets/resources/examples/Screenshot 2019-05-21 at 10.45.52.png b/assets/resources/examples/Screenshot 2019-05-21 at 10.45.52.png new file mode 100644 index 000000000..0b935420b Binary files /dev/null and b/assets/resources/examples/Screenshot 2019-05-21 at 10.45.52.png differ diff --git a/frontend/src/pages/library/originals.vue b/frontend/src/pages/library/originals.vue index f64f2702a..21442e747 100644 --- a/frontend/src/pages/library/originals.vue +++ b/frontend/src/pages/library/originals.vue @@ -66,6 +66,18 @@ Index update + + + The index currently contains {{ config.count.hidden }} hidden files. Their format may not be supported, + they haven't been converted to JPEG yet or there are duplicates. + @@ -87,6 +99,7 @@ return { settings: new Settings(this.$config.settings()), readonly: this.$config.get("readonly"), + config: this.$config.values, started: false, busy: false, loading: false, diff --git a/internal/config/client.go b/internal/config/client.go index 9e115c969..ce9862216 100644 --- a/internal/config/client.go +++ b/internal/config/client.go @@ -59,6 +59,7 @@ func (c *Config) PublicClientConfig() ClientConfig { var count = struct { Photos uint `json:"photos"` + Hidden uint `json:"hidden"` Favorites uint `json:"favorites"` Private uint `json:"private"` Stories uint `json:"stories"` @@ -131,6 +132,7 @@ func (c *Config) ClientConfig() ClientConfig { var count = struct { Photos uint `json:"photos"` + Hidden uint `json:"hidden"` Favorites uint `json:"favorites"` Private uint `json:"private"` Stories uint `json:"stories"` @@ -141,7 +143,7 @@ func (c *Config) ClientConfig() ClientConfig { }{} db.Table("photos"). - Select("COUNT(*) AS photos, SUM(photo_favorite) AS favorites, SUM(photo_private) AS private, SUM(photo_story) AS stories"). + Select("SUM(photo_quality = -1) AS hidden, SUM(photo_quality >= 0) AS photos, SUM(photo_favorite) AS favorites, SUM(photo_private) AS private, SUM(photo_story) AS stories"). Where("deleted_at IS NULL"). Take(&count) diff --git a/internal/entity/file.go b/internal/entity/file.go index 5d34088fd..2b9a3e67b 100644 --- a/internal/entity/file.go +++ b/internal/entity/file.go @@ -35,8 +35,8 @@ type File struct { FileOrientation int FileAspectRatio float32 `gorm:"type:FLOAT;"` FileMainColor string `gorm:"type:varbinary(16);index;"` - FileColors string `gorm:"type:binary(9);"` - FileLuminance string `gorm:"type:binary(9);"` + FileColors string `gorm:"type:varbinary(9);"` + FileLuminance string `gorm:"type:varbinary(9);"` FileDiff uint32 FileChroma uint8 FileNotes string `gorm:"type:text"` diff --git a/internal/photoprism/colors_test.go b/internal/photoprism/colors_test.go index d635023fb..9406c91b0 100644 --- a/internal/photoprism/colors_test.go +++ b/internal/photoprism/colors_test.go @@ -52,6 +52,12 @@ func TestMediaFile_Colors_Testdata(t *testing.T) { Luminance: colors.LightMap{0x9, 0x5, 0xb, 0x6, 0x1, 0x6, 0xa, 0x1, 0x8}, Chroma: 20, }, + "Screenshot 2019-05-21 at 10.45.52.png": { + Colors: colors.Colors{}, + MainColor: 0, + Luminance: colors.LightMap{}, + Chroma: 0, + }, } if err := fastwalk.Walk(conf.ExamplesPath(), func(filename string, info os.FileMode) error { @@ -61,7 +67,12 @@ func TestMediaFile_Colors_Testdata(t *testing.T) { mediaFile, err := NewMediaFile(filename) - if err != nil || !mediaFile.IsJpeg() { + if err != nil { + t.Fatal(err) + } + + if !mediaFile.IsJpeg() { + t.Logf("not a jpeg: %s", filepath.Base(mediaFile.FileName())) return nil } diff --git a/internal/photoprism/index_mediafile.go b/internal/photoprism/index_mediafile.go index d06799ca3..c11231865 100644 --- a/internal/photoprism/index_mediafile.go +++ b/internal/photoprism/index_mediafile.go @@ -56,14 +56,16 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) ( start := time.Now() - var photo entity.Photo - var description entity.Description - var file, primaryFile entity.File - var metaData meta.Data var photoQuery, fileQuery *gorm.DB var locKeywords []string + file, primaryFile := entity.File{}, entity.File{} + + photo := entity.Photo{} + metaData := meta.Data{} + description := entity.Description{} labels := classify.Labels{} + fileBase := m.Base(ind.conf.Settings().Index.Group) filePath := m.RelativePath(ind.originalsPath()) fileName := m.RelativeName(ind.originalsPath()) @@ -120,6 +122,8 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) ( if photoExists { ind.db.Model(&photo).Related(&description) + } else { + photo.PhotoQuality = -1 } if fileHash == "" { @@ -377,7 +381,9 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) ( log.Warnf("%s (%s)", err.Error(), photo.PhotoUUID) } } else { - photo.PhotoQuality = photo.QualityScore() + if photo.PhotoQuality >= 0 { + photo.PhotoQuality = photo.QualityScore() + } if err := ind.db.Unscoped().Save(&photo).Error; err != nil { log.Errorf("index: %s", err) diff --git a/internal/photoprism/purge.go b/internal/photoprism/purge.go index 8d2c59007..e2ef2532d 100644 --- a/internal/photoprism/purge.go +++ b/internal/photoprism/purge.go @@ -163,6 +163,8 @@ func (prg *Purge) Start(opt PurgeOptions) (purgedFiles map[string]bool, purgedPh offset += limit } + err = q.ResetPhotosQuality() + return purgedFiles, purgedPhotos, err } diff --git a/internal/query/photo.go b/internal/query/photo.go index d63126ec0..15bc00bb3 100644 --- a/internal/query/photo.go +++ b/internal/query/photo.go @@ -469,3 +469,10 @@ func (q *Query) MissingPhotos(limit int, offset int) (entities []entity.Photo, e return entities, err } + +// ResetPhotosQuality resets the quality of photos without primary file to -1. +func (q *Query) ResetPhotosQuality() error { + return q.db.Table("photos"). + Where("id IN (SELECT photos.id FROM photos LEFT JOIN files ON photos.id = files.photo_id AND files.file_primary = 1 WHERE files.id IS NULL GROUP BY photos.id)"). + Update("photo_quality", -1).Error +} diff --git a/pkg/fs/base.go b/pkg/fs/base.go index 7780203c3..0f9f78d8f 100644 --- a/pkg/fs/base.go +++ b/pkg/fs/base.go @@ -2,6 +2,7 @@ package fs import ( "path/filepath" + "strconv" "strings" ) @@ -9,8 +10,8 @@ import ( func Base(fileName string, stripSequence bool) string { basename := filepath.Base(fileName) - if end := strings.Index(basename, "."); end != -1 { - // ignore everything behind the first dot in the file name + // strip file type extension + if end := strings.LastIndex(basename, "."); end != -1 { basename = basename[:end] } @@ -18,7 +19,14 @@ func Base(fileName string, stripSequence bool) string { return basename } - // common sequential naming schemes + // strip numeric extensions like .0000, .0001, .4542353245,.... + if dot := strings.LastIndex(basename, "."); dot != -1 { + if i, err := strconv.Atoi(basename[dot+1:]); err == nil && i >= 0 { + basename = basename[:dot] + } + } + + // other common sequential naming schemes if end := strings.Index(basename, " ("); end != -1 { // copies created by Chrome & Windows, example: IMG_1234 (2) basename = basename[:end] diff --git a/pkg/fs/base_test.go b/pkg/fs/base_test.go index 1f82f3218..f540e2ec0 100644 --- a/pkg/fs/base_test.go +++ b/pkg/fs/base_test.go @@ -7,13 +7,15 @@ import ( ) func TestBase(t *testing.T) { - t.Run("Test.jpg", func(t *testing.T) { - result := Base("/testdata/Test.jpg", true) - assert.Equal(t, "Test", result) + t.Run("Screenshot 2019-05-21 at 10.45.52.png", func(t *testing.T) { + regular := Base("Screenshot 2019-05-21 at 10.45.52.png", false) + assert.Equal(t, "Screenshot 2019-05-21 at 10.45.52", regular) + stripped := Base("Screenshot 2019-05-21 at 10.45.52.png", true) + assert.Equal(t, "Screenshot 2019-05-21 at 10.45", stripped) }) - t.Run("Test.3453453.jpg", func(t *testing.T) { - result := Base("/testdata/Test.3453453.jpg", true) + t.Run("Test.jpg", func(t *testing.T) { + result := Base("/testdata/Test.jpg", true) assert.Equal(t, "Test", result) }) @@ -33,8 +35,19 @@ func TestBase(t *testing.T) { }) t.Run("Test.3453453.jpg", func(t *testing.T) { - result := Base("/testdata/Test.3453453.jpg", false) - assert.Equal(t, "Test", result) + regular := Base("/testdata/Test.3453453.jpg", false) + assert.Equal(t, "Test.3453453", regular) + + stripped := Base("/testdata/Test.3453453.jpg", true) + assert.Equal(t, "Test", stripped) + }) + + t.Run("/foo/bar.0000.ZIP", func(t *testing.T) { + regular := Base("/foo/bar.0000.ZIP", false) + assert.Equal(t, "bar.0000", regular) + + stripped := Base("/foo/bar.0000.ZIP", true) + assert.Equal(t, "bar", stripped) }) t.Run("Test copy 3.jpg", func(t *testing.T) {