Include filename in days

old-stable24^2
Varun Patil 2022-10-28 18:11:58 -07:00
parent 9209b8f55d
commit cd2f714e92
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)
{
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

View File

@ -102,10 +102,17 @@ trait TimelineQueryDays
// We don't actually use m.datetaken here, but postgres
// needs that all fields in ORDER BY are also in SELECT
// 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')
;
// JOIN with filecache for existing files
$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
if (null !== $day_ids) {
@ -155,7 +162,30 @@ trait TimelineQueryDays
*/
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) {
// 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
if (isset($row['path']) && !empty($row['path'])) {
if (0 === strpos($row['path'], $basePath)) {
$row['filename'] = substr($row['path'], \strlen($basePath));
$row['filename'] = $davPath.substr($row['path'], \strlen($basePath));
}
unset($row['path']);
}

View File

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

View File

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

View File

@ -3,14 +3,7 @@ import { getCurrentUser } from "@nextcloud/auth";
import { generateUrl } from "@nextcloud/router";
import { showError } from "@nextcloud/dialogs";
import { translate as t, translatePlural as n } from "@nextcloud/l10n";
import {
IAlbum,
IDay,
IExtendedPhoto,
IFileInfo,
IPhoto,
ITag,
} from "../../types";
import { IAlbum, IDay, IFileInfo, IPhoto, ITag } from "../../types";
import { constants } from "../Utils";
import axios from "@nextcloud/axios";
import client from "../DavClient";
@ -270,15 +263,13 @@ export function getAlbumFileInfos(
albumUser: string,
albumName: string
): IFileInfo[] {
const ephotos = photos as IExtendedPhoto[];
const uid = getCurrentUser()?.uid;
const collection =
albumUser === uid
? `/photos/${uid}/albums/${albumName}`
: `/photos/${uid}/sharedalbums/${albumName} (${albumUser})`;
return ephotos.map((photo) => {
return photos.map((photo) => {
const basename =
albumUser === uid
? `${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);
}
// 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
const fileIds = photos.map((photo) => photo.fileid);
const fileIds = photosWithoutFilename.map((photo) => photo.fileid);
// Divide fileIds into chunks of GET_FILE_CHUNK_SIZE
const chunks = [];
@ -56,8 +81,10 @@ export async function getFiles(photos: IPhoto[]): Promise<IFileInfo[]> {
}
// Get file infos for each chunk
const fileInfos = await Promise.all(chunks.map(getFilesInternal));
return fileInfos.flat();
const ef = await Promise.all(chunks.map(getFilesInternal));
fileInfos = fileInfos.concat(ef.flat());
return fileInfos;
}
/**
@ -152,16 +179,6 @@ export async function* runInParallel<T>(
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
*
@ -189,11 +206,15 @@ export async function* deletePhotos(photos: IPhoto[]) {
fileInfos = fileInfos.filter((f) => fileIdsSet.has(f.fileid));
const calls = fileInfos.map((fileInfo) => async () => {
try {
await deleteFile(fileInfo.filename);
await client.deleteFile(fileInfo.originalFilename);
return fileInfo.fileid;
} catch (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;
}
});

View File

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