Merge branch 'develop'
This commit is contained in:
commit
2516463517
44 changed files with 1101 additions and 226 deletions
|
@ -54,6 +54,9 @@ class Config {
|
|||
const type = ev.split(".")[1];
|
||||
|
||||
switch (type) {
|
||||
case "videos":
|
||||
this.values.count.videos += data.count;
|
||||
break;
|
||||
case "favorites":
|
||||
this.values.count.favorites += data.count;
|
||||
break;
|
||||
|
|
|
@ -55,9 +55,7 @@
|
|||
</v-list-tile-action>
|
||||
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
<translate>Photos</translate>
|
||||
</v-list-tile-title>
|
||||
<v-list-tile-title>{{ $gettext('Photos') }}</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
||||
|
@ -65,37 +63,44 @@
|
|||
<v-list-tile slot="activator" to="/photos" @click.stop="" class="p-navigation-photos">
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
<translate>Photos</translate>
|
||||
<translate>{{ $gettext('Photos') }}</translate>
|
||||
<span v-if="config.count.photos > 0" class="p-navigation-count">{{ config.count.photos }}</span>
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
||||
<v-list-tile :to="{name: 'photos', query: { q: 'mono:true quality:3' }}" :exact="true" @click="">
|
||||
<v-list-tile :to="{name: 'photos', query: { q: 'mono:true quality:3 photo:true' }}" :exact="true" @click="">
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
<translate>Monochrome</translate>
|
||||
</v-list-tile-title>
|
||||
<v-list-tile-title>{{ $gettext('Monochrome') }}</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
||||
<v-list-tile to="/review" @click="" v-if="$config.feature('review')">
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
<translate>Review</translate>
|
||||
</v-list-tile-title>
|
||||
<v-list-tile-title>{{ $gettext('Review') }}</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
||||
<v-list-tile to="/archive" @click="" class="p-navigation-archive" v-if="$config.feature('archive')">
|
||||
<v-list-tile to="/archive" @click="" class="p-navigation-archive" v-show="$config.feature('archive')">
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
<translate>Archive</translate>
|
||||
</v-list-tile-title>
|
||||
<v-list-tile-title>{{ $gettext('Archive') }}</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
</v-list-group>
|
||||
|
||||
<v-list-tile to="/videos" @click="" class="p-navigation-video">
|
||||
<v-list-tile-action>
|
||||
<v-icon>slideshow</v-icon>
|
||||
</v-list-tile-action>
|
||||
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
<span>{{ $gettext('Videos') }}</span>
|
||||
<span v-show="config.count.videos > 0" class="p-navigation-count">{{ config.count.videos }}</span>
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
||||
<v-list-tile to="/favorites" @click="" class="p-navigation-favorites">
|
||||
<v-list-tile-action>
|
||||
<v-icon>favorite</v-icon>
|
||||
|
@ -103,21 +108,21 @@
|
|||
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
<translate>Favorites</translate>
|
||||
<span v-if="config.count.favorites > 0" class="p-navigation-count">{{ config.count.favorites }}</span>
|
||||
<span>{{ $gettext('Favorites') }}</span>
|
||||
<span v-show="config.count.favorites > 0" class="p-navigation-count">{{ config.count.favorites }}</span>
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
||||
<v-list-tile to="/private" @click="" class="p-navigation-private" v-if="$config.feature('private')" >
|
||||
<v-list-tile to="/private" @click="" class="p-navigation-private" v-show="$config.feature('private')" >
|
||||
<v-list-tile-action>
|
||||
<v-icon>lock</v-icon>
|
||||
</v-list-tile-action>
|
||||
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
<span v-translate>Private</span>
|
||||
<span v-if="config.count.private > 0" class="p-navigation-count">{{ config.count.private }}</span>
|
||||
<span>{{ $gettext('Private') }}</span>
|
||||
<span v-show="config.count.private > 0" class="p-navigation-count">{{ config.count.private }}</span>
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
@ -129,7 +134,7 @@
|
|||
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
<translate>Albums</translate>
|
||||
<span>{{ $gettext('Albums') }}</span>
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
@ -138,7 +143,7 @@
|
|||
<v-list-tile slot="activator" to="/albums" @click.stop="">
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
<translate>Albums</translate>
|
||||
<span>{{ $gettext('Albums') }}</span>
|
||||
<span v-if="config.count.albums > 0" class="p-navigation-count">{{ config.count.albums }}</span>
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
|
@ -154,36 +159,36 @@
|
|||
</v-list-tile>
|
||||
</v-list-group>
|
||||
|
||||
<v-list-tile to="/labels" @click="" class="p-navigation-labels" v-if="$config.feature('labels')">
|
||||
<v-list-tile to="/labels" @click="" class="p-navigation-labels" v-show="$config.feature('labels')">
|
||||
<v-list-tile-action>
|
||||
<v-icon>label</v-icon>
|
||||
</v-list-tile-action>
|
||||
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
<translate>Labels</translate>
|
||||
<span v-if="config.count.labels > 0"
|
||||
<span>{{ $gettext('Labels') }}</span>
|
||||
<span v-show="config.count.labels > 0"
|
||||
class="p-navigation-count">{{ config.count.labels }}</span>
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
||||
<v-list-tile :to="{ name: 'places' }" @click="" class="p-navigation-places"
|
||||
v-if="$config.feature('places')">
|
||||
v-show="$config.feature('places')">
|
||||
<v-list-tile-action>
|
||||
<v-icon>place</v-icon>
|
||||
</v-list-tile-action>
|
||||
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
<translate>Places</translate>
|
||||
<span v-if="config.count.places > 0"
|
||||
<span>{{ $gettext('Places') }}</span>
|
||||
<span v-show="config.count.places > 0"
|
||||
class="p-navigation-count">{{ config.count.places }}</span>
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
||||
<!-- v-list-tile to="/discover" @click="" class="p-navigation-discover" v-if="config.experimental">
|
||||
<!-- v-list-tile to="/discover" @click="" class="p-navigation-discover" v-show="config.experimental">
|
||||
<v-list-tile-action>
|
||||
<v-icon>color_lens</v-icon>
|
||||
</v-list-tile-action>
|
||||
|
@ -222,43 +227,43 @@
|
|||
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
<translate>Library</translate>
|
||||
<span>{{ $gettext('Library') }}</span>
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
||||
<v-list-tile to="/settings" @click="" class="p-navigation-settings" v-if="!config.disableSettings">
|
||||
<v-list-tile to="/settings" @click="" class="p-navigation-settings" v-show="!config.disableSettings">
|
||||
<v-list-tile-action>
|
||||
<v-icon>settings</v-icon>
|
||||
</v-list-tile-action>
|
||||
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
<translate>Settings</translate>
|
||||
<span>{{ $gettext('Settings') }}</span>
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
||||
<v-list-tile @click="logout" class="p-navigation-logout" v-if="!public && auth">
|
||||
<v-list-tile @click="logout" class="p-navigation-logout" v-show="!public && auth">
|
||||
<v-list-tile-action>
|
||||
<v-icon>power_settings_new</v-icon>
|
||||
</v-list-tile-action>
|
||||
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
<translate>Logout</translate>
|
||||
<span>{{ $gettext('Logout') }}</span>
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
||||
<v-list-tile to="/login" @click="" class="p-navigation-login" v-if="!auth">
|
||||
<v-list-tile to="/login" @click="" class="p-navigation-login" v-show="!auth">
|
||||
<v-list-tile-action>
|
||||
<v-icon>lock</v-icon>
|
||||
</v-list-tile-action>
|
||||
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
<translate>Login</translate>
|
||||
<span>{{ $gettext('Login') }}</span>
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
|
|
@ -68,12 +68,12 @@
|
|||
<v-icon v-else color="accent lighten-3" class="t-like t-off">favorite_border</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn v-if="photo.PhotoVideo" color="white" :ripple="false"
|
||||
<v-btn v-if="photo.PhotoVideo && photo.isMP4()" color="white" :ripple="false"
|
||||
outline large fab absolute class="p-photo-play opacity-75" :depressed="false"
|
||||
@click.stop.prevent="openPhoto(index, true)">
|
||||
<v-icon color="white" class="action-play">play_arrow</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else-if="photo.Files.length > 1" :ripple="false"
|
||||
<v-btn v-else-if="!photo.PhotoVideo && photo.Files.length > 1" :ripple="false"
|
||||
icon flat large absolute class="p-photo-merged opacity-75"
|
||||
@click.stop.prevent="openPhoto(index, true)">
|
||||
<v-icon color="white" class="action-burst">burst_mode</v-icon>
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
flat icon large absolute class="p-photo-select">
|
||||
<v-icon color="white" class="t-select t-on">check_circle</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else-if="!selection.length && props.item.PhotoVideo" :ripple="false"
|
||||
<v-btn v-else-if="!selection.length && props.item.PhotoVideo && props.item.isMP4()" :ripple="false"
|
||||
flat icon large absolute class="p-photo-play opacity-75"
|
||||
@click.stop.prevent="openPhoto(props.index, true)">
|
||||
<v-icon color="white" class="action-play">play_arrow</v-icon>
|
||||
|
@ -144,7 +144,7 @@
|
|||
} else if(this.photos[index]) {
|
||||
let photo = this.photos[index];
|
||||
|
||||
if(photo.PhotoVideo) {
|
||||
if(photo.PhotoVideo && photo.isMP4()) {
|
||||
this.openPhoto(index, true);
|
||||
} else {
|
||||
this.openPhoto(index, false);
|
||||
|
|
|
@ -67,12 +67,12 @@
|
|||
<v-icon v-else color="accent lighten-3" class="t-like t-off">favorite_border</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn v-if="photo.PhotoVideo" color="white"
|
||||
<v-btn v-if="photo.PhotoVideo && photo.isMP4()" color="white"
|
||||
outline fab absolute class="p-photo-play opacity-75" :depressed="false" :ripple="false"
|
||||
@click.stop.prevent="openPhoto(index, true)">
|
||||
<v-icon color="white" class="action-play">play_arrow</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else-if="photo.Files.length > 1" :ripple="false"
|
||||
<v-btn v-else-if="!photo.PhotoVideo && photo.Files.length > 1" :ripple="false"
|
||||
icon flat small absolute class="p-photo-merged opacity-75"
|
||||
@click.stop.prevent="openPhoto(index, true)">
|
||||
<v-icon color="white" class="action-burst">burst_mode</v-icon>
|
||||
|
|
|
@ -105,6 +105,14 @@ class Photo extends RestModel {
|
|||
this.FileHeight = file.FileHeight;
|
||||
}
|
||||
|
||||
isMP4() {
|
||||
if (!this.Files) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.Files.findIndex(f => f.FileType === "mp4") !== -1;
|
||||
}
|
||||
|
||||
videoFile() {
|
||||
if (!this.Files) {
|
||||
return false;
|
||||
|
|
|
@ -142,7 +142,11 @@
|
|||
}
|
||||
|
||||
if (showMerged && this.results[index].PhotoVideo) {
|
||||
Event.publish("dialog.video", {play: this.results[index], album: null});
|
||||
if(this.results[index].isMP4()) {
|
||||
Event.publish("dialog.video", {play: this.results[index], album: this.album});
|
||||
} else {
|
||||
this.$viewer.show(Thumb.fromPhotos(this.results), index);
|
||||
}
|
||||
} else if (showMerged) {
|
||||
this.$viewer.show(Thumb.fromFiles([this.results[index]]), 0)
|
||||
} else {
|
||||
|
|
|
@ -115,16 +115,16 @@
|
|||
computed: {
|
||||
context: function () {
|
||||
if (!this.staticFilter) {
|
||||
return "photos"
|
||||
return "photos";
|
||||
}
|
||||
|
||||
if (this.staticFilter.archived) {
|
||||
return "archive"
|
||||
} else if (this.staticFilter.favorites) {
|
||||
return "favorites"
|
||||
return "archive";
|
||||
} else if (this.staticFilter.favorite) {
|
||||
return "favorites";
|
||||
}
|
||||
|
||||
return ""
|
||||
return "";
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -179,7 +179,11 @@
|
|||
}
|
||||
|
||||
if (showMerged && this.results[index].PhotoVideo) {
|
||||
Event.publish("dialog.video", {play: this.results[index], album: null});
|
||||
if(this.results[index].isMP4()) {
|
||||
Event.publish("dialog.video", {play: this.results[index], album: null});
|
||||
} else {
|
||||
this.$viewer.show(Thumb.fromPhotos(this.results), index);
|
||||
}
|
||||
} else if (showMerged) {
|
||||
this.$viewer.show(Thumb.fromFiles([this.results[index]]), 0)
|
||||
} else {
|
||||
|
|
|
@ -49,7 +49,14 @@ export default [
|
|||
path: "/favorites",
|
||||
component: Photos,
|
||||
meta: {title: "Favorites", auth: true},
|
||||
props: {staticFilter: {favorites: true}},
|
||||
props: {staticFilter: {favorite: true}},
|
||||
},
|
||||
{
|
||||
name: "videos",
|
||||
path: "/videos",
|
||||
component: Photos,
|
||||
meta: {title: "Videos", auth: true},
|
||||
props: {staticFilter: {video: true}},
|
||||
},
|
||||
{
|
||||
name: "review",
|
||||
|
|
29
internal/api/import_test.go
Normal file
29
internal/api/import_test.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tidwall/gjson"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetImportOptions(t *testing.T) {
|
||||
t.Run("successful request", func(t *testing.T) {
|
||||
app, router, conf := NewApiTest()
|
||||
GetImportOptions(router, conf)
|
||||
r := PerformRequest(app, "GET", "/api/v1/import")
|
||||
assert.True(t, gjson.Get(r.Body.String(), "dirs").IsArray())
|
||||
assert.Equal(t, http.StatusOK, r.Code)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCancelImport(t *testing.T) {
|
||||
t.Run("successful request", func(t *testing.T) {
|
||||
app, router, conf := NewApiTest()
|
||||
CancelImport(router, conf)
|
||||
r := PerformRequest(app, "DELETE", "/api/v1/import")
|
||||
val := gjson.Get(r.Body.String(), "message")
|
||||
assert.Equal(t, "import canceled", val.String())
|
||||
assert.Equal(t, http.StatusOK, r.Code)
|
||||
})
|
||||
}
|
29
internal/api/index_test.go
Normal file
29
internal/api/index_test.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tidwall/gjson"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetIndexingOptions(t *testing.T) {
|
||||
t.Run("successful request", func(t *testing.T) {
|
||||
app, router, conf := NewApiTest()
|
||||
GetIndexingOptions(router, conf)
|
||||
r := PerformRequest(app, "GET", "/api/v1/index")
|
||||
assert.True(t, gjson.Get(r.Body.String(), "dirs").IsArray())
|
||||
assert.Equal(t, http.StatusOK, r.Code)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCancelIndex(t *testing.T) {
|
||||
t.Run("successful request", func(t *testing.T) {
|
||||
app, router, conf := NewApiTest()
|
||||
CancelIndexing(router, conf)
|
||||
r := PerformRequest(app, "DELETE", "/api/v1/index")
|
||||
val := gjson.Get(r.Body.String(), "message")
|
||||
assert.Equal(t, "indexing canceled", val.String())
|
||||
assert.Equal(t, http.StatusOK, r.Code)
|
||||
})
|
||||
}
|
|
@ -64,14 +64,17 @@ func TestLikeLabel(t *testing.T) {
|
|||
t.Run("like existing label", func(t *testing.T) {
|
||||
app, router, ctx := NewApiTest()
|
||||
GetLabels(router, ctx)
|
||||
r2 := PerformRequest(app, "GET", "/api/v1/labels?count=1&q=updatelabel")
|
||||
val := gjson.Get(r2.Body.String(), `#(LabelSlug=="updatelabel").LabelFavorite`)
|
||||
r2 := PerformRequest(app, "GET", "/api/v1/labels?count=1&q=likeLabel")
|
||||
t.Log(r2.Body.String())
|
||||
val := gjson.Get(r2.Body.String(), `#(LabelSlug=="likeLabel").LabelFavorite`)
|
||||
assert.Equal(t, "false", val.String())
|
||||
LikeLabel(router, ctx)
|
||||
r := PerformRequest(app, "POST", "/api/v1/labels/lt9k3pw1wowuy3c7/like")
|
||||
r := PerformRequest(app, "POST", "/api/v1/labels/lt9k3pw1wowuy3c9/like")
|
||||
t.Log(r.Body.String())
|
||||
assert.Equal(t, http.StatusOK, r.Code)
|
||||
r3 := PerformRequest(app, "GET", "/api/v1/labels?count=1&q=updatelabel")
|
||||
val2 := gjson.Get(r3.Body.String(), `#(LabelSlug=="updatelabel").LabelFavorite`)
|
||||
r3 := PerformRequest(app, "GET", "/api/v1/labels?count=1&q=likeLabel")
|
||||
t.Log(r3.Body.String())
|
||||
val2 := gjson.Get(r3.Body.String(), `#(LabelSlug=="likeLabel").LabelFavorite`)
|
||||
assert.Equal(t, "true", val2.String())
|
||||
})
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ import (
|
|||
// offset: int Result offset
|
||||
// before: date Find photos taken before (format: "2006-01-02")
|
||||
// after: date Find photos taken after (format: "2006-01-02")
|
||||
// favorites: bool Find favorites only
|
||||
// favorite: bool Find favorites only
|
||||
func GetPhotos(router *gin.RouterGroup, conf *config.Config) {
|
||||
router.GET("/photos", func(c *gin.Context) {
|
||||
if Unauthorized(c, conf) {
|
||||
|
|
34
internal/api/video_test.go
Normal file
34
internal/api/video_test.go
Normal file
|
@ -0,0 +1,34 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetVideo(t *testing.T) {
|
||||
t.Run("invalid hash", func(t *testing.T) {
|
||||
app, router, conf := NewApiTest()
|
||||
GetVideo(router, conf)
|
||||
r := PerformRequest(app, "GET", "/api/v1/videos/xxx/mp4")
|
||||
assert.Equal(t, http.StatusOK, r.Code)
|
||||
})
|
||||
t.Run("invalid type", func(t *testing.T) {
|
||||
app, router, conf := NewApiTest()
|
||||
GetVideo(router, conf)
|
||||
r := PerformRequest(app, "GET", "/api/v1/videos/acad9168fa6acc5c5c2965ddf6ec465ca42fd831/xxx")
|
||||
assert.Equal(t, http.StatusOK, r.Code)
|
||||
})
|
||||
t.Run("file for video not found", func(t *testing.T) {
|
||||
app, router, conf := NewApiTest()
|
||||
GetVideo(router, conf)
|
||||
r := PerformRequest(app, "GET", "/api/v1/videos/acad9168fa6acc5c5c2965ddf6ec465ca42fd831/mp4")
|
||||
assert.Equal(t, http.StatusOK, r.Code)
|
||||
})
|
||||
t.Run("file with error", func(t *testing.T) {
|
||||
app, router, conf := NewApiTest()
|
||||
GetVideo(router, conf)
|
||||
r := PerformRequest(app, "GET", "/api/v1/videos/acad9168fa6acc5c5c2965ddf6ec465ca42fd832/mp4")
|
||||
assert.Equal(t, http.StatusOK, r.Code)
|
||||
})
|
||||
}
|
28
internal/api/websocket_test.go
Normal file
28
internal/api/websocket_test.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestWebsocket(t *testing.T) {
|
||||
t.Run("bad request", func(t *testing.T) {
|
||||
app, router, conf := NewApiTest()
|
||||
Websocket(router, conf)
|
||||
r := PerformRequest(app, "GET", "/api/v1/ws")
|
||||
assert.Equal(t, http.StatusBadRequest, r.Code)
|
||||
})
|
||||
t.Run("router nil", func(t *testing.T) {
|
||||
app, _, conf := NewApiTest()
|
||||
Websocket(nil, conf)
|
||||
r := PerformRequest(app, "GET", "/api/v1/ws")
|
||||
assert.Equal(t, http.StatusNotFound, r.Code)
|
||||
})
|
||||
t.Run("conf nil", func(t *testing.T) {
|
||||
app, router, _ := NewApiTest()
|
||||
Websocket(router, nil)
|
||||
r := PerformRequest(app, "GET", "/api/v1/ws")
|
||||
assert.Equal(t, http.StatusNotFound, r.Code)
|
||||
})
|
||||
}
|
|
@ -59,6 +59,7 @@ func (c *Config) PublicClientConfig() ClientConfig {
|
|||
|
||||
var count = struct {
|
||||
Photos uint `json:"photos"`
|
||||
Videos uint `json:"videos"`
|
||||
Hidden uint `json:"hidden"`
|
||||
Favorites uint `json:"favorites"`
|
||||
Private uint `json:"private"`
|
||||
|
@ -132,6 +133,7 @@ func (c *Config) ClientConfig() ClientConfig {
|
|||
|
||||
var count = struct {
|
||||
Photos uint `json:"photos"`
|
||||
Videos uint `json:"videos"`
|
||||
Hidden uint `json:"hidden"`
|
||||
Favorites uint `json:"favorites"`
|
||||
Private uint `json:"private"`
|
||||
|
@ -143,7 +145,7 @@ func (c *Config) ClientConfig() ClientConfig {
|
|||
}{}
|
||||
|
||||
db.Table("photos").
|
||||
Select("SUM(photo_quality = -1) AS hidden, SUM(photo_quality >= 0) AS photos, SUM(photo_favorite) AS favorites, SUM(photo_private) AS private").
|
||||
Select("SUM(photo_video = 1 AND photo_quality >= 0) AS videos, SUM(photo_quality = -1) AS hidden, SUM(photo_quality >= 0) AS photos, SUM(photo_favorite) AS favorites, SUM(photo_private) AS private").
|
||||
Where("deleted_at IS NULL").
|
||||
Take(&count)
|
||||
|
||||
|
|
|
@ -193,7 +193,7 @@ var FileFixtures = map[string]File{
|
|||
FileDiff: 800,
|
||||
FileChroma: 4,
|
||||
FileNotes: "",
|
||||
FileError: "",
|
||||
FileError: "Error",
|
||||
Share: []FileShare{},
|
||||
Sync: []FileSync{},
|
||||
Links: []Link{},
|
||||
|
@ -281,6 +281,123 @@ var FileFixtures = map[string]File{
|
|||
UpdatedIn: 0,
|
||||
DeletedAt: nil,
|
||||
},
|
||||
"Photo18.jpg": {
|
||||
ID: 1000007,
|
||||
Photo: nil, // no pointer here because related photo is deleted
|
||||
PhotoID: 1000018,
|
||||
PhotoUUID: "pt9jtdre2lvl0y25",
|
||||
FileUUID: "ft6es39w45bnlqdw",
|
||||
FileName: "Photo18.jpg",
|
||||
OriginalName: "Photo18.jpg",
|
||||
FileHash: "acad9168fa6acc5c5c2965ddf6ec465ca42fd820",
|
||||
FileModified: time.Date(2017, 1, 6, 2, 6, 51, 0, time.UTC),
|
||||
FileSize: 500,
|
||||
FileType: "jpg",
|
||||
FileMime: "image/jpg",
|
||||
FilePrimary: true,
|
||||
FileSidecar: false,
|
||||
FileVideo: false,
|
||||
FileMissing: false,
|
||||
FileDuplicate: false,
|
||||
FilePortrait: false,
|
||||
FileWidth: 1200,
|
||||
FileHeight: 1600,
|
||||
FileOrientation: 6,
|
||||
FileAspectRatio: 0.75,
|
||||
FileMainColor: "green",
|
||||
FileColors: "266111000",
|
||||
FileLuminance: "DC42844C8",
|
||||
FileDiff: 800,
|
||||
FileChroma: 0,
|
||||
FileNotes: "",
|
||||
FileError: "",
|
||||
Share: []FileShare{},
|
||||
Sync: []FileSync{},
|
||||
Links: []Link{},
|
||||
CreatedAt: time.Date(2018, 1, 1, 2, 6, 51, 0, time.UTC),
|
||||
CreatedIn: 2,
|
||||
UpdatedAt: time.Date(2029, 3, 28, 14, 6, 0, 0, time.UTC),
|
||||
UpdatedIn: 0,
|
||||
DeletedAt: nil,
|
||||
},
|
||||
"Video.mp4": {
|
||||
ID: 1000008,
|
||||
Photo: PhotoFixtures.Pointer("Photo10"),
|
||||
PhotoID: 1000010,
|
||||
PhotoUUID: "pt9jtdre2lvl0y17",
|
||||
FileUUID: "ft71s39w45bnlqdw",
|
||||
FileName: "Video.mp4",
|
||||
OriginalName: "Video.mp4",
|
||||
FileHash: "acad9168fa6acc5c5c2965ddf6ec465ca42fd831",
|
||||
FileModified: time.Date(2017, 1, 6, 2, 6, 51, 0, time.UTC),
|
||||
FileSize: 500,
|
||||
FileType: "mp4",
|
||||
FileMime: "video/mp4",
|
||||
FilePrimary: true,
|
||||
FileSidecar: false,
|
||||
FileVideo: true,
|
||||
FileMissing: false,
|
||||
FileDuplicate: false,
|
||||
FilePortrait: false,
|
||||
FileWidth: 1200,
|
||||
FileHeight: 1600,
|
||||
FileOrientation: 6,
|
||||
FileAspectRatio: 0.75,
|
||||
FileMainColor: "green",
|
||||
FileColors: "266111000",
|
||||
FileLuminance: "DC42844C8",
|
||||
FileDiff: 800,
|
||||
FileChroma: 0,
|
||||
FileNotes: "",
|
||||
FileError: "",
|
||||
Share: []FileShare{},
|
||||
Sync: []FileSync{},
|
||||
Links: []Link{},
|
||||
CreatedAt: time.Date(2018, 1, 1, 2, 6, 51, 0, time.UTC),
|
||||
CreatedIn: 2,
|
||||
UpdatedAt: time.Date(2029, 3, 28, 14, 6, 0, 0, time.UTC),
|
||||
UpdatedIn: 0,
|
||||
DeletedAt: nil,
|
||||
},
|
||||
"VideoWithError.mp4": {
|
||||
ID: 1000009,
|
||||
Photo: PhotoFixtures.Pointer("Photo10"),
|
||||
PhotoID: 1000010,
|
||||
PhotoUUID: "pt9jtdre2lvl0y17",
|
||||
FileUUID: "ft72s39w45bnlqdw",
|
||||
FileName: "VideoError.mp4",
|
||||
OriginalName: "VideoError.mp4",
|
||||
FileHash: "acad9168fa6acc5c5c2965ddf6ec465ca42fd832",
|
||||
FileModified: time.Date(2017, 1, 6, 2, 6, 51, 0, time.UTC),
|
||||
FileSize: 500,
|
||||
FileType: "mp4",
|
||||
FileMime: "video/mp4",
|
||||
FilePrimary: false,
|
||||
FileSidecar: false,
|
||||
FileVideo: true,
|
||||
FileMissing: false,
|
||||
FileDuplicate: false,
|
||||
FilePortrait: false,
|
||||
FileWidth: 1200,
|
||||
FileHeight: 1600,
|
||||
FileOrientation: 6,
|
||||
FileAspectRatio: 0.75,
|
||||
FileMainColor: "green",
|
||||
FileColors: "266111000",
|
||||
FileLuminance: "DC42844C8",
|
||||
FileDiff: 800,
|
||||
FileChroma: 0,
|
||||
FileNotes: "",
|
||||
FileError: "Error",
|
||||
Share: []FileShare{},
|
||||
Sync: []FileSync{},
|
||||
Links: []Link{},
|
||||
CreatedAt: time.Date(2018, 1, 1, 2, 6, 51, 0, time.UTC),
|
||||
CreatedIn: 2,
|
||||
UpdatedAt: time.Date(2029, 3, 28, 14, 6, 0, 0, time.UTC),
|
||||
UpdatedIn: 0,
|
||||
DeletedAt: nil,
|
||||
},
|
||||
}
|
||||
|
||||
var FileFixturesExampleJPG = FileFixtures["exampleFileName.jpg"]
|
||||
|
|
|
@ -27,7 +27,7 @@ var FileShareFixtures = FileShareMap{
|
|||
FileID: 1000000,
|
||||
AccountID: 1000000,
|
||||
RemoteName: "name for remote",
|
||||
Status: "test",
|
||||
Status: FileShareShared,
|
||||
Error: "",
|
||||
Errors: 0,
|
||||
File: nil,
|
||||
|
@ -39,7 +39,7 @@ var FileShareFixtures = FileShareMap{
|
|||
FileID: 1000000,
|
||||
AccountID: 1000001,
|
||||
RemoteName: "name for remote",
|
||||
Status: "test",
|
||||
Status: FileShareNew,
|
||||
Error: "",
|
||||
Errors: 0,
|
||||
File: nil,
|
||||
|
|
36
internal/entity/file_share_fixtures_test.go
Normal file
36
internal/entity/file_share_fixtures_test.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package entity
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFileShareMap_Get(t *testing.T) {
|
||||
t.Run("get existing fileshare", func(t *testing.T) {
|
||||
r := FileShareFixtures.Get("FileShare1", 0, 0, "")
|
||||
assert.Equal(t, uint(1000000), r.AccountID)
|
||||
assert.Equal(t, "name for remote", r.RemoteName)
|
||||
assert.IsType(t, FileShare{}, r)
|
||||
})
|
||||
t.Run("get not existing fileshare", func(t *testing.T) {
|
||||
r := FileShareFixtures.Get("FileShareXXX", 123, 888, "new remote name")
|
||||
assert.Equal(t, uint(888), r.AccountID)
|
||||
assert.Equal(t, "new remote name", r.RemoteName)
|
||||
assert.IsType(t, FileShare{}, r)
|
||||
})
|
||||
}
|
||||
|
||||
func TestFileShareMap_Pointer(t *testing.T) {
|
||||
t.Run("get existing fileshare pointer", func(t *testing.T) {
|
||||
r := FileShareFixtures.Pointer("FileShare1", 0, 0, "")
|
||||
assert.Equal(t, uint(1000000), r.AccountID)
|
||||
assert.Equal(t, "name for remote", r.RemoteName)
|
||||
assert.IsType(t, &FileShare{}, r)
|
||||
})
|
||||
t.Run("get not existing fileshare pointer", func(t *testing.T) {
|
||||
r := FileShareFixtures.Pointer("FileShareYYY", 456, 889, "new remote name for pointer")
|
||||
assert.Equal(t, uint(889), r.AccountID)
|
||||
assert.Equal(t, "new remote name for pointer", r.RemoteName)
|
||||
assert.IsType(t, &FileShare{}, r)
|
||||
})
|
||||
}
|
36
internal/entity/file_sync_fixtures_test.go
Normal file
36
internal/entity/file_sync_fixtures_test.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package entity
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFileSyncMap_Get(t *testing.T) {
|
||||
t.Run("get existing filesync", func(t *testing.T) {
|
||||
r := FileSyncFixtures.Get("FileSync1", 0, "")
|
||||
assert.Equal(t, uint(1000000), r.AccountID)
|
||||
assert.Equal(t, "name for remote sync", r.RemoteName)
|
||||
assert.IsType(t, FileSync{}, r)
|
||||
})
|
||||
t.Run("get not existing filesync", func(t *testing.T) {
|
||||
r := FileSyncFixtures.Get("FileSyncXXX", 123, "new remote name for sync")
|
||||
assert.Equal(t, uint(123), r.AccountID)
|
||||
assert.Equal(t, "new remote name for sync", r.RemoteName)
|
||||
assert.IsType(t, FileSync{}, r)
|
||||
})
|
||||
}
|
||||
|
||||
func TestFileSyncMap_Pointer(t *testing.T) {
|
||||
t.Run("get existing filesync pointer", func(t *testing.T) {
|
||||
r := FileSyncFixtures.Pointer("FileSync1", 0, "")
|
||||
assert.Equal(t, uint(1000000), r.AccountID)
|
||||
assert.Equal(t, "name for remote sync", r.RemoteName)
|
||||
assert.IsType(t, &FileSync{}, r)
|
||||
})
|
||||
t.Run("get not existing filesync pointer", func(t *testing.T) {
|
||||
r := FileSyncFixtures.Pointer("FileSyncYYY", 456, "new remote name for sync pointer")
|
||||
assert.Equal(t, uint(456), r.AccountID)
|
||||
assert.Equal(t, "new remote name for sync pointer", r.RemoteName)
|
||||
assert.IsType(t, &FileSync{}, r)
|
||||
})
|
||||
}
|
|
@ -51,6 +51,7 @@ func TestFile_DownloadFileName(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestFile_Changed(t *testing.T) {
|
||||
var deletedAt = time.Date(2019, 01, 15, 0, 0, 0, 0, time.UTC)
|
||||
t.Run("different modified times", func(t *testing.T) {
|
||||
file := &File{Photo: nil, FileType: "jpg", FileSize: 500, FileModified: time.Date(2019, 01, 15, 0, 0, 0, 0, time.UTC)}
|
||||
time := time.Date(2020, 01, 15, 0, 0, 0, 0, time.UTC)
|
||||
|
@ -66,6 +67,11 @@ func TestFile_Changed(t *testing.T) {
|
|||
time := time.Date(2019, 01, 15, 0, 0, 0, 0, time.UTC)
|
||||
assert.Equal(t, false, file.Changed(500, time))
|
||||
})
|
||||
t.Run("deleted", func(t *testing.T) {
|
||||
file := &File{Photo: nil, FileType: "jpg", FileSize: 500, FileModified: time.Date(2019, 01, 15, 0, 0, 0, 0, time.UTC), DeletedAt: &deletedAt}
|
||||
time := time.Date(2019, 01, 15, 0, 0, 0, 0, time.UTC)
|
||||
assert.Equal(t, true, file.Changed(500, time))
|
||||
})
|
||||
}
|
||||
|
||||
func TestFile_Purge(t *testing.T) {
|
||||
|
@ -74,3 +80,28 @@ func TestFile_Purge(t *testing.T) {
|
|||
assert.Equal(t, nil, file.Purge())
|
||||
})
|
||||
}
|
||||
|
||||
func TestFile_AllFilesMissing(t *testing.T) {
|
||||
t.Run("true", func(t *testing.T) {
|
||||
photo := &Photo{TakenAtLocal: time.Date(2019, 01, 15, 0, 0, 0, 0, time.UTC), PhotoTitle: ""}
|
||||
file := &File{Photo: photo, FileType: "jpg", PhotoUUID: "123", FileUUID: "123", FileMissing: true}
|
||||
file2 := &File{Photo: photo, FileType: "jpg", PhotoUUID: "123", FileUUID: "456", FileMissing: true}
|
||||
assert.True(t, file.AllFilesMissing())
|
||||
assert.NotEmpty(t, file2)
|
||||
})
|
||||
//TODO test false
|
||||
/*t.Run("false", func(t *testing.T) {
|
||||
file := FileFixturesExampleJPG
|
||||
assert.False(t, file.AllFilesMissing())
|
||||
assert.NotEmpty(t, file)
|
||||
})*/
|
||||
}
|
||||
|
||||
func TestFile_Save(t *testing.T) {
|
||||
t.Run("record not found", func(t *testing.T) {
|
||||
file := &File{Photo: nil, FileType: "jpg", PhotoUUID: "123", FileUUID: "123"}
|
||||
err := file.Save()
|
||||
assert.Equal(t, "record not found", err.Error())
|
||||
})
|
||||
//TODO test success
|
||||
}
|
||||
|
|
|
@ -158,6 +158,24 @@ var LabelFixtures = LabelMap{
|
|||
DeletedAt: nil,
|
||||
New: false,
|
||||
},
|
||||
"likeLabel": {
|
||||
ID: 1000007,
|
||||
LabelUUID: "lt9k3pw1wowuy3c9",
|
||||
LabelSlug: "likeLabel",
|
||||
CustomSlug: "likeLabel",
|
||||
LabelName: "likeLabel",
|
||||
LabelPriority: 3,
|
||||
LabelFavorite: false,
|
||||
LabelDescription: "",
|
||||
LabelNotes: "",
|
||||
PhotoCount: 1,
|
||||
LabelCategories: []*Label{},
|
||||
Links: []Link{},
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
DeletedAt: nil,
|
||||
New: false,
|
||||
},
|
||||
}
|
||||
|
||||
// CreateLabelFixtures inserts known entities into the database for testing.
|
||||
|
|
34
internal/entity/lens_fixtures_test.go
Normal file
34
internal/entity/lens_fixtures_test.go
Normal file
|
@ -0,0 +1,34 @@
|
|||
package entity
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLensMap_Get(t *testing.T) {
|
||||
t.Run("get existing lens", func(t *testing.T) {
|
||||
r := LensFixtures.Get("lens-f-380")
|
||||
assert.Equal(t, uint(1000000), r.ID)
|
||||
assert.Equal(t, "lens-f-380", r.LensSlug)
|
||||
assert.IsType(t, Lens{}, r)
|
||||
})
|
||||
t.Run("get not existing lens", func(t *testing.T) {
|
||||
r := LensFixtures.Get("Lens 123")
|
||||
assert.Equal(t, "lens-123", r.LensSlug)
|
||||
assert.IsType(t, Lens{}, r)
|
||||
})
|
||||
}
|
||||
|
||||
func TestLensMap_Pointer(t *testing.T) {
|
||||
t.Run("get existing lens pointer", func(t *testing.T) {
|
||||
r := LensFixtures.Pointer("lens-f-380")
|
||||
assert.Equal(t, uint(1000000), r.ID)
|
||||
assert.Equal(t, "lens-f-380", r.LensSlug)
|
||||
assert.IsType(t, &Lens{}, r)
|
||||
})
|
||||
t.Run("get not existing lens pointer", func(t *testing.T) {
|
||||
r := LensFixtures.Pointer("Lens new")
|
||||
assert.Equal(t, "lens-new", r.LensSlug)
|
||||
assert.IsType(t, &Lens{}, r)
|
||||
})
|
||||
}
|
|
@ -5,6 +5,7 @@ import (
|
|||
)
|
||||
|
||||
var editTime = time.Date(2008, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
var deleteTime = time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
type PhotoMap map[string]Photo
|
||||
|
||||
|
@ -35,7 +36,7 @@ var PhotoFixtures = PhotoMap{
|
|||
TitleSrc: "",
|
||||
PhotoPath: "2790/02",
|
||||
PhotoName: "19800101_000002_D640C559",
|
||||
PhotoQuality: 3,
|
||||
PhotoQuality: 4,
|
||||
PhotoResolution: 2,
|
||||
PhotoFavorite: false,
|
||||
PhotoPrivate: false,
|
||||
|
@ -65,7 +66,9 @@ var PhotoFixtures = PhotoMap{
|
|||
Location: nil,
|
||||
Place: PlaceFixtures.Pointer("teotihuacan"),
|
||||
Links: []Link{},
|
||||
Keywords: []Keyword{},
|
||||
Keywords: []Keyword{
|
||||
KeywordFixtures.Get("bridge"),
|
||||
},
|
||||
Albums: []Album{
|
||||
AlbumFixtures.Get("holiday-2030"),
|
||||
},
|
||||
|
@ -295,7 +298,7 @@ var PhotoFixtures = PhotoMap{
|
|||
PhotoQuality: 3,
|
||||
PhotoResolution: 2,
|
||||
PhotoFavorite: false,
|
||||
PhotoPrivate: false,
|
||||
PhotoPrivate: true,
|
||||
PhotoVideo: false,
|
||||
PhotoLat: -21.342636,
|
||||
PhotoLng: 55.466944,
|
||||
|
@ -913,11 +916,64 @@ var PhotoFixtures = PhotoMap{
|
|||
Keywords: []Keyword{},
|
||||
Albums: []Album{},
|
||||
Files: []File{},
|
||||
Labels: []PhotoLabel{LabelFixtures.PhotoLabel(10000015, "landscape", 20, "image")},
|
||||
CreatedAt: time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
UpdatedAt: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
EditedAt: nil,
|
||||
DeletedAt: nil,
|
||||
Labels: []PhotoLabel{
|
||||
LabelFixtures.PhotoLabel(10000015, "landscape", 20, "image"),
|
||||
LabelFixtures.PhotoLabel(10000018, "likeLabel", 20, "image")},
|
||||
CreatedAt: time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
UpdatedAt: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
EditedAt: nil,
|
||||
DeletedAt: nil,
|
||||
},
|
||||
"Photo18": {
|
||||
ID: 1000018,
|
||||
PhotoUUID: "pt9jtdre2lvl0y25",
|
||||
TakenAt: time.Date(2013, 11, 11, 9, 7, 18, 0, time.UTC),
|
||||
TakenAtLocal: time.Date(2013, 11, 11, 9, 7, 18, 0, time.UTC),
|
||||
TakenSrc: "",
|
||||
PhotoTitle: "ArchivedChroma0",
|
||||
TitleSrc: "",
|
||||
PhotoPath: "",
|
||||
PhotoName: "",
|
||||
PhotoQuality: 0,
|
||||
PhotoResolution: 0,
|
||||
PhotoFavorite: true,
|
||||
PhotoPrivate: false,
|
||||
PhotoVideo: false,
|
||||
PhotoLat: 1.234,
|
||||
PhotoLng: 4.321,
|
||||
PhotoAltitude: 3,
|
||||
PhotoIso: 0,
|
||||
PhotoFocalLength: 0,
|
||||
PhotoFNumber: 0,
|
||||
PhotoExposure: "",
|
||||
CameraID: 0,
|
||||
CameraSerial: "",
|
||||
CameraSrc: "",
|
||||
LensID: 0,
|
||||
PlaceID: "",
|
||||
LocationID: "",
|
||||
LocationSrc: "location",
|
||||
TimeZone: "",
|
||||
PhotoCountry: "",
|
||||
PhotoYear: 0,
|
||||
PhotoMonth: 0,
|
||||
Description: DescriptionFixtures.Get("lake", 1000015),
|
||||
DescriptionSrc: "location",
|
||||
Camera: CameraFixtures.Pointer("canon-eos-6d"),
|
||||
Lens: LensFixtures.Pointer("lens-f-380"),
|
||||
Location: &LocationFixturesMexico,
|
||||
Place: PlaceFixtures.Pointer("teotihuacan"),
|
||||
Links: []Link{},
|
||||
Keywords: []Keyword{},
|
||||
Albums: []Album{},
|
||||
Files: []File{},
|
||||
Labels: []PhotoLabel{
|
||||
LabelFixtures.PhotoLabel(10000018, "landscape", 20, "image"),
|
||||
LabelFixtures.PhotoLabel(10000018, "likeLabel", 20, "image")},
|
||||
CreatedAt: time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
UpdatedAt: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
EditedAt: nil,
|
||||
DeletedAt: &deleteTime,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,10 @@ var PhotoKeywordFixtures = PhotoKeywordMap{
|
|||
PhotoID: 1000001,
|
||||
KeywordID: 1000001,
|
||||
},
|
||||
"3": {
|
||||
PhotoID: 1000000,
|
||||
KeywordID: 1000000,
|
||||
},
|
||||
}
|
||||
|
||||
// CreatePhotoKeywordFixtures inserts known entities into the database for testing.
|
||||
|
|
|
@ -2,14 +2,14 @@ package form
|
|||
|
||||
// AlbumSearch represents search form fields for "/api/v1/albums".
|
||||
type AlbumSearch struct {
|
||||
Query string `form:"q"`
|
||||
ID string `form:"id"`
|
||||
Slug string `form:"slug"`
|
||||
Name string `form:"name"`
|
||||
Favorites bool `form:"favorites"`
|
||||
Count int `form:"count" binding:"required"`
|
||||
Offset int `form:"offset"`
|
||||
Order string `form:"order"`
|
||||
Query string `form:"q"`
|
||||
ID string `form:"id"`
|
||||
Slug string `form:"slug"`
|
||||
Name string `form:"name"`
|
||||
Favorite bool `form:"favorite"`
|
||||
Count int `form:"count" binding:"required"`
|
||||
Offset int `form:"offset"`
|
||||
Order string `form:"order"`
|
||||
}
|
||||
|
||||
func (f *AlbumSearch) GetQuery() string {
|
||||
|
|
|
@ -16,7 +16,7 @@ func TestAlbumSearchForm(t *testing.T) {
|
|||
func TestParseQueryStringAlbum(t *testing.T) {
|
||||
|
||||
t.Run("valid query", func(t *testing.T) {
|
||||
form := &AlbumSearch{Query: "slug:album1 favorites:true count:10"}
|
||||
form := &AlbumSearch{Query: "slug:album1 favorite:true count:10"}
|
||||
|
||||
err := form.ParseQueryString()
|
||||
|
||||
|
@ -27,11 +27,11 @@ func TestParseQueryStringAlbum(t *testing.T) {
|
|||
}
|
||||
|
||||
assert.Equal(t, "album1", form.Slug)
|
||||
assert.Equal(t, true, form.Favorites)
|
||||
assert.Equal(t, true, form.Favorite)
|
||||
assert.Equal(t, 10, form.Count)
|
||||
})
|
||||
t.Run("valid query 2", func(t *testing.T) {
|
||||
form := &AlbumSearch{Query: "name:album1 favorites:false offset:100 order:newest query:\"query text\""}
|
||||
form := &AlbumSearch{Query: "name:album1 favorite:false offset:100 order:newest query:\"query text\""}
|
||||
|
||||
err := form.ParseQueryString()
|
||||
|
||||
|
@ -42,7 +42,7 @@ func TestParseQueryStringAlbum(t *testing.T) {
|
|||
}
|
||||
|
||||
assert.Equal(t, "album1", form.Name)
|
||||
assert.Equal(t, false, form.Favorites)
|
||||
assert.Equal(t, false, form.Favorite)
|
||||
assert.Equal(t, 100, form.Offset)
|
||||
assert.Equal(t, "newest", form.Order)
|
||||
assert.Equal(t, "query text", form.Query)
|
||||
|
@ -74,7 +74,7 @@ func TestParseQueryStringAlbum(t *testing.T) {
|
|||
assert.Equal(t, "unknown filter: Xxx", err.Error())
|
||||
})
|
||||
t.Run("query for favorites with uncommon bool value", func(t *testing.T) {
|
||||
form := &AlbumSearch{Query: "favorites:cat"}
|
||||
form := &AlbumSearch{Query: "favorite:cat"}
|
||||
|
||||
err := form.ParseQueryString()
|
||||
|
||||
|
@ -82,7 +82,7 @@ func TestParseQueryStringAlbum(t *testing.T) {
|
|||
t.Fatal("err should be nil")
|
||||
}
|
||||
|
||||
assert.True(t, form.Favorites)
|
||||
assert.True(t, form.Favorite)
|
||||
})
|
||||
t.Run("query for count with invalid type", func(t *testing.T) {
|
||||
form := &AlbumSearch{Query: "count:cat"}
|
||||
|
|
|
@ -2,15 +2,15 @@ package form
|
|||
|
||||
// PhotoSearch represents search form fields for "/api/v1/labels".
|
||||
type LabelSearch struct {
|
||||
Query string `form:"q"`
|
||||
ID string `form:"id"`
|
||||
Slug string `form:"slug"`
|
||||
Name string `form:"name"`
|
||||
All bool `form:"all"`
|
||||
Favorites bool `form:"favorites"`
|
||||
Count int `form:"count" binding:"required"`
|
||||
Offset int `form:"offset"`
|
||||
Order string `form:"order"`
|
||||
Query string `form:"q"`
|
||||
ID string `form:"id"`
|
||||
Slug string `form:"slug"`
|
||||
Name string `form:"name"`
|
||||
All bool `form:"all"`
|
||||
Favorite bool `form:"favorite"`
|
||||
Count int `form:"count" binding:"required"`
|
||||
Offset int `form:"offset"`
|
||||
Order string `form:"order"`
|
||||
}
|
||||
|
||||
func (f *LabelSearch) GetQuery() string {
|
||||
|
|
|
@ -16,7 +16,7 @@ func TestLabelSearchForm(t *testing.T) {
|
|||
func TestParseQueryStringLabel(t *testing.T) {
|
||||
|
||||
t.Run("valid query", func(t *testing.T) {
|
||||
form := &LabelSearch{Query: "name:cat favorites:true count:10 all:false query:\"query text\""}
|
||||
form := &LabelSearch{Query: "name:cat favorite:true count:10 all:false query:\"query text\""}
|
||||
|
||||
err := form.ParseQueryString()
|
||||
|
||||
|
@ -26,13 +26,13 @@ func TestParseQueryStringLabel(t *testing.T) {
|
|||
t.Fatal("err should be nil")
|
||||
}
|
||||
assert.Equal(t, "cat", form.Name)
|
||||
assert.Equal(t, true, form.Favorites)
|
||||
assert.Equal(t, true, form.Favorite)
|
||||
assert.Equal(t, 10, form.Count)
|
||||
assert.Equal(t, false, form.All)
|
||||
assert.Equal(t, "query text", form.Query)
|
||||
})
|
||||
t.Run("valid query 2", func(t *testing.T) {
|
||||
form := &LabelSearch{Query: "slug:cat favorites:false offset:2 order:oldest"}
|
||||
form := &LabelSearch{Query: "slug:cat favorite:false offset:2 order:oldest"}
|
||||
|
||||
err := form.ParseQueryString()
|
||||
|
||||
|
@ -42,7 +42,7 @@ func TestParseQueryStringLabel(t *testing.T) {
|
|||
t.Fatal("err should be nil")
|
||||
}
|
||||
assert.Equal(t, "cat", form.Slug)
|
||||
assert.Equal(t, false, form.Favorites)
|
||||
assert.Equal(t, false, form.Favorite)
|
||||
assert.Equal(t, 2, form.Offset)
|
||||
assert.Equal(t, "oldest", form.Order)
|
||||
})
|
||||
|
@ -73,7 +73,7 @@ func TestParseQueryStringLabel(t *testing.T) {
|
|||
assert.Equal(t, "unknown filter: Xxx", err.Error())
|
||||
})
|
||||
t.Run("query for favorites with uncommon bool value", func(t *testing.T) {
|
||||
form := &LabelSearch{Query: "favorites:0.99"}
|
||||
form := &LabelSearch{Query: "favorite:0.99"}
|
||||
|
||||
err := form.ParseQueryString()
|
||||
|
||||
|
@ -81,7 +81,7 @@ func TestParseQueryStringLabel(t *testing.T) {
|
|||
t.Fatal("err should be nil")
|
||||
}
|
||||
|
||||
assert.True(t, form.Favorites)
|
||||
assert.True(t, form.Favorite)
|
||||
})
|
||||
t.Run("query for count with invalid type", func(t *testing.T) {
|
||||
form := &LabelSearch{Query: "count:2019-01-15"}
|
||||
|
|
|
@ -10,6 +10,8 @@ type PhotoSearch struct {
|
|||
ID string `form:"id"`
|
||||
Title string `form:"title"`
|
||||
Hash string `form:"hash"`
|
||||
Video bool `form:"video"`
|
||||
Photo bool `form:"photo"`
|
||||
Duplicate bool `form:"duplicate"`
|
||||
Archived bool `form:"archived"`
|
||||
Error bool `form:"error"`
|
||||
|
@ -25,24 +27,24 @@ type PhotoSearch struct {
|
|||
Location bool `form:"location"`
|
||||
Album string `form:"album"`
|
||||
Label string `form:"label"`
|
||||
Country string `form:"country"`
|
||||
Year uint `form:"year"`
|
||||
Month uint `form:"month"`
|
||||
Color string `form:"color"`
|
||||
Quality int `form:"quality"`
|
||||
Review bool `form:"review"`
|
||||
Camera int `form:"camera"`
|
||||
Lens int `form:"lens"`
|
||||
Before time.Time `form:"before" time_format:"2006-01-02"`
|
||||
After time.Time `form:"after" time_format:"2006-01-02"`
|
||||
Favorites bool `form:"favorites"`
|
||||
Public bool `form:"public"`
|
||||
Private bool `form:"private"`
|
||||
Safe bool `form:"safe"`
|
||||
Count int `form:"count" binding:"required"`
|
||||
Offset int `form:"offset"`
|
||||
Order string `form:"order"`
|
||||
Merged bool `form:"merged"`
|
||||
Country string `form:"country"`
|
||||
Year uint `form:"year"`
|
||||
Month uint `form:"month"`
|
||||
Color string `form:"color"`
|
||||
Quality int `form:"quality"`
|
||||
Review bool `form:"review"`
|
||||
Camera int `form:"camera"`
|
||||
Lens int `form:"lens"`
|
||||
Before time.Time `form:"before" time_format:"2006-01-02"`
|
||||
After time.Time `form:"after" time_format:"2006-01-02"`
|
||||
Favorite bool `form:"favorite"`
|
||||
Public bool `form:"public"`
|
||||
Private bool `form:"private"`
|
||||
Safe bool `form:"safe"`
|
||||
Count int `form:"count" binding:"required"`
|
||||
Offset int `form:"offset"`
|
||||
Order string `form:"order"`
|
||||
Merged bool `form:"merged"`
|
||||
}
|
||||
|
||||
func (f *PhotoSearch) GetQuery() string {
|
||||
|
|
|
@ -18,7 +18,7 @@ func TestPhotoSearchForm(t *testing.T) {
|
|||
func TestParseQueryString(t *testing.T) {
|
||||
|
||||
t.Run("valid query", func(t *testing.T) {
|
||||
form := &PhotoSearch{Query: "label:cat query:\"fooBar baz\" before:2019-01-15 camera:23 favorites:false dist:25000 lat:33.45343166666667"}
|
||||
form := &PhotoSearch{Query: "label:cat query:\"fooBar baz\" before:2019-01-15 camera:23 favorite:false dist:25000 lat:33.45343166666667"}
|
||||
|
||||
err := form.ParseQueryString()
|
||||
|
||||
|
@ -32,12 +32,12 @@ func TestParseQueryString(t *testing.T) {
|
|||
assert.Equal(t, "foobar baz", form.Query)
|
||||
assert.Equal(t, 23, form.Camera)
|
||||
assert.Equal(t, time.Date(2019, 01, 15, 0, 0, 0, 0, time.UTC), form.Before)
|
||||
assert.Equal(t, false, form.Favorites)
|
||||
assert.Equal(t, false, form.Favorite)
|
||||
assert.Equal(t, uint(0x61a8), form.Dist)
|
||||
assert.Equal(t, float32(33.45343), form.Lat)
|
||||
})
|
||||
t.Run("valid query 2", func(t *testing.T) {
|
||||
form := &PhotoSearch{Query: "chroma:200 title:\"test\" after:2018-01-15 duplicate:false favorites:true lng:33.45343166666667"}
|
||||
form := &PhotoSearch{Query: "chroma:200 title:\"test\" after:2018-01-15 duplicate:false favorite:true lng:33.45343166666667"}
|
||||
|
||||
err := form.ParseQueryString()
|
||||
|
||||
|
@ -80,7 +80,7 @@ func TestParseQueryString(t *testing.T) {
|
|||
assert.Equal(t, "unknown filter: Xxx", err.Error())
|
||||
})
|
||||
t.Run("query for favorites with uncommon bool value", func(t *testing.T) {
|
||||
form := &PhotoSearch{Query: "favorites:cat"}
|
||||
form := &PhotoSearch{Query: "favorite:cat"}
|
||||
|
||||
err := form.ParseQueryString()
|
||||
|
||||
|
@ -88,7 +88,7 @@ func TestParseQueryString(t *testing.T) {
|
|||
t.Fatal("err should NOT be nil")
|
||||
}
|
||||
|
||||
assert.True(t, form.Favorites)
|
||||
assert.True(t, form.Favorite)
|
||||
})
|
||||
t.Run("query for lat with invalid type", func(t *testing.T) {
|
||||
form := &PhotoSearch{Query: "lat:cat"}
|
||||
|
|
|
@ -357,6 +357,12 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
|
|||
})
|
||||
}
|
||||
|
||||
if photo.PhotoVideo {
|
||||
event.Publish("count.videos", event.Data{
|
||||
"count": 1,
|
||||
})
|
||||
}
|
||||
|
||||
event.EntitiesCreated("photos", []entity.Photo{photo})
|
||||
}
|
||||
|
||||
|
|
|
@ -556,6 +556,11 @@ func (m *MediaFile) IsVideo() bool {
|
|||
return m.MediaType() == fs.MediaVideo
|
||||
}
|
||||
|
||||
// IsPlayableVideo returns true if this is a supported video file format.
|
||||
func (m *MediaFile) IsPlayableVideo() bool {
|
||||
return m.MediaType() == fs.MediaVideo && m.HasFileType(fs.TypeMP4)
|
||||
}
|
||||
|
||||
// IsPhoto returns true if this file is a photo / image.
|
||||
func (m *MediaFile) IsPhoto() bool {
|
||||
return m.IsJpeg() || m.IsRaw() || m.IsHEIF() || m.IsImageOther()
|
||||
|
|
|
@ -85,7 +85,7 @@ func Albums(f form.AlbumSearch) (results []AlbumResult, err error) {
|
|||
s = s.Where("LOWER(albums.album_name) LIKE ?", likeString)
|
||||
}
|
||||
|
||||
if f.Favorites {
|
||||
if f.Favorite {
|
||||
s = s.Where("albums.album_favorite = 1")
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ func TestAlbums(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("favorites true", func(t *testing.T) {
|
||||
query := form.NewAlbumSearch("favorites:true count:10000")
|
||||
query := form.NewAlbumSearch("favorite:true count:10000")
|
||||
|
||||
result, err := Albums(query)
|
||||
|
||||
|
@ -102,14 +102,14 @@ func TestAlbums(t *testing.T) {
|
|||
})
|
||||
t.Run("search for existing ID", func(t *testing.T) {
|
||||
f := form.AlbumSearch{
|
||||
Query: "",
|
||||
ID: "at9lxuqxpogaaba7",
|
||||
Slug: "",
|
||||
Name: "",
|
||||
Favorites: false,
|
||||
Count: 0,
|
||||
Offset: 0,
|
||||
Order: "",
|
||||
Query: "",
|
||||
ID: "at9lxuqxpogaaba7",
|
||||
Slug: "",
|
||||
Name: "",
|
||||
Favorite: false,
|
||||
Count: 0,
|
||||
Offset: 0,
|
||||
Order: "",
|
||||
}
|
||||
|
||||
result, err := Albums(f)
|
||||
|
|
|
@ -4,11 +4,12 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestFileShares(t *testing.T) {
|
||||
t.Run("search for id and status", func(t *testing.T) {
|
||||
r, err := FileShares(uint(1000001), "test")
|
||||
r, err := FileShares(uint(1000001), "new")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -21,21 +22,18 @@ func TestFileShares(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestExpiredFileShares(t *testing.T) {
|
||||
//TODO Find way to not overwrite updated at in test db
|
||||
/*t.Run("expired file share exists", func(t *testing.T) {
|
||||
t.Log(entity.AccountFixtureWebdavDummy.ShareExpires)
|
||||
time.Sleep(10 * time.Second)
|
||||
t.Run("expired file share exists", func(t *testing.T) {
|
||||
time.Sleep(2 * time.Second)
|
||||
r, err := ExpiredFileShares(entity.AccountFixtureWebdavDummy)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("%+v", r)
|
||||
|
||||
assert.LessOrEqual(t, 1, len(r))
|
||||
for _, r := range r {
|
||||
assert.IsType(t, entity.FileShare{}, r)
|
||||
}
|
||||
})*/
|
||||
})
|
||||
t.Run("expired file does not exists", func(t *testing.T) {
|
||||
r, err := ExpiredFileShares(entity.AccountFixtureWebdavDummy2)
|
||||
if err != nil {
|
||||
|
|
|
@ -27,7 +27,7 @@ func TestGeo(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, "Neckarbrücke", result[0].PhotoTitle)
|
||||
assert.LessOrEqual(t, 2, len(result))
|
||||
|
||||
})
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ func Labels(f form.LabelSearch) (results []LabelResult, err error) {
|
|||
}
|
||||
}
|
||||
|
||||
if f.Favorites {
|
||||
if f.Favorite {
|
||||
s = s.Where("labels.label_favorite = 1")
|
||||
}
|
||||
|
||||
|
|
|
@ -35,8 +35,34 @@ func TestLabels(t *testing.T) {
|
|||
}
|
||||
})
|
||||
|
||||
t.Run("search for cow", func(t *testing.T) {
|
||||
query := form.NewLabelSearch("Query:cow Count:1005 Order:slug")
|
||||
result, err := Labels(query)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Logf("results: %+v", result)
|
||||
|
||||
assert.LessOrEqual(t, 1, len(result))
|
||||
|
||||
for _, r := range result {
|
||||
assert.IsType(t, LabelResult{}, r)
|
||||
assert.NotEmpty(t, r.ID)
|
||||
assert.NotEmpty(t, r.LabelName)
|
||||
assert.NotEmpty(t, r.LabelSlug)
|
||||
assert.NotEmpty(t, r.CustomSlug)
|
||||
|
||||
if fix, ok := entity.LabelFixtures[r.LabelSlug]; ok {
|
||||
assert.Equal(t, fix.LabelName, r.LabelName)
|
||||
assert.Equal(t, fix.LabelSlug, r.LabelSlug)
|
||||
assert.Equal(t, fix.CustomSlug, r.CustomSlug)
|
||||
}
|
||||
}
|
||||
})
|
||||
t.Run("search for favorites", func(t *testing.T) {
|
||||
query := form.NewLabelSearch("Favorites:true Count:15")
|
||||
query := form.NewLabelSearch("Favorite:true Count:15")
|
||||
result, err := Labels(query)
|
||||
|
||||
if err != nil {
|
||||
|
@ -83,15 +109,15 @@ func TestLabels(t *testing.T) {
|
|||
|
||||
t.Run("search for ID", func(t *testing.T) {
|
||||
f := form.LabelSearch{
|
||||
Query: "",
|
||||
ID: "lt9k3pw1wowuy3c4",
|
||||
Slug: "",
|
||||
Name: "",
|
||||
All: false,
|
||||
Favorites: false,
|
||||
Count: 0,
|
||||
Offset: 0,
|
||||
Order: "",
|
||||
Query: "",
|
||||
ID: "lt9k3pw1wowuy3c4",
|
||||
Slug: "",
|
||||
Name: "",
|
||||
All: false,
|
||||
Favorite: false,
|
||||
Count: 0,
|
||||
Offset: 0,
|
||||
Order: "",
|
||||
}
|
||||
|
||||
result, err := Labels(f)
|
||||
|
|
|
@ -24,6 +24,7 @@ func Photos(f form.PhotoSearch) (results PhotosResults, count int, err error) {
|
|||
|
||||
// s.LogMode(true)
|
||||
|
||||
// Main search query, avoids (slow) left joins.
|
||||
s = s.Table("photos").
|
||||
Select(`photos.*,
|
||||
files.id AS file_id, files.file_uuid, files.file_primary, files.file_missing, files.file_name, files.file_hash,
|
||||
|
@ -33,12 +34,12 @@ func Photos(f form.PhotoSearch) (results PhotosResults, count int, err error) {
|
|||
cameras.camera_make, cameras.camera_model,
|
||||
lenses.lens_make, lenses.lens_model,
|
||||
places.loc_label, places.loc_city, places.loc_state, places.loc_country`).
|
||||
Joins("JOIN files ON files.photo_id = photos.id AND files.file_missing = 0 AND files.deleted_at IS NULL AND (files.file_type = 'jpg' OR files.file_video)").
|
||||
Joins("JOIN cameras ON cameras.id = photos.camera_id").
|
||||
Joins("JOIN lenses ON lenses.id = photos.lens_id").
|
||||
Joins("JOIN places ON photos.place_id = places.id").
|
||||
Group("photos.id, files.id")
|
||||
Joins("JOIN files ON photos.id = files.photo_id AND files.file_missing = 0 AND files.deleted_at IS NULL").
|
||||
Joins("JOIN cameras ON photos.camera_id = cameras.id").
|
||||
Joins("JOIN lenses ON photos.lens_id = lenses.id").
|
||||
Joins("JOIN places ON photos.place_id = places.id")
|
||||
|
||||
// Shortcut for known photo ids.
|
||||
if f.ID != "" {
|
||||
s = s.Where("photos.photo_uuid = ?", f.ID)
|
||||
s = s.Order("files.file_primary DESC")
|
||||
|
@ -54,6 +55,16 @@ func Photos(f form.PhotoSearch) (results PhotosResults, count int, err error) {
|
|||
return results, len(results), nil
|
||||
}
|
||||
|
||||
// Filter by media type.
|
||||
if f.Video {
|
||||
s = s.Where("(files.file_type = 'jpg' OR files.file_video = 1) AND photos.photo_video = 1")
|
||||
} else if f.Photo {
|
||||
s = s.Where("files.file_type = 'jpg' AND photos.photo_video = 1")
|
||||
} else {
|
||||
s = s.Where("(files.file_type = 'jpg' OR files.file_video = 1)")
|
||||
}
|
||||
|
||||
// Filter by label, label category and keywords.
|
||||
var categories []entity.Category
|
||||
var label entity.Label
|
||||
var labels []entity.Label
|
||||
|
@ -77,6 +88,7 @@ func Photos(f form.PhotoSearch) (results PhotosResults, count int, err error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Filter by location.
|
||||
if f.Location == true {
|
||||
s = s.Where("location_id > 0")
|
||||
|
||||
|
@ -116,6 +128,7 @@ func Photos(f form.PhotoSearch) (results PhotosResults, count int, err error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Filter by status.
|
||||
if f.Archived {
|
||||
s = s.Where("photos.deleted_at IS NOT NULL")
|
||||
} else {
|
||||
|
@ -134,6 +147,7 @@ func Photos(f form.PhotoSearch) (results PhotosResults, count int, err error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Filter by additional flags and metadata.
|
||||
if f.Error {
|
||||
s = s.Where("files.file_error <> ''")
|
||||
}
|
||||
|
@ -162,7 +176,7 @@ func Photos(f form.PhotoSearch) (results PhotosResults, count int, err error) {
|
|||
s = s.Where("files.file_main_color = ?", strings.ToLower(f.Color))
|
||||
}
|
||||
|
||||
if f.Favorites {
|
||||
if f.Favorite {
|
||||
s = s.Where("photos.photo_favorite = 1")
|
||||
}
|
||||
|
||||
|
@ -212,7 +226,7 @@ func Photos(f form.PhotoSearch) (results PhotosResults, count int, err error) {
|
|||
f.Dist = 5000
|
||||
}
|
||||
|
||||
// Inaccurate distance search, but probably 'good enough' for now
|
||||
// Filter by distance (approximation).
|
||||
if f.Lat > 0 {
|
||||
latMin := f.Lat - SearchRadius*float32(f.Dist)
|
||||
latMax := f.Lat + SearchRadius*float32(f.Dist)
|
||||
|
@ -233,6 +247,7 @@ func Photos(f form.PhotoSearch) (results PhotosResults, count int, err error) {
|
|||
s = s.Where("photos.taken_at >= ?", f.After.Format("2006-01-02"))
|
||||
}
|
||||
|
||||
// Set sort order for results.
|
||||
switch f.Order {
|
||||
case entity.SortOrderRelevance:
|
||||
if f.Label != "" {
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
)
|
||||
|
||||
func TestPhotos(t *testing.T) {
|
||||
t.Run("normal query", func(t *testing.T) {
|
||||
t.Run("search all", func(t *testing.T) {
|
||||
var f form.PhotoSearch
|
||||
f.Query = ""
|
||||
f.Count = 10
|
||||
|
@ -35,6 +35,36 @@ func TestPhotos(t *testing.T) {
|
|||
}
|
||||
}
|
||||
})
|
||||
t.Run("search for ID and merged", func(t *testing.T) {
|
||||
var f form.PhotoSearch
|
||||
f.Query = ""
|
||||
f.Count = 5000
|
||||
f.Offset = 0
|
||||
f.ID = "pt9jtdre2lvl0yh7"
|
||||
f.Merged = true
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.LessOrEqual(t, 1, len(photos))
|
||||
})
|
||||
t.Run("search for ID with merged false", func(t *testing.T) {
|
||||
var f form.PhotoSearch
|
||||
f.Query = ""
|
||||
f.Count = 5000
|
||||
f.Offset = 0
|
||||
f.ID = "pt9jtdre2lvl0yh7"
|
||||
f.Merged = false
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.LessOrEqual(t, 1, len(photos))
|
||||
})
|
||||
t.Run("label query dog", func(t *testing.T) {
|
||||
var f form.PhotoSearch
|
||||
f.Query = "label:dog"
|
||||
|
@ -45,7 +75,6 @@ func TestPhotos(t *testing.T) {
|
|||
|
||||
assert.Equal(t, "label dog not found", err.Error())
|
||||
assert.Empty(t, photos)
|
||||
//t.Logf("results: %+v", photos)
|
||||
})
|
||||
t.Run("label query landscape", func(t *testing.T) {
|
||||
var f form.PhotoSearch
|
||||
|
@ -89,7 +118,143 @@ func TestPhotos(t *testing.T) {
|
|||
}
|
||||
|
||||
assert.LessOrEqual(t, 3, len(photos))
|
||||
})
|
||||
t.Run("form.location true and keyword", func(t *testing.T) {
|
||||
var f form.PhotoSearch
|
||||
f.Query = "bridge"
|
||||
f.Count = 10
|
||||
f.Offset = 0
|
||||
f.Location = true
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.LessOrEqual(t, 1, len(photos))
|
||||
})
|
||||
t.Run("query too short", func(t *testing.T) {
|
||||
var f form.PhotoSearch
|
||||
f.Query = "a"
|
||||
f.Count = 5000
|
||||
f.Offset = 0
|
||||
f.Location = false
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
|
||||
assert.Equal(t, "query too short", err.Error())
|
||||
assert.Empty(t, photos)
|
||||
})
|
||||
t.Run("search for keyword", func(t *testing.T) {
|
||||
var f form.PhotoSearch
|
||||
f.Query = "bridge"
|
||||
f.Count = 5000
|
||||
f.Offset = 0
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.LessOrEqual(t, 2, len(photos))
|
||||
})
|
||||
t.Run("search for label in query", func(t *testing.T) {
|
||||
var f form.PhotoSearch
|
||||
f.Query = "flower"
|
||||
f.Count = 5000
|
||||
f.Offset = 0
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.LessOrEqual(t, 1, len(photos))
|
||||
})
|
||||
t.Run("search for archived", func(t *testing.T) {
|
||||
|
||||
var f form.PhotoSearch
|
||||
f.Query = ""
|
||||
f.Count = 5000
|
||||
f.Offset = 0
|
||||
f.Archived = true
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.LessOrEqual(t, 1, len(photos))
|
||||
})
|
||||
t.Run("search for private", func(t *testing.T) {
|
||||
var f form.PhotoSearch
|
||||
f.Query = ""
|
||||
f.Count = 5000
|
||||
f.Offset = 0
|
||||
f.Private = true
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.LessOrEqual(t, 1, len(photos))
|
||||
})
|
||||
t.Run("search for public", func(t *testing.T) {
|
||||
var f form.PhotoSearch
|
||||
f.Query = ""
|
||||
f.Count = 5000
|
||||
f.Offset = 0
|
||||
f.Public = true
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.LessOrEqual(t, 3, len(photos))
|
||||
})
|
||||
t.Run("search for review", func(t *testing.T) {
|
||||
var f form.PhotoSearch
|
||||
f.Query = ""
|
||||
f.Count = 5000
|
||||
f.Offset = 0
|
||||
f.Review = true
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.LessOrEqual(t, 1, len(photos))
|
||||
})
|
||||
t.Run("search for quality", func(t *testing.T) {
|
||||
var f form.PhotoSearch
|
||||
f.Query = ""
|
||||
f.Count = 5000
|
||||
f.Offset = 0
|
||||
f.Quality = 4
|
||||
f.Private = false
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.LessOrEqual(t, 1, len(photos))
|
||||
})
|
||||
t.Run("search for file error", func(t *testing.T) {
|
||||
var f form.PhotoSearch
|
||||
f.Query = ""
|
||||
f.Count = 5000
|
||||
f.Offset = 0
|
||||
f.Error = true
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.LessOrEqual(t, 1, len(photos))
|
||||
})
|
||||
t.Run("form.camera", func(t *testing.T) {
|
||||
var f form.PhotoSearch
|
||||
|
@ -123,7 +288,7 @@ func TestPhotos(t *testing.T) {
|
|||
})
|
||||
t.Run("form.favorites", func(t *testing.T) {
|
||||
var f form.PhotoSearch
|
||||
f.Query = "favorites:true"
|
||||
f.Query = "favorite:true"
|
||||
f.Count = 10
|
||||
f.Offset = 0
|
||||
|
||||
|
@ -213,9 +378,10 @@ func TestPhotos(t *testing.T) {
|
|||
})
|
||||
t.Run("form.mono", func(t *testing.T) {
|
||||
var f form.PhotoSearch
|
||||
f.Query = "mono:false"
|
||||
f.Query = "mono:true"
|
||||
f.Count = 10
|
||||
f.Offset = 0
|
||||
f.Archived = true
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
|
||||
|
@ -223,7 +389,7 @@ func TestPhotos(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.LessOrEqual(t, 4, len(photos))
|
||||
assert.LessOrEqual(t, 1, len(photos))
|
||||
})
|
||||
t.Run("form.chroma >9 Order:similar", func(t *testing.T) {
|
||||
var f form.PhotoSearch
|
||||
|
@ -352,53 +518,6 @@ func TestPhotos(t *testing.T) {
|
|||
|
||||
photos, _, err := Photos(f)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.LessOrEqual(t, 1, len(photos))
|
||||
})
|
||||
t.Run("search for private, archived, review", func(t *testing.T) {
|
||||
var f form.PhotoSearch
|
||||
f.Query = ""
|
||||
f.Count = 5000
|
||||
f.Offset = 0
|
||||
f.Private = true
|
||||
f.Archived = true
|
||||
f.Review = true
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Empty(t, photos)
|
||||
})
|
||||
t.Run("search for archived and public", func(t *testing.T) {
|
||||
var f form.PhotoSearch
|
||||
f.Query = ""
|
||||
f.Count = 5000
|
||||
f.Offset = 0
|
||||
f.Archived = true
|
||||
f.Public = true
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Empty(t, photos)
|
||||
//TODO create test fixture
|
||||
})
|
||||
t.Run("search for ID", func(t *testing.T) {
|
||||
var f form.PhotoSearch
|
||||
f.Query = ""
|
||||
f.Count = 5000
|
||||
f.Offset = 0
|
||||
f.ID = "pt9jtdre2lvl0yh7"
|
||||
f.Merged = true
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -10,37 +10,130 @@ import (
|
|||
)
|
||||
|
||||
func TestResampleOptions(t *testing.T) {
|
||||
method, filter, format := ResampleOptions(ResamplePng, ResampleFillCenter, ResampleDefault)
|
||||
t.Run("ResamplePng, FillCenter", func(t *testing.T) {
|
||||
method, filter, format := ResampleOptions(ResamplePng, ResampleFillCenter, ResampleDefault)
|
||||
|
||||
assert.Equal(t, ResampleFillCenter, method)
|
||||
assert.Equal(t, imaging.Lanczos.Support, filter.Support)
|
||||
assert.Equal(t, fs.TypePng, format)
|
||||
assert.Equal(t, ResampleFillCenter, method)
|
||||
assert.Equal(t, imaging.Lanczos.Support, filter.Support)
|
||||
assert.Equal(t, fs.TypePng, 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.Support)
|
||||
assert.Equal(t, fs.TypeJpeg, 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.Support)
|
||||
assert.Equal(t, fs.TypeJpeg, format)
|
||||
})
|
||||
}
|
||||
|
||||
func TestResample(t *testing.T) {
|
||||
tile50 := Types["tile_50"]
|
||||
t.Run("tile50 options", func(t *testing.T) {
|
||||
tile50 := Types["tile_50"]
|
||||
|
||||
src := "testdata/example.jpg"
|
||||
src := "testdata/example.jpg"
|
||||
|
||||
assert.FileExists(t, src)
|
||||
assert.FileExists(t, src)
|
||||
|
||||
img, err := imaging.Open(src, imaging.AutoOrientation(true))
|
||||
img, err := imaging.Open(src, imaging.AutoOrientation(true))
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
bounds := img.Bounds()
|
||||
bounds := img.Bounds()
|
||||
|
||||
assert.Equal(t, 750, bounds.Max.X)
|
||||
assert.Equal(t, 500, bounds.Max.Y)
|
||||
assert.Equal(t, 750, bounds.Max.X)
|
||||
assert.Equal(t, 500, bounds.Max.Y)
|
||||
|
||||
result := *Resample(&img, tile50.Width, tile50.Height, tile50.Options...)
|
||||
result := *Resample(&img, tile50.Width, tile50.Height, tile50.Options...)
|
||||
|
||||
boundsNew := result.Bounds()
|
||||
boundsNew := result.Bounds()
|
||||
|
||||
assert.Equal(t, 50, boundsNew.Max.X)
|
||||
assert.Equal(t, 50, boundsNew.Max.Y)
|
||||
assert.Equal(t, 50, boundsNew.Max.X)
|
||||
assert.Equal(t, 50, boundsNew.Max.Y)
|
||||
})
|
||||
t.Run("left_224 options", func(t *testing.T) {
|
||||
left_224 := Types["left_224"]
|
||||
|
||||
src := "testdata/example.jpg"
|
||||
|
||||
assert.FileExists(t, src)
|
||||
|
||||
img, err := imaging.Open(src, imaging.AutoOrientation(true))
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
bounds := img.Bounds()
|
||||
|
||||
assert.Equal(t, 750, bounds.Max.X)
|
||||
assert.Equal(t, 500, bounds.Max.Y)
|
||||
|
||||
result := *Resample(&img, left_224.Width, left_224.Height, left_224.Options...)
|
||||
|
||||
boundsNew := result.Bounds()
|
||||
|
||||
assert.Equal(t, 224, boundsNew.Max.X)
|
||||
assert.Equal(t, 224, boundsNew.Max.Y)
|
||||
})
|
||||
t.Run("right_224 options", func(t *testing.T) {
|
||||
right_224 := Types["right_224"]
|
||||
|
||||
src := "testdata/example.jpg"
|
||||
|
||||
assert.FileExists(t, src)
|
||||
|
||||
img, err := imaging.Open(src, imaging.AutoOrientation(true))
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
bounds := img.Bounds()
|
||||
|
||||
assert.Equal(t, 750, bounds.Max.X)
|
||||
assert.Equal(t, 500, bounds.Max.Y)
|
||||
|
||||
result := *Resample(&img, right_224.Width, right_224.Height, right_224.Options...)
|
||||
|
||||
boundsNew := result.Bounds()
|
||||
|
||||
assert.Equal(t, 224, boundsNew.Max.X)
|
||||
assert.Equal(t, 224, boundsNew.Max.Y)
|
||||
})
|
||||
t.Run("fit_1280 options", func(t *testing.T) {
|
||||
fit_1280 := Types["fit_1280"]
|
||||
|
||||
src := "testdata/example.jpg"
|
||||
|
||||
assert.FileExists(t, src)
|
||||
|
||||
img, err := imaging.Open(src, imaging.AutoOrientation(true))
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
bounds := img.Bounds()
|
||||
|
||||
assert.Equal(t, 750, bounds.Max.X)
|
||||
assert.Equal(t, 500, bounds.Max.Y)
|
||||
|
||||
result := *Resample(&img, fit_1280.Width, fit_1280.Height, fit_1280.Options...)
|
||||
|
||||
boundsNew := result.Bounds()
|
||||
|
||||
assert.Equal(t, 750, boundsNew.Max.X)
|
||||
assert.Equal(t, 500, boundsNew.Max.Y)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPostfix(t *testing.T) {
|
||||
|
@ -75,6 +168,38 @@ func TestFilename(t *testing.T) {
|
|||
|
||||
assert.Equal(t, "testdata/1/2/3/123456789098765432_720x720_fit.jpg", result)
|
||||
})
|
||||
t.Run("invalid width", func(t *testing.T) {
|
||||
colorThumb := Types["colors"]
|
||||
|
||||
result, err := Filename("123456789098765432", "testdata", -2, colorThumb.Height, colorThumb.Options...)
|
||||
|
||||
assert.Equal(t, "resample: width exceeds limit (-2)", err.Error())
|
||||
assert.Empty(t, result)
|
||||
})
|
||||
t.Run("invalid height", func(t *testing.T) {
|
||||
colorThumb := Types["colors"]
|
||||
|
||||
result, err := Filename("123456789098765432", "testdata", colorThumb.Width, -3, colorThumb.Options...)
|
||||
|
||||
assert.Equal(t, "resample: height exceeds limit (-3)", err.Error())
|
||||
assert.Empty(t, result)
|
||||
})
|
||||
t.Run("invalid hash", func(t *testing.T) {
|
||||
colorThumb := Types["colors"]
|
||||
|
||||
result, err := Filename("12", "testdata", colorThumb.Width, colorThumb.Height, colorThumb.Options...)
|
||||
|
||||
assert.Equal(t, "resample: file hash is empty or too short (12)", err.Error())
|
||||
assert.Empty(t, result)
|
||||
})
|
||||
t.Run("invalid thumb path", func(t *testing.T) {
|
||||
colorThumb := Types["colors"]
|
||||
|
||||
result, err := Filename("123456789098765432", "", colorThumb.Width, colorThumb.Height, colorThumb.Options...)
|
||||
|
||||
assert.Equal(t, "resample: folder is empty", err.Error())
|
||||
assert.Empty(t, result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestFromFile(t *testing.T) {
|
||||
|
@ -107,6 +232,14 @@ func TestFromFile(t *testing.T) {
|
|||
assert.Equal(t, "", fileName)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
t.Run("empty filename", func(t *testing.T) {
|
||||
colorThumb := Types["colors"]
|
||||
|
||||
fileName, err := FromFile("", "193456789098765432", "testdata", colorThumb.Width, colorThumb.Height, colorThumb.Options...)
|
||||
|
||||
assert.Equal(t, "", fileName)
|
||||
assert.Equal(t, "resample: image filename is empty or too short ()", err.Error())
|
||||
})
|
||||
}
|
||||
|
||||
func TestFromCache(t *testing.T) {
|
||||
|
@ -136,6 +269,25 @@ func TestFromCache(t *testing.T) {
|
|||
assert.Equal(t, "", fileName)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
t.Run("invalid hash", func(t *testing.T) {
|
||||
tile50 := Types["tile_50"]
|
||||
src := "testdata/example.jpg"
|
||||
|
||||
assert.FileExists(t, src)
|
||||
|
||||
fileName, err := FromCache(src, "12", "testdata", tile50.Width, tile50.Height, tile50.Options...)
|
||||
|
||||
assert.Equal(t, "resample: file hash is empty or too short (12)", err.Error())
|
||||
assert.Empty(t, fileName)
|
||||
})
|
||||
t.Run("empty filename", func(t *testing.T) {
|
||||
tile50 := Types["tile_50"]
|
||||
|
||||
fileName, err := FromCache("", "193456789098765432", "testdata", tile50.Width, tile50.Height, tile50.Options...)
|
||||
|
||||
assert.Equal(t, "resample: image filename is empty or too short ()", err.Error())
|
||||
assert.Empty(t, fileName)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
|
@ -176,4 +328,52 @@ func TestCreate(t *testing.T) {
|
|||
assert.Equal(t, 500, boundsNew.Max.X)
|
||||
assert.Equal(t, 500, boundsNew.Max.Y)
|
||||
})
|
||||
t.Run("invalid width", func(t *testing.T) {
|
||||
tile500 := Types["tile_500"]
|
||||
src := "testdata/example.jpg"
|
||||
dst := "testdata/example.tile_500.jpg"
|
||||
|
||||
assert.FileExists(t, src)
|
||||
assert.NoFileExists(t, dst)
|
||||
|
||||
img, err := imaging.Open(src, imaging.AutoOrientation(true))
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
bounds := img.Bounds()
|
||||
|
||||
assert.Equal(t, 750, bounds.Max.X)
|
||||
assert.Equal(t, 500, bounds.Max.Y)
|
||||
|
||||
resized, err := Create(&img, dst, -5, tile500.Height, tile500.Options...)
|
||||
|
||||
assert.Equal(t, "resample: width has an invalid value (-5)", err.Error())
|
||||
t.Log(resized)
|
||||
})
|
||||
t.Run("invalid height", func(t *testing.T) {
|
||||
tile500 := Types["tile_500"]
|
||||
src := "testdata/example.jpg"
|
||||
dst := "testdata/example.tile_500.jpg"
|
||||
|
||||
assert.FileExists(t, src)
|
||||
assert.NoFileExists(t, dst)
|
||||
|
||||
img, err := imaging.Open(src, imaging.AutoOrientation(true))
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
bounds := img.Bounds()
|
||||
|
||||
assert.Equal(t, 750, bounds.Max.X)
|
||||
assert.Equal(t, 500, bounds.Max.Y)
|
||||
|
||||
resized, err := Create(&img, dst, tile500.Width, -3, tile500.Options...)
|
||||
|
||||
assert.Equal(t, "resample: height has an invalid value (-3)", err.Error())
|
||||
t.Log(resized)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -39,3 +39,18 @@ func TestType_SkipPreRender(t *testing.T) {
|
|||
Size = 3840
|
||||
Limit = 3840
|
||||
}
|
||||
|
||||
func TestResampleFilter_Imaging(t *testing.T) {
|
||||
t.Run("Blackman", func(t *testing.T) {
|
||||
r := ResampleBlackman.Imaging()
|
||||
assert.Equal(t, float64(3), r.Support)
|
||||
})
|
||||
t.Run("Cubic", func(t *testing.T) {
|
||||
r := ResampleCubic.Imaging()
|
||||
assert.Equal(t, float64(2), r.Support)
|
||||
})
|
||||
t.Run("Linear", func(t *testing.T) {
|
||||
r := ResampleLinear.Imaging()
|
||||
assert.Equal(t, float64(1), r.Support)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
)
|
||||
|
||||
func Dirs(root string, recursive bool) (result []string, err error) {
|
||||
result = []string{}
|
||||
ignore := NewIgnoreList(".ppignore", true, false)
|
||||
mutex := sync.Mutex{}
|
||||
|
||||
|
|
Loading…
Reference in a new issue