Port album adding
parent
6087e5fb0a
commit
49373969a7
|
@ -27,7 +27,7 @@
|
||||||
<!-- Selection Modals -->
|
<!-- Selection Modals -->
|
||||||
<EditDate ref="editDate" @refresh="refresh" />
|
<EditDate ref="editDate" @refresh="refresh" />
|
||||||
<FaceMoveModal ref="faceMoveModal" @moved="deletePhotos" :updateLoading="updateLoading" />
|
<FaceMoveModal ref="faceMoveModal" @moved="deletePhotos" :updateLoading="updateLoading" />
|
||||||
<AddToAlbumModal ref="addToAlbumModal" />
|
<AddToAlbumModal ref="addToAlbumModal" @added="clearSelection" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -6,19 +6,25 @@
|
||||||
|
|
||||||
<div class="outer">
|
<div class="outer">
|
||||||
<AlbumPicker @select="selectAlbum" />
|
<AlbumPicker @select="selectAlbum" />
|
||||||
|
|
||||||
|
<div v-if="processing" class="info-pad">
|
||||||
|
{{ t('memories', 'Processing … {n}/{m}', {
|
||||||
|
n: photosDone,
|
||||||
|
m: photos.length,
|
||||||
|
}) }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Emit, Mixins, Prop } from 'vue-property-decorator';
|
import { Component, Emit, Mixins, Prop } from 'vue-property-decorator';
|
||||||
import { showError } from '@nextcloud/dialogs';
|
import { showInfo } from '@nextcloud/dialogs';
|
||||||
import { IAlbum, IPhoto, ITag } from '../../types';
|
import { IAlbum, IPhoto } from '../../types';
|
||||||
import AlbumPicker from './AlbumPicker.vue';
|
import AlbumPicker from './AlbumPicker.vue';
|
||||||
|
|
||||||
import Modal from './Modal.vue';
|
import Modal from './Modal.vue';
|
||||||
import GlobalMixin from '../../mixins/GlobalMixin';
|
import GlobalMixin from '../../mixins/GlobalMixin';
|
||||||
import client from '../../services/DavClient';
|
|
||||||
import * as dav from '../../services/DavRequests';
|
import * as dav from '../../services/DavRequests';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -30,28 +36,38 @@ import * as dav from '../../services/DavRequests';
|
||||||
export default class AddToAlbumModal extends Mixins(GlobalMixin) {
|
export default class AddToAlbumModal extends Mixins(GlobalMixin) {
|
||||||
private show = false;
|
private show = false;
|
||||||
private photos: IPhoto[] = [];
|
private photos: IPhoto[] = [];
|
||||||
|
private photosDone: number = 0;
|
||||||
@Prop()
|
private processing: boolean = false;
|
||||||
private updateLoading: (delta: number) => void;
|
|
||||||
|
|
||||||
public open(photos: IPhoto[]) {
|
public open(photos: IPhoto[]) {
|
||||||
if (this.photos.length) {
|
this.photosDone = 0;
|
||||||
// is processing
|
this.processing = false;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.show = true;
|
this.show = true;
|
||||||
this.photos = photos;
|
this.photos = photos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Emit('added')
|
||||||
|
public added(photos: IPhoto[]) {}
|
||||||
|
|
||||||
@Emit('close')
|
@Emit('close')
|
||||||
public close() {
|
public close() {
|
||||||
this.photos = [];
|
this.photos = [];
|
||||||
|
this.processing = false;
|
||||||
this.show = false;
|
this.show = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async selectAlbum(album: IAlbum) {
|
public async selectAlbum(album: IAlbum) {
|
||||||
console.log('selectAlbum', album);
|
const name = album.name || album.album_id.toString();
|
||||||
|
const gen = dav.addToAlbum(album.user, name, this.photos.map(p => p.fileid));
|
||||||
|
this.processing = true;
|
||||||
|
|
||||||
|
for await (const fids of gen) {
|
||||||
|
this.photosDone += fids.filter(f => f).length;
|
||||||
|
this.added(this.photos.filter(p => fids.includes(p.fileid)));
|
||||||
|
}
|
||||||
|
|
||||||
|
showInfo(this.t('memories', '{n} photos added to album', { n: this.photosDone }));
|
||||||
|
this.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -60,4 +76,8 @@ export default class AddToAlbumModal extends Mixins(GlobalMixin) {
|
||||||
.outer {
|
.outer {
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.info-pad {
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
|
@ -115,7 +115,10 @@ async function getFilesInternal(fileIds: number[]): Promise<IFileInfo[]> {
|
||||||
let response: any = await client.getDirectoryContents('', options);
|
let response: any = await client.getDirectoryContents('', options);
|
||||||
return response.data
|
return response.data
|
||||||
.map((data: any) => genFileInfo(data))
|
.map((data: any) => genFileInfo(data))
|
||||||
.map((data: any) => Object.assign({}, data, { filename: data.filename.replace(prefixPath, '') }));
|
.map((data: any) => Object.assign({}, data, {
|
||||||
|
originalFilename: data.filename,
|
||||||
|
filename: data.filename.replace(prefixPath, '')
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -578,3 +581,39 @@ export async function* removeFaceImages(user: string, name: string, fileIds: num
|
||||||
} as ITag)),
|
} as ITag)),
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove images from a face.
|
||||||
|
*
|
||||||
|
* @param user User ID of album
|
||||||
|
* @param name Name of album (or ID)
|
||||||
|
* @param fileIds List of file IDs to add
|
||||||
|
* @returns Generator
|
||||||
|
*/
|
||||||
|
export async function* addToAlbum(user: string, name: string, fileIds: number[]) {
|
||||||
|
// Get files data
|
||||||
|
let fileInfos = await getFiles(fileIds.filter(f => f));
|
||||||
|
|
||||||
|
// Add each file
|
||||||
|
const calls = fileInfos.map((f) => async () => {
|
||||||
|
try {
|
||||||
|
await client.copyFile(
|
||||||
|
f.originalFilename,
|
||||||
|
`/photos/${user}/albums/${name}/${f.basename}`,
|
||||||
|
)
|
||||||
|
return f.fileid;
|
||||||
|
} catch (e) {
|
||||||
|
if (e.response?.status === 409) {
|
||||||
|
// File already exists, all good
|
||||||
|
return f.fileid;
|
||||||
|
}
|
||||||
|
|
||||||
|
showError(t('memories', 'Failed to add {filename} to album.', {
|
||||||
|
filename: f.filename,
|
||||||
|
}));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
yield* runInParallel(calls, 10);
|
||||||
|
}
|
|
@ -5,6 +5,8 @@ export type IFileInfo = {
|
||||||
fileid: number;
|
fileid: number;
|
||||||
/** Full file name, e.g. /pi/test/Qx0dq7dvEXA.jpg */
|
/** Full file name, e.g. /pi/test/Qx0dq7dvEXA.jpg */
|
||||||
filename: string;
|
filename: string;
|
||||||
|
/** Original file name, e.g. /files/admin/pi/test/Qx0dq7dvEXA.jpg */
|
||||||
|
originalFilename: string;
|
||||||
/** Base name of file e.g. Qx0dq7dvEXA.jpg */
|
/** Base name of file e.g. Qx0dq7dvEXA.jpg */
|
||||||
basename: string;
|
basename: string;
|
||||||
/** Etag identifier */
|
/** Etag identifier */
|
||||||
|
|
Loading…
Reference in New Issue