Add photos from other albums (clone)

Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
Michael Mayer 2020-06-14 11:39:53 +02:00
parent d89356c715
commit 961fda3b11
6 changed files with 92 additions and 5 deletions

View file

@ -35,7 +35,18 @@
>
<v-icon>get_app</v-icon>
</v-btn>
<v-btn
fab
dark
small
:title="labels.clone"
color="album"
:disabled="selection.length === 0"
@click.stop="dialog.album = true"
class="p-album-clipboard-clone"
>
<v-icon>folder</v-icon>
</v-btn>
<v-btn
fab
dark
@ -61,6 +72,8 @@
</v-btn>
</v-speed-dial>
</v-container>
<p-photo-album-dialog :show="dialog.album" @cancel="dialog.album = false"
@confirm="cloneAlbums"></p-photo-album-dialog>
<p-album-delete-dialog :show="dialog.delete" @cancel="dialog.delete = false"
@confirm="batchDelete"></p-album-delete-dialog>
</div>
@ -81,10 +94,12 @@
expanded: false,
dialog: {
delete: false,
album: false,
edit: false,
},
labels: {
download: this.$gettext("Download"),
clone: this.$gettext("Add to album"),
delete: this.$gettext("Delete"),
},
@ -95,6 +110,14 @@
this.clearSelection();
this.expanded = false;
},
cloneAlbums(ppid) {
this.dialog.album = false;
Api.post(`albums/${ppid}/clone`, {"albums": this.selection}).then(() => this.onCloned());
},
onCloned() {
this.clearClipboard();
},
batchDelete() {
this.dialog.delete = false;

View file

@ -242,6 +242,58 @@ func DislikeAlbum(router *gin.RouterGroup, conf *config.Config) {
})
}
// POST /api/v1/albums/:uid/clone
func CloneAlbums(router *gin.RouterGroup, conf *config.Config) {
router.POST("/albums/:uid/clone", func(c *gin.Context) {
if Unauthorized(c, conf) {
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
return
}
a, err := query.AlbumByUID(c.Param("uid"))
if err != nil {
c.AbortWithStatusJSON(http.StatusNotFound, ErrAlbumNotFound)
return
}
var f form.Selection
if err := c.BindJSON(&f); err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
return
}
var added []entity.PhotoAlbum
for _, uid := range f.Albums {
cloneAlbum, err := query.AlbumByUID(uid)
if err != nil {
log.Errorf("album: %s", err)
continue
}
photos, err := query.AlbumPhotos(cloneAlbum, 10000)
if err != nil {
log.Errorf("album: %s", err)
continue
}
added = append(added, a.AddPhotos(photos.UIDs())...)
}
if len(added) > 0 {
event.Success(fmt.Sprintf("selection added to %s", txt.Quote(a.Title())))
PublishAlbumEvent(EntityUpdated, a.AlbumUID, c)
}
c.JSON(http.StatusOK, gin.H{"message": "album contents cloned", "album": a, "added": added})
})
}
// POST /api/v1/albums/:uid/photos
func AddPhotosToAlbum(router *gin.RouterGroup, conf *config.Config) {
router.POST("/albums/:uid/photos", func(c *gin.Context) {
@ -333,7 +385,7 @@ func RemovePhotosFromAlbum(router *gin.RouterGroup, conf *config.Config) {
})
}
// GET /albums/:uid/dl
// GET /api/v1/albums/:uid/dl
func DownloadAlbum(router *gin.RouterGroup, conf *config.Config) {
router.GET("/albums/:uid/dl", func(c *gin.Context) {
if InvalidDownloadToken(c, conf) {

View file

@ -19,7 +19,7 @@ import (
type Photos []Photo
// UIDs returns a slice of unique photo IDs.
// UIDs returns a slice of photo UIDs.
func (m Photos) UIDs() []string {
result := make([]string, len(m))

View file

@ -88,6 +88,17 @@ type PhotoResult struct {
type PhotoResults []PhotoResult
// UIDs returns a slice of photo UIDs.
func (m PhotoResults) UIDs() []string {
result := make([]string, len(m))
for i, el := range m {
result[i] = el.PhotoUID
}
return result
}
func (m PhotoResults) Merged() (PhotoResults, int, error) {
count := len(m)
merged := make([]PhotoResult, 0, count)

View file

@ -31,7 +31,7 @@ func PhotoSelection(f form.Selection) (results entity.Photos, err error) {
OR photos.photo_path IN (
SELECT a.path FROM folders a WHERE a.folder_uid IN (?) UNION
SELECT b.path FROM folders a JOIN folders b ON b.path LIKE %s WHERE a.folder_uid IN (?))
OR photos.photo_uid IN (SELECT photo_uid FROM photos_albums WHERE album_uid IN (?))
OR photos.photo_uid IN (SELECT photo_uid FROM photos_albums WHERE hidden = 0 AND album_uid IN (?))
OR photos.id IN (SELECT pl.photo_id FROM photos_labels pl JOIN labels l ON pl.label_id = l.id AND l.deleted_at IS NULL WHERE l.label_uid IN (?))
OR photos.id IN (SELECT pl.photo_id FROM photos_labels pl JOIN categories c ON c.label_id = pl.label_id JOIN labels lc ON lc.id = c.category_id AND lc.deleted_at IS NULL WHERE lc.label_uid IN (?))`,
concat)
@ -70,7 +70,7 @@ func FileSelection(f form.Selection) (results entity.Files, err error) {
OR photos.photo_path IN (
SELECT a.path FROM folders a WHERE a.folder_uid IN (?) UNION
SELECT b.path FROM folders a JOIN folders b ON b.path LIKE %s WHERE a.folder_uid IN (?))
OR photos.photo_uid IN (SELECT photo_uid FROM photos_albums WHERE album_uid IN (?))
OR photos.photo_uid IN (SELECT photo_uid FROM photos_albums WHERE hidden = 0 AND album_uid IN (?))
OR photos.id IN (SELECT pl.photo_id FROM photos_labels pl JOIN labels l ON pl.label_id = l.id AND l.deleted_at IS NULL WHERE l.label_uid IN (?))
OR photos.id IN (SELECT pl.photo_id FROM photos_labels pl JOIN categories c ON c.label_id = pl.label_id JOIN labels lc ON lc.id = c.category_id AND lc.deleted_at IS NULL WHERE lc.label_uid IN (?))`,
concat)

View file

@ -87,6 +87,7 @@ func registerRoutes(router *gin.Engine, conf *config.Config) {
api.LikeAlbum(v1, conf)
api.DislikeAlbum(v1, conf)
api.AlbumThumbnail(v1, conf)
api.CloneAlbums(v1, conf)
api.AddPhotosToAlbum(v1, conf)
api.RemovePhotosFromAlbum(v1, conf)