thumbnailer: better caching

This commit is contained in:
2 * r + 2 * t 2025-05-05 19:06:32 +10:00
parent 57f815bdc8
commit 1a650b73bc
3 changed files with 75 additions and 66 deletions

View file

@ -28,23 +28,25 @@ Item {
id: two id: two
} }
component Img: Image { component Img: CachingImage {
id: img id: img
function update(): void { function update(): void {
if (source === root.source) const srcPath = `${root.source}`.slice(7);
if (thumbnail.originalPath === srcPath) {
root.current = this; root.current = this;
else } else
source = root.source; path = srcPath;
} }
anchors.fill: parent anchors.fill: parent
asynchronous: true asynchronous: true
cache: false
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
opacity: 0 opacity: 0
scale: Wallpapers.showPreview ? 1 : 0.8 scale: Wallpapers.showPreview ? 1 : 0.8
sourceSize.width: width
sourceSize.height: height
onStatusChanged: { onStatusChanged: {
if (status === Image.Ready) if (status === Image.Ready)

View file

@ -1,7 +1,9 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior: Bound
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
import QtQuick
import Qt.labs.platform import Qt.labs.platform
Singleton { Singleton {
@ -9,36 +11,64 @@ Singleton {
readonly property string thumbDir: `${StandardPaths.standardLocations(StandardPaths.GenericCacheLocation)[0]}/caelestia/thumbnails`.slice(7) readonly property string thumbDir: `${StandardPaths.standardLocations(StandardPaths.GenericCacheLocation)[0]}/caelestia/thumbnails`.slice(7)
function go(sha: string, path: string, width: int, height: int): string { function go(path: string, width: int, height: int): var {
if (!sha || !path || !width || !height) return thumbComp.createObject(root, {
return ""; originalPath: path,
width: width,
const thumbPath = `${thumbDir}/${sha}@${width}x${height}-exact.png`; height: height
});
thumbProc.path = path;
thumbProc.thumbPath = thumbPath;
thumbProc.width = width;
thumbProc.height = height;
thumbProc.startDetached();
return thumbPath;
} }
Process { component Thumbnail: QtObject {
id: thumbProc id: obj
required property string originalPath
required property int width
required property int height
property string path property string path
property string thumbPath
property int width
property int height
command: ["fish", "-c", ` readonly property Process shaProc: Process {
if ! test -f ${thumbPath} running: true
set -l size (identify -ping -format '%w\n%h' ${path}) command: ["sha1sum", obj.originalPath]
if test $size[1] -gt ${width} -o $size[2] -gt ${height} stdout: SplitParser {
magick ${path} -thumbnail ${width}x${height}^ -background none -gravity center -extent ${width}x${height} -unsharp 0x.5 ${thumbPath} onRead: data => {
const sha = data.split(" ")[0];
obj.path = `${root.thumbDir}/${sha}@${obj.width}x${obj.height}-exact.png`;
obj.thumbProc.running = true;
}
}
}
readonly property Process thumbProc: Process {
command: ["fish", "-c", `
if test -f ${obj.path}
exit 1
else
set -l size (identify -ping -format '%w\n%h' ${obj.originalPath})
if test $size[1] -gt ${obj.width} -o $size[2] -gt ${obj.height}
magick ${obj.originalPath} -${obj.width > 1024 || obj.height > 1024 ? "resize" : "thumbnail"} ${obj.width}x${obj.height}^ -background none -gravity center -extent ${obj.width}x${obj.height} -unsharp 0x.5 ${obj.path}
else
cp ${obj.originalPath} ${obj.path}
end end
end end`]
`] onExited: code => {
if (code === 0) {
const path = obj.path;
obj.path = "";
obj.path = path;
}
}
}
function reload(): void {
shaProc.running = true;
}
}
Component {
id: thumbComp
Thumbnail {}
} }
} }

View file

@ -5,46 +5,23 @@ import QtQuick
Image { Image {
id: root id: root
required property string path property string path
property string thumbnail readonly property Thumbnailer.Thumbnail thumbnail: Thumbnailer.go(path, width, height)
source: { source: thumbnail.path ? `file://${thumbnail.path}` : ""
if (thumbnail)
return `file://${thumbnail}`;
shaProc.running = true;
return "";
}
asynchronous: true asynchronous: true
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
onPathChanged: shaProc.running = true onPathChanged: {
onWidthChanged: shaProc.running = true thumbnail.originalPath = path;
onHeightChanged: shaProc.running = true thumbnail.reload();
onStatusChanged: {
if (status === Image.Error)
waitProc.running = true;
} }
onWidthChanged: {
Process { thumbnail.width = width;
id: shaProc thumbnail.reload();
command: ["sha1sum", root.path]
stdout: SplitParser {
onRead: data => root.thumbnail = Thumbnailer.go(data.split(" ")[0], root.path, root.width, root.height)
}
} }
onHeightChanged: {
Process { thumbnail.height = height;
id: waitProc thumbnail.reload();
command: ["inotifywait", "-q", "-e", "close_write", "--format", "%w%f", "-m", root.thumbnail.slice(0, root.thumbnail.lastIndexOf("/"))]
stdout: SplitParser {
onRead: file => {
if (file === root.thumbnail) {
root.source = file;
waitProc.running = false;
}
}
}
} }
} }