FileMaker 作るメディア管理DBの話。メタデータを取得して専用メタテーブルに展開して利用します。ExifTool を利用します。
このページ内の見出し
ExifTool の取得と利用 – 概要と前提
以前、FileMakerでメディア管理 – メタデータの取得で、取得をレベルに分けました。
LEVEL 1 何もしない
LEVEL 2 GetAsText
LEVEL 3 GetContainerAttribute
LEVEL 4 AppleScript
LEVEL 5 Exiftool
今回は LEVEL 5 ExifTool を使用してでメタデータを取得するお話です。
前提として、メインのテーブルがすでにあってメディア管理が成り立っている状態です。IDがあり、オブジェクトフィールドがあります。つまり FileMakerで作るメディア管理データベースの存在が前提ということで、このポストはFileMakerメディア管理 実作編 Ver.5 とも連動しています。
ExifTool をインストール
ExifTool はメタデータの取得や編集ができるコマンドラインで使うツールです。公式サイトにインストーラーを用意してくださっているので、ダウンロードしてインストールします → exiftool.org
ExifTool のちょっと詳しい話は ExifToolのかんたんなメモ をご覧ください。
取得と利用
ExifTool を使って何をするかというと、一つは、ファイルからメタデータをゲットします。もうひとつはファイルにメタデータを書き込みます。
ゲット
メタデータをゲットすると言っても、一つのファイルのメタデータをゲットするのか、フォルダを指定してその中のファイルからだーっとゲットするのかという違いがあります。
何をゲットするのかという話もあります。特定のタグを指定して頂くこともあるでしょうし、個別にタグを指定せず、ファイルが持っているメタデータをだーっと全部頂くこともあります。ここでは、全部のメタデータをだーっと頂く話になります。
頂いたメタデータをどうするのかという話もあります。頂いたデータはタグ名と値のリストになっていますから、これを別途メタデータテーブルにだーっと展開してレコード化します。
ゲットした次
さらに、一部の特定タグをレコードのフィールドとして昇格させ、フィールド値としていただきます。これはどういうことかというと、例えばだーっとメタデータを頂くとその中に「DateTimeCreated」というタグがあったりします。これをただメタデータのひとつとして放置せず、「オリジナル作成日」あるいは「撮影日」という特別に作成したフィールドの値として頂くという話です。
いい加減だーだー言うのを辞めろと今声が聞こえました。
埋め込み
メタデータの書き込み、埋め込みと言ったほうがいいのか、これは、一つのファイルにメタデータを埋め込みます。メタデータの編集とも言えます。
編集と言っても、メタデータそのものを編集することの他に、FileMaker の特定フィールドをメタデータのタグとして移転させるということも含みます。例えばメインレコードのテーブルに「作成者」というフィールドがあって、元の Exif に by-line タグ が存在しない場合、by-line タグを作成してフィールドの値を書き込みます。
CodeRun の前提
ExifTool はコマンドラインで命じるツールで、普通はターミナルにたたたっとタイプして使います。ここでは、FileMaker 書類の内部から直接命じることを前提とします。これは、CodeManager というコード管理&実行システムの CodeRun を使います。なんのこっちゃと今思いましたね。当然ですね。これに関しては次の記事を参照してください。
→ Macで実行可能なFileMakerコード管理 CodeManager v2
ExifTool のコマンドやオプションに触れても、実行のさせ方についてはここでは触れませんのでその前提でお願いします。
取得
ファイルやフォルダ内ファイルからメタデータを取得するとき、以前は「ファイルが一つだけならこう」「フォルダから複数ファイル分ならこう」とExifTool コマンドを分けていました。でも今はファイル単体でもフォルダごとでも最初のステップはほぼ同じです。即ち、ExifTool のオプションは次のようにします。
ExifTool のオプション
exiftool -j -api largefilesupport=1 -s -g -d '%Y/%m/%d %H:%M:%S' $POSIX1
最後の $POSIX1 は、ここに目的パスが入ります。仮置きマイ変数です。パスはファイルでもフォルダでもOK。フォルダの場合、最後に / を付けないでね。
-j
JSON 形式で出力させます。これにより、ファイル単体でもフォルダごとでも同じように扱えるようになります。JSON を使う理由は、ただいちびっているからではありません。普通にゲットすると、拡張子のないファイルでエラーになるからです。JSON のほうが安全確実です。
訳註: いちびる = 調子に乗る、格好を付ける
-api largefilesupport=1
でかいファイルをサポートするオプションです。ムービーファイルでは必須では。
-s, -g
-s は短いタグ名、-g はグループの取得です。-s はわざわざ付けなくていいのかな。わからないけど付けてます。
-d ‘%Y/%m/%d %H:%M:%S’
取得する日付のフォーマット。2022/08/22 0:30:45 という形で取得します。
-r
-r オプションを付けると、フォルダの場合、フォルダの中のフォルダまで全部含めます。サブフォルダ含むってやつです。フォルダを指定してゲットする場合は -r を付けるか付けないか、分かれ目になります。
以上のExifToolのコマンドに、最後オマケを付け足します。何かというと、結果をクリップボードに収めます。
| pbcopy
これを付け足して完了。メタデータをゲットするコマンドは、ファイル単体またはフォルダ内ファイル(階層限定)の場合はこう
exiftool -j -api largefilesupport=1 -s -g -d '%Y/%m/%d %H:%M:%S' $POSIX1 | pbcopy
サブフォルダも含める場合はこう
exiftool -j -api largefilesupport=1 -r -s -g -d '%Y/%m/%d %H:%M:%S' $POSIX1 | pbcopy
このどちらかを(ユーザーが)選んで実行させます。
コマンドをAppleScript に翻訳して実行してクリップボードに入った結果をグローバルフィールドである「結果フィールド」にペーストするまでを、スクリプトとして作成しておきます。
FileMakerメディア管理 実作編 Ver.5 をダウンロードされていたら、CodeRun や Exif 関連で実際のスクリプトが確認できます。
フィールドにゲットした JSON を展開
JSON 形式の結果が結果フィールドに収まりました。
結果フィールドの JSON データを今からどうにかしなければなりません。難易度高いです。計算式やスクリプトで乗り切りましょう。
JSON
JSON とはなんぞや。それはややこしい書式です。[ ] とか { } がやたらと出てきますが、理屈は「配列の配列」を格納しています。FileMaker には JSON を取り扱う関数が用意されていまして、難しいけど何とかなります。私も理解に乏しいです。これから間違ったことを書いたらすいません。
-j を付けてゲットした JSON データは、ファイルとフォルダでは中身が異なります。
ファイル単体を指定した処理の場合、取得した JSON データは、一つのファイルのメタデータの束です。「タグ名とその値」が束になっています。束が一つ。タンタバです。
フォルダを指定した場合、ファイルの「タグ名とその値」の束が、ファイルの数だけさらに束になっています。束の束でタバタバです。
- フォルダの場合:
- タバタバ(結果フィールドG)を タンタバに分解して各レコードのフィールドに格納
- 各レコードのタンタバをタグ名と値に分解してメタテーブルに展開
- ファイルの場合:
- タンタバ(結果フィールドG)をレコードに転記(レコードのフィールド)
- タンタバをタグ名と値に分解してメタテーブルに展開
いいかげんダバダバシャバダバ言うのを辞めろと苛立ちの声が今聞こえました。
スクリプト
フォルダの場合
フォルダの場合に結果フィールドのタバタバ・・・複数レコード分のメタデータ群をレコードごとに分配することは意外と骨が折れます。なぜなら、どのレコードのデータなのか JSON 君は知るよしもないからです。従って、JSON展開の専用テーブルを作成してまずはそこに展開します。
FileMaker の関数 JSONListKeys() を使います。まずは結果フィールドを$JSON という変数に収めた後、JSONListKeys ( $JSON ; “” ) で分解。この分解は、タンタバ…レコードごとの束です。これを Loop で1行ごとに処理していきます。新規レコードの特定フィールドに分配していくわけです。
こうしてレコードごとにタンタバ・・・レコード単体のメタデータの束がまとめて収容されました。引き続きJSON形式のままです。
次にやることは、このデータからパスを見つけ出すことです。JSON データから「SourceFile」を見つけて、パスフィールドに格納、それを元にパスモドキを作成します。パスモドキが作られたら、既存のデータとリレーションが成立します。
パスモドキとは、パスから作る照合用フィールドです。
なぜパスそのものを照合してはいけないかというと、こちらに詳しく。
FileMakerで正しくリレーションできない照合の問題、原因と解決【重要】
これで、既存レコード、またはインポート途中ならインポートテーブルのレコードと繋がりましたので、タンタバのフィールドを既存レコードまたはインポートテーブルの該当フィールドに転記します。
一応、実作編V5からスクリプトを転記しておきます。あくまで参考程度に。
#*********************************************************
#GLB結果フィールドを分解して Import2テーブルにレコード化
# 結果の JSON を分解、ImportListまたはImport2 にレコード化、「結果」フィールドに格納
#*********************************************************
#インポート作業中なら import2 に展開、
#スクリプト引数 で 作業レイアウトを指定。なければ import2
If [ not IsEmpty ( Get ( スクリプト引数 ) ) ]
レイアウト切り替え [ Get ( スクリプト引数 ) ]
Else
レイアウト切り替え [ 「ImportList2」 (Import2) ]
End If
変数を設定 [ $table; 値:Get ( レイアウトテーブル名 ) ]
#——————————————–
#CodeRun の GLB 結果をテーブルにレコード化
# JSONエレメントとして分割
#——————————————–
# 別テーブルを使う理由 – 最初にパス情報がないから。
#——————————
#JSONの塊を最初の分解。レコード化する。
変数を設定 [ $JSON; 値:CodeRun::GLB_結果フィールド ]
変数を設定 [ $JLK; 値:JSONListKeys ( $JSON ; “” ) ]
変数を設定 [ $vc; 値:ValueCount ( $JLK ) ]
変数を設定 [ $c; 値:-1 ]
#注) json関数では1行目を0とカウントするので
Loop
変数を設定 [ $c; 値:$c+1 ]
Exit Loop If [ $c = $vc ]
#$c を -1 から始めたので vc もひとつ減らしておく。つまり > ではなく、 = にする。
変数を設定 [ $JGE; 値:JSONGetElement ( $JSON ; $c ) ]
#ここで新規レコード
新規レコード/検索条件
#最初の分解JSONである $JGE を記入。
フィールドを名前で設定 [ $table & “::結果フィールド”; $JGE ]
End Loop
#***** 以上で、テーブルの結果フィールドに分解された JSON が入った
#——————————————————-
#ときどき、空のレコードが現れるので、結果フィールド = 検索して消しておく
#——————————————————-
エラー処理 [ オン ]
検索モードに切り替え [ ]
フィールドを名前で設定 [ $table & “::結果フィールド”; “=” ]
検索実行 [ ]
If [ Get ( 対象レコード数 )>0 ]
レコード/検索条件削除 [ ダイアログなし ]
End If
全レコードを表示
#——————————————————-
#JSON から「SourceFile」を見つけて posix にセットする
# (なぜかJSONlistValueで取得できないので JSONGetElementを使う)
#——————————————————-
レコード/検索条件/ページへ移動 [ 最初の ]
変数を設定 [ $vc; 値:Get(対象レコード数) ]
Loop
If [ IsEmpty ( Evaluate ( $table & “::結果フィールド” ) ) ]
Else
#json
変数を設定 [ $JSON2; 値:Evaluate ( $table & “::結果フィールド” ) ]
#見やすく整形 – 必要かどうかテストすること
変数を設定 [ $jf; 値:JSONFormatElements ( $JSON2 ) ]
#SourceFileを見つけて filePosix に転記
変数を設定 [ $JGE_source; 値:JSONGetElement ( $JSON2 ; “SourceFile” ) ]
フィールドを名前で設定 [ $table & “::filePosix”; $JGE_source ]
End If
レコード/検索条件/ページへ移動 [ 次の; 最後まできたら終了 ]
変数を設定 [ $c; 値:Get(レコード番号) ]
End Loop
#***** 以上で、指定テーブルに posix が入り、パスモドキが作られ、MediaDBと接続された
今、各レコードの該当フィールドにタンタバがそれぞれ入りましたので、次の個別処理に進みます。
ファイルの場合
ファイルの場合は、既存レコードが明確なので結果フィールドGの内容をまずそのまま既存レコードの該当フィールドに転記します。何も面倒はありません。
今、レコードの該当フィールドにタンタバが入りましたので、次の個別処理に進みます。
個別処理
レコードの該当フィールドにはタンタバが入っています。このJSONデータを分解してメインテーブルと繋がるメタテーブルに分配していきます。
まずはメタテーブルを開いて、そのレコードの既存のメタデータがあれば全部削除します。これから行うことは問答無用にメタデータを分配してレコードを追加する作業です。「既存との比較」のような面倒なことは放棄します。
そしてここ重要なことですが、ここから先は野蛮な方法を取っています。JSON関数を使いこなしてレコードに分配することが、作成者の実力では無理でした。
だからくどくど説明しません。やったことは、置換関数で { } やら [ ] を取り除いて改行テキストにして、一行ずつ「タグ名」と「値」を判断しながら新規レコードして追加していくという作業です。
もうちょっと作者に知恵が付いたら、JSON関数を使った賢いやり方で分解・分配していけると思いますがとりあえず。
ほぼ役に立ちませんが、v5のスクリプトはこんなのです。
#**************************************************
#レコードの ExifAll を meta テーブルにレコード化
# Subdata の ExifToolALL にファイル毎の JSON が入っている状態
# meta テーブルにレコードとして分配する
#**************************************************
#このスクリプトは、新規ウインドウで作業して閉じるので、ループに不向き。単発用として。
#———————————————-
# mediaDB の準備 – 変数
#———————————————-
変数を設定 [ $windowName; 値:Get(ウインドウスタイル) ]
#IDを保存
変数を設定 [ $ID; 値:mediaDB::ID ]
#type は “ExifTool”
変数を設定 [ $type; 値:”ExifTool” ]
#json
変数を設定 [ $j; 値:JSONFormatElements ( MediadbSubdata::ExifToolAll ) ]
#———————————————-
#ID と type で検索し、該当レコードを一旦全部削除
#———————————————-
スクリプト実行 [ 「新規miniウインドウをダイアログで開く」; 引数: “meta_Edit” ]
ウインドウタイトルの設定 [ ウインドウ: “meta_Edit”; 現在のファイル; 新規タイトル: “Get Exif” ]
レイアウト切り替え [ 「meta_Edit」 (media_meta) ]
#=============================================================================================
#ID と type で検索。該当を全部削除してから新たに始める
検索モードに切り替え [ ]
フィールド設定 [ media_meta::ID; $ID ]
フィールド設定 [ media_meta::type; $type ]
エラー処理 [ オン ]
検索実行 [ ]
If [ Get ( 対象レコード数 )>0 ]
対象レコード削除 [ ダイアログなし ]
End If
全レコードを表示
対象外のみを表示
#———————————————-
#開始
#———————————————-
#JSON をリストとして捉え、行ごとの処理。 $j
変数を設定 [ $list; 値:Let ( [ /* 1. sourceFile だけ何故か特別。パスがすでにあるので消してよい */ $j = JSONDeleteElement ( $j ; “SourceFile” ) ; /* 2. 一旦整形してから */ $j = JSONFormatElements ( $j ); /* 3. ” “、[ ]、タブ、カンマを取っ払う */ /* $j = Substitute ( $j ; [ “\”” ; “” ]; [ “[” ; “” ];[ “]” ; “” ];[ “,” ; “” ];[ Char(9) ; “” ] ) ; */ /* 3-2. ” “、[ ]、タブ、カンマを取っ払う – 修正 カンマを残す */ $j = Substitute ( $j ; [ “\”” ; “” ]; [ “[” ; “” ];[ “]” ; “” ];[ Char(9) ; “” ] ) ; /* 4. { } を取っ払う */ $j = Substitute ( $j ; [ “{” & ¶ ; “” ] ;[ ¶ & “}” ; “” ]) ; /* 5. sep を定義する */ $sep = ” : ” ]; $j ) ]
変数を設定 [ $vc; 値:ValueCount ( $list ) ]
変数を設定 [ $c; 値:0 ]
Loop
変数を設定 [ $c; 値:$c + 1 ]
Exit Loop If [ $c>$vc ]
変数を設定 [ $gv; 値:GetValue ( $list ; $c ) ]
#多分、行には次の二種類しかないので処理を分ける。
#1. $sep で終わる -> グループ名 -> グループ名を保持するだけ
#2. それ以外 -> sep を改行に変換、gv1 が key gv2 が value -> 新規レコードに展開
If [ Right ( $gv ; 3 ) = $sep ]
変数を設定 [ $group; 値:Substitute ( $gv ; $sep ; “” ) ]
Else
変数を設定 [ $kv; 値:Substitute ( $gv ; $sep ; ¶ ) ]
変数を設定 [ $k; 値:GetValue ( $kv ; 1 ) ]
変数を設定 [ $v; 値:GetValue ( $kv ; 2 ) ]
#最後のカンマを取り去る
変数を設定 [ $v; 値:If ( Right ( $v ; 1 ) = “,” ; Left ( $v ; Length ( $v )-1 ) ) ]
#新規レコード
If [ not IsEmpty ( $k ) and not IsEmpty ( $v ) ]
新規レコード/検索条件
フィールド設定 [ media_meta::ID; $ID ]
フィールド設定 [ media_meta::type; $type ]
フィールド設定 [ media_meta::group; $group ]
フィールド設定 [ media_meta::key; $k ]
フィールド設定 [ media_meta::value; $v ]
End If
End If
End Loop
スクリプト実行 [ 「個別処理 QT 0000 を削除」 ]
スクリプト実行 [ 「個別処理 Durasion の S 表記を整形」 ]
ウインドウを閉じる [ 名前: “Get Exif”; 現在のファイル ]
#=============================================================================================
興味ある方は V5 をダウンロードして「レコードの JSON を meta にレコード化」スクリプトを参照してください。
取得の話が長くなってしまいましたが、こうしてレコードと繋がったメタテーブルに ExifTool でゲットしたタグと値のリストが作られました。
結果を利用
さて、ExifTool のコマンドを実行させた結果を FileMaker で利用します。それって例えばどういうことですか。こういうことです。
そのまま閲覧する
メタテーブルにデータが網羅されますから、ポータル表示などを使って閲覧します。

編集する
もしかしたらタグの値を編集したいことがあるかもしれません。編集してはいけない・できないタグも多くありますが。これは、メタテーブルの編集であって、実際のファイルのメタデータを変更する行為ではありません。データベース上でのみでの変更です。
Tips
タグの値を編集したら、編集したことを記録させましょう。実作編 v5 では、更新スタンプとmeta_logというフィールドに記録し、見た目も変更させます。
フィールドに昇格
レコードのフィールドとして特定タグを昇格させます。例えば「DateTimeCreated」を「オリジナル作成日」フィールドに転記したり「MIMEType」を「MIMEType」フィールドに転記したりします。そんな必要があるのか。たまにあります。レコードが検索しやすくなったりソートのネタにできたりその他取り扱い的に有利になるからです。
フィールドに昇格させる方法
メタテーブルのレコードにある特定タグの値を、メインテーブルの特定フィールドに上手く転記する良い方法あるでしょうか。
良いかどうかわかりませんが、こうしました。ExifToolTagsテーブルを新規にこしらえて利用しました。またテーブル作ったんか。はい。
フィールドに昇格させるために取った方法が、Exif データをファイルに書き込む際にも役に立ちました。章を新たにしてご説明。
ExifToolTags テーブル
ExifToolでメタデータをゲットしてメタテーブルに網羅したわけですが、ある特定のタグに対して特別な措置を施したいことがあります。そういうとき、普通はタグ名を検索して絞り込んて処理したりします。でもそれってマジ面倒。融通も利かなくなるし。他の方法を探ります。
タグ名のカタログを作れば良い
タグ名カタログを作ればいいんじゃないかと思って、それを作りました。タグ名メインのテーブル、その名も ExifToolTags テーブルです。
メタデータのテーブルはすでにありますから、それを利用してまずはだーっと転記してタグ名カタログを完成させます。「値の例」なんかも転記しておくと便利。何なら「説明」フィールド作って説明をメモっておいたりします。
このテーブルはExifToolでゲット出来るタグのカタログと化します。metaテーブルに合わせて未登録タグがあれば自動で増えていく仕組みもつくりました。これだけでも結構使えるデータベースではないでしょうか。

さてお立ち会い。このタグカタログにさらにフィールドを付け足します。「FieldName」フィールドです。なんですかそれは。それはフィールド名を記すためのフィールドです。図で見ての通り、特定タグの fieldName フィールドには、何やらフルフィールド名が記されていますね。こうしておくことで、メインテーブルのフィールドとタグ名が密接な間柄になります。
メタデータをゲットするスクリプトのループの中で、タグ名と共に fieldName が存在していることを確認すれば、そのフィールドに値を転記することができます。
同じように、別途「fieldNameExport」というフィールドをこしらえまして、こちらはメタデータを埋め込むときに特定タグのみを指定することに利用できます。
メタデータを埋め込む
ExifTool オプション
-タグ名=
メタデータを埋め込むには、-タグ名= で値を書きます。コピーライトを埋め込むには、-Copyright=’© 2022 Digitalboo’ のように書けばいいですね。そのタグが既存にあれば上書き、なければ追加されます。コピーライトタグを消したいときは -Copyright= と、値を書かずにおきます。
-overwrite_original_in_place
もうひとつ良いオプションがあります。-overwrite_original_in_place です。これは、強制的上書きするのですが、基本のところを変更しないようにするオプションです。-overwrite_original では、ファイル変更日などがすべて変わってしまいますがそれを防げます。
IPTC の対処
大事な話がひとつありました。ExifTool で編集、追加は容易に出来ますが、言葉の壁によってダダハマりして大層苦労しました。Keywords など、一部のタグがどうしても文字化けするんです。苦労話は聞きたくないでしょうからすっ飛ばしますが、答えは、そのタグが IPTC タグであったからでした。IPTC は、素のままでは UTF=8 のキャラクタを認識できないのでした。特別にオプションを足します。それはこうです。
-iptc:all -codedcharacterset=utf8
ということで、それらを踏まえてターミナルでたたたっとやれば簡単ですが、これをメディアDBとどのように関連づけて操作体系を作るかです。
これが最高のやりかたやで。と、自信たっぷりには何も言えません。苦肉の策で現状どうやっているかだけ軽くご説明します。
CodeRun
簡単に言うと、CodeRun を操作します。CodeRun は特定のコードをテンプレ的に使用する仕組みですが、ちょっと特殊な使い方として、そのテンプレそのものを作り変えてしまいます。
CodeRun の基本のテンプレコードはこうしておきます。
この基本のテンプレコードの中に、編集したいタグを付け加えていきます。そういうスクリプトを作っておきます。はて。編集したいタグとは。それはレコード上で編集したタグです。
ということで、まずはメタテーブル上で変更されたタグを抽出します。これは meta_log フィールドを頼りに実現できます。他に、フィールドのいくつかがメタデータと関連づけられていますから、そのフィールドに変更があれば、そのタグも抽出します。
管理DBで変更があったタグをこうして抽出し、リスト化した後、工夫して CodeRun に書き込み、実行させます。
例えば、著作権絡みのメタデータを軒並み編集した後にスクリプトを発動するとこのようになります。

あとは CoddRun するだけで埋め込めますね。
なんという雑な説明。これは CodeRun について熟知していないとわけわからんですね。でも説明しだすとたいへんなので。すいません。実作編V5をダウンロードして、その中のCodeRun テーブルやスクリプトを直接覗いてみてください。

以上、メタデータの取得と利用 LEVEL 5 ExifTool 編をお送りしました。
このポストは以下の記事に関連しています。