Port album adding
parent
6087e5fb0a
commit
49373969a7
|
@ -27,7 +27,7 @@
|
|||
<!-- Selection Modals -->
|
||||
<EditDate ref="editDate" @refresh="refresh" />
|
||||
<FaceMoveModal ref="faceMoveModal" @moved="deletePhotos" :updateLoading="updateLoading" />
|
||||
<AddToAlbumModal ref="addToAlbumModal" />
|
||||
<AddToAlbumModal ref="addToAlbumModal" @added="clearSelection" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -6,19 +6,25 @@
|
|||
|
||||
<div class="outer">
|
||||
<AlbumPicker @select="selectAlbum" />
|
||||
|
||||
<div v-if="processing" class="info-pad">
|
||||
{{ t('memories', 'Processing … {n}/{m}', {
|
||||
n: photosDone,
|
||||
m: photos.length,
|
||||
}) }}
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Emit, Mixins, Prop } from 'vue-property-decorator';
|
||||
import { showError } from '@nextcloud/dialogs';
|
||||
import { IAlbum, IPhoto, ITag } from '../../types';
|
||||
import { showInfo } from '@nextcloud/dialogs';
|
||||
import { IAlbum, IPhoto } from '../../types';
|
||||
import AlbumPicker from './AlbumPicker.vue';
|
||||
|
||||
import Modal from './Modal.vue';
|
||||
import GlobalMixin from '../../mixins/GlobalMixin';
|
||||
import client from '../../services/DavClient';
|
||||
import * as dav from '../../services/DavRequests';
|
||||
|
||||
@Component({
|
||||
|
@ -30,28 +36,38 @@ import * as dav from '../../services/DavRequests';
|
|||
export default class AddToAlbumModal extends Mixins(GlobalMixin) {
|
||||
private show = false;
|
||||
private photos: IPhoto[] = [];
|
||||
|
||||
@Prop()
|
||||
private updateLoading: (delta: number) => void;
|
||||
private photosDone: number = 0;
|
||||
private processing: boolean = false;
|
||||
|
||||
public open(photos: IPhoto[]) {
|
||||
if (this.photos.length) {
|
||||
// is processing
|
||||
return;
|
||||
}
|
||||
|
||||
this.photosDone = 0;
|
||||
this.processing = false;
|
||||
this.show = true;
|
||||
this.photos = photos;
|
||||
}
|
||||
|
||||
@Emit('added')
|
||||
public added(photos: IPhoto[]) {}
|
||||
|
||||
@Emit('close')
|
||||
public close() {
|
||||
this.photos = [];
|
||||
this.processing = false;
|
||||
this.show = false;
|
||||
}
|
||||
|
||||
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>
|
||||
|
@ -60,4 +76,8 @@ export default class AddToAlbumModal extends Mixins(GlobalMixin) {
|
|||
.outer {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.info-pad {
|
||||
margin-top: 6px;
|
||||
}
|
||||
</style>
|
|
@ -115,7 +115,10 @@ async function getFilesInternal(fileIds: number[]): Promise<IFileInfo[]> {
|
|||
let response: any = await client.getDirectoryContents('', options);
|
||||
return response.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, '')
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -556,7 +559,7 @@ export async function* removeFaceImages(user: string, name: string, fileIds: num
|
|||
/**
|
||||
* Get list of albums and convert to Days response
|
||||
*/
|
||||
export async function getAlbumsData(): Promise<IDay[]> {
|
||||
export async function getAlbumsData(): Promise<IDay[]> {
|
||||
let data: IAlbum[] = [];
|
||||
try {
|
||||
const res = await axios.get<typeof data>(generateUrl('/apps/memories/api/albums'));
|
||||
|
@ -577,4 +580,40 @@ export async function* removeFaceImages(user: string, name: string, fileIds: num
|
|||
isalbum: true,
|
||||
} 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;
|
||||
/** Full file name, e.g. /pi/test/Qx0dq7dvEXA.jpg */
|
||||
filename: string;
|
||||
/** Original file name, e.g. /files/admin/pi/test/Qx0dq7dvEXA.jpg */
|
||||
originalFilename: string;
|
||||
/** Base name of file e.g. Qx0dq7dvEXA.jpg */
|
||||
basename: string;
|
||||
/** Etag identifier */
|
||||
|
|
Loading…
Reference in New Issue