diff --git a/Makefile b/Makefile index 98cc9b615..428adbada 100644 --- a/Makefile +++ b/Makefile @@ -780,7 +780,7 @@ fmt-js: fmt-go: go fmt ./pkg/... ./internal/... ./cmd/... gofmt -w -s pkg internal cmd - goimports -w pkg internal cmd + goimports -w -local "github.com/photoprism" pkg internal cmd tidy: go mod tidy users: diff --git a/internal/ai/classify/gen.go b/internal/ai/classify/gen.go index 304b90960..f8546cfad 100644 --- a/internal/ai/classify/gen.go +++ b/internal/ai/classify/gen.go @@ -11,9 +11,10 @@ import ( "text/template" "unicode" + "gopkg.in/yaml.v2" + "github.com/photoprism/photoprism/pkg/clean" "github.com/photoprism/photoprism/pkg/fs" - "gopkg.in/yaml.v2" ) // LabelRule defines the rule for a given Label diff --git a/internal/ai/classify/tensorflow.go b/internal/ai/classify/tensorflow.go index 4a04d3065..f9448dde1 100644 --- a/internal/ai/classify/tensorflow.go +++ b/internal/ai/classify/tensorflow.go @@ -13,9 +13,9 @@ import ( "sort" "strings" + "github.com/disintegration/imaging" tf "github.com/tensorflow/tensorflow/tensorflow/go" - "github.com/disintegration/imaging" "github.com/photoprism/photoprism/pkg/clean" ) diff --git a/internal/ai/face/detector.go b/internal/ai/face/detector.go index 7139af2c2..9c9a53be3 100644 --- a/internal/ai/face/detector.go +++ b/internal/ai/face/detector.go @@ -11,6 +11,7 @@ import ( "sort" pigo "github.com/esimov/pigo/core" + "github.com/photoprism/photoprism/pkg/clean" "github.com/photoprism/photoprism/pkg/fs" ) diff --git a/internal/ai/face/detector_test.go b/internal/ai/face/detector_test.go index afc4d2d7f..bc0139e41 100644 --- a/internal/ai/face/detector_test.go +++ b/internal/ai/face/detector_test.go @@ -6,9 +6,9 @@ import ( "testing" "github.com/dustin/go-humanize/english" + "github.com/stretchr/testify/assert" "github.com/photoprism/photoprism/pkg/fs/fastwalk" - "github.com/stretchr/testify/assert" ) func TestDetect(t *testing.T) { diff --git a/internal/ai/nsfw/detector.go b/internal/ai/nsfw/detector.go index e894137ce..0542b31e0 100644 --- a/internal/ai/nsfw/detector.go +++ b/internal/ai/nsfw/detector.go @@ -7,10 +7,11 @@ import ( "path/filepath" "sync" - "github.com/photoprism/photoprism/pkg/clean" - "github.com/photoprism/photoprism/pkg/fs" tf "github.com/tensorflow/tensorflow/tensorflow/go" "github.com/tensorflow/tensorflow/tensorflow/go/op" + + "github.com/photoprism/photoprism/pkg/clean" + "github.com/photoprism/photoprism/pkg/fs" ) // Detector uses TensorFlow to label drawing, hentai, neutral, porn and sexy images. @@ -29,7 +30,7 @@ func New(modelPath string) *Detector { // File returns matching labels for a jpeg media file. func (t *Detector) File(filename string) (result Labels, err error) { - if fs.MimeType(filename) != fs.MimeTypeJPEG { + if fs.MimeType(filename) != fs.MimeTypeJpeg { return result, fmt.Errorf("nsfw: %s is not a jpeg file", clean.Log(filepath.Base(filename))) } diff --git a/internal/api/abort.go b/internal/api/abort.go index 3cdf5a048..2aa08e39a 100644 --- a/internal/api/abort.go +++ b/internal/api/abort.go @@ -12,7 +12,7 @@ import ( "github.com/photoprism/photoprism/pkg/authn" "github.com/photoprism/photoprism/pkg/clean" "github.com/photoprism/photoprism/pkg/i18n" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) //go:embed embed/video.mp4 @@ -128,6 +128,6 @@ func AbortVideo(c *gin.Context) { func AbortVideoWithStatus(c *gin.Context, code int) { if c != nil { - c.Data(code, header.ContentTypeAVC32, brokenVideo) + c.Data(code, header.ContentTypeMp4Avc720, brokenVideo) } } diff --git a/internal/api/api_auth.go b/internal/api/api_auth.go index a68c6ddc2..1c4c31719 100644 --- a/internal/api/api_auth.go +++ b/internal/api/api_auth.go @@ -8,7 +8,7 @@ import ( "github.com/photoprism/photoprism/internal/event" "github.com/photoprism/photoprism/pkg/authn" "github.com/photoprism/photoprism/pkg/clean" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) // Auth checks if the user is authorized to access a resource with the given permission diff --git a/internal/api/api_auth_test.go b/internal/api/api_auth_test.go index 168c999fa..e6b7657f5 100644 --- a/internal/api/api_auth_test.go +++ b/internal/api/api_auth_test.go @@ -10,7 +10,7 @@ import ( "github.com/photoprism/photoprism/internal/auth/acl" "github.com/photoprism/photoprism/internal/auth/session" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) func TestAuth(t *testing.T) { diff --git a/internal/api/api_request.go b/internal/api/api_request.go index a115545d2..5708cfac0 100644 --- a/internal/api/api_request.go +++ b/internal/api/api_request.go @@ -3,7 +3,7 @@ package api import ( "github.com/gin-gonic/gin" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) // ClientIP returns the client IP address from the request context or a placeholder if it is unknown. diff --git a/internal/api/api_response_headers.go b/internal/api/api_response_headers.go index 546c6f2ad..71908911e 100644 --- a/internal/api/api_response_headers.go +++ b/internal/api/api_response_headers.go @@ -8,7 +8,7 @@ import ( "github.com/photoprism/photoprism/internal/entity" "github.com/photoprism/photoprism/internal/photoprism/get" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) // AddCountHeader adds the actual result count to the response. diff --git a/internal/api/api_test.go b/internal/api/api_test.go index e5e686383..fbe34879a 100644 --- a/internal/api/api_test.go +++ b/internal/api/api_test.go @@ -16,7 +16,7 @@ import ( "github.com/photoprism/photoprism/internal/form" "github.com/photoprism/photoprism/internal/photoprism/get" "github.com/photoprism/photoprism/internal/server/limiter" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) type CloseableResponseRecorder struct { diff --git a/internal/api/cache.go b/internal/api/cache.go index a353e0b09..84e3649cf 100644 --- a/internal/api/cache.go +++ b/internal/api/cache.go @@ -12,7 +12,7 @@ import ( "github.com/photoprism/photoprism/internal/photoprism/get" "github.com/photoprism/photoprism/internal/thumb" "github.com/photoprism/photoprism/pkg/fs" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" "github.com/photoprism/photoprism/pkg/rnd" ) @@ -63,7 +63,7 @@ func RemoveFromAlbumCoverCache(uid string) { } // Delete share preview, if exists. - if sharePreview := path.Join(get.Config().ThumbCachePath(), "share", uid+fs.ExtJPEG); fs.FileExists(sharePreview) { + if sharePreview := path.Join(get.Config().ThumbCachePath(), "share", uid+fs.ExtJpeg); fs.FileExists(sharePreview) { _ = os.Remove(sharePreview) } diff --git a/internal/api/cache_test.go b/internal/api/cache_test.go index 53d983f19..b9ef41d6d 100644 --- a/internal/api/cache_test.go +++ b/internal/api/cache_test.go @@ -7,7 +7,7 @@ import ( "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) func TestAddVideoCacheHeader(t *testing.T) { diff --git a/internal/api/docs.go b/internal/api/docs.go index 81ec07bfc..ddd8bf5f4 100644 --- a/internal/api/docs.go +++ b/internal/api/docs.go @@ -13,7 +13,7 @@ import ( swagger "github.com/swaggo/gin-swagger" "github.com/photoprism/photoprism/internal/photoprism/get" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) //go:embed swagger.json diff --git a/internal/api/oauth_authorize.go b/internal/api/oauth_authorize.go index a40dc0949..8270adb07 100644 --- a/internal/api/oauth_authorize.go +++ b/internal/api/oauth_authorize.go @@ -9,7 +9,7 @@ import ( "github.com/photoprism/photoprism/internal/photoprism/get" "github.com/photoprism/photoprism/pkg/authn" "github.com/photoprism/photoprism/pkg/i18n" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) // OAuthAuthorize should gather consent and authorization from resource owners when using the diff --git a/internal/api/oauth_revoke.go b/internal/api/oauth_revoke.go index 54129163b..81fa748ea 100644 --- a/internal/api/oauth_revoke.go +++ b/internal/api/oauth_revoke.go @@ -14,7 +14,7 @@ import ( "github.com/photoprism/photoprism/pkg/authn" "github.com/photoprism/photoprism/pkg/clean" "github.com/photoprism/photoprism/pkg/i18n" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" "github.com/photoprism/photoprism/pkg/rnd" ) diff --git a/internal/api/oauth_revoke_test.go b/internal/api/oauth_revoke_test.go index 53e207912..f6b566bd3 100644 --- a/internal/api/oauth_revoke_test.go +++ b/internal/api/oauth_revoke_test.go @@ -13,7 +13,7 @@ import ( "github.com/photoprism/photoprism/internal/config" "github.com/photoprism/photoprism/internal/entity" "github.com/photoprism/photoprism/internal/form" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" "github.com/photoprism/photoprism/pkg/rnd" ) diff --git a/internal/api/oauth_token.go b/internal/api/oauth_token.go index 18ea9f06e..f5caaea70 100644 --- a/internal/api/oauth_token.go +++ b/internal/api/oauth_token.go @@ -16,7 +16,7 @@ import ( "github.com/photoprism/photoprism/internal/server/limiter" "github.com/photoprism/photoprism/pkg/authn" "github.com/photoprism/photoprism/pkg/clean" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) // OAuthToken creates a new access token for clients that authenticate with valid OAuth2 client credentials. diff --git a/internal/api/oauth_token_test.go b/internal/api/oauth_token_test.go index 695d47ec3..16d06f8f0 100644 --- a/internal/api/oauth_token_test.go +++ b/internal/api/oauth_token_test.go @@ -11,7 +11,7 @@ import ( "github.com/photoprism/photoprism/internal/config" "github.com/photoprism/photoprism/pkg/authn" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) func TestOAuthToken(t *testing.T) { diff --git a/internal/api/oauth_userinfo.go b/internal/api/oauth_userinfo.go index 402278c69..40f3abca2 100644 --- a/internal/api/oauth_userinfo.go +++ b/internal/api/oauth_userinfo.go @@ -9,7 +9,7 @@ import ( "github.com/photoprism/photoprism/internal/photoprism/get" "github.com/photoprism/photoprism/pkg/authn" "github.com/photoprism/photoprism/pkg/i18n" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) // OAuthUserinfo should return information about the authenticated user, diff --git a/internal/api/oidc_login.go b/internal/api/oidc_login.go index 15ee9ebf8..c94d8b35a 100644 --- a/internal/api/oidc_login.go +++ b/internal/api/oidc_login.go @@ -10,7 +10,7 @@ import ( "github.com/photoprism/photoprism/internal/server/limiter" "github.com/photoprism/photoprism/pkg/authn" "github.com/photoprism/photoprism/pkg/i18n" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) // OIDCLogin redirects a browser to the login page of the configured OpenID Connect provider, if any. diff --git a/internal/api/oidc_redirect.go b/internal/api/oidc_redirect.go index 8748abed2..a07cc97d7 100644 --- a/internal/api/oidc_redirect.go +++ b/internal/api/oidc_redirect.go @@ -17,7 +17,7 @@ import ( "github.com/photoprism/photoprism/pkg/authn" "github.com/photoprism/photoprism/pkg/clean" "github.com/photoprism/photoprism/pkg/i18n" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" "github.com/photoprism/photoprism/pkg/rnd" "github.com/photoprism/photoprism/pkg/time/unix" "github.com/photoprism/photoprism/pkg/txt" diff --git a/internal/api/oidc_redirect_test.go b/internal/api/oidc_redirect_test.go index 93751b350..57833d492 100644 --- a/internal/api/oidc_redirect_test.go +++ b/internal/api/oidc_redirect_test.go @@ -4,8 +4,9 @@ import ( "net/http" "testing" - "github.com/photoprism/photoprism/internal/config" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/config" ) func TestOIDCRedirect(t *testing.T) { diff --git a/internal/api/photos.go b/internal/api/photos.go index 22b182129..7982fe89c 100644 --- a/internal/api/photos.go +++ b/internal/api/photos.go @@ -206,7 +206,7 @@ func GetPhotoYaml(router *gin.RouterGroup) { } if c.Query("download") != "" { - AddDownloadHeader(c, clean.UID(c.Param("uid"))+fs.ExtYAML) + AddDownloadHeader(c, clean.UID(c.Param("uid"))+fs.ExtYaml) } c.Data(http.StatusOK, "text/x-yaml; charset=utf-8", data) diff --git a/internal/api/photos_search_geo.go b/internal/api/photos_search_geo.go index 469e15268..759606328 100644 --- a/internal/api/photos_search_geo.go +++ b/internal/api/photos_search_geo.go @@ -12,7 +12,7 @@ import ( "github.com/photoprism/photoprism/internal/form" "github.com/photoprism/photoprism/internal/photoprism/get" "github.com/photoprism/photoprism/pkg/clean" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" "github.com/photoprism/photoprism/pkg/txt" ) diff --git a/internal/api/services_upload_test.go b/internal/api/services_upload_test.go index 7bc3b256b..4ff313ebb 100644 --- a/internal/api/services_upload_test.go +++ b/internal/api/services_upload_test.go @@ -4,9 +4,10 @@ import ( "net/http" "testing" - "github.com/photoprism/photoprism/pkg/i18n" "github.com/stretchr/testify/assert" "github.com/tidwall/gjson" + + "github.com/photoprism/photoprism/pkg/i18n" ) func TestUploadToService(t *testing.T) { diff --git a/internal/api/session_create.go b/internal/api/session_create.go index 71fe2b79d..659a8c259 100644 --- a/internal/api/session_create.go +++ b/internal/api/session_create.go @@ -13,7 +13,7 @@ import ( "github.com/photoprism/photoprism/internal/server/limiter" "github.com/photoprism/photoprism/pkg/authn" "github.com/photoprism/photoprism/pkg/i18n" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) // CreateSession creates a new client session and returns it as JSON if authentication was successful. diff --git a/internal/api/session_delete.go b/internal/api/session_delete.go index 7718d25b8..ce8a8c183 100644 --- a/internal/api/session_delete.go +++ b/internal/api/session_delete.go @@ -3,6 +3,8 @@ package api import ( "net/http" + "github.com/photoprism/photoprism/pkg/media/http/header" + "github.com/gin-gonic/gin" "github.com/photoprism/photoprism/internal/auth/acl" @@ -13,7 +15,6 @@ import ( "github.com/photoprism/photoprism/pkg/authn" "github.com/photoprism/photoprism/pkg/clean" "github.com/photoprism/photoprism/pkg/i18n" - "github.com/photoprism/photoprism/pkg/net/header" "github.com/photoprism/photoprism/pkg/rnd" ) diff --git a/internal/api/session_get.go b/internal/api/session_get.go index c4ab4beaa..7fb5c01c0 100644 --- a/internal/api/session_get.go +++ b/internal/api/session_get.go @@ -8,7 +8,7 @@ import ( "github.com/photoprism/photoprism/internal/auth/acl" "github.com/photoprism/photoprism/internal/photoprism/get" "github.com/photoprism/photoprism/pkg/clean" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" "github.com/photoprism/photoprism/pkg/rnd" ) diff --git a/internal/api/share_preview.go b/internal/api/share_preview.go index 67f1c9071..148d921ec 100644 --- a/internal/api/share_preview.go +++ b/internal/api/share_preview.go @@ -49,7 +49,7 @@ func SharePreview(router *gin.RouterGroup) { return } - previewFilename := filepath.Join(thumbPath, shared+fs.ExtJPEG) + previewFilename := filepath.Join(thumbPath, shared+fs.ExtJpeg) expires := entity.Now().Add(-1 * time.Hour) diff --git a/internal/api/thumbnails.go b/internal/api/thumbnails.go index 59f637ff1..098fbd87d 100644 --- a/internal/api/thumbnails.go +++ b/internal/api/thumbnails.go @@ -148,7 +148,7 @@ func GetThumb(router *gin.RouterGroup) { } // Find supported preview image if media file is not a JPEG or PNG. - if f.NoJPEG() && f.NoPNG() { + if f.NoJpeg() && f.NoPng() { if f, err = query.FileByPhotoUID(f.PhotoUID); err != nil { c.Data(http.StatusOK, "image/svg+xml", fileIconSvg) return diff --git a/internal/api/users_avatar.go b/internal/api/users_avatar.go index c1c7acbaf..42ce5e6ac 100644 --- a/internal/api/users_avatar.go +++ b/internal/api/users_avatar.go @@ -99,9 +99,9 @@ func UploadUserAvatar(router *gin.RouterGroup) { return } else { switch { - case mimeType.Is(fs.MimeTypePNG): + case mimeType.Is(fs.MimeTypePng): fileName = "avatar.png" - case mimeType.Is(fs.MimeTypeJPEG): + case mimeType.Is(fs.MimeTypeJpeg): fileName = "avatar.jpg" default: event.AuditWarn([]string{ClientIP(c), "session %s", "upload avatar", " %s not supported"}, s.RefID, mimeType) diff --git a/internal/api/users_passcode.go b/internal/api/users_passcode.go index a7c03f33c..981118bd5 100644 --- a/internal/api/users_passcode.go +++ b/internal/api/users_passcode.go @@ -17,7 +17,7 @@ import ( "github.com/photoprism/photoprism/pkg/authn" "github.com/photoprism/photoprism/pkg/clean" "github.com/photoprism/photoprism/pkg/i18n" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" "github.com/photoprism/photoprism/pkg/rnd" ) diff --git a/internal/api/users_sessions_test.go b/internal/api/users_sessions_test.go index baf32d6af..7fb8a0f26 100644 --- a/internal/api/users_sessions_test.go +++ b/internal/api/users_sessions_test.go @@ -4,8 +4,9 @@ import ( "net/http" "testing" - "github.com/photoprism/photoprism/internal/config" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/config" ) func TestFindUserSessions(t *testing.T) { diff --git a/internal/api/users_update_test.go b/internal/api/users_update_test.go index 45434f5eb..7c4a1cdec 100644 --- a/internal/api/users_update_test.go +++ b/internal/api/users_update_test.go @@ -6,10 +6,11 @@ import ( "net/http" "testing" + "github.com/stretchr/testify/assert" + "github.com/photoprism/photoprism/internal/config" "github.com/photoprism/photoprism/internal/entity" "github.com/photoprism/photoprism/internal/form" - "github.com/stretchr/testify/assert" ) func TestUpdateUser(t *testing.T) { diff --git a/internal/api/video.go b/internal/api/video.go index 4fd5b72e6..3523c84f6 100644 --- a/internal/api/video.go +++ b/internal/api/video.go @@ -13,8 +13,8 @@ import ( "github.com/photoprism/photoprism/internal/photoprism/get" "github.com/photoprism/photoprism/pkg/clean" "github.com/photoprism/photoprism/pkg/fs" + "github.com/photoprism/photoprism/pkg/media/http/header" "github.com/photoprism/photoprism/pkg/media/video" - "github.com/photoprism/photoprism/pkg/net/header" "github.com/photoprism/photoprism/pkg/rnd" ) @@ -153,7 +153,7 @@ func GetVideo(router *gin.RouterGroup) { if avcFile, avcErr := conv.ToAvc(mediaFile, get.Config().FFmpegEncoder(), false, false); avcFile != nil && avcErr == nil { videoFileName = avcFile.FileName() - AddContentTypeHeader(c, header.ContentTypeAVC) + AddContentTypeHeader(c, header.ContentTypeMp4Avc) } else { // Log error and default to 404.mp4 log.Errorf("video: failed to transcode %s", clean.Log(f.FileName)) diff --git a/internal/api/video_test.go b/internal/api/video_test.go index 78cf2a68f..f77ee5e90 100644 --- a/internal/api/video_test.go +++ b/internal/api/video_test.go @@ -9,15 +9,15 @@ import ( "github.com/photoprism/photoprism/internal/config" "github.com/photoprism/photoprism/pkg/clean" + "github.com/photoprism/photoprism/pkg/media/http/header" "github.com/photoprism/photoprism/pkg/media/video" - "github.com/photoprism/photoprism/pkg/net/header" ) func TestGetVideo(t *testing.T) { t.Run("ContentTypeAVC", func(t *testing.T) { - assert.Equal(t, header.ContentTypeAVC, clean.ContentType("video/mp4; codecs=\"avc1\"")) + assert.Equal(t, header.ContentTypeMp4Avc, clean.ContentType("video/mp4; codecs=\"avc1\"")) mimeType := fmt.Sprintf("video/mp4; codecs=\"%s\"", clean.Codec("avc1")) - assert.Equal(t, header.ContentTypeAVC, video.ContentType(mimeType, "mp4", "avc1")) + assert.Equal(t, header.ContentTypeMp4Avc, video.ContentType(mimeType, "mp4", "avc1")) }) t.Run("NoHash", func(t *testing.T) { diff --git a/internal/auth/oidc/register_test.go b/internal/auth/oidc/register_test.go index ff836c750..e6b05f807 100644 --- a/internal/auth/oidc/register_test.go +++ b/internal/auth/oidc/register_test.go @@ -3,8 +3,9 @@ package oidc import ( "testing" - "github.com/photoprism/photoprism/internal/config" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/config" ) func TestClientConfig(t *testing.T) { diff --git a/internal/config/config_app_test.go b/internal/config/config_app_test.go index 3276c03ec..ba4785137 100644 --- a/internal/config/config_app_test.go +++ b/internal/config/config_app_test.go @@ -4,8 +4,9 @@ import ( "strings" "testing" - "github.com/photoprism/photoprism/internal/config/pwa" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/config/pwa" ) func TestConfig_AppName(t *testing.T) { diff --git a/internal/config/config_cdn_test.go b/internal/config/config_cdn_test.go index 538735254..b5904dbe2 100644 --- a/internal/config/config_cdn_test.go +++ b/internal/config/config_cdn_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) func TestConfig_CdnUrl(t *testing.T) { diff --git a/internal/config/config_customize.go b/internal/config/config_customize.go index ce19844bd..be955b315 100644 --- a/internal/config/config_customize.go +++ b/internal/config/config_customize.go @@ -65,7 +65,7 @@ func (c *Config) WallpaperUri() string { wallpaperUri = "welcome.jpg" } else if !strings.Contains(wallpaperUri, ".") { - wallpaperUri += fs.ExtJPEG + wallpaperUri += fs.ExtJpeg } // Complete URI as needed if file path is valid. diff --git a/internal/config/config_server.go b/internal/config/config_server.go index b571fc3c6..a740564e3 100644 --- a/internal/config/config_server.go +++ b/internal/config/config_server.go @@ -9,8 +9,8 @@ import ( "github.com/photoprism/photoprism/internal/config/ttl" "github.com/photoprism/photoprism/internal/server/limiter" "github.com/photoprism/photoprism/pkg/fs" - "github.com/photoprism/photoprism/pkg/net/header" - "github.com/photoprism/photoprism/pkg/net/scheme" + "github.com/photoprism/photoprism/pkg/media/http/header" + "github.com/photoprism/photoprism/pkg/media/http/scheme" ) const ( diff --git a/internal/config/config_server_test.go b/internal/config/config_server_test.go index 1c9b2ef81..a43f43b06 100644 --- a/internal/config/config_server_test.go +++ b/internal/config/config_server_test.go @@ -8,7 +8,7 @@ import ( "github.com/photoprism/photoprism/internal/config/ttl" "github.com/photoprism/photoprism/pkg/fs" - "github.com/photoprism/photoprism/pkg/net/scheme" + "github.com/photoprism/photoprism/pkg/media/http/scheme" "github.com/photoprism/photoprism/pkg/txt" ) diff --git a/internal/config/config_thumb_test.go b/internal/config/config_thumb_test.go index f567ac59c..823f801cb 100644 --- a/internal/config/config_thumb_test.go +++ b/internal/config/config_thumb_test.go @@ -3,8 +3,9 @@ package config import ( "testing" - "github.com/photoprism/photoprism/internal/thumb" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/thumb" ) func TestConfig_ConvertSize(t *testing.T) { diff --git a/internal/config/flags.go b/internal/config/flags.go index 96dd58c41..5c3039097 100644 --- a/internal/config/flags.go +++ b/internal/config/flags.go @@ -15,8 +15,8 @@ import ( "github.com/photoprism/photoprism/pkg/authn" "github.com/photoprism/photoprism/pkg/i18n" "github.com/photoprism/photoprism/pkg/media" - "github.com/photoprism/photoprism/pkg/net/header" - "github.com/photoprism/photoprism/pkg/net/scheme" + "github.com/photoprism/photoprism/pkg/media/http/header" + "github.com/photoprism/photoprism/pkg/media/http/scheme" "github.com/photoprism/photoprism/pkg/txt" ) diff --git a/internal/config/options_test.go b/internal/config/options_test.go index b290df49c..dc7d54411 100644 --- a/internal/config/options_test.go +++ b/internal/config/options_test.go @@ -3,8 +3,9 @@ package config import ( "testing" - "github.com/photoprism/photoprism/pkg/fs" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/pkg/fs" ) func TestNewOptions(t *testing.T) { diff --git a/internal/config/test_test.go b/internal/config/test_test.go index de9d517d5..867f9f2fb 100644 --- a/internal/config/test_test.go +++ b/internal/config/test_test.go @@ -4,9 +4,10 @@ import ( "testing" "github.com/jinzhu/gorm" - "github.com/photoprism/photoprism/pkg/fs" "github.com/stretchr/testify/assert" "github.com/urfave/cli/v2" + + "github.com/photoprism/photoprism/pkg/fs" ) func TestConfig_TestdataPath2(t *testing.T) { diff --git a/internal/entity/album_test.go b/internal/entity/album_test.go index 3949de9e6..e63dcb381 100644 --- a/internal/entity/album_test.go +++ b/internal/entity/album_test.go @@ -5,10 +5,10 @@ import ( "testing" "time" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" "github.com/photoprism/photoprism/internal/entity/sortby" + "github.com/photoprism/photoprism/internal/form" "github.com/photoprism/photoprism/pkg/txt" ) diff --git a/internal/entity/album_yaml.go b/internal/entity/album_yaml.go index 35e389d5d..e799c93e3 100644 --- a/internal/entity/album_yaml.go +++ b/internal/entity/album_yaml.go @@ -68,7 +68,7 @@ func (m *Album) YamlFileName(backupPath string) (absolute, relative string, err return "", "", fmt.Errorf("album uid is empty") } - relative = filepath.Join(m.AlbumType, m.AlbumUID+fs.ExtYAML) + relative = filepath.Join(m.AlbumType, m.AlbumUID+fs.ExtYaml) if backupPath == "" { return "", relative, fmt.Errorf("backup path is empty") diff --git a/internal/entity/auth_client_add_test.go b/internal/entity/auth_client_add_test.go index 9cbdc2675..4308eea74 100644 --- a/internal/entity/auth_client_add_test.go +++ b/internal/entity/auth_client_add_test.go @@ -3,8 +3,9 @@ package entity import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func Test_AddClient(t *testing.T) { diff --git a/internal/entity/auth_session.go b/internal/entity/auth_session.go index be31a1ba4..392cef5b3 100644 --- a/internal/entity/auth_session.go +++ b/internal/entity/auth_session.go @@ -18,7 +18,7 @@ import ( "github.com/photoprism/photoprism/pkg/clean" "github.com/photoprism/photoprism/pkg/i18n" "github.com/photoprism/photoprism/pkg/list" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" "github.com/photoprism/photoprism/pkg/rnd" "github.com/photoprism/photoprism/pkg/time/unix" "github.com/photoprism/photoprism/pkg/txt" diff --git a/internal/entity/auth_session_login.go b/internal/entity/auth_session_login.go index f43e4d38a..2c691c66f 100644 --- a/internal/entity/auth_session_login.go +++ b/internal/entity/auth_session_login.go @@ -13,7 +13,7 @@ import ( "github.com/photoprism/photoprism/pkg/authn" "github.com/photoprism/photoprism/pkg/clean" "github.com/photoprism/photoprism/pkg/i18n" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" "github.com/photoprism/photoprism/pkg/rnd" "github.com/photoprism/photoprism/pkg/txt" ) diff --git a/internal/entity/auth_session_test.go b/internal/entity/auth_session_test.go index d8ec6b9e2..f518edf6b 100644 --- a/internal/entity/auth_session_test.go +++ b/internal/entity/auth_session_test.go @@ -10,7 +10,7 @@ import ( "github.com/photoprism/photoprism/internal/auth/acl" "github.com/photoprism/photoprism/pkg/authn" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" "github.com/photoprism/photoprism/pkg/rnd" "github.com/photoprism/photoprism/pkg/time/unix" "github.com/photoprism/photoprism/pkg/txt/report" diff --git a/internal/entity/auth_user_cli.go b/internal/entity/auth_user_cli.go index cc9587065..1f4deab41 100644 --- a/internal/entity/auth_user_cli.go +++ b/internal/entity/auth_user_cli.go @@ -1,10 +1,10 @@ package entity import ( - "github.com/photoprism/photoprism/pkg/authn" "github.com/urfave/cli/v2" "github.com/photoprism/photoprism/internal/form" + "github.com/photoprism/photoprism/pkg/authn" "github.com/photoprism/photoprism/pkg/clean" ) diff --git a/internal/entity/error.go b/internal/entity/error.go index 07e337cdf..a73faefc5 100644 --- a/internal/entity/error.go +++ b/internal/entity/error.go @@ -3,8 +3,9 @@ package entity import ( "time" - "github.com/photoprism/photoprism/internal/event" "github.com/sirupsen/logrus" + + "github.com/photoprism/photoprism/internal/event" ) // Error represents an error message log. diff --git a/internal/entity/file.go b/internal/entity/file.go index ee046a40f..03d0bfe6a 100644 --- a/internal/entity/file.go +++ b/internal/entity/file.go @@ -360,7 +360,7 @@ func (m *File) ReplaceHash(newHash string) error { m.FileHash = newHash // Ok to skip updating related tables? - if m.NoJPEG() || m.FileHash == "" { + if m.NoJpeg() || m.FileHash == "" { return nil } @@ -590,13 +590,13 @@ func (m *File) RelatedPhoto() *Photo { } // NoJPEG returns true if the file is not a JPEG image. -func (m *File) NoJPEG() bool { - return fs.ImageJPEG.NotEqual(m.FileType) +func (m *File) NoJpeg() bool { + return fs.ImageJpeg.NotEqual(m.FileType) } // NoPNG returns true if the file is not a PNG image. -func (m *File) NoPNG() bool { - return fs.ImagePNG.NotEqual(m.FileType) +func (m *File) NoPng() bool { + return fs.ImagePng.NotEqual(m.FileType) } // Type returns the file type. diff --git a/internal/entity/file_fixtures.go b/internal/entity/file_fixtures.go index bd4755ae6..b298c71f7 100644 --- a/internal/entity/file_fixtures.go +++ b/internal/entity/file_fixtures.go @@ -2602,7 +2602,7 @@ var FileFixtures = FileMap{ FileHash: "pcad9168fa6acc5c5c2965adf6ec465ca42fd3451", FileSize: 921858, FileCodec: "jpeg", - FileType: string(fs.ImageJPEG), + FileType: string(fs.ImageJpeg), MediaType: media.Image.String(), FileMime: "image/jpg", FilePrimary: true, diff --git a/internal/entity/file_test.go b/internal/entity/file_test.go index 1eee8ba5d..3724f7803 100644 --- a/internal/entity/file_test.go +++ b/internal/entity/file_test.go @@ -11,8 +11,8 @@ import ( "github.com/photoprism/photoprism/pkg/clean" "github.com/photoprism/photoprism/pkg/fs" "github.com/photoprism/photoprism/pkg/media/colors" + "github.com/photoprism/photoprism/pkg/media/http/header" "github.com/photoprism/photoprism/pkg/media/projection" - "github.com/photoprism/photoprism/pkg/net/header" "github.com/photoprism/photoprism/pkg/rnd" ) @@ -63,7 +63,7 @@ func TestFile_ShareFileName(t *testing.T) { assert.Contains(t, filename, "20190115-000000-Berlin-Morning-Mood") assert.Equal(t, "20190115-000000-Berlin-Morning-Mood.jpg", filename) - assert.Contains(t, filename, fs.ExtJPEG) + assert.Contains(t, filename, fs.ExtJpeg) }) t.Run("WithPhotoTitleSequence", func(t *testing.T) { photo := &Photo{TakenAtLocal: time.Date(2019, 01, 15, 0, 0, 0, 0, time.UTC), PhotoTitle: "Berlin / Morning Mood"} @@ -73,7 +73,7 @@ func TestFile_ShareFileName(t *testing.T) { assert.Contains(t, filename, "20190115-000000-Berlin-Morning-Mood") assert.Equal(t, "20190115-000000-Berlin-Morning-Mood (2).jpg", filename) - assert.Contains(t, filename, fs.ExtJPEG) + assert.Contains(t, filename, fs.ExtJpeg) }) t.Run("EmptyPhotoTitle", func(t *testing.T) { photo := &Photo{TakenAtLocal: time.Date(2019, 01, 15, 0, 0, 0, 0, time.UTC), PhotoTitle: ""} @@ -303,14 +303,14 @@ func TestFile_Links(t *testing.T) { }) } -func TestFile_NoJPEG(t *testing.T) { +func TestFile_NoJpeg(t *testing.T) { t.Run("true", func(t *testing.T) { file := &File{Photo: nil, FileType: "xmp", FileSize: 500} - assert.True(t, file.NoJPEG()) + assert.True(t, file.NoJpeg()) }) t.Run("false", func(t *testing.T) { file := &File{Photo: nil, FileType: "jpg", FileSize: 500} - assert.False(t, file.NoJPEG()) + assert.False(t, file.NoJpeg()) }) } @@ -432,12 +432,12 @@ func TestFile_OriginalBase(t *testing.T) { filename := file.OriginalBase(0) assert.Contains(t, filename, "20190115-000000-Berlin-Morning-Mood") - assert.Contains(t, filename, fs.ExtJPEG) + assert.Contains(t, filename, fs.ExtJpeg) filename2 := file.OriginalBase(1) assert.Contains(t, filename2, "20190115-000000-Berlin-Morning-Mood") assert.Contains(t, filename2, "(1)") - assert.Contains(t, filename2, fs.ExtJPEG) + assert.Contains(t, filename2, fs.ExtJpeg) }) t.Run("original name empty", func(t *testing.T) { photo := &Photo{TakenAtLocal: time.Date(2019, 01, 15, 0, 0, 0, 0, time.UTC), PhotoTitle: "Berlin / Morning Mood"} @@ -446,12 +446,12 @@ func TestFile_OriginalBase(t *testing.T) { filename := file.OriginalBase(0) assert.Contains(t, filename, "sonnenaufgang") - assert.Contains(t, filename, fs.ExtJPEG) + assert.Contains(t, filename, fs.ExtJpeg) filename2 := file.OriginalBase(1) assert.Contains(t, filename2, "sonnenaufgang") assert.Contains(t, filename2, "(1)") - assert.Contains(t, filename2, fs.ExtJPEG) + assert.Contains(t, filename2, fs.ExtJpeg) }) t.Run("original name not empty", func(t *testing.T) { photo := &Photo{TakenAtLocal: time.Date(2019, 01, 15, 0, 0, 0, 0, time.UTC), PhotoTitle: "Berlin / Morning Mood"} @@ -460,12 +460,12 @@ func TestFile_OriginalBase(t *testing.T) { filename := file.OriginalBase(0) assert.Contains(t, filename, "Sonnenaufgang") - assert.Contains(t, filename, fs.ExtJPEG) + assert.Contains(t, filename, fs.ExtJpeg) filename2 := file.OriginalBase(1) assert.Contains(t, filename2, "Sonnenaufgang") assert.Contains(t, filename2, "(1)") - assert.Contains(t, filename2, fs.ExtJPEG) + assert.Contains(t, filename2, fs.ExtJpeg) }) } @@ -476,12 +476,12 @@ func TestFile_DownloadName(t *testing.T) { filename := file.DownloadName(customize.DownloadNameFile, 0) assert.Contains(t, filename, "filename") - assert.Contains(t, filename, fs.ExtJPEG) + assert.Contains(t, filename, fs.ExtJpeg) filename2 := file.DownloadName(customize.DownloadNameOriginal, 1) assert.Contains(t, filename2, "originalName") assert.Contains(t, filename2, "(1)") - assert.Contains(t, filename2, fs.ExtJPEG) + assert.Contains(t, filename2, fs.ExtJpeg) filename3 := file.DownloadName("xxx", 0) assert.Contains(t, filename3, "20190115-000000-Berlin-Morning-Mood") @@ -881,14 +881,14 @@ func TestFile_ContentType(t *testing.T) { t.Run("Image", func(t *testing.T) { m := FileFixtures.Get("exampleFileName.jpg") assert.Equal(t, false, m.FileVideo) - assert.Equal(t, header.ContentTypeJPEG, m.ContentType()) + assert.Equal(t, header.ContentTypeJpeg, m.ContentType()) }) t.Run("Video", func(t *testing.T) { avc := FileFixtures.Get("Video.mp4") assert.Equal(t, true, avc.FileVideo) - assert.Equal(t, header.ContentTypeAVC, avc.ContentType()) + assert.Equal(t, header.ContentTypeMp4Avc, avc.ContentType()) hevc := FileFixtures.Get("Photo21.mp4") assert.Equal(t, true, hevc.FileVideo) - assert.Equal(t, header.ContentTypeHEVC, hevc.ContentType()) + assert.Equal(t, header.ContentTypeMp4Hevc, hevc.ContentType()) }) } diff --git a/internal/entity/link_test.go b/internal/entity/link_test.go index 48d57f260..af85da1b0 100644 --- a/internal/entity/link_test.go +++ b/internal/entity/link_test.go @@ -3,8 +3,9 @@ package entity import ( "testing" - "github.com/photoprism/photoprism/pkg/rnd" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/pkg/rnd" ) func TestNewLink(t *testing.T) { diff --git a/internal/entity/marker_test.go b/internal/entity/marker_test.go index c9da869b2..dd322d591 100644 --- a/internal/entity/marker_test.go +++ b/internal/entity/marker_test.go @@ -4,9 +4,10 @@ import ( "fmt" "testing" + "github.com/stretchr/testify/assert" + "github.com/photoprism/photoprism/internal/form" "github.com/photoprism/photoprism/internal/thumb/crop" - "github.com/stretchr/testify/assert" ) var testArea = crop.Area{ diff --git a/internal/entity/migrate/status.go b/internal/entity/migrate/status.go index afff85c16..3f46aa66e 100644 --- a/internal/entity/migrate/status.go +++ b/internal/entity/migrate/status.go @@ -5,6 +5,7 @@ import ( "github.com/dustin/go-humanize/english" "github.com/jinzhu/gorm" + "github.com/photoprism/photoprism/pkg/list" ) diff --git a/internal/entity/passcode.go b/internal/entity/passcode.go index b2103e97f..47bfa8119 100644 --- a/internal/entity/passcode.go +++ b/internal/entity/passcode.go @@ -8,12 +8,12 @@ import ( "image/png" "time" + "github.com/pquerna/otp" "github.com/pquerna/otp/totp" "github.com/photoprism/photoprism/pkg/authn" "github.com/photoprism/photoprism/pkg/clean" "github.com/photoprism/photoprism/pkg/rnd" - "github.com/pquerna/otp" ) // Passcode represents a two-factor authentication key. @@ -326,7 +326,7 @@ func (m *Passcode) Image(size int) (image.Image, error) { } // PNG returns a PNG image buffer with a QR Code that can be used to initialize compatible authenticator apps. -func (m *Passcode) PNG(size int) *bytes.Buffer { +func (m *Passcode) Png(size int) *bytes.Buffer { if m == nil { return nil } diff --git a/internal/entity/passcode_json.go b/internal/entity/passcode_json.go index c48b1e4e9..ae4398003 100644 --- a/internal/entity/passcode_json.go +++ b/internal/entity/passcode_json.go @@ -23,7 +23,7 @@ func (m *Passcode) MarshalJSON() ([]byte, error) { UID: m.UID, Type: m.KeyType, Secret: m.Secret(), - QRCode: media.Base64(m.PNG(350)), + QRCode: media.Base64(m.Png(350)), RecoveryCode: m.RecoveryCode, CreatedAt: m.CreatedAt, UpdatedAt: m.UpdatedAt, diff --git a/internal/entity/photo_location.go b/internal/entity/photo_location.go index 91bc525a0..2f35f6233 100644 --- a/internal/entity/photo_location.go +++ b/internal/entity/photo_location.go @@ -7,12 +7,13 @@ import ( "github.com/dustin/go-humanize/english" + "gopkg.in/photoprism/go-tz.v2/tz" + "github.com/photoprism/photoprism/internal/ai/classify" "github.com/photoprism/photoprism/internal/service/maps" "github.com/photoprism/photoprism/pkg/clean" "github.com/photoprism/photoprism/pkg/geo" "github.com/photoprism/photoprism/pkg/txt" - "gopkg.in/photoprism/go-tz.v2/tz" ) // SetCoordinates changes the photo lat, lng and altitude if not empty and from an acceptable source. diff --git a/internal/entity/photo_merge.go b/internal/entity/photo_merge.go index 4d9a84ddb..a72a8a64e 100644 --- a/internal/entity/photo_merge.go +++ b/internal/entity/photo_merge.go @@ -4,6 +4,7 @@ import ( "sync" "github.com/jinzhu/gorm" + "github.com/photoprism/photoprism/pkg/rnd" ) diff --git a/internal/entity/photo_yaml.go b/internal/entity/photo_yaml.go index b9931b0cb..fb7a2ada4 100644 --- a/internal/entity/photo_yaml.go +++ b/internal/entity/photo_yaml.go @@ -67,8 +67,8 @@ func (m *Photo) SaveAsYaml(fileName string) error { // YamlFileName returns both the absolute file path and the relative name for the YAML sidecar file, e.g. for logging. func (m *Photo) YamlFileName(originalsPath, sidecarPath string) (absolute, relative string, err error) { - absolute, err = fs.FileName(filepath.Join(originalsPath, m.PhotoPath, m.PhotoName), sidecarPath, originalsPath, fs.ExtYAML) - relative = filepath.Join(m.PhotoPath, m.PhotoName) + fs.ExtYAML + absolute, err = fs.FileName(filepath.Join(originalsPath, m.PhotoPath, m.PhotoName), sidecarPath, originalsPath, fs.ExtYaml) + relative = filepath.Join(m.PhotoPath, m.PhotoName) + fs.ExtYaml return absolute, relative, err } diff --git a/internal/entity/query/account_uploads_test.go b/internal/entity/query/account_uploads_test.go index 2dfee72df..30669c37b 100644 --- a/internal/entity/query/account_uploads_test.go +++ b/internal/entity/query/account_uploads_test.go @@ -3,8 +3,9 @@ package query import ( "testing" - "github.com/photoprism/photoprism/internal/entity" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/entity" ) func TestAccountUploads(t *testing.T) { diff --git a/internal/entity/query/file_selection.go b/internal/entity/query/file_selection.go index df0dac4a6..65a89a797 100644 --- a/internal/entity/query/file_selection.go +++ b/internal/entity/query/file_selection.go @@ -61,13 +61,13 @@ func ShareSelection(originals bool) FileSelection { } omitTypes = []string{ - fs.ImagePNG.String(), - fs.ImageWebP.String(), - fs.ImageTIFF.String(), - fs.ImageAVIF.String(), - fs.ImageHEIC.String(), - fs.ImageBMP.String(), - fs.ImageGIF.String(), + fs.ImagePng.String(), + fs.ImageWebp.String(), + fs.ImageTiff.String(), + fs.ImageAvif.String(), + fs.ImageHeic.String(), + fs.ImageBmp.String(), + fs.ImageGif.String(), } } diff --git a/internal/entity/query/file_shares_test.go b/internal/entity/query/file_shares_test.go index 31ff37a57..5da72d44f 100644 --- a/internal/entity/query/file_shares_test.go +++ b/internal/entity/query/file_shares_test.go @@ -4,8 +4,9 @@ import ( "testing" "time" - "github.com/photoprism/photoprism/internal/entity" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/entity" ) func TestFileShares(t *testing.T) { diff --git a/internal/entity/query/file_syncs_test.go b/internal/entity/query/file_syncs_test.go index db2df05d9..da3e899cd 100644 --- a/internal/entity/query/file_syncs_test.go +++ b/internal/entity/query/file_syncs_test.go @@ -3,8 +3,9 @@ package query import ( "testing" - "github.com/photoprism/photoprism/internal/entity" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/entity" ) func TestFileSyncs(t *testing.T) { diff --git a/internal/entity/query/files.go b/internal/entity/query/files.go index 472ce0854..3bda64c7d 100644 --- a/internal/entity/query/files.go +++ b/internal/entity/query/files.go @@ -85,7 +85,7 @@ func VideoByPhotoUID(photoUID string) (*entity.File, error) { } err := Db().Where("photo_uid = ? AND file_missing = 0", photoUID). - Where("file_video = 1 OR file_duration > 0 OR file_frames > 0 OR file_type = ?", fs.ImageGIF). + Where("file_video = 1 OR file_duration > 0 OR file_frames > 0 OR file_type = ?", fs.ImageGif). Order("file_error ASC, file_video DESC, file_duration DESC, file_frames DESC"). Preload("Photo").First(&f).Error diff --git a/internal/entity/query/files_test.go b/internal/entity/query/files_test.go index c5faeb2eb..d9f14de12 100644 --- a/internal/entity/query/files_test.go +++ b/internal/entity/query/files_test.go @@ -4,8 +4,9 @@ import ( "testing" "time" - "github.com/photoprism/photoprism/internal/entity" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/entity" ) func TestFilesByPath(t *testing.T) { diff --git a/internal/entity/query/folders_test.go b/internal/entity/query/folders_test.go index 26388fae9..b619c715d 100644 --- a/internal/entity/query/folders_test.go +++ b/internal/entity/query/folders_test.go @@ -3,8 +3,9 @@ package query import ( "testing" - "github.com/photoprism/photoprism/internal/entity" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/entity" ) func TestFolderCoverByUID(t *testing.T) { diff --git a/internal/entity/query/purge_test.go b/internal/entity/query/purge_test.go index fcfddf2a8..5e1b9cf18 100644 --- a/internal/entity/query/purge_test.go +++ b/internal/entity/query/purge_test.go @@ -4,8 +4,9 @@ import ( "testing" "time" - "github.com/photoprism/photoprism/internal/entity" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/entity" ) func TestPurgeOrphans(t *testing.T) { diff --git a/internal/entity/query/query.go b/internal/entity/query/query.go index cc6d043f7..9ef22f9a3 100644 --- a/internal/entity/query/query.go +++ b/internal/entity/query/query.go @@ -26,6 +26,7 @@ package query import ( "github.com/jinzhu/gorm" + "github.com/photoprism/photoprism/internal/entity" "github.com/photoprism/photoprism/internal/event" ) diff --git a/internal/entity/search/accounts_test.go b/internal/entity/search/accounts_test.go index be8f117fd..f63cadfc8 100644 --- a/internal/entity/search/accounts_test.go +++ b/internal/entity/search/accounts_test.go @@ -3,9 +3,10 @@ package search import ( "testing" + "github.com/stretchr/testify/assert" + "github.com/photoprism/photoprism/internal/entity" "github.com/photoprism/photoprism/internal/form" - "github.com/stretchr/testify/assert" ) func TestAccounts(t *testing.T) { diff --git a/internal/entity/search/photos_filter_album_test.go b/internal/entity/search/photos_filter_album_test.go index ee877450d..620de5b24 100644 --- a/internal/entity/search/photos_filter_album_test.go +++ b/internal/entity/search/photos_filter_album_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosFilterAlbum(t *testing.T) { diff --git a/internal/entity/search/photos_filter_alt_test.go b/internal/entity/search/photos_filter_alt_test.go index 87edb7347..1a67f3295 100644 --- a/internal/entity/search/photos_filter_alt_test.go +++ b/internal/entity/search/photos_filter_alt_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosFilterAlt(t *testing.T) { diff --git a/internal/entity/search/photos_filter_animated_test.go b/internal/entity/search/photos_filter_animated_test.go index 7a4cc8b97..da9630839 100644 --- a/internal/entity/search/photos_filter_animated_test.go +++ b/internal/entity/search/photos_filter_animated_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosQueryAnimated(t *testing.T) { diff --git a/internal/entity/search/photos_filter_archived_test.go b/internal/entity/search/photos_filter_archived_test.go index 07769c25b..031051540 100644 --- a/internal/entity/search/photos_filter_archived_test.go +++ b/internal/entity/search/photos_filter_archived_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosQueryArchived(t *testing.T) { diff --git a/internal/entity/search/photos_filter_error_test.go b/internal/entity/search/photos_filter_error_test.go index 82deb111b..681422806 100644 --- a/internal/entity/search/photos_filter_error_test.go +++ b/internal/entity/search/photos_filter_error_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosQueryError(t *testing.T) { diff --git a/internal/entity/search/photos_filter_f_number_test.go b/internal/entity/search/photos_filter_f_number_test.go index c91c62a2f..54d353a5a 100644 --- a/internal/entity/search/photos_filter_f_number_test.go +++ b/internal/entity/search/photos_filter_f_number_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosFilterFNumber(t *testing.T) { diff --git a/internal/entity/search/photos_filter_faces_test.go b/internal/entity/search/photos_filter_faces_test.go index 9bee6aa14..f7fba952d 100644 --- a/internal/entity/search/photos_filter_faces_test.go +++ b/internal/entity/search/photos_filter_faces_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosFilterFaces(t *testing.T) { diff --git a/internal/entity/search/photos_filter_favorite_test.go b/internal/entity/search/photos_filter_favorite_test.go index 484e42e04..30e66c709 100644 --- a/internal/entity/search/photos_filter_favorite_test.go +++ b/internal/entity/search/photos_filter_favorite_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosQueryFavorite(t *testing.T) { diff --git a/internal/entity/search/photos_filter_filename_test.go b/internal/entity/search/photos_filter_filename_test.go index 6c18966b5..ca72a7e67 100644 --- a/internal/entity/search/photos_filter_filename_test.go +++ b/internal/entity/search/photos_filter_filename_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosFilterFilename(t *testing.T) { diff --git a/internal/entity/search/photos_filter_focal_length_test.go b/internal/entity/search/photos_filter_focal_length_test.go index 4e0a14fc7..c24b8e66e 100644 --- a/internal/entity/search/photos_filter_focal_length_test.go +++ b/internal/entity/search/photos_filter_focal_length_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosFilterFocalLength(t *testing.T) { diff --git a/internal/entity/search/photos_filter_folder_test.go b/internal/entity/search/photos_filter_folder_test.go index 6433001a2..f8a579ee1 100644 --- a/internal/entity/search/photos_filter_folder_test.go +++ b/internal/entity/search/photos_filter_folder_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosFilterFolder(t *testing.T) { diff --git a/internal/entity/search/photos_filter_geo_test.go b/internal/entity/search/photos_filter_geo_test.go index f087c6833..c90e52c4e 100644 --- a/internal/entity/search/photos_filter_geo_test.go +++ b/internal/entity/search/photos_filter_geo_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosQueryGeo(t *testing.T) { diff --git a/internal/entity/search/photos_filter_hidden_test.go b/internal/entity/search/photos_filter_hidden_test.go index 804280d15..419cc1b83 100644 --- a/internal/entity/search/photos_filter_hidden_test.go +++ b/internal/entity/search/photos_filter_hidden_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosQueryHidden(t *testing.T) { diff --git a/internal/entity/search/photos_filter_iso_test.go b/internal/entity/search/photos_filter_iso_test.go index fc9e5367d..e5bdccb08 100644 --- a/internal/entity/search/photos_filter_iso_test.go +++ b/internal/entity/search/photos_filter_iso_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosFilterIso(t *testing.T) { diff --git a/internal/entity/search/photos_filter_keywords_test.go b/internal/entity/search/photos_filter_keywords_test.go index ec6b56195..84f920dc6 100644 --- a/internal/entity/search/photos_filter_keywords_test.go +++ b/internal/entity/search/photos_filter_keywords_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosFilterKeywords(t *testing.T) { diff --git a/internal/entity/search/photos_filter_label_test.go b/internal/entity/search/photos_filter_label_test.go index 63dc53ccc..9283b96b1 100644 --- a/internal/entity/search/photos_filter_label_test.go +++ b/internal/entity/search/photos_filter_label_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosFilterLabel(t *testing.T) { diff --git a/internal/entity/search/photos_filter_landscape_test.go b/internal/entity/search/photos_filter_landscape_test.go index 9dfc60b64..0c68dbd27 100644 --- a/internal/entity/search/photos_filter_landscape_test.go +++ b/internal/entity/search/photos_filter_landscape_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosQueryLandscape(t *testing.T) { diff --git a/internal/entity/search/photos_filter_live_test.go b/internal/entity/search/photos_filter_live_test.go index 66fb30b61..0f378a8a7 100644 --- a/internal/entity/search/photos_filter_live_test.go +++ b/internal/entity/search/photos_filter_live_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosQueryLive(t *testing.T) { diff --git a/internal/entity/search/photos_filter_mono_test.go b/internal/entity/search/photos_filter_mono_test.go index c2b504741..b74b4cc4e 100644 --- a/internal/entity/search/photos_filter_mono_test.go +++ b/internal/entity/search/photos_filter_mono_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosQueryMono(t *testing.T) { diff --git a/internal/entity/search/photos_filter_name_test.go b/internal/entity/search/photos_filter_name_test.go index 7f8e87fa6..0e46c01a7 100644 --- a/internal/entity/search/photos_filter_name_test.go +++ b/internal/entity/search/photos_filter_name_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosFilterName(t *testing.T) { diff --git a/internal/entity/search/photos_filter_panorama_test.go b/internal/entity/search/photos_filter_panorama_test.go index 729e84acf..15fbed047 100644 --- a/internal/entity/search/photos_filter_panorama_test.go +++ b/internal/entity/search/photos_filter_panorama_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosQueryPanorama(t *testing.T) { diff --git a/internal/entity/search/photos_filter_path_test.go b/internal/entity/search/photos_filter_path_test.go index 05f328fa1..13e815783 100644 --- a/internal/entity/search/photos_filter_path_test.go +++ b/internal/entity/search/photos_filter_path_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosFilterPath(t *testing.T) { diff --git a/internal/entity/search/photos_filter_photo_test.go b/internal/entity/search/photos_filter_photo_test.go index b57aecada..8311e58ed 100644 --- a/internal/entity/search/photos_filter_photo_test.go +++ b/internal/entity/search/photos_filter_photo_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosQueryPhoto(t *testing.T) { diff --git a/internal/entity/search/photos_filter_portrait_test.go b/internal/entity/search/photos_filter_portrait_test.go index 072c4b643..848771901 100644 --- a/internal/entity/search/photos_filter_portrait_test.go +++ b/internal/entity/search/photos_filter_portrait_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosQueryPortrait(t *testing.T) { diff --git a/internal/entity/search/photos_filter_primary_test.go b/internal/entity/search/photos_filter_primary_test.go index 41c683ab4..3042234bc 100644 --- a/internal/entity/search/photos_filter_primary_test.go +++ b/internal/entity/search/photos_filter_primary_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosQueryPrimary(t *testing.T) { diff --git a/internal/entity/search/photos_filter_private_test.go b/internal/entity/search/photos_filter_private_test.go index 813d789c4..5d431817d 100644 --- a/internal/entity/search/photos_filter_private_test.go +++ b/internal/entity/search/photos_filter_private_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosQueryPrivate(t *testing.T) { diff --git a/internal/entity/search/photos_filter_public_test.go b/internal/entity/search/photos_filter_public_test.go index 91012639b..bc97f50db 100644 --- a/internal/entity/search/photos_filter_public_test.go +++ b/internal/entity/search/photos_filter_public_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosQueryPublic(t *testing.T) { diff --git a/internal/entity/search/photos_filter_raw_test.go b/internal/entity/search/photos_filter_raw_test.go index 03f360873..2ec2bb7e4 100644 --- a/internal/entity/search/photos_filter_raw_test.go +++ b/internal/entity/search/photos_filter_raw_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosQueryRaw(t *testing.T) { diff --git a/internal/entity/search/photos_filter_resolution_test.go b/internal/entity/search/photos_filter_resolution_test.go index 63b6e61f2..f184b955b 100644 --- a/internal/entity/search/photos_filter_resolution_test.go +++ b/internal/entity/search/photos_filter_resolution_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosFilterResolution(t *testing.T) { diff --git a/internal/entity/search/photos_filter_review_test.go b/internal/entity/search/photos_filter_review_test.go index 9a9d41953..6a162e4ee 100644 --- a/internal/entity/search/photos_filter_review_test.go +++ b/internal/entity/search/photos_filter_review_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosQueryReview(t *testing.T) { diff --git a/internal/entity/search/photos_filter_scan_test.go b/internal/entity/search/photos_filter_scan_test.go index ba3458e97..de757241f 100644 --- a/internal/entity/search/photos_filter_scan_test.go +++ b/internal/entity/search/photos_filter_scan_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosQueryScan(t *testing.T) { diff --git a/internal/entity/search/photos_filter_square_test.go b/internal/entity/search/photos_filter_square_test.go index 441ff821a..c5b5bd75d 100644 --- a/internal/entity/search/photos_filter_square_test.go +++ b/internal/entity/search/photos_filter_square_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosQuerySquare(t *testing.T) { diff --git a/internal/entity/search/photos_filter_stack_test.go b/internal/entity/search/photos_filter_stack_test.go index 9ad4897c5..95a35fdcc 100644 --- a/internal/entity/search/photos_filter_stack_test.go +++ b/internal/entity/search/photos_filter_stack_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosQueryStack(t *testing.T) { diff --git a/internal/entity/search/photos_filter_stackable_test.go b/internal/entity/search/photos_filter_stackable_test.go index 9f341a193..525831a57 100644 --- a/internal/entity/search/photos_filter_stackable_test.go +++ b/internal/entity/search/photos_filter_stackable_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosQueryStackable(t *testing.T) { diff --git a/internal/entity/search/photos_filter_title_test.go b/internal/entity/search/photos_filter_title_test.go index 6c7fd4cd8..727b39077 100644 --- a/internal/entity/search/photos_filter_title_test.go +++ b/internal/entity/search/photos_filter_title_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosFilterTitle(t *testing.T) { diff --git a/internal/entity/search/photos_filter_unsorted_test.go b/internal/entity/search/photos_filter_unsorted_test.go index 15f31481a..a724c9912 100644 --- a/internal/entity/search/photos_filter_unsorted_test.go +++ b/internal/entity/search/photos_filter_unsorted_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosQueryUnsorted(t *testing.T) { diff --git a/internal/entity/search/photos_filter_unstacked_test.go b/internal/entity/search/photos_filter_unstacked_test.go index c8459fc90..50a9325d7 100644 --- a/internal/entity/search/photos_filter_unstacked_test.go +++ b/internal/entity/search/photos_filter_unstacked_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosQueryUnstacked(t *testing.T) { diff --git a/internal/entity/search/photos_filter_vector_test.go b/internal/entity/search/photos_filter_vector_test.go index bd4f2cbad..2f953c440 100644 --- a/internal/entity/search/photos_filter_vector_test.go +++ b/internal/entity/search/photos_filter_vector_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosQueryVector(t *testing.T) { diff --git a/internal/entity/search/photos_filter_video_test.go b/internal/entity/search/photos_filter_video_test.go index d301a19e0..019016cc4 100644 --- a/internal/entity/search/photos_filter_video_test.go +++ b/internal/entity/search/photos_filter_video_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosQueryVideo(t *testing.T) { diff --git a/internal/entity/search/photos_geo_filter_favorite_test.go b/internal/entity/search/photos_geo_filter_favorite_test.go index e20d01798..19ba05a52 100644 --- a/internal/entity/search/photos_geo_filter_favorite_test.go +++ b/internal/entity/search/photos_geo_filter_favorite_test.go @@ -3,8 +3,9 @@ package search import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestPhotosGeoQueryFavorite(t *testing.T) { diff --git a/internal/entity/search/photos_results_test.go b/internal/entity/search/photos_results_test.go index 7dfc961c2..f876b3610 100644 --- a/internal/entity/search/photos_results_test.go +++ b/internal/entity/search/photos_results_test.go @@ -8,8 +8,8 @@ import ( "github.com/photoprism/photoprism/internal/entity" "github.com/photoprism/photoprism/pkg/media" + "github.com/photoprism/photoprism/pkg/media/http/header" "github.com/photoprism/photoprism/pkg/media/video" - "github.com/photoprism/photoprism/pkg/net/header" ) func TestPhoto_Ids(t *testing.T) { @@ -181,8 +181,8 @@ func TestPhoto_MediaInfo(t *testing.T) { { FileVideo: true, MediaType: media.Video.String(), - FileMime: header.ContentTypeAVC, - FileCodec: video.CodecAVC, + FileMime: header.ContentTypeMp4Avc, + FileCodec: video.CodecAvc, FileHash: "53c89dcfa006c9e592dd9e6db4b31cd57be64b81", }, }, @@ -192,8 +192,8 @@ func TestPhoto_MediaInfo(t *testing.T) { mediaHash, mediaCodec, mediaMime := r.MediaInfo() assert.Equal(t, "53c89dcfa006c9e592dd9e6db4b31cd57be64b81", mediaHash) - assert.Equal(t, video.CodecAVC, mediaCodec) - assert.Equal(t, header.ContentTypeAVC, mediaMime) + assert.Equal(t, video.CodecAvc, mediaCodec) + assert.Equal(t, header.ContentTypeMp4Avc, mediaMime) }) t.Run("VideoCodecHVC", func(t *testing.T) { r := Photo{ @@ -210,28 +210,28 @@ func TestPhoto_MediaInfo(t *testing.T) { { FileVideo: false, MediaType: media.Image.String(), - FileMime: header.ContentTypeJPEG, + FileMime: header.ContentTypeJpeg, FileCodec: "jpeg", }, { FileVideo: true, MediaType: media.Video.String(), - FileMime: header.ContentTypeAVC, + FileMime: header.ContentTypeMp4Avc, FileCodec: "xyz", FileHash: "", }, { FileVideo: true, MediaType: media.Video.String(), - FileCodec: video.CodecHEVC, - FileMime: header.ContentTypeHEVC, + FileCodec: video.CodecHevc, + FileMime: header.ContentTypeMp4Hevc, FileHash: "057258b0c88c2e017ec171cc8799a5df7badbadf", }, { FileVideo: true, MediaType: media.Video.String(), - FileCodec: video.CodecAVC, - FileMime: header.ContentTypeAVC, + FileCodec: video.CodecAvc, + FileMime: header.ContentTypeMp4Avc, FileHash: "ddb3f44eb500d7669cbe0a95e66d5a63f642487d", }, }, @@ -241,8 +241,8 @@ func TestPhoto_MediaInfo(t *testing.T) { mediaHash, mediaCodec, mediaMime := r.MediaInfo() assert.Equal(t, "057258b0c88c2e017ec171cc8799a5df7badbadf", mediaHash) - assert.Equal(t, video.CodecHEVC, mediaCodec) - assert.Equal(t, header.ContentTypeHEVC, mediaMime) + assert.Equal(t, video.CodecHevc, mediaCodec) + assert.Equal(t, header.ContentTypeMp4Hevc, mediaMime) }) t.Run("NoVideoHash", func(t *testing.T) { r := Photo{ @@ -259,7 +259,7 @@ func TestPhoto_MediaInfo(t *testing.T) { { FileVideo: true, MediaType: media.Video.String(), - FileMime: header.ContentTypeAVC, + FileMime: header.ContentTypeMp4Avc, FileHash: "", }, }, diff --git a/internal/entity/search/search_test.go b/internal/entity/search/search_test.go index 3dd609a50..3c7744bac 100644 --- a/internal/entity/search/search_test.go +++ b/internal/entity/search/search_test.go @@ -4,8 +4,9 @@ import ( "os" "testing" - "github.com/photoprism/photoprism/internal/entity" "github.com/sirupsen/logrus" + + "github.com/photoprism/photoprism/internal/entity" ) func TestMain(m *testing.M) { diff --git a/internal/entity/service_test.go b/internal/entity/service_test.go index c72c721fb..46e1109b4 100644 --- a/internal/entity/service_test.go +++ b/internal/entity/service_test.go @@ -3,8 +3,9 @@ package entity import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestCreateService(t *testing.T) { diff --git a/internal/form/label.go b/internal/form/label.go index d92728e6b..c20a5b3ad 100644 --- a/internal/form/label.go +++ b/internal/form/label.go @@ -1,10 +1,11 @@ package form import ( + "github.com/ulule/deepcopier" + "github.com/photoprism/photoprism/pkg/clean" "github.com/photoprism/photoprism/pkg/i18n" "github.com/photoprism/photoprism/pkg/txt" - "github.com/ulule/deepcopier" ) // Label represents a label edit form. diff --git a/internal/meta/exif_parser.go b/internal/meta/exif_parser.go index 6a1229dd6..a38681700 100644 --- a/internal/meta/exif_parser.go +++ b/internal/meta/exif_parser.go @@ -31,7 +31,7 @@ func RawExif(fileName string, fileFormat fs.Type, bruteForce bool) (rawExif []by // Try Exif parser for specific media file format first. switch fileFormat { - case fs.ImageJPEG: + case fs.ImageJpeg: jpegMp := jpegstructure.NewJpegMediaParser() sl, err := jpegMp.ParseFile(fileName) @@ -53,7 +53,7 @@ func RawExif(fileName string, fileFormat fs.Type, bruteForce bool) (rawExif []by parsed = true } } - case fs.ImagePNG: + case fs.ImagePng: pngMp := pngstructure.NewPngMediaParser() cs, err := pngMp.ParseFile(fileName) @@ -73,7 +73,7 @@ func RawExif(fileName string, fileFormat fs.Type, bruteForce bool) (rawExif []by parsed = true } } - case fs.ImageHEIF, fs.ImageHEIC, fs.ImageHEICS, fs.ImageAVIF, fs.ImageAVIFS: + case fs.ImageHeif, fs.ImageHeic, fs.ImageHeicS, fs.ImageAvif, fs.ImageAvifS: heicMp := heicexif.NewHeicExifMediaParser() cs, err := heicMp.ParseFile(fileName) @@ -93,7 +93,7 @@ func RawExif(fileName string, fileFormat fs.Type, bruteForce bool) (rawExif []by parsed = true } } - case fs.ImageTIFF: + case fs.ImageTiff: tiffMp := tiffstructure.NewTiffMediaParser() cs, err := tiffMp.ParseFile(fileName) diff --git a/internal/meta/exif_test.go b/internal/meta/exif_test.go index 6393f70e4..59d977494 100644 --- a/internal/meta/exif_test.go +++ b/internal/meta/exif_test.go @@ -11,7 +11,7 @@ import ( func TestExif(t *testing.T) { t.Run("iptc-2014.jpg", func(t *testing.T) { - data, err := Exif("testdata/iptc-2014.jpg", fs.ImageJPEG, true) + data, err := Exif("testdata/iptc-2014.jpg", fs.ImageJpeg, true) if err != nil { t.Fatal(err) @@ -39,7 +39,7 @@ func TestExif(t *testing.T) { }) t.Run("iptc-2016.jpg", func(t *testing.T) { - data, err := Exif("testdata/iptc-2016.jpg", fs.ImageJPEG, true) + data, err := Exif("testdata/iptc-2016.jpg", fs.ImageJpeg, true) if err != nil { t.Fatal(err) @@ -67,7 +67,7 @@ func TestExif(t *testing.T) { }) t.Run("photoshop.jpg", func(t *testing.T) { - data, err := Exif("testdata/photoshop.jpg", fs.ImageJPEG, true) + data, err := Exif("testdata/photoshop.jpg", fs.ImageJpeg, true) if err != nil { t.Fatal(err) @@ -100,7 +100,7 @@ func TestExif(t *testing.T) { }) t.Run("ladybug.jpg", func(t *testing.T) { - data, err := Exif("testdata/ladybug.jpg", fs.ImageJPEG, true) + data, err := Exif("testdata/ladybug.jpg", fs.ImageJpeg, true) if err != nil { t.Fatal(err) @@ -133,7 +133,7 @@ func TestExif(t *testing.T) { }) t.Run("gopro_hd2.jpg", func(t *testing.T) { - data, err := Exif("testdata/gopro_hd2.jpg", fs.ImageJPEG, true) + data, err := Exif("testdata/gopro_hd2.jpg", fs.ImageJpeg, true) if err != nil { t.Fatal(err) @@ -163,7 +163,7 @@ func TestExif(t *testing.T) { }) t.Run("tweethog.png", func(t *testing.T) { - _, err := Exif("testdata/tweethog.png", fs.ImagePNG, true) + _, err := Exif("testdata/tweethog.png", fs.ImagePng, true) if err == nil { t.Fatal("err should NOT be nil") @@ -173,7 +173,7 @@ func TestExif(t *testing.T) { }) t.Run("iphone_7.heic", func(t *testing.T) { - data, err := Exif("testdata/iphone_7.heic", fs.ImageHEIC, true) + data, err := Exif("testdata/iphone_7.heic", fs.ImageHeic, true) if err != nil { t.Fatal(err) } @@ -194,7 +194,7 @@ func TestExif(t *testing.T) { }) t.Run("gps-2000.jpg", func(t *testing.T) { - data, err := Exif("testdata/gps-2000.jpg", fs.ImageJPEG, true) + data, err := Exif("testdata/gps-2000.jpg", fs.ImageJpeg, true) if err != nil { t.Fatal(err) @@ -222,7 +222,7 @@ func TestExif(t *testing.T) { }) t.Run("image-2011.jpg", func(t *testing.T) { - data, err := Exif("testdata/image-2011.jpg", fs.ImageJPEG, true) + data, err := Exif("testdata/image-2011.jpg", fs.ImageJpeg, true) if err != nil { t.Fatal(err) @@ -259,7 +259,7 @@ func TestExif(t *testing.T) { }) t.Run("ship.jpg", func(t *testing.T) { - data, err := Exif("testdata/ship.jpg", fs.ImageJPEG, true) + data, err := Exif("testdata/ship.jpg", fs.ImageJpeg, true) if err != nil { t.Fatal(err) @@ -281,7 +281,7 @@ func TestExif(t *testing.T) { }) t.Run("no-exif-data.jpg", func(t *testing.T) { - _, err := Exif("testdata/no-exif-data.jpg", fs.ImageJPEG, false) + _, err := Exif("testdata/no-exif-data.jpg", fs.ImageJpeg, false) if err == nil { t.Fatal("err should NOT be nil") @@ -291,7 +291,7 @@ func TestExif(t *testing.T) { }) t.Run("no-exif-data.jpg/BruteForce", func(t *testing.T) { - _, err := Exif("testdata/no-exif-data.jpg", fs.ImageJPEG, true) + _, err := Exif("testdata/no-exif-data.jpg", fs.ImageJpeg, true) if err == nil { t.Fatal("err should NOT be nil") @@ -301,7 +301,7 @@ func TestExif(t *testing.T) { }) t.Run("screenshot.png", func(t *testing.T) { - data, err := Exif("testdata/screenshot.png", fs.ImagePNG, true) + data, err := Exif("testdata/screenshot.png", fs.ImagePng, true) if err != nil { t.Fatal(err) @@ -312,7 +312,7 @@ func TestExif(t *testing.T) { }) t.Run("orientation.jpg", func(t *testing.T) { - data, err := Exif("testdata/orientation.jpg", fs.ImageJPEG, true) + data, err := Exif("testdata/orientation.jpg", fs.ImageJpeg, true) if err != nil { t.Fatal(err) @@ -340,19 +340,19 @@ func TestExif(t *testing.T) { }) t.Run("gopher-preview.jpg", func(t *testing.T) { - _, err := Exif("testdata/gopher-preview.jpg", fs.ImageJPEG, false) + _, err := Exif("testdata/gopher-preview.jpg", fs.ImageJpeg, false) assert.EqualError(t, err, "found no exif header") }) t.Run("gopher-preview.jpg/BruteForce", func(t *testing.T) { - _, err := Exif("testdata/gopher-preview.jpg", fs.ImageJPEG, true) + _, err := Exif("testdata/gopher-preview.jpg", fs.ImageJpeg, true) assert.EqualError(t, err, "found no exif data") }) t.Run("huawei-gps-error.jpg", func(t *testing.T) { - data, err := Exif("testdata/huawei-gps-error.jpg", fs.ImageJPEG, true) + data, err := Exif("testdata/huawei-gps-error.jpg", fs.ImageJpeg, true) if err != nil { t.Fatal(err) @@ -374,7 +374,7 @@ func TestExif(t *testing.T) { }) t.Run("panorama360.jpg", func(t *testing.T) { - data, err := Exif("testdata/panorama360.jpg", fs.ImageJPEG, true) + data, err := Exif("testdata/panorama360.jpg", fs.ImageJpeg, true) if err != nil { t.Fatal(err) @@ -406,7 +406,7 @@ func TestExif(t *testing.T) { }) t.Run("exif-example.tiff", func(t *testing.T) { - data, err := Exif("testdata/exif-example.tiff", fs.ImageTIFF, true) + data, err := Exif("testdata/exif-example.tiff", fs.ImageTiff, true) if err != nil { t.Fatal(err) @@ -438,7 +438,7 @@ func TestExif(t *testing.T) { }) t.Run("out-of-range-500.jpg", func(t *testing.T) { - data, err := Exif("testdata/out-of-range-500.jpg", fs.ImageJPEG, true) + data, err := Exif("testdata/out-of-range-500.jpg", fs.ImageJpeg, true) if err != nil { t.Fatal(err) @@ -470,7 +470,7 @@ func TestExif(t *testing.T) { }) t.Run("digikam.jpg", func(t *testing.T) { - data, err := Exif("testdata/digikam.jpg", fs.ImageJPEG, true) + data, err := Exif("testdata/digikam.jpg", fs.ImageJpeg, true) if err != nil { t.Fatal(err) @@ -505,7 +505,7 @@ func TestExif(t *testing.T) { }) t.Run("notebook.jpg", func(t *testing.T) { - data, err := Exif("testdata/notebook.jpg", fs.ImageJPEG, true) + data, err := Exif("testdata/notebook.jpg", fs.ImageJpeg, true) if err != nil { t.Fatal(err) @@ -526,7 +526,7 @@ func TestExif(t *testing.T) { }) t.Run("snow.jpg", func(t *testing.T) { - data, err := Exif("testdata/snow.jpg", fs.ImageJPEG, true) + data, err := Exif("testdata/snow.jpg", fs.ImageJpeg, true) if err != nil { t.Fatal(err) @@ -547,7 +547,7 @@ func TestExif(t *testing.T) { }) t.Run("keywords.jpg", func(t *testing.T) { - data, err := Exif("testdata/keywords.jpg", fs.ImageJPEG, true) + data, err := Exif("testdata/keywords.jpg", fs.ImageJpeg, true) if err != nil { t.Fatal(err) @@ -567,7 +567,7 @@ func TestExif(t *testing.T) { }) t.Run("Iceland-P3.jpg", func(t *testing.T) { - data, err := Exif("testdata/Iceland-P3.jpg", fs.ImageJPEG, true) + data, err := Exif("testdata/Iceland-P3.jpg", fs.ImageJpeg, true) if err != nil { t.Fatal(err) @@ -599,7 +599,7 @@ func TestExif(t *testing.T) { }) t.Run("Iceland-sRGB.jpg", func(t *testing.T) { - data, err := Exif("testdata/Iceland-sRGB.jpg", fs.ImageJPEG, true) + data, err := Exif("testdata/Iceland-sRGB.jpg", fs.ImageJpeg, true) if err != nil { t.Fatal(err) @@ -631,7 +631,7 @@ func TestExif(t *testing.T) { }) t.Run("animated.gif", func(t *testing.T) { - _, err := Exif("testdata/animated.gif", fs.ImageGIF, true) + _, err := Exif("testdata/animated.gif", fs.ImageGif, true) if err == nil { t.Fatal("error expected") @@ -641,7 +641,7 @@ func TestExif(t *testing.T) { }) t.Run("aurora.jpg", func(t *testing.T) { - data, err := Exif("testdata/aurora.jpg", fs.ImageJPEG, false) + data, err := Exif("testdata/aurora.jpg", fs.ImageJpeg, false) if err != nil { t.Fatal(err) @@ -656,7 +656,7 @@ func TestExif(t *testing.T) { }) t.Run("buggy_panorama.jpg", func(t *testing.T) { - data, err := Exif("testdata/buggy_panorama.jpg", fs.ImageJPEG, false) + data, err := Exif("testdata/buggy_panorama.jpg", fs.ImageJpeg, false) if err != nil { t.Fatal(err) @@ -672,7 +672,7 @@ func TestExif(t *testing.T) { }) t.Run("altitude.jpg", func(t *testing.T) { - data, err := Exif("testdata/altitude.jpg", fs.ImageJPEG, false) + data, err := Exif("testdata/altitude.jpg", fs.ImageJpeg, false) if err != nil { t.Fatal(err) diff --git a/internal/meta/json_exiftool.go b/internal/meta/json_exiftool.go index c1165684b..b35082b91 100644 --- a/internal/meta/json_exiftool.go +++ b/internal/meta/json_exiftool.go @@ -21,7 +21,7 @@ import ( "github.com/photoprism/photoprism/pkg/txt" ) -const MimeVideoMP4 = "video/mp4" +const MimeVideoMp4 = "video/mp4" const MimeQuicktime = "video/quicktime" // Exiftool parses JSON sidecar data as created by Exiftool. @@ -232,7 +232,7 @@ func (data *Data) Exiftool(jsonData []byte, originalName string) (err error) { // Has time zone offset? if _, offset := data.TakenAtLocal.Zone(); offset != 0 && !data.TakenAtLocal.IsZero() { hasTimeOffset = true - } else if mt, ok := data.json["MIMEType"]; ok && data.TakenAtLocal.IsZero() && (mt == MimeVideoMP4 || mt == MimeQuicktime) { + } else if mt, ok := data.json["MIMEType"]; ok && data.TakenAtLocal.IsZero() && (mt == MimeVideoMp4 || mt == MimeQuicktime) { // Assume default time zone for MP4 & Quicktime videos is UTC. // see https://exiftool.org/TagNames/QuickTime.html log.Debugf("metadata: default time zone for %s is UTC (%s)", logName, clean.Log(mt)) @@ -396,7 +396,7 @@ func (data *Data) Exiftool(jsonData []byte, originalName string) (err error) { } // Flag Samsung/Google Motion Photos as live media. - if data.HasVideoEmbedded && (data.MimeType == fs.MimeTypeJPEG || data.MimeType == fs.MimeTypeHEIC) { + if data.HasVideoEmbedded && (data.MimeType == fs.MimeTypeJpeg || data.MimeType == fs.MimeTypeHeic) { data.MediaType = media.Live } diff --git a/internal/meta/json_gphotos.go b/internal/meta/json_gphotos.go index f7fe35f9f..bee6891fc 100644 --- a/internal/meta/json_gphotos.go +++ b/internal/meta/json_gphotos.go @@ -6,8 +6,9 @@ import ( "runtime/debug" "time" - "github.com/photoprism/photoprism/pkg/txt" "gopkg.in/photoprism/go-tz.v2/tz" + + "github.com/photoprism/photoprism/pkg/txt" ) type GPhoto struct { diff --git a/internal/meta/json_test.go b/internal/meta/json_test.go index 16697d4aa..856ce16d0 100644 --- a/internal/meta/json_test.go +++ b/internal/meta/json_test.go @@ -112,7 +112,7 @@ func TestJSON(t *testing.T) { } assert.Equal(t, "earth.ogv", data.FileName) - assert.Equal(t, video.CodecOGV, data.Codec) + assert.Equal(t, video.CodecTheora, data.Codec) assert.Equal(t, "0s", data.Duration.String()) assert.Equal(t, 1280, data.Width) assert.Equal(t, 720, data.Height) @@ -128,7 +128,7 @@ func TestJSON(t *testing.T) { } assert.Equal(t, "earth.vp8.webm", data.FileName) - assert.Equal(t, video.CodecVP8, data.Codec) + assert.Equal(t, video.CodecVp8, data.Codec) assert.Equal(t, "30s", data.Duration.String()) assert.Equal(t, 1920, data.Width) assert.Equal(t, 1080, data.Height) @@ -144,7 +144,7 @@ func TestJSON(t *testing.T) { } assert.Equal(t, "earth-animation.ogv.720p.vp9.webm", data.FileName) - assert.Equal(t, video.CodecVP9, data.Codec) + assert.Equal(t, video.CodecVp9, data.Codec) assert.Equal(t, "8.03s", data.Duration.String()) assert.Equal(t, 1280, data.Width) assert.Equal(t, 720, data.Height) @@ -705,7 +705,7 @@ func TestJSON(t *testing.T) { t.Fatal(err) } - assert.Equal(t, video.CodecAVC, data.Codec) + assert.Equal(t, video.CodecAvc, data.Codec) assert.Equal(t, "10.67s", data.Duration.String()) assert.Equal(t, "2015-12-06 18:22:29 +0000 UTC", data.TakenAtLocal.String()) assert.Equal(t, "2015-12-06 15:22:29 +0000 UTC", data.TakenAt.String()) @@ -730,7 +730,7 @@ func TestJSON(t *testing.T) { t.Fatal(err) } - assert.Equal(t, video.CodecHEVC, data.Codec) + assert.Equal(t, video.CodecHevc, data.Codec) assert.Equal(t, "6.83s", data.Duration.String()) assert.Equal(t, "2020-12-22 02:45:43 +0000 UTC", data.TakenAtLocal.String()) assert.Equal(t, "2020-12-22 01:45:43 +0000 UTC", data.TakenAt.String()) @@ -754,7 +754,7 @@ func TestJSON(t *testing.T) { t.Fatal(err) } - assert.Equal(t, video.CodecHEVC, data.Codec) + assert.Equal(t, video.CodecHevc, data.Codec) assert.Equal(t, "2.15s", data.Duration.String()) assert.Equal(t, "2019-12-12 20:47:21 +0000 UTC", data.TakenAtLocal.String()) assert.Equal(t, "2019-12-13 01:47:21 +0000 UTC", data.TakenAt.String()) @@ -792,7 +792,7 @@ func TestJSON(t *testing.T) { t.Fatal(err) } - assert.Equal(t, video.CodecAVC, data.Codec) + assert.Equal(t, video.CodecAvc, data.Codec) assert.Equal(t, "6.09s", data.Duration.String()) assert.Equal(t, "2022-06-25 06:50:58 +0000 UTC", data.TakenAtLocal.String()) assert.Equal(t, "2022-06-25 04:50:58 +0000 UTC", data.TakenAt.String()) @@ -969,7 +969,7 @@ func TestJSON(t *testing.T) { t.Fatal(err) } - assert.Equal(t, video.CodecAVC, data.Codec) + assert.Equal(t, video.CodecAvc, data.Codec) assert.Equal(t, "1.03s", data.Duration.String()) assert.Equal(t, "2012-07-11 07:16:01 +0000 UTC", data.TakenAtLocal.String()) assert.Equal(t, "2012-07-11 05:16:01 +0000 UTC", data.TakenAt.String()) @@ -986,7 +986,7 @@ func TestJSON(t *testing.T) { t.Fatal(err) } - assert.Equal(t, video.CodecAVC, data.Codec) + assert.Equal(t, video.CodecAvc, data.Codec) assert.Equal(t, "1.03s", data.Duration.String()) assert.Equal(t, "2012-07-11 07:16:01 +0000 UTC", data.TakenAtLocal.String()) assert.Equal(t, "2012-07-11 05:16:01 +0000 UTC", data.TakenAt.String()) @@ -1003,7 +1003,7 @@ func TestJSON(t *testing.T) { t.Fatal(err) } - assert.Equal(t, video.CodecAVC, data.Codec) + assert.Equal(t, video.CodecAvc, data.Codec) assert.Equal(t, "1.03s", data.Duration.String()) assert.Equal(t, "2012-07-11 07:16:01 +0000 UTC", data.TakenAtLocal.String()) assert.Equal(t, "2012-07-11 05:16:01 +0000 UTC", data.TakenAt.String()) diff --git a/internal/meta/video.go b/internal/meta/video.go index 1cbcf4253..251357568 100644 --- a/internal/meta/video.go +++ b/internal/meta/video.go @@ -5,9 +5,9 @@ import ( ) const CodecUnknown = "" -const CodecAv1 = video.CodecAV1 -const CodecAvc1 = video.CodecAVC -const CodecHvc1 = video.CodecHEVC +const CodecAv1 = video.CodecAv1 +const CodecAvc1 = video.CodecAvc +const CodecHvc1 = video.CodecHevc const CodecJpeg = "jpeg" const CodecHeic = "heic" const CodecXMP = "xmp" diff --git a/internal/photoprism/convert_fix.go b/internal/photoprism/convert_fix.go index ff1bf4a69..d9e1f6af6 100644 --- a/internal/photoprism/convert_fix.go +++ b/internal/photoprism/convert_fix.go @@ -21,7 +21,7 @@ func (w *Convert) FixJpeg(f *MediaFile, force bool) (*MediaFile, error) { logName := clean.Log(f.RootRelName()) - if w.conf.DisableImageMagick() || !w.imageMagickExclude.Allow(fs.ExtJPEG) { + if w.conf.DisableImageMagick() || !w.imageMagickExclude.Allow(fs.ExtJpeg) { return nil, fmt.Errorf("convert: ImageMagick must be enabled to re-encode %s", logName) } @@ -42,7 +42,7 @@ func (w *Convert) FixJpeg(f *MediaFile, force bool) (*MediaFile, error) { cacheDir := w.conf.MediaFileCachePath(fileHash) // Compose cache filename. - cacheName := filepath.Join(cacheDir, fileHash+fs.ExtJPEG) + cacheName := filepath.Join(cacheDir, fileHash+fs.ExtJpeg) mediaFile, err := NewMediaFile(cacheName) diff --git a/internal/photoprism/convert_image.go b/internal/photoprism/convert_image.go index e86c25735..3fcc7fdb4 100644 --- a/internal/photoprism/convert_image.go +++ b/internal/photoprism/convert_image.go @@ -36,10 +36,10 @@ func (w *Convert) ToImage(f *MediaFile, force bool) (result *MediaFile, err erro return f, nil } - imageName := fs.ImagePNG.FindFirst(f.FileName(), []string{w.conf.SidecarPath(), fs.PPHiddenPathname}, w.conf.OriginalsPath(), false) + imageName := fs.ImagePng.FindFirst(f.FileName(), []string{w.conf.SidecarPath(), fs.PPHiddenPathname}, w.conf.OriginalsPath(), false) if imageName == "" { - imageName = fs.ImageJPEG.FindFirst(f.FileName(), []string{w.conf.SidecarPath(), fs.PPHiddenPathname}, w.conf.OriginalsPath(), false) + imageName = fs.ImageJpeg.FindFirst(f.FileName(), []string{w.conf.SidecarPath(), fs.PPHiddenPathname}, w.conf.OriginalsPath(), false) } mediaFile, err := NewMediaFile(imageName) @@ -59,9 +59,9 @@ func (w *Convert) ToImage(f *MediaFile, force bool) (result *MediaFile, err erro if !w.conf.VectorEnabled() { return nil, fmt.Errorf("convert: vector graphics support disabled (%s)", clean.Log(f.RootRelName())) } - imageName, _ = fs.FileName(f.FileName(), w.conf.SidecarPath(), w.conf.OriginalsPath(), fs.ExtPNG) + imageName, _ = fs.FileName(f.FileName(), w.conf.SidecarPath(), w.conf.OriginalsPath(), fs.ExtPng) } else { - imageName, _ = fs.FileName(f.FileName(), w.conf.SidecarPath(), w.conf.OriginalsPath(), fs.ExtJPEG) + imageName, _ = fs.FileName(f.FileName(), w.conf.SidecarPath(), w.conf.OriginalsPath(), fs.ExtJpeg) } if !w.conf.SidecarWritable() { @@ -88,9 +88,9 @@ func (w *Convert) ToImage(f *MediaFile, force bool) (result *MediaFile, err erro // Create PNG or JPEG image from source file. switch fs.LowerExt(imageName) { - case fs.ExtPNG: + case fs.ExtPng: _, err = thumb.Png(f.FileName(), imageName, f.Orientation()) - case fs.ExtJPEG: + case fs.ExtJpeg: _, err = thumb.Jpeg(f.FileName(), imageName, f.Orientation()) default: return nil, fmt.Errorf("convert: unspported target format %s (%s)", fs.LowerExt(imageName), clean.Log(f.RootRelName())) @@ -100,7 +100,7 @@ func (w *Convert) ToImage(f *MediaFile, force bool) (result *MediaFile, err erro if err == nil { log.Infof("convert: %s created in %s (%s)", clean.Log(filepath.Base(imageName)), time.Since(start), f.FileType()) return NewMediaFile(imageName) - } else if !f.IsTIFF() && !f.IsWebP() { + } else if !f.IsTiff() && !f.IsWebp() { // See https://github.com/photoprism/photoprism/issues/1612 // for TIFF file format compatibility. return nil, err @@ -113,12 +113,12 @@ func (w *Convert) ToImage(f *MediaFile, force bool) (result *MediaFile, err erro var expectedMime string switch fs.LowerExt(imageName) { - case fs.ExtPNG: + case fs.ExtPng: cmds, useMutex, err = w.PngConvertCmds(f, imageName) - expectedMime = fs.MimeTypePNG - case fs.ExtJPEG: + expectedMime = fs.MimeTypePng + case fs.ExtJpeg: cmds, useMutex, err = w.JpegConvertCmds(f, imageName, xmpName) - expectedMime = fs.MimeTypeJPEG + expectedMime = fs.MimeTypeJpeg default: return nil, fmt.Errorf("convert: unspported target format %s (%s)", fs.LowerExt(imageName), clean.Log(f.RootRelName())) } diff --git a/internal/photoprism/convert_image_jpeg.go b/internal/photoprism/convert_image_jpeg.go index b58270ec6..4b9c12c63 100644 --- a/internal/photoprism/convert_image_jpeg.go +++ b/internal/photoprism/convert_image_jpeg.go @@ -23,21 +23,21 @@ func (w *Convert) JpegConvertCmds(f *MediaFile, jpegName string, xmpName string) maxSize := strconv.Itoa(w.conf.JpegSize()) // Apple Scriptable image processing system: https://ss64.com/osx/sips.html - if (f.IsRaw() || f.IsHEIF()) && w.conf.SipsEnabled() && w.sipsExclude.Allow(fileExt) { + if (f.IsRaw() || f.IsHeif()) && w.conf.SipsEnabled() && w.sipsExclude.Allow(fileExt) { result = append(result, NewConvertCmd( exec.Command(w.conf.SipsBin(), "-Z", maxSize, "-s", "format", "jpeg", "--out", jpegName, f.FileName())), ) } // Extract a video still image for use as a thumbnail (poster image). - if f.IsAnimated() && !f.IsWebP() && w.conf.FFmpegEnabled() { + if f.IsAnimated() && !f.IsWebp() && w.conf.FFmpegEnabled() { result = append(result, NewConvertCmd( ffmpeg.ExtractJpegImageCmd(f.FileName(), jpegName, encode.NewPreviewImageOptions(w.conf.FFmpegBin(), f.Duration()))), ) } // Use heif-convert for HEIC/HEIF and AVIF image files. - if (f.IsHEIC() || f.IsAVIF()) && w.conf.HeifConvertEnabled() { + if (f.IsHeic() || f.IsAVIF()) && w.conf.HeifConvertEnabled() { result = append(result, NewConvertCmd( exec.Command(w.conf.HeifConvertBin(), "-q", w.conf.JpegQuality().String(), f.FileName(), jpegName)). WithOrientation(w.conf.HeifConvertOrientation()), @@ -110,7 +110,7 @@ func (w *Convert) JpegConvertCmds(f *MediaFile, jpegName string, xmpName string) // Try ImageMagick for other image file formats if allowed. if w.conf.ImageMagickEnabled() && w.imageMagickExclude.Allow(fileExt) && - (f.IsImage() && !f.IsJpegXL() && !f.IsRaw() && !f.IsHEIF() || f.IsVector() && w.conf.VectorEnabled()) { + (f.IsImage() && !f.IsJpegXL() && !f.IsRaw() && !f.IsHeif() || f.IsVector() && w.conf.VectorEnabled()) { quality := fmt.Sprintf("%d", w.conf.JpegQuality()) resize := fmt.Sprintf("%dx%d>", w.conf.JpegSize(), w.conf.JpegSize()) args := []string{f.FileName(), "-flatten", "-resize", resize, "-quality", quality, jpegName} diff --git a/internal/photoprism/convert_image_png.go b/internal/photoprism/convert_image_png.go index 303aaf297..9b1692ef2 100644 --- a/internal/photoprism/convert_image_png.go +++ b/internal/photoprism/convert_image_png.go @@ -22,14 +22,14 @@ func (w *Convert) PngConvertCmds(f *MediaFile, pngName string) (result ConvertCm maxSize := strconv.Itoa(w.conf.PngSize()) // Apple Scriptable image processing system: https://ss64.com/osx/sips.html - if (f.IsRaw() || f.IsHEIF()) && w.conf.SipsEnabled() && w.sipsExclude.Allow(fileExt) { + if (f.IsRaw() || f.IsHeif()) && w.conf.SipsEnabled() && w.sipsExclude.Allow(fileExt) { result = append(result, NewConvertCmd( exec.Command(w.conf.SipsBin(), "-Z", maxSize, "-s", "format", "png", "--out", pngName, f.FileName())), ) } // Extract a video still image that can be used as preview. - if f.IsAnimated() && !f.IsWebP() && w.conf.FFmpegEnabled() { + if f.IsAnimated() && !f.IsWebp() && w.conf.FFmpegEnabled() { // Use "ffmpeg" to extract a PNG still image from the video. result = append(result, NewConvertCmd( ffmpeg.ExtractPngImageCmd(f.FileName(), pngName, encode.NewPreviewImageOptions(w.conf.FFmpegBin(), f.Duration()))), @@ -37,7 +37,7 @@ func (w *Convert) PngConvertCmds(f *MediaFile, pngName string) (result ConvertCm } // Use heif-convert for HEIC/HEIF and AVIF image files. - if (f.IsHEIC() || f.IsAVIF()) && w.conf.HeifConvertEnabled() { + if (f.IsHeic() || f.IsAVIF()) && w.conf.HeifConvertEnabled() { result = append(result, NewConvertCmd( exec.Command(w.conf.HeifConvertBin(), f.FileName(), pngName)). WithOrientation(w.conf.HeifConvertOrientation()), @@ -59,7 +59,7 @@ func (w *Convert) PngConvertCmds(f *MediaFile, pngName string) (result ConvertCm exec.Command(w.conf.RsvgConvertBin(), args...)), ) } else if w.conf.ImageMagickEnabled() && w.imageMagickExclude.Allow(fileExt) && - (f.IsImage() && !f.IsJpegXL() && !f.IsRaw() && !f.IsHEIF() || f.IsVector() && w.conf.VectorEnabled()) { + (f.IsImage() && !f.IsJpegXL() && !f.IsRaw() && !f.IsHeif() || f.IsVector() && w.conf.VectorEnabled()) { resize := fmt.Sprintf("%dx%d>", w.conf.PngSize(), w.conf.PngSize()) args := []string{f.FileName(), "-flatten", "-resize", resize, pngName} result = append(result, NewConvertCmd( diff --git a/internal/photoprism/convert_image_test.go b/internal/photoprism/convert_image_test.go index 0e9b1d9e6..f6fcf828a 100644 --- a/internal/photoprism/convert_image_test.go +++ b/internal/photoprism/convert_image_test.go @@ -6,9 +6,10 @@ import ( "strings" "testing" + "github.com/stretchr/testify/assert" + "github.com/photoprism/photoprism/internal/config" "github.com/photoprism/photoprism/pkg/fs" - "github.com/stretchr/testify/assert" ) func TestConvert_ToImage(t *testing.T) { diff --git a/internal/photoprism/convert_sidecar_json_test.go b/internal/photoprism/convert_sidecar_json_test.go index da39c5164..65d1cc2b9 100644 --- a/internal/photoprism/convert_sidecar_json_test.go +++ b/internal/photoprism/convert_sidecar_json_test.go @@ -5,9 +5,10 @@ import ( "path/filepath" "testing" + "github.com/stretchr/testify/assert" + "github.com/photoprism/photoprism/internal/config" "github.com/photoprism/photoprism/pkg/fs" - "github.com/stretchr/testify/assert" ) func TestConvert_ToJson(t *testing.T) { diff --git a/internal/photoprism/convert_test.go b/internal/photoprism/convert_test.go index 615570fa7..2bcc1c647 100644 --- a/internal/photoprism/convert_test.go +++ b/internal/photoprism/convert_test.go @@ -5,9 +5,10 @@ import ( "path/filepath" "testing" + "github.com/stretchr/testify/assert" + "github.com/photoprism/photoprism/internal/config" "github.com/photoprism/photoprism/pkg/fs" - "github.com/stretchr/testify/assert" ) func TestNewConvert(t *testing.T) { diff --git a/internal/photoprism/convert_video_avc.go b/internal/photoprism/convert_video_avc.go index fdfa7968e..e77ebe133 100644 --- a/internal/photoprism/convert_video_avc.go +++ b/internal/photoprism/convert_video_avc.go @@ -36,9 +36,9 @@ func (w *Convert) ToAvc(f *MediaFile, encoder encode.Encoder, noMutex, force boo // Use .mp4 file extension for animated images and .avi for videos. if f.IsAnimatedImage() { - avcName = fs.VideoMP4.FindFirst(f.FileName(), []string{w.conf.SidecarPath(), fs.PPHiddenPathname}, w.conf.OriginalsPath(), false) + avcName = fs.VideoMp4.FindFirst(f.FileName(), []string{w.conf.SidecarPath(), fs.PPHiddenPathname}, w.conf.OriginalsPath(), false) } else { - avcName = fs.VideoAVC.FindFirst(f.FileName(), []string{w.conf.SidecarPath(), fs.PPHiddenPathname}, w.conf.OriginalsPath(), false) + avcName = fs.VideoAvc.FindFirst(f.FileName(), []string{w.conf.SidecarPath(), fs.PPHiddenPathname}, w.conf.OriginalsPath(), false) } mediaFile, err := NewMediaFile(avcName) @@ -61,9 +61,9 @@ func (w *Convert) ToAvc(f *MediaFile, encoder encode.Encoder, noMutex, force boo // Use .mp4 file extension for animated images and .avi for videos. if f.IsAnimatedImage() { - avcName, _ = fs.FileName(f.FileName(), w.conf.SidecarPath(), w.conf.OriginalsPath(), fs.ExtMP4) + avcName, _ = fs.FileName(f.FileName(), w.conf.SidecarPath(), w.conf.OriginalsPath(), fs.ExtMp4) } else { - avcName, _ = fs.FileName(f.FileName(), w.conf.SidecarPath(), w.conf.OriginalsPath(), fs.ExtAVC) + avcName, _ = fs.FileName(f.FileName(), w.conf.SidecarPath(), w.conf.OriginalsPath(), fs.ExtAvc) } cmd, useMutex, err := w.TranscodeToAvcCmd(f, avcName, encoder) @@ -110,7 +110,7 @@ func (w *Convert) ToAvc(f *MediaFile, encoder encode.Encoder, noMutex, force boo "xmpName": "", }) - log.Infof("%s: transcoding %s to %s", encoder, relName, fs.VideoAVC) + log.Infof("%s: transcoding %s to %s", encoder, relName, fs.VideoAvc) // Log exact command for debugging in trace mode. log.Trace(cmd.String()) @@ -165,7 +165,7 @@ func (w *Convert) TranscodeToAvcCmd(f *MediaFile, avcName string, encoder encode } // Try to transcode animated WebP images with ImageMagick. - if w.conf.ImageMagickEnabled() && f.IsWebP() && w.imageMagickExclude.Allow(fileExt) { + if w.conf.ImageMagickEnabled() && f.IsWebp() && w.imageMagickExclude.Allow(fileExt) { return exec.Command(w.conf.ImageMagickBin(), f.FileName(), avcName), false, nil } diff --git a/internal/photoprism/faces_stats.go b/internal/photoprism/faces_stats.go index cf877a68f..e25777245 100644 --- a/internal/photoprism/faces_stats.go +++ b/internal/photoprism/faces_stats.go @@ -2,6 +2,7 @@ package photoprism import ( "github.com/montanaflynn/stats" + "github.com/photoprism/photoprism/internal/entity/query" ) diff --git a/internal/photoprism/files_test.go b/internal/photoprism/files_test.go index 36778ec43..4fcb9ad1e 100644 --- a/internal/photoprism/files_test.go +++ b/internal/photoprism/files_test.go @@ -4,8 +4,9 @@ import ( "testing" "time" - "github.com/photoprism/photoprism/internal/entity" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/entity" ) func TestFiles_Ignore(t *testing.T) { diff --git a/internal/photoprism/import_test.go b/internal/photoprism/import_test.go index c84f1d6da..36ae6ca5a 100644 --- a/internal/photoprism/import_test.go +++ b/internal/photoprism/import_test.go @@ -3,11 +3,12 @@ package photoprism import ( "testing" + "github.com/stretchr/testify/assert" + "github.com/photoprism/photoprism/internal/ai/classify" "github.com/photoprism/photoprism/internal/ai/face" "github.com/photoprism/photoprism/internal/ai/nsfw" "github.com/photoprism/photoprism/internal/config" - "github.com/stretchr/testify/assert" ) func TestNewImport(t *testing.T) { diff --git a/internal/photoprism/import_worker_test.go b/internal/photoprism/import_worker_test.go index fe34c7ca5..a2430d88d 100644 --- a/internal/photoprism/import_worker_test.go +++ b/internal/photoprism/import_worker_test.go @@ -3,12 +3,13 @@ package photoprism import ( "testing" + "github.com/stretchr/testify/assert" + "github.com/photoprism/photoprism/internal/ai/classify" "github.com/photoprism/photoprism/internal/ai/face" "github.com/photoprism/photoprism/internal/ai/nsfw" "github.com/photoprism/photoprism/internal/config" "github.com/photoprism/photoprism/internal/entity" - "github.com/stretchr/testify/assert" ) func TestImportWorker_OriginalFileNames(t *testing.T) { diff --git a/internal/photoprism/index_mediafile.go b/internal/photoprism/index_mediafile.go index 91113c0fd..829abc34a 100644 --- a/internal/photoprism/index_mediafile.go +++ b/internal/photoprism/index_mediafile.go @@ -259,7 +259,7 @@ func (ind *Index) UserMediaFile(m *MediaFile, o IndexOptions, originalName, phot photo.PhotoStack = entity.IsStackable } - if yamlName := fs.SidecarYAML.FindFirst(m.FileName(), []string{Config().SidecarPath(), fs.PPHiddenPathname}, Config().OriginalsPath(), stripSequence); yamlName != "" { + if yamlName := fs.SidecarYaml.FindFirst(m.FileName(), []string{Config().SidecarPath(), fs.PPHiddenPathname}, Config().OriginalsPath(), stripSequence); yamlName != "" { if err = photo.LoadFromYaml(yamlName); err != nil { log.Errorf("index: %s in %s (restore from yaml)", err.Error(), logName) } else if photo.HasUID() { @@ -527,7 +527,7 @@ func (ind *Index) UserMediaFile(m *MediaFile, o IndexOptions, originalName, phot file.SetSoftware(data.Software) // Get video metadata from embedded file? - if !m.IsHEIC() || !data.HasVideoEmbedded { + if !m.IsHeic() || !data.HasVideoEmbedded { file.SetDuration(data.Duration) file.SetFPS(data.FPS) file.SetFrames(data.Frames) @@ -816,7 +816,7 @@ func (ind *Index) UserMediaFile(m *MediaFile, o IndexOptions, originalName, phot file.ModTime = modTime.UTC().Truncate(time.Second).Unix() // Detect ICC color profile for JPEGs if still unknown at this point. - if file.FileColorProfile == "" && fs.ImageJPEG.Equal(file.FileType) { + if file.FileColorProfile == "" && fs.ImageJpeg.Equal(file.FileType) { file.SetColorProfile(m.ColorProfile()) } diff --git a/internal/photoprism/mediafile.go b/internal/photoprism/mediafile.go index 891fc4653..e4c8bc2e1 100644 --- a/internal/photoprism/mediafile.go +++ b/internal/photoprism/mediafile.go @@ -708,14 +708,14 @@ func (m *MediaFile) IsPreviewImage() bool { return false } - return m.IsJpeg() || m.IsPNG() + return m.IsJpeg() || m.IsPng() } // IsJpeg checks if the file is a JPEG image with a supported file type extension. func (m *MediaFile) IsJpeg() bool { if m == nil { return false - } else if fs.FileType(m.fileName) != fs.ImageJPEG { + } else if fs.FileType(m.fileName) != fs.ImageJpeg { // Thumbnails and other JPEGs with an unsupported file extension are no longer indexed as // JPEG to improve performance (skips mime type detection) and to avoid follow-up issues // with external tools that rely on a correct file extension. @@ -723,49 +723,49 @@ func (m *MediaFile) IsJpeg() bool { } // Check the mime type after other tests have passed to improve performance. - return m.HasMimeType(fs.MimeTypeJPEG) + return m.HasMimeType(fs.MimeTypeJpeg) } // IsJpegXL checks if the file is a JPEG XL image with a supported file type extension. func (m *MediaFile) IsJpegXL() bool { - if fs.FileType(m.fileName) != fs.ImageJPEGXL { + if fs.FileType(m.fileName) != fs.ImageJpegXL { return false } // Check the mime type after other tests have passed to improve performance. - return m.HasMimeType(fs.MimeTypeJPEGXL) + return m.HasMimeType(fs.MimeTypeJpegXL) } // IsPNG checks if the file is a PNG image with a supported file type extension. -func (m *MediaFile) IsPNG() bool { - if fs.FileType(m.fileName) != fs.ImagePNG { +func (m *MediaFile) IsPng() bool { + if fs.FileType(m.fileName) != fs.ImagePng { // Files with an incorrect file extension are no longer // recognized as PNG to improve indexing performance. return false } // Check the mime type after other tests have passed to improve performance. - return m.HasMimeType(fs.MimeTypePNG) || m.HasMimeType(fs.MimeTypeAPNG) + return m.HasMimeType(fs.MimeTypePng) || m.HasMimeType(fs.MimeTypeAPng) } // IsGIF checks if the file is a GIF image with a supported file type extension. -func (m *MediaFile) IsGIF() bool { - if fs.FileType(m.fileName) != fs.ImageGIF { +func (m *MediaFile) IsGif() bool { + if fs.FileType(m.fileName) != fs.ImageGif { return false } // Check the mime type after other tests have passed to improve performance. - return m.HasMimeType(fs.MimeTypeGIF) + return m.HasMimeType(fs.MimeTypeGif) } // IsTIFF checks if the file is a TIFF image with a supported file type extension. -func (m *MediaFile) IsTIFF() bool { - if fs.FileType(m.fileName) != fs.ImageTIFF { +func (m *MediaFile) IsTiff() bool { + if fs.FileType(m.fileName) != fs.ImageTiff { return false } // Check the mime type after other tests have passed to improve performance. - return m.HasMimeType(fs.MimeTypeTIFF) + return m.HasMimeType(fs.MimeTypeTiff) } // IsDNG checks if the file is a Adobe Digital Negative (DNG) image with a supported file type extension. @@ -778,56 +778,56 @@ func (m *MediaFile) IsDNG() bool { } // IsHEIF checks if the file is a High Efficiency Image File Format (HEIF) container with a supported file type extension. -func (m *MediaFile) IsHEIF() bool { - return m.IsHEIC() || m.IsHEICS() || m.IsAVIF() || m.IsAVIFS() +func (m *MediaFile) IsHeif() bool { + return m.IsHeic() || m.IsHeicS() || m.IsAVIF() || m.IsAVIFS() } // IsHEIC checks if the file is a High Efficiency Image Container (HEIC) image with a supported file type extension. -func (m *MediaFile) IsHEIC() bool { - if t := fs.FileType(m.fileName); t != fs.ImageHEIF && t != fs.ImageHEIC { +func (m *MediaFile) IsHeic() bool { + if t := fs.FileType(m.fileName); t != fs.ImageHeif && t != fs.ImageHeic { return false } // Check the mime type after other tests have passed to improve performance. - return m.HasMimeType(fs.MimeTypeHEIC) || m.HasMimeType(fs.MimeTypeHEICS) + return m.HasMimeType(fs.MimeTypeHeic) || m.HasMimeType(fs.MimeTypeHeicS) } // IsHEICS checks if the file is a HEIC image sequence with a supported file type extension. -func (m *MediaFile) IsHEICS() bool { - return m.HasFileType(fs.ImageHEICS) +func (m *MediaFile) IsHeicS() bool { + return m.HasFileType(fs.ImageHeicS) } // IsAVIF checks if the file is an AV1 Image File Format image with a supported file type extension. func (m *MediaFile) IsAVIF() bool { - if t := fs.FileType(m.fileName); t != fs.ImageAVIF { + if t := fs.FileType(m.fileName); t != fs.ImageAvif { return false } - return m.HasMimeType(fs.MimeTypeAVIF) + return m.HasMimeType(fs.MimeTypeAvif) } // IsAVIFS checks if the file is an AVIF image sequence with a supported file type extension. func (m *MediaFile) IsAVIFS() bool { - return m.HasFileType(fs.ImageAVIFS) + return m.HasFileType(fs.ImageAvifS) } // IsBMP checks if the file is a bitmap image with a supported file type extension. -func (m *MediaFile) IsBMP() bool { - if fs.FileType(m.fileName) != fs.ImageBMP { +func (m *MediaFile) IsBmp() bool { + if fs.FileType(m.fileName) != fs.ImageBmp { return false } // Check the mime type after other tests have passed to improve performance. - return m.HasMimeType(fs.MimeTypeBMP) + return m.HasMimeType(fs.MimeTypeBmp) } // IsWebP checks if the file is a WebP image file with a supported file type extension. -func (m *MediaFile) IsWebP() bool { - if fs.FileType(m.fileName) != fs.ImageWebP { +func (m *MediaFile) IsWebp() bool { + if fs.FileType(m.fileName) != fs.ImageWebp { return false } - return m.HasMimeType(fs.MimeTypeWebP) + return m.HasMimeType(fs.MimeTypeWebp) } // Duration returns the duration is the media content is playable. @@ -842,26 +842,26 @@ func (m *MediaFile) IsAnimatedImage() bool { // IsJSON checks if the file is a JSON sidecar file with a supported file type extension. func (m *MediaFile) IsJSON() bool { - return m.HasFileType(fs.SidecarJSON) + return m.HasFileType(fs.SidecarJson) } // FileType returns the file type (jpg, gif, tiff,...). func (m *MediaFile) FileType() fs.Type { switch { case m.IsJpeg(): - return fs.ImageJPEG - case m.IsPNG(): - return fs.ImagePNG - case m.IsGIF(): - return fs.ImageGIF - case m.IsBMP(): - return fs.ImageBMP + return fs.ImageJpeg + case m.IsPng(): + return fs.ImagePng + case m.IsGif(): + return fs.ImageGif + case m.IsBmp(): + return fs.ImageBmp case m.IsDNG(): return fs.ImageDNG case m.IsAVIF(): - return fs.ImageAVIF - case m.IsHEIC(): - return fs.ImageHEIC + return fs.ImageAvif + case m.IsHeic(): + return fs.ImageHeic default: return fs.FileType(m.fileName) } @@ -891,16 +891,16 @@ func (m *MediaFile) CheckType() error { // Perform mime type checks for selected file types. var valid bool switch fileType { - case fs.ImageJPEG: - valid = mimeType == fs.MimeTypeJPEG - case fs.ImagePNG: - valid = mimeType == fs.MimeTypePNG || mimeType == fs.MimeTypeAPNG - case fs.ImageGIF: - valid = mimeType == fs.MimeTypeGIF - case fs.ImageTIFF: - valid = mimeType == fs.MimeTypeTIFF - case fs.ImageHEIC, fs.ImageHEIF: - valid = mimeType == fs.MimeTypeHEIC || mimeType == fs.MimeTypeHEICS + case fs.ImageJpeg: + valid = mimeType == fs.MimeTypeJpeg + case fs.ImagePng: + valid = mimeType == fs.MimeTypePng || mimeType == fs.MimeTypeAPng + case fs.ImageGif: + valid = mimeType == fs.MimeTypeGif + case fs.ImageTiff: + valid = mimeType == fs.MimeTypeTiff + case fs.ImageHeic, fs.ImageHeif: + valid = mimeType == fs.MimeTypeHeic || mimeType == fs.MimeTypeHeicS default: // Skip mime type check. Note: Checks for additional formats and/or generic // checks based on the media content type can be added over time as needed. @@ -932,7 +932,7 @@ func (m *MediaFile) HasMediaType(mediaType media.Type) bool { // HasFileType checks if the file has the given file type. func (m *MediaFile) HasFileType(fileType fs.Type) bool { - if fileType == fs.ImageJPEG { + if fileType == fs.ImageJpeg { return m.IsJpeg() } @@ -1003,15 +1003,15 @@ func (m *MediaFile) InSidecar() bool { func (m *MediaFile) NeedsTranscoding() bool { if m.NotAnimated() { return false - } else if m.HasFileType(fs.VideoAVC) || m.HasFileType(fs.VideoMP4) && m.MetaData().CodecAvc() { + } else if m.HasFileType(fs.VideoAvc) || m.HasFileType(fs.VideoMp4) && m.MetaData().CodecAvc() { return false } if m.IsAnimatedImage() { - return fs.VideoMP4.FindFirst(m.FileName(), []string{Config().SidecarPath(), fs.PPHiddenPathname}, Config().OriginalsPath(), false) == "" + return fs.VideoMp4.FindFirst(m.FileName(), []string{Config().SidecarPath(), fs.PPHiddenPathname}, Config().OriginalsPath(), false) == "" } - return fs.VideoAVC.FindFirst(m.FileName(), []string{Config().SidecarPath(), fs.PPHiddenPathname}, Config().OriginalsPath(), false) == "" + return fs.VideoAvc.FindFirst(m.FileName(), []string{Config().SidecarPath(), fs.PPHiddenPathname}, Config().OriginalsPath(), false) == "" } // SkipTranscoding checks if the media file is not animated or has already been transcoded to a playable format. @@ -1022,7 +1022,7 @@ func (m *MediaFile) SkipTranscoding() bool { // IsImageOther returns true if this is a PNG, GIF, BMP, TIFF, or WebP file. func (m *MediaFile) IsImageOther() bool { switch { - case m.IsPNG(), m.IsGIF(), m.IsTIFF(), m.IsBMP(), m.IsWebP(): + case m.IsPng(), m.IsGif(), m.IsTiff(), m.IsBmp(), m.IsWebp(): return true default: return false @@ -1036,12 +1036,12 @@ func (m *MediaFile) IsImageNative() bool { // IsLive checks if the file is a live photo. func (m *MediaFile) IsLive() bool { - if m.IsHEIC() { - return fs.VideoMOV.FindFirst(m.FileName(), []string{}, Config().OriginalsPath(), false) != "" + if m.IsHeic() { + return fs.VideoMov.FindFirst(m.FileName(), []string{}, Config().OriginalsPath(), false) != "" } if m.IsVideo() { - return fs.ImageHEIC.FindFirst(m.FileName(), []string{}, Config().OriginalsPath(), false) != "" + return fs.ImageHeic.FindFirst(m.FileName(), []string{}, Config().OriginalsPath(), false) != "" } return m.MetaData().MediaType == media.Live && m.VideoInfo().Compatible @@ -1049,7 +1049,7 @@ func (m *MediaFile) IsLive() bool { // ExifSupported returns true if parsing exif metadata is supported for the media file type. func (m *MediaFile) ExifSupported() bool { - return m.IsJpeg() || m.IsRaw() || m.IsHEIF() || m.IsPNG() || m.IsTIFF() + return m.IsJpeg() || m.IsRaw() || m.IsHeif() || m.IsPng() || m.IsTiff() } // IsMedia returns true if this is a media file (photo or video, not sidecar or other). @@ -1069,14 +1069,14 @@ func (m *MediaFile) PreviewImage() (*MediaFile, error) { return nil, fmt.Errorf("%s is empty", m.RootRelName()) } - jpegName := fs.ImageJPEG.FindFirst(m.FileName(), + jpegName := fs.ImageJpeg.FindFirst(m.FileName(), []string{Config().SidecarPath(), fs.PPHiddenPathname}, Config().OriginalsPath(), false) if jpegName != "" { return NewMediaFile(jpegName) } - pngName := fs.ImagePNG.FindFirst(m.FileName(), + pngName := fs.ImagePng.FindFirst(m.FileName(), []string{Config().SidecarPath(), fs.PPHiddenPathname}, Config().OriginalsPath(), false) if pngName != "" { @@ -1097,17 +1097,17 @@ func (m *MediaFile) HasPreviewImage() bool { return true } - jpegName := fs.ImageJPEG.FindFirst(m.FileName(), + jpegName := fs.ImageJpeg.FindFirst(m.FileName(), []string{Config().SidecarPath(), fs.PPHiddenPathname}, Config().OriginalsPath(), false) - if m.hasPreviewImage = fs.MimeType(jpegName) == fs.MimeTypeJPEG; m.hasPreviewImage { + if m.hasPreviewImage = fs.MimeType(jpegName) == fs.MimeTypeJpeg; m.hasPreviewImage { return true } - pngName := fs.ImagePNG.FindFirst(m.FileName(), + pngName := fs.ImagePng.FindFirst(m.FileName(), []string{Config().SidecarPath(), fs.PPHiddenPathname}, Config().OriginalsPath(), false) - if m.hasPreviewImage = fs.MimeType(pngName) == fs.MimeTypePNG; m.hasPreviewImage { + if m.hasPreviewImage = fs.MimeType(pngName) == fs.MimeTypePng; m.hasPreviewImage { return true } diff --git a/internal/photoprism/mediafile_heic_test.go b/internal/photoprism/mediafile_heic_test.go index e6ccb9512..42fc51b46 100644 --- a/internal/photoprism/mediafile_heic_test.go +++ b/internal/photoprism/mediafile_heic_test.go @@ -11,7 +11,7 @@ import ( "github.com/photoprism/photoprism/internal/meta" ) -func TestMediaFile_HEIC(t *testing.T) { +func TestMediaFile_Heic(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode.") } diff --git a/internal/photoprism/mediafile_meta.go b/internal/photoprism/mediafile_meta.go index e6a9ebfe7..0e9284b2d 100644 --- a/internal/photoprism/mediafile_meta.go +++ b/internal/photoprism/mediafile_meta.go @@ -16,7 +16,7 @@ func (m *MediaFile) HasSidecarJson() bool { return true } - return fs.SidecarJSON.FindFirst(m.FileName(), []string{Config().SidecarPath(), fs.PPHiddenPathname}, Config().OriginalsPath(), false) != "" + return fs.SidecarJson.FindFirst(m.FileName(), []string{Config().SidecarPath(), fs.PPHiddenPathname}, Config().OriginalsPath(), false) != "" } // SidecarJsonName returns the corresponding JSON sidecar file name as used by Google Photos (and potentially other apps). @@ -98,7 +98,7 @@ func (m *MediaFile) MetaData() (result meta.Data) { // Parse regular JSON sidecar files ("img_1234.json") if !m.IsSidecar() { - if jsonFiles := fs.SidecarJSON.FindAll(m.FileName(), []string{Config().SidecarPath(), fs.PPHiddenPathname}, Config().OriginalsPath(), false); len(jsonFiles) == 0 { + if jsonFiles := fs.SidecarJson.FindAll(m.FileName(), []string{Config().SidecarPath(), fs.PPHiddenPathname}, Config().OriginalsPath(), false); len(jsonFiles) == 0 { log.Tracef("metadata: found no additional sidecar file for %s", clean.Log(filepath.Base(m.FileName()))) } else { for _, jsonFile := range jsonFiles { diff --git a/internal/photoprism/mediafile_meta_test.go b/internal/photoprism/mediafile_meta_test.go index f2a98fe56..dcc375bd6 100644 --- a/internal/photoprism/mediafile_meta_test.go +++ b/internal/photoprism/mediafile_meta_test.go @@ -154,7 +154,7 @@ func TestMediaFile_CreateExifToolJson(t *testing.T) { }) } -func TestMediaFile_Exif_JPEG(t *testing.T) { +func TestMediaFile_Exif_Jpeg(t *testing.T) { conf := config.TestConfig() t.Run("elephants.jpg", func(t *testing.T) { @@ -375,14 +375,14 @@ func TestMediaFile_VideoInfo(t *testing.T) { info := mf.VideoInfo() - assert.Equal(t, video.MP4, info.VideoType) - assert.Equal(t, video.CodecAVC, info.VideoCodec) + assert.Equal(t, video.Mp4, info.VideoType) + assert.Equal(t, video.CodecAvc, info.VideoCodec) assert.Equal(t, 1440, info.VideoWidth) assert.Equal(t, 1080, info.VideoHeight) assert.Equal(t, int64(2685814), info.VideoOffset) assert.Equal(t, int64(0), info.ThumbOffset) assert.Equal(t, "2.933s", info.Duration.String()) - assert.Equal(t, fs.ImageJPEG, info.FileType) + assert.Equal(t, fs.ImageJpeg, info.FileType) assert.Equal(t, media.Live, info.MediaType) }, ) @@ -405,7 +405,7 @@ func TestMediaFile_VideoInfo(t *testing.T) { assert.Equal(t, int64(-1), info.VideoOffset) assert.Equal(t, int64(-1), info.ThumbOffset) assert.Equal(t, time.Duration(0), info.Duration) - assert.Equal(t, fs.ImageJPEG, info.FileType) + assert.Equal(t, fs.ImageJpeg, info.FileType) assert.Equal(t, media.Image, info.MediaType) }, ) diff --git a/internal/photoprism/mediafile_related.go b/internal/photoprism/mediafile_related.go index 03581f8ed..5843770c5 100644 --- a/internal/photoprism/mediafile_related.go +++ b/internal/photoprism/mediafile_related.go @@ -59,7 +59,7 @@ func (m *MediaFile) RelatedFiles(stripSequence bool) (result RelatedFiles, err e matches = list.Join(matches, files) } - isHEIC := false + isHeic := false // Process files that matched the pattern. for _, fileName := range matches { @@ -89,14 +89,14 @@ func (m *MediaFile) RelatedFiles(stripSequence bool) (result RelatedFiles, err e result.Main = f } else if f.IsVector() { result.Main = f - } else if f.IsHEIC() { - isHEIC = true + } else if f.IsHeic() { + isHeic = true result.Main = f - } else if f.IsHEIF() { + } else if f.IsHeif() { result.Main = f } else if f.IsImage() && !f.IsPreviewImage() && !f.IsThumb() { result.Main = f - } else if f.IsVideo() && !isHEIC { + } else if f.IsVideo() && !isHeic { result.Main = f } else if result.Main != nil && f.IsPreviewImage() { if result.Main.IsPreviewImage() && len(result.Main.FileName()) > len(f.FileName()) { @@ -119,11 +119,11 @@ func (m *MediaFile) RelatedFiles(stripSequence bool) (result RelatedFiles, err e // Add hidden preview image if needed. if !result.HasPreview() { - if jpegName := fs.ImageJPEG.FindFirst(result.Main.FileName(), []string{Config().SidecarPath(), fs.PPHiddenPathname}, Config().OriginalsPath(), stripSequence); jpegName != "" { + if jpegName := fs.ImageJpeg.FindFirst(result.Main.FileName(), []string{Config().SidecarPath(), fs.PPHiddenPathname}, Config().OriginalsPath(), stripSequence); jpegName != "" { if resultFile, _ := NewMediaFile(jpegName); resultFile.Ok() { result.Files = append(result.Files, resultFile) } - } else if pngName := fs.ImagePNG.FindFirst(result.Main.FileName(), []string{Config().SidecarPath(), fs.PPHiddenPathname}, Config().OriginalsPath(), stripSequence); pngName != "" { + } else if pngName := fs.ImagePng.FindFirst(result.Main.FileName(), []string{Config().SidecarPath(), fs.PPHiddenPathname}, Config().OriginalsPath(), stripSequence); pngName != "" { if resultFile, _ := NewMediaFile(pngName); resultFile.Ok() { result.Files = append(result.Files, resultFile) } diff --git a/internal/photoprism/mediafile_test.go b/internal/photoprism/mediafile_test.go index 76137095e..e51824d0e 100644 --- a/internal/photoprism/mediafile_test.go +++ b/internal/photoprism/mediafile_test.go @@ -13,7 +13,7 @@ import ( "github.com/photoprism/photoprism/internal/config" "github.com/photoprism/photoprism/internal/entity" "github.com/photoprism/photoprism/pkg/fs" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) func TestMediaFile_Ok(t *testing.T) { @@ -721,7 +721,7 @@ func TestMediaFile_MimeType(t *testing.T) { if err != nil { t.Fatal(err) } - assert.Equal(t, fs.MimeTypeAVIF, mediaFile.MimeType()) + assert.Equal(t, fs.MimeTypeAvif, mediaFile.MimeType()) assert.True(t, mediaFile.IsAVIF()) }) t.Run("iphone_7.heic", func(t *testing.T) { @@ -729,8 +729,8 @@ func TestMediaFile_MimeType(t *testing.T) { if err != nil { t.Fatal(err) } - assert.Equal(t, fs.MimeTypeHEIC, mediaFile.MimeType()) - assert.True(t, mediaFile.IsHEIC()) + assert.Equal(t, fs.MimeTypeHeic, mediaFile.MimeType()) + assert.True(t, mediaFile.IsHeic()) }) t.Run("IMG_4120.AAE", func(t *testing.T) { mediaFile, err := NewMediaFile(c.ExamplesPath() + "/IMG_4120.AAE") @@ -738,7 +738,7 @@ func TestMediaFile_MimeType(t *testing.T) { t.Fatal(err) } - assert.Equal(t, fs.MimeTypeXML, mediaFile.BaseType()) + assert.Equal(t, fs.MimeTypeXml, mediaFile.BaseType()) assert.Equal(t, "text/xml", mediaFile.BaseType()) assert.Equal(t, "text/xml; charset=utf-8", mediaFile.MimeType()) assert.Equal(t, true, mediaFile.HasMimeType("text/xml")) @@ -927,7 +927,7 @@ func TestMediaFile_Extension(t *testing.T) { if err != nil { t.Fatal(err) } - assert.Equal(t, fs.ExtJPEG, mediaFile.Extension()) + assert.Equal(t, fs.ExtJpeg, mediaFile.Extension()) }) } @@ -997,7 +997,7 @@ func TestMediaFile_HasType(t *testing.T) { }) } -func TestMediaFile_IsHEIC(t *testing.T) { +func TestMediaFile_IsHeic(t *testing.T) { c := config.TestConfig() t.Run("iphone_7.json", func(t *testing.T) { @@ -1005,28 +1005,28 @@ func TestMediaFile_IsHEIC(t *testing.T) { if err != nil { t.Fatal(err) } - assert.Equal(t, false, mediaFile.IsHEIC()) + assert.Equal(t, false, mediaFile.IsHeic()) }) t.Run("iphone_7.heic", func(t *testing.T) { mediaFile, err := NewMediaFile(c.ExamplesPath() + "/iphone_7.heic") if err != nil { t.Fatal(err) } - assert.Equal(t, true, mediaFile.IsHEIC()) + assert.Equal(t, true, mediaFile.IsHeic()) }) t.Run("canon_eos_6d.dng", func(t *testing.T) { mediaFile, err := NewMediaFile(c.ExamplesPath() + "/canon_eos_6d.dng") if err != nil { t.Fatal(err) } - assert.Equal(t, false, mediaFile.IsHEIC()) + assert.Equal(t, false, mediaFile.IsHeic()) }) t.Run("elephants.jpg", func(t *testing.T) { mediaFile, err := NewMediaFile(c.ExamplesPath() + "/elephants.jpg") if err != nil { t.Fatal(err) } - assert.Equal(t, false, mediaFile.IsHEIC()) + assert.Equal(t, false, mediaFile.IsHeic()) }) } @@ -1072,7 +1072,7 @@ func TestMediaFile_IsPng(t *testing.T) { if err != nil { t.Fatal(err) } - assert.Equal(t, false, mediaFile.IsPNG()) + assert.Equal(t, false, mediaFile.IsPng()) }) t.Run("tweethog.png", func(t *testing.T) { mediaFile, err := NewMediaFile(c.ExamplesPath() + "/tweethog.png") @@ -1081,9 +1081,9 @@ func TestMediaFile_IsPng(t *testing.T) { t.Fatal(err) } - assert.Equal(t, fs.ImagePNG, mediaFile.FileType()) + assert.Equal(t, fs.ImagePng, mediaFile.FileType()) assert.Equal(t, "image/png", mediaFile.MimeType()) - assert.Equal(t, true, mediaFile.IsPNG()) + assert.Equal(t, true, mediaFile.IsPng()) }) } @@ -1095,27 +1095,27 @@ func TestMediaFile_IsTiff(t *testing.T) { if err != nil { t.Fatal(err) } - assert.Equal(t, fs.SidecarJSON, mediaFile.FileType()) - assert.Equal(t, fs.MimeTypeJSON, mediaFile.MimeType()) - assert.Equal(t, false, mediaFile.IsTIFF()) + assert.Equal(t, fs.SidecarJson, mediaFile.FileType()) + assert.Equal(t, fs.MimeTypeJson, mediaFile.MimeType()) + assert.Equal(t, false, mediaFile.IsTiff()) }) t.Run("purple.tiff", func(t *testing.T) { mediaFile, err := NewMediaFile(c.ExamplesPath() + "/purple.tiff") if err != nil { t.Fatal(err) } - assert.Equal(t, fs.ImageTIFF, mediaFile.FileType()) + assert.Equal(t, fs.ImageTiff, mediaFile.FileType()) assert.Equal(t, "image/tiff", mediaFile.MimeType()) - assert.Equal(t, true, mediaFile.IsTIFF()) + assert.Equal(t, true, mediaFile.IsTiff()) }) t.Run("example.tiff", func(t *testing.T) { mediaFile, err := NewMediaFile(c.ExamplesPath() + "/example.tif") if err != nil { t.Fatal(err) } - assert.Equal(t, fs.ImageTIFF, mediaFile.FileType()) + assert.Equal(t, fs.ImageTiff, mediaFile.FileType()) assert.Equal(t, "image/tiff", mediaFile.MimeType()) - assert.Equal(t, true, mediaFile.IsTIFF()) + assert.Equal(t, true, mediaFile.IsTiff()) }) } @@ -1142,10 +1142,10 @@ func TestMediaFile_IsImageOther(t *testing.T) { t.Fatal(err) } assert.Equal(t, false, mediaFile.IsJpeg()) - assert.Equal(t, false, mediaFile.IsGIF()) - assert.Equal(t, true, mediaFile.IsPNG()) - assert.Equal(t, false, mediaFile.IsBMP()) - assert.Equal(t, false, mediaFile.IsWebP()) + assert.Equal(t, false, mediaFile.IsGif()) + assert.Equal(t, true, mediaFile.IsPng()) + assert.Equal(t, false, mediaFile.IsBmp()) + assert.Equal(t, false, mediaFile.IsWebp()) assert.Equal(t, true, mediaFile.IsImage()) assert.Equal(t, true, mediaFile.IsImageNative()) assert.Equal(t, true, mediaFile.IsImageOther()) @@ -1157,12 +1157,12 @@ func TestMediaFile_IsImageOther(t *testing.T) { if err != nil { t.Fatal(err) } - assert.Equal(t, fs.ImageBMP, mediaFile.FileType()) + assert.Equal(t, fs.ImageBmp, mediaFile.FileType()) assert.Equal(t, "image/bmp", mediaFile.MimeType()) assert.Equal(t, false, mediaFile.IsJpeg()) - assert.Equal(t, false, mediaFile.IsGIF()) - assert.Equal(t, true, mediaFile.IsBMP()) - assert.Equal(t, false, mediaFile.IsWebP()) + assert.Equal(t, false, mediaFile.IsGif()) + assert.Equal(t, true, mediaFile.IsBmp()) + assert.Equal(t, false, mediaFile.IsWebp()) assert.Equal(t, true, mediaFile.IsImage()) assert.Equal(t, true, mediaFile.IsImageNative()) assert.Equal(t, true, mediaFile.IsImageOther()) @@ -1175,12 +1175,12 @@ func TestMediaFile_IsImageOther(t *testing.T) { t.Fatal(err) } - assert.Equal(t, fs.ImageGIF, mediaFile.FileType()) + assert.Equal(t, fs.ImageGif, mediaFile.FileType()) assert.Equal(t, "image/gif", mediaFile.MimeType()) assert.Equal(t, false, mediaFile.IsJpeg()) - assert.Equal(t, true, mediaFile.IsGIF()) - assert.Equal(t, false, mediaFile.IsBMP()) - assert.Equal(t, false, mediaFile.IsWebP()) + assert.Equal(t, true, mediaFile.IsGif()) + assert.Equal(t, false, mediaFile.IsBmp()) + assert.Equal(t, false, mediaFile.IsWebp()) assert.Equal(t, true, mediaFile.IsImage()) assert.Equal(t, true, mediaFile.IsImageNative()) assert.Equal(t, true, mediaFile.IsImageOther()) @@ -1194,12 +1194,12 @@ func TestMediaFile_IsImageOther(t *testing.T) { t.Fatal(err) } - assert.Equal(t, fs.ImageWebP, mediaFile.FileType()) - assert.Equal(t, fs.MimeTypeWebP, mediaFile.MimeType()) + assert.Equal(t, fs.ImageWebp, mediaFile.FileType()) + assert.Equal(t, fs.MimeTypeWebp, mediaFile.MimeType()) assert.Equal(t, false, mediaFile.IsJpeg()) - assert.Equal(t, false, mediaFile.IsGIF()) - assert.Equal(t, false, mediaFile.IsBMP()) - assert.Equal(t, true, mediaFile.IsWebP()) + assert.Equal(t, false, mediaFile.IsGif()) + assert.Equal(t, false, mediaFile.IsBmp()) + assert.Equal(t, true, mediaFile.IsWebp()) assert.Equal(t, true, mediaFile.IsImage()) assert.Equal(t, true, mediaFile.IsImageNative()) assert.Equal(t, true, mediaFile.IsImageOther()) @@ -1434,11 +1434,11 @@ func TestMediaFile_IsAnimated(t *testing.T) { assert.Equal(t, true, f.IsAnimatedImage()) assert.Equal(t, true, f.ExifSupported()) assert.Equal(t, false, f.IsVideo()) - assert.Equal(t, false, f.IsGIF()) - assert.Equal(t, false, f.IsWebP()) + assert.Equal(t, false, f.IsGif()) + assert.Equal(t, false, f.IsWebp()) assert.Equal(t, false, f.IsAVIF()) - assert.Equal(t, false, f.IsHEIC()) - assert.Equal(t, false, f.IsHEICS()) + assert.Equal(t, false, f.IsHeic()) + assert.Equal(t, false, f.IsHeicS()) assert.Equal(t, false, f.IsSidecar()) } }) @@ -1447,17 +1447,17 @@ func TestMediaFile_IsAnimated(t *testing.T) { t.Fatal(err) } else { assert.Equal(t, true, f.IsImage()) - assert.Equal(t, true, f.IsWebP()) + assert.Equal(t, true, f.IsWebp()) assert.Equal(t, true, f.IsAnimated()) assert.Equal(t, false, f.NotAnimated()) assert.Equal(t, true, f.IsAnimatedImage()) assert.Equal(t, false, f.ExifSupported()) assert.Equal(t, false, f.IsVideo()) - assert.Equal(t, false, f.IsGIF()) + assert.Equal(t, false, f.IsGif()) assert.Equal(t, false, f.IsAVIF()) assert.Equal(t, false, f.IsAVIFS()) - assert.Equal(t, false, f.IsHEIC()) - assert.Equal(t, false, f.IsHEICS()) + assert.Equal(t, false, f.IsHeic()) + assert.Equal(t, false, f.IsHeicS()) assert.Equal(t, false, f.IsSidecar()) } }) @@ -1469,7 +1469,7 @@ func TestMediaFile_IsAnimated(t *testing.T) { assert.Equal(t, false, f.IsVideo()) assert.Equal(t, false, f.IsAnimated()) assert.Equal(t, true, f.NotAnimated()) - assert.Equal(t, true, f.IsGIF()) + assert.Equal(t, true, f.IsGif()) assert.Equal(t, false, f.IsAnimatedImage()) assert.Equal(t, false, f.IsSidecar()) } @@ -1482,7 +1482,7 @@ func TestMediaFile_IsAnimated(t *testing.T) { assert.Equal(t, false, f.IsVideo()) assert.Equal(t, true, f.IsAnimated()) assert.Equal(t, false, f.NotAnimated()) - assert.Equal(t, true, f.IsGIF()) + assert.Equal(t, true, f.IsGif()) assert.Equal(t, true, f.IsAnimatedImage()) assert.Equal(t, false, f.IsSidecar()) } @@ -1495,7 +1495,7 @@ func TestMediaFile_IsAnimated(t *testing.T) { assert.Equal(t, true, f.IsVideo()) assert.Equal(t, true, f.IsAnimated()) assert.Equal(t, false, f.NotAnimated()) - assert.Equal(t, false, f.IsGIF()) + assert.Equal(t, false, f.IsGif()) assert.Equal(t, false, f.IsAnimatedImage()) assert.Equal(t, false, f.IsSidecar()) } @@ -2119,12 +2119,12 @@ func TestMediaFile_FileType(t *testing.T) { // JPEG to improve performance (skips mime type detection) and to avoid follow-up issues // with external tools that rely on a correct file extension. assert.False(t, m.IsJpeg()) - assert.False(t, m.IsPNG()) + assert.False(t, m.IsPng()) assert.Equal(t, "png", string(m.FileType())) assert.Equal(t, "image/jpeg", m.MimeType()) assert.Equal(t, "image/jpeg", m.BaseType()) assert.Equal(t, true, m.HasMimeType("image/jpeg")) - assert.Equal(t, fs.ImagePNG, m.FileType()) + assert.Equal(t, fs.ImagePng, m.FileType()) assert.Equal(t, ".png", m.Extension()) }) t.Run("ImageThumb", func(t *testing.T) { @@ -2136,7 +2136,7 @@ func TestMediaFile_FileType(t *testing.T) { assert.True(t, m.IsThumb()) assert.False(t, m.IsJpeg()) - assert.False(t, m.IsPNG()) + assert.False(t, m.IsPng()) assert.Equal(t, "thm", string(m.FileType())) assert.Equal(t, "image/jpeg", m.MimeType()) assert.Equal(t, true, m.HasMimeType("image/jpeg")) diff --git a/internal/photoprism/photoprism_test.go b/internal/photoprism/photoprism_test.go index e69929ebf..eb5591aeb 100644 --- a/internal/photoprism/photoprism_test.go +++ b/internal/photoprism/photoprism_test.go @@ -4,8 +4,9 @@ import ( "os" "testing" - "github.com/photoprism/photoprism/internal/config" "github.com/sirupsen/logrus" + + "github.com/photoprism/photoprism/internal/config" ) func TestMain(m *testing.M) { diff --git a/internal/photoprism/photos_test.go b/internal/photoprism/photos_test.go index 414ddd9e6..20b4b395c 100644 --- a/internal/photoprism/photos_test.go +++ b/internal/photoprism/photos_test.go @@ -4,8 +4,9 @@ import ( "testing" "time" - "github.com/photoprism/photoprism/pkg/geo/s2" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/pkg/geo/s2" ) func TestPhotos_Init(t *testing.T) { diff --git a/internal/photoprism/related_test.go b/internal/photoprism/related_test.go index 9dc3138de..5f9dc4dbe 100644 --- a/internal/photoprism/related_test.go +++ b/internal/photoprism/related_test.go @@ -174,7 +174,7 @@ func TestRelatedFiles_MainFileType(t *testing.T) { Files: MediaFiles{}, Main: mediaFile, } - assert.Equal(t, string(fs.ImageJPEG), relatedFiles.MainFileType()) + assert.Equal(t, string(fs.ImageJpeg), relatedFiles.MainFileType()) }) t.Run("Heif", func(t *testing.T) { mediaFile, err := NewMediaFile(cfg.ExamplesPath() + "/telegram_2020-01-30_09-57-18.jpg") @@ -193,7 +193,7 @@ func TestRelatedFiles_MainFileType(t *testing.T) { Files: MediaFiles{mediaFile, mediaFile2}, Main: mediaFile3, } - assert.Equal(t, string(fs.ImageHEIC), relatedFiles.MainFileType()) + assert.Equal(t, string(fs.ImageHeic), relatedFiles.MainFileType()) }) } diff --git a/internal/photoprism/testdata/agpl.svg b/internal/photoprism/testdata/agpl.svg index ebc7e7fa8..b39a191e3 100644 --- a/internal/photoprism/testdata/agpl.svg +++ b/internal/photoprism/testdata/agpl.svg @@ -1 +1 @@ -licenselicenseAGPLAGPL \ No newline at end of file +licenselicenseAGPLAGPL \ No newline at end of file diff --git a/internal/photoprism/timezone_test.go b/internal/photoprism/timezone_test.go index 9485870b0..79e8c20c0 100644 --- a/internal/photoprism/timezone_test.go +++ b/internal/photoprism/timezone_test.go @@ -3,8 +3,9 @@ package photoprism import ( "testing" - "github.com/photoprism/photoprism/internal/config" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/config" ) func TestMediaFile_TimeZone(t *testing.T) { diff --git a/internal/server/api.go b/internal/server/api.go index fa81f7aa9..bdab284c9 100644 --- a/internal/server/api.go +++ b/internal/server/api.go @@ -6,7 +6,7 @@ import ( "github.com/gin-gonic/gin" "github.com/photoprism/photoprism/internal/config" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) // Api is a middleware that sets additional response headers when serving REST API requests. diff --git a/internal/server/routes_static.go b/internal/server/routes_static.go index 3de318f6c..ef7d4b7e3 100644 --- a/internal/server/routes_static.go +++ b/internal/server/routes_static.go @@ -8,7 +8,7 @@ import ( "github.com/photoprism/photoprism/internal/api" "github.com/photoprism/photoprism/internal/config" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) // registerStaticRoutes adds routes for serving static content and templates. diff --git a/internal/server/routes_webapp.go b/internal/server/routes_webapp.go index a609321b2..b697d5ce5 100644 --- a/internal/server/routes_webapp.go +++ b/internal/server/routes_webapp.go @@ -8,7 +8,7 @@ import ( "github.com/photoprism/photoprism/internal/api" "github.com/photoprism/photoprism/internal/config" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) // registerWebAppRoutes adds routes for the web user interface. diff --git a/internal/server/routes_webdav.go b/internal/server/routes_webdav.go index 38ef3d0ce..95b74af3d 100644 --- a/internal/server/routes_webdav.go +++ b/internal/server/routes_webdav.go @@ -2,6 +2,7 @@ package server import ( "github.com/gin-gonic/gin" + "github.com/photoprism/photoprism/internal/config" ) diff --git a/internal/server/security.go b/internal/server/security.go index 4c95f9ae4..5717ab5f1 100644 --- a/internal/server/security.go +++ b/internal/server/security.go @@ -5,7 +5,7 @@ import ( "github.com/photoprism/photoprism/internal/api" "github.com/photoprism/photoprism/internal/config" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) // Security is a middleware that adds security-related headers to the server's response. diff --git a/internal/server/start.go b/internal/server/start.go index 6c8334f1c..c3b89e1f5 100644 --- a/internal/server/start.go +++ b/internal/server/start.go @@ -20,7 +20,7 @@ import ( "github.com/photoprism/photoprism/internal/server/process" "github.com/photoprism/photoprism/pkg/clean" "github.com/photoprism/photoprism/pkg/fs" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" "github.com/photoprism/photoprism/pkg/txt" ) diff --git a/internal/server/static.go b/internal/server/static.go index ebdd948c5..52fb56aa7 100644 --- a/internal/server/static.go +++ b/internal/server/static.go @@ -6,7 +6,7 @@ import ( "github.com/gin-gonic/gin" "github.com/photoprism/photoprism/internal/config" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) // Static is a middleware that adds static content-related headers to the server's response. diff --git a/internal/server/webdav.go b/internal/server/webdav.go index 2470d6aa5..2c5ae58ec 100644 --- a/internal/server/webdav.go +++ b/internal/server/webdav.go @@ -16,7 +16,7 @@ import ( "github.com/photoprism/photoprism/internal/workers/auto" "github.com/photoprism/photoprism/pkg/clean" "github.com/photoprism/photoprism/pkg/fs" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" "github.com/photoprism/photoprism/pkg/txt" ) @@ -158,7 +158,7 @@ func WebDAVFileName(request *http.Request, router *gin.RouterGroup, conf *config // WebDAVSetFavoriteFlag adds the favorite flag to files uploaded via WebDAV. func WebDAVSetFavoriteFlag(fileName string) { - yamlName := fs.AbsPrefix(fileName, false) + fs.ExtYAML + yamlName := fs.AbsPrefix(fileName, false) + fs.ExtYaml // Abort if YAML file already exists to avoid overwriting metadata. if fs.FileExists(yamlName) { diff --git a/internal/server/webdav_auth.go b/internal/server/webdav_auth.go index 75b3576a8..c4e3a6a1a 100644 --- a/internal/server/webdav_auth.go +++ b/internal/server/webdav_auth.go @@ -20,7 +20,7 @@ import ( "github.com/photoprism/photoprism/pkg/authn" "github.com/photoprism/photoprism/pkg/clean" "github.com/photoprism/photoprism/pkg/fs" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" "github.com/photoprism/photoprism/pkg/rnd" ) diff --git a/internal/server/webdav_auth_session.go b/internal/server/webdav_auth_session.go index 40873e41a..1bc1839fc 100644 --- a/internal/server/webdav_auth_session.go +++ b/internal/server/webdav_auth_session.go @@ -7,7 +7,7 @@ import ( "github.com/photoprism/photoprism/internal/event" "github.com/photoprism/photoprism/internal/server/limiter" "github.com/photoprism/photoprism/pkg/authn" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" "github.com/photoprism/photoprism/pkg/rnd" ) diff --git a/internal/server/webdav_auth_test.go b/internal/server/webdav_auth_test.go index f05b25379..cc96c0b40 100644 --- a/internal/server/webdav_auth_test.go +++ b/internal/server/webdav_auth_test.go @@ -13,7 +13,7 @@ import ( "github.com/photoprism/photoprism/internal/auth/acl" "github.com/photoprism/photoprism/internal/config" "github.com/photoprism/photoprism/internal/entity" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" "github.com/photoprism/photoprism/pkg/rnd" ) diff --git a/internal/service/hub/config.go b/internal/service/hub/config.go index ac33cbc94..c1a50e57a 100644 --- a/internal/service/hub/config.go +++ b/internal/service/hub/config.go @@ -22,7 +22,7 @@ import ( "github.com/photoprism/photoprism/internal/service/hub/places" "github.com/photoprism/photoprism/pkg/clean" "github.com/photoprism/photoprism/pkg/fs" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) type Status string diff --git a/internal/service/hub/feedback.go b/internal/service/hub/feedback.go index 36c7fbb7b..94b90b697 100644 --- a/internal/service/hub/feedback.go +++ b/internal/service/hub/feedback.go @@ -9,7 +9,7 @@ import ( "time" "github.com/photoprism/photoprism/internal/form" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" "github.com/photoprism/photoprism/pkg/txt" ) diff --git a/internal/service/hub/feedback_test.go b/internal/service/hub/feedback_test.go index a7ee48af9..7050b0f9d 100644 --- a/internal/service/hub/feedback_test.go +++ b/internal/service/hub/feedback_test.go @@ -3,8 +3,9 @@ package hub import ( "testing" - "github.com/photoprism/photoprism/internal/form" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/form" ) func TestNewFeedback(t *testing.T) { diff --git a/internal/service/webdav/client_test.go b/internal/service/webdav/client_test.go index 7aba2b9c1..83da909cc 100644 --- a/internal/service/webdav/client_test.go +++ b/internal/service/webdav/client_test.go @@ -118,7 +118,7 @@ func TestClient_UploadAndDelete(t *testing.T) { assert.IsType(t, &Client{}, c) - tempName := rnd.UUID() + fs.ExtJPEG + tempName := rnd.UUID() + fs.ExtJpeg if err := c.Upload(fs.Abs("testdata/example.jpg"), tempName); err != nil { t.Fatal(err) diff --git a/internal/thumb/avatar/user.go b/internal/thumb/avatar/user.go index 3fe4f6fd6..51cbb0eb9 100644 --- a/internal/thumb/avatar/user.go +++ b/internal/thumb/avatar/user.go @@ -39,10 +39,10 @@ func SetUserImageURL(m *entity.User, imageUrl, imageSrc, thumbPath string) error return fmt.Errorf("failed to detect avatar type (%w)", mimeErr) } else { switch { - case mimeType.Is(fs.MimeTypePNG): - imageName = tmpName + fs.ExtPNG - case mimeType.Is(fs.MimeTypeJPEG): - imageName = tmpName + fs.ExtJPEG + case mimeType.Is(fs.MimeTypePng): + imageName = tmpName + fs.ExtPng + case mimeType.Is(fs.MimeTypeJpeg): + imageName = tmpName + fs.ExtJpeg default: return fmt.Errorf("invalid avatar image type %s", mimeType) } diff --git a/internal/thumb/create.go b/internal/thumb/create.go index 889f75be1..59fdd1fe9 100644 --- a/internal/thumb/create.go +++ b/internal/thumb/create.go @@ -136,7 +136,7 @@ func Create(img image.Image, fileName string, width, height int, opts ...Resampl var quality imaging.EncodeOption - if fs.FileType(fileName) == fs.ImagePNG { + if fs.FileType(fileName) == fs.ImagePng { quality = imaging.PNGCompressionLevel(png.DefaultCompression) } else { quality = JpegQuality(width, height).EncodeOption() diff --git a/internal/thumb/create_test.go b/internal/thumb/create_test.go index e6af88c33..06ad1af29 100644 --- a/internal/thumb/create_test.go +++ b/internal/thumb/create_test.go @@ -18,21 +18,21 @@ func TestResampleOptions(t *testing.T) { assert.Equal(t, ResampleFillCenter, method) assert.Equal(t, imaging.Lanczos.Support, filter.Imaging().Support) - assert.Equal(t, fs.ImagePNG, format) + assert.Equal(t, fs.ImagePng, format) }) t.Run("ResampleNearestNeighbor, FillTopLeft", func(t *testing.T) { method, filter, format := ResampleOptions(ResampleNearestNeighbor, ResampleFillTopLeft) assert.Equal(t, ResampleFillTopLeft, method) assert.Equal(t, imaging.NearestNeighbor.Support, filter.Imaging().Support) - assert.Equal(t, fs.ImageJPEG, format) + assert.Equal(t, fs.ImageJpeg, format) }) t.Run("ResampleNearestNeighbor, FillBottomRight", func(t *testing.T) { method, filter, format := ResampleOptions(ResampleNearestNeighbor, ResampleFillBottomRight) assert.Equal(t, ResampleFillBottomRight, method) assert.Equal(t, imaging.NearestNeighbor.Support, filter.Imaging().Support) - assert.Equal(t, fs.ImageJPEG, format) + assert.Equal(t, fs.ImageJpeg, format) }) } diff --git a/internal/thumb/crop/cache.go b/internal/thumb/crop/cache.go index 0bfe22a7d..93ef300bf 100644 --- a/internal/thumb/crop/cache.go +++ b/internal/thumb/crop/cache.go @@ -2,9 +2,8 @@ package crop import ( "fmt" - "path/filepath" - "path" + "path/filepath" "github.com/photoprism/photoprism/pkg/clean" "github.com/photoprism/photoprism/pkg/fs" @@ -39,7 +38,7 @@ func FileName(hash, area string, width, height int, thumbPath string) (fileName return "", fmt.Errorf("crop: invalid size %dx%d", width, height) } - fileName = path.Join(thumbPath, hash[0:1], hash[1:2], hash[2:3], fmt.Sprintf("%s_%dx%d_crop_%s%s", hash, width, height, area, fs.ExtJPEG)) + fileName = path.Join(thumbPath, hash[0:1], hash[1:2], hash[2:3], fmt.Sprintf("%s_%dx%d_crop_%s%s", hash, width, height, area, fs.ExtJpeg)) return fileName, nil } diff --git a/internal/thumb/crop/image.go b/internal/thumb/crop/image.go index fda2e1fb2..fff80f210 100644 --- a/internal/thumb/crop/image.go +++ b/internal/thumb/crop/image.go @@ -10,6 +10,7 @@ import ( "strings" "github.com/disintegration/imaging" + "github.com/photoprism/photoprism/internal/thumb" "github.com/photoprism/photoprism/pkg/clean" "github.com/photoprism/photoprism/pkg/fs" @@ -49,7 +50,7 @@ func ImageFromThumb(thumbName string, area Area, size Size, cache bool) (img ima } // Compose cached crop image file name. - cropBase := fmt.Sprintf("%s_%dx%d_crop_%s%s", hash, size.Width, size.Height, area.String(), fs.ExtJPEG) + cropBase := fmt.Sprintf("%s_%dx%d_crop_%s%s", hash, size.Width, size.Height, area.String(), fs.ExtJpeg) cropName := filepath.Join(filePath, cropBase) // Cached? diff --git a/internal/thumb/crop/names.go b/internal/thumb/crop/names.go index 23a1cc884..548ed2498 100644 --- a/internal/thumb/crop/names.go +++ b/internal/thumb/crop/names.go @@ -7,7 +7,7 @@ type Name string // Jpeg returns the crop name with a jpeg file extension suffix as string. func (n Name) Jpeg() string { - return string(n) + fs.ExtJPEG + return string(n) + fs.ExtJpeg } // Names of standard crop sizes. diff --git a/internal/thumb/crop/request.go b/internal/thumb/crop/request.go index c00e35393..e30e83264 100644 --- a/internal/thumb/crop/request.go +++ b/internal/thumb/crop/request.go @@ -8,6 +8,7 @@ import ( "path/filepath" "github.com/disintegration/imaging" + "github.com/photoprism/photoprism/internal/thumb" "github.com/photoprism/photoprism/pkg/fs" ) @@ -27,7 +28,7 @@ func FromRequest(hash, area string, size Size, thumbPath string) (fileName strin } // Compose cached crop image file name. - cropBase := fmt.Sprintf("%s_%dx%d_crop_%s%s", hash, size.Width, size.Height, area, fs.ExtJPEG) + cropBase := fmt.Sprintf("%s_%dx%d_crop_%s%s", hash, size.Width, size.Height, area, fs.ExtJpeg) cropName := filepath.Join(filepath.Dir(thumbName), cropBase) imageBuffer, err := os.ReadFile(thumbName) diff --git a/internal/thumb/filter.go b/internal/thumb/filter.go index cd18cd9ad..bad2e41b2 100644 --- a/internal/thumb/filter.go +++ b/internal/thumb/filter.go @@ -3,6 +3,7 @@ package thumb import ( "github.com/davidbyttow/govips/v2/vips" "github.com/disintegration/imaging" + "github.com/photoprism/photoprism/pkg/clean" ) diff --git a/internal/thumb/frame/collage_test.go b/internal/thumb/frame/collage_test.go index 05101cccd..324e81dce 100644 --- a/internal/thumb/frame/collage_test.go +++ b/internal/thumb/frame/collage_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/disintegration/imaging" + "github.com/photoprism/photoprism/pkg/fs" "github.com/stretchr/testify/assert" @@ -31,7 +32,7 @@ func TestCollage(t *testing.T) { assert.NoError(t, err) mimeType := fs.MimeType(saveName) - assert.Equal(t, fs.MimeTypeJPEG, mimeType) + assert.Equal(t, fs.MimeTypeJpeg, mimeType) _ = os.Remove(saveName) }) @@ -55,7 +56,7 @@ func TestCollage(t *testing.T) { assert.NoError(t, err) mimeType := fs.MimeType(saveName) - assert.Equal(t, fs.MimeTypeJPEG, mimeType) + assert.Equal(t, fs.MimeTypeJpeg, mimeType) _ = os.Remove(saveName) }) @@ -72,7 +73,7 @@ func TestCollage(t *testing.T) { assert.NoError(t, err) mimeType := fs.MimeType(saveName) - assert.Equal(t, fs.MimeTypeJPEG, mimeType) + assert.Equal(t, fs.MimeTypeJpeg, mimeType) _ = os.Remove(saveName) }) @@ -99,7 +100,7 @@ func TestCollage(t *testing.T) { assert.NoError(t, err) mimeType := fs.MimeType(saveName) - assert.Equal(t, fs.MimeTypeJPEG, mimeType) + assert.Equal(t, fs.MimeTypeJpeg, mimeType) _ = os.Remove(saveName) diff --git a/internal/thumb/frame/image_test.go b/internal/thumb/frame/image_test.go index 16fc447f4..6b0dce531 100644 --- a/internal/thumb/frame/image_test.go +++ b/internal/thumb/frame/image_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/disintegration/imaging" + "github.com/photoprism/photoprism/pkg/fs" "github.com/stretchr/testify/assert" @@ -25,7 +26,7 @@ func TestImage(t *testing.T) { assert.NoError(t, err) mimeType := fs.MimeType(saveName) - assert.Equal(t, fs.MimeTypePNG, mimeType) + assert.Equal(t, fs.MimeTypePng, mimeType) _ = os.Remove(saveName) }) @@ -45,7 +46,7 @@ func TestImage(t *testing.T) { assert.NoError(t, err) mimeType := fs.MimeType(saveName) - assert.Equal(t, fs.MimeTypePNG, mimeType) + assert.Equal(t, fs.MimeTypePng, mimeType) _ = os.Remove(saveName) }) diff --git a/internal/thumb/frame/polaroid_test.go b/internal/thumb/frame/polaroid_test.go index a60a55e39..97634f040 100644 --- a/internal/thumb/frame/polaroid_test.go +++ b/internal/thumb/frame/polaroid_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/disintegration/imaging" + "github.com/photoprism/photoprism/pkg/fs" "github.com/stretchr/testify/assert" @@ -25,7 +26,7 @@ func TestPolaroid(t *testing.T) { assert.NoError(t, err) mimeType := fs.MimeType(saveName) - assert.Equal(t, fs.MimeTypePNG, mimeType) + assert.Equal(t, fs.MimeTypePng, mimeType) _ = os.Remove(saveName) }) diff --git a/internal/thumb/jpeg_test.go b/internal/thumb/jpeg_test.go index 450a3d513..0c3a8d3f5 100644 --- a/internal/thumb/jpeg_test.go +++ b/internal/thumb/jpeg_test.go @@ -15,7 +15,7 @@ func TestJpeg(t *testing.T) { for _, ext := range formats { t.Run(ext, func(t *testing.T) { src := "testdata/example." + ext - dst := "testdata/example." + ext + fs.ExtJPEG + dst := "testdata/example." + ext + fs.ExtJpeg assert.NoFileExists(t, dst) @@ -37,7 +37,7 @@ func TestJpeg(t *testing.T) { }) t.Run("OrientationFlipH", func(t *testing.T) { src := "testdata/example." + ext - dst := "testdata/example." + ext + fs.ExtJPEG + dst := "testdata/example." + ext + fs.ExtJpeg assert.NoFileExists(t, dst) @@ -59,7 +59,7 @@ func TestJpeg(t *testing.T) { }) t.Run("OrientationFlipV", func(t *testing.T) { src := "testdata/example." + ext - dst := "testdata/example." + ext + fs.ExtJPEG + dst := "testdata/example." + ext + fs.ExtJpeg assert.NoFileExists(t, dst) @@ -81,7 +81,7 @@ func TestJpeg(t *testing.T) { }) t.Run("OrientationRotate90", func(t *testing.T) { src := "testdata/example." + ext - dst := "testdata/example." + ext + fs.ExtJPEG + dst := "testdata/example." + ext + fs.ExtJpeg assert.NoFileExists(t, dst) @@ -103,7 +103,7 @@ func TestJpeg(t *testing.T) { }) t.Run("OrientationRotate180", func(t *testing.T) { src := "testdata/example." + ext - dst := "testdata/example." + ext + fs.ExtJPEG + dst := "testdata/example." + ext + fs.ExtJpeg assert.NoFileExists(t, dst) @@ -125,7 +125,7 @@ func TestJpeg(t *testing.T) { }) t.Run("OrientationTranspose", func(t *testing.T) { src := "testdata/example." + ext - dst := "testdata/example." + ext + fs.ExtJPEG + dst := "testdata/example." + ext + fs.ExtJpeg assert.NoFileExists(t, dst) @@ -147,7 +147,7 @@ func TestJpeg(t *testing.T) { }) t.Run("OrientationTransverse", func(t *testing.T) { src := "testdata/example." + ext - dst := "testdata/example." + ext + fs.ExtJPEG + dst := "testdata/example." + ext + fs.ExtJpeg assert.NoFileExists(t, dst) @@ -169,7 +169,7 @@ func TestJpeg(t *testing.T) { }) t.Run("OrientationUnspecified", func(t *testing.T) { src := "testdata/example." + ext - dst := "testdata/example." + ext + fs.ExtJPEG + dst := "testdata/example." + ext + fs.ExtJpeg assert.NoFileExists(t, dst) @@ -191,7 +191,7 @@ func TestJpeg(t *testing.T) { }) t.Run("OrientationNormal", func(t *testing.T) { src := "testdata/example." + ext - dst := "testdata/example." + ext + fs.ExtJPEG + dst := "testdata/example." + ext + fs.ExtJpeg assert.NoFileExists(t, dst) @@ -213,7 +213,7 @@ func TestJpeg(t *testing.T) { }) t.Run("invalid orientation", func(t *testing.T) { src := "testdata/example." + ext - dst := "testdata/example." + ext + fs.ExtJPEG + dst := "testdata/example." + ext + fs.ExtJpeg assert.NoFileExists(t, dst) diff --git a/internal/thumb/names.go b/internal/thumb/names.go index e56c54528..832e819f6 100644 --- a/internal/thumb/names.go +++ b/internal/thumb/names.go @@ -7,7 +7,7 @@ type Name string // Jpeg returns the thumbnail name with a jpeg file extension suffix as string. func (n Name) Jpeg() string { - return string(n) + fs.ExtJPEG + return string(n) + fs.ExtJpeg } // String returns the thumbnail name as string. diff --git a/internal/thumb/open.go b/internal/thumb/open.go index e3f09e904..83a0e5fd9 100644 --- a/internal/thumb/open.go +++ b/internal/thumb/open.go @@ -22,7 +22,7 @@ func Open(fileName string, orientation int) (result image.Image, err error) { } // Open JPEG image with color processing? - if Color != ColorNone && fs.FileType(fileName) == fs.ImageJPEG { + if Color != ColorNone && fs.FileType(fileName) == fs.ImageJpeg { return OpenJpeg(fileName, orientation) } diff --git a/internal/thumb/open_jpeg.go b/internal/thumb/open_jpeg.go index 307df7a6d..e1e968ac3 100644 --- a/internal/thumb/open_jpeg.go +++ b/internal/thumb/open_jpeg.go @@ -10,6 +10,7 @@ import ( "github.com/disintegration/imaging" "github.com/mandykoh/prism/meta" "github.com/mandykoh/prism/meta/autometa" + "github.com/photoprism/photoprism/pkg/clean" "github.com/photoprism/photoprism/pkg/media/colors" ) diff --git a/internal/thumb/options.go b/internal/thumb/options.go index 84cfa0af8..8609f5a35 100644 --- a/internal/thumb/options.go +++ b/internal/thumb/options.go @@ -44,12 +44,12 @@ func (o Options) Contains(option ResampleOption) bool { func ResampleOptions(opts ...ResampleOption) (method ResampleOption, filter ResampleFilter, format fs.Type) { method = ResampleFit filter = Filter - format = fs.ImageJPEG + format = fs.ImageJpeg for _, option := range opts { switch option { case ResamplePng: - format = fs.ImagePNG + format = fs.ImagePng case ResampleNearestNeighbor: filter = ResampleNearest case ResampleDefault: diff --git a/internal/thumb/png_test.go b/internal/thumb/png_test.go index 22aa0459d..eade17e1f 100644 --- a/internal/thumb/png_test.go +++ b/internal/thumb/png_test.go @@ -15,7 +15,7 @@ func TestPng(t *testing.T) { for _, ext := range formats { t.Run(ext, func(t *testing.T) { src := "testdata/example." + ext - dst := "testdata/example." + ext + fs.ExtPNG + dst := "testdata/example." + ext + fs.ExtPng assert.NoFileExists(t, dst) @@ -37,7 +37,7 @@ func TestPng(t *testing.T) { }) t.Run("OrientationFlipH", func(t *testing.T) { src := "testdata/example." + ext - dst := "testdata/example." + ext + fs.ExtPNG + dst := "testdata/example." + ext + fs.ExtPng assert.NoFileExists(t, dst) @@ -59,7 +59,7 @@ func TestPng(t *testing.T) { }) t.Run("OrientationFlipV", func(t *testing.T) { src := "testdata/example." + ext - dst := "testdata/example." + ext + fs.ExtPNG + dst := "testdata/example." + ext + fs.ExtPng assert.NoFileExists(t, dst) @@ -81,7 +81,7 @@ func TestPng(t *testing.T) { }) t.Run("OrientationRotate90", func(t *testing.T) { src := "testdata/example." + ext - dst := "testdata/example." + ext + fs.ExtPNG + dst := "testdata/example." + ext + fs.ExtPng assert.NoFileExists(t, dst) @@ -103,7 +103,7 @@ func TestPng(t *testing.T) { }) t.Run("OrientationRotate180", func(t *testing.T) { src := "testdata/example." + ext - dst := "testdata/example." + ext + fs.ExtPNG + dst := "testdata/example." + ext + fs.ExtPng assert.NoFileExists(t, dst) @@ -125,7 +125,7 @@ func TestPng(t *testing.T) { }) t.Run("OrientationTranspose", func(t *testing.T) { src := "testdata/example." + ext - dst := "testdata/example." + ext + fs.ExtPNG + dst := "testdata/example." + ext + fs.ExtPng assert.NoFileExists(t, dst) @@ -147,7 +147,7 @@ func TestPng(t *testing.T) { }) t.Run("OrientationTransverse", func(t *testing.T) { src := "testdata/example." + ext - dst := "testdata/example." + ext + fs.ExtPNG + dst := "testdata/example." + ext + fs.ExtPng assert.NoFileExists(t, dst) @@ -169,7 +169,7 @@ func TestPng(t *testing.T) { }) t.Run("OrientationUnspecified", func(t *testing.T) { src := "testdata/example." + ext - dst := "testdata/example." + ext + fs.ExtPNG + dst := "testdata/example." + ext + fs.ExtPng assert.NoFileExists(t, dst) @@ -191,7 +191,7 @@ func TestPng(t *testing.T) { }) t.Run("OrientationNormal", func(t *testing.T) { src := "testdata/example." + ext - dst := "testdata/example." + ext + fs.ExtPNG + dst := "testdata/example." + ext + fs.ExtPng assert.NoFileExists(t, dst) @@ -213,7 +213,7 @@ func TestPng(t *testing.T) { }) t.Run("invalid orientation", func(t *testing.T) { src := "testdata/example." + ext - dst := "testdata/example." + ext + fs.ExtPNG + dst := "testdata/example." + ext + fs.ExtPng assert.NoFileExists(t, dst) diff --git a/internal/thumb/thumb.go b/internal/thumb/thumb.go index 23050164d..258e226de 100644 --- a/internal/thumb/thumb.go +++ b/internal/thumb/thumb.go @@ -26,11 +26,10 @@ package thumb import ( "fmt" - "math" - _ "image/gif" _ "image/jpeg" _ "image/png" + "math" _ "golang.org/x/image/bmp" _ "golang.org/x/image/tiff" diff --git a/internal/thumb/vips.go b/internal/thumb/vips.go index 7f33216b9..3e1979a6a 100644 --- a/internal/thumb/vips.go +++ b/internal/thumb/vips.go @@ -90,7 +90,7 @@ func Vips(imageName string, imageBuffer []byte, hash, thumbPath string, width, h // Export to standard image format. switch fs.FileType(thumbName) { - case fs.ImagePNG: + case fs.ImagePng: thumbBuffer, _, err = img.ExportPng(VipsPngExportParams(width, height)) default: thumbBuffer, _, err = img.ExportJpeg(VipsJpegExportParams(width, height)) diff --git a/internal/workers/share.go b/internal/workers/share.go index 709a56840..748dc777d 100644 --- a/internal/workers/share.go +++ b/internal/workers/share.go @@ -120,7 +120,7 @@ func (w *Share) Start() (err error) { srcFileName := photoprism.FileName(file.File.FileRoot, file.File.FileName) - if fs.ImageJPEG.Equal(file.File.FileType) && size.Width > 0 && size.Height > 0 { + if fs.ImageJpeg.Equal(file.File.FileType) && size.Width > 0 && size.Height > 0 { srcFileName, err = thumb.FromFile(srcFileName, file.File.FileHash, w.conf.ThumbCachePath(), size.Width, size.Height, file.File.FileOrientation, size.Options...) if err != nil { diff --git a/internal/workers/share_test.go b/internal/workers/share_test.go index 68d78ce4e..1998feb00 100644 --- a/internal/workers/share_test.go +++ b/internal/workers/share_test.go @@ -5,8 +5,9 @@ import ( "github.com/photoprism/photoprism/internal/mutex" - "github.com/photoprism/photoprism/internal/config" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/config" ) func TestNewShare(t *testing.T) { diff --git a/internal/workers/sync_download_test.go b/internal/workers/sync_download_test.go index a3e9af28d..fb23868dd 100644 --- a/internal/workers/sync_download_test.go +++ b/internal/workers/sync_download_test.go @@ -6,8 +6,9 @@ import ( "github.com/photoprism/photoprism/internal/entity" - "github.com/photoprism/photoprism/internal/config" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/config" ) func TestSync_download(t *testing.T) { diff --git a/internal/workers/sync_test.go b/internal/workers/sync_test.go index dc636c02e..e10f6b88a 100644 --- a/internal/workers/sync_test.go +++ b/internal/workers/sync_test.go @@ -5,8 +5,9 @@ import ( "github.com/photoprism/photoprism/internal/mutex" - "github.com/photoprism/photoprism/internal/config" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/config" ) func TestNewSync(t *testing.T) { diff --git a/pkg/clean/content_type.go b/pkg/clean/content_type.go index 92ea434e2..d1fd8da71 100644 --- a/pkg/clean/content_type.go +++ b/pkg/clean/content_type.go @@ -3,7 +3,7 @@ package clean import ( "strings" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) // ContentType normalizes media content type strings, see https://en.wikipedia.org/wiki/Media_type. @@ -15,7 +15,7 @@ func ContentType(s string) string { s = Type(s) // Replace "video/quicktime" with "video/mp4" as the container formats are largely compatible. - s = strings.Replace(s, header.ContentTypeQT, header.ContentTypeMP4, 1) + s = strings.Replace(s, header.ContentTypeMov, header.ContentTypeMp4, 1) switch s { case "": @@ -34,26 +34,26 @@ func ContentType(s string) string { case "image/svg": return header.ContentTypeSVG case "image/jpe", "image/jpg": - return header.ContentTypeJPEG + return header.ContentTypeJpeg case "video/mp4; codecs=\"avc\"", "video/mp4; codecs=\"avc1\"": - return header.ContentTypeAVC // Advanced Video Coding (AVC), also known as H.264 + return header.ContentTypeMp4Avc // Advanced Video Coding (AVC), also known as H.264 case "video/mp4; codecs=\"hvc\"", "video/mp4; codecs=\"hvc1\"", "video/mp4; codecs=\"hevc\"": - return header.ContentTypeHEVC // HEVC MP4 Main10 Profile + return header.ContentTypeMp4Hevc // HEVC Mp4 Main10 Profile case "video/mp4; codecs=\"hev\"", "video/mp4; codecs=\"hev1\"": - return header.ContentTypeHEV1 // HEVC bitstream with the parameter sets stored in the samples, not supported on macOS + return header.ContentTypeMp4Hev1 // HEVC bitstream with the parameter sets stored in the samples, not supported on macOS case "video/webm; codecs=\"vp8\"", "video/webm; codecs=\"vp08\"": - return header.ContentTypeVP8 // Google WebM container with VP8 video + return header.ContentTypeWebmVp8 // Google WebM container with VP8 video case "video/webm; codecs=\"vp9\"", "video/webm; codecs=\"vp09\"": - return header.ContentTypeVP9 // Google WebM container with VP9 video + return header.ContentTypeWebmVp9 // Google WebM container with VP9 video case "video/webm; codecs=\"av1\"", "video/webm; codecs=\"av01\"": - return header.ContentTypeAV1 // Google WebM container with AV1 video + return header.ContentTypeWebmAv1 // Google WebM container with AV1 video } return s diff --git a/pkg/fs/bytes.go b/pkg/fs/bytes.go index 1fcac7640..1af222b1f 100644 --- a/pkg/fs/bytes.go +++ b/pkg/fs/bytes.go @@ -1,8 +1,8 @@ package fs -// Kilobyte (KB), Megabyte (MB), and Gigabyte (GB) size constants. +// KiloByte, MegaByte, and GigaByte size constants. const ( - KB = 1024 - MB = KB * 1024 - GB = MB * 1024 + KiloByte = 1024 + MegaByte = KiloByte * 1024 + GigaByte = MegaByte * 1024 ) diff --git a/pkg/fs/file_ext.go b/pkg/fs/file_ext.go index 285f6599c..6a38bf1f4 100644 --- a/pkg/fs/file_ext.go +++ b/pkg/fs/file_ext.go @@ -6,18 +6,23 @@ import ( ) const ( - ExtJPEG = ".jpg" - ExtPNG = ".png" - ExtDNG = ".dng" - ExtTHM = ".thm" - ExtAVC = ".avc" - ExtHEVC = ".hevc" - ExtHEV1 = ".hev1" - ExtVVC = ".vvc" - ExtEVC = ".evc" - ExtMP4 = ".mp4" - ExtMOV = ".mov" - ExtYAML = ".yml" + ExtJpeg = ".jpg" + ExtPng = ".png" + ExtDng = ".dng" + ExtThm = ".thm" + ExtAvc = ".avc" + ExtHevc = ".hevc" + ExtHev1 = ".hev1" + ExtVvc = ".vvc" + ExtEvc = ".evc" + ExtMp4 = ".mp4" + ExtMov = ".mov" + ExtYaml = ".yml" + ExtJson = ".json" + ExtXml = ".xml" + ExtXMP = ".xmp" + ExtTxt = ".txt" + ExtMd = ".md" ) // Ext returns all extension of a file name including the dots. diff --git a/pkg/fs/file_exts.go b/pkg/fs/file_exts.go index 6781578f0..7b0d0776d 100644 --- a/pkg/fs/file_exts.go +++ b/pkg/fs/file_exts.go @@ -10,35 +10,35 @@ type FileExtensions map[string]Type // Extensions contains the filename extensions of file formats known to PhotoPrism. var Extensions = FileExtensions{ - ExtJPEG: ImageJPEG, // .jpg - ".jpeg": ImageJPEG, - ".jpe": ImageJPEG, - ".jif": ImageJPEG, - ".jfif": ImageJPEG, - ".jfi": ImageJPEG, - ".jxl": ImageJPEGXL, - ExtTHM: ImageThumb, - ".tif": ImageTIFF, - ".tiff": ImageTIFF, - ".psd": ImagePSD, - ExtPNG: ImagePNG, // .png - ".apng": ImagePNG, - ".pnga": ImagePNG, - ".pn": ImagePNG, - ".gif": ImageGIF, - ".bmp": ImageBMP, - ExtDNG: ImageDNG, // .dng - ".avif": ImageAVIF, - ".avis": ImageAVIFS, - ".avifs": ImageAVIFS, - ".hif": ImageHEIC, - ".heif": ImageHEIC, - ".heic": ImageHEIC, - ".avci": ImageHEIC, - ".avcs": ImageHEIC, - ".heifs": ImageHEICS, - ".heics": ImageHEICS, - ".webp": ImageWebP, + ExtJpeg: ImageJpeg, // .jpg + ".jpeg": ImageJpeg, + ".jpe": ImageJpeg, + ".jif": ImageJpeg, + ".jfif": ImageJpeg, + ".jfi": ImageJpeg, + ".jxl": ImageJpegXL, + ExtThm: ImageThumb, + ".tif": ImageTiff, + ".tiff": ImageTiff, + ".psd": ImagePsd, + ExtPng: ImagePng, // .png + ".apng": ImagePng, + ".pnga": ImagePng, + ".pn": ImagePng, + ".gif": ImageGif, + ".bmp": ImageBmp, + ExtDng: ImageDNG, // .dng + ".avif": ImageAvif, + ".avis": ImageAvifS, + ".avifs": ImageAvifS, + ".hif": ImageHeic, + ".heif": ImageHeic, + ".heic": ImageHeic, + ".avci": ImageHeic, + ".avcs": ImageHeic, + ".heifs": ImageHeicS, + ".heics": ImageHeicS, + ".webp": ImageWebp, ".mpo": ImageMPO, ".3fr": ImageRaw, ".ari": ImageRaw, @@ -80,36 +80,36 @@ var Extensions = FileExtensions{ ".srw": ImageRaw, ".sr2": ImageRaw, ".x3f": ImageRaw, - ExtAVC: VideoAVC, // .avc - ExtHEVC: VideoHEVC, // .hevc - ExtHEV1: VideoHEV1, // .hev1 - ExtVVC: VideoVVC, // .vvc - ExtEVC: VideoEVC, // .evc - ".mov": VideoMOV, - ".qt": VideoMOV, + ExtAvc: VideoAvc, // .avc + ExtHevc: VideoHevc, // .hevc + ExtHev1: VideoHev1, // .hev1 + ExtVvc: VideoVvc, // .vvc + ExtEvc: VideoEvc, // .evc + ".mov": VideoMov, + ".qt": VideoMov, ".avi": VideoAVI, - ".av1": VideoAV1, - ".mpg": VideoMPG, - ".mpeg": VideoMPG, - ".mjpg": VideoMJPG, - ".mjpeg": VideoMJPG, - ".mp2": VideoMP2, - ".mpv": VideoMP2, - ".mp": VideoMP4, - ExtMP4: VideoMP4, // .mp4 + ".av1": VideoAv1, + ".mpg": VideoMpeg, + ".mpeg": VideoMpeg, + ".mjpg": VideoMjpeg, + ".mjpeg": VideoMjpeg, + ".mp2": VideoMp2, + ".mpv": VideoMp2, + ".mp": VideoMp4, + ExtMp4: VideoMp4, // .mp4 ".m4v": VideoM4V, ".mxf": VideoMXF, ".3gp": Video3GP, ".3g2": Video3G2, ".flv": VideoFlash, ".f4v": VideoFlash, - ".mkv": VideoMKV, - ".mts": VideoAVCHD, + ".mkv": VideoMkv, + ".mts": VideoAvcHD, ".m2ts": VideoBDAV, - ".ogv": VideoOGV, - ".ogg": VideoOGV, - ".ogx": VideoOGV, - ".webm": VideoWebM, + ".ogv": VideoTheora, + ".ogg": VideoTheora, + ".ogx": VideoTheora, + ".webm": VideoWebm, ".asf": VideoASF, ".wmv": VideoWMV, ".dv": VideoDV, @@ -125,15 +125,15 @@ var Extensions = FileExtensions{ ".ept": VectorEPS, ".epsf": VectorEPS, ".epsi": VectorEPS, - ".xmp": SidecarXMP, - ".aae": SidecarAAE, - ".xml": SidecarXML, - ExtYAML: SidecarYAML, // .yml - ".yaml": SidecarYAML, - ".json": SidecarJSON, - ".txt": SidecarText, + ExtXMP: SidecarXMP, + ".aae": SidecarAppleXml, + ExtXml: SidecarXml, + ExtYaml: SidecarYaml, // .yml + ".yaml": SidecarYaml, + ExtJson: SidecarJson, + ExtTxt: SidecarText, ".nfo": SidecarInfo, - ".md": SidecarMarkdown, + ExtMd: SidecarMarkdown, ".markdown": SidecarMarkdown, } diff --git a/pkg/fs/file_info.go b/pkg/fs/file_info.go index 8d0f8779f..e64bfce9e 100644 --- a/pkg/fs/file_info.go +++ b/pkg/fs/file_info.go @@ -6,31 +6,31 @@ type TypeMap map[Type]string var TypeInfo = TypeMap{ ImageRaw: "Unprocessed Sensor Data", ImageDNG: "Adobe Digital Negative", - ImageJPEG: "Joint Photographic Experts Group (JPEG)", - ImageJPEGXL: "JPEG XL", + ImageJpeg: "Joint Photographic Experts Group (JPEG)", + ImageJpegXL: "JPEG XL", ImageThumb: "Thumbnail Image", - ImagePNG: "Portable Network Graphics", - ImageGIF: "Graphics Interchange Format", - ImageTIFF: "Tag Image File Format", - ImagePSD: "Adobe Photoshop", - ImageBMP: "Bitmap", + ImagePng: "Portable Network Graphics", + ImageGif: "Graphics Interchange Format", + ImageTiff: "Tag Image File Format", + ImagePsd: "Adobe Photoshop", + ImageBmp: "Bitmap", ImageMPO: "Stereoscopic JPEG (3D)", - ImageAVIF: "AV1 Image File Format", - ImageAVIFS: "AV1 Image Sequence", - ImageHEIF: "High Efficiency Image File Format", - ImageHEIC: "High Efficiency Image Container", - ImageHEICS: "HEIC Image Sequence", - ImageWebP: "Google WebP", - VideoWebM: "Google WebM", - VideoMP2: "MPEG 2 (H.262)", - VideoAVC: "Advanced Video Coding (H.264, MPEG-4 Part 10)", - VideoHEVC: "High Efficiency Video Coding (H.265)", - VideoHEV1: "High Efficiency Video Coding (HEVC) Bitstream", - VideoVVC: "Versatile Video Coding (H.266)", - VideoEVC: "Essential Video Coding (MPEG-5 Part 1)", - VideoAV1: "AOMedia Video 1", - VideoMOV: "Apple QuickTime", - VideoMP4: "Multimedia Container (MPEG-4 Part 14)", + ImageAvif: "AV1 Image File Format", + ImageAvifS: "AV1 Image Sequence", + ImageHeif: "High Efficiency Image File Format", + ImageHeic: "High Efficiency Image Container", + ImageHeicS: "HEIC Image Sequence", + ImageWebp: "Google WebP", + VideoWebm: "Google WebM", + VideoMp2: "MPEG 2 (H.262)", + VideoAvc: "Advanced Video Coding (H.264, MPEG-4 Part 10)", + VideoHevc: "High Efficiency Video Coding (H.265)", + VideoHev1: "High Efficiency Video Coding (HEVC) Bitstream", + VideoVvc: "Versatile Video Coding (H.266)", + VideoEvc: "Essential Video Coding (MPEG-5 Part 1)", + VideoAv1: "AOMedia Video 1", + VideoMov: "Apple QuickTime", + VideoMp4: "Multimedia Container (MPEG-4 Part 14)", VideoM4V: "Apple iTunes Multimedia Container", VideoMXF: "Material Exchange Format", VideoAVI: "Microsoft Audio Video Interleave", @@ -40,21 +40,21 @@ var TypeInfo = TypeMap{ Video3GP: "Mobile Multimedia Container (3G)", Video3G2: "Mobile Multimedia Container (CDMA2000)", VideoFlash: "Adobe Flash", - VideoMKV: "Matroska Multimedia Container", - VideoMPG: "Moving Picture Experts Group (MPEG)", - VideoMJPG: "Motion JPEG", - VideoAVCHD: "Advanced Video Coding High Definition (AVCHD)", + VideoMkv: "Matroska Multimedia Container", + VideoMpeg: "Moving Picture Experts Group (MPEG)", + VideoMjpeg: "Motion JPEG", + VideoAvcHD: "Advanced Video Coding High Definition (AVCHD)", VideoBDAV: "Blu-ray MPEG-2 Transport Stream", - VideoOGV: "Ogg Media (OGG)", + VideoTheora: "Ogg Media (OGG)", VectorSVG: "Scalable Vector Graphics", VectorAI: "Adobe Illustrator", VectorPS: "Adobe PostScript", VectorEPS: "Encapsulated PostScript", SidecarXMP: "Adobe Extensible Metadata Platform", - SidecarAAE: "Apple Image Edits XML", - SidecarXML: "Extensible Markup Language", - SidecarJSON: "Serialized JSON Data (Exiftool, Google Photos)", - SidecarYAML: "Serialized YAML Data (Config, Metadata)", + SidecarAppleXml: "Apple Image Edits XML", + SidecarXml: "Extensible Markup Language", + SidecarJson: "Serialized JSON Data (Exiftool, Google Photos)", + SidecarYaml: "Serialized YAML Data (Config, Metadata)", SidecarText: "Plain Text", SidecarInfo: "Info Text", SidecarMarkdown: "Markdown Formatted Text", diff --git a/pkg/fs/file_type_animated.go b/pkg/fs/file_type_animated.go index 1e5236b55..0b337a51a 100644 --- a/pkg/fs/file_type_animated.go +++ b/pkg/fs/file_type_animated.go @@ -2,11 +2,11 @@ package fs // TypeAnimated maps animated file types to their mime type. var TypeAnimated = TypeMap{ - ImageGIF: MimeTypeGIF, - ImagePNG: MimeTypeAPNG, - ImageWebP: MimeTypeWebP, - ImageAVIF: MimeTypeAVIFS, - ImageAVIFS: MimeTypeAVIFS, - ImageHEIC: MimeTypeHEICS, - ImageHEICS: MimeTypeHEICS, + ImageGif: MimeTypeGif, + ImagePng: MimeTypeAPng, + ImageWebp: MimeTypeWebp, + ImageAvif: MimeTypeAvifS, + ImageAvifS: MimeTypeAvifS, + ImageHeic: MimeTypeHeicS, + ImageHeicS: MimeTypeHeicS, } diff --git a/pkg/fs/file_type_test.go b/pkg/fs/file_type_test.go index 7e7e7d8eb..93e52bec0 100644 --- a/pkg/fs/file_type_test.go +++ b/pkg/fs/file_type_test.go @@ -8,29 +8,29 @@ import ( func TestType_String(t *testing.T) { t.Run("jpg", func(t *testing.T) { - assert.Equal(t, "jpg", ImageJPEG.String()) + assert.Equal(t, "jpg", ImageJpeg.String()) }) } func TestType_Equal(t *testing.T) { t.Run("jpg", func(t *testing.T) { - assert.True(t, ImageJPEG.Equal("jpg")) + assert.True(t, ImageJpeg.Equal("jpg")) }) } func TestType_NotEqual(t *testing.T) { t.Run("jpg", func(t *testing.T) { - assert.False(t, ImageJPEG.NotEqual("JPG")) - assert.True(t, ImageJPEG.NotEqual("xmp")) + assert.False(t, ImageJpeg.NotEqual("JPG")) + assert.True(t, ImageJpeg.NotEqual("xmp")) }) } func TestType_DefaultExt(t *testing.T) { t.Run("jpg", func(t *testing.T) { - assert.Equal(t, ".jpg", ImageJPEG.DefaultExt()) + assert.Equal(t, ".jpg", ImageJpeg.DefaultExt()) }) t.Run("avif", func(t *testing.T) { - assert.Equal(t, ".avif", ImageAVIF.DefaultExt()) + assert.Equal(t, ".avif", ImageAvif.DefaultExt()) }) } @@ -48,44 +48,44 @@ func TestToType(t *testing.T) { func TestType_Is(t *testing.T) { t.Run("Empty", func(t *testing.T) { - assert.False(t, ImageJPEG.Equal("")) + assert.False(t, ImageJpeg.Equal("")) }) t.Run("Upper", func(t *testing.T) { - assert.True(t, ImageJPEG.Equal("JPG")) + assert.True(t, ImageJpeg.Equal("JPG")) }) t.Run("Lower", func(t *testing.T) { - assert.True(t, ImageJPEG.Equal("jpg")) + assert.True(t, ImageJpeg.Equal("jpg")) }) t.Run("False", func(t *testing.T) { - assert.False(t, ImageJPEG.Equal("raw")) + assert.False(t, ImageJpeg.Equal("raw")) }) } func TestType_Find(t *testing.T) { t.Run("find jpg", func(t *testing.T) { - result := ImageJPEG.Find("testdata/test.xmp", false) + result := ImageJpeg.Find("testdata/test.xmp", false) assert.Equal(t, "testdata/test.jpg", result) }) t.Run("upper ext", func(t *testing.T) { - result := ImageJPEG.Find("testdata/test.XMP", false) + result := ImageJpeg.Find("testdata/test.XMP", false) assert.Equal(t, "testdata/test.jpg", result) }) t.Run("with sequence", func(t *testing.T) { - result := ImageJPEG.Find("testdata/test (2).xmp", false) + result := ImageJpeg.Find("testdata/test (2).xmp", false) assert.Equal(t, "", result) }) t.Run("strip sequence", func(t *testing.T) { - result := ImageJPEG.Find("testdata/test (2).xmp", true) + result := ImageJpeg.Find("testdata/test (2).xmp", true) assert.Equal(t, "testdata/test.jpg", result) }) t.Run("name upper", func(t *testing.T) { - result := ImageJPEG.Find("testdata/CATYELLOW.xmp", true) + result := ImageJpeg.Find("testdata/CATYELLOW.xmp", true) assert.Equal(t, "testdata/CATYELLOW.jpg", result) }) t.Run("name lower", func(t *testing.T) { - result := ImageJPEG.Find("testdata/chameleon_lime.xmp", true) + result := ImageJpeg.Find("testdata/chameleon_lime.xmp", true) assert.Equal(t, "testdata/chameleon_lime.jpg", result) }) } @@ -110,47 +110,47 @@ func TestType_FindFirst(t *testing.T) { assert.Equal(t, "testdata/.photoprism/test.xmp", result) }) t.Run("find jpg", func(t *testing.T) { - result := ImageJPEG.FindFirst("testdata/test.xmp", dirs, "", false) + result := ImageJpeg.FindFirst("testdata/test.xmp", dirs, "", false) assert.Equal(t, "testdata/test.jpg", result) }) t.Run("find jpg abs", func(t *testing.T) { - result := ImageJPEG.FindFirst(Abs("testdata/test.xmp"), dirs, "", false) + result := ImageJpeg.FindFirst(Abs("testdata/test.xmp"), dirs, "", false) assert.Equal(t, Abs("testdata/test.jpg"), result) }) t.Run("upper ext", func(t *testing.T) { - result := ImageJPEG.FindFirst("testdata/test.XMP", dirs, "", false) + result := ImageJpeg.FindFirst("testdata/test.XMP", dirs, "", false) assert.Equal(t, "testdata/test.jpg", result) }) t.Run("with sequence", func(t *testing.T) { - result := ImageJPEG.FindFirst("testdata/test (2).xmp", dirs, "", false) + result := ImageJpeg.FindFirst("testdata/test (2).xmp", dirs, "", false) assert.Equal(t, "", result) }) t.Run("strip sequence", func(t *testing.T) { - result := ImageJPEG.FindFirst("testdata/test (2).xmp", dirs, "", true) + result := ImageJpeg.FindFirst("testdata/test (2).xmp", dirs, "", true) assert.Equal(t, "testdata/test.jpg", result) }) t.Run("name upper", func(t *testing.T) { - result := ImageJPEG.FindFirst("testdata/CATYELLOW.xmp", dirs, "", true) + result := ImageJpeg.FindFirst("testdata/CATYELLOW.xmp", dirs, "", true) assert.Equal(t, "testdata/CATYELLOW.jpg", result) }) t.Run("name lower", func(t *testing.T) { - result := ImageJPEG.FindFirst("testdata/chameleon_lime.xmp", dirs, "", true) + result := ImageJpeg.FindFirst("testdata/chameleon_lime.xmp", dirs, "", true) assert.Equal(t, "testdata/chameleon_lime.jpg", result) }) t.Run("example_bmp_notfound", func(t *testing.T) { - result := ImageBMP.FindFirst("testdata/example.00001.jpg", dirs, "", true) + result := ImageBmp.FindFirst("testdata/example.00001.jpg", dirs, "", true) assert.Equal(t, "", result) }) t.Run("example_bmp_found", func(t *testing.T) { - result := ImageBMP.FindFirst("testdata/example.00001.jpg", []string{"directory"}, "", true) + result := ImageBmp.FindFirst("testdata/example.00001.jpg", []string{"directory"}, "", true) assert.Equal(t, "testdata/directory/example.bmp", result) }) t.Run("example_png_found", func(t *testing.T) { - result := ImagePNG.FindFirst("testdata/example.00001.jpg", []string{"directory", "directory/subdirectory"}, "", true) + result := ImagePng.FindFirst("testdata/example.00001.jpg", []string{"directory", "directory/subdirectory"}, "", true) assert.Equal(t, "testdata/directory/subdirectory/example.png", result) }) t.Run("example_bmp_found", func(t *testing.T) { - result := ImageBMP.FindFirst(Abs("testdata/example.00001.jpg"), []string{"directory"}, Abs("testdata"), true) + result := ImageBmp.FindFirst(Abs("testdata/example.00001.jpg"), []string{"directory"}, Abs("testdata"), true) assert.Equal(t, Abs("testdata/directory/example.bmp"), result) }) } @@ -159,7 +159,7 @@ func TestType_FindAll(t *testing.T) { dirs := []string{PPHiddenPathname} t.Run("CATYELLOW.jpg", func(t *testing.T) { - result := ImageJPEG.FindAll("testdata/CATYELLOW.JSON", dirs, "", false) + result := ImageJpeg.FindAll("testdata/CATYELLOW.JSON", dirs, "", false) assert.Contains(t, result, "testdata/CATYELLOW.jpg") }) } @@ -171,7 +171,7 @@ func TestFileType(t *testing.T) { }) t.Run("JPEG", func(t *testing.T) { result := FileType("testdata/test.jpg") - assert.Equal(t, ImageJPEG, result) + assert.Equal(t, ImageJpeg, result) }) t.Run("RawCRW", func(t *testing.T) { result := FileType("testdata/test (jpg).crw") @@ -181,7 +181,7 @@ func TestFileType(t *testing.T) { result := FileType("testdata/test (jpg).CR2") assert.Equal(t, ImageRaw, result) }) - t.Run("MP4", func(t *testing.T) { + t.Run("Mp4", func(t *testing.T) { assert.Equal(t, Type("mp4"), FileType("file.mp")) }) } @@ -196,7 +196,7 @@ func TestIsAnimatedImage(t *testing.T) { t.Run("RawCRW", func(t *testing.T) { assert.False(t, IsAnimatedImage("testdata/test (jpg).crw")) }) - t.Run("MP4", func(t *testing.T) { + t.Run("Mp4", func(t *testing.T) { assert.False(t, IsAnimatedImage("file.mp")) assert.False(t, IsAnimatedImage("file.mp4")) }) diff --git a/pkg/fs/file_types.go b/pkg/fs/file_types.go index 4b1c54d42..47a2e8fce 100644 --- a/pkg/fs/file_types.go +++ b/pkg/fs/file_types.go @@ -21,50 +21,50 @@ const ( // Supported media.Image file types: const ( - ImageJPEG Type = "jpg" // JPEG Image - ImageJPEGXL Type = "jxl" // JPEG XL Image + ImageJpeg Type = "jpg" // JPEG Image + ImageJpegXL Type = "jxl" // JPEG XL Image ImageThumb Type = "thm" // Thumbnail Image - ImagePNG Type = "png" // PNG Image - ImageGIF Type = "gif" // GIF Image - ImageTIFF Type = "tiff" // TIFF Image - ImagePSD Type = "psd" // Adobe Photoshop - ImageBMP Type = "bmp" // BMP Image + ImagePng Type = "png" // PNG Image + ImageGif Type = "gif" // GIF Image + ImageTiff Type = "tiff" // TIFF Image + ImagePsd Type = "psd" // Adobe Photoshop + ImageBmp Type = "bmp" // BMP Image ImageMPO Type = "mpo" // Stereoscopic Image that consists of two JPG images that are combined into one 3D image - ImageAVIF Type = "avif" // AV1 Image File (AVIF) - ImageAVIFS Type = "avifs" // AV1 Image Sequence (Animated AVIF) - ImageHEIF Type = "heif" // High Efficiency Image File Format (HEIF) - ImageHEIC Type = "heic" // High Efficiency Image Container (HEIC) - ImageHEICS Type = "heics" // HEIC Image Sequence - ImageWebP Type = "webp" // Google WebP Image + ImageAvif Type = "avif" // AV1 Image File (AVIF) + ImageAvifS Type = "avifs" // AV1 Image Sequence (Animated AVIF) + ImageHeif Type = "heif" // High Efficiency Image File Format (HEIF) + ImageHeic Type = "heic" // High Efficiency Image Container (HEIC) + ImageHeicS Type = "heics" // HEIC Image Sequence + ImageWebp Type = "webp" // Google WebP Image ) // Supported media.Video file types: const ( - VideoWebM Type = "webm" // Google WebM Video - VideoHEVC Type = "hevc" // H.265, High Efficiency Video Coding (HEVC) - VideoHEV1 Type = "hev1" // HEVC Bitstream, not supported on macOS - VideoAVI Type = "avi" // Microsoft Audio Video Interleave (AVI) - VideoAVC Type = "avc" // H.264, Advanced Video Coding (AVC, MPEG-4 Part 10) - VideoVVC Type = "vvc" // H.266, Versatile Video Coding (VVC) - VideoEVC Type = "evc" // Essential Video Coding (MPEG-5 Part 1) - VideoAV1 Type = "av1" // Alliance for Open Media Video - VideoMPG Type = "mpg" // Moving Picture Experts Group (MPEG) - VideoMJPG Type = "mjpg" // Motion JPEG (M-JPEG) - VideoMP2 Type = "mp2" // MPEG-2, H.222/H.262 - VideoMP4 Type = "mp4" // MPEG-4 Container based on QuickTime, can contain AVC, HEVC,... - VideoM4V Type = "m4v" // Apple iTunes MPEG-4 Container, optionally with DRM copy protection - VideoMKV Type = "mkv" // Matroska Multimedia Container, free and open - VideoMOV Type = "mov" // QuickTime File Format, can contain AVC, HEVC,... - VideoMXF Type = "mxf" // Material Exchange Format - Video3GP Type = "3gp" // Mobile Multimedia Container, MPEG-4 Part 12 - Video3G2 Type = "3g2" // Similar to 3GP, consumes less space & bandwidth - VideoFlash Type = "flv" // Flash Video - VideoAVCHD Type = "mts" // AVCHD (Advanced Video Coding High Definition) - VideoBDAV Type = "m2ts" // Blu-ray MPEG-2 Transport Stream - VideoOGV Type = "ogv" // Ogg container format maintained by the Xiph.Org, free and open - VideoASF Type = "asf" // Advanced Systems/Streaming Format (ASF) - VideoWMV Type = "wmv" // Windows Media Video (based on ASF) - VideoDV Type = "dv" // DV Video (https://en.wikipedia.org/wiki/DV) + VideoWebm Type = "webm" // Google WebM Video + VideoHevc Type = "hevc" // H.265, High Efficiency Video Coding (HEVC) + VideoHev1 Type = "hev1" // HEVC Bitstream, not supported on macOS + VideoAvc Type = "avc" // H.264, Advanced Video Coding (AVC, MPEG-4 Part 10) + VideoVvc Type = "vvc" // H.266, Versatile Video Coding (VVC) + VideoEvc Type = "evc" // Essential Video Coding (MPEG-5 Part 1) + VideoAv1 Type = "av1" // Alliance for Open Media Video + VideoMpeg Type = "mpg" // Moving Picture Experts Group (MPEG) + VideoMjpeg Type = "mjpg" // Motion JPEG (M-JPEG) + VideoMp2 Type = "mp2" // MPEG-2, H.222/H.262 + VideoMp4 Type = "mp4" // MPEG-4 Container based on QuickTime, can contain AVC, HEVC,... + VideoM4V Type = "m4v" // Apple iTunes MPEG-4 Container, optionally with DRM copy protection + VideoMkv Type = "mkv" // Matroska Multimedia Container, free and open + VideoMov Type = "mov" // QuickTime File Format, can contain AVC, HEVC,... + VideoMXF Type = "mxf" // Material Exchange Format + Video3GP Type = "3gp" // Mobile Multimedia Container, MPEG-4 Part 12 + Video3G2 Type = "3g2" // Similar to 3GP, consumes less space & bandwidth + VideoFlash Type = "flv" // Flash Video + VideoAvcHD Type = "mts" // AVCHD (Advanced Video Coding High Definition) + VideoBDAV Type = "m2ts" // Blu-ray MPEG-2 Transport Stream + VideoTheora Type = "ogv" // Ogg container format maintained by the Xiph.Org, free and open + VideoASF Type = "asf" // Advanced Systems/Streaming Format (ASF) + VideoAVI Type = "avi" // Microsoft Audio Video Interleave (AVI) + VideoWMV Type = "wmv" // Windows Media Video (based on ASF) + VideoDV Type = "dv" // DV Video (https://en.wikipedia.org/wiki/DV) ) // Supported media.Vector file types: @@ -77,11 +77,11 @@ const ( // Supported media.Sidecar file types: const ( + SidecarYaml Type = "yml" // YAML metadata / config / sidecar file + SidecarJson Type = "json" // JSON metadata / config / sidecar file + SidecarXml Type = "xml" // XML metadata / config / sidecar file + SidecarAppleXml Type = "aae" // Apple image edits sidecar file (based on XML) SidecarXMP Type = "xmp" // Adobe XMP sidecar file (XML) - SidecarXML Type = "xml" // XML metadata / config / sidecar file - SidecarAAE Type = "aae" // Apple image edits sidecar file (based on XML) - SidecarYAML Type = "yml" // YAML metadata / config / sidecar file - SidecarJSON Type = "json" // JSON metadata / config / sidecar file SidecarText Type = "txt" // Text config / sidecar file SidecarInfo Type = "nfo" // Info text file as used by e.g. Plex Media Server SidecarMarkdown Type = "md" // Markdown text sidecar file diff --git a/pkg/fs/mime.go b/pkg/fs/mime.go index df172cd6e..7996f55b8 100644 --- a/pkg/fs/mime.go +++ b/pkg/fs/mime.go @@ -10,28 +10,28 @@ import ( const ( MimeTypeUnknown = "" MimeTypeBinary = "application/octet-stream" - MimeTypeJPEG = "image/jpeg" - MimeTypeJPEGXL = "image/jxl" - MimeTypePNG = "image/png" - MimeTypeAPNG = "image/vnd.mozilla.apng" - MimeTypeGIF = "image/gif" - MimeTypeBMP = "image/bmp" - MimeTypeTIFF = "image/tiff" + MimeTypeJpeg = "image/jpeg" + MimeTypeJpegXL = "image/jxl" + MimeTypePng = "image/png" + MimeTypeAPng = "image/vnd.mozilla.apng" + MimeTypeGif = "image/gif" + MimeTypeBmp = "image/bmp" + MimeTypeTiff = "image/tiff" MimeTypeDNG = "image/dng" - MimeTypeAVIF = "image/avif" - MimeTypeAVIFS = "image/avif-sequence" - MimeTypeHEIC = "image/heic" - MimeTypeHEICS = "image/heic-sequence" - MimeTypeWebP = "image/webp" - MimeTypeMP4 = "video/mp4" - MimeTypeMOV = "video/quicktime" + MimeTypeAvif = "image/avif" + MimeTypeAvifS = "image/avif-sequence" + MimeTypeHeic = "image/heic" + MimeTypeHeicS = "image/heic-sequence" + MimeTypeWebp = "image/webp" + MimeTypeMp4 = "video/mp4" + MimeTypeMov = "video/quicktime" MimeTypeSVG = "image/svg+xml" MimeTypeAI = "application/vnd.adobe.illustrator" MimeTypePS = "application/postscript" MimeTypeEPS = "image/eps" MimeTypeText = "text/plain" - MimeTypeXML = "text/xml" - MimeTypeJSON = "application/json" + MimeTypeXml = "text/xml" + MimeTypeJson = "application/json" ) // MimeType returns the mimetype of a file, or an empty string if it could not be determined. @@ -51,11 +51,11 @@ func MimeType(filename string) (mimeType string) { // formats, which otherwise cannot be reliably distinguished: switch fileType { // Apple QuickTime Container - case VideoMOV: - return MimeTypeMOV + case VideoMov: + return MimeTypeMov // MPEG-4 AVC Video - case VideoAVC: - return MimeTypeMP4 + case VideoAvc: + return MimeTypeMp4 // Adobe Digital Negative case ImageDNG: return MimeTypeDNG @@ -88,21 +88,21 @@ func MimeType(filename string) (mimeType string) { // If it could be detected, try to determine mime type from extension: if mimeType == MimeTypeUnknown { switch fileType { - // MP4 Multimedia Container - case VideoMP4, VideoHEVC, VideoHEV1: - return MimeTypeMP4 + // Mp4 Multimedia Container + case VideoMp4, VideoHevc, VideoHev1: + return MimeTypeMp4 // AV1 Image File - case ImageAVIF: - return MimeTypeAVIF + case ImageAvif: + return MimeTypeAvif // AV1 Image File Sequence - case ImageAVIFS: - return MimeTypeAVIFS + case ImageAvifS: + return MimeTypeAvifS // High Efficiency Image Container - case ImageHEIC, ImageHEIF: - return MimeTypeHEIC + case ImageHeic, ImageHeif: + return MimeTypeHeic // High Efficiency Image Container Sequence - case ImageHEICS: - return MimeTypeHEICS + case ImageHeicS: + return MimeTypeHeicS } } diff --git a/pkg/fs/mime_test.go b/pkg/fs/mime_test.go index bbe10897e..ebeb684a7 100644 --- a/pkg/fs/mime_test.go +++ b/pkg/fs/mime_test.go @@ -7,7 +7,7 @@ import ( ) func TestMimeType(t *testing.T) { - t.Run("MP4", func(t *testing.T) { + t.Run("Mp4", func(t *testing.T) { filename := Abs("./testdata/test.mp4") mimeType := MimeType(filename) assert.Equal(t, "video/mp4", mimeType) @@ -79,7 +79,7 @@ func TestMimeType(t *testing.T) { } func TestBaseType(t *testing.T) { - t.Run("MP4", func(t *testing.T) { + t.Run("Mp4", func(t *testing.T) { filename := Abs("./testdata/test.mp4") mimeType := BaseType(MimeType(filename)) assert.Equal(t, "video/mp4", mimeType) @@ -156,14 +156,14 @@ func TestIsType(t *testing.T) { assert.True(t, IsType("video/jpg", "video/jpg")) assert.True(t, IsType("video/jpeg", "video/jpeg")) assert.True(t, IsType("video/mp4", "video/mp4")) - assert.True(t, IsType("video/mp4", MimeTypeMP4)) - assert.True(t, IsType("video/mp4", "video/MP4")) - assert.True(t, IsType("video/mp4", "video/MP4; codecs=\"avc1.640028\"")) + assert.True(t, IsType("video/mp4", MimeTypeMp4)) + assert.True(t, IsType("video/mp4", "video/Mp4")) + assert.True(t, IsType("video/mp4", "video/Mp4; codecs=\"avc1.640028\"")) }) t.Run("False", func(t *testing.T) { - assert.False(t, IsType("", MimeTypeMP4)) + assert.False(t, IsType("", MimeTypeMp4)) assert.False(t, IsType("video/jpeg", "video/jpg")) assert.False(t, IsType("video/mp4", MimeTypeUnknown)) - assert.False(t, IsType(MimeTypeMP4, MimeTypeJPEG)) + assert.False(t, IsType(MimeTypeMp4, MimeTypeJpeg)) }) } diff --git a/pkg/geo/pluscode/pluscode.go b/pkg/geo/pluscode/pluscode.go index fbdfcb1b0..947b75580 100644 --- a/pkg/geo/pluscode/pluscode.go +++ b/pkg/geo/pluscode/pluscode.go @@ -28,6 +28,7 @@ import ( "fmt" olc "github.com/google/open-location-code/go" + "github.com/photoprism/photoprism/pkg/geo/s2" ) diff --git a/pkg/media/base64_test.go b/pkg/media/base64_test.go index 868f81960..b1fe1f785 100644 --- a/pkg/media/base64_test.go +++ b/pkg/media/base64_test.go @@ -13,12 +13,12 @@ import ( const gopher = `iVBORw0KGgoAAAANSUhEUgAAAEsAAAA8CAAAAAALAhhPAAAFfUlEQVRYw62XeWwUVRzHf2+OPbo9d7tsWyiyaZti6eWGAhISoIGKECEKCAiJJkYTiUgTMYSIosYYBBIUIxoSPIINEBDi2VhwkQrVsj1ESgu9doHWdrul7ba73WNm3vOPtsseM9MdwvvrzTs+8/t95ze/33sI5BqiabU6m9En8oNjduLnAEDLUsQXFF8tQ5oxK3vmnNmDSMtrncks9Hhtt/qeWZapHb1ha3UqYSWVl2ZmpWgaXMXGohQAvmeop3bjTRtv6SgaK/Pb9/bFzUrYslbFAmHPp+3WhAYdr+7GN/YnpN46Opv55VDsJkoEpMrY/vO2BIYQ6LLvm0ThY3MzDzzeSJeeWNyTkgnIE5ePKsvKlcg/0T9QMzXalwXMlj54z4c0rh/mzEfr+FgWEz2w6uk8dkzFAgcARAgNp1ZYef8bH2AgvuStbc2/i6CiWGj98y2tw2l4FAXKkQBIf+exyRnteY83LfEwDQAYCoK+P6bxkZm/0966LxcAAILHB56kgD95PPxltuYcMtFTWw/FKkY/6Opf3GGd9ZF+Qp6mzJxzuRSractOmJrH1u8XTvWFHINNkLQLMR+XHXvfPPHw967raE1xxwtA36IMRfkAAG29/7mLuQcb2WOnsJReZGfpiHsSBX81cvMKywYZHhX5hFPtOqPGWZCXnhWGAu6lX91ElKXSalcLXu3UaOXVay57ZSe5f6Gpx7J2MXAsi7EqSp09b/MirKSyJfnfEEgeDjl8FgDAfvewP03zZ+AJ0m9aFRM8eEHBDRKjfcreDXnZdQuAxXpT2NRJ7xl3UkLBhuVGU16gZiGOgZmrSbRdqkILuL/yYoSXHHkl9KXgqNu3PB8oRg0geC5vFmLjad6mUyTKLmF3OtraWDIfACyXqmephaDABawfpi6tqqBZytfQMqOz6S09iWXhktrRaB8Xz4Yi/8gyABDm5NVe6qq/3VzPrcjELWrebVuyY2T7ar4zQyybUCtsQ5Es1FGaZVrRVQwAgHGW2ZCRZshI5bGQi7HesyE972pOSeMM0dSktlzxRdrlqb3Osa6CCS8IJoQQQgBAbTAa5l5epO34rJszibJI8rxLfGzcp1dRosutGeb2VDNgqYrwTiPNsLxXiPi3dz7LiS1WBRBDBOnqEjyy3aQb+/bLiJzz9dIkscVBBLxMfSEac7kO4Fpkngi0ruNBeSOal+u8jgOuqPz12nryMLCniEjtOOOmpt+KEIqsEdocJjYXwrh9OZqWJQyPCTo67LNS/TdxLAv6R5ZNK9npEjbYdT33gRo4o5oTqR34R+OmaSzDBWsAIPhuRcgyoteNi9gF0KzNYWVItPf2TLoXEg+7isNC7uJkgo1iQWOfRSP9NR11RtbZZ3OMG/VhL6jvx+J1m87+RCfJChAtEBQkSBX2PnSiihc/Twh3j0h7qdYQAoRVsRGmq7HU2QRbaxVGa1D6nIOqaIWRjyRZpHMQKWKpZM5feA+lzC4ZFultV8S6T0mzQGhQohi5I8iw+CsqBSxhFMuwyLgSwbghGb0AiIKkSDmGZVmJSiKihsiyOAUs70UkywooYP0bii9GdH4sfr1UNysd3fUyLLMQN+rsmo3grHl9VNJHbbwxoa47Vw5gupIqrZcjPh9R4Nye3nRDk199V+aetmvVtDRE8/+cbgAAgMIWGb3UA0MGLE9SCbWX670TDy1y98c3D27eppUjsZ6fql3jcd5rUe7+ZIlLNQny3Rd+E5Tct3WVhTM5RBCEdiEK0b6B+/ca2gYU393nFj/n1AygRQxPIUA043M42u85+z2SnssKrPl8Mx76NL3E6eXc3be7OD+H4WHbJkKI8AU8irbITQjZ+0hQcPEgId/Fn/pl9crKH02+5o2b9T/eMx7pKoskYgAAAABJRU5ErkJggg==` // gopherPNG creates an io.Reader by decoding the base64 encoded image data string in the gopher constant. -func gopherPNG() io.Reader { return base64.NewDecoder(base64.StdEncoding, strings.NewReader(gopher)) } +func gopherPng() io.Reader { return base64.NewDecoder(base64.StdEncoding, strings.NewReader(gopher)) } func TestBase64(t *testing.T) { t.Run("PNG", func(t *testing.T) { buf := new(bytes.Buffer) - _, bufErr := buf.ReadFrom(gopherPNG()) + _, bufErr := buf.ReadFrom(gopherPng()) assert.NoError(t, bufErr) assert.Equal(t, "data:image/png;base64,"+gopher, Base64(buf)) }) diff --git a/pkg/media/formats.go b/pkg/media/formats.go index c2b098f3d..90d7dd1d0 100644 --- a/pkg/media/formats.go +++ b/pkg/media/formats.go @@ -6,26 +6,26 @@ import "github.com/photoprism/photoprism/pkg/fs" var Formats = map[fs.Type]Type{ fs.ImageRaw: Raw, fs.ImageDNG: Raw, - fs.ImageJPEG: Image, - fs.ImageJPEGXL: Image, + fs.ImageJpeg: Image, + fs.ImageJpegXL: Image, fs.ImageThumb: Image, - fs.ImagePNG: Image, - fs.ImageGIF: Image, - fs.ImageTIFF: Image, - fs.ImagePSD: Image, - fs.ImageBMP: Image, + fs.ImagePng: Image, + fs.ImageGif: Image, + fs.ImageTiff: Image, + fs.ImagePsd: Image, + fs.ImageBmp: Image, fs.ImageMPO: Image, - fs.ImageAVIF: Image, - fs.ImageAVIFS: Image, - fs.ImageHEIF: Image, - fs.ImageHEIC: Image, - fs.ImageHEICS: Image, - fs.ImageWebP: Image, + fs.ImageAvif: Image, + fs.ImageAvifS: Image, + fs.ImageHeif: Image, + fs.ImageHeic: Image, + fs.ImageHeicS: Image, + fs.ImageWebp: Image, fs.SidecarXMP: Sidecar, - fs.SidecarXML: Sidecar, - fs.SidecarAAE: Sidecar, - fs.SidecarYAML: Sidecar, - fs.SidecarJSON: Sidecar, + fs.SidecarXml: Sidecar, + fs.SidecarAppleXml: Sidecar, + fs.SidecarYaml: Sidecar, + fs.SidecarJson: Sidecar, fs.SidecarText: Sidecar, fs.SidecarInfo: Sidecar, fs.SidecarMarkdown: Sidecar, @@ -33,28 +33,28 @@ var Formats = map[fs.Type]Type{ fs.VectorAI: Vector, fs.VectorPS: Vector, fs.VectorEPS: Vector, - fs.VideoWebM: Video, - fs.VideoAVC: Video, - fs.VideoHEVC: Video, - fs.VideoHEV1: Video, - fs.VideoVVC: Video, - fs.VideoEVC: Video, + fs.VideoWebm: Video, + fs.VideoAvc: Video, + fs.VideoHevc: Video, + fs.VideoHev1: Video, + fs.VideoVvc: Video, + fs.VideoEvc: Video, fs.VideoAVI: Video, - fs.VideoAV1: Video, - fs.VideoMPG: Video, - fs.VideoMJPG: Video, - fs.VideoMP2: Video, - fs.VideoMP4: Video, + fs.VideoAv1: Video, + fs.VideoMpeg: Video, + fs.VideoMjpeg: Video, + fs.VideoMp2: Video, + fs.VideoMp4: Video, fs.VideoM4V: Video, - fs.VideoMKV: Video, - fs.VideoMOV: Video, + fs.VideoMkv: Video, + fs.VideoMov: Video, fs.VideoMXF: Video, fs.Video3GP: Video, fs.Video3G2: Video, fs.VideoFlash: Video, - fs.VideoAVCHD: Video, + fs.VideoAvcHD: Video, fs.VideoBDAV: Video, - fs.VideoOGV: Video, + fs.VideoTheora: Video, fs.VideoASF: Video, fs.VideoWMV: Video, fs.VideoDV: Video, diff --git a/pkg/net/header/auth.go b/pkg/media/http/header/auth.go similarity index 100% rename from pkg/net/header/auth.go rename to pkg/media/http/header/auth.go diff --git a/pkg/net/header/auth_test.go b/pkg/media/http/header/auth_test.go similarity index 100% rename from pkg/net/header/auth_test.go rename to pkg/media/http/header/auth_test.go diff --git a/pkg/net/header/bots.go b/pkg/media/http/header/bots.go similarity index 100% rename from pkg/net/header/bots.go rename to pkg/media/http/header/bots.go diff --git a/pkg/net/header/bots_test.go b/pkg/media/http/header/bots_test.go similarity index 100% rename from pkg/net/header/bots_test.go rename to pkg/media/http/header/bots_test.go diff --git a/pkg/net/header/cache.go b/pkg/media/http/header/cache.go similarity index 100% rename from pkg/net/header/cache.go rename to pkg/media/http/header/cache.go diff --git a/pkg/net/header/cache_test.go b/pkg/media/http/header/cache_test.go similarity index 100% rename from pkg/net/header/cache_test.go rename to pkg/media/http/header/cache_test.go diff --git a/pkg/net/header/cdn.go b/pkg/media/http/header/cdn.go similarity index 100% rename from pkg/net/header/cdn.go rename to pkg/media/http/header/cdn.go diff --git a/pkg/net/header/cdn_test.go b/pkg/media/http/header/cdn_test.go similarity index 100% rename from pkg/net/header/cdn_test.go rename to pkg/media/http/header/cdn_test.go diff --git a/pkg/net/header/cidr.go b/pkg/media/http/header/cidr.go similarity index 100% rename from pkg/net/header/cidr.go rename to pkg/media/http/header/cidr.go diff --git a/pkg/media/http/header/content.go b/pkg/media/http/header/content.go new file mode 100644 index 000000000..03bbebe19 --- /dev/null +++ b/pkg/media/http/header/content.go @@ -0,0 +1,16 @@ +package header + +// Content-related request and response header names. +const ( + Accept = "Accept" + AcceptEncoding = "Accept-Encoding" + AcceptLanguage = "Accept-Language" + AcceptRanges = "Accept-Ranges" + ContentType = "Content-Type" + ContentDisposition = "Content-Disposition" + ContentEncoding = "Content-Encoding" + ContentRange = "Content-Range" + Location = "Location" + Origin = "Origin" + Vary = "Vary" +) diff --git a/pkg/net/header/content_test.go b/pkg/media/http/header/content_test.go similarity index 86% rename from pkg/net/header/content_test.go rename to pkg/media/http/header/content_test.go index ad0a94630..c5cbf8f76 100644 --- a/pkg/net/header/content_test.go +++ b/pkg/media/http/header/content_test.go @@ -7,7 +7,7 @@ import ( ) func TestContent(t *testing.T) { - t.Run("Header", func(t *testing.T) { + t.Run("Headers", func(t *testing.T) { assert.Equal(t, "Accept", Accept) assert.Equal(t, "Accept-Encoding", AcceptEncoding) assert.Equal(t, "Accept-Language", AcceptLanguage) @@ -20,16 +20,16 @@ func TestContent(t *testing.T) { assert.Equal(t, "Origin", Origin) assert.Equal(t, "Vary", Vary) }) - t.Run("Values", func(t *testing.T) { + t.Run("Types", func(t *testing.T) { assert.Equal(t, "application/x-www-form-urlencoded", ContentTypeForm) assert.Equal(t, "multipart/form-data", ContentTypeMultipart) assert.Equal(t, "application/json", ContentTypeJson) assert.Equal(t, "application/json; charset=utf-8", ContentTypeJsonUtf8) assert.Equal(t, "text/html; charset=utf-8", ContentTypeHtml) assert.Equal(t, "text/plain; charset=utf-8", ContentTypeText) - assert.Equal(t, "image/png", ContentTypePNG) - assert.Equal(t, "image/jpeg", ContentTypeJPEG) + assert.Equal(t, "image/png", ContentTypePng) + assert.Equal(t, "image/jpeg", ContentTypeJpeg) assert.Equal(t, "image/svg+xml", ContentTypeSVG) - assert.Equal(t, "video/mp4; codecs=\"avc1.640028\"", ContentTypeAVC) + assert.Equal(t, "video/mp4; codecs=\"avc1.640028\"", ContentTypeMp4Avc) }) } diff --git a/pkg/media/http/header/content_types.go b/pkg/media/http/header/content_types.go new file mode 100644 index 000000000..9d62033f2 --- /dev/null +++ b/pkg/media/http/header/content_types.go @@ -0,0 +1,47 @@ +package header + +/* + Standard content types for use in HTTP headers and the web interface. + + Browser support can be tested on one or more of the following sites: + - https://ott.dolby.com/codec_test/index.html + - https://dmnsgn.github.io/media-codecs/ + - https://cconcolato.github.io/media-mime-support/ + - https://thorium.rocks/misc/h265-tester.html +*/ + +// Standard ContentType identifiers for audio and video files. +const ( + ContentTypeMov = "video/quicktime" + ContentTypeMp4 = "video/mp4" + ContentTypeMp4Avc720 = ContentTypeMp4 + "; codecs=\"avc1.640020\"" // MPEG-4 AVC, High Profile Level 3.2 + ContentTypeMp4Avc = ContentTypeMp4 + "; codecs=\"avc1.640028\"" // MPEG-4 AVC, High Profile Level 4.0 + ContentTypeMp4Hevc = ContentTypeMp4 + "; codecs=\"hvc1.2.4.L120.B0\"" // HEVC Mp4 Main10 Profile, Main Tier, Level 4.0 + ContentTypeMp4Hev1 = ContentTypeMp4 + "; codecs=\"hev1.2.4.L120.B0\"" // HEVC Bitstream, not supported on macOS + ContentTypeMp4Vvc = ContentTypeMp4 + "; codecs=\"vvc1\"" // Versatile Video Coding (VVC), also known as H.266 + ContentTypeMp4Evc = ContentTypeMp4 + "; codecs=\"evc1\"" // MPEG-5 Essential Video Coding (EVC), also known as ISO/IEC 23094-1 + ContentTypeTheora = "video/ogg" + ContentTypeWebm = "video/webm" + ContentTypeWebmVp8 = "video/webm; codecs=\"vp8\"" + ContentTypeWebmVp9 = "video/webm; codecs=\"vp09.00.10.08\"" + ContentTypeWebmAv1 = "video/webm; codecs=\"av01.2.10M.10\"" +) + +// Standard ContentType identifiers for images and vector graphics. +const ( + ContentTypePng = "image/png" + ContentTypeJpeg = "image/jpeg" + ContentTypeSVG = "image/svg+xml" +) + +// Standard ContentType identifiers for markup and sidecar files. +const ( + ContentTypeBinary = "application/octet-stream" + ContentTypeForm = "application/x-www-form-urlencoded" + ContentTypeMultipart = "multipart/form-data" + ContentTypeJson = "application/json" + ContentTypeJsonUtf8 = "application/json; charset=utf-8" + ContentTypeHtml = "text/html; charset=utf-8" + ContentTypeText = "text/plain; charset=utf-8" + ContentTypePDF = "application/pdf" +) diff --git a/pkg/net/header/cors.go b/pkg/media/http/header/cors.go similarity index 100% rename from pkg/net/header/cors.go rename to pkg/media/http/header/cors.go diff --git a/pkg/net/header/cors_test.go b/pkg/media/http/header/cors_test.go similarity index 100% rename from pkg/net/header/cors_test.go rename to pkg/media/http/header/cors_test.go diff --git a/pkg/net/header/duration.go b/pkg/media/http/header/duration.go similarity index 100% rename from pkg/net/header/duration.go rename to pkg/media/http/header/duration.go diff --git a/pkg/net/header/duration_test.go b/pkg/media/http/header/duration_test.go similarity index 100% rename from pkg/net/header/duration_test.go rename to pkg/media/http/header/duration_test.go diff --git a/pkg/net/header/header.go b/pkg/media/http/header/header.go similarity index 100% rename from pkg/net/header/header.go rename to pkg/media/http/header/header.go diff --git a/pkg/net/header/id.go b/pkg/media/http/header/id.go similarity index 100% rename from pkg/net/header/id.go rename to pkg/media/http/header/id.go diff --git a/pkg/net/header/id_test.go b/pkg/media/http/header/id_test.go similarity index 100% rename from pkg/net/header/id_test.go rename to pkg/media/http/header/id_test.go diff --git a/pkg/net/header/ip.go b/pkg/media/http/header/ip.go similarity index 100% rename from pkg/net/header/ip.go rename to pkg/media/http/header/ip.go diff --git a/pkg/net/header/ip_test.go b/pkg/media/http/header/ip_test.go similarity index 100% rename from pkg/net/header/ip_test.go rename to pkg/media/http/header/ip_test.go diff --git a/pkg/net/header/proto.go b/pkg/media/http/header/proto.go similarity index 100% rename from pkg/net/header/proto.go rename to pkg/media/http/header/proto.go diff --git a/pkg/net/header/request.go b/pkg/media/http/header/request.go similarity index 100% rename from pkg/net/header/request.go rename to pkg/media/http/header/request.go diff --git a/pkg/net/header/request_test.go b/pkg/media/http/header/request_test.go similarity index 100% rename from pkg/net/header/request_test.go rename to pkg/media/http/header/request_test.go diff --git a/pkg/net/header/robots.go b/pkg/media/http/header/robots.go similarity index 100% rename from pkg/net/header/robots.go rename to pkg/media/http/header/robots.go diff --git a/pkg/net/header/robots_test.go b/pkg/media/http/header/robots_test.go similarity index 100% rename from pkg/net/header/robots_test.go rename to pkg/media/http/header/robots_test.go diff --git a/pkg/net/header/security.go b/pkg/media/http/header/security.go similarity index 100% rename from pkg/net/header/security.go rename to pkg/media/http/header/security.go diff --git a/pkg/net/header/values.go b/pkg/media/http/header/values.go similarity index 100% rename from pkg/net/header/values.go rename to pkg/media/http/header/values.go diff --git a/pkg/net/header/webdav.go b/pkg/media/http/header/webdav.go similarity index 100% rename from pkg/net/header/webdav.go rename to pkg/media/http/header/webdav.go diff --git a/pkg/net/scheme/http.go b/pkg/media/http/scheme/http.go similarity index 100% rename from pkg/net/scheme/http.go rename to pkg/media/http/scheme/http.go diff --git a/pkg/net/scheme/scheme.go b/pkg/media/http/scheme/scheme.go similarity index 100% rename from pkg/net/scheme/scheme.go rename to pkg/media/http/scheme/scheme.go diff --git a/pkg/net/scheme/scheme_test.go b/pkg/media/http/scheme/scheme_test.go similarity index 100% rename from pkg/net/scheme/scheme_test.go rename to pkg/media/http/scheme/scheme_test.go diff --git a/pkg/net/scheme/unix.go b/pkg/media/http/scheme/unix.go similarity index 100% rename from pkg/net/scheme/unix.go rename to pkg/media/http/scheme/unix.go diff --git a/pkg/media/preview.go b/pkg/media/preview.go index fe946ca86..596779c56 100644 --- a/pkg/media/preview.go +++ b/pkg/media/preview.go @@ -8,5 +8,5 @@ import ( "github.com/photoprism/photoprism/pkg/fs" ) -var PreviewFileTypes = []string{fs.ImageJPEG.String(), fs.ImagePNG.String()} +var PreviewFileTypes = []string{fs.ImageJpeg.String(), fs.ImagePng.String()} var PreviewExpr = gorm.Expr("'" + strings.Join(PreviewFileTypes, "','") + "'") diff --git a/pkg/media/report_test.go b/pkg/media/report_test.go index e501b69cf..1d1fbe9ca 100644 --- a/pkg/media/report_test.go +++ b/pkg/media/report_test.go @@ -3,8 +3,9 @@ package media import ( "testing" - "github.com/photoprism/photoprism/pkg/fs" "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/pkg/fs" ) func TestReport(t *testing.T) { diff --git a/pkg/media/video/brands.go b/pkg/media/video/brands.go index ae1cf7dd9..ceeaa8ee6 100644 --- a/pkg/media/video/brands.go +++ b/pkg/media/video/brands.go @@ -23,6 +23,7 @@ var ( ChunkISO8 = Chunk{'i', 's', 'o', '8'} ChunkISO9 = Chunk{'i', 's', 'o', '9'} ChunkAVC1 = Chunk{'a', 'v', 'c', '1'} + ChunkAVC3 = Chunk{'a', 'v', 'c', '3'} ChunkHEV1 = Chunk{'h', 'e', 'v', '1'} ChunkHVC1 = Chunk{'h', 'v', 'c', '1'} ChunkAV01 = Chunk{'a', 'v', '0', '1'} @@ -48,8 +49,10 @@ var CompatibleBrands = Chunks{ ChunkISO8, ChunkISO9, ChunkAVC1, + ChunkAVC3, ChunkHEV1, ChunkHVC1, + ChunkHEIC, ChunkAV01, ChunkAV1C, ChunkMMP4, diff --git a/pkg/media/video/chunk.go b/pkg/media/video/chunk.go index 271da2cfd..0ec5993e4 100644 --- a/pkg/media/video/chunk.go +++ b/pkg/media/video/chunk.go @@ -8,8 +8,9 @@ import ( "io" "os" - "github.com/photoprism/photoprism/pkg/fs" "github.com/sunfish-shogi/bufseekio" + + "github.com/photoprism/photoprism/pkg/fs" ) // Chunk represents a fixed length file chunk. diff --git a/pkg/media/video/codec_profile.go b/pkg/media/video/codec_profile.go new file mode 100644 index 000000000..fe6e76842 --- /dev/null +++ b/pkg/media/video/codec_profile.go @@ -0,0 +1,14 @@ +package video + +type CodecProfile struct { + Codec Codec + Profile string + Level int + SubLevel int + Bitrate int + Resolution int + Framerate int + ID string +} + +type CodecProfiles []CodecProfile diff --git a/pkg/media/video/codecs.go b/pkg/media/video/codecs.go index c8d38e59a..df861cdb1 100644 --- a/pkg/media/video/codecs.go +++ b/pkg/media/video/codecs.go @@ -11,16 +11,17 @@ type Codec = string // - https://thorium.rocks/misc/h265-tester.html const ( CodecUnknown Codec = "" - CodecAVC Codec = "avc1" // Advanced Video Coding (AVC), also known as H.264 - CodecHEVC Codec = "hvc1" // High Efficiency Video Coding (HEVC), also known as H.265 - CodecHEV1 Codec = "hev1" // HEVC bitstream with the parameter sets stored in the samples, not supported on macOS - CodecVVC Codec = "vvc1" // Versatile Video Coding (VVC), also known as H.266 - CodecEVC Codec = "evc1" // MPEG-5 Essential Video Coding (EVC), also known as ISO/IEC 23094-1 - CodecAV1 Codec = "av01" // AOMedia Video 1 (AV1) - CodecVP8 Codec = "vp8" // Google VP8 - CodecVP9 Codec = "vp09" // Google VP9 - CodecOGV Codec = "ogv" // Ogg Vorbis Video - CodecWebM Codec = "webm" // Google WebM + CodecAvc Codec = "avc1" // Advanced Video Coding (AVC), also known as H.264 + CodecAvc3 Codec = "avc3" // AVC bitstream with the parameter sets stored in the samples, not supported on macOS + CodecHevc Codec = "hvc1" // High Efficiency Video Coding (HEVC), also known as H.265 + CodecHev1 Codec = "hev1" // HEVC bitstream with the parameter sets stored in the samples, not supported on macOS + CodecVvc Codec = "vvc1" // Versatile Video Coding (VVC), also known as H.266 + CodecEvc Codec = "evc1" // MPEG-5 Essential Video Coding (EVC), also known as ISO/IEC 23094-1 + CodecAv1 Codec = "av01" // AOMedia Video 1 (AV1) + CodecVp8 Codec = "vp8" // Google VP8 + CodecVp9 Codec = "vp09" // Google VP9 + CodecTheora Codec = "ogv" // Ogg Vorbis Video + CodecWebm Codec = "webm" // Google WebM ) // Codecs maps supported string identifiers to standard Codec types. @@ -28,56 +29,56 @@ var Codecs = StandardCodecs{ "": CodecUnknown, "a_opus": CodecUnknown, "a_vorbis": CodecUnknown, - "avc": CodecAVC, - "avc1": CodecAVC, - "avc3": CodecAVC, - "v_avc": CodecAVC, - "v_avc1": CodecAVC, - "iso/avc": CodecAVC, - "v_mpeg4/avc": CodecAVC, - "v_mpeg4/iso/avc": CodecAVC, - "hevc": CodecHEVC, - "hevC": CodecHEVC, - "hvc": CodecHEVC, - "hvc1": CodecHEVC, - "v_hvc": CodecHEVC, - "v_hvc1": CodecHEVC, - "hvcC": CodecHEVC, - "hvcc": CodecHEVC, - "hev": CodecHEV1, - "hev1": CodecHEV1, - "evc": CodecEVC, - "evc1": CodecEVC, - "evcC": CodecEVC, - "evcc": CodecEVC, - "v_evc": CodecEVC, - "v_evc1": CodecEVC, - "vvc": CodecVVC, - "vvcC": CodecVVC, - "vvcc": CodecVVC, - "vvc1": CodecVVC, - "v_vvc": CodecVVC, - "v_vvc1": CodecVVC, - "av1f": CodecAV1, - "av1m": CodecAV1, - "av1M": CodecAV1, - "av1s": CodecAV1, - "av1c": CodecAV1, - "av1C": CodecAV1, - "av1": CodecAV1, - "av01": CodecAV1, - "v_av1": CodecAV1, - "v_av01": CodecAV1, - "vp8": CodecVP8, - "vp08": CodecVP8, - "vp80": CodecVP8, - "v_vp8": CodecVP8, - "vp9": CodecVP9, - "vp09": CodecVP9, - "vp90": CodecVP9, - "v_vp9": CodecVP9, - "ogv": CodecOGV, - "webm": CodecWebM, + "avc": CodecAvc, + CodecAvc: CodecAvc, + CodecAvc3: CodecAvc, + "v_avc": CodecAvc, + "v_avc1": CodecAvc, + "iso/avc": CodecAvc, + "v_mpeg4/avc": CodecAvc, + "v_mpeg4/iso/avc": CodecAvc, + "hevc": CodecHevc, + "hevC": CodecHevc, + "hvc": CodecHevc, + CodecHevc: CodecHevc, + "v_hvc": CodecHevc, + "v_hvc1": CodecHevc, + "hvcC": CodecHevc, + "hvcc": CodecHevc, + "hev": CodecHev1, + CodecHev1: CodecHev1, + "evc": CodecEvc, + CodecEvc: CodecEvc, + "evcC": CodecEvc, + "evcc": CodecEvc, + "v_evc": CodecEvc, + "v_evc1": CodecEvc, + "vvc": CodecVvc, + "vvcC": CodecVvc, + "vvcc": CodecVvc, + CodecVvc: CodecVvc, + "v_vvc": CodecVvc, + "v_vvc1": CodecVvc, + "av1f": CodecAv1, + "av1m": CodecAv1, + "av1M": CodecAv1, + "av1s": CodecAv1, + "av1c": CodecAv1, + "av1C": CodecAv1, + "av1": CodecAv1, + CodecAv1: CodecAv1, + "v_av1": CodecAv1, + "v_av01": CodecAv1, + CodecVp8: CodecVp8, + "vp08": CodecVp8, + "vp80": CodecVp8, + "v_vp8": CodecVp8, + "vp9": CodecVp9, + CodecVp9: CodecVp9, + "vp90": CodecVp9, + "v_vp9": CodecVp9, + CodecTheora: CodecTheora, + CodecWebm: CodecWebm, } // StandardCodecs maps strings to codec types. diff --git a/pkg/media/video/codecs_test.go b/pkg/media/video/codecs_test.go index 241437560..6922ee15c 100644 --- a/pkg/media/video/codecs_test.go +++ b/pkg/media/video/codecs_test.go @@ -7,19 +7,19 @@ func TestCodecs(t *testing.T) { t.Fatal("default codec should be CodecUnknown") } - if val := Codecs["avc"]; val != CodecAVC { + if val := Codecs["avc"]; val != CodecAvc { t.Fatal("codec should be CodecAVC") } - if val := Codecs["av1"]; val != CodecAV1 { + if val := Codecs["av1"]; val != CodecAv1 { t.Fatal("codec should be CodecAV1") } - if val := Codecs["evc"]; val != CodecEVC { + if val := Codecs["evc"]; val != CodecEvc { t.Fatal("codec should be CodecEVC") } - if val := Codecs["vvcC"]; val != CodecVVC { + if val := Codecs["vvcC"]; val != CodecVvc { t.Fatal("codec should be CodecVVC") } } diff --git a/pkg/media/video/content_type.go b/pkg/media/video/content_type.go index 755b30b42..058855103 100644 --- a/pkg/media/video/content_type.go +++ b/pkg/media/video/content_type.go @@ -5,7 +5,8 @@ import ( "strings" "github.com/photoprism/photoprism/pkg/clean" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/fs" + "github.com/photoprism/photoprism/pkg/media/http/header" ) // ContentType returns a normalized video content type strings based on the video file type and codec. @@ -25,27 +26,27 @@ func ContentType(mediaType, fileType, videoCodec string) string { switch c { case "mov", "mp4": - mediaType = header.ContentTypeMP4 - case CodecAVC, "avc": - mediaType = header.ContentTypeAVC // Advanced Video Coding (AVC), also known as H.264 - case CodecHEVC, "hvc", "hevc": - mediaType = header.ContentTypeHEVC // High Efficiency Video Coding (HEVC), also known as H.265 - case CodecHEV1, "hev": - mediaType = header.ContentTypeHEV1 // High Efficiency Video Coding (HEVC) Bitstream - case CodecVVC, "vvc": - mediaType = header.ContentTypeVVC // Versatile Video Coding (VVC), also known as H.266 - case CodecEVC, "evc": - mediaType = header.ContentTypeEVC // MPEG-5 Essential Video Coding (EVC), also known as ISO/IEC 23094-1 - case CodecVP8, "vp08": - mediaType = header.ContentTypeVP8 - case CodecVP9, "vp9": - mediaType = header.ContentTypeVP9 - case CodecAV1, "av1": - mediaType = header.ContentTypeAV1 - case CodecOGV, "ogg": - mediaType = header.ContentTypeOGV - case CodecWebM: - mediaType = header.ContentTypeWebM + mediaType = header.ContentTypeMp4 + case CodecAvc, "avc": + mediaType = header.ContentTypeMp4Avc // Advanced Video Coding (AVC), also known as H.264 + case CodecHevc, "hvc", "hevc": + mediaType = header.ContentTypeMp4Hevc // High Efficiency Video Coding (HEVC), also known as H.265 + case CodecHev1, "hev": + mediaType = header.ContentTypeMp4Hev1 // High Efficiency Video Coding (HEVC) Bitstream + case CodecVvc, "vvc": + mediaType = header.ContentTypeMp4Vvc // Versatile Video Coding (VVC), also known as H.266 + case CodecEvc, "evc": + mediaType = header.ContentTypeMp4Evc // MPEG-5 Essential Video Coding (EVC), also known as ISO/IEC 23094-1 + case CodecVp8, "vp08": + mediaType = header.ContentTypeWebmVp8 + case CodecVp9, "vp9": + mediaType = header.ContentTypeWebmVp9 + case CodecAv1, "av1": + mediaType = header.ContentTypeWebmAv1 + case CodecTheora, "ogg": + mediaType = header.ContentTypeTheora + case string(fs.VideoWebm): + mediaType = header.ContentTypeWebm } } diff --git a/pkg/media/video/content_type_test.go b/pkg/media/video/content_type_test.go index b0a834f5e..c15f1a93f 100644 --- a/pkg/media/video/content_type_test.go +++ b/pkg/media/video/content_type_test.go @@ -6,23 +6,23 @@ import ( "github.com/stretchr/testify/assert" "github.com/photoprism/photoprism/pkg/fs" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) func TestContentType(t *testing.T) { t.Run("QuickTime", func(t *testing.T) { - assert.Equal(t, fs.MimeTypeMP4, ContentType(fs.MimeTypeMOV, "", "")) + assert.Equal(t, fs.MimeTypeMp4, ContentType(fs.MimeTypeMov, "", "")) }) t.Run("QuickTime_HVC", func(t *testing.T) { - assert.Equal(t, header.ContentTypeHEVC, ContentType(fs.MimeTypeMOV, "mov", CodecHEVC)) + assert.Equal(t, header.ContentTypeMp4Hevc, ContentType(fs.MimeTypeMov, "mov", CodecHevc)) }) - t.Run("MP4", func(t *testing.T) { - assert.Equal(t, fs.MimeTypeMP4, ContentType(fs.MimeTypeMP4, "", "")) + t.Run("Mp4", func(t *testing.T) { + assert.Equal(t, fs.MimeTypeMp4, ContentType(fs.MimeTypeMp4, "", "")) }) - t.Run("MP4_AVC", func(t *testing.T) { - assert.Equal(t, header.ContentTypeAVC, ContentType(fs.MimeTypeMP4, "", CodecAVC)) + t.Run("Mp4_AVC", func(t *testing.T) { + assert.Equal(t, header.ContentTypeMp4Avc, ContentType(fs.MimeTypeMp4, "", CodecAvc)) }) - t.Run("MP4_HVC", func(t *testing.T) { - assert.Equal(t, header.ContentTypeHEVC, ContentType(fs.MimeTypeMP4, "", CodecHEVC)) + t.Run("Mp4_HVC", func(t *testing.T) { + assert.Equal(t, header.ContentTypeMp4Hevc, ContentType(fs.MimeTypeMp4, "", CodecHevc)) }) } diff --git a/pkg/media/video/info.go b/pkg/media/video/info.go index 8c1db61ef..61536d26b 100644 --- a/pkg/media/video/info.go +++ b/pkg/media/video/info.go @@ -5,7 +5,7 @@ import ( "github.com/photoprism/photoprism/pkg/fs" "github.com/photoprism/photoprism/pkg/media" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) // Info represents video file information. @@ -76,22 +76,22 @@ func (info Info) VideoContentType() string { return ContentType(info.VideoMimeType, info.FileType.String(), info.VideoCodec) } -// VideoFileExt returns the appropriate video file extension based on the mime type and defaults to fs.ExtMP4 otherwise. +// VideoFileExt returns the appropriate video file extension based on the mime type and defaults to fs.ExtMp4 otherwise. func (info Info) VideoFileExt() string { switch info.VideoMimeType { - case fs.MimeTypeMOV: - return fs.ExtMOV + case fs.MimeTypeMov: + return fs.ExtMov default: - return fs.ExtMP4 + return fs.ExtMp4 } } -// VideoFileType returns the video type based on the mime type and defaults to fs.VideoMP4 otherwise. +// VideoFileType returns the video type based on the mime type and defaults to fs.VideoMp4 otherwise. func (info Info) VideoFileType() fs.Type { switch info.VideoMimeType { - case fs.MimeTypeMOV: - return fs.VideoMOV + case fs.MimeTypeMov: + return fs.VideoMov default: - return fs.VideoMP4 + return fs.VideoMp4 } } diff --git a/pkg/media/video/info_test.go b/pkg/media/video/info_test.go index fcbc9770a..e66c6a9e0 100644 --- a/pkg/media/video/info_test.go +++ b/pkg/media/video/info_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/photoprism/photoprism/pkg/fs" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) func TestInfo(t *testing.T) { @@ -26,20 +26,20 @@ func TestInfo(t *testing.T) { }) t.Run("VideoContentType", func(t *testing.T) { info := NewInfo() - info.VideoMimeType = fs.MimeTypeMP4 - info.VideoCodec = CodecAVC - assert.Equal(t, header.ContentTypeAVC, info.VideoContentType()) + info.VideoMimeType = fs.MimeTypeMp4 + info.VideoCodec = CodecAvc + assert.Equal(t, header.ContentTypeMp4Avc, info.VideoContentType()) }) t.Run("VideoFileExt", func(t *testing.T) { info := NewInfo() - info.VideoMimeType = fs.MimeTypeMP4 - info.VideoCodec = CodecAVC - assert.Equal(t, fs.ExtMP4, info.VideoFileExt()) + info.VideoMimeType = fs.MimeTypeMp4 + info.VideoCodec = CodecAvc + assert.Equal(t, fs.ExtMp4, info.VideoFileExt()) }) t.Run("VideoFileType", func(t *testing.T) { info := NewInfo() - info.VideoMimeType = fs.MimeTypeMP4 - info.VideoCodec = CodecAVC - assert.Equal(t, fs.VideoMP4, info.VideoFileType()) + info.VideoMimeType = fs.MimeTypeMp4 + info.VideoCodec = CodecAvc + assert.Equal(t, fs.VideoMp4, info.VideoFileType()) }) } diff --git a/pkg/media/video/probe.go b/pkg/media/video/probe.go index 502bd7814..31450a785 100644 --- a/pkg/media/video/probe.go +++ b/pkg/media/video/probe.go @@ -85,7 +85,7 @@ func Probe(file io.ReadSeeker) (info Info, err error) { var video *mp4.ProbeInfo - // Detect MP4 video metadata. + // Detect Mp4 video metadata. if video, err = mp4.Probe(videoFile); err != nil { return info, err } @@ -93,8 +93,8 @@ func Probe(file io.ReadSeeker) (info Info, err error) { // Check compatibility. if CompatibleBrands.ContainsAny(video.CompatibleBrands) { info.Compatible = true - info.VideoType = MP4 - info.VideoMimeType = fs.MimeTypeMP4 + info.VideoType = Mp4 + info.VideoMimeType = fs.MimeTypeMp4 info.FPS = 30.0 // TODO: Detect actual frames per second! if info.VideoOffset > 0 { @@ -102,17 +102,17 @@ func Probe(file io.ReadSeeker) (info Info, err error) { info.ThumbOffset = 0 } else { info.MediaType = media.Video - info.FileType = fs.VideoMP4 + info.FileType = fs.VideoMp4 } } // Check major brand. switch video.MajorBrand { case ChunkQT.Get(): - info.VideoType = MOV - info.VideoMimeType = fs.MimeTypeMOV + info.VideoType = Mov + info.VideoMimeType = fs.MimeTypeMov if info.MediaType == media.Video { - info.FileType = fs.VideoMOV + info.FileType = fs.VideoMov } } @@ -128,12 +128,12 @@ func Probe(file io.ReadSeeker) (info Info, err error) { switch track.Codec { case mp4.CodecAVC1: - info.VideoCodec = CodecAVC + info.VideoCodec = CodecAvc } if avc := track.AVC; avc != nil { if info.VideoMimeType == "" { - info.VideoMimeType = fs.MimeTypeMP4 + info.VideoMimeType = fs.MimeTypeMp4 } if w := int(avc.Width); w > info.VideoWidth { info.VideoWidth = w @@ -148,9 +148,9 @@ func Probe(file io.ReadSeeker) (info Info, err error) { // see https://stackoverflow.com/questions/63468587/what-hevc-codec-tag-to-use-with-fmp4-hvc1-or-hev1. if info.VideoCodec == "" { if fileOffset, fileErr := ChunkHVC1.DataOffset(file, -1); fileOffset > 0 && fileErr == nil { - info.VideoCodec = CodecHEVC - } else if fileOffset, fileErr = ChunkHEV1.DataOffset(file, 5*fs.MB); fileOffset > 0 && fileErr == nil { - info.VideoCodec = CodecHEV1 + info.VideoCodec = CodecHevc + } else if fileOffset, fileErr = ChunkHEV1.DataOffset(file, 5*fs.MegaByte); fileOffset > 0 && fileErr == nil { + info.VideoCodec = CodecHev1 } } diff --git a/pkg/media/video/probe_test.go b/pkg/media/video/probe_test.go index ada5a43aa..fd3f0c8fa 100644 --- a/pkg/media/video/probe_test.go +++ b/pkg/media/video/probe_test.go @@ -9,7 +9,7 @@ import ( "github.com/photoprism/photoprism/pkg/fs" "github.com/photoprism/photoprism/pkg/media" - "github.com/photoprism/photoprism/pkg/net/header" + "github.com/photoprism/photoprism/pkg/media/http/header" ) func TestProbeFile(t *testing.T) { @@ -33,14 +33,14 @@ func TestProbeFile(t *testing.T) { assert.Equal(t, fileName, info.FileName) assert.Equal(t, int64(55061), info.FileSize) - assert.Equal(t, fs.VideoMP4, info.FileType) - assert.Equal(t, MP4, info.VideoType) + assert.Equal(t, fs.VideoMp4, info.FileType) + assert.Equal(t, Mp4, info.VideoType) assert.Equal(t, int64(0), info.VideoOffset) assert.Equal(t, int64(-1), info.ThumbOffset) assert.Equal(t, media.Video, info.MediaType) - assert.Equal(t, CodecAVC, info.VideoCodec) - assert.Equal(t, fs.MimeTypeMP4, info.VideoMimeType) - assert.Equal(t, header.ContentTypeAVC, info.VideoContentType()) + assert.Equal(t, CodecAvc, info.VideoCodec) + assert.Equal(t, fs.MimeTypeMp4, info.VideoMimeType) + assert.Equal(t, header.ContentTypeMp4Avc, info.VideoContentType()) assert.Equal(t, "810.0081ms", info.Duration.String()) assert.InEpsilon(t, 0.81, info.Duration.Seconds(), 0.01) assert.Equal(t, 1, info.Tracks) @@ -60,14 +60,14 @@ func TestProbeFile(t *testing.T) { assert.Equal(t, fileName, info.FileName) assert.Equal(t, int64(217963), info.FileSize) - assert.Equal(t, fs.VideoMP4, info.FileType) - assert.Equal(t, MP4, info.VideoType) + assert.Equal(t, fs.VideoMp4, info.FileType) + assert.Equal(t, Mp4, info.VideoType) assert.Equal(t, int64(0), info.VideoOffset) assert.Equal(t, int64(-1), info.ThumbOffset) assert.Equal(t, media.Video, info.MediaType) - assert.Equal(t, CodecAVC, info.VideoCodec) - assert.Equal(t, fs.MimeTypeMP4, info.VideoMimeType) - assert.Equal(t, header.ContentTypeAVC, info.VideoContentType()) + assert.Equal(t, CodecAvc, info.VideoCodec) + assert.Equal(t, fs.MimeTypeMp4, info.VideoMimeType) + assert.Equal(t, header.ContentTypeMp4Avc, info.VideoContentType()) assert.Equal(t, "1.066666666s", info.Duration.String()) assert.InEpsilon(t, 1.066, info.Duration.Seconds(), 0.01) assert.Equal(t, 2, info.Tracks) @@ -87,14 +87,14 @@ func TestProbeFile(t *testing.T) { assert.Equal(t, fileName, info.FileName) assert.Equal(t, int64(710953), info.FileSize) - assert.Equal(t, fs.VideoMOV, info.FileType) - assert.Equal(t, MOV, info.VideoType) + assert.Equal(t, fs.VideoMov, info.FileType) + assert.Equal(t, Mov, info.VideoType) assert.Equal(t, int64(0), info.VideoOffset) assert.Equal(t, int64(-1), info.ThumbOffset) assert.Equal(t, media.Video, info.MediaType) - assert.Equal(t, CodecHEVC, info.VideoCodec) - assert.Equal(t, fs.MimeTypeMOV, info.VideoMimeType) - assert.Equal(t, header.ContentTypeHEVC, info.VideoContentType()) + assert.Equal(t, CodecHevc, info.VideoCodec) + assert.Equal(t, fs.MimeTypeMov, info.VideoMimeType) + assert.Equal(t, header.ContentTypeMp4Hevc, info.VideoContentType()) assert.Equal(t, "1.166666666s", info.Duration.String()) assert.InEpsilon(t, 1.166, info.Duration.Seconds(), 0.01) assert.Equal(t, 5, info.Tracks) @@ -114,7 +114,7 @@ func TestProbeFile(t *testing.T) { assert.Equal(t, fileName, info.FileName) assert.Equal(t, int64(475190), info.FileSize) - assert.Equal(t, fs.VideoMOV, info.FileType) + assert.Equal(t, fs.VideoMov, info.FileType) assert.Equal(t, Unknown, info.VideoType) assert.Equal(t, int64(-1), info.VideoOffset) assert.Equal(t, int64(-1), info.ThumbOffset) @@ -140,14 +140,14 @@ func TestProbeFile(t *testing.T) { assert.Equal(t, fileName, info.FileName) assert.Equal(t, int64(31487), info.FileSize) - assert.Equal(t, fs.ImageJPEG, info.FileType) - assert.Equal(t, MP4, info.VideoType) + assert.Equal(t, fs.ImageJpeg, info.FileType) + assert.Equal(t, Mp4, info.VideoType) assert.Equal(t, int64(23209), info.VideoOffset) assert.Equal(t, int64(0), info.ThumbOffset) assert.Equal(t, media.Live, info.MediaType) - assert.Equal(t, CodecAVC, info.VideoCodec) - assert.Equal(t, fs.MimeTypeMP4, info.VideoMimeType) - assert.Equal(t, header.ContentTypeAVC, info.VideoContentType()) + assert.Equal(t, CodecAvc, info.VideoCodec) + assert.Equal(t, fs.MimeTypeMp4, info.VideoMimeType) + assert.Equal(t, header.ContentTypeMp4Avc, info.VideoContentType()) assert.Equal(t, "1.024s", info.Duration.String()) assert.InEpsilon(t, 1.024, info.Duration.Seconds(), 0.01) assert.Equal(t, 2, info.Tracks) @@ -173,14 +173,14 @@ func TestProbe(t *testing.T) { assert.Equal(t, "", info.FileName) assert.Equal(t, int64(-1), info.FileSize) - assert.Equal(t, fs.VideoMP4, info.FileType) - assert.Equal(t, MP4, info.VideoType) + assert.Equal(t, fs.VideoMp4, info.FileType) + assert.Equal(t, Mp4, info.VideoType) assert.Equal(t, int64(0), info.VideoOffset) assert.Equal(t, int64(-1), info.ThumbOffset) assert.Equal(t, media.Video, info.MediaType) - assert.Equal(t, CodecAVC, info.VideoCodec) - assert.Equal(t, fs.MimeTypeMP4, info.VideoMimeType) - assert.Equal(t, header.ContentTypeAVC, info.VideoContentType()) + assert.Equal(t, CodecAvc, info.VideoCodec) + assert.Equal(t, fs.MimeTypeMp4, info.VideoMimeType) + assert.Equal(t, header.ContentTypeMp4Avc, info.VideoContentType()) assert.Equal(t, "810.0081ms", info.Duration.String()) assert.InEpsilon(t, 0.81, info.Duration.Seconds(), 0.01) assert.Equal(t, 1, info.Tracks) @@ -203,14 +203,14 @@ func TestProbe(t *testing.T) { assert.Equal(t, "", info.FileName) assert.Equal(t, int64(-1), info.FileSize) - assert.Equal(t, fs.VideoMP4, info.FileType) - assert.Equal(t, MP4, info.VideoType) + assert.Equal(t, fs.VideoMp4, info.FileType) + assert.Equal(t, Mp4, info.VideoType) assert.Equal(t, int64(0), info.VideoOffset) assert.Equal(t, int64(-1), info.ThumbOffset) assert.Equal(t, media.Video, info.MediaType) - assert.Equal(t, CodecAVC, info.VideoCodec) - assert.Equal(t, fs.MimeTypeMP4, info.VideoMimeType) - assert.Equal(t, header.ContentTypeAVC, info.VideoContentType()) + assert.Equal(t, CodecAvc, info.VideoCodec) + assert.Equal(t, fs.MimeTypeMp4, info.VideoMimeType) + assert.Equal(t, header.ContentTypeMp4Avc, info.VideoContentType()) assert.Equal(t, "1.024s", info.Duration.String()) assert.InEpsilon(t, 1.024, info.Duration.Seconds(), 0.01) assert.Equal(t, 2, info.Tracks) @@ -234,13 +234,13 @@ func TestProbe(t *testing.T) { assert.Equal(t, "", info.FileName) assert.Equal(t, int64(-1), info.FileSize) assert.Equal(t, fs.TypeUnknown, info.FileType) - assert.Equal(t, MP4, info.VideoType) + assert.Equal(t, Mp4, info.VideoType) assert.Equal(t, int64(23209), info.VideoOffset) assert.Equal(t, int64(0), info.ThumbOffset) assert.Equal(t, media.Live, info.MediaType) - assert.Equal(t, CodecAVC, info.VideoCodec) - assert.Equal(t, fs.MimeTypeMP4, info.VideoMimeType) - assert.Equal(t, header.ContentTypeAVC, info.VideoContentType()) + assert.Equal(t, CodecAvc, info.VideoCodec) + assert.Equal(t, fs.MimeTypeMp4, info.VideoMimeType) + assert.Equal(t, header.ContentTypeMp4Avc, info.VideoContentType()) assert.Equal(t, "1.024s", info.Duration.String()) assert.InEpsilon(t, 1.024, info.Duration.Seconds(), 0.01) assert.Equal(t, 2, info.Tracks) diff --git a/pkg/media/video/profiles.go b/pkg/media/video/profiles.go new file mode 100644 index 000000000..9b772950b --- /dev/null +++ b/pkg/media/video/profiles.go @@ -0,0 +1,26 @@ +package video + +// Profile represents a video codec profile name, +// see https://en.wikipedia.org/wiki/Advanced_Video_Coding#Profiles. +type Profile = string + +const ( + ProfileBaseline Profile = "BaseLine" + ProfileMain Profile = "Main" + ProfileHigh Profile = "High" +) + +// Profiles contains common video codec profiles together with their ContentType ID, +// maximum bitrate, resolution, and frame rate (if known). +var Profiles = CodecProfiles{ + {Codec: CodecAvc, Profile: ProfileBaseline, Level: 30, Bitrate: 10000, ID: "avc1.66.30"}, // iOS friendly + {Codec: CodecAvc, Profile: ProfileBaseline, Level: 30, Bitrate: 10000, ID: "avc1.42001e"}, + {Codec: CodecAvc, Profile: ProfileBaseline, Level: 31, Bitrate: 14000, ID: "avc1.42001f"}, + {Codec: CodecAvc, Profile: ProfileMain, Level: 30, Bitrate: 10000, ID: "avc1.77.30"}, // iOS friendly + {Codec: CodecAvc, Profile: ProfileMain, Level: 30, Bitrate: 10000, ID: "avc1.4d001e"}, + {Codec: CodecAvc, Profile: ProfileMain, Level: 31, Bitrate: 14000, ID: "avc1.4d001f"}, + {Codec: CodecAvc, Profile: ProfileMain, Level: 40, Bitrate: 20000, ID: "avc1.4d0028"}, + {Codec: CodecAvc, Profile: ProfileHigh, Level: 31, Bitrate: 17500, ID: "avc1.64001f"}, + {Codec: CodecAvc, Profile: ProfileHigh, Level: 40, Bitrate: 25000, ID: "avc1.640028"}, + {Codec: CodecAvc, Profile: ProfileHigh, Level: 41, Bitrate: 62500, ID: "avc1.640029"}, +} diff --git a/pkg/media/video/standards.go b/pkg/media/video/standards.go index 19c697044..c2dab6b27 100644 --- a/pkg/media/video/standards.go +++ b/pkg/media/video/standards.go @@ -2,37 +2,37 @@ package video // Types maps identifiers to standards. var Types = Standards{ - "": AVC, - "mp4": MP4, - "mpeg4": MP4, - "avc": AVC, - "avc1": AVC, - "avc3": AVC, // H.264 with the parameter sets stored in the samples instead of the sample descriptions. - "hevc": HEVC, - "hevC": HEVC, - "hvc": HEVC, - "hvc1": HEVC, - "v_hvc": HEVC, - "v_hvc1": HEVC, - "hev": HEV1, // H.265 with the parameter sets stored in the samples instead of the sample descriptions. - "hev1": HEV1, - "evc": EVC, - "evc1": EVC, - "evcC": EVC, - "vvc": VVC, - "vvc1": VVC, - "vvcC": VVC, - "vp8": VP8, - "vp08": VP8, - "vp80": VP8, - "vp9": VP9, - "vp09": VP9, - "vp90": VP9, - "av1": AV1, - "av01": AV1, - "ogg": OGV, - "ogv": OGV, - "webm": WebM, + "": Avc, + "mp4": Mp4, + "mpeg4": Mp4, + "avc": Avc, + "avc1": Avc, + "avc3": Avc, // H.264 with the parameter sets stored in the samples instead of the sample descriptions. + "hevc": Hevc, + "hevC": Hevc, + "hvc": Hevc, + "hvc1": Hevc, + "v_hvc": Hevc, + "v_hvc1": Hevc, + "hev": Hev1, // H.265 with the parameter sets stored in the samples instead of the sample descriptions. + "hev1": Hev1, + "evc": Evc, + "evc1": Evc, + "evcC": Evc, + "vvc": Vvc, + "vvc1": Vvc, + "vvcC": Vvc, + "vp8": Vp8, + "vp08": Vp8, + "vp80": Vp8, + "vp9": Vp9, + "vp09": Vp9, + "vp90": Vp9, + "av1": Av1, + "av01": Av1, + "ogg": Theora, + "ogv": Theora, + "webm": Webm, } // Standards maps names to standardized formats. diff --git a/pkg/media/video/type.go b/pkg/media/video/type.go index 5cfebafbe..99500e2de 100644 --- a/pkg/media/video/type.go +++ b/pkg/media/video/type.go @@ -8,6 +8,7 @@ import ( type Type struct { Codec Codec FileType fs.Type + ContentType string WidthLimit int HeightLimit int Public bool diff --git a/pkg/media/video/types.go b/pkg/media/video/types.go index 2a382e891..f26759e42 100644 --- a/pkg/media/video/types.go +++ b/pkg/media/video/types.go @@ -2,118 +2,120 @@ package video import ( "github.com/photoprism/photoprism/pkg/fs" + "github.com/photoprism/photoprism/pkg/media/http/header" ) -// Unknown represents an unknown video file type. +// Unknown represents an unknown and/or unsupported video format. var Unknown = Type{ Codec: CodecUnknown, FileType: fs.TypeUnknown, } -// MP4 is a Multimedia Container (MPEG-4 Part 14). -var MP4 = Type{ - Codec: CodecAVC, - FileType: fs.VideoMP4, +// Mp4 specifies the MPEG-4 Part 14 multimedia container format. +var Mp4 = Type{ + Codec: CodecAvc, + FileType: fs.VideoMp4, + ContentType: header.ContentTypeMp4Avc, WidthLimit: 8192, HeightLimit: 4320, Public: true, } -// MOV are QuickTime videos based on the MPEG-4 format, -var MOV = Type{ - Codec: CodecAVC, - FileType: fs.VideoMOV, +// Mov specifies the Apple QuickTime (QT) container format. +var Mov = Type{ + Codec: CodecAvc, + FileType: fs.VideoMov, WidthLimit: 8192, HeightLimit: 4320, Public: true, } -// AVC aka Advanced Video Coding (H.264). -var AVC = Type{ - Codec: CodecAVC, - FileType: fs.VideoAVC, +// Avc specifies the MPEG-4 Advanced Video Coding (H.264) format. +var Avc = Type{ + Codec: CodecAvc, + FileType: fs.VideoAvc, WidthLimit: 8192, HeightLimit: 4320, Public: true, } -// HEVC aka High Efficiency Video Coding (H.265), with the parameter sets stored in the sample descriptions. -var HEVC = Type{ - Codec: CodecHEVC, - FileType: fs.VideoHEVC, +// Hevc specifies the generally compatible High Efficiency Video Coding (H.265) format. +var Hevc = Type{ + Codec: CodecHevc, + FileType: fs.VideoHevc, WidthLimit: 0, HeightLimit: 0, Public: false, } -// HEV1 (High Efficiency Video Coding) bitstream with the parameter sets stored in the samples, -// not supported on macOS: https://ott.dolby.com/codec_test/index.html -var HEV1 = Type{ - Codec: CodecHEV1, - FileType: fs.VideoHEV1, +// Hev1 specifies the HEVC bitstream format with the parameter sets stored in the samples (not supported on macOS): +// https://ott.dolby.com/codec_test/index.html +var Hev1 = Type{ + Codec: CodecHev1, + FileType: fs.VideoHev1, WidthLimit: 0, HeightLimit: 0, Public: false, } -// EVC aka Essential Video Coding (MPEG-5 Part 1). -var EVC = Type{ - Codec: CodecEVC, - FileType: fs.VideoEVC, +// Vvc specifies the Versatile Video Coding (H.266) format. +var Vvc = Type{ + Codec: CodecVvc, + FileType: fs.VideoVvc, WidthLimit: 0, HeightLimit: 0, Public: false, } -// VVC aka Versatile Video Coding (H.266). -var VVC = Type{ - Codec: CodecVVC, - FileType: fs.VideoVVC, +// Evc specifies the Essential Video Coding (MPEG-5 Part 1) format. +var Evc = Type{ + Codec: CodecEvc, + FileType: fs.VideoEvc, WidthLimit: 0, HeightLimit: 0, Public: false, } -// VP8 + Google WebM. -var VP8 = Type{ - Codec: CodecVP8, - FileType: fs.VideoWebM, +// Vp8 specifies a Google VP8 video in a WebM multimedia container. +var Vp8 = Type{ + Codec: CodecVp8, + FileType: fs.VideoWebm, WidthLimit: 0, HeightLimit: 0, Public: false, } -// VP9 + Google WebM. -var VP9 = Type{ - Codec: CodecVP9, - FileType: fs.VideoWebM, +// Vp9 specifies a Google VP9 video in a WebM multimedia container. +var Vp9 = Type{ + Codec: CodecVp9, + FileType: fs.VideoWebm, WidthLimit: 0, HeightLimit: 0, Public: false, } -// AV1 + Google WebM. -var AV1 = Type{ - Codec: CodecAV1, - FileType: fs.VideoWebM, +// Av1 specifies an AV1 (AOMedia Video 1) video in a WebM multimedia container. +var Av1 = Type{ + Codec: CodecAv1, + FileType: fs.VideoWebm, WidthLimit: 0, HeightLimit: 0, Public: false, } -// OGV aka Ogg/Theora. -var OGV = Type{ - Codec: CodecOGV, - FileType: fs.VideoOGV, +// Theora specifies OGV video with Vorbis audio in an OGG multimedia container. +var Theora = Type{ + Codec: CodecTheora, + FileType: fs.VideoTheora, WidthLimit: 0, HeightLimit: 0, Public: false, } -// WebM Container. -var WebM = Type{ +// Webm specifies the Google WebM multimedia container format. +var Webm = Type{ Codec: CodecUnknown, - FileType: fs.VideoWebM, + FileType: fs.VideoWebm, WidthLimit: 0, HeightLimit: 0, Public: false, diff --git a/pkg/media/video/types_test.go b/pkg/media/video/types_test.go index d5cbec2b6..f3308776e 100644 --- a/pkg/media/video/types_test.go +++ b/pkg/media/video/types_test.go @@ -3,15 +3,15 @@ package video import "testing" func TestTypes(t *testing.T) { - if val := Types[""]; val != AVC { + if val := Types[""]; val != Avc { t.Fatal("default type should be avc") } - if val := Types["mp4"]; val != MP4 { + if val := Types["mp4"]; val != Mp4 { t.Fatal("mp4 type should be mp4") } - if val := Types["avc"]; val != AVC { + if val := Types["avc"]; val != Avc { t.Fatal("mp4 type should be avc") } } diff --git a/pkg/net/header/content.go b/pkg/net/header/content.go deleted file mode 100644 index ef2ee24ca..000000000 --- a/pkg/net/header/content.go +++ /dev/null @@ -1,50 +0,0 @@ -package header - -// Standard content request and response header names. -const ( - Accept = "Accept" - AcceptEncoding = "Accept-Encoding" - AcceptLanguage = "Accept-Language" - AcceptRanges = "Accept-Ranges" - ContentType = "Content-Type" - ContentDisposition = "Content-Disposition" - ContentEncoding = "Content-Encoding" - ContentRange = "Content-Range" - Location = "Location" - Origin = "Origin" - Vary = "Vary" -) - -// Standard ContentType header values. -// -// Browser support can be tested by visiting one of the following sites: -// - https://ott.dolby.com/codec_test/index.html -// - https://dmnsgn.github.io/media-codecs/ -// - https://cconcolato.github.io/media-mime-support/ -// - https://thorium.rocks/misc/h265-tester.html -const ( - ContentTypeBinary = "application/octet-stream" - ContentTypeForm = "application/x-www-form-urlencoded" - ContentTypeMultipart = "multipart/form-data" - ContentTypeJson = "application/json" - ContentTypeJsonUtf8 = "application/json; charset=utf-8" - ContentTypeHtml = "text/html; charset=utf-8" - ContentTypeText = "text/plain; charset=utf-8" - ContentTypePDF = "application/pdf" - ContentTypePNG = "image/png" - ContentTypeJPEG = "image/jpeg" - ContentTypeSVG = "image/svg+xml" - ContentTypeQT = "video/quicktime" - ContentTypeMP4 = "video/mp4" - ContentTypeAVC32 = ContentTypeMP4 + "; codecs=\"avc1.640020\"" // MPEG-4 AVC, High Profile Level 3.2 - ContentTypeAVC = ContentTypeMP4 + "; codecs=\"avc1.640028\"" // MPEG-4 AVC, High Profile Level 4.0 - ContentTypeHEVC = ContentTypeMP4 + "; codecs=\"hvc1.2.4.L120.B0\"" // HEVC MP4 Main10 Profile, Main Tier, Level 4.0 - ContentTypeHEV1 = ContentTypeMP4 + "; codecs=\"hev1.2.4.L120.B0\"" // HEVC Bitstream, not supported on macOS - ContentTypeVVC = ContentTypeMP4 + "; codecs=\"vvc1\"" // Versatile Video Coding (VVC), also known as H.266 - ContentTypeEVC = ContentTypeMP4 + "; codecs=\"evc1\"" // MPEG-5 Essential Video Coding (EVC), also known as ISO/IEC 23094-1 - ContentTypeOGV = "video/ogg" - ContentTypeWebM = "video/webm" - ContentTypeVP8 = "video/webm; codecs=\"vp8\"" - ContentTypeVP9 = "video/webm; codecs=\"vp09.00.10.08\"" - ContentTypeAV1 = "video/webm; codecs=\"av01.2.10M.10\"" -)