albums: refactor backend
Signed-off-by: Varun Patil <radialapps@gmail.com>pull/767/head
parent
912e05fae8
commit
4da3bd938a
|
@ -94,14 +94,12 @@ class AlbumsBackend extends Backend
|
||||||
|
|
||||||
// Run actual query
|
// Run actual query
|
||||||
$list = [];
|
$list = [];
|
||||||
$t = (int) $request->getParam('t', 0);
|
$fileid = (int) $request->getParam('fileid', -1);
|
||||||
$fileid = (int) $request->getParam('fid', -1);
|
|
||||||
if ($t & 1) { // personal
|
// personal albums
|
||||||
$list = array_merge($list, $this->albumsQuery->getList(Util::getUID(), $fileid));
|
$list = array_merge($list, $this->albumsQuery->getList(Util::getUID(), false, $fileid));
|
||||||
}
|
// shared albums
|
||||||
if ($t & 2) { // shared
|
$list = array_merge($list, $this->albumsQuery->getList(Util::getUID(), true, $fileid));
|
||||||
$list = array_merge($list, $this->albumsQuery->getList(Util::getUID(), $fileid, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove elements with duplicate album_id
|
// Remove elements with duplicate album_id
|
||||||
$seenIds = [];
|
$seenIds = [];
|
||||||
|
|
|
@ -16,17 +16,17 @@ class AlbumsQuery
|
||||||
$this->connection = $connection;
|
$this->connection = $connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get list of albums */
|
/**
|
||||||
public function getList(string $uid, int $fileId, bool $shared = false)
|
* Get list of albums.
|
||||||
|
*
|
||||||
|
* @param bool $shared Whether to get shared albums
|
||||||
|
* @param int $fileid File to filter by
|
||||||
|
*/
|
||||||
|
public function getList(string $uid, bool $shared = false, int $fileid = -1)
|
||||||
{
|
{
|
||||||
$query = $this->connection->getQueryBuilder();
|
$query = $this->connection->getQueryBuilder();
|
||||||
$allPhotosQuery = $this->connection->getQueryBuilder();
|
|
||||||
|
|
||||||
// SELECT everything from albums
|
// SELECT everything from albums
|
||||||
$allPhotosQuery->select('album_id')->from('photos_albums_files');
|
|
||||||
$allPhotosQuery->where(
|
|
||||||
$allPhotosQuery->expr()->eq('file_id', $allPhotosQuery->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))
|
|
||||||
);
|
|
||||||
$count = $query->func()->count($query->createFunction('DISTINCT m.fileid'), 'count');
|
$count = $query->func()->count($query->createFunction('DISTINCT m.fileid'), 'count');
|
||||||
$query->select(
|
$query->select(
|
||||||
'pa.album_id',
|
'pa.album_id',
|
||||||
|
@ -67,14 +67,22 @@ class AlbumsQuery
|
||||||
$query->orderBy('pa.created', 'DESC');
|
$query->orderBy('pa.created', 'DESC');
|
||||||
$query->addOrderBy('pa.album_id', 'DESC'); // tie-breaker
|
$query->addOrderBy('pa.album_id', 'DESC'); // tie-breaker
|
||||||
|
|
||||||
|
// WHERE these albums contain fileid if specified
|
||||||
|
if ($fileid > 0) {
|
||||||
|
$fSq = $this->connection->getQueryBuilder()
|
||||||
|
->select('paf.file_id')
|
||||||
|
->from('photos_albums_files', 'paf')
|
||||||
|
->where($query->expr()->andX(
|
||||||
|
$query->expr()->eq('paf.album_id', 'pa.album_id'),
|
||||||
|
$query->expr()->eq('paf.file_id', $query->createNamedParameter($fileid, IQueryBuilder::PARAM_INT)),
|
||||||
|
))
|
||||||
|
->getSQL()
|
||||||
|
;
|
||||||
|
$query->andWhere($query->createFunction("EXISTS ({$fSq})"));
|
||||||
|
}
|
||||||
|
|
||||||
// FETCH all albums
|
// FETCH all albums
|
||||||
$albums = $query->executeQuery()->fetchAll();
|
$albums = $query->executeQuery()->fetchAll();
|
||||||
$allPhotos = $allPhotosQuery->executeQuery()->fetchAll();
|
|
||||||
$albumIds = array();
|
|
||||||
|
|
||||||
foreach ($allPhotos as &$album) {
|
|
||||||
$albumIds[$album['album_id']] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post process
|
// Post process
|
||||||
foreach ($albums as &$row) {
|
foreach ($albums as &$row) {
|
||||||
|
@ -83,7 +91,6 @@ class AlbumsQuery
|
||||||
$row['album_id'] = $albumId;
|
$row['album_id'] = $albumId;
|
||||||
$row['created'] = (int) $row['created'];
|
$row['created'] = (int) $row['created'];
|
||||||
$row['last_added_photo'] = (int) $row['last_added_photo'];
|
$row['last_added_photo'] = (int) $row['last_added_photo'];
|
||||||
$row['has_file'] = !!$albumIds[$albumId];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $albums;
|
return $albums;
|
||||||
|
|
|
@ -89,7 +89,7 @@ export default defineComponent({
|
||||||
await this.$refs.dtm?.refresh?.();
|
await this.$refs.dtm?.refresh?.();
|
||||||
|
|
||||||
if (this.routeIsAlbums) {
|
if (this.routeIsAlbums) {
|
||||||
this.items = await dav.getAlbums(3, this.config.album_list_sort);
|
this.items = await dav.getAlbums(this.config.album_list_sort);
|
||||||
} else if (this.routeIsTags) {
|
} else if (this.routeIsTags) {
|
||||||
this.items = await dav.getTags();
|
this.items = await dav.getTags();
|
||||||
} else if (this.routeIsPeople) {
|
} else if (this.routeIsPeople) {
|
||||||
|
|
|
@ -73,8 +73,8 @@ import LocationIcon from 'vue-material-design-icons/MapMarker.vue';
|
||||||
import TagIcon from 'vue-material-design-icons/Tag.vue';
|
import TagIcon from 'vue-material-design-icons/Tag.vue';
|
||||||
|
|
||||||
import * as utils from '../services/Utils';
|
import * as utils from '../services/Utils';
|
||||||
|
import * as dav from '../services/DavRequests';
|
||||||
import { API } from '../services/API';
|
import { API } from '../services/API';
|
||||||
import router from '../router';
|
|
||||||
|
|
||||||
import type { IAlbum, IImageInfo, IPhoto } from '../types';
|
import type { IAlbum, IImageInfo, IPhoto } from '../types';
|
||||||
|
|
||||||
|
@ -359,17 +359,13 @@ export default defineComponent({
|
||||||
async refreshAlbums(): Promise<IAlbum[]> {
|
async refreshAlbums(): Promise<IAlbum[]> {
|
||||||
const state = this.state;
|
const state = this.state;
|
||||||
|
|
||||||
// get album list
|
|
||||||
let list: IAlbum[] = [];
|
let list: IAlbum[] = [];
|
||||||
try {
|
try {
|
||||||
list = (await axios.get<IAlbum[]>(API.ALBUM_LIST(3, this.fileid!))).data;
|
list = await dav.getAlbums(1, this.fileid!);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('metadata: failed to load albums', e);
|
console.error('metadata: failed to load albums', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter albums containing this file
|
|
||||||
list = list.filter((a) => a.has_file);
|
|
||||||
|
|
||||||
if (state !== this.state) return list;
|
if (state !== this.state) return list;
|
||||||
return (this.albums = list);
|
return (this.albums = list);
|
||||||
},
|
},
|
||||||
|
|
|
@ -67,13 +67,11 @@ import { defineComponent, PropType } from 'vue';
|
||||||
import AlbumForm from './AlbumForm.vue';
|
import AlbumForm from './AlbumForm.vue';
|
||||||
import AlbumsList from './AlbumsList.vue';
|
import AlbumsList from './AlbumsList.vue';
|
||||||
|
|
||||||
import axios from '@nextcloud/axios';
|
|
||||||
|
|
||||||
import NcButton from '@nextcloud/vue/dist/Components/NcButton';
|
import NcButton from '@nextcloud/vue/dist/Components/NcButton';
|
||||||
const NcListItem = () => import('@nextcloud/vue/dist/Components/NcListItem');
|
const NcListItem = () => import('@nextcloud/vue/dist/Components/NcListItem');
|
||||||
|
|
||||||
|
import * as dav from '../../services/DavRequests';
|
||||||
import { IAlbum, IPhoto } from '../../types';
|
import { IAlbum, IPhoto } from '../../types';
|
||||||
import { API } from '../../services/API';
|
|
||||||
|
|
||||||
import PlusIcon from 'vue-material-design-icons/Plus.vue';
|
import PlusIcon from 'vue-material-design-icons/Plus.vue';
|
||||||
import CheckIcon from 'vue-material-design-icons/Check.vue';
|
import CheckIcon from 'vue-material-design-icons/Check.vue';
|
||||||
|
@ -108,9 +106,11 @@ export default defineComponent({
|
||||||
loadingAlbums: true,
|
loadingAlbums: true,
|
||||||
/** List of all albums */
|
/** List of all albums */
|
||||||
albums: [] as IAlbum[],
|
albums: [] as IAlbum[],
|
||||||
/** All selected albums */
|
/** Initial selection */
|
||||||
|
initSelection: new Set<IAlbum>(),
|
||||||
|
/** Selected albums */
|
||||||
selection: new Set<IAlbum>(),
|
selection: new Set<IAlbum>(),
|
||||||
/** Deselected albums that were previously selected */
|
/** Deselected albums that were initially selected */
|
||||||
deselection: new Set<IAlbum>(),
|
deselection: new Set<IAlbum>(),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
@ -128,14 +128,21 @@ export default defineComponent({
|
||||||
try {
|
try {
|
||||||
this.loadingAlbums = true;
|
this.loadingAlbums = true;
|
||||||
|
|
||||||
// this only makes sense when we try to add single photo to albums
|
// get all albums
|
||||||
const fileid = this.photos.length === 1 ? this.photos[0].fileid : -1;
|
this.albums = await dav.getAlbums();
|
||||||
|
|
||||||
// get albums, possibly for one photo
|
// reset selection
|
||||||
const res = await axios.get<IAlbum[]>(API.ALBUM_LIST(3, fileid));
|
this.initSelection = new Set();
|
||||||
this.albums = res.data;
|
this.selection = new Set();
|
||||||
this.selection = new Set(this.albums.filter((album) => album.has_file));
|
|
||||||
this.deselection = new Set();
|
this.deselection = new Set();
|
||||||
|
|
||||||
|
// if only one photo is selected, get the albums of that photo
|
||||||
|
const fileid = this.photos.length === 1 ? this.photos[0].fileid : 0;
|
||||||
|
if (fileid) {
|
||||||
|
const selIds = new Set((await dav.getAlbums(1, fileid)).map((a) => a.album_id));
|
||||||
|
this.initSelection = new Set(this.albums.filter((a) => selIds.has(a.album_id)));
|
||||||
|
this.selection = new Set(this.initSelection);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -150,7 +157,7 @@ export default defineComponent({
|
||||||
this.selection.delete(album);
|
this.selection.delete(album);
|
||||||
|
|
||||||
// deselection only if originally selected
|
// deselection only if originally selected
|
||||||
if (album.has_file) {
|
if (this.initSelection.has(album)) {
|
||||||
this.deselection.add(album);
|
this.deselection.add(album);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -37,7 +37,7 @@ export enum DaysFilterType {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class API {
|
export class API {
|
||||||
static Q(url: string, query: string | URLSearchParams | Object | undefined | null) {
|
static Q(url: string, query: string | URLSearchParams | Object | undefined | null): string {
|
||||||
if (!query) return url;
|
if (!query) return url;
|
||||||
|
|
||||||
if (typeof query === 'object') {
|
if (typeof query === 'object') {
|
||||||
|
@ -84,8 +84,8 @@ export class API {
|
||||||
return tok(gen(`${BASE}/folders/sub`));
|
return tok(gen(`${BASE}/folders/sub`));
|
||||||
}
|
}
|
||||||
|
|
||||||
static ALBUM_LIST(t: 1 | 2 | 3 = 3, photoId: number = -1) {
|
static ALBUM_LIST(fileid?: number) {
|
||||||
return gen(`${BASE}/clusters/albums?t=${t}&fid=${photoId}`);
|
return API.Q(gen(`${BASE}/clusters/albums`), { fileid });
|
||||||
}
|
}
|
||||||
|
|
||||||
static ALBUM_DOWNLOAD(user: string, name: string) {
|
static ALBUM_DOWNLOAD(user: string, name: string) {
|
||||||
|
|
|
@ -22,14 +22,14 @@ export function getAlbumPath(user: string, name: string) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get list of albums.
|
* Get list of albums.
|
||||||
* @param type Type of albums to get; 1 = personal, 2 = shared, 3 = all
|
* @param sort Sort order; 1 = by date, 2 = by name
|
||||||
* @param sortOrder Sort order; 1 = by date, 2 = by name
|
* @param fileid Optional file ID to get albums for
|
||||||
*/
|
*/
|
||||||
export async function getAlbums(type: 1 | 2 | 3, sortOrder: 1 | 2) {
|
export async function getAlbums(sort: 1 | 2 = 1, fileid?: number) {
|
||||||
const data = (await axios.get<IAlbum[]>(API.ALBUM_LIST(type))).data;
|
const data = (await axios.get<IAlbum[]>(API.ALBUM_LIST(fileid))).data;
|
||||||
|
|
||||||
// Sort the response
|
// Sort the response
|
||||||
switch (sortOrder) {
|
switch (sort) {
|
||||||
case 2:
|
case 2:
|
||||||
data.sort((a, b) => a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }));
|
data.sort((a, b) => a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }));
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -140,8 +140,6 @@ export interface IAlbum extends ICluster {
|
||||||
location: string;
|
location: string;
|
||||||
/** File ID of last added photo */
|
/** File ID of last added photo */
|
||||||
last_added_photo: number;
|
last_added_photo: number;
|
||||||
/** Whether an album contains the file */
|
|
||||||
has_file: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IFace extends ICluster {
|
export interface IFace extends ICluster {
|
||||||
|
|
Loading…
Reference in New Issue