feat(web,server): add thumbhash support (#2649)

* add thumbhash: server generation and web impl

* move logic to infra & use byta in db

* remove unnecesary logs

* update generated API and simplify thumbhash gen

* fix check errors

* removed unnecessary library and css tag

* style edits

* syntax mistake

* update server test, change thumbhash job name

* fix tests

* Update server/src/domain/asset/response-dto/asset-response.dto.ts

Co-authored-by: Thomas <9749173+uhthomas@users.noreply.github.com>

* add unit test, change migration date

* change to official thumbhash impl

* update call method to not use eval

* "generate missing" looks for thumbhash

* improve queue & improve syntax

* update syntax again

* update tests

* fix thumbhash generation

* consolidate queueing to avoid duplication

* cover all types of incorrect thumbnail cases

* split out jest tasks

* put back thumbnail duration loading for images without thumbhash

* Remove stray package.json

---------

Co-authored-by: Luke McCarthy <mail@lukehmcc.com>
Co-authored-by: Thomas <9749173+uhthomas@users.noreply.github.com>
Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
Covalent
2023-06-17 23:22:31 -04:00
committed by GitHub
parent 3512140148
commit 3e804f16df
29 changed files with 354 additions and 29 deletions

61
web/package-lock.json generated
View File

@@ -20,7 +20,8 @@
"rxjs": "^7.8.0",
"socket.io-client": "^4.6.1",
"svelte-local-storage-store": "^0.5.0",
"svelte-material-icons": "^3.0.4"
"svelte-material-icons": "^3.0.4",
"unlazy": "^0.8.9"
},
"devDependencies": {
"@babel/preset-env": "^7.20.2",
@@ -4134,6 +4135,15 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@unlazy/core": {
"version": "0.8.9",
"resolved": "https://registry.npmjs.org/@unlazy/core/-/core-0.8.9.tgz",
"integrity": "sha512-DQ4WB/cuEWTknU/59uRwpSipvMAJzBDmRyaHDUc1RcXi0Z7/Vcl0EE7BpROxEynqd1EI+2oMWQaDLyXffUdUiA==",
"dependencies": {
"fast-blurhash": "^1.1.2",
"thumbhash": "^0.1.1"
}
},
"node_modules/@zoom-image/core": {
"version": "0.18.2",
"resolved": "https://registry.npmjs.org/@zoom-image/core/-/core-0.18.2.tgz",
@@ -5945,6 +5955,11 @@
"node": ">= 14"
}
},
"node_modules/fast-blurhash": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/fast-blurhash/-/fast-blurhash-1.1.2.tgz",
"integrity": "sha512-lJVOgYSlahqkRhrKumNx/SGB2F/qS0D1z7xjGYjb5EZJRtlzySGMniZjkQ9h9Rv8sPmM/V9orEgRiMwazDNH6A=="
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -11217,6 +11232,11 @@
"node": ">=0.8"
}
},
"node_modules/thumbhash": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/thumbhash/-/thumbhash-0.1.1.tgz",
"integrity": "sha512-kH5pKeIIBPQXAOni2AiY/Cu/NKdkFREdpH+TLdM0g6WA7RriCv0kPLgP731ady67MhTAqrVG/4mnEeibVuCJcg=="
},
"node_modules/tiny-glob": {
"version": "0.2.9",
"resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz",
@@ -11441,6 +11461,18 @@
"node": ">= 4.0.0"
}
},
"node_modules/unlazy": {
"version": "0.8.9",
"resolved": "https://registry.npmjs.org/unlazy/-/unlazy-0.8.9.tgz",
"integrity": "sha512-lRCuXN20N1esqSQqtSVBLAw9GJz0lcBuOBs3UGGw7cFWHQlWJVZZ3OviwOl42f1CnVHjAON1rs2hIdJWgMAUyg==",
"dependencies": {
"@unlazy/core": "0.8.9"
},
"peerDependencies": {
"fast-blurhash": "^1.1.2",
"thumbhash": "^0.1.1"
}
},
"node_modules/update-browserslist-db": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
@@ -14739,6 +14771,15 @@
"eslint-visitor-keys": "^3.3.0"
}
},
"@unlazy/core": {
"version": "0.8.9",
"resolved": "https://registry.npmjs.org/@unlazy/core/-/core-0.8.9.tgz",
"integrity": "sha512-DQ4WB/cuEWTknU/59uRwpSipvMAJzBDmRyaHDUc1RcXi0Z7/Vcl0EE7BpROxEynqd1EI+2oMWQaDLyXffUdUiA==",
"requires": {
"fast-blurhash": "^1.1.2",
"thumbhash": "^0.1.1"
}
},
"@zoom-image/core": {
"version": "0.18.2",
"resolved": "https://registry.npmjs.org/@zoom-image/core/-/core-0.18.2.tgz",
@@ -16053,6 +16094,11 @@
"source-map-support": "^0.5.21"
}
},
"fast-blurhash": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/fast-blurhash/-/fast-blurhash-1.1.2.tgz",
"integrity": "sha512-lJVOgYSlahqkRhrKumNx/SGB2F/qS0D1z7xjGYjb5EZJRtlzySGMniZjkQ9h9Rv8sPmM/V9orEgRiMwazDNH6A=="
},
"fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -19861,6 +19907,11 @@
"thenify": ">= 3.1.0 < 4"
}
},
"thumbhash": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/thumbhash/-/thumbhash-0.1.1.tgz",
"integrity": "sha512-kH5pKeIIBPQXAOni2AiY/Cu/NKdkFREdpH+TLdM0g6WA7RriCv0kPLgP731ady67MhTAqrVG/4mnEeibVuCJcg=="
},
"tiny-glob": {
"version": "0.2.9",
"resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz",
@@ -20023,6 +20074,14 @@
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
"dev": true
},
"unlazy": {
"version": "0.8.9",
"resolved": "https://registry.npmjs.org/unlazy/-/unlazy-0.8.9.tgz",
"integrity": "sha512-lRCuXN20N1esqSQqtSVBLAw9GJz0lcBuOBs3UGGw7cFWHQlWJVZZ3OviwOl42f1CnVHjAON1rs2hIdJWgMAUyg==",
"requires": {
"@unlazy/core": "0.8.9"
}
},
"update-browserslist-db": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",

View File

@@ -70,6 +70,7 @@
"rxjs": "^7.8.0",
"socket.io-client": "^4.6.1",
"svelte-local-storage-store": "^0.5.0",
"svelte-material-icons": "^3.0.4"
"svelte-material-icons": "^3.0.4",
"unlazy": "^0.8.9"
}
}

View File

@@ -637,6 +637,12 @@ export interface AssetResponseDto {
* @memberof AssetResponseDto
*/
'resized': boolean;
/**
* base64 encoded thumbhash
* @type {string}
* @memberof AssetResponseDto
*/
'thumbhash': string | null;
/**
*
* @type {string}

View File

@@ -120,6 +120,7 @@
altText={person.name}
widthStyle="90px"
heightStyle="90px"
thumbhash={null}
/>
<p class="font-medium mt-1 truncate">{person.name}</p>
</a>

View File

@@ -1,27 +1,58 @@
<script lang="ts">
import { onMount } from 'svelte';
import { lazyLoad } from 'unlazy';
import { imageLoad } from '$lib/utils/image-load';
export let url: string;
export let altText: string;
export let heightStyle: string | undefined = undefined;
export let widthStyle: string;
export let thumbhash: string | null = null;
export let curve = false;
export let shadow = false;
export let circle = false;
let loading = true;
let imageElement: HTMLImageElement;
onMount(() => {
if (thumbhash) {
lazyLoad(imageElement, {
hash: thumbhash,
hashType: 'thumbhash'
});
}
});
</script>
<img
style:width={widthStyle}
style:height={heightStyle}
src={url}
alt={altText}
class="object-cover transition-opacity duration-300"
class:rounded-lg={curve}
class:shadow-lg={shadow}
class:rounded-full={circle}
class:opacity-0={loading}
draggable="false"
use:imageLoad
on:image-load|once={() => (loading = false)}
/>
{#if thumbhash}
<img
style:width={widthStyle}
style:height={heightStyle}
data-src={url}
alt={altText}
class="object-cover"
class:rounded-lg={curve}
class:shadow-lg={shadow}
class:rounded-full={circle}
draggable="false"
bind:this={imageElement}
/>
<!-- not everthing yet has thumbhash support so the old method is kept -->
{:else}
<img
style:width={widthStyle}
style:height={heightStyle}
src={url}
alt={altText}
class="object-cover transition-opacity duration-300"
class:rounded-lg={curve}
class:shadow-lg={shadow}
class:rounded-full={circle}
class:opacity-0={loading}
draggable="false"
use:imageLoad
on:image-load|once={() => (loading = false)}
/>
{/if}

View File

@@ -129,6 +129,7 @@
altText={asset.originalFileName}
widthStyle="{width}px"
heightStyle="{height}px"
thumbhash={asset.thumbhash}
/>
{:else}
<div class="w-full h-full p-4 flex items-center justify-center">