diff --git a/Dockerfile b/Dockerfile index a7228a840..d232a799f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM photoprism/development:20200715 +FROM photoprism/development:20200721 # Set up project directory WORKDIR "/go/src/github.com/photoprism/photoprism" diff --git a/assets/examples/earth.avi b/assets/examples/earth.avi new file mode 100644 index 000000000..cc5ef6a7f Binary files /dev/null and b/assets/examples/earth.avi differ diff --git a/assets/examples/earth.mov b/assets/examples/earth.mov new file mode 100644 index 000000000..28c89a1cd Binary files /dev/null and b/assets/examples/earth.mov differ diff --git a/docker/photoprism/Dockerfile b/docker/photoprism/Dockerfile index 501f514be..333db5596 100644 --- a/docker/photoprism/Dockerfile +++ b/docker/photoprism/Dockerfile @@ -1,4 +1,4 @@ -FROM photoprism/development:20200715 as build +FROM photoprism/development:20200721 as build # Set up project directory WORKDIR "/go/src/github.com/photoprism/photoprism" diff --git a/docker/photoprism/arm64/Dockerfile b/docker/photoprism/arm64/Dockerfile index 2fc9a5522..83a3309c2 100644 --- a/docker/photoprism/arm64/Dockerfile +++ b/docker/photoprism/arm64/Dockerfile @@ -41,6 +41,7 @@ RUN apt-get update && apt-get upgrade && \ make \ wget \ git \ + gettext \ tzdata \ gconf-service diff --git a/go.mod b/go.mod index 9e5fc3976..3bbbc4208 100644 --- a/go.mod +++ b/go.mod @@ -18,13 +18,13 @@ require ( github.com/dsoprea/go-utility v0.0.0-20200717064901-2fccff4aa15e // indirect github.com/dustin/go-humanize v1.0.0 github.com/gin-gonic/gin v1.6.3 - github.com/go-errors/errors v1.1.1 // indirect github.com/go-playground/validator/v10 v10.3.0 // indirect github.com/golang/geo v0.0.0-20200319012246-673a6f80352d github.com/golang/protobuf v1.4.2 // indirect github.com/google/open-location-code/go v0.0.0-20200603075809-e28188e71340 github.com/gorilla/websocket v1.4.2 github.com/gosimple/slug v1.9.0 + github.com/h2non/filetype v1.1.0 github.com/jinzhu/gorm v1.9.14 github.com/jinzhu/inflection v1.0.0 github.com/json-iterator/go v1.1.10 // indirect diff --git a/go.sum b/go.sum index 42dcb424b..4af9c8db7 100644 --- a/go.sum +++ b/go.sum @@ -46,29 +46,17 @@ github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4 h1:bVaiYo8amn7L github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E= github.com/dsoprea/go-exif/v2 v2.0.0-20200520183328-015129a9efd5/go.mod h1:9EXlPeHfblFFnwu5UOqmP2eoZfJyAZ2Ri/Vki33ajO0= github.com/dsoprea/go-exif/v2 v2.0.0-20200604193436-ca8584a0e1c4/go.mod h1:9EXlPeHfblFFnwu5UOqmP2eoZfJyAZ2Ri/Vki33ajO0= -github.com/dsoprea/go-exif/v2 v2.0.0-20200717063959-46b1a0cd1772 h1:M49UNOTa5sLju107lAoMsm93B/fHD02vWIoskmXMBm8= -github.com/dsoprea/go-exif/v2 v2.0.0-20200717063959-46b1a0cd1772/go.mod h1:oKrjk2kb3rAR5NbtSTLUMvMSbc+k8ZosI3MaVH47noc= -github.com/dsoprea/go-exif/v2 v2.0.0-20200717071058-9393e7afd446 h1:ruDG+2wFz+k/mDNy8x1UqWEItWNLXpvGlLv05+TlZt4= -github.com/dsoprea/go-exif/v2 v2.0.0-20200717071058-9393e7afd446/go.mod h1:oKrjk2kb3rAR5NbtSTLUMvMSbc+k8ZosI3MaVH47noc= github.com/dsoprea/go-exif/v2 v2.0.0-20200721051611-3a80916d1fb8 h1:CDv1mPzK6J1vdCeXGoVdFKJqKKTTfnbsPIu9VcWkurM= github.com/dsoprea/go-exif/v2 v2.0.0-20200721051611-3a80916d1fb8/go.mod h1:oKrjk2kb3rAR5NbtSTLUMvMSbc+k8ZosI3MaVH47noc= github.com/dsoprea/go-exif/v3 v3.0.0-20200717053412-08f1b6708903/go.mod h1:0nsO1ce0mh5czxGeLo4+OCZ/C6Eo6ZlMWsz7rH/Gxv8= -github.com/dsoprea/go-exif/v3 v3.0.0-20200717063959-46b1a0cd1772 h1:l/wfrK3wEH7sYpJe+Y8ZdFJW3AmsDgPoAQq2RLgKPSQ= -github.com/dsoprea/go-exif/v3 v3.0.0-20200717063959-46b1a0cd1772/go.mod h1:0nsO1ce0mh5czxGeLo4+OCZ/C6Eo6ZlMWsz7rH/Gxv8= -github.com/dsoprea/go-exif/v3 v3.0.0-20200717071058-9393e7afd446 h1:96yylb+JH415u6V7ykNtnEBLaZUwS1S31TnAezcvnNE= -github.com/dsoprea/go-exif/v3 v3.0.0-20200717071058-9393e7afd446/go.mod h1:cg5SNYKHMmzxsr9X6ZeLh/nfBRHHp5PngtEPcujONtk= github.com/dsoprea/go-exif/v3 v3.0.0-20200721051611-3a80916d1fb8 h1:xMu5i9w1hAuS+0v7pw44wd4Y7eJoxfYiCkRQnEEvGd4= github.com/dsoprea/go-exif/v3 v3.0.0-20200721051611-3a80916d1fb8/go.mod h1:cg5SNYKHMmzxsr9X6ZeLh/nfBRHHp5PngtEPcujONtk= -github.com/dsoprea/go-heic-exif-extractor v0.0.0-20200520190950-3ae4ff88a0d1 h1:8Tbo+OYgg7i2G3fltmpWq1if1e752aMX7Zv/sNWWJUk= -github.com/dsoprea/go-heic-exif-extractor v0.0.0-20200520190950-3ae4ff88a0d1/go.mod h1:UwRKreeVikXn5OarSnt4OqovcEjsIgZVuc5svj7G5w4= github.com/dsoprea/go-heic-exif-extractor v0.0.0-20200717090456-b3d9dcddffd1 h1:R/EEzpxqQxeEcJ/z0EFTI1U6XsuOnepyp5o1uZg5c2E= github.com/dsoprea/go-heic-exif-extractor v0.0.0-20200717090456-b3d9dcddffd1/go.mod h1:UwRKreeVikXn5OarSnt4OqovcEjsIgZVuc5svj7G5w4= github.com/dsoprea/go-iptc v0.0.0-20200609062250-162ae6b44feb h1:gwjJjUr6FY7zAWVEueFPrcRHhd9+IK81TcItbqw2du4= github.com/dsoprea/go-iptc v0.0.0-20200609062250-162ae6b44feb/go.mod h1:kYIdx9N9NaOyD7U6D+YtExN7QhRm+5kq7//yOsRXQtM= github.com/dsoprea/go-iptc v0.0.0-20200610044640-bc9ca208b413 h1:YDRiMEm32T60Kpm35YzOK9ZHgjsS1Qrid+XskNcsdp8= github.com/dsoprea/go-iptc v0.0.0-20200610044640-bc9ca208b413/go.mod h1:kYIdx9N9NaOyD7U6D+YtExN7QhRm+5kq7//yOsRXQtM= -github.com/dsoprea/go-jpeg-image-structure v0.0.0-20200615034914-d40a386309d2 h1:8HmMqu64P4ZDGtcVwZDfmS4xuLXYjf2iery8teY7d9c= -github.com/dsoprea/go-jpeg-image-structure v0.0.0-20200615034914-d40a386309d2/go.mod h1:ZoOP3yUG0HD1T4IUjIFsz/2OAB2yB4YX6NSm4K+uJRg= github.com/dsoprea/go-jpeg-image-structure v0.0.0-20200717085400-dd2ba56ee6b8 h1:cXCR9FOOkTEZ3t+asmy3lLv2AKYAah2igfx7WnNnVMc= github.com/dsoprea/go-jpeg-image-structure v0.0.0-20200717085400-dd2ba56ee6b8/go.mod h1:ZoOP3yUG0HD1T4IUjIFsz/2OAB2yB4YX6NSm4K+uJRg= github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696 h1:VGFnZAcLwPpt1sHlAxml+pGLZz9A2s+K/s1YNhPC91Y= @@ -175,6 +163,8 @@ github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0U github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gosimple/slug v1.9.0 h1:r5vDcYrFz9BmfIAMC829un9hq7hKM4cHUrsv36LbEqs= github.com/gosimple/slug v1.9.0/go.mod h1:AMZ+sOVe65uByN3kgEyf9WEBKBCSS+dJjMX9x4vDJbg= +github.com/h2non/filetype v1.1.0 h1:Or/gjocJrJRNK/Cri/TDEKFjAR+cfG6eK65NGYB6gBA= +github.com/h2non/filetype v1.1.0/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= diff --git a/internal/config/test.go b/internal/config/test.go index 126976d3a..3baba479b 100644 --- a/internal/config/test.go +++ b/internal/config/test.go @@ -61,8 +61,6 @@ func NewTestParams() *Params { DetectNSFW: true, UploadNSFW: false, SidecarPath: fs.HiddenPath, - DarktableBin: "/usr/bin/darktable-cli", - ExifToolBin: "/usr/bin/exiftool", AssetsPath: assetsPath, StoragePath: testDataPath, CachePath: testDataPath + "/cache", diff --git a/internal/entity/entity_test.go b/internal/entity/entity_test.go index 608a1c7ca..f9bd681ec 100644 --- a/internal/entity/entity_test.go +++ b/internal/entity/entity_test.go @@ -11,6 +11,10 @@ func TestMain(m *testing.M) { log = logrus.StandardLogger() log.SetLevel(logrus.DebugLevel) + if err := os.Remove(".test.db"); err == nil { + log.Debugln("removed .test.db") + } + db := InitTestDb(os.Getenv("PHOTOPRISM_TEST_DRIVER"), os.Getenv("PHOTOPRISM_TEST_DSN")) defer db.Close() diff --git a/internal/photoprism/filename_test.go b/internal/photoprism/filename_test.go index 5c166c59e..41b1ed5d4 100644 --- a/internal/photoprism/filename_test.go +++ b/internal/photoprism/filename_test.go @@ -1,19 +1,21 @@ package photoprism import ( + "github.com/photoprism/photoprism/internal/config" "github.com/stretchr/testify/assert" "testing" ) func TestFileName(t *testing.T) { + conf := config.TestConfig() t.Run("sidecar", func(t *testing.T) { assert.Equal(t, ".photoprism/test.jpg", FileName("sidecar", "test.jpg")) }) t.Run("import", func(t *testing.T) { - assert.Equal(t, "/go/src/github.com/photoprism/photoprism/storage/testdata/import/test.jpg", FileName("import", "test.jpg")) + assert.Equal(t, conf.ImportPath()+"/test.jpg", FileName("import", "test.jpg")) }) t.Run("examples", func(t *testing.T) { - assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/examples/test.jpg", FileName("examples", "test.jpg")) + assert.Equal(t, conf.ExamplesPath()+"/test.jpg", FileName("examples", "test.jpg")) }) } diff --git a/internal/photoprism/mediafile.go b/internal/photoprism/mediafile.go index 2c6e3ace1..1ba9c1692 100644 --- a/internal/photoprism/mediafile.go +++ b/internal/photoprism/mediafile.go @@ -294,32 +294,44 @@ func (m *MediaFile) RelatedFiles(stripSequence bool) (result RelatedFiles, err e matches = append(matches, name) } - for _, filename := range matches { - resultFile, err := NewMediaFile(filename) + for _, fileName := range matches { + f, err := NewMediaFile(fileName) if err != nil { + log.Warnf("mediafile: %s in %s", err, txt.Quote(filepath.Base(fileName))) continue } - if result.Main == nil && resultFile.IsJpeg() { - result.Main = resultFile - } else if resultFile.IsRaw() { - result.Main = resultFile - } else if resultFile.IsHEIF() { - result.Main = resultFile - } else if resultFile.IsJpeg() && len(result.Main.FileName()) > len(resultFile.FileName()) { - result.Main = resultFile - } else if resultFile.IsImageOther() { - result.Main = resultFile - } else if resultFile.IsVideo() { - result.Main = resultFile + if f.FileSize() == 0 { + log.Warnf("mediafile: %s is empty", txt.Quote(filepath.Base(fileName))) + continue } - result.Files = append(result.Files, resultFile) + if result.Main == nil && f.IsJpeg() { + result.Main = f + } else if f.IsRaw() { + result.Main = f + } else if f.IsHEIF() { + result.Main = f + } else if f.IsJpeg() && len(result.Main.FileName()) > len(f.FileName()) { + result.Main = f + } else if f.IsImageOther() { + result.Main = f + } else if f.IsVideo() { + result.Main = f + } + + result.Files = append(result.Files, f) } - if result.Main == nil { - return result, fmt.Errorf("no main file found for %s", txt.Quote(m.BaseName())) + if len(result.Files) == 0 || result.Main == nil { + t := m.MimeType() + + if t == "" { + t = "unknown type" + } + + return result, fmt.Errorf("no supported files found for %s (%s)", txt.Quote(m.BaseName()), t) } // Add hidden JPEG if exists. @@ -622,11 +634,26 @@ func (m *MediaFile) IsGif() bool { return m.MimeType() == fs.MimeTypeGif } +// IsTiff returns true if this is a TIFF file. +func (m *MediaFile) IsTiff() bool { + return m.HasFileType(fs.TypeTiff) && m.MimeType() == fs.MimeTypeTiff +} + +// IsHEIF returns true if this is a High Efficiency Image File Format file. +func (m *MediaFile) IsHEIF() bool { + return m.MimeType() == fs.MimeTypeHEIF +} + // IsBitmap returns true if this is a bitmap file. func (m *MediaFile) IsBitmap() bool { return m.MimeType() == fs.MimeTypeBitmap } +// IsVideo returns true if this is a video file. +func (m *MediaFile) IsVideo() bool { + return strings.HasPrefix(m.MimeType(), "video/") +} + // IsJson return true if this media file is a json sidecar file. func (m *MediaFile) IsJson() bool { return m.HasFileType(fs.TypeJson) @@ -641,6 +668,8 @@ func (m *MediaFile) FileType() fs.FileType { return fs.TypePng case m.IsGif(): return fs.TypeGif + case m.IsHEIF(): + return fs.TypeHEIF case m.IsBitmap(): return fs.TypeBitmap default: @@ -667,11 +696,6 @@ func (m *MediaFile) IsRaw() bool { return m.HasFileType(fs.TypeRaw) } -// IsTiff returns true if this is a TIFF file. -func (m *MediaFile) IsTiff() bool { - return m.HasFileType(fs.TypeTiff) -} - // IsImageOther returns true if this is a PNG, GIF, BMP or TIFF file. func (m *MediaFile) IsImageOther() bool { switch { @@ -682,11 +706,6 @@ func (m *MediaFile) IsImageOther() bool { } } -// IsHEIF returns true if this is a High Efficiency Image File Format file. -func (m *MediaFile) IsHEIF() bool { - return m.HasFileType(fs.TypeHEIF) -} - // IsXMP returns true if this is a XMP sidecar file. func (m *MediaFile) IsXMP() bool { return m.FileType() == fs.TypeXMP @@ -697,14 +716,9 @@ func (m *MediaFile) IsSidecar() bool { return m.MediaType() == fs.MediaSidecar } -// IsVideo returns true if this is a video file. -func (m *MediaFile) IsVideo() bool { - return m.MediaType() == fs.MediaVideo -} - // IsPlayableVideo returns true if this is a supported video file format. func (m *MediaFile) IsPlayableVideo() bool { - return m.MediaType() == fs.MediaVideo && m.HasFileType(fs.TypeMP4) + return m.IsVideo() && m.HasFileType(fs.TypeMP4) } // IsPhoto returns true if this file is a photo / image. diff --git a/internal/photoprism/mediafile_test.go b/internal/photoprism/mediafile_test.go index 5454deef1..57d691bd4 100644 --- a/internal/photoprism/mediafile_test.go +++ b/internal/photoprism/mediafile_test.go @@ -3,6 +3,7 @@ package photoprism import ( "os" "path/filepath" + "strings" "testing" "github.com/photoprism/photoprism/internal/config" @@ -522,6 +523,8 @@ func TestMediaFile_RelatedFiles(t *testing.T) { t.Fatalf("length is %d, should be 4", len(related.Files)) } + t.Logf("FILE: %s, %s", related.Main.FileType(), related.Main.MimeType()) + assert.Equal(t, "2015-02-04.jpg", related.Main.BaseName()) assert.Equal(t, "2015-02-04.jpg", related.Files[0].BaseName()) @@ -650,12 +653,12 @@ func TestMediaFile_RelName(t *testing.T) { } t.Run("directory with end slash", func(t *testing.T) { - filename := mediaFile.RelName("/go/src/github.com/photoprism/photoprism/assets/") + filename := mediaFile.RelName(conf.AssetsPath()) assert.Equal(t, "examples/tree_white.jpg", filename) }) t.Run("directory without end slash", func(t *testing.T) { - filename := mediaFile.RelName("/go/src/github.com/photoprism/photoprism/assets") + filename := mediaFile.RelName(conf.AssetsPath()) assert.Equal(t, "examples/tree_white.jpg", filename) }) t.Run("directory not part of filename", func(t *testing.T) { @@ -663,7 +666,7 @@ func TestMediaFile_RelName(t *testing.T) { assert.Equal(t, conf.ExamplesPath()+"/tree_white.jpg", filename) }) t.Run("directory equals example path", func(t *testing.T) { - filename := mediaFile.RelName("/go/src/github.com/photoprism/photoprism/assets/examples") + filename := mediaFile.RelName(conf.ExamplesPath()) assert.Equal(t, "tree_white.jpg", filename) }) } @@ -679,11 +682,11 @@ func TestMediaFile_RelativePath(t *testing.T) { } t.Run("directory with end slash", func(t *testing.T) { - path := mediaFile.RelPath("/go/src/github.com/photoprism/photoprism/assets/") + path := mediaFile.RelPath(conf.AssetsPath() + "/") assert.Equal(t, "examples", path) }) t.Run("directory without end slash", func(t *testing.T) { - path := mediaFile.RelPath("/go/src/github.com/photoprism/photoprism/assets") + path := mediaFile.RelPath(conf.AssetsPath()) assert.Equal(t, "examples", path) }) t.Run("directory equals filepath", func(t *testing.T) { @@ -692,7 +695,7 @@ func TestMediaFile_RelativePath(t *testing.T) { }) t.Run("directory does not match filepath", func(t *testing.T) { path := mediaFile.RelPath("xxx") - assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/examples", path) + assert.Equal(t, conf.ExamplesPath(), path) }) mediaFile, err = NewMediaFile(conf.ExamplesPath() + "/.photoprism/example.jpg") @@ -724,15 +727,15 @@ func TestMediaFile_RelativeBasename(t *testing.T) { } t.Run("directory with end slash", func(t *testing.T) { - basename := mediaFile.RelPrefix("/go/src/github.com/photoprism/photoprism/assets/", true) + basename := mediaFile.RelPrefix(conf.AssetsPath()+"/", true) assert.Equal(t, "examples/tree_white", basename) }) t.Run("directory without end slash", func(t *testing.T) { - basename := mediaFile.RelPrefix("/go/src/github.com/photoprism/photoprism/assets", true) + basename := mediaFile.RelPrefix(conf.AssetsPath(), true) assert.Equal(t, "examples/tree_white", basename) }) t.Run("directory equals example path", func(t *testing.T) { - basename := mediaFile.RelPrefix("/go/src/github.com/photoprism/photoprism/assets/examples/", true) + basename := mediaFile.RelPrefix(conf.ExamplesPath(), true) assert.Equal(t, "tree_white", basename) }) @@ -796,7 +799,7 @@ func TestMediaFile_MimeType(t *testing.T) { if err != nil { t.Fatal(err) } - assert.Equal(t, "application/octet-stream", mediaFile.MimeType()) + assert.Equal(t, "image/tiff", mediaFile.MimeType()) }) @@ -805,7 +808,7 @@ func TestMediaFile_MimeType(t *testing.T) { if err != nil { t.Fatal(err) } - assert.Equal(t, "text/plain; charset=utf-8", mediaFile.MimeType()) + assert.Equal(t, "", mediaFile.MimeType()) }) t.Run("iphone_7.json", func(t *testing.T) { @@ -813,7 +816,7 @@ func TestMediaFile_MimeType(t *testing.T) { if err != nil { t.Fatal(err) } - assert.Equal(t, "text/plain; charset=utf-8", mediaFile.MimeType()) + assert.Equal(t, "", mediaFile.MimeType()) }) t.Run("iphone_7.heic", func(t *testing.T) { @@ -821,7 +824,7 @@ func TestMediaFile_MimeType(t *testing.T) { if err != nil { t.Fatal(err) } - assert.Equal(t, "application/octet-stream", mediaFile.MimeType()) + assert.Equal(t, "image/heif", mediaFile.MimeType()) }) t.Run("IMG_4120.AAE", func(t *testing.T) { @@ -829,7 +832,31 @@ func TestMediaFile_MimeType(t *testing.T) { if err != nil { t.Fatal(err) } - assert.Equal(t, "text/xml; charset=utf-8", mediaFile.MimeType()) + assert.Equal(t, "", mediaFile.MimeType()) + }) + + t.Run("earth.mov", func(t *testing.T) { + if f, err := NewMediaFile(filepath.Join(conf.ExamplesPath(), "earth.mov")); err != nil { + t.Fatal(err) + } else { + assert.Equal(t, "video/quicktime", f.MimeType()) + } + }) + + t.Run("blue-go-video.mp4", func(t *testing.T) { + if f, err := NewMediaFile(filepath.Join(conf.ExamplesPath(), "blue-go-video.mp4")); err != nil { + t.Fatal(err) + } else { + assert.Equal(t, "video/mp4", f.MimeType()) + } + }) + + t.Run("earth.avi", func(t *testing.T) { + if f, err := NewMediaFile(filepath.Join(conf.ExamplesPath(), "earth.avi")); err != nil { + t.Fatal(err) + } else { + assert.Equal(t, "video/x-msvideo", f.MimeType()) + } }) } @@ -1087,6 +1114,7 @@ func TestMediaFile_IsRaw(t *testing.T) { if err != nil { t.Fatal(err) } + assert.Equal(t, true, mediaFile.IsRaw()) }) t.Run("/elephants.jpg", func(t *testing.T) { @@ -1134,7 +1162,7 @@ func TestMediaFile_IsTiff(t *testing.T) { t.Fatal(err) } assert.Equal(t, fs.TypeJson, mediaFile.FileType()) - assert.Equal(t, "text/plain; charset=utf-8", mediaFile.MimeType()) + assert.Equal(t, "", mediaFile.MimeType()) assert.Equal(t, false, mediaFile.IsTiff()) }) t.Run("/purple.tiff", func(t *testing.T) { @@ -1145,7 +1173,7 @@ func TestMediaFile_IsTiff(t *testing.T) { t.Fatal(err) } assert.Equal(t, fs.TypeTiff, mediaFile.FileType()) - assert.Equal(t, "application/octet-stream", mediaFile.MimeType()) + assert.Equal(t, "image/tiff", mediaFile.MimeType()) assert.Equal(t, true, mediaFile.IsTiff()) }) t.Run("/example.tiff", func(t *testing.T) { @@ -1156,7 +1184,7 @@ func TestMediaFile_IsTiff(t *testing.T) { t.Fatal(err) } assert.Equal(t, fs.TypeTiff, mediaFile.FileType()) - assert.Equal(t, "application/octet-stream", mediaFile.MimeType()) + assert.Equal(t, "image/tiff", mediaFile.MimeType()) assert.Equal(t, true, mediaFile.IsTiff()) }) } @@ -1285,7 +1313,7 @@ func TestMediaFile_IsSidecar(t *testing.T) { } func TestMediaFile_IsPhoto(t *testing.T) { - t.Run("/iphone_7.json", func(t *testing.T) { + t.Run("iphone_7.json", func(t *testing.T) { conf := config.TestConfig() mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/iphone_7.json") @@ -1294,14 +1322,14 @@ func TestMediaFile_IsPhoto(t *testing.T) { } assert.Equal(t, false, mediaFile.IsPhoto()) }) - t.Run("/iphone_7.xmp", func(t *testing.T) { + t.Run("iphone_7.xmp", func(t *testing.T) { conf := config.TestConfig() mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/iphone_7.xmp") assert.Nil(t, err) assert.Equal(t, false, mediaFile.IsPhoto()) }) - t.Run("/iphone_7.heic", func(t *testing.T) { + t.Run("iphone_7.heic", func(t *testing.T) { conf := config.TestConfig() mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/iphone_7.heic") @@ -1310,7 +1338,7 @@ func TestMediaFile_IsPhoto(t *testing.T) { } assert.Equal(t, true, mediaFile.IsPhoto()) }) - t.Run("/canon_eos_6d.dng", func(t *testing.T) { + t.Run("canon_eos_6d.dng", func(t *testing.T) { conf := config.TestConfig() mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/canon_eos_6d.dng") @@ -1319,7 +1347,7 @@ func TestMediaFile_IsPhoto(t *testing.T) { } assert.Equal(t, true, mediaFile.IsPhoto()) }) - t.Run("/elephants.jpg", func(t *testing.T) { + t.Run("elephants.jpg", func(t *testing.T) { conf := config.TestConfig() mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/elephants.jpg") @@ -1329,25 +1357,37 @@ func TestMediaFile_IsPhoto(t *testing.T) { } func TestMediaFile_IsVideo(t *testing.T) { - t.Run("/christmas.mp4", func(t *testing.T) { - conf := config.TestConfig() + conf := config.TestConfig() - mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/christmas.mp4") - if err != nil { + t.Run("christmas.mp4", func(t *testing.T) { + if f, err := NewMediaFile(filepath.Join(conf.ExamplesPath(), "christmas.mp4")); err != nil { t.Fatal(err) + } else { + assert.Equal(t, false, f.IsPhoto()) + assert.Equal(t, true, f.IsVideo()) + assert.Equal(t, false, f.IsJson()) + assert.Equal(t, false, f.IsSidecar()) } - assert.Equal(t, false, mediaFile.IsPhoto()) }) - t.Run("/canon_eos_6d.dng", func(t *testing.T) { - conf := config.TestConfig() - - mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/canon_eos_6d.dng") - - if err != nil { + t.Run("canon_eos_6d.dng", func(t *testing.T) { + if f, err := NewMediaFile(filepath.Join(conf.ExamplesPath(), "canon_eos_6d.dng")); err != nil { t.Fatal(err) + } else { + assert.Equal(t, true, f.IsPhoto()) + assert.Equal(t, false, f.IsVideo()) + assert.Equal(t, false, f.IsJson()) + assert.Equal(t, false, f.IsSidecar()) + } + }) + t.Run("iphone_7.json", func(t *testing.T) { + if f, err := NewMediaFile(filepath.Join(conf.ExamplesPath(), "iphone_7.json")); err != nil { + t.Fatal(err) + } else { + assert.Equal(t, false, f.IsPhoto()) + assert.Equal(t, false, f.IsVideo()) + assert.Equal(t, true, f.IsJson()) + assert.Equal(t, true, f.IsSidecar()) } - - assert.Equal(t, true, mediaFile.IsPhoto()) }) } @@ -1844,7 +1884,7 @@ func TestMediaFile_JsonName(t *testing.T) { } name := mediaFile.JsonName() - assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/examples/blue-go-video.mp4.json", name) + assert.True(t, strings.HasSuffix(name, "/assets/examples/blue-go-video.mp4.json")) }) } @@ -1941,7 +1981,7 @@ func TestMediaFile_SubDirectory(t *testing.T) { } subdir := mediaFile.SubDir("xxx") - assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/examples/xxx", subdir) + assert.True(t, strings.HasSuffix(subdir, "/assets/examples/xxx")) }) } diff --git a/internal/photoprism/photoprism_test.go b/internal/photoprism/photoprism_test.go index 03d1c7a9e..4027e9545 100644 --- a/internal/photoprism/photoprism_test.go +++ b/internal/photoprism/photoprism_test.go @@ -12,6 +12,10 @@ func TestMain(m *testing.M) { log = logrus.StandardLogger() log.SetLevel(logrus.DebugLevel) + if err := os.Remove(".test.db"); err == nil { + log.Debugln("removed .test.db") + } + c := config.TestConfig() SetConfig(c) diff --git a/internal/photoprism/resample_test.go b/internal/photoprism/resample_test.go index f59531cd4..f70d53929 100644 --- a/internal/photoprism/resample_test.go +++ b/internal/photoprism/resample_test.go @@ -2,6 +2,7 @@ package photoprism import ( "os" + "strings" "testing" "github.com/disintegration/imaging" @@ -60,11 +61,16 @@ func TestThumb_Filename(t *testing.T) { t.Run("", func(t *testing.T) { filename, err := thumb.Filename("99988", thumbsPath, 150, 150, thumb.ResampleFit, thumb.ResampleNearestNeighbor) - assert.Nil(t, err) - assert.Equal(t, "/go/src/github.com/photoprism/photoprism/storage/testdata/cache/_tmp/9/9/9/99988_150x150_fit.jpg", filename) + + if err != nil { + t.Fatal(err) + } + + assert.True(t, strings.HasSuffix(filename, "/storage/testdata/cache/_tmp/9/9/9/99988_150x150_fit.jpg")) }) t.Run("hash too short", func(t *testing.T) { _, err := thumb.Filename("999", thumbsPath, 150, 150, thumb.ResampleFit, thumb.ResampleNearestNeighbor) + if err == nil { t.FailNow() } diff --git a/internal/photoprism/testdata/2018-04-12 19:24:49.mov b/internal/photoprism/testdata/2018-04-12 19:24:49.mov new file mode 100644 index 000000000..e69de29bb diff --git a/internal/query/query_test.go b/internal/query/query_test.go index 4ada7a01f..8a46902ce 100644 --- a/internal/query/query_test.go +++ b/internal/query/query_test.go @@ -13,6 +13,10 @@ func TestMain(m *testing.M) { log = logrus.StandardLogger() log.SetLevel(logrus.DebugLevel) + if err := os.Remove(".test.db"); err == nil { + log.Debugln("removed .test.db") + } + db := entity.InitTestDb(os.Getenv("PHOTOPRISM_TEST_DRIVER"), os.Getenv("PHOTOPRISM_TEST_DSN")) defer db.Close() diff --git a/internal/workers/workers_test.go b/internal/workers/workers_test.go index 2f6faab74..b6e4422b6 100644 --- a/internal/workers/workers_test.go +++ b/internal/workers/workers_test.go @@ -12,6 +12,10 @@ func TestMain(m *testing.M) { log = logrus.StandardLogger() log.SetLevel(logrus.DebugLevel) + if err := os.Remove(".test.db"); err == nil { + log.Debugln("removed .test.db") + } + c := config.TestConfig() code := m.Run() diff --git a/pkg/fs/mime.go b/pkg/fs/mime.go index 8adcda989..464ecd0c8 100644 --- a/pkg/fs/mime.go +++ b/pkg/fs/mime.go @@ -1,8 +1,9 @@ package fs import ( - "net/http" "os" + + "github.com/h2non/filetype" ) const ( @@ -10,6 +11,8 @@ const ( MimeTypePng = "image/png" MimeTypeGif = "image/gif" MimeTypeBitmap = "image/bmp" + MimeTypeTiff = "image/tiff" + MimeTypeHEIF = "image/heif" ) // MimeType returns the mime type of a file, empty string if unknown. @@ -22,14 +25,14 @@ func MimeType(filename string) string { defer handle.Close() - // Only the first 512 bytes are used to sniff the content type. - buffer := make([]byte, 512) + // Only the first 261 bytes are used to sniff the content type. + buffer := make([]byte, 261) - _, err = handle.Read(buffer) - - if err != nil { + if _, err := handle.Read(buffer); err != nil { return "" + } else if t, err := filetype.Get(buffer); err != nil { + return "" + } else { + return t.MIME.Value } - - return http.DetectContentType(buffer) }