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
|
||||
$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')
|
||||
->innerJoin('m', 'filecache', 'f', $query->expr()->eq('m.fileid', 'f.fileid'))
|
||||
->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['mapcluster'] = (int) $row['mapcluster'];
|
||||
$row['dayid'] = (int) $row['dayid'];
|
||||
$row['h'] = (int) $row['h'];
|
||||
$row['w'] = (int) $row['w'];
|
||||
}
|
||||
|
||||
return $files;
|
||||
|
|
|
@ -48,7 +48,7 @@ declare global {
|
|||
|
||||
var mViewer: {
|
||||
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;
|
||||
isOpen: () => boolean;
|
||||
};
|
||||
|
|
|
@ -68,7 +68,7 @@ export default defineComponent({
|
|||
etag: this.album.album_id,
|
||||
flag: 0,
|
||||
} 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);
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<div class="previews fill-block">
|
||||
<div class="preview-container fill-block">
|
||||
<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>
|
||||
|
@ -28,7 +28,7 @@
|
|||
import { defineComponent, PropType } from 'vue';
|
||||
import { IFolder, IPhoto } from '../../types';
|
||||
|
||||
import { getPreviewUrl } from '../../services/utils/helpers';
|
||||
import * as utils from '../../services/utils/helpers';
|
||||
|
||||
import UserConfig from '../../mixins/UserConfig';
|
||||
import FolderIcon from 'vue-material-design-icons/Folder.vue';
|
||||
|
@ -53,8 +53,6 @@ export default defineComponent({
|
|||
previews: [] as IPhoto[],
|
||||
// Error occured fetching thumbs
|
||||
error: false,
|
||||
// Passthrough
|
||||
getPreviewUrl,
|
||||
}),
|
||||
|
||||
computed: {
|
||||
|
@ -107,6 +105,14 @@ export default defineComponent({
|
|||
}
|
||||
}
|
||||
},
|
||||
|
||||
/** Get preview url */
|
||||
previewUrl(info: IPhoto) {
|
||||
return utils.getPreviewUrl({
|
||||
photo: info,
|
||||
sqsize: 256,
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -160,7 +160,7 @@ export default defineComponent({
|
|||
|
||||
/** Get url of the photo */
|
||||
url() {
|
||||
let base = 256;
|
||||
let base: 256 | 512 = 256;
|
||||
|
||||
// Check if displayed size is larger than the image
|
||||
if (this.data.dispH! > base * 0.9 && this.data.dispW! > base * 0.9) {
|
||||
|
@ -172,13 +172,10 @@ export default defineComponent({
|
|||
base = 512;
|
||||
}
|
||||
|
||||
// Make the shorter dimension equal to base
|
||||
let size = base;
|
||||
if (this.data.w && this.data.h) {
|
||||
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);
|
||||
return utils.getPreviewUrl({
|
||||
photo: this.data,
|
||||
msize: base,
|
||||
});
|
||||
},
|
||||
|
||||
/** Set src with overlay face rect */
|
||||
|
|
|
@ -91,13 +91,12 @@ export default defineComponent({
|
|||
|
||||
methods: {
|
||||
toCoverUrl(fileId: string | number) {
|
||||
return getPreviewUrl(
|
||||
{
|
||||
return getPreviewUrl({
|
||||
photo: {
|
||||
fileid: Number(fileId),
|
||||
} as IPhoto,
|
||||
true,
|
||||
256
|
||||
);
|
||||
sqsize: 256,
|
||||
});
|
||||
},
|
||||
|
||||
albumCreatedHandler() {
|
||||
|
|
|
@ -154,7 +154,10 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
async sharePreview() {
|
||||
const src = utils.getPreviewUrl(this.photo!, false, 2048);
|
||||
const src = utils.getPreviewUrl({
|
||||
photo: this.photo!,
|
||||
size: 2048,
|
||||
});
|
||||
this.shareWithHref(src, true);
|
||||
},
|
||||
|
||||
|
|
|
@ -281,7 +281,10 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
clusterPreviewUrl(cluster: IMarkerCluster) {
|
||||
return utils.getPreviewUrl(cluster.preview, false, 256);
|
||||
return utils.getPreviewUrl({
|
||||
photo: cluster.preview,
|
||||
msize: 256,
|
||||
});
|
||||
},
|
||||
|
||||
clusterIconClass(cluster: IMarkerCluster) {
|
||||
|
|
|
@ -151,7 +151,10 @@ export default defineComponent({
|
|||
|
||||
// Get random photo
|
||||
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();
|
||||
|
|
|
@ -49,7 +49,7 @@ export default defineComponent({
|
|||
return {
|
||||
source:
|
||||
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),
|
||||
|
||||
defaultSavedImageName: this.defaultSavedImageName,
|
||||
|
|
|
@ -694,7 +694,8 @@ export default defineComponent({
|
|||
preload(dayIdx + 3);
|
||||
|
||||
// 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
|
||||
return {
|
||||
|
@ -739,7 +740,7 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
/** 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;
|
||||
const photoswipe = await this.createBase({
|
||||
index: list.findIndex((p) => p.fileid === photo.fileid),
|
||||
|
@ -750,7 +751,7 @@ export default defineComponent({
|
|||
|
||||
photoswipe.addFilter('itemData', (itemData, 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;
|
||||
|
@ -759,7 +760,7 @@ export default defineComponent({
|
|||
|
||||
/** Get base data object */
|
||||
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;
|
||||
|
||||
// Preview aren't animated
|
||||
|
|
|
@ -11,8 +11,48 @@ export function isMobile() {
|
|||
return globalThis.windowInnerWidth <= 768;
|
||||
}
|
||||
|
||||
/** Get preview URL from photo object */
|
||||
export function getPreviewUrl(photo: IPhoto, square: boolean, size: number | [number, number] | 'screen') {
|
||||
/** Preview generation options */
|
||||
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
|
||||
if (photo.flag & constants.c.FLAG_IS_LOCAL) {
|
||||
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];
|
||||
}
|
||||
|
||||
// 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
|
||||
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), {
|
||||
c: photo.etag,
|
||||
|
|
Loading…
Reference in New Issue