My quick photo tagging app

Tuesday, May 14, 2024   (updated 3 days later)

Comments: 4   (latest 3 days later)

Tagged: photos, phogg, tagging, python, javascript, programming

I spent a couple of weeks in Italy and took a lot of photos. I mean, some photos. A lot for me. I'm not much of a photographer. (My dad took a lot of photos.)

Upon coming home, I realized that searching my photo folder had gotten to be a nuisance. It was just a directory of 1800-ish images. The only metadata was the creation date. Can't we at least have some kind of text tag system?

But how do you apply tags? Suddenly this was a design problem.

Interrupting my own narrative with a TLDR: I wrote a photo tagging app, but it's not documented or supported or anything. So you probably shouldn't try it. If you want to, great! Here's the repo. But this is a for-me project.

And this blog entry is a "what have I been up to?" post, not a project announcement.

Okay, where were we? Right, photo tagging systems.

I asked around and got a couple of reasonable suggestions:

  • Apply Mac filesystem tags in the Finder.

  • Try DigiKam, an open-source cross-platform photo management app.

  • Keep notes in a text file.

None of these felt right. DigiKam is ponderous; it does ten thousand things, of which I only care about one. I didn't want to swing that kind of mass around. (Yes, I use GnuIMP for trivial image edits, but that's different. Ahem.)

As for Mac Finder tags -- sure, I'm using a Mac already. But I'd need a way to export that data to some portable format. File metadata is fragile. (Someday this Mac will die. My next machine will probably be another Mac, but...) So there's still a design problem.

Anyhow, I have my Finder windows pretty stripped down by default. Rearranging the UI for photo-tagging would impinge on the rest of my carefully curated desktop experience. Change sucks.

Text files? You know I love text files. My financial records for my gamedev business are a big text file. (Okay, and three spreadsheets. But mostly the text file.)

Problem is, photos aren't text. I want to see the pictures and the tags together in one window.

So it seems like it's time to build a thing.


But what to build it in? Platforms? We got platforms!

  • Mac native app? (Swift? ObjC?)
  • Python and some kind of UI toolkit?
  • Command-line Python?
  • HTML/JS and Electron?
  • HTML/JS and a web server app?
  • C++/KDE? (Or GTK, or...)
  • Rust?

Believe it or not, I gave serious consideration to all of these systems. They all could be made to work. My train of thought about the tradeoffs:

While I have a lot of iOS app experience, it's quite out of date these days. Anyhow MacOS is fairly different at the UI toolkit level. I'd be learning a lot -- that's both a pro and a con! But I don't have much need for Mac app coding (which is why I've never tried it) so the positive is pretty limited.

Also, again, what if my next computer isn't a Mac? Linux people have been telling me for fifteen years that MacOS was becoming too locked-down to seriously use. I'm sure they'll be right any minute now.

Python is my favorite quick-project language, but I've never built a UI app in it. I don't even know what the UI toolkit options are. I see someone built an FAQ site on the subject. Of course there are too many options and my brain shuts down.

Rust is even worse on the UI front. I admit that I didn't give this option serious consideration.

C++? I suspect it's the most popular GUI-app language, so the toolkits must be infinite. (Including many of the same ones that Python supports. C++ can also bind to the native MacOS UI.) I only mentioned KDE above because I know GnuIMP uses it. (EDIT: Whoops, it uses GTK, thanks Daniel Smith for the note.) I'm sure this path would work out okay, but I bet it would also be bulky. C++ is heavyweight, well-established UI toolkits are heavyweight, it's just going to be a lot.

Command-line Python seems like a dead end, since I said "I want to see the pictures and the tags together in one window." But MacOS has a QuickLook feature that opens a temporary display window for any file. Maybe I could commandeer that for browsing images? A search for "macos command line quicklook" brings up some discussion, mostly about the qlmanage tool. Promising! But browsing images one at a time doesn't seem ideal. I want to scroll through lots of them.

HTML is a great way to browse images. Its UI model is primitive, even after all these years of web apps, but I don't need much beyond checkboxes and input fields.

However, the road to an HTML app forks right on your doorstep. Are you installing a web service or building an Electron app?

I've used Electron before, notably for the Lectrote IF interpreter. I know how to make it go. It's cross-platform and well-supported. The two big disadvantages are:

  • Electron is enormous. An app download runs nearly 100 MB. (160 MB for a universal Mac app, but I'm off of Intel these days so I don't need universal.)

  • Electron has pretty regular code churn. My last Lectrote release was September, using Electron version 24; the latest is 30. And you do have to adjust your code every few versions. (Some kind of Lectrote bug turned up when I tested Electron 25 or 26. I'll have to upgrade and deal with it someday.)

On the up side, once you build an Electron app, the binary remains very stable across OS upgrades. At least that's my experience. But I hate fossil code, and I don't really want to come back in five years and discover that I need to rebuild my photo system from more-or-less scratch.

What about the web service? Installing and running a web server is a headache. Installing server-side scripts is another headache, with as many options as the UI toolkit world. Python? Perl? PHP? What's CGI look like these days? (Complicated, turns out.)

But here's where things get circumstantial. I've already worked through most of these headaches. I spent a couple of months this winter building a new admin interface for the IF Archive.

A screenshot of the Archive admin web app. It shows listings for a couple of recently-uploaded Twine games, with buttons labelled "Zip", "Move", "Rename", and "Delete". The private IF Archive admin tool.

This isn't publicly available -- it's meant for Archive volunteers, who need to move files around and edit metadata on the regular. It's very Web-1.0, as you can see.

Behind the scenes, this admin tool is a Python app running in Apache via the WSGI interface. (Like CGI, but modern and Pythonic.) And I developed it on my home Mac! Which means I already have the WSGI Apache module running, and I know how to configure it.

I've even written a tiny app framework for making web apps. Currently tinyapp is just a directory in the admintool repo, but now that I've used it twice, I suppose I need to split it out into a separate project. Here's a minimal example of tinyapp:

from tinyapp.app import TinyApp
from tinyapp.handler import ReqHandler
from tinyapp.constants import PLAINTEXT

class han_Home(ReqHandler):
    def do_get(self, req):
        req.set_content_type(PLAINTEXT)
        yield 'Hello world.\n'

appinstance = TinyApp([
    ('', han_Home),
])

(This is modelled on Tornado's web-app API. I've used Tornado in the past, but not with Apache or WSGI. For simple projects it was easier to start from scratch.)


Doing this server-side (as opposed to a desktop app) has another circumstantial advantage. I have a house media server -- a Mac Mini on a shelf. It runs Apache. Why not move my photo collection there? There's no particular reason that those files need to take up space on my desktop machine. Sure, I browse them sometimes, but the photo app will be good for browsing.

Okay, so I have a plan: Apache/WSGI/Python back end, HTML/JS/jQuery front end.

(HTML forms were fine for the Archive admintool, but this is going to have a more dynamic UI. Select a checkbox, update a bunch of image tags. Search, filter, enter text with tab completion. jQuery is an old friend for that stuff.)

So all that remains is to draw the rest of the owl!

A screenshot of the photo web app. It has a column of photo thumbnail images and a large list of text tags. Credit to William Reimann for those interestingly carved rocks. The watercolor sheet shows natural pigments collected by Hanna Bernbaum. The glowy disc thing is an tech/audio project by Steve Pomeroy. The photo in the lower right is the southernmost Alp by Lake Garda -- Monte Pizzocolo, I think. And the flowers in the upper left are purple deadnettle.

I don't have much to say about the photo app itself. It shows a bunch of image thumbnails and tag checkboxes. It stores the tags in a SQLite file. Also JSON and plain greppable text, so that if the app completely rots I'll still have the raw tag data.

Here's the source. I named it "Phogg", more or less following my blog generator project, "Bloggor".

(I could have named it "Phoggor" but that just didn't sit right.)

Phogg is a tidy little project. 700 lines of Python, 800 lines of JS. Of course I'm just exporting the code-bulk to my regular web browser! Modern web browsers are the kaiju of the software world. (The guys who named "Mozilla" had no idea what they were invoking.) But we've all become numb to that.

Like I said, I have about 1800 images. (More if I decide to download all of my dad's Italy photos.) It's not a huge collection as photo collections go, but I worried about performance. <img loading=lazy> turned out to be a good idea. Also, I added some code to downscale images into thumbnails, which I store as static files. This adds an extra step when importing photos, but it's worth it.

The only surprise was that formatting 1800 timestamps with Python's datetime module was a detectable drag. I used the older time.strftime() instead. It's not as smart about timezones, but I don't have good timezone data on these photos anyhow.

Features not yet implemented: undo/redo. Gotta take a look at this repo.


And that's what I've been doing the past week. The initial commit was seven days ago, so I feel pretty good about the time investment.

Can you use Phogg? Feel free -- at your own risk. I haven't written up much in the way of install instructions. I'm not accepting change requests. I made this for my own use.

Fun, though!


Comments from Mastodon