Include filename in days

old-stable24
Varun Patil 2022-10-28 18:11:58 -07:00
parent 87e96141c4
commit 38b37ad32f
7 changed files with 77 additions and 45 deletions

View File

@ -45,14 +45,6 @@ class TimelineQuery
public function transformExtraFields(IQueryBuilder &$query, string $uid, array &$fields) public function transformExtraFields(IQueryBuilder &$query, string $uid, array &$fields)
{ {
if (\in_array('basename', $fields, true)) {
$query->addSelect('f.name AS basename');
}
if (\in_array('mimetype', $fields, true)) {
$query->join('f', 'mimetypes', 'mimetypes', $query->expr()->eq('f.mimetype', 'mimetypes.id'));
$query->addSelect('mimetypes.mimetype');
}
} }
public function getInfoById(int $id): array public function getInfoById(int $id): array

View File

@ -102,10 +102,17 @@ trait TimelineQueryDays
// We don't actually use m.datetaken here, but postgres // We don't actually use m.datetaken here, but postgres
// needs that all fields in ORDER BY are also in SELECT // needs that all fields in ORDER BY are also in SELECT
// when using DISTINCT on selected fields // when using DISTINCT on selected fields
$query->select($fileid, 'f.etag', 'f.path', 'm.isvideo', 'vco.categoryid', 'm.datetaken', 'm.dayid', 'm.w', 'm.h') $query->select($fileid, 'm.isvideo', 'm.datetaken', 'm.dayid', 'm.w', 'm.h')
->from('memories', 'm') ->from('memories', 'm')
; ;
// JOIN with filecache for existing files
$query = $this->joinFilecache($query, $folder, $recursive, $archive); $query = $this->joinFilecache($query, $folder, $recursive, $archive);
$query->addSelect('f.etag', 'f.path', 'f.name AS basename');
// JOIN with mimetypes to get the mimetype
$query->join('f', 'mimetypes', 'mimetypes', $query->expr()->eq('f.mimetype', 'mimetypes.id'));
$query->addSelect('mimetypes.mimetype');
// Filter by dayid unless wildcard // Filter by dayid unless wildcard
if (null !== $day_ids) { if (null !== $day_ids) {
@ -155,7 +162,30 @@ trait TimelineQueryDays
*/ */
private function processDay(&$day, $folder) private function processDay(&$day, $folder)
{ {
$basePath = null !== $folder ? $folder->getInternalPath() : '#__#__#'; $basePath = '#__#__#';
$davPath = '/';
if (null !== $folder) {
// No way to get the internal path from the folder
$query = $this->connection->getQueryBuilder();
$query->select('path')
->from('filecache')
->where($query->expr()->eq('fileid', $query->createNamedParameter($folder->getId(), IQueryBuilder::PARAM_INT)))
;
$path = $query->executeQuery()->fetchOne();
$basePath = $path ?: $basePath;
// Get user facing path
// getPath looks like /user/files/... but we want /files/user/...
// Split at / and swap these
$actualPath = $folder->getPath();
$actualPath = explode('/', $actualPath);
if (\count($actualPath) >= 3) {
$tmp = $actualPath[1];
$actualPath[1] = $actualPath[2];
$actualPath[2] = $tmp;
$davPath = implode('/', $actualPath);
}
}
foreach ($day as &$row) { foreach ($day as &$row) {
// We don't need date taken (see query builder) // We don't need date taken (see query builder)
@ -178,7 +208,7 @@ trait TimelineQueryDays
// Check if path exists and starts with basePath and remove // Check if path exists and starts with basePath and remove
if (isset($row['path']) && !empty($row['path'])) { if (isset($row['path']) && !empty($row['path'])) {
if (0 === strpos($row['path'], $basePath)) { if (0 === strpos($row['path'], $basePath)) {
$row['filename'] = substr($row['path'], \strlen($basePath)); $row['filename'] = $davPath.substr($row['path'], \strlen($basePath));
} }
unset($row['path']); unset($row['path']);
} }

View File

@ -23,6 +23,7 @@ trait TimelineQueryFilters
$query->expr()->eq('vco.objid', 'm.fileid'), $query->expr()->eq('vco.objid', 'm.fileid'),
$query->expr()->in('vco.categoryid', $this->getFavoriteVCategoryFun($query, $userId)), $query->expr()->in('vco.categoryid', $this->getFavoriteVCategoryFun($query, $userId)),
)); ));
$query->addSelect('vco.categoryid');
} }
public function transformVideoFilter(IQueryBuilder &$query, string $userId) public function transformVideoFilter(IQueryBuilder &$query, string $userId)

View File

@ -21,7 +21,7 @@
*/ */
import { generateUrl } from "@nextcloud/router"; import { generateUrl } from "@nextcloud/router";
import camelcase from "camelcase"; import camelcase from "camelcase";
import { IExtendedPhoto, IFileInfo, IPhoto } from "../types"; import { IFileInfo, IPhoto } from "../types";
import { isNumber } from "./NumberUtils"; import { isNumber } from "./NumberUtils";
/** /**

View File

@ -3,14 +3,7 @@ import { getCurrentUser } from "@nextcloud/auth";
import { generateUrl } from "@nextcloud/router"; import { generateUrl } from "@nextcloud/router";
import { showError } from "@nextcloud/dialogs"; import { showError } from "@nextcloud/dialogs";
import { translate as t, translatePlural as n } from "@nextcloud/l10n"; import { translate as t, translatePlural as n } from "@nextcloud/l10n";
import { import { IAlbum, IDay, IFileInfo, IPhoto, ITag } from "../../types";
IAlbum,
IDay,
IExtendedPhoto,
IFileInfo,
IPhoto,
ITag,
} from "../../types";
import { constants } from "../Utils"; import { constants } from "../Utils";
import axios from "@nextcloud/axios"; import axios from "@nextcloud/axios";
import client from "../DavClient"; import client from "../DavClient";
@ -270,15 +263,13 @@ export function getAlbumFileInfos(
albumUser: string, albumUser: string,
albumName: string albumName: string
): IFileInfo[] { ): IFileInfo[] {
const ephotos = photos as IExtendedPhoto[];
const uid = getCurrentUser()?.uid; const uid = getCurrentUser()?.uid;
const collection = const collection =
albumUser === uid albumUser === uid
? `/photos/${uid}/albums/${albumName}` ? `/photos/${uid}/albums/${albumName}`
: `/photos/${uid}/sharedalbums/${albumName} (${albumUser})`; : `/photos/${uid}/sharedalbums/${albumName} (${albumUser})`;
return ephotos.map((photo) => { return photos.map((photo) => {
const basename = const basename =
albumUser === uid albumUser === uid
? `${photo.fileid}-${photo.basename}` ? `${photo.fileid}-${photo.basename}`

View File

@ -46,8 +46,33 @@ export async function getFiles(photos: IPhoto[]): Promise<IFileInfo[]> {
return getAlbumFileInfos(photos, route.params.user, route.params.name); return getAlbumFileInfos(photos, route.params.user, route.params.name);
} }
// Get file infos
let fileInfos: IFileInfo[] = [];
// Get all photos that already have and don't have a filename
const photosWithFilename = photos.filter((photo) => photo.filename);
fileInfos = fileInfos.concat(
photosWithFilename.map((photo) => {
return {
fileid: photo.fileid,
filename: photo.filename.split("/").slice(3).join("/"),
originalFilename: photo.filename,
basename: photo.basename,
mime: photo.mimetype,
hasPreview: true,
etag: photo.etag,
} as IFileInfo;
})
);
// Next: get all photos that have no filename using ID
if (photosWithFilename.length === photos.length) {
return fileInfos;
}
const photosWithoutFilename = photos.filter((photo) => !photo.filename);
// Get file IDs array // Get file IDs array
const fileIds = photos.map((photo) => photo.fileid); const fileIds = photosWithoutFilename.map((photo) => photo.fileid);
// Divide fileIds into chunks of GET_FILE_CHUNK_SIZE // Divide fileIds into chunks of GET_FILE_CHUNK_SIZE
const chunks = []; const chunks = [];
@ -56,8 +81,10 @@ export async function getFiles(photos: IPhoto[]): Promise<IFileInfo[]> {
} }
// Get file infos for each chunk // Get file infos for each chunk
const fileInfos = await Promise.all(chunks.map(getFilesInternal)); const ef = await Promise.all(chunks.map(getFilesInternal));
return fileInfos.flat(); fileInfos = fileInfos.concat(ef.flat());
return fileInfos;
} }
/** /**
@ -152,16 +179,6 @@ export async function* runInParallel<T>(
return; return;
} }
/**
* Delete a single file
*
* @param path path to the file
*/
export async function deleteFile(path: string) {
const prefixPath = `/files/${getCurrentUser()?.uid}`;
return await client.deleteFile(`${prefixPath}${path}`);
}
/** /**
* Delete all files in a given list of Ids * Delete all files in a given list of Ids
* *
@ -189,11 +206,15 @@ export async function* deletePhotos(photos: IPhoto[]) {
fileInfos = fileInfos.filter((f) => fileIdsSet.has(f.fileid)); fileInfos = fileInfos.filter((f) => fileIdsSet.has(f.fileid));
const calls = fileInfos.map((fileInfo) => async () => { const calls = fileInfos.map((fileInfo) => async () => {
try { try {
await deleteFile(fileInfo.filename); await client.deleteFile(fileInfo.originalFilename);
return fileInfo.fileid; return fileInfo.fileid;
} catch (error) { } catch (error) {
console.error("Failed to delete", fileInfo, error); console.error("Failed to delete", fileInfo, error);
showError(t("memories", "Failed to delete {fileName}.", fileInfo)); showError(
t("memories", "Failed to delete {fileName}.", {
fileName: fileInfo.filename,
})
);
return 0; return 0;
} }
}); });

View File

@ -39,6 +39,10 @@ export type IPhoto = {
etag?: string; etag?: string;
/** Path to file */ /** Path to file */
filename?: string; filename?: string;
/** Base name of file */
basename?: string;
/** Mime type of file */
mimetype?: string;
/** Bit flags */ /** Bit flags */
flag: number; flag: number;
/** DayID from server */ /** DayID from server */
@ -81,13 +85,6 @@ export type IPhoto = {
datetaken?: number; datetaken?: number;
}; };
export interface IExtendedPhoto extends IPhoto {
/** Base name of file */
basename: string;
/** Mime type of file */
mimetype: string;
}
export interface IFolder extends IPhoto { export interface IFolder extends IPhoto {
/** Path to folder */ /** Path to folder */
path: string; path: string;