Port album adding

old-stable24
Varun Patil 2022-10-26 22:49:57 -07:00
parent 6087e5fb0a
commit 49373969a7
4 changed files with 77 additions and 16 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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, '')
}));
} }
/** /**
@ -556,7 +559,7 @@ export async function* removeFaceImages(user: string, name: string, fileIds: num
/** /**
* Get list of albums and convert to Days response * Get list of albums and convert to Days response
*/ */
export async function getAlbumsData(): Promise<IDay[]> { export async function getAlbumsData(): Promise<IDay[]> {
let data: IAlbum[] = []; let data: IAlbum[] = [];
try { try {
const res = await axios.get<typeof data>(generateUrl('/apps/memories/api/albums')); 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, isalbum: true,
} 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);
}

View File

@ -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 */