I recently bought an ARZOPA 10.1" Digital Picture Frameexternal link . I’ve been thinking about a device like this for a long time, and I even wondered if I could build one myself with a Raspberry Pi and an old monitor. Thankfully, some clever folks in China have already built them and are selling them at a very reasonable price. I was considering a 10" or 14" device, but since I’d never heard of the ARZOPA company, I didn’t want to start with the more expensive option. So, I settled on the 10" version.

Specifications

  • Model: P101W
  • Built-in Storage: 32GB
  • Resolution: 1280x800
  • Aspect Ratio: 16:10
  • Contrast Ratio: 1500:1
  • Software: Frameo
  • Power: 7W

My Plan

These devices are Wi-Fi connected and integrate with the Frameo Appexternal link , which allows you to upload photos from anywhere (not my preferred security model, to be honest). The app is quite limited, as it only allows you to upload 10 files at a time and movies with a maximum length of 15 seconds.

Although it has some interesting options. For example, I could send a frame like this to my grandmothers and share sweet photos of my kids whenever I would normally share them via WhatsApp or Signal. That’s quite convenient, I have to admit.

For me, the most interesting option was to get my entire (or at least a large part of it) photo collection on the device. My photo collection is currently almost 700GB and contains over 100,000 files. As you might have seen above, this frame only has 32GB of storage, but it can be extended with SD cards. I had an old 64GB card lying around.

My idea was this: most of my photos were taken with a 20Mpix camera or mobile phones with even more megapixels. This frame doesn’t need such a high resolution. If I could generate thumbnails, maybe I could save enough space to put all my photos on it.

Generating Miniatures: The First Attempt

I started with find, added xargs, and used ImageMagick to generate a bunch of thumbnails. However, doing this on my entire photo library would take ages. The xargs command supports the -P flag, which allows you to run multiple convert tasks at the same time. Setting it to the output of nproc allows me to utilize all my cores. Now I can run it on 20 cores!

Requirements

  • ImageMagick
  • ExifTool
  • Make

On Ubuntu/Debian, you can install them with:

Install prerequisites
sudo apt update
sudo apt install -y imagemagick libimage-exiftool-perl make

Note

I know it’s spooky to see in 2025 that the most popular tool to manipulate Exif data on Linux is written in Perl! 🤣

I started crafting a Makefile. I really like Makefiles for such tasks. A declarative approach makes execution easier, after the pain of figuring out how to write it. 🤣

The Makefile to automate things a bit
# Makefile

.PHONY: all generate-miniatures prune-to-first-batch

# detect number of processors (fallback to 1)
NPROC := $(shell nproc 2>/dev/null || echo 1)
RESOLUTION := 1280x800
SDCARD := /media/user/06E5-AE3E
SOURCE_DIR := /storage/photos
TARGET_DIR := miniatures-$(RESOLUTION)-$(shell date +%Y%m%d)
IGNORE_FILE := .frameoignore

all: generate-miniatures

generate-miniatures:
	@mkdir -p $(TARGET_DIR)
	@find original/ -type f \( -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.heic' \) -print0 | \
		xargs -0 -P$(NPROC) -I{} sh -c 'f="$$1"; rel=$${f#original/}; dir=$$(dirname "$$rel"); name=$$(basename "$$rel"); base=$${name%.*}; if [ "$$dir" = "." ]; then outdir="$(TARGET_DIR)"; else outdir="$(TARGET_DIR)/$$dir"; fi; mkdir -p "$$outdir"; out="$$outdir/$$base.webp"; convert -limit thread 1 "$$f" -auto-orient -resize $(RESOLUTION) "$$out"; command -v exiftool >/dev/null 2>&1 && exiftool -overwrite_original -TagsFromFile "$$f" "-all:all>all:all" "$$out" || true; touch -r "$$f" "$$out"' _ {}

sync-to-sdcard:
	@echo "Syncing $(TARGET_DIR)/ -> $(SDCARD)/ (preserve mtimes, compare by size, no ownership)"
	@rsync -rltW --size-only --del --progress $(TARGET_DIR)/ $(SDCARD)

prune-ignored:
	@echo "Pruning $(TARGET_DIR)/ based on $(IGNORE_FILE)"
	@find $(TARGET_DIR) -type f -print0 | \
		xargs -0 -I{} bash -c '\
			f="$$1"; rel=$${f#$(TARGET_DIR)/}; \
			if [ -f "$(IGNORE_FILE)" ]; then \
				while IFS= read -r pat || [ -n "$$pat" ]; do \
					case "$$pat" in ""|\#*) continue;; esac; \
					if [[ "$$rel" == $$pat ]]; then \
						echo "rm $$f (matched $$pat)"; rm -f "$$f"; exit 0; \
					fi; \
				done < "$(IGNORE_FILE)"; \
			fi' _ {}

I have three stages here:

  1. generate-miniatures: As the name suggests, this generates the thumbnails.
  2. prune-ignored: This allows me to remove stuff that I don’t want.
  3. sync-to-sdcard: A simple way to have a resumable copy.

It works, but it has a few pitfalls. It generates quality thumbnails, but exiftool copies all the metadata to them. Metadata can contain fields like “thumbnails” which are not needed at all in the minimized versions. Also, I tried to implement “ignore file” logic during the thumbnail generation process to avoid creating thumbnails that I would need to remove later. It was hard and messy. It would have been easier if I didn’t want parallelism, but with 100,000 files, that would be a waste. I spent some time playing with it, but the more bash I added, the more I got lost in the weeds. It would be easier to use a more civilized language to code it.

Just for reference - minimization took 1h 15min, and all miniatures had 20GB. Ouch!

Not too long ago, I wrote a tool for templating and building Docker images in parallel - template-dockerfilesexternal link . I thought maybe I could reuse some of it. So I started writing a new tool.

Second Try: A Dedicated Tool

I wrote the frameo-miniaturesexternal link tool in Go. It was actually an exercise to “vibe code” it with Google’s Antigravityexternal link . I have to admit, I’m really happy with the outcome.

Let me share some examples:

Generate miniatures with my own tool
frameo-miniatures -i ~/Photos -o /media/user/sd-card

The tool takes a few more parameters, like:

  • --resolution of the frame
  • --format webp (default), jpg
  • --quality (0-100) 75 by default
  • --workers 1 per core by default

If you’re interested in the full capabilities of the tool, check out its READMEexternal link .

With my own tool, I was able to resolve many smaller issues that would have been too tricky with bash scripts. For example, I saw that the scripts were creating thumbnails with sizes like 600x800 because that was the second limit on the command line. With my own tool, I’m doing rotation according to Exif and I generate thumbnails knowing that the frame can be rotated to a horizontal or vertical position, so I always use the “wider” border to have them in optimal resolution despite the orientation of the frame.

The integrated, .gitignore-style ignore file allowed me to easily build a list of events that I don’t want on my frame (I have almost 300 rules there already).

Everything runs in parallel and does not require any external dependencies. This makes this tool really lightweight on configuration, but powerful on execution.

On my i7-14600k , I was able to generate all the thumbnails in around 54 minutes, with the final directory weighing only 5.6 GB! With a 32GB SD card limit, that leaves me a lot of space. I didn’t expect such a good result, as the thumbnails generated by ImageMagick were around 20GB, so almost 4 times more. But as I mentioned, they contained the full Exif data, and that can weigh a bit.

Additionally, when I add new pictures to my photo library, I can quickly regenerate thumbnails, skipping what’s already there, and it takes just a few minutes. It wasn’t easy to implement such logic with pure bash scripts. It’s not that it wasn’t possible, but it just wasn’t convenient.

One thing that I haven’t figured out is what these frames use as a “photo creation date”. Despite synchronizing the creation/modification dates and the corresponding Exif metadata, I still see that the frame shows the date they were added for these pictures. Maybe they don’t read those attributes. I will play around with it and extend my new tool when I figure it out.

The last thing that I’m missing in this setup is online synchronization of the thumbnails generated on my PC with the Frame. I would really love to see Syncthingexternal link running on my frame. Maybe I’ll get there. 😉


Enjoyed this post? Buy Me a Coffee at ko-fi.com