From e7fecd3b276b09ac66cb162f0b232fa3e9a0d7a0 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 27 Jun 2020 13:08:45 +0200 Subject: [PATCH] Improve album sharing and album UX #18 #309 Signed-off-by: Michael Mayer --- frontend/src/common/viewer.js | 2 +- frontend/src/dialog/account/edit.vue | 6 ++--- frontend/src/dialog/p-share-dialog.vue | 9 ++++--- frontend/src/model/account.js | 2 +- frontend/src/model/album.js | 4 --- frontend/src/model/file.js | 4 --- frontend/src/model/folder.js | 4 --- frontend/src/model/label.js | 4 --- frontend/src/model/link.js | 29 +++++++++++++++------ frontend/src/model/photo.js | 4 --- frontend/src/model/rest.js | 13 ++++++++-- frontend/src/model/user.js | 4 --- frontend/src/pages/album/photos.vue | 34 ++++++++++++++++++++++-- frontend/src/pages/photos.vue | 36 +++++++++++++++++++++++++- frontend/src/share/photos.vue | 34 ++++++++++++++++++++++-- internal/api/link.go | 8 +++--- internal/api/link_test.go | 18 ++++++------- internal/api/share.go | 17 ++++++++---- internal/entity/link.go | 26 ++++++++++++++----- internal/form/link.go | 5 ++-- pkg/txt/resources/stopwords.txt | 12 +++++++++ pkg/txt/stopwords.go | 12 +++++++++ 22 files changed, 213 insertions(+), 74 deletions(-) diff --git a/frontend/src/common/viewer.js b/frontend/src/common/viewer.js index 94a9facba..9df9fa42f 100644 --- a/frontend/src/common/viewer.js +++ b/frontend/src/common/viewer.js @@ -57,7 +57,7 @@ class Viewer { show(items, index = 0) { if (!Array.isArray(items) || items.length === 0 || index >= items.length) { - console.log("Array passed to gallery was empty:", items); + console.log("photo list passed to gallery was empty:", items); return; } diff --git a/frontend/src/dialog/account/edit.vue b/frontend/src/dialog/account/edit.vue index 229123ccd..c91a55c60 100644 --- a/frontend/src/dialog/account/edit.vue +++ b/frontend/src/dialog/account/edit.vue @@ -103,13 +103,13 @@ @@ -346,7 +346,7 @@ AccType: this.$gettext("Type"), SharePath: this.$gettext("Default Folder"), ShareSize: this.$gettext("Size"), - ShareExpires: this.$gettext("Expires"), + Expires: this.$gettext("Expires"), SyncPath: this.$gettext("Folder"), SyncInterval: this.$gettext("Interval"), SyncFilenames: this.$gettext("Preserve filenames"), diff --git a/frontend/src/dialog/p-share-dialog.vue b/frontend/src/dialog/p-share-dialog.vue index 13baca15f..effc46050 100644 --- a/frontend/src/dialog/p-share-dialog.vue +++ b/frontend/src/dialog/p-share-dialog.vue @@ -20,7 +20,8 @@ @@ -46,7 +47,7 @@ color="secondary-dark" item-text="text" item-value="value" - v-model="link.ShareExpires" + v-model="link.Expires" :items="items.expires" class="input-expires" > @@ -59,7 +60,7 @@ :label="$gettext('Secret')" :placeholder="$gettext('Token')" color="secondary-dark" - v-model="link.ShareToken" + v-model="link.Token" class="input-secret" > @@ -171,7 +172,7 @@ expires(link) { let result = this.$gettext('Expires'); - if (link.ShareExpires <= 0) { + if (link.Expires <= 0) { return result } diff --git a/frontend/src/model/account.js b/frontend/src/model/account.js index 7fe04eb16..01254b892 100644 --- a/frontend/src/model/account.js +++ b/frontend/src/model/account.js @@ -49,7 +49,7 @@ export class Account extends RestModel { RetryLimit: 3, SharePath: "/", ShareSize: "", - ShareExpires: 0, + Expires: 0, SyncPath: "/", SyncStatus: "", SyncInterval: 86400, diff --git a/frontend/src/model/album.js b/frontend/src/model/album.js index 06786df4c..23b3d9103 100644 --- a/frontend/src/model/album.js +++ b/frontend/src/model/album.js @@ -66,10 +66,6 @@ export class Album extends RestModel { return this.Slug; } - getId() { - return this.UID; - } - getTitle() { return this.Title; } diff --git a/frontend/src/model/file.js b/frontend/src/model/file.js index 0bfcd38f4..fecf02aa5 100644 --- a/frontend/src/model/file.js +++ b/frontend/src/model/file.js @@ -98,10 +98,6 @@ export class File extends RestModel { return this.Root + "/" + this.Name; } - getId() { - return this.UID; - } - thumbnailUrl(type) { if(this.Error) { return "/api/v1/svg/broken"; diff --git a/frontend/src/model/folder.js b/frontend/src/model/folder.js index 7f339b557..7c92ea8ea 100644 --- a/frontend/src/model/folder.js +++ b/frontend/src/model/folder.js @@ -85,10 +85,6 @@ export class Folder extends RestModel { return this.Root + "/" + this.Path; } - getId() { - return this.UID; - } - thumbnailUrl() { return "/api/v1/svg/folder"; } diff --git a/frontend/src/model/label.js b/frontend/src/model/label.js index e8047c7b0..197451a1e 100644 --- a/frontend/src/model/label.js +++ b/frontend/src/model/label.js @@ -56,10 +56,6 @@ export class Label extends RestModel { return this.Slug; } - getId() { - return this.UID; - } - getTitle() { return this.Name; } diff --git a/frontend/src/model/link.js b/frontend/src/model/link.js index 7bcc4debc..82a4b979a 100644 --- a/frontend/src/model/link.js +++ b/frontend/src/model/link.js @@ -36,30 +36,35 @@ export default class Link extends Model { getDefaults() { return { UID: "", - ShareUID: "", - ShareToken: "", - ShareExpires: 0, + Share: "", + Slug: "", + Token: "", + Expires: 0, Password: "", HasPassword: false, CanComment: false, CanEdit: false, CreatedAt: "", - UpdatedAt: "", + ModifiedAt: "", }; } url() { - let token = this.ShareToken.toLowerCase(); + let token = this.Token.toLowerCase(); if(!token) { token = "..."; } - return `${window.location.origin}/s/${token}/${this.ShareUID}`; + if(this.hasSlug()) { + return `${window.location.origin}/s/${token}/${this.Slug}`; + } + + return `${window.location.origin}/s/${token}/${this.Share}`; } caption() { - return `/s/${this.ShareToken.toLowerCase()}`; + return `/s/${this.Token.toLowerCase()}`; } getId() { @@ -70,6 +75,14 @@ export default class Link extends Model { return !!this.getId(); } + getSlug() { + return this.Slug ? this.Slug : ""; + } + + hasSlug() { + return !!this.getSlug(); + } + clone() { return new this.constructor(this.getValues()); } @@ -95,7 +108,7 @@ export default class Link extends Model { } expires() { - return DateTime.fromISO(this.UpdatedAt).plus({ seconds: this.ShareExpires }).toLocaleString(DateTime.DATE_SHORT); + return DateTime.fromISO(this.UpdatedAt).plus({ seconds: this.Expires }).toLocaleString(DateTime.DATE_SHORT); } static getCollectionResource() { diff --git a/frontend/src/model/photo.js b/frontend/src/model/photo.js index 74ca6b643..817d8590c 100644 --- a/frontend/src/model/photo.js +++ b/frontend/src/model/photo.js @@ -146,10 +146,6 @@ export class Photo extends RestModel { return this.Title; } - getId() { - return this.UID; - } - getTitle() { return this.Title; } diff --git a/frontend/src/model/rest.js b/frontend/src/model/rest.js index 6bcec449b..7080e0ccd 100644 --- a/frontend/src/model/rest.js +++ b/frontend/src/model/rest.js @@ -35,13 +35,17 @@ import Link from "./link"; export class Rest extends Model { getId() { - return this.ID; + return this.UID ? this.UID : this.ID; } hasId() { return !!this.getId(); } + getSlug() { + return this.Slug ? this.Slug : ""; + } + clone() { return new this.constructor(this.getValues()); } @@ -86,7 +90,8 @@ export class Rest extends Model { return Api .post(this.getEntityResource() + "/links", { "Password": password ? password : "", - "ShareExpires": expires ? expires : 0, + "Expires": expires ? expires : 0, + "Slug": this.getSlug(), "CanEdit": false, "CanComment": false, }) @@ -148,6 +153,10 @@ export class Rest extends Model { return Api.options(this.getCollectionResource()).then(resp => Promise.resolve(new Form(resp.data))); } + static limit() { + return 3333; + } + static search(params) { const options = { params: params, diff --git a/frontend/src/model/user.js b/frontend/src/model/user.js index 3693dab5d..a52d09f7c 100644 --- a/frontend/src/model/user.js +++ b/frontend/src/model/user.js @@ -70,10 +70,6 @@ export class User extends RestModel { return this.FirstName + " " + this.LastName; } - getId() { - return this.ID; - } - getRegisterForm() { return Api.options(this.getEntityResource() + "/register").then(response => Promise.resolve(new Form(response.data))); } diff --git a/frontend/src/pages/album/photos.vue b/frontend/src/pages/album/photos.vue index d6af789d6..ea6fa62c9 100644 --- a/frontend/src/pages/album/photos.vue +++ b/frontend/src/pages/album/photos.vue @@ -92,7 +92,7 @@ uid: uid, results: [], scrollDisabled: true, - pageSize: 120, + pageSize: 60, offset: 0, page: 0, selection: this.$clipboard.selection, @@ -157,11 +157,41 @@ } else if (showMerged) { this.$viewer.show(Thumb.fromFiles([this.results[index]]), 0) } else { - this.$viewer.show(Thumb.fromPhotos(this.results), index); + this.findAll().then((results) => { + this.$viewer.show(Thumb.fromPhotos(results), index); + }); } return true; }, + findAll() { + if(this.scrollDisabled) { + return Promise.resolve(this.results); + } + + const count = Photo.limit(); + const offset = 0; + + const params = { + count: count, + offset: offset, + album: this.uid, + filter: this.model.Filter ? this.model.Filter : "", + merged: true, + }; + + Object.assign(params, this.lastFilter); + + if (this.staticFilter) { + Object.assign(params, this.staticFilter); + } + + return Photo.search(params).then(resp => { + return Promise.resolve(resp.models); + }).catch(() => { + return Promise.resolve(this.results); + }); + }, loadMore() { if (this.scrollDisabled) return; diff --git a/frontend/src/pages/photos.vue b/frontend/src/pages/photos.vue index f4e0a2cab..90cfe5fd1 100644 --- a/frontend/src/pages/photos.vue +++ b/frontend/src/pages/photos.vue @@ -106,6 +106,7 @@ listen: false, dirty: false, results: [], + allResults: [], scrollDisabled: true, pageSize: 60, offset: 0, @@ -197,9 +198,42 @@ } else if (showMerged) { this.$viewer.show(Thumb.fromFiles([this.results[index]]), 0) } else { - this.$viewer.show(Thumb.fromPhotos(this.results), index); + this.findAll().then((results) => { + this.$viewer.show(Thumb.fromPhotos(results), index); + }); } }, + findAll() { + if(this.scrollDisabled) { + return Promise.resolve(this.results); + } + + if(this.allResults && !this.dirty) { + return Promise.resolve(this.allResults); + } + + const count = Photo.limit(); + const offset = 0; + + const params = { + count: count, + offset: offset, + merged: true, + }; + + Object.assign(params, this.lastFilter); + + if (this.staticFilter) { + Object.assign(params, this.staticFilter); + } + + return Photo.search(params).then(resp => { + this.allResults = resp.models + return Promise.resolve(this.allResults); + }).catch(() => { + return Promise.resolve(this.results); + }); + }, loadMore() { if (this.scrollDisabled) return; diff --git a/frontend/src/share/photos.vue b/frontend/src/share/photos.vue index f1273aa08..ee7f0d49f 100644 --- a/frontend/src/share/photos.vue +++ b/frontend/src/share/photos.vue @@ -130,7 +130,7 @@ uid: uid, results: [], scrollDisabled: true, - pageSize: 120, + pageSize: 60, offset: 0, page: 0, selection: this.$clipboard.selection, @@ -200,11 +200,41 @@ } else if (showMerged) { this.$viewer.show(Thumb.fromFiles([this.results[index]]), 0) } else { - this.$viewer.show(Thumb.fromPhotos(this.results), index); + this.findAll().then((results) => { + this.$viewer.show(Thumb.fromPhotos(results), index); + }); } return true; }, + findAll() { + if(this.scrollDisabled) { + return Promise.resolve(this.results); + } + + const count = Photo.limit(); + const offset = 0; + + const params = { + count: count, + offset: offset, + album: this.uid, + filter: this.model.Filter ? this.model.Filter : "", + merged: true, + }; + + Object.assign(params, this.lastFilter); + + if (this.staticFilter) { + Object.assign(params, this.staticFilter); + } + + return Photo.search(params).then(resp => { + return Promise.resolve(resp.models); + }).catch(() => { + return Promise.resolve(this.results); + }); + }, loadMore() { if (this.scrollDisabled) return; diff --git a/internal/api/link.go b/internal/api/link.go index b3936f942..cdca1a358 100644 --- a/internal/api/link.go +++ b/internal/api/link.go @@ -31,6 +31,8 @@ func UpdateLink(c *gin.Context) { link := entity.FindLink(c.Param("link")) + link.SetSlug(f.ShareSlug) + link.MaxViews = f.MaxViews link.ShareExpires = f.ShareExpires if f.ShareToken != "" { @@ -93,9 +95,9 @@ func CreateLink(c *gin.Context) { link := entity.NewLink(c.Param("uid"), f.CanComment, f.CanEdit) - if f.ShareExpires > 0 { - link.ShareExpires = f.ShareExpires - } + link.SetSlug(f.ShareSlug) + link.MaxViews = f.MaxViews + link.ShareExpires = f.ShareExpires if f.Password != "" { if err := link.SetPassword(f.Password); err != nil { diff --git a/internal/api/link_test.go b/internal/api/link_test.go index 9e1619115..c1cf89874 100644 --- a/internal/api/link_test.go +++ b/internal/api/link_test.go @@ -18,7 +18,7 @@ func TestLinkAlbum(t *testing.T) { CreateAlbumLink(router) - resp := PerformRequestWithBody(app, "POST", "/api/v1/albums/at9lxuqxpogaaba7/links", `{"Password": "foobar", "ShareExpires": 0, "CanEdit": true}`) + resp := PerformRequestWithBody(app, "POST", "/api/v1/albums/at9lxuqxpogaaba7/links", `{"Password": "foobar", "Expires": 0, "CanEdit": true}`) if resp.Code != http.StatusOK { t.Fatal(resp.Body.String()) @@ -39,7 +39,7 @@ func TestLinkAlbum(t *testing.T) { t.Run("album does not exist", func(t *testing.T) { app, router, _ := NewApiTest() CreateAlbumLink(router) - resp := PerformRequestWithBody(app, "POST", "/api/v1/albums/xxx/links", `{"Password": "foobar", "ShareExpires": 0, "CanEdit": true}`) + resp := PerformRequestWithBody(app, "POST", "/api/v1/albums/xxx/links", `{"Password": "foobar", "Expires": 0, "CanEdit": true}`) if resp.Code != http.StatusNotFound { t.Fatal(resp.Body.String()) @@ -53,7 +53,7 @@ func TestLinkAlbum(t *testing.T) { CreateAlbumLink(router) - resp := PerformRequestWithBody(app, "POST", "/api/v1/albums/at9lxuqxpogaaba7/links", `{"Password": "foobar", "ShareExpires": "abc", "CanEdit": true}`) + resp := PerformRequestWithBody(app, "POST", "/api/v1/albums/at9lxuqxpogaaba7/links", `{"Password": "foobar", "Expires": "abc", "CanEdit": true}`) if resp.Code != http.StatusBadRequest { t.Fatal(resp.Body.String()) @@ -69,7 +69,7 @@ func TestLinkPhoto(t *testing.T) { CreatePhotoLink(router) - resp := PerformRequestWithBody(app, "POST", "/api/v1/photos/pt9jtdre2lvl0yh7/links", `{"Password": "foobar", "ShareExpires": 0, "CanEdit": true}`) + resp := PerformRequestWithBody(app, "POST", "/api/v1/photos/pt9jtdre2lvl0yh7/links", `{"Password": "foobar", "Expires": 0, "CanEdit": true}`) assert.Equal(t, http.StatusOK, resp.Code) if err := json.Unmarshal(resp.Body.Bytes(), &link); err != nil { @@ -88,7 +88,7 @@ func TestLinkPhoto(t *testing.T) { CreatePhotoLink(router) - resp := PerformRequestWithBody(app, "POST", "/api/v1/photos/xxx/link", `{"Password": "foobar", "ShareExpires": 0, "CanEdit": true}`) + resp := PerformRequestWithBody(app, "POST", "/api/v1/photos/xxx/link", `{"Password": "foobar", "Expires": 0, "CanEdit": true}`) if resp.Code != http.StatusNotFound { t.Fatal(resp.Body.String()) @@ -97,7 +97,7 @@ func TestLinkPhoto(t *testing.T) { t.Run("invalid request", func(t *testing.T) { app, router, _ := NewApiTest() CreatePhotoLink(router) - r := PerformRequestWithBody(app, "POST", "/api/v1/photos/pt9jtdre2lvl0yh7/links", `{"xxx": 123, "ShareExpires": "abc", "CanEdit": "xxx"}`) + r := PerformRequestWithBody(app, "POST", "/api/v1/photos/pt9jtdre2lvl0yh7/links", `{"xxx": 123, "Expires": "abc", "CanEdit": "xxx"}`) assert.Equal(t, http.StatusBadRequest, r.Code) }) } @@ -110,7 +110,7 @@ func TestLinkLabel(t *testing.T) { CreateLabelLink(router) - resp := PerformRequestWithBody(app, "POST", "/api/v1/labels/lt9k3pw1wowuy3c2/links", `{"Password": "foobar", "ShareExpires": 0, "CanEdit": true}`) + resp := PerformRequestWithBody(app, "POST", "/api/v1/labels/lt9k3pw1wowuy3c2/links", `{"Password": "foobar", "Expires": 0, "CanEdit": true}`) assert.Equal(t, http.StatusOK, resp.Code) if err := json.Unmarshal(resp.Body.Bytes(), &link); err != nil { @@ -127,7 +127,7 @@ func TestLinkLabel(t *testing.T) { t.Run("label not found", func(t *testing.T) { app, router, _ := NewApiTest() CreateLabelLink(router) - resp := PerformRequestWithBody(app, "POST", "/api/v1/labels/xxx/links", `{"Password": "foobar", "ShareExpires": 0, "CanEdit": true}`) + resp := PerformRequestWithBody(app, "POST", "/api/v1/labels/xxx/links", `{"Password": "foobar", "Expires": 0, "CanEdit": true}`) if resp.Code != http.StatusNotFound { t.Fatal(resp.Body.String()) @@ -136,7 +136,7 @@ func TestLinkLabel(t *testing.T) { t.Run("invalid request", func(t *testing.T) { app, router, _ := NewApiTest() CreateLabelLink(router) - r := PerformRequestWithBody(app, "POST", "/api/v1/labels/lt9k3pw1wowuy3c2/links", `{"xxx": 123, "ShareExpires": "abc", "CanEdit": "xxx"}`) + r := PerformRequestWithBody(app, "POST", "/api/v1/labels/lt9k3pw1wowuy3c2/links", `{"xxx": 123, "Expires": "abc", "CanEdit": "xxx"}`) assert.Equal(t, http.StatusBadRequest, r.Code) }) } diff --git a/internal/api/share.go b/internal/api/share.go index a09da7a45..55e704ef5 100644 --- a/internal/api/share.go +++ b/internal/api/share.go @@ -34,9 +34,9 @@ func Shares(router *gin.RouterGroup) { conf := service.Config() shareToken := c.Param("token") - shareUID := c.Param("uid") + share := c.Param("uid") - links := entity.FindValidLinks(shareToken, shareUID) + links := entity.FindValidLinks(shareToken, share) if len(links) != 1 { log.Warn("share: invalid token or uid") @@ -44,10 +44,17 @@ func Shares(router *gin.RouterGroup) { return } - clientConfig := conf.GuestConfig() - clientConfig.SitePreview = fmt.Sprintf("%ss/%s/%s/preview", clientConfig.SiteUrl, shareToken, shareUID) + uid := links[0].ShareUID - if a, err := query.AlbumByUID(shareUID); err == nil { + if uid != share { + c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("/s/%s/%s", shareToken, uid)) + return + } + + clientConfig := conf.GuestConfig() + clientConfig.SitePreview = fmt.Sprintf("%ss/%s/%s/preview", clientConfig.SiteUrl, shareToken, uid) + + if a, err := query.AlbumByUID(uid); err == nil { clientConfig.SiteCaption = a.AlbumTitle if a.AlbumDescription != "" { diff --git a/internal/entity/link.go b/internal/entity/link.go index c7779e31f..98555c254 100644 --- a/internal/entity/link.go +++ b/internal/entity/link.go @@ -4,8 +4,11 @@ import ( "fmt" "time" + "github.com/gosimple/slug" "github.com/jinzhu/gorm" + "github.com/photoprism/photoprism/pkg/rnd" + "github.com/photoprism/photoprism/pkg/txt" ) type Links []Link @@ -13,9 +16,10 @@ type Links []Link // Link represents a sharing link. type Link struct { LinkUID string `gorm:"type:varbinary(42);primary_key;" json:"UID,omitempty" yaml:"UID,omitempty"` - ShareUID string `gorm:"type:varbinary(42);unique_index:idx_links_uid_token;" json:"ShareUID"` - ShareToken string `gorm:"type:varbinary(255);unique_index:idx_links_uid_token;" json:"ShareToken"` - ShareExpires int `json:"ShareExpires" yaml:"ShareExpires,omitempty"` + ShareUID string `gorm:"type:varbinary(42);unique_index:idx_links_uid_token;" json:"Share" yaml:"Share"` + ShareSlug string `gorm:"type:varbinary(255);index;" json:"Slug" yaml:"Slug,omitempty"` + ShareToken string `gorm:"type:varbinary(255);unique_index:idx_links_uid_token;" json:"Token" yaml:"Token,omitempty"` + ShareExpires int `json:"Expires" yaml:"Expires,omitempty"` ShareViews uint `json:"ShareViews" yaml:"-"` MaxViews uint `json:"MaxViews" yaml:"-"` HasPassword bool `json:"HasPassword" yaml:"HasPassword,omitempty"` @@ -76,6 +80,10 @@ func (m *Link) Expired() bool { return now.Before(expires) } +func (m *Link) SetSlug(s string) { + m.ShareSlug = slug.Make(txt.Clip(s, txt.ClipSlug)) +} + func (m *Link) SetPassword(password string) error { pw := NewPassword(m.LinkUID, password) @@ -140,8 +148,8 @@ func FindLink(linkUID string) *Link { } // FindLinks returns a slice of links for a token and share UID (at least one must be provided). -func FindLinks(shareToken, shareUID string) (result Links) { - if shareToken == "" && shareUID == "" { +func FindLinks(shareToken, share string) (result Links) { + if shareToken == "" && share == "" { log.Errorf("link: share token and uid must not be empty at the same time (find links)") return []Link{} } @@ -152,8 +160,12 @@ func FindLinks(shareToken, shareUID string) (result Links) { q = q.Where("share_token = ?", shareToken) } - if shareUID != "" { - q = q.Where("share_uid = ?", shareUID) + if share != "" { + if rnd.IsPPID(share, 'a') { + q = q.Where("share_uid = ?", share) + } else { + q = q.Where("share_slug = ?", share) + } } if err := q.Find(&result).Error; err != nil { diff --git a/internal/form/link.go b/internal/form/link.go index 9ccf87013..d4b5b90fb 100644 --- a/internal/form/link.go +++ b/internal/form/link.go @@ -3,8 +3,9 @@ package form // Link represents a link sharing form. type Link struct { Password string `json:"Password"` - ShareToken string `json:"ShareToken"` - ShareExpires int `json:"ShareExpires"` + ShareSlug string `json:"Slug"` + ShareToken string `json:"Token"` + ShareExpires int `json:"Expires"` MaxViews uint `json:"MaxViews"` CanComment bool `json:"CanComment"` CanEdit bool `json:"CanEdit"` diff --git a/pkg/txt/resources/stopwords.txt b/pkg/txt/resources/stopwords.txt index 26a6dba31..2cf22b0bd 100644 --- a/pkg/txt/resources/stopwords.txt +++ b/pkg/txt/resources/stopwords.txt @@ -1,8 +1,18 @@ olymp +olympus +ilford +fujifilm +fuji +leica +panasonic sony canon nikon iphone +flickr +picasa +backup +backups imac ipad android @@ -14,7 +24,9 @@ pict picture pictures upload +uploads download +downloads temp var lib diff --git a/pkg/txt/stopwords.go b/pkg/txt/stopwords.go index 86f1d3893..544dcd1c9 100644 --- a/pkg/txt/stopwords.go +++ b/pkg/txt/stopwords.go @@ -4,10 +4,20 @@ package txt // StopWords contains a list of stopwords for full-text indexing. var StopWords = map[string]bool{ "olymp": true, + "olympus": true, + "ilford": true, + "fujifilm": true, + "fuji": true, + "leica": true, + "panasonic": true, "sony": true, "canon": true, "nikon": true, "iphone": true, + "flickr": true, + "picasa": true, + "backup": true, + "backups": true, "imac": true, "ipad": true, "android": true, @@ -19,7 +29,9 @@ var StopWords = map[string]bool{ "picture": true, "pictures": true, "upload": true, + "uploads": true, "download": true, + "downloads": true, "temp": true, "var": true, "lib": true,