refactor: use consistent preview sizes (fix #679)
Signed-off-by: Varun Patil <radialapps@gmail.com>pull/685/head
parent
078f4f7a5c
commit
4e283ecc93
|
@ -107,7 +107,7 @@ trait TimelineQueryMap
|
||||||
|
|
||||||
// SELECT these files from the filecache
|
// SELECT these files from the filecache
|
||||||
$query = $this->connection->getQueryBuilder();
|
$query = $this->connection->getQueryBuilder();
|
||||||
$query->select('m.fileid', 'm.dayid', 'm.mapcluster', 'f.etag')
|
$query->select('m.fileid', 'm.dayid', 'm.mapcluster', 'm.h', 'm.w', 'f.etag')
|
||||||
->from('memories', 'm')
|
->from('memories', 'm')
|
||||||
->innerJoin('m', 'filecache', 'f', $query->expr()->eq('m.fileid', 'f.fileid'))
|
->innerJoin('m', 'filecache', 'f', $query->expr()->eq('m.fileid', 'f.fileid'))
|
||||||
->where($query->expr()->in('m.fileid', $query->createNamedParameter($fileIds, IQueryBuilder::PARAM_INT_ARRAY)))
|
->where($query->expr()->in('m.fileid', $query->createNamedParameter($fileIds, IQueryBuilder::PARAM_INT_ARRAY)))
|
||||||
|
@ -119,6 +119,8 @@ trait TimelineQueryMap
|
||||||
$row['fileid'] = (int) $row['fileid'];
|
$row['fileid'] = (int) $row['fileid'];
|
||||||
$row['mapcluster'] = (int) $row['mapcluster'];
|
$row['mapcluster'] = (int) $row['mapcluster'];
|
||||||
$row['dayid'] = (int) $row['dayid'];
|
$row['dayid'] = (int) $row['dayid'];
|
||||||
|
$row['h'] = (int) $row['h'];
|
||||||
|
$row['w'] = (int) $row['w'];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $files;
|
return $files;
|
||||||
|
|
|
@ -48,7 +48,7 @@ declare global {
|
||||||
|
|
||||||
var mViewer: {
|
var mViewer: {
|
||||||
open: (anchorPhoto: IPhoto, rows: IRow[]) => Promise<void>;
|
open: (anchorPhoto: IPhoto, rows: IRow[]) => Promise<void>;
|
||||||
openStatic(photo: IPhoto, list: IPhoto[], thumbSize?: number): Promise<void>;
|
openStatic(photo: IPhoto, list: IPhoto[], thumbSize?: 256 | 512): Promise<void>;
|
||||||
close: () => void;
|
close: () => void;
|
||||||
isOpen: () => boolean;
|
isOpen: () => boolean;
|
||||||
};
|
};
|
||||||
|
|
|
@ -68,7 +68,7 @@ export default defineComponent({
|
||||||
etag: this.album.album_id,
|
etag: this.album.album_id,
|
||||||
flag: 0,
|
flag: 0,
|
||||||
} as unknown as IPhoto;
|
} as unknown as IPhoto;
|
||||||
return getPreviewUrl(mock, true, 512);
|
return getPreviewUrl({ photo: mock, sqsize: 512 });
|
||||||
}
|
}
|
||||||
|
|
||||||
return API.CLUSTER_PREVIEW(this.data.cluster_type, this.data.cluster_id);
|
return API.CLUSTER_PREVIEW(this.data.cluster_type, this.data.cluster_id);
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
<div class="previews fill-block">
|
<div class="previews fill-block">
|
||||||
<div class="preview-container fill-block">
|
<div class="preview-container fill-block">
|
||||||
<div class="img-outer" v-for="info of previews" :key="info.fileid">
|
<div class="img-outer" v-for="info of previews" :key="info.fileid">
|
||||||
<XImg class="ximg fill-block" :src="getPreviewUrl(info, true, 256)" />
|
<XImg class="ximg fill-block" :src="previewUrl(info)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
import { defineComponent, PropType } from 'vue';
|
import { defineComponent, PropType } from 'vue';
|
||||||
import { IFolder, IPhoto } from '../../types';
|
import { IFolder, IPhoto } from '../../types';
|
||||||
|
|
||||||
import { getPreviewUrl } from '../../services/utils/helpers';
|
import * as utils from '../../services/utils/helpers';
|
||||||
|
|
||||||
import UserConfig from '../../mixins/UserConfig';
|
import UserConfig from '../../mixins/UserConfig';
|
||||||
import FolderIcon from 'vue-material-design-icons/Folder.vue';
|
import FolderIcon from 'vue-material-design-icons/Folder.vue';
|
||||||
|
@ -53,8 +53,6 @@ export default defineComponent({
|
||||||
previews: [] as IPhoto[],
|
previews: [] as IPhoto[],
|
||||||
// Error occured fetching thumbs
|
// Error occured fetching thumbs
|
||||||
error: false,
|
error: false,
|
||||||
// Passthrough
|
|
||||||
getPreviewUrl,
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -107,6 +105,14 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** Get preview url */
|
||||||
|
previewUrl(info: IPhoto) {
|
||||||
|
return utils.getPreviewUrl({
|
||||||
|
photo: info,
|
||||||
|
sqsize: 256,
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -160,7 +160,7 @@ export default defineComponent({
|
||||||
|
|
||||||
/** Get url of the photo */
|
/** Get url of the photo */
|
||||||
url() {
|
url() {
|
||||||
let base = 256;
|
let base: 256 | 512 = 256;
|
||||||
|
|
||||||
// Check if displayed size is larger than the image
|
// Check if displayed size is larger than the image
|
||||||
if (this.data.dispH! > base * 0.9 && this.data.dispW! > base * 0.9) {
|
if (this.data.dispH! > base * 0.9 && this.data.dispW! > base * 0.9) {
|
||||||
|
@ -172,13 +172,10 @@ export default defineComponent({
|
||||||
base = 512;
|
base = 512;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make the shorter dimension equal to base
|
return utils.getPreviewUrl({
|
||||||
let size = base;
|
photo: this.data,
|
||||||
if (this.data.w && this.data.h) {
|
msize: base,
|
||||||
size = Math.floor((base * Math.max(this.data.w, this.data.h)) / Math.min(this.data.w, this.data.h)) - 1;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
return utils.getPreviewUrl(this.data, false, size);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Set src with overlay face rect */
|
/** Set src with overlay face rect */
|
||||||
|
|
|
@ -91,13 +91,12 @@ export default defineComponent({
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
toCoverUrl(fileId: string | number) {
|
toCoverUrl(fileId: string | number) {
|
||||||
return getPreviewUrl(
|
return getPreviewUrl({
|
||||||
{
|
photo: {
|
||||||
fileid: Number(fileId),
|
fileid: Number(fileId),
|
||||||
} as IPhoto,
|
} as IPhoto,
|
||||||
true,
|
sqsize: 256,
|
||||||
256
|
});
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
albumCreatedHandler() {
|
albumCreatedHandler() {
|
||||||
|
|
|
@ -154,7 +154,10 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
async sharePreview() {
|
async sharePreview() {
|
||||||
const src = utils.getPreviewUrl(this.photo!, false, 2048);
|
const src = utils.getPreviewUrl({
|
||||||
|
photo: this.photo!,
|
||||||
|
size: 2048,
|
||||||
|
});
|
||||||
this.shareWithHref(src, true);
|
this.shareWithHref(src, true);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -281,7 +281,10 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
clusterPreviewUrl(cluster: IMarkerCluster) {
|
clusterPreviewUrl(cluster: IMarkerCluster) {
|
||||||
return utils.getPreviewUrl(cluster.preview, false, 256);
|
return utils.getPreviewUrl({
|
||||||
|
photo: cluster.preview,
|
||||||
|
msize: 256,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
clusterIconClass(cluster: IMarkerCluster) {
|
clusterIconClass(cluster: IMarkerCluster) {
|
||||||
|
|
|
@ -151,7 +151,10 @@ export default defineComponent({
|
||||||
|
|
||||||
// Get random photo
|
// Get random photo
|
||||||
year.preview ||= utils.randomChoice(year.photos);
|
year.preview ||= utils.randomChoice(year.photos);
|
||||||
year.url = utils.getPreviewUrl(year.preview, false, 512);
|
year.url = utils.getPreviewUrl({
|
||||||
|
photo: year.preview,
|
||||||
|
msize: 512,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.$nextTick();
|
await this.$nextTick();
|
||||||
|
|
|
@ -49,7 +49,7 @@ export default defineComponent({
|
||||||
return {
|
return {
|
||||||
source:
|
source:
|
||||||
this.photo.h && this.photo.w
|
this.photo.h && this.photo.w
|
||||||
? utils.getPreviewUrl(this.photo, false, 'screen')
|
? utils.getPreviewUrl({ photo: this.photo, size: 'screen' })
|
||||||
: API.IMAGE_DECODABLE(this.photo.fileid, this.photo.etag),
|
: API.IMAGE_DECODABLE(this.photo.fileid, this.photo.etag),
|
||||||
|
|
||||||
defaultSavedImageName: this.defaultSavedImageName,
|
defaultSavedImageName: this.defaultSavedImageName,
|
||||||
|
|
|
@ -694,7 +694,8 @@ export default defineComponent({
|
||||||
preload(dayIdx + 3);
|
preload(dayIdx + 3);
|
||||||
|
|
||||||
// Get thumb image
|
// Get thumb image
|
||||||
const thumbSrc: string = this.thumbElem(photo)?.getAttribute('src') || utils.getPreviewUrl(photo, false, 256);
|
const thumbSrc: string =
|
||||||
|
this.thumbElem(photo)?.getAttribute('src') || utils.getPreviewUrl({ photo, msize: 256 });
|
||||||
|
|
||||||
// Get full image
|
// Get full image
|
||||||
return {
|
return {
|
||||||
|
@ -739,7 +740,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Open with a static list of photos */
|
/** Open with a static list of photos */
|
||||||
async openStatic(photo: IPhoto, list: IPhoto[], thumbSize?: number) {
|
async openStatic(photo: IPhoto, list: IPhoto[], thumbSize?: 256 | 512) {
|
||||||
this.list = list;
|
this.list = list;
|
||||||
const photoswipe = await this.createBase({
|
const photoswipe = await this.createBase({
|
||||||
index: list.findIndex((p) => p.fileid === photo.fileid),
|
index: list.findIndex((p) => p.fileid === photo.fileid),
|
||||||
|
@ -750,7 +751,7 @@ export default defineComponent({
|
||||||
|
|
||||||
photoswipe.addFilter('itemData', (itemData, index) => ({
|
photoswipe.addFilter('itemData', (itemData, index) => ({
|
||||||
...this.getItemData(this.list[index]),
|
...this.getItemData(this.list[index]),
|
||||||
msrc: thumbSize ? utils.getPreviewUrl(photo, false, thumbSize) : undefined,
|
msrc: thumbSize ? utils.getPreviewUrl({ photo, msize: thumbSize }) : undefined,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.isOpen = true;
|
this.isOpen = true;
|
||||||
|
@ -759,7 +760,7 @@ export default defineComponent({
|
||||||
|
|
||||||
/** Get base data object */
|
/** Get base data object */
|
||||||
getItemData(photo: IPhoto) {
|
getItemData(photo: IPhoto) {
|
||||||
let previewUrl = utils.getPreviewUrl(photo, false, 'screen');
|
let previewUrl = utils.getPreviewUrl({ photo, size: 'screen' });
|
||||||
const isvideo = photo.flag & this.c.FLAG_IS_VIDEO;
|
const isvideo = photo.flag & this.c.FLAG_IS_VIDEO;
|
||||||
|
|
||||||
// Preview aren't animated
|
// Preview aren't animated
|
||||||
|
|
|
@ -11,8 +11,48 @@ export function isMobile() {
|
||||||
return globalThis.windowInnerWidth <= 768;
|
return globalThis.windowInnerWidth <= 768;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get preview URL from photo object */
|
/** Preview generation options */
|
||||||
export function getPreviewUrl(photo: IPhoto, square: boolean, size: number | [number, number] | 'screen') {
|
type PreviewOpts = {
|
||||||
|
/** Photo object to create preview for */
|
||||||
|
photo: IPhoto;
|
||||||
|
};
|
||||||
|
type PreviewOptsSize = PreviewOpts & {
|
||||||
|
/**
|
||||||
|
* Directly specify the size of the preview.
|
||||||
|
* If you already know the size of the photo, use msize instead,
|
||||||
|
* so that caching can be utilized best. A size of 256 is not allowed
|
||||||
|
* here size the thumbnails are not pre-generated.
|
||||||
|
*/
|
||||||
|
size: 512 | 1024 | 2048 | [number, number] | 'screen';
|
||||||
|
};
|
||||||
|
type PreviewOptsMsize = PreviewOpts & {
|
||||||
|
/**
|
||||||
|
* Size of minimum edge of the preview (recommended).
|
||||||
|
* This can only be used if the photo object has width and height.
|
||||||
|
*/
|
||||||
|
msize: 256 | 512 | 1024 | 2048;
|
||||||
|
};
|
||||||
|
type PreviewOptsSquare = PreviewOpts & {
|
||||||
|
/**
|
||||||
|
* Size of the square preview.
|
||||||
|
* Note that these will still be cached and requested with XImg multipreview.
|
||||||
|
*/
|
||||||
|
sqsize: 256 | 512 | 1024;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get preview URL from photo object
|
||||||
|
*
|
||||||
|
* @param opts Preview options
|
||||||
|
*/
|
||||||
|
export function getPreviewUrl(opts: PreviewOptsSize | PreviewOptsMsize | PreviewOptsSquare) {
|
||||||
|
// Destructure does not work with union types
|
||||||
|
let { photo, size, msize, sqsize } = opts as PreviewOptsSize & PreviewOptsMsize & PreviewOptsSquare;
|
||||||
|
|
||||||
|
// Square size is just size
|
||||||
|
const square = sqsize !== undefined;
|
||||||
|
if (square) size = sqsize as any;
|
||||||
|
|
||||||
// Native preview
|
// Native preview
|
||||||
if (photo.flag & constants.c.FLAG_IS_LOCAL) {
|
if (photo.flag & constants.c.FLAG_IS_LOCAL) {
|
||||||
return API.Q(nativex.API.IMAGE_PREVIEW(photo.fileid), { c: photo.etag });
|
return API.Q(nativex.API.IMAGE_PREVIEW(photo.fileid), { c: photo.etag });
|
||||||
|
@ -25,8 +65,18 @@ export function getPreviewUrl(photo: IPhoto, square: boolean, size: number | [nu
|
||||||
size = [sw, sh];
|
size = [sw, sh];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Base size conversion
|
||||||
|
if (msize !== undefined) {
|
||||||
|
if (photo.w && photo.h) {
|
||||||
|
size = (Math.floor((msize * Math.max(photo.w, photo.h)) / Math.min(photo.w, photo.h)) - 1) as any;
|
||||||
|
} else {
|
||||||
|
console.warn('Photo has no width or height but using msize');
|
||||||
|
size = msize === 256 ? 512 : msize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Convert to array
|
// Convert to array
|
||||||
const [x, y] = typeof size === 'number' ? [size, size] : size;
|
const [x, y] = typeof size === 'number' ? [size, size] : size!;
|
||||||
|
|
||||||
return API.Q(API.IMAGE_PREVIEW(photo.fileid), {
|
return API.Q(API.IMAGE_PREVIEW(photo.fileid), {
|
||||||
c: photo.etag,
|
c: photo.etag,
|
||||||
|
|
Loading…
Reference in New Issue