How can I write a script that finds my top-rated photos?


I'm not sure if I'll be able to keep it up, but I'm going to see if I can make Monday "Little Programs" day, where I solve simple problems with little programs.

Today's little program is a script that goes through your Pictures folder and picks out your top-rated photos.

The key step here is extracting the rating, which goes by the name System.Rating in the shell property system. The method which does the extraction is Shell­Folder­Item.Extended­Property.

var shell = new ActiveXObject("Shell.Application");
var picturesFolder = shell.Namespace(39); // CSIDL_MYPICTURES
var items = picturesFolder.Items();
var SHCONTF_NONFOLDERS = 64;
items.Filter(SHCONTF_NONFOLDERS, "*.jpg");
for (var i = 0; i < items.Count; i++) {
  var item = items.Item(i);
  if (item.ExtendedProperty("System.Rating") >= 80) {
    WScript.StdOut.WriteLine(item.Path);
  }
}

Wow, that was way easier than doing it in C++!

That program searches one folder, but let's say we want to do a full recursive search. No problem. Take the code we wrote and shove it into a helper function process­Files­In­Folder, then call it as part of a recursive directory search.

function processFilesInFolder(folder) {
  var items = folder.Items();
  var SHCONTF_NONFOLDERS = 64;
  items.Filter(SHCONTF_NONFOLDERS, "*.jpg");
  for (var i = 0; i < items.Count; i++) {
    var item = items.Item(i);
    if (item.ExtendedProperty("System.Rating") >= 80) {
      WScript.StdOut.WriteLine(item.Path);
    }
  }
}

function recursiveProcessFolder(folder) {
  processFilesInFolder(folder);
  var items = folder.Items();
  var SHCONTF_FOLDERS = 32;
  items.Filter(SHCONTF_FOLDERS, "*");
  for (var i = 0; i < items.Count; i++) {
    recursiveProcessFolder(items.Item(i).GetFolder);
  }
}

var shell = new ActiveXObject("Shell.Application");
var picturesFolder = shell.Namespace(39);
recursiveProcessFolder(picturesFolder);

You can use this as a jumping-off point for whatever you want to do with your top-rated pictures, like copy them to your digital photo frame.

Comments (25)
  1. kinokijuf says:

    What’s “rating”?

  2. John says:

    Is there a property for specific features of human anatomy?  I'm asking for a friend, of course.

  3. xpclient says:

    Search box, type rating:5 stars. Windows 7 made an awesome change in this area. It autocompletes the AQS. I wish the Start Menu search box autocompleted the AQS. Also, more of the AQS needs to be exposed through a GUI.

    ["Windows 7 made an awesome change in this area…" Wait, so now you admit that there was a change for the better? You previously said that every change made after Windows XP SP2 was for evil, and "An app must be serving its users really well if it hasn't changed much", and "Whatever team worked on Vista/7 Explorer shell have no understanding of usability." -Raymond]
  4. John Doe says:

    "Little Programs Monday", excelent! I hope you can keep it up for long!

    Nitpick: why not declare CSIDL_MYPICTURES?

    Suggestion: since these scripts are enormous time savers, how about a way to run a script from a resource in C++, optionally retaining the environment/script engine instance between script executions? It might as well be very useful to have objects/functions injected into the environment/script engine instance. For instance, include() and require() other scripts from the resources, the difference being that required scripts are loaded only once in the same environment/script engine instance.

    In general, this technique could be proper for huge code blocks of COM automation that are plain ugly in C++, no matter what helper or wrapper classes you choose or develop. For instance, you cannot call an undeclared method in C++, because it's not a dynamically typed language, so you'll always end up invoking methods by name, even if you create a class that wraps those invocations.

    And you wouldn't worry about reference counting and smart pointers, although you have to be careful about circular references.

  5. Jack says:

    Nice, I'm really looking forward to Little Programs Monday now. (Much more so than Patch Tuesday anyway.)

  6. xpclient says:

    Very good. Apply everything I say conveniently omitting its context.

    [The first comment was in response to a deleted feature. But it wasn't clear from context whether you mean that deleting a feature is an act of evil, or that you meant that everything that happened since XPSP2 was an act of evil. You did say after all "plain evil". If you merely meant that deleting features is evil (but other MS actions may or may not be evil), then I apologize for misunderstanding. -Raymond]
  7. Joshua says:

    ["Windows 7 made an awesome change in this area…" Wait, so now you admit that there was a change for the better? You previously said that every change made after Windows XP SP2 was for evil, and "An app must be serving its users really well if it hasn't changed much", and "Whatever team worked on Vista/7 Explorer shell have no understanding of usability." -Raymond]

    Now who's trolling who?

  8. Brian_EE says:

    @xpclient: "Very good. Apply everything I say conveniently omitting its context."

    Must be because Win7 conveniently removed the "XPclient" context menu from the shell.

  9. Adam Rosenfield says:

    How are the actual ratings stored on disk?  I tried rating a JPEG, but I didn't see any changes in the EXIF data (though the file's timestamp was updated).

  10. Entegy says:

    What were you using to view EXIF data? Maybe it didn't have a chance to refresh.

  11. @Adam Rosenfield: in an alternate data stream, I would guess.

  12. Leo Davidson says:

    Very minor thing but it might be worth saying the code is JScript to help people not familiar with how it looks.

  13. Adam Rosenfield says:

    @Entegy: Cygwin's exif(1) program.

    @Matteo: Good idea, but that also doesn't seem to be the case.  "dir /R" doesn't report any new alternate data streams.

  14. John says:

    Seems to be putting the metadata inside the file.  Empty jpg = 2KB, adding a rating = 9KB.  Some binary data in addition to some XML.  I suppose jpg format allows for extra data blobs to be inserted, though the purist in me frowns on modifying files for extraneous metadata such as this.

  15. @Adam Rosenfield: now that you checked, I seem to recall a post by Raymond about the fact that they stopped storing metadata in ADS because they cannot be copied to non-NTFS volumes.

    Oh, here it is: blogs.msdn.com/…/10299322.aspx

  16. Gabe says:

    Leo Davidson: Indeed, I thought it was possibly C# until I got to the part where he defined functions.

  17. Adam Rosenfield says:

    Upon further inspection, it looks like the rating is stored inline in the JPEG file using some XML; either it's not part of the EXIF data, or Cygwin's exif(1) program is ignorant of it.

    I tried posting the XML, but methinks it got caught in the blog's spam filter.  It contained various tags such as <?xpacket begin>, <x:xmpmeta …>, <rdf:RDF …>, <rdf:Description …>, etc.  The actual rating is there twice, once in a <xmp:Rating> tag, and once in a <MicrosoftPhoto:Rating> tag.

    [Extensible Metadata Platform, an ISO standard. -Raymond]
  18. SomeGuyOnTheInternet says:

    Man it would be so cool to be flamed by Raymond Chen: pbs.twimg.com/…/A_5iVsaCQAAAp_I.jpg:large

  19. Ian Yates says:

    @SomeGuyOnTheInternet – That's gold!  :)

  20. Evan says:

    Can we pleeeeease make this discussion about the post and not xpclient?

    Anyway, I second John Doe and Jack: even though I don't really have use for today's particular script (I use a dedicated program for photo management), but I'm *really* looking forward to see where this series goes.

  21. Patrick C. says:

    Wow, it really warms my heart to see someone else writing WSH scripts in JScript. Judging from the dearth of examples online, I thought I was the only person doing that.

  22. ChuckOp says:

    Love the concept of Little Programs Monday.  Question though; how can I use this from PowerShell?  Maybe I'll research that myself…

  23. Tim says:

    ChuckOp, anything you can do with WSH you can do with PowerShell.

    "New-Object -ComObject" is the key.

  24. danwalker.ak @ gmail says:

    I love the idea (and implementation thus far of Little Programs Monday.  Thank you for this bite-sized feature, Mr. Chen.

Comments are closed.

Skip to main content