Clean-up KDE Thumbnails

metadata

KDE creates thumbnails of all your files when you view them in Dolphin or Konqueror. It then saves these thumbnails so that if you look at the photo again it doesn’t have to regenerate the thumbnail. This is a really efficient way of doing things. Plus, it stores the thumbnails in a central place instead of, like Windows, leaving little Thumbs.db files around the place. The drawback with the current system is that if you delete a photo then its thumbnail is not deleted along with it. This means that if you simply rename a folder then a huge swathe of saved thumbnails will no longer refer to the correct files - thus being useless. After a while these accumulate and occupy disk space when they really shouldn’t.

Version 2

This version is completely unrelated to the first one - it is simply a BASH script that relies on exiftool to do the inspection of the PNG meta-data. This is the one that I run regularly on my Linux boxes. I will leave the old version here too for archival.

01: #!/usr/bin/env bash
02: 
03: # This script is copyrighted to Thomas Guymer and is licensed under the
04: # Creative Commons Attribution Non-Commercial 2.0 UK: England & Wales Licence.
05: 
06: # This script can be run in two ways:
07: # * type "bash script.sh" into a console window; or
08: # * type "./script.sh" into a console window.
09: 
10: # This script will:
11: #   * search for all the saved thumbnails on your KDE system; then
12: #   * check if the original file is present and delete the thumbnail if it is
13: #     not.
14: 
15: # Check that the required software is installed ...
16: exiftool=$(which exiftool 2> /dev/null)
17: if [[ ! -x $exiftool ]]; then
18:     echo "ERROR: \"exiftool\" is not installed." >&2
19:     exit 1
20: fi
21: 
22: # Define function ...
23: urldecode() {
24:     # NOTE: https://unix.stackexchange.com/a/187256
25:     local url_encoded="${1//+/ }"
26:     printf '%b' "${url_encoded//%/\\x}"
27: }
28: 
29: # Initialize counters and kernel type ...
30: kern=$(uname)
31: count=0
32: total=0
33: 
34: # Loop over directories (if present) ...
35: for dir in "$HOME/.thumbnails"/*; do
36:     [[ ! -d "$dir" ]] && continue
37: 
38:     echo "Removing surplus thumbnails from \"$dir\" ..."
39: 
40:     # Loop over PNGs (if present) ...
41:     for png in "$dir"/*.png; do
42:         [[ ! -f "$png" ]] && continue
43: 
44:         # Extract URI tag (skip if not present) and extract URI from tag string
45:         # (skipping thumbnails for SFTP files) ...
46:         uri=$($exiftool -ThumbURI "$png" 2> /dev/null)
47:         [[ ${#uri} -eq 0 ]] && continue
48:         uri=${uri##* }
49:         [[ ${uri:0:7} == "sftp://" ]] && continue
50: 
51:         # Decide how to handle the URI ...
52:         if [[ ${uri:0:7} == "file://" ]]; then
53:             # Decode the URI, trim the URI and skip if the original file still
54:             # exists ...
55:             uri=$(urldecode "$uri")
56:             uri=${uri:7}
57:             [[ -f "$uri" ]] && continue
58: 
59:             # Increment counters and remove thumbnail ...
60:             count=$((count + 1))
61:             case $kern in
62:                 Darwin)
63:                     total=$((total + $(stat -f %z "$png")))
64:                     ;;
65:                 FreeBSD)
66:                     total=$((total + $(stat -f %z "$png")))
67:                     ;;
68:                 Linux)
69:                     total=$((total + $(stat -c %s "$png")))
70:                     ;;
71:                 *)
72:                     echo "ERROR: Unknown kernel type." >&2
73:                     exit 1
74:             esac
75:             rm "$png"
76:         else
77:             echo "ERROR: An unknown URI type has been found." >&2
78:             echo "       Thumbnail => $png" >&2
79:             echo "       URI       => $uri" >&2
80:             exit 1
81:         fi
82:     done
83: done
84: 
85: echo "Removed $count thumbnails which totaled $((total / 1024 / 1024)) MiB."
86: 
You may also download “cleanup-kde-thumbnails.sh” directly or view “cleanup-kde-thumbnails.sh” on GitHub Gist.

Version 1

To quote the comment in the script:

This script will: search for all the saved thumbnails on your KDE system; then check if the original file is present and delete the thumbnail if it is not; or delete the thumbnail if it references the trash can.

001: #!/usr/bin/php
002: <?php
003: // This script is copyrighted to Thomas Guymer and is licensed under the
004: // Creative Commons Attribution Non-Commercial 2.0 UK: England & Wales Licence.
005: 
006: // This script can be run in two ways:
007: // * type "php systemThumbnails.php" into a console window; or
008: // * type "./systemThumbnails.php" into a console window.
009: 
010: // This script will:
011: //   * search for all the saved thumbnails on your KDE system; then
012: //   * check if the original file is present and delete the thumbnail if it is
013: //     not; or
014: //   * delete the thumbnail if it references the trash can.
015: 
016: $username = "user1";
017: 
018: // DO NOT EDIT BELOW THIS LINE
019: 
020: // Search for 'normal' thumbnails
021: echo " > Searching for thumbnails ... ";
022: $folderPath = "/home/" . $username . "/.thumbnails/normal";
023: if($handle = opendir($folderPath)) {
024:     while(false !== ($file = readdir($handle))) {
025:         if($file !== "." && $file !== "..") {
026:             $contents[] = $folderPath . "/" . $file;
027:         }
028:     }
029:     closedir($handle);
030: }
031: // Search for 'large' thumbnails
032: $folderPath = "/home/" . $username . "/.thumbnails/large";
033: if($handle = opendir($folderPath)) {
034:     while(false !== ($file = readdir($handle))) {
035:         if($file !== "." && $file !== "..") {
036:             $contents[] = $folderPath . "/" . $file;
037:         }
038:     }
039:     closedir($handle);
040: }
041: // Start checking thumbnails
042: if(!empty($contents)) {
043:     sort($contents);
044:     echo "done. (There are " . count($contents) . ")\n";
045:     echo "   > Processing ... ";
046:     $size = 0;
047:     $counter = 0;
048:     foreach($contents as $key => $value) {
049:         $file = file_get_contents($value);
050:         if(stripos($file, "Thumb::URI") !== false && stripos($file, "IDAT") !== false) {
051:             if(stripos($file, "tEXtThumb::URI") !== false) {
052:                 // Find chunk length
053:                 $integer = substr($file, stripos($file, "Thumb::URI")-8, 4);
054:                 // Collect data
055:                 $start = stripos($file, "Thumb::URI") + strlen("Thumb::URI") + 1; // This skips the null byte, as defined in the PNG specification https://www.w3.org/TR/2003/REC-PNG-20031110/#11tEXt
056:                 $length = ord(substr($integer, 0, 1)) + ord(substr($integer, 1, 1)) + ord(substr($integer, 2, 1)) + ord(substr($integer, 3, 1)) - strlen("Thumb::URI") - 1;
057:                 unset($integer);
058:                 $original = substr($file, $start, $length);
059:                 unset($start, $length);
060:                 // Decode
061:                 $original = utf8_encode($original);
062:                 $original = urldecode($original);
063:             }
064:             elseif(stripos($file, "zTXtThumb::URI") !== false) {
065:                 // Find chunk length
066:                 $integer = substr($file, stripos($file, "Thumb::URI")-8, 4);
067:                 // Collect data
068:                 $start = stripos($file, "Thumb::URI") + strlen("Thumb::URI") + 2; // This skips special characters, as defined in the PNG specification https://www.w3.org/TR/2003/REC-PNG-20031110/#11zTXt
069:                 $length = ord(substr($integer, 0, 1)) + ord(substr($integer, 1, 1)) + ord(substr($integer, 2, 1)) + ord(substr($integer, 3, 1)) - strlen("Thumb::URI") - 2;
070:                 unset($integer);
071:                 $original = substr($file, $start, $length);
072:                 unset($start, $length);
073:                 // Uncompress
074:                 $original = gzuncompress($original);
075:                 // Decode
076:                 $original = utf8_encode($original);
077:                 $original = urldecode($original);
078:             }
079:             else {
080:                 echo "\nERROR: The meta data in " . $value . " is not in a recognised format.\n";
081:                 exit(1);
082:             }
083:             // Check if the file can be deleted
084:             if(stripos($original, "file://") === 0) {
085:                 // Remove bumf
086:                 $original = substr($original, strlen("file://"));
087:                 // Check if original exists
088:                 if(!file_exists($original)) {
089:                     $size = $size + filesize($value);
090:                     $counter = $counter + 1;
091:                     unlink($value);
092:                 }
093:             }
094:             elseif(stripos($original, "trash:/") === 0) {
095:                 $size = $size + filesize($value);
096:                 $counter = $counter + 1;
097:                 unlink($value);
098:             }
099:             else {
100:                 echo "\nERROR: " . $original . " is not a local file. (" . $value . ")\n";
101:             }
102:             unset($original);
103:         }
104:         else {
105:             echo "\nERROR: " . $value . " does not contain correct meta data.\n";
106:         }
107:         unset($file);
108:     }
109:     $size = round($size/(1024*1024),1);
110:     echo "done.\n";
111:     echo " > " . $counter . " thumbnails were deleted which totaled " . $size . "MB.\n";
112: }
113: else {
114:     echo "done. (There are no thumbnails on your system.)\n";
115: }
116: ?>
117: 
You may also download “cleanup-kde-thumbnails.php” directly or view “cleanup-kde-thumbnails.php” on GitHub Gist.

I hope this script is useful for you. The first time I ran it it removed 90 MBs of redundant thumbnails for me!