forked from Shinonome/dots-hyprland
wallpaper selector: progress indicator for thumbnail generation
This commit is contained in:
@@ -16,8 +16,9 @@ Image {
|
|||||||
property string thumbnailSizeName: Images.thumbnailSizeNameForDimensions(sourceSize.width, sourceSize.height)
|
property string thumbnailSizeName: Images.thumbnailSizeNameForDimensions(sourceSize.width, sourceSize.height)
|
||||||
property string thumbnailPath: {
|
property string thumbnailPath: {
|
||||||
if (sourcePath.length == 0) return;
|
if (sourcePath.length == 0) return;
|
||||||
const resolvedUrl = Qt.resolvedUrl(sourcePath);
|
const resolvedUrlWithoutFileProtocol = FileUtils.trimFileProtocol(`${Qt.resolvedUrl(sourcePath)}`);
|
||||||
const md5Hash = Qt.md5(resolvedUrl);
|
const encodedUrlWithoutFileProtocol = resolvedUrlWithoutFileProtocol.split("/").map(part => encodeURIComponent(part)).join("/");
|
||||||
|
const md5Hash = Qt.md5(`file://${encodedUrlWithoutFileProtocol}`);
|
||||||
return `${Directories.genericCache}/thumbnails/${thumbnailSizeName}/${md5Hash}.png`;
|
return `${Directories.genericCache}/thumbnails/${thumbnailSizeName}/${md5Hash}.png`;
|
||||||
}
|
}
|
||||||
source: thumbnailPath
|
source: thumbnailPath
|
||||||
|
|||||||
@@ -80,6 +80,12 @@ MouseArea {
|
|||||||
thumbnailImage.source = "";
|
thumbnailImage.source = "";
|
||||||
thumbnailImage.source = thumbnailImage.thumbnailPath;
|
thumbnailImage.source = thumbnailImage.thumbnailPath;
|
||||||
}
|
}
|
||||||
|
function onThumbnailGeneratedFile(filePath) {
|
||||||
|
if (thumbnailImage.status !== Image.Error) return;
|
||||||
|
if (Qt.resolvedUrl(thumbnailImage.sourcePath) !== Qt.resolvedUrl(filePath)) return;
|
||||||
|
thumbnailImage.source = "";
|
||||||
|
thumbnailImage.source = thumbnailImage.thumbnailPath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layer.enabled: true
|
layer.enabled: true
|
||||||
|
|||||||
@@ -205,7 +205,9 @@ Item {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
|
||||||
StyledProgressBar {
|
StyledIndeterminateProgressBar {
|
||||||
|
id: indeterminateProgressBar
|
||||||
|
visible: Wallpapers.thumbnailGenerationRunning && value == 0
|
||||||
anchors {
|
anchors {
|
||||||
bottom: parent.top
|
bottom: parent.top
|
||||||
left: parent.left
|
left: parent.left
|
||||||
@@ -213,7 +215,12 @@ Item {
|
|||||||
leftMargin: 4
|
leftMargin: 4
|
||||||
rightMargin: 4
|
rightMargin: 4
|
||||||
}
|
}
|
||||||
indeterminate: true
|
}
|
||||||
|
|
||||||
|
StyledProgressBar {
|
||||||
|
visible: Wallpapers.thumbnailGenerationRunning && value > 0
|
||||||
|
value: Wallpapers.thumbnailGenerationProgress
|
||||||
|
anchors.fill: indeterminateProgressBar
|
||||||
}
|
}
|
||||||
|
|
||||||
GridView {
|
GridView {
|
||||||
|
|||||||
@@ -47,6 +47,12 @@ generate_thumbnail() {
|
|||||||
local src="$1"
|
local src="$1"
|
||||||
local abs_path
|
local abs_path
|
||||||
abs_path="$(realpath "$src")"
|
abs_path="$(realpath "$src")"
|
||||||
|
# Skip files with multiple frames (GIFs, videos, etc.)
|
||||||
|
case "${abs_path,,}" in
|
||||||
|
*.gif|*.mp4|*.webm|*.mkv|*.avi|*.mov)
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
esac
|
||||||
local encoded_path
|
local encoded_path
|
||||||
encoded_path="$(urlencode "$abs_path")"
|
encoded_path="$(urlencode "$abs_path")"
|
||||||
local uri
|
local uri
|
||||||
@@ -58,7 +64,7 @@ generate_thumbnail() {
|
|||||||
if [ -f "$out" ]; then
|
if [ -f "$out" ]; then
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
magick "$abs_path" -resize ${THUMBNAIL_SIZE}x${THUMBNAIL_SIZE} "$out"
|
magick "$abs_path" -resize "${THUMBNAIL_SIZE}x${THUMBNAIL_SIZE}" "$out"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Parse arguments
|
# Parse arguments
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env -S\_/bin/sh\_-xc\_"source\_\$(eval\_echo\_\$ILLOGICAL_IMPULSE_VIRTUAL_ENV)/bin/activate&&exec\_python\_-E\_"\$0"\_"\$@""
|
#!/usr/bin/env -S\_/bin/sh\_-c\_"source\_\$(eval\_echo\_\$ILLOGICAL_IMPULSE_VIRTUAL_ENV)/bin/activate&&exec\_python\_-E\_"\$0"\_"\$@""
|
||||||
|
|
||||||
# From https://github.com/difference-engine/thumbnail-generator-ubuntu (MIT License)
|
# From https://github.com/difference-engine/thumbnail-generator-ubuntu (MIT License)
|
||||||
# Since the script is small and the maintainers seem inactive to accept my PR (#11) I decided to just copy it over.
|
# Since the script is small and the maintainers seem inactive to accept my PR (#11) I decided to just copy it over.
|
||||||
@@ -57,13 +57,22 @@ def make_thumbnail(fpath: str) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
@logger.catch()
|
@logger.catch()
|
||||||
def thumbnail_folder(*, dir_path: Path, workers: int, only_images: bool, recursive: bool) -> None:
|
def thumbnail_folder(*, dir_path: Path, workers: int, only_images: bool, recursive: bool, machine_progress: bool = False) -> None:
|
||||||
all_files = get_all_files(dir_path=dir_path, recursive=recursive)
|
all_files = get_all_files(dir_path=dir_path, recursive=recursive)
|
||||||
if only_images:
|
if only_images:
|
||||||
all_files = get_all_images(all_files=all_files)
|
all_files = get_all_images(all_files=all_files)
|
||||||
all_files = [str(fpath) for fpath in all_files]
|
all_files = [str(fpath) for fpath in all_files]
|
||||||
with Pool(processes=workers) as p:
|
if machine_progress:
|
||||||
list(tqdm(p.imap(make_thumbnail, all_files), total=len(all_files)))
|
completed = 0
|
||||||
|
total = len(all_files)
|
||||||
|
with Pool(processes=workers) as p:
|
||||||
|
for result in p.imap(make_thumbnail, all_files):
|
||||||
|
completed += 1
|
||||||
|
print(f"PROGRESS {completed}/{total} FILE {all_files[completed-1]}")
|
||||||
|
sys.stdout.flush()
|
||||||
|
else:
|
||||||
|
with Pool(processes=workers) as p:
|
||||||
|
list(tqdm(p.imap(make_thumbnail, all_files), total=len(all_files)))
|
||||||
|
|
||||||
|
|
||||||
def get_all_images(*, all_files: List[Path]) -> List[Path]:
|
def get_all_images(*, all_files: List[Path]) -> List[Path]:
|
||||||
@@ -96,12 +105,13 @@ def get_all_files(*, dir_path: Path, recursive: bool) -> List[Path]:
|
|||||||
"-i", "--only_images", is_flag=True, default=False, help="Whether to only look for images to be thumbnailed"
|
"-i", "--only_images", is_flag=True, default=False, help="Whether to only look for images to be thumbnailed"
|
||||||
)
|
)
|
||||||
@click.option("-r", "--recursive", is_flag=True, default=False, help="Whether to recursively look for files")
|
@click.option("-r", "--recursive", is_flag=True, default=False, help="Whether to recursively look for files")
|
||||||
def main(img_dirs: str, size: str, workers: str, only_images: bool, recursive: bool) -> None:
|
@click.option("--machine_progress", is_flag=True, default=False, help="Print machine-readable progress lines instead of a progress bar")
|
||||||
|
def main(img_dirs: str, size: str, workers: str, only_images: bool, recursive: bool, machine_progress: bool) -> None:
|
||||||
img_dirs = [Path(img_dir) for img_dir in img_dirs.split()]
|
img_dirs = [Path(img_dir) for img_dir in img_dirs.split()]
|
||||||
global factory
|
global factory
|
||||||
factory = GnomeDesktop.DesktopThumbnailFactory.new(thumbnail_size_map[size])
|
factory = GnomeDesktop.DesktopThumbnailFactory.new(thumbnail_size_map[size])
|
||||||
for img_dir in img_dirs:
|
for img_dir in img_dirs:
|
||||||
thumbnail_folder(dir_path=img_dir, workers=workers, only_images=only_images, recursive=recursive)
|
thumbnail_folder(dir_path=img_dir, workers=workers, only_images=only_images, recursive=recursive, machine_progress=machine_progress)
|
||||||
print("Thumbnail Generation Completed!")
|
print("Thumbnail Generation Completed!")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -23,9 +23,12 @@ Singleton {
|
|||||||
"jpg", "jpeg", "png", "webp", "avif", "bmp", "svg"
|
"jpg", "jpeg", "png", "webp", "avif", "bmp", "svg"
|
||||||
]
|
]
|
||||||
property list<string> wallpapers: [] // List of absolute file paths (without file://)
|
property list<string> wallpapers: [] // List of absolute file paths (without file://)
|
||||||
|
readonly property bool thumbnailGenerationRunning: thumbgenProc.running
|
||||||
|
property real thumbnailGenerationProgress: 0
|
||||||
|
|
||||||
signal changed()
|
signal changed()
|
||||||
signal thumbnailGenerated(directory: string)
|
signal thumbnailGenerated(directory: string)
|
||||||
|
signal thumbnailGeneratedFile(filePath: string)
|
||||||
|
|
||||||
// Executions
|
// Executions
|
||||||
Process {
|
Process {
|
||||||
@@ -126,13 +129,30 @@ Singleton {
|
|||||||
thumbgenProc.running = false
|
thumbgenProc.running = false
|
||||||
thumbgenProc.command = [
|
thumbgenProc.command = [
|
||||||
"bash", "-c",
|
"bash", "-c",
|
||||||
`${thumbgenScriptPath} --size ${size} -d ${root.directory} || ${generateThumbnailsMagicScriptPath} --size ${size} -d ${root.directory}`,
|
`${thumbgenScriptPath} --size ${size} --machine_progress -d ${root.directory} || ${generateThumbnailsMagicScriptPath} --size ${size} -d ${root.directory}`,
|
||||||
]
|
]
|
||||||
|
root.thumbnailGenerationProgress = 0
|
||||||
thumbgenProc.running = true
|
thumbgenProc.running = true
|
||||||
}
|
}
|
||||||
Process {
|
Process {
|
||||||
id: thumbgenProc
|
id: thumbgenProc
|
||||||
property string directory
|
property string directory
|
||||||
|
stdout: SplitParser {
|
||||||
|
onRead: data => {
|
||||||
|
// print("thumb gen proc:", data)
|
||||||
|
let match = data.match(/PROGRESS (\d+)\/(\d+)/)
|
||||||
|
if (match) {
|
||||||
|
const completed = parseInt(match[1])
|
||||||
|
const total = parseInt(match[2])
|
||||||
|
root.thumbnailGenerationProgress = completed / total
|
||||||
|
}
|
||||||
|
match = data.match(/FILE (.+)/)
|
||||||
|
if (match) {
|
||||||
|
const filePath = match[1]
|
||||||
|
root.thumbnailGeneratedFile(filePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
onExited: (exitCode, exitStatus) => {
|
onExited: (exitCode, exitStatus) => {
|
||||||
root.thumbnailGenerated(thumbgenProc.directory)
|
root.thumbnailGenerated(thumbgenProc.directory)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user