perf: reduce metadata queries
Signed-off-by: Varun Patil <radialapps@gmail.com>pull/767/head
parent
d00411670e
commit
66f9616942
|
@ -87,7 +87,7 @@ class AlbumsBackend extends Backend
|
|||
$this->tq->allowEmptyRoot();
|
||||
}
|
||||
|
||||
public function getClusters(int $fileid = 0): array
|
||||
protected function getClustersI(int $fileid = 0): array
|
||||
{
|
||||
// Run actual queries
|
||||
$list = [];
|
||||
|
|
|
@ -61,9 +61,12 @@ abstract class Backend
|
|||
/**
|
||||
* Get the cluster list for the current user.
|
||||
*
|
||||
* If the signature of this function changes, the
|
||||
* getClusters function must be updated to match.
|
||||
*
|
||||
* @param int $fileid Filter clusters by file ID (optional)
|
||||
*/
|
||||
abstract public function getClusters(int $fileid = 0): array;
|
||||
abstract protected function getClustersI(int $fileid = 0): array;
|
||||
|
||||
/**
|
||||
* Get a cluster ID for the given cluster.
|
||||
|
@ -125,10 +128,27 @@ abstract class Backend
|
|||
return (int) $photo['fileid'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the getClusters implementation and appends the
|
||||
* result with the cluster_id and cluster_type values.
|
||||
*
|
||||
* @param int $fileid Filter clusters by file ID (optional)
|
||||
*/
|
||||
public final function getClusters(int $fileid): array {
|
||||
$list = $this->getClustersI($fileid);
|
||||
|
||||
foreach ($list as &$cluster) {
|
||||
$cluster['cluster_id'] = $this->getClusterId($cluster);
|
||||
$cluster['cluster_type'] = $this->clusterType();
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the backend. Do not override.
|
||||
*/
|
||||
public static function register(): void
|
||||
public static final function register(): void
|
||||
{
|
||||
Manager::register(static::clusterType(), static::class);
|
||||
}
|
||||
|
|
|
@ -123,7 +123,7 @@ class FaceRecognitionBackend extends Backend
|
|||
unset($row['face_x'], $row['face_y'], $row['face_w'], $row['face_h'], $row['image_height'], $row['image_width']);
|
||||
}
|
||||
|
||||
public function getClusters(int $fileid = 0): array
|
||||
protected function getClustersI(int $fileid = 0): array
|
||||
{
|
||||
if ($fileid) {
|
||||
throw new \Exception('FaceRecognitionBackend: fileid filter not implemented');
|
||||
|
|
|
@ -63,7 +63,7 @@ class PlacesBackend extends Backend
|
|||
));
|
||||
}
|
||||
|
||||
public function getClusters(int $fileid = 0): array
|
||||
protected function getClustersI(int $fileid = 0): array
|
||||
{
|
||||
if ($fileid) {
|
||||
throw new \Exception('PlacesBackend: fileid filter not implemented');
|
||||
|
|
|
@ -129,7 +129,7 @@ class RecognizeBackend extends Backend
|
|||
unset($row['face_w'], $row['face_h'], $row['face_x'], $row['face_y']);
|
||||
}
|
||||
|
||||
public function getClusters(int $fileid = 0): array
|
||||
protected function getClustersI(int $fileid = 0): array
|
||||
{
|
||||
$query = $this->tq->getBuilder();
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ class TagsBackend extends Backend
|
|||
));
|
||||
}
|
||||
|
||||
public function getClusters(int $fileid = 0): array
|
||||
protected function getClustersI(int $fileid = 0): array
|
||||
{
|
||||
if ($fileid) {
|
||||
throw new \Exception('TagsBackend: fileid filter not implemented');
|
||||
|
|
|
@ -47,12 +47,6 @@ class ClustersController extends GenericApiController
|
|||
|
||||
$list = $this->backend->getClusters($fileid);
|
||||
|
||||
// Set cluster_id and cluster_type for each cluster
|
||||
foreach ($list as &$cluster) {
|
||||
$cluster['cluster_id'] = $this->backend->getClusterId($cluster);
|
||||
$cluster['cluster_type'] = $this->backend->clusterType();
|
||||
}
|
||||
|
||||
return new JSONResponse($list, Http::STATUS_OK);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -180,31 +180,19 @@ class ImageController extends GenericApiController
|
|||
int $id,
|
||||
bool $basic = false,
|
||||
bool $current = false,
|
||||
bool $tags = false
|
||||
bool $tags = false,
|
||||
string $clusters = ''
|
||||
): Http\Response {
|
||||
return Util::guardEx(function () use ($id, $basic, $current, $tags) {
|
||||
return Util::guardEx(function () use ($id, $basic, $current, $tags, $clusters) {
|
||||
$file = $this->fs->getUserFile($id);
|
||||
|
||||
// Get the image info
|
||||
$info = $this->timelineQuery->getInfoById($file->getId(), $basic);
|
||||
$info = $this->timelineQuery->getInfoById($id, $basic);
|
||||
|
||||
// Add fileid and etag
|
||||
$info['fileid'] = $file->getId();
|
||||
$info['etag'] = $file->getEtag();
|
||||
|
||||
// Allow these ony for logged in users
|
||||
if (null !== $this->userSession->getUser()) {
|
||||
// Get list of tags for this file
|
||||
if ($tags) {
|
||||
$info['tags'] = $this->getTags($file->getId());
|
||||
}
|
||||
|
||||
// Get latest exif data if requested
|
||||
if ($current) {
|
||||
$info['current'] = Exif::getExifFromFile($file);
|
||||
}
|
||||
}
|
||||
|
||||
// Inject permissions and convert to string
|
||||
$info['permissions'] = \OCA\Memories\Util::permissionsToStr($file->getPermissions());
|
||||
|
||||
|
@ -213,6 +201,31 @@ class ImageController extends GenericApiController
|
|||
$info['size'] = $file->getSize();
|
||||
$info['basename'] = $file->getName();
|
||||
|
||||
// Allow these ony for logged in users
|
||||
if (null !== $this->userSession->getUser()) {
|
||||
// Get list of tags for this file
|
||||
if ($tags) {
|
||||
$info['tags'] = $this->getTags($id);
|
||||
}
|
||||
|
||||
// Get latest exif data if requested
|
||||
if ($current) {
|
||||
$info['current'] = Exif::getExifFromFile($file);
|
||||
}
|
||||
|
||||
// Get clusters for this file
|
||||
if ($clusters) {
|
||||
$clist = [];
|
||||
foreach (explode(',', $clusters) as $type) {
|
||||
$backend = \OC::$server->get(\OCA\Memories\ClustersBackend\Manager::class)->get($type);
|
||||
if ($backend->isEnabled()) {
|
||||
$clist[$type] = $backend->getClusters($id);
|
||||
}
|
||||
}
|
||||
$info['clusters'] = $clist;
|
||||
}
|
||||
}
|
||||
|
||||
return new JSONResponse($info, Http::STATUS_OK);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -110,28 +110,23 @@ export default defineComponent({
|
|||
mixins: [UserConfig],
|
||||
|
||||
data: () => ({
|
||||
// Basic info and metadata
|
||||
fileid: null as number | null,
|
||||
filename: '',
|
||||
exif: {} as { [prop: string]: any },
|
||||
baseInfo: {} as IImageInfo,
|
||||
|
||||
// Cluster lists
|
||||
albums: [] as IAlbum[],
|
||||
people: [] as IFace[],
|
||||
|
||||
loading: 0,
|
||||
state: 0,
|
||||
}),
|
||||
|
||||
mounted() {
|
||||
subscribe('files:file:updated', this.handleFileUpdated);
|
||||
subscribe('memories:albums:update', this.refreshAlbums);
|
||||
subscribe('memories:albums:update', this.refresh);
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
unsubscribe('files:file:updated', this.handleFileUpdated);
|
||||
unsubscribe('memories:albums:update', this.refreshAlbums);
|
||||
unsubscribe('memories:albums:update', this.refresh);
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
@ -351,6 +346,14 @@ export default defineComponent({
|
|||
mapFullUrl(): string {
|
||||
return `https://www.openstreetmap.org/?mlat=${this.lat}&mlon=${this.lon}#map=18/${this.lat}/${this.lon}`;
|
||||
},
|
||||
|
||||
albums(): IAlbum[] {
|
||||
return this.baseInfo?.clusters?.albums ?? [];
|
||||
},
|
||||
|
||||
people(): IFace[] {
|
||||
return this.baseInfo?.clusters?.recognize ?? [];
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
@ -359,35 +362,34 @@ export default defineComponent({
|
|||
this.loading = 0;
|
||||
this.fileid = null;
|
||||
this.exif = {};
|
||||
this.albums = [];
|
||||
this.people = [];
|
||||
|
||||
const url = API.Q(utils.getImageInfoUrl(photo), { tags: 1 });
|
||||
// which clusters to get
|
||||
const clusters = [
|
||||
this.config.albums_enabled ? 'albums' : null,
|
||||
this.config.recognize_enabled ? 'recognize' : null,
|
||||
]
|
||||
.filter((c) => c)
|
||||
.join(',');
|
||||
|
||||
// get tags if enabled
|
||||
const tags = this.config.systemtags_enabled ? 1 : 0;
|
||||
|
||||
// get image info
|
||||
const url = API.Q(utils.getImageInfoUrl(photo), { tags, clusters });
|
||||
const res = await this.guardState(axios.get<IImageInfo>(url));
|
||||
if (!res) return null;
|
||||
|
||||
// unwrap basic info
|
||||
this.fileid = res.data.fileid;
|
||||
this.filename = res.data.basename;
|
||||
this.exif = res.data.exif || {};
|
||||
this.baseInfo = res.data;
|
||||
|
||||
// trigger other refreshes
|
||||
if (!utils.isLocalPhoto(photo)) {
|
||||
this.refreshAlbums();
|
||||
this.refreshPeople();
|
||||
}
|
||||
|
||||
return this.baseInfo;
|
||||
},
|
||||
|
||||
async refreshAlbums(): Promise<void> {
|
||||
if (!this.config.albums_enabled) return;
|
||||
this.albums = (await this.guardState(dav.getAlbums(1, this.fileid!))) ?? this.albums;
|
||||
},
|
||||
|
||||
async refreshPeople(): Promise<void> {
|
||||
if (!this.config.recognize_enabled) return;
|
||||
this.people = (await this.guardState(dav.getFaceList('recognize', this.fileid!))) ?? this.people;
|
||||
async refresh() {
|
||||
if (this.fileid) await this.update(this.fileid);
|
||||
},
|
||||
|
||||
async guardState<T>(promise: Promise<T>): Promise<T | null> {
|
||||
|
@ -406,7 +408,7 @@ export default defineComponent({
|
|||
|
||||
handleFileUpdated({ fileid }: { fileid: number }) {
|
||||
if (fileid && this.fileid === fileid) {
|
||||
this.update(this.fileid);
|
||||
this.refresh();
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
@ -84,8 +84,8 @@ export class API {
|
|||
return tok(gen(`${BASE}/folders/sub`));
|
||||
}
|
||||
|
||||
static ALBUM_LIST(fileid?: number) {
|
||||
return API.Q(gen(`${BASE}/clusters/albums`), { fileid });
|
||||
static ALBUM_LIST() {
|
||||
return gen(`${BASE}/clusters/albums`);
|
||||
}
|
||||
|
||||
static ALBUM_DOWNLOAD(user: string, name: string) {
|
||||
|
@ -106,8 +106,8 @@ export class API {
|
|||
return gen(`${BASE}/tags/set/{fileid}`, { fileid });
|
||||
}
|
||||
|
||||
static FACE_LIST(app: 'recognize' | 'facerecognition', fileid?: number) {
|
||||
return API.Q(gen(`${BASE}/clusters/${app}`), { fileid });
|
||||
static FACE_LIST(app: 'recognize' | 'facerecognition') {
|
||||
return gen(`${BASE}/clusters/${app}`);
|
||||
}
|
||||
|
||||
static CLUSTER_PREVIEW(backend: ClusterTypes, name: string | number) {
|
||||
|
|
|
@ -26,7 +26,9 @@ export function getAlbumPath(user: string, name: string) {
|
|||
* @param fileid Optional file ID to get albums for
|
||||
*/
|
||||
export async function getAlbums(sort: 1 | 2 = 1, fileid?: number) {
|
||||
const data = (await axios.get<IAlbum[]>(API.ALBUM_LIST(fileid))).data;
|
||||
const url = API.Q(API.ALBUM_LIST(), { fileid });
|
||||
const res = await axios.get<IAlbum[]>(url);
|
||||
const data = res.data;
|
||||
|
||||
// Sort the response
|
||||
switch (sort) {
|
||||
|
|
|
@ -11,10 +11,9 @@ import * as base from './base';
|
|||
/**
|
||||
* Get list of faces
|
||||
* @param app Backend app to use
|
||||
* @param fileid File to filter by (optional)
|
||||
*/
|
||||
export async function getFaceList(app: 'recognize' | 'facerecognition', fileid?: number) {
|
||||
return (await axios.get<IFace[]>(API.FACE_LIST(app, fileid))).data;
|
||||
export async function getFaceList(app: 'recognize' | 'facerecognition') {
|
||||
return (await axios.get<IFace[]>(API.FACE_LIST(app))).data;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -105,6 +105,11 @@ export interface IImageInfo {
|
|||
Description?: string;
|
||||
[other: string]: unknown;
|
||||
};
|
||||
|
||||
clusters?: {
|
||||
albums?: IAlbum[];
|
||||
recognize?: IFace[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface IFolder extends IPhoto {
|
||||
|
|
Loading…
Reference in New Issue