I really don't get why more users haven't embraced it for photos.
<snip>...and if it didn't require lots of custom fields and complex expressions...
I thought about this today as I set about tidying up the formatting of our image slideshow caption on the HTPC, and kind of get your point. Naturally, I think our slideshow caption is brilliant, and viewers definately enjoy the information, and, those times we just want the pictures, a button on the remote control turns off the OSD.
It's been a while, but I think that by default, MC simply uses the [Caption] field for slideshow OSD. This may be fine out of the box, but for us, we would always be looking at each other and discussing how old was so-and-so then?, or where was that? or when was that?
All of that info is right there in MC, so, how hard could it be to have it shown, right there in the caption?
Great eh? This is pretty much 'set and forget', but the 'set' part of that is a bit mental...
All of my image tags are held in the [Keywords] field. This makes exporting from Lightroom to MC absolutely painless as they arrive in MC pretty much fully tagged. I prepend an exclamation mark on all the nested branches to float them to the top of the keywords tree, like so:
It can then be a bit cumbersome in theater view on the HTPC, drilling in and out of keywords for some things, so, I set about extracting that info from [Keywords] into separate custom fields. These give me the contents of the specified branch, minus the root level label.
[Population]:Replace(ListGrep([keywords],!People),!People\,)&datatype=[list]
[Occasion]:Replace(ListGrep([keywords],!Events),!Events\,)&datatype=[list]
[Location]:Replace(ListGrep([keywords],!Places),!Places\,)&datatype=[list]
On the second line, the photographer, camera, and lens information are simple MC library fields, but, we need to try and format the caption to be grammatically correct regardless of the camera name, using "an" and "a" correctly, and only displaying lens info if it is actually present.
Finally, we track the ages of some people...
Among other things, auto import tags every photo imported with the raw birth date of each of these people, then, if any of them become tagged in a photo, their corresponding library field wakes up and calculates their age at the time the photo was taken, like so:
if(isequal([keywords],friends\alex,8),
if(isequal(formatdate(math(now()-([date,0]-[age-alex-birthday,0])),elapsed),days,8),
ifelse(
isequal(formatdate(math(now()-([date,0]-[age-alex-birthday,0])),elapsed),7,3),removeright(formatdate(math(now()-([date,0]-[age-alex-birthday,0])),elapsed),7) Days,
isequal(formatdate(math(now()-([date,0]-[age-alex-birthday,0])),elapsed),60,3),formatnumber(math(formatnumber(formatdate(math(now()-([date,0]-[age-alex-birthday,0])),elapsed))//7),0,,Weeks,Week),
!isequal(formatdate(math(now()-([date,0]-[age-alex-birthday,0])),elapsed),60,3),formatnumber(math(formatnumber(formatdate(math(now()-([date,0]-[age-alex-birthday,0])),elapsed))//30),0,,Months,Month)),
if(isequal(formatdate(math(now()-([date,0]-[age-alex-birthday,0])),elapsed),:,8),listitem(formatdate(math(now()-([date,0]-[age-alex-birthday,0])),elapsed),0,:) Hours,
ifelse(isequal(formatdate(math(now()-([date,0]-[age-alex-birthday,0])),elapsed),hours,8),formatnumber(math(formatnumber(formatdate(math(now()-([date,0]-[age-alex-birthday,0])),elapsed))//24),0,,Days,Day),
isequal(formatdate(math(now()-([date,0]-[age-alex-birthday,0])),elapsed),2,3),removeright(formatnumber(formatdate(math(now()-([date,0]-[age-alex-birthday,0])),elapsed),2),3) Year,
!isequal(formatdate(math(now()-([date,0]-[age-alex-birthday,0])),elapsed),2,3),removeright(formatnumber(formatdate(math(now()-([date,0]-[age-alex-birthday,0])),elapsed),2),3) Years))),)
auto import:I also created an "Age" tree for use in a standard panes view, which can be interesting fun:
listbuild(1,;,
ifelse(
isequal([Age-Vada],hour,8),Hours\[Age-Vada],
isequal([Age-Vada],day,8),Days\[Age-Vada],
isequal([Age-Vada],week,8),Weeks\[Age-Vada],
isequal([Age-Vada],month,8),Months\[Age-Vada],
isequal([Age-Vada],year,8),Years\[Age-Vada]),
ifelse(
isequal([Age-Caroline],hour,8),Hours\[Age-Caroline],
isequal([Age-Caroline],day,8),Days\[Age-Caroline],
isequal([Age-Caroline],week,8),Weeks\[Age-Caroline],
isequal([Age-Caroline],month,8),Months\[Age-Caroline],
isequal([Age-Caroline],year,8),Years\[Age-Caroline]),
ifelse(
isequal([Age-Tracy],hour,8),Hours\[Age-Tracy],
isequal([Age-Tracy],day,8),Days\[Age-Tracy],
isequal([Age-Tracy],week,8),Weeks\[Age-Tracy],
isequal([Age-Tracy],month,8),Months\[Age-Tracy],
isequal([Age-Tracy],year,8),Years\[Age-Tracy]),
ifelse(
isequal([Age-Stephen],hour,8),Hours\[Age-Stephen],
isequal([Age-Stephen],day,8),Days\[Age-Stephen],
isequal([Age-Stephen],week,8),Weeks\[Age-Stephen],
isequal([Age-Stephen],month,8),Months\[Age-Stephen],
isequal([Age-Stephen],year,8),Years\[Age-Stephen]),
ifelse(
isequal([Age-elli],hour,8),Hours\[Age-elli],
isequal([Age-elli],day,8),Days\[Age-elli],
isequal([Age-elli],week,8),Weeks\[Age-elli],
isequal([Age-elli],month,8),Months\[Age-elli],
isequal([Age-elli],year,8),Years\[Age-elli]),
ifelse(
isequal([Age-Nettie],hour,8),Hours\[Age-Nettie],
isequal([Age-Nettie],day,8),Days\[Age-Nettie],
isequal([Age-Nettie],week,8),Weeks\[Age-Nettie],
isequal([Age-Nettie],month,8),Months\[Age-Nettie],
isequal([Age-Nettie],year,8),Years\[Age-Nettie]),
ifelse(
isequal([Age-Mark],hour,8),Hours\[Age-Mark],
isequal([Age-Mark],day,8),Days\[Age-Mark],
isequal([Age-Mark],week,8),Weeks\[Age-Mark],
isequal([Age-Mark],month,8),Months\[Age-Mark],
isequal([Age-Mark],year,8),Years\[Age-Mark]),
ifelse(
isequal([Age-MarkB],hour,8),Hours\[Age-MarkB],
isequal([Age-MarkB],day,8),Days\[Age-MarkB],
isequal([Age-MarkB],week,8),Weeks\[Age-MarkB],
isequal([Age-MarkB],month,8),Months\[Age-MarkB],
isequal([Age-MarkB],year,8),Years\[Age-MarkB]),
ifelse(
isequal([Age-amiee],hour,8),Hours\[Age-amiee],
isequal([Age-amiee],day,8),Days\[Age-amiee],
isequal([Age-amiee],week,8),Weeks\[Age-amiee],
isequal([Age-amiee],Month,8),Months\[Age-amiee],
isequal([Age-amiee],year,8),Years\[Age-amiee]),
ifelse(
isequal([Age-tracy c],hour,8),Hours\[Age-tracy c],
isequal([Age-tracy c],day,8),Days\[Age-tracy c],
isequal([Age-tracy c],week,8),Weeks\[Age-tracy c],
isequal([Age-tracy c],Month,8),Months\[Age-tracy c],
isequal([Age-tracy c],year,8),Years\[Age-tracy c]),
ifelse(
isequal([Age-alex],hour,8),Hours\[Age-alex],
isequal([Age-alex],day,8),Days\[Age-alex],
isequal([Age-alex],week,8),Weeks\[Age-alex],
isequal([Age-alex],Month,8),Months\[Age-alex],
isequal([Age-alex],year,8),Years\[Age-alex]),
ifelse(
isequal([Age-megan],hour,8),Hours\[Age-megan],
isequal([Age-megan],day,8),Days\[Age-megan],
isequal([Age-megan],week,8),Weeks\[Age-megan],
isequal([Age-megan],Month,8),Months\[Age-megan],
isequal([Age-megan],year,8),Years\[Age-megan]),
ifelse(
isequal([Age-kinley],hour,8),Hours\[Age-kinley],
isequal([Age-kinley],day,8),Days\[Age-kinley],
isequal([Age-kinley],week,8),Weeks\[Age-kinley],
isequal([Age-kinley],month,8),Months\[Age-kinley],
isequal([Age-kinley],year,8),Years\[Age-kinley]))&datatype=[list]
Again, in the OSD, when more than one person, this needs to be presented in a grammatically correct way, and, if there are no ages to display, just show the date, but if there are ages to display, show the date at the end, separated with a " - "
Getting back to the final OSD output:
First, here's the actual expression code entered into the slideshow caption setting in MC's options:
ifelse(isequal([osd],;!,8),replace([osd],;!,/,/ ),isequal([osd],;,8),replace([osd],;,/,/ ),1,[osd])
Photographer: [artist], using if(regex([camera],/#^([aAeEiIoOuUhH])#/,0),an,a) if(isequal([camera],nikon d5000,8),[camera] with if(isequal([lens],10,8),a,an) removeright([lens],10) lens at [focal length],[camera])
[baby's age][baby's date]
On the first line, we pose a series of questions:if [Caption] is not empty, use that, otherwise,
if [Occasion] is not empty, use that, otherwise,
if [Location] is not empty, use that, otherwise, leave it blank.
For this, and (laughably perhaps) a bit of simplicity, all of the heavy lifting is performed in the [OSD] field, which goes like so:
ifelse(!isempty([caption]),[caption],!isempty([occasion]),if(isequal([occasion],!,8),regex([Occasion],/#!(.+)\\(.+)#/,-1,0)if(isequal([R1],\!,8),replace([R1],\!,/,/ ),[R1]): [R2],[occasion]),!isempty([location]),if(isequal([location],!,8),regex([location],/#!(.+)\\(.+)#/,-1,0)if(isequal([R1],\!,8),replace([R1],\!,/,/ ),[R1]): [R2],if(isequal([location,0],;,8),replace([location,0],;,:/ ),[location])),1,[caption])
What this does, is pose the questions asked above, and then format the result, removing semi-colons, back slashes and exclamation marks, inserting commas and spaces where required, with final bits of tidying up performed via a second pass over the output of this OSD field.
On the second line, the photographer and camera info is cleaned up and presented using:Photographer: [artist], using if(regex([camera],/#^([aAeEiIoOuUhH])#/,0),an,a) if(isequal([camera],nikon d5000,8),[camera] with if(isequal([lens],10,8),a,an) removeright([lens],10) lens at [focal length],[camera])
Finally, the third line brings us back to people's ages:The [baby's age] field gathers and cleanly presents any age information if it is present:
if(regex(listbuild(1,=,ifelse(isequal([keywords],people\family\kinley,8),Kinley/ aged/ [age-kinley]),ifelse(isequal([keywords],people\family\amiee,8),Amiee/ aged/ [age-amiee]),ifelse(isequal([keywords],people\family\elli,8),Elli/ aged/ [age-elli]),ifelse(isequal([keywords],people\family\vada,8),Vada/ aged/ [age-vada]),ifelse(isequal([keywords],people\family\stephen,8),Stephen/ aged/ [age-Stephen]),ifelse(isequal([keywords],people\family\tracy,8),Tracy/ aged/ [age-tracy]),ifelse(isequal([keywords],people\family\caroline,8),Caroline/ aged/ [age-caroline]),ifelse(isequal([keywords],people\family\markb,8),MarkB/ aged/ [age-markb]),ifelse(isequal([keywords],people\family\nettie,8),Nettie/ aged/ [age-nettie]),ifelse(isequal([keywords],people\friends\Alex,8),Alex/ aged/ [age-alex]),ifelse(isequal([keywords],people\friends\megan,8),Megan/ aged/ [age-megan]),ifelse(isequal([keywords],people\friends\Tracy c,8),Tracy C/ aged/ [age-tracy c]),ifelse(isequal([keywords],people\family\markw,8),Mark/ aged/ [age-mark])),/#^(.+?)(?:=([^=]+))?$#/),listbuild(1, / and/ , replace([R1], =, /,/ ), [R2]), )
MrC kindly provided the regex magic for this one. As you add ages to track, you just need to add an "IfElse()" condition for that person and the expression takes care of the required grammatical output. It's very clever.
As I also want the date on this third line, on its own if there are no ages, and hyphenated if there are, [baby's date] takes care of that using:
ifelse(!isempty([age]),/ - formatdate([date,0],dd MMMM yyyy),1,formatdate([date,0],dd MMMM yyyy))
and there you have it.
Now, as mental as this appears, very minor tweaking aside, these background fields and expressions have been quietly getting on with it for me for a good ten years or more, and the continued pleasure the results provide for people viewing slideshows on the TV definately makes the initial setup worth the time spent on it.
The challenge to the JRiver team, who I am sure would like to see a jump in users utilising the image capabilities of MC, is to make all of this possible in a way that is friendly for first time users. Similarly, the same applies to the tooltip/theater view file info panel customisation thread. There is no denying it's popularity, but the hoops involved to arrive at those ends is waay beyond the scope of first time users.
For MC27, why not build the configurators for these things into MC and so make them easily accessible to everyone
References:Importing Lightroom keyword heirarchy into the MC [Keywords] field.Deriving Age from Date of photo? (apologies for the seemingly haphazzard presentation of this one)
Create Custom Tooltips/TheaterView Screens Using Dolby etc. Logos (Currently 13 pages long, with 30,800 views)
-marko