Fastest way to add Title/Comments to JPEG?
August 12, 2018 7:19 PM   Subscribe

On Windows, I can use the File Manager to type in Title and Description text for my JPG photos. But it takes a large number of mouse-clicks. What's a good app/program that'll allow me to type in the text the quickest?
posted by storybored to Computers & Internet (9 answers total) 4 users marked this as a favorite
Sounds like a job for a script. Here's a proposed workflow:

1. Drag and drop a folder of photos onto the icon for describe-photos.cmd.

The script makes a text file inside the same folder called Descriptions.txt containing all the photo names in sorted order, each one followed by a tab character and a line break, then opens that text file in Notepad on the left of your screen and a Windows Explorer thumbnails view of the folder on the right.

2. Position the insertion point at the end of the first line in the Notepad file.

3. For each line in the file, type a title, hit Tab, type a description, hit down-arrow. Occasionally scroll the thumbnails window so you can see where you're up to.

4. Once you've entered as much text as you want to, close the Notepad window and click Yes when asked if you want to save it.

5. The script updates all the files with the titles and descriptions you've entered.

If you wanted to stop halfway and pick the job up again later, dragging and dropping the same folder onto the same script could make it re-open any Descriptions.txt that already exists inside that folder rather than creating a new one. It would also be easy to pre-populate newly created Descriptions.txt files with any existing titles and descriptions that already existed inside any of the listed photos, to make it interoperate smoothly with information entered by other means.

The proposed format for Descriptions.txt would also let you open it in any spreadsheet program like Excel or Calc. Don't know why you'd want to but it sounds vaguely useful.

The script required to make this work should be pretty straightforward. Let me know if this workflow would suit you and I'll happily write, test and post one.
posted by flabdablet at 10:58 PM on August 12, 2018

Just thinking about it a bit more, it would probably be better if a new Descriptions.txt always got rebuilt using the filenames from a photos folder, plus any existing titles and descriptions found inside the photo files themselves, every time you drag and drop the folder onto the script. That would let you move photos around from folder to folder without needing to worry about their associated Descriptions.txt files getting stale.

If you specifically wanted to work with an existing Descriptions.txt file rather than having the script rebuild it from the folder, that could be made possible by dragging and dropping Descriptions.txt itself, rather than its containing folder, onto the script.
posted by flabdablet at 11:06 PM on August 12, 2018

Photo Mechanic was built to help news photographers edit and caption photos quickly, one by one, or batch. There's a 30 day free trial.
posted by thenormshow at 6:55 AM on August 13, 2018

Flabdablet, that is awesomely generous! I'd be really happy to have a script that does this.
posted by storybored at 8:07 AM on August 13, 2018

Haven't forgotten about this, just having an unexpected amount of trouble working out how to read and especially write to the required properties from a script. That sort of thing ought to be trivial but apparently isn't. Bloody Windows. Watch this space.
posted by flabdablet at 3:32 AM on August 15, 2018

Okay, so this turned out a lot bigger and uglier and needed lots more annoying problems solved than I thought it would, but it works now. Mostly. For me. With any luck it will work for you too.

Open a new Notepad window and paste in the script below, from the line beginning with $b='\' to the #*/ line, inclusive. Save the result as edit-properties.js (when you're doing the Save, change the "Save as type" option from "Text Documents (*.txt)" to "All Files", otherwise Notepad will make edit-properties.js.txt which won't work).

To use the script, drag a folder with photos in it and drop it onto edit-properties.js. The folder will eventually open in an Explorer window, shortly followed by a Notepad window with a text file more or less as envisioned above. You can also drag and drop individual photo files, or selected groups of photo files, or the properties.txt file itself that the script creates when it runs.

Before you use this, have somebody you trust cast an eye over it to make sure it's not trying to do anything nefarious. Closest it gets to nefarious is requesting permission for temporary admin rights in order to copy a downloaded command into your Windows system folder. The command in question is downloaded from one of Microsoft's own servers, so it should be legit.

Let me know what breaks and I'll work with you to fix it.
$b='\'<#';ax=ActiveXObject;ws=WScript;fs=new ax('Scripting.FileSystemObject')
slice(0,-3)+'-'+new ax('Scriptlet.TypeLib').Guid.slice(1,37)+'.ps1')
cmd='powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -File "'+ps1+'"'
a=ws.Arguments;for(i=0;i<a.length;++i)cmd+=' "'+a(i)+'"'
new ax('WScript.Shell').Run(cmd,0,true);fs.DeleteFile(ps1,true)/*#>

# This is a polyglot script, syntactically correct in both PowerShell and
# JScript. A tiny amount of code is common to both languages, the rest being
# wrapped in the inactive language's comment markers. If the file is saved
# with a .js filename extension, the JScript part makes a temporary .ps1 copy
# and runs it with PowerShell, bypassing the default execution policy that
# usually blocks the use of .ps1 scripts and hiding the PowerShell console.

$propfile = 'properties.txt'
$propnames = 'System.Title', 'System.Subject', 'System.Comment'

function csv {
	"'$($Args -replace "'","''" -join "','")'"

function show {
	iex "[System.Windows.MessageBox]::Show($(csv @Args))"

function skip($count, $items) {
	$items[$count..($items.length - $count)]

# Make sure PROP command is available

add-type -AssemblyName PresentationFramework
$sha = new-object -ComObject Shell.Application
$wsh = new-object -ComObject WScript.Shell
$exe = "$env:SystemRoot\System32\prop.exe"
if (!(test-path $exe)) {
	$ok = show ('This script relies on the PROP command, which '+
	'Windows does not include as standard. PROP is available from '+
	"Microsoft's Codeplex archive of open-source software. Click "+
	'OK to download and install it or Cancel to quit.') `
	'PROP command missing' OkCancel Warning
	if ($ok -eq 'OK') {
		$zip = ''
		$url = "$zip"
		$tmp = mkdir $env:temp\$([guid]::newguid())
		$web = new-object net.webclient
		$web.DownloadFile($url, "$tmp\$zip")
		if (test-path $tmp\$zip) {
			$rel = 'd0195ec9-6f9b-47b3-a01a-795f0b7b2bb5'
			$snt = $sha.NameSpace("$tmp")
			ren $tmp\$rel "$"
			if (test-path $tmp\prop.exe) {
				$arglist = '/c', 'copy', '/b', "$tmp\prop.exe", $exe
				(start cmd $arglist -verb runas -window hidden -pass).WaitForExit()
		del -recurse $tmp

if (!(test-path $exe)) {
	$ok = show 'PROP not installed - quitting.' 'PROP command missing'

# Build properties files for all specified files and folders (one properties
# file for each input folder, even if input folders arrive with separate args)

$old = ''
@($Args | sort | get-item) + $null |
	if ($_.PSIsContainer) {
		$folder = $_.FullName
		$file = '*'
	else {
		$folder = $_.Directory.FullName
		$file = $_.Name
	if ($old -ne $folder) {
		if ($old) {
			if ($props.length -gt 1) {
				$props >$old\$propfile
			$old # to next pipeline stage
		$props = @()
		$old = $folder
	if ($file) {
		$dump = prop dump -output:table $folder\$file $propnames |
			where {$_.contains("`t")}
		$dump = @($dump) -replace '\t+$',''
		if ($props.length) {$props += skip 1 $dump}
		else {$props += $dump}
} |

# Edit each folder's properties file

	start $_ # Open Explorer window
	sleep 1
	start -wait notepad $_\$propfile
	$_ # to next pipeline stage

	# Close Explorer window (probably)
	$wsh.AppActivate($_) | out-null
} |

# Update properties from properties files

	$folder = $_
	$props = @(get-content $folder\$propfile)
	$header = @($props[0] -split "`t")
	skip 1 $props | %{
		$f = @($_ -split "`t")
		$i = 0
		prop set $folder\$($f[0]) (
			skip 1 $header | %{


posted by flabdablet at 9:31 AM on August 24, 2018 [3 favorites]

The window positioning is kind of crap, unfortunately. One of the problems that was just too annoying to solve was working out how to make PowerShell send WinKey+Left and WinKey+Right to a couple of open windows, to tile them on the left and right sides of the screen. So if you need to see the contents of both the Notepad and Explorer windows at once, you'll still have to do that by hand; sorry.

If anybody out there knows of a reasonably concise way to tile a couple of selected windows from PowerShell, please do share.
posted by flabdablet at 9:59 AM on August 24, 2018

This is truly excellent flabdablet! Just did a quick run of it on a directory of half a dozen jpegs. Worked like a charm. Hats off to you, sir!
posted by storybored at 1:03 PM on August 28, 2018

Glad to hear it worked for you.

The prop command it installs might also be of some use to you on its own. If you open a cmd or PowerShell window and enter

prop /?

you'll get the following brief overview of what it can do:
Supported commands:
  dump        Dumps item properties to the console.
  set         Sets properties on an item.
  schema      Manipulates the proprety schema.
  events      Displays property system eventlog data.
Further details about each of the subcommands can be got by adding /? to the end of each, for example prop dump /?

Playing with prop for a while leaves me with the impression that it's buggy and unfinished, and it's unlikely to get finished because it doesn't seem to have made the jump to Github since Microsoft shuttered Codeplex, but it works well enough for this script's purposes.

I remain fairly astonished by the fact that I could find no tidier way of interacting with the Windows properties subsystem.
posted by flabdablet at 12:50 AM on August 29, 2018

« Older Dating on the Other Side of the Train Tracks   |   What to gift myself Newer »
This thread is closed to new comments.