🌈️ apply a wallpaper based on the weather outside
0

Configure Feed

Select the types of activity you want to include in your feed.

feat: rewrite in zx, store data in cache file instead of directories

Angel Wang (Jun 20, 2026, 1:29 PM -0600) 6642b97e 889c7407

+107 -85
+107
grade.mjs
··· 1 + #!/usr/bin/env zx 2 + import "fs"; 3 + 4 + await $`rm -f cache.json`; 5 + // await $`lutgen apply -p catppuccin-latte -l 8 -L 0.001 -P -r 21.5 ./orig/* -o ./graded`; 6 + cd("./graded"); 7 + 8 + const files = await fs.promises.opendir("."); 9 + 10 + let json = {}; 11 + 12 + // set up the data storage 13 + let lightnessData = []; 14 + for (var i = 0; i < 10; i += 2) { 15 + lightnessData.push({ lightness: i / 10, paths: [] }); 16 + } 17 + 18 + let chromaData = []; 19 + for (var i = 0; i < 10; i += 2) { 20 + chromaData.push({ chroma: i / 100, paths: [] }); 21 + } 22 + 23 + let hueData = []; 24 + for (var i = 0; i < 360; i += 90) { 25 + hueData.push({ hue: i, paths: [] }); 26 + } 27 + 28 + // analyze the files 29 + for await (const file of files) { 30 + const stat = await fs.promises.stat(file.name); 31 + if (stat.isFile()) { 32 + let output = 33 + await $`magick ${file.name} -colorspace Oklch -kmeans 10 -format "%c" histogram:info:`.then( 34 + (s) => 35 + s.stdout 36 + .split("\n") 37 + .map((substr) => substr.trim()) 38 + .filter((n) => n), 39 + ); 40 + 41 + if (output.length < 5) { 42 + // do it again but with more violence 43 + console.log( 44 + `-kmeans 10 of ${file.name} didn't give enough colours, trying again with a higher value...`, 45 + ); 46 + output = 47 + await $`magick ${file.name} -colorspace Oklch -kmeans 40 -format "%c" histogram:info:`.then( 48 + (s) => 49 + s.stdout 50 + .split("\n") 51 + .map((substr) => substr.trim()) 52 + .filter((n) => n), 53 + ); 54 + } 55 + 56 + let colours = output.map((entry) => { 57 + let arr = entry.split(" "); 58 + arr.pop(); 59 + // [# of pixels, oklab colour, hex code] 60 + return [parseInt(arr[0]), arr[1].replace(/[()]/g, "").split(","), arr[2]]; 61 + }); 62 + 63 + colours.sort((a, b) => b[0] - a[0]); 64 + 65 + const dominantColour = colours[0][1]; 66 + console.log( 67 + `Got oklch(${dominantColour}) as dominant colour of ${file.name}!`, 68 + ); 69 + const lightness = dominantColour[0]; 70 + const chroma = dominantColour[1]; 71 + const hue = dominantColour[2]; 72 + 73 + lightnessData.forEach((n) => { 74 + let targetLightness = Math.floor(lightness * 5) / 5; 75 + if (n.lightness == targetLightness) { 76 + n.paths.push(file.name); 77 + return; 78 + } 79 + }); 80 + 81 + chromaData.forEach((n) => { 82 + let targetChroma = Math.floor(chroma * 50) / 50; 83 + if (n.chroma == targetChroma) { 84 + n.paths.push(file.name); 85 + return; 86 + } 87 + }); 88 + 89 + hueData.forEach((n) => { 90 + let targetHue = Math.floor(hue / 90) * 90; 91 + if (n.hue == targetHue) { 92 + n.paths.push(file.name); 93 + return; 94 + } 95 + }); 96 + 97 + json["lightnessData"] = lightnessData; 98 + json["chromaData"] = chromaData; 99 + json["hueData"] = hueData; 100 + 101 + fs.writeFile("./cache.json", JSON.stringify(json), err => {}) 102 + 103 + // past 0.3, the image is way too dark to have any discernable colour 104 + } 105 + } 106 + 107 +
-85
grade.sh
··· 1 - #!/usr/bin/env bash 2 - 3 - # RULES: 4 - # Eliminate golden first, it's the outlier 5 - # LAB -> a greater than 50: golden 6 - # The rest of the categories: 7 - # LAB -> l less than 45: night 8 - # LAB -> l less than 60: day 9 - # LAB -> l all other: noon 10 - # Clear/overcast 11 - # HSI -> s less than 10: overcast 12 - # HSI -> l else: clear 13 - 14 - rm -r graded && mkdir -p graded/{night,golden,day,noon}/{clear,overcast} 15 - 16 - lutgen apply -p catppuccin-latte -l 8 -L 0.001 -P -r 21.5 ./orig/* -o ./graded 17 - 18 - cd graded 19 - 20 - 21 - for file in *; do 22 - if [ -f "$file" ]; then 23 - l=$(magick $file -colorspace Oklch -channel 0 -separate +channel -format "%[fx:100*u.mean]\n" info:) 24 - # c=$(magick $file -colorspace Oklch -channel 1 -separate +channel -format "%[fx:100*u.mean]\n" info:) 25 - # h=$(magick $file -colorspace Oklch -channel b -separate +channel -format "%[fx:u.mean]\n" info:) 26 - 27 - echo $l 28 - 29 - # if (( $(echo "$h > 285" | bc -l) )) || (( $(echo "$h < 90" | bc -l) )) ; then 30 - # echo $h 31 - # mv $file golden/$file 32 - # fi 33 - fi 34 - done 35 - 36 - 37 - 38 - # for file in *; do 39 - # if [ -f "$file" ]; then 40 - # b=$(magick $file -colorspace LAB -channel b -separate +channel -format "%[fx:100*u.mean]\n" info:) 41 - # # l=$(magick $file -colorspace LAB -channel r -separate +channel -format "%[fx:100*u.mean]\n" info:) 42 - # # && (( $(echo "$l > 50" | bc -l) )) 43 - # a=$(magick $file -colorspace LAB -channel g -separate +channel -format "%[fx:100*u.mean]\n" info:) 44 - 45 - # if (( $(echo "$a > 51" | bc -l) )) && (( $(echo "$b > 47" | bc -l) )) ; then 46 - # mv $file golden/$file 47 - # fi 48 - 49 - # # for file in golden/*; do 50 - # # if [ -f "$file" ]; then 51 - # # if (( $(echo "$a < 55" | bc -l) )) && (( $(echo "$b < 55" | bc -l) )); then 52 - # # mv $file ${file%/*}/overcast/${file#*/} 53 - # # else 54 - # # mv $file ${file%/*}/clear/${file#*/} 55 - # # fi 56 - # # fi 57 - # # done 58 - 59 - # fi 60 - # done 61 - 62 - # for file in *; do 63 - # if [ -f "$file" ]; then 64 - # l=$(magick $file -colorspace LAB -channel r -separate +channel -format "%[fx:100*u.mean]\n" info:) 65 - # if (( $(echo "$l < 50" | bc -l) )); then 66 - # mv $file night/$file 67 - # elif (( $(echo "$l < 60" | bc -l) )); then 68 - # mv $file day/$file 69 - # else 70 - # mv $file noon/$file 71 - # fi 72 - # fi 73 - # done 74 - 75 - # for file in */*; do 76 - # if [ -f "$file" ]; then 77 - # s=$(magick $file -colorspace HSI -channel g -separate +channel -format "%[fx:100*u.mean]\n" info:) 78 - 79 - # if (( $(echo "$s < 15" | bc -l) )); then 80 - # mv $file ${file%/*}/overcast/${file#*/} 81 - # else 82 - # mv $file ${file%/*}/clear/${file#*/} 83 - # fi 84 - # fi 85 - # done