FileMakerでメディア管理のデータベースを作っています。サムネイルを再生成するスクリプトの詳細。
下記ページの補足でもあります。
FileMakerメディア管理 作成と構築 R3 サムネイル
更新したのはサムネイル、強制的に再生成。対応できないタイプには代替アイコン用意して、おまけに動画のサムネイル、スクリンキャプチャで作ります。
サムネイルフィールド 問題と改善
サムネイルフィールドはシンプルな計算式だけを備えていました。格納で登録するとオブジェクトフィールドからGetThumbnail関数でサムネイルを作ります。また、フォルダからインポートするときはFileMakerのインポーターが自動でサムネイルを持ってきてくれます。
問題と要望
しかし、以下のような問題やそれに対する要望が発生していました。
- 参照で新規登録したときはサムネイルが作られず空だったり「?」になったりします。フォルダインポートでも、「更新」すると「?」になることがあります。
- サムネイルが空や「?」になっているレコードがあるので、「サムネイルを作成」ボタンで再生成したい。
- 動画や音声ファイルなど、GetThumbnail関数が対応していないファイルタイプではサムネイルが空で寂しいです。
これらの問題や要望に応えていこうじゃありませんか。
改善事項
改善した事柄は以下です。
- サムネイルを強制的に再生成するスクリプトを作った
- サムネイル未対応のファイルタイプ用にアイコンを表示する仕組みを作った
- 動画のポスターイメージを作成する仕組みを作った(Macのみ)
これらを実現するために、フィールドを追加したり値一覧を作ったりその他いろいろな技を用いました。
- 作業用のグローバルなオブジェクトフィールドを追加した
- 代替アイコンのグローバルフィールドとデータを用意した
- FM仕草のファイルタイプ分類フィールドを追加した
- ファイルタイプを判別するため値一覧を作成した
- 拡張子のフィールドを追加した
- 動画ポスターイメージ専用のオブジェクトフィールドを一つ追加した
- 動画の画面をキャプチャして保存できるようにした
サムネイルを何とかするためだけにいろいろとやったものです。
サムネイルを再生成するスクリプトを作る
サムネイルが「?」表示になってしまうケースがあります。参照で読み込んだオブジェクトを再度フォルダインポートして更新してしまったときなどに発生します。参照メディアから再度サムネイルを作ることはGetThumbnail関数では出来ないので、強引に作る必要があります。
仮のグローバルフィールドを用意して、パスを頼りに一旦格納で読み込み、サムネイルを作るという力技です。スクリプトの流れはこうです。
参照で登録しているレコードには参照元のFM形式のパスありますね。そのパスを元にグローバルオブジェクトフィールドに格納で挿入します。
格納で挿入するからGetThumbnail関数が効きます。計算でサムネイルを作り、サムネイルフィールドに記入します。グローバルオブジェクトフィールドはもう用が済んだので “” で空にします。
この件について FileMakerメディア管理 – 参照ファイルのサムネイルを作りたい にありますのでご参照ください。
これで参照ファイルのサムネイルを強制的に作ることが出来ます。簡単ですね。解決ですか。いいえ。今回それでは済みません。他の問題や要望に応えます。
最終的にサムネイルを再生成するスクリプトを完成させるのですが、今書いたこれはその中の一部です。まだスクリプトは完成しません。ここから長い旅が始まります。
非対応メディアの代替アイコン
FileMakerが対応している画像タイプなら関数でサムネイルを作れますが、動画やオーディオのファイルでは関数でサムネイルをゲットできません。
サムネが付かないときは、代替アイコンを表示させようではありませんか。それやってみます。
FileMaker対応のファイルタイプ
根本的な問題として、FileMakerで扱えるメディアファイルは少ないです。正直「メディア管理」と偉そうに言えるレベルのデータベースは元々作れません。
どういうファイルが扱えるか、FileMakerのヘルプ「オブジェクトフィールドのデータ操作」で確認することができます。
オブジェクトフィールドのデータ操作 ver.17
オブジェクトフィールドのデータ操作 ver.19
以下はv19のデータですが、これだけのファイルタイプしか扱えません。17ではPhotoshopのpsdすら扱えない酷い有様でした(FileMaker でメディア管理 ・・・ Photoshop .psd の扱い)
ファイルタイプ サポートされる形式タイプ ピクチャ Encapsulated Postscript (.eps)
GIF (.gif)
HEIF/HEIC (.heic) (macOS, iOS, iPadOS)
JPEG/JFIF (.jpg)
PDF (.pdf) (macOS)
Photoshop (.psd) (macOS)
PNG (.png)
TIFF (.tif)
Windows ビットマップ (.bmp)
Windows メタファイル/拡張メタファイル (.wmf/.emf) (Windows)オーディオ/ビデオ AIFF オーディオファイル (aif、.aiff)
AVI ムービー (.avi)
FLAC オーディオファイル (.flac) (iOS、iPadOS)
MP3 オーディオファイル (.mp3)
MPEG-4 オーディオファイル (.m4a)
MPEG-4 ムービー (.mp4)
MPEG ムービー (.mpg、.mpeg)
MPEG-4 ビデオファイル (.m4v)
QuickTime ムービー (.mov、.qt) (下記のメモを参照)
Sun オーディオファイル (.au)
WAVE オーディオファイル (wav)
Windows Media Audio (.wma)
Windows Media Video (.wmv)https://help.claris.com/ja/pro-help/content/data-in-container-fields.html?Highlight=オブジェクトフィールド
非対応のメディアファイルを登録するとアイコンの表示となります。
アイコンの表示は理にかなっているし、むしろ美しいです。しかしこのアイコンをサムネイルに転記することができません。転記せずそのまま表示すれば良いという話ですが、そこは堪えてください。
FMヘルプページを参考に分類を作る
ビデオファイル、オーディオファイル、非対応ファイルのデフォルトのアイコンを登録しておき、代替としてこれをサムネイルフィールドに表示させます。
これを実現するためには、あらかじめビデオなのかオーディオなのか非対応タイプなのかを分類してやる必要があります。分類に応じて代替画像をサムネイルフィールドに収めるんです。
レコードごとに分類をチクチク手で書いていくなんてご免ですね。自動で分類させたいので仕込みましょう。良い方法があります。値一覧を使うんです。
上記FileMakerのヘルプページに扱えるファイルタイプが載っています。幸い、というと変ですが、幸い扱えるタイプが少なすぎて手動でリストを作るのも苦痛になりません。
値一覧で「FMvideo」と「FMaudio」と「FMpicture」を作成し、ヘルプページに載っているタイプ(拡張子)をリストします。ヘルプページではオーディオとビデオが一緒くたに書いてありますが、これはさすがに分けたいです。目視と手動で振り分けましょう。
分類の値一覧ができました。この値一覧はあとでフィルタにも使えますよ。それはともかく、レコードに新たな分類のためのフィールドを作りましょう。そうですね、名付けて FMType です。
フィールド FMType の計算式
今こうしてフィールドを作りましたのでまだ何も入力されていません。オプションに計算式を書きましょう。いっそ、フィールドタイプを計算式にしますか。そのほうが楽できますね。
計算式の流れはこうなります。
1 まず登録しているメディアの拡張子を取得します。
2 次に、その拡張子が値一覧のどのFM分類に含まれるか調べ、合致する値一覧に分類の値を与えます。例えば拡張子が jpg なら値一覧「FMpicture」に含まれるから「picture」という値にするというようなことを決めておくわけです。「FMvideo」に含まれていれば「video」とかね。
ちょいと面倒なだけで、式自体は簡単に作れそうに思えます。でもここからまだ困難は続くのです。
拡張子フィールドと拡張子をゲットする計算式
拡張子を抜き出すのは簡単なことですか?みんなはそうかもしれません。私にとっては難解でした。それで、計算式で拡張子をゲットして格納する拡張子フィールドを作りました。
「何を大袈裟な。パスかファイル名を「.」で改行して最後の行が拡張子じゃねえかよ」いいえそんな簡単な話じゃありません。例えばこんなファイル名がうちのハードディスクにたんまりあります。「画像.貼り付け用」「レリーフ図面.1991.04.22」「ホテル清掃マン.psd.copy1」「追加プロジェクト」「さぼてん坊やの血塗られた生涯」どうしますか。これ。
この話だけで記事一本分の内容ありますが省略します。結論として、以下の計算式によって拡張子フィールドに拡張子が入りました。まだパーフェクトとは言えませんがこれ以上は無理。
// パスまたはファイル名から拡張子を抽出 Let ( [ txt = FimeNameOrPath ; ext = GetValue ( Substitute ( txt ; "." ; ¶ ) ; ValueCount ( Substitute ( txt ; "." ; ¶ ) ) ) ; F = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQUSTUVWYZ" ; Ft = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQUSTUVWYZ" ; Fn = "1234567890" ; extF = Filter ( ext ; F ); extN = Filter ( ext ; Fn ); extL1 = Filter ( Left ( ext ; 1 ) ; Ft ) ; ext = If ( ext = extF ; ext ; "" ); ext = If ( ext > extN ; ext ; "" ); ext = If ( not IsEmpty ( extL1 ) ; ext ;"" ) ]; ext )
※ 太字の FimeNameOrPath にパスまたはファイル名をあてがいます。
拡張子フィールドに拡張子がはいりましたので、値一覧で作ったFM分類を利用して、そのレコードのFMTypeを計算で導き出し、分類します。
FMType 計算式
FMType フィールドの計算式です。拡張子と分類リストを比較して該当があればFMTypeとして分類します。
Let ( [ ext = MediaDB::拡張子 ; picture = ValueListItems ( Get(ファイル名) ; "FMpicture" ) ; audio = ValueListItems ( Get(ファイル名) ; "FMaudio" ) ; video = ValueListItems ( Get(ファイル名) ; "FMvideo" ) ; FMT = Case ( not IsEmpty ( FilterValues ( picture ; ext ) ) ; "picture" ; not IsEmpty ( FilterValues ( audio ; ext ) ) ; "audio" ; not IsEmpty ( FilterValues ( video ; ext ) ) ; "video" ; ext = "pdf" or ext = "PDF" ; "pdf" ; "other" ) ]; FMT )
太字が分類の値になります。これにて、レコードに FMType が記入され分類されました。やっと次に行けます。
代替アイコンを作る
サムネイルが作成できるのは FMType が”picture”のメディアだけです。それ以外の、video と audio そしてその他 other の代替アイコンを作りましょう。
グローバルなオブジェクトフィールドを三つ作り、代替画像を登録します。オーディオ、ビデオ、その他アイコンをドラッグで登録しました。ドラッグでオブジェクトを挿入するとファイル内部の見えないところにコピーされます。
とりあえず三つ作りましたが、この作業が楽しければもっと増やしてもいいです。あと、これは後々未来の話ですが、代替アイコンのアイデアを発展させると「ファイルタイプと代表アイコン」のデータベースを作りたくなります。そして実際にそれを作りました。その話は今は避けておきます。未来の話です。
未対応タイプのサムネイルに代替アイコンを記入する
レコードに拡張子が入り、FMType が分類され、未対応アイコンがグローバルに登録されました。あとは、サムネイルフィールドに計算でぶち込むか、スクリプトで計算して結果をサムネイルフィールドにぶち込みます。
if より case で進めた方がいいかもしれませんね。FMType が picture なら GetThumbnail でサムネイルを作ります。これを最後に書きます。後は上に「other なら other アイコン、video なら video アイコン、audio なら audio アイコン」というように指定するだけ。
この計算式をサムネイルフィールドのオプションに設置しておきます。ただし「フィールドに既存の値が存在する場合は置き換えない」にチェックを入れておきます。
サムネイル再生成をスクリプトで行うので、フィールド定義の計算式が優先されないようにするんです。
今このポストでいろいろとフィールド定義の中に計算式を仕込んでいますが、将来的にはフィールド定義の計算式をなるべく使わないように再定義していくことになります。スクリプトからそれを行います。理由はいろいろありますが、フィールド定義でごちゃごちゃ計算させるより、スクリプトで外部からビシっと指示したほうがいいんですよ。以前は逆に思ってました。今は「フィールド定義はなるべくフラットが良い」と思ってるんです。ただ思ってるだけです。
で、その計算式は具体的に以下のようになりました。
Case ( GetAsBoolean ( MediaDB::posterImage ) = 1 ; GetThumbnail ( MediaDB::posterImage ; 150 ; 150 ) ; MediaDB::FMType = "other" ; GetThumbnail ( 設定::GLB_icon_def ; 100 ; 100 ); MediaDB::FMType = "video" ; GetThumbnail ( 設定::GLB_icon_video ; 100 ; 100 ); MediaDB::FMType = "audio" ; GetThumbnail ( 設定::GLB_icon_audio ; 100 ; 100 ); GetThumbnail ( MediaDB::オブジェクト ; 150 ; 150) )
こうして「サムネイル」という名のフィールドは小さな画像またはアイコンを格納する役割を得ました。
・・・ところで、上記計算式、case の最初の2行を見ると GetAsBoolean ( MediaDB::posterImage ) を使った聞き捨てならない式があります。それ何やねん。そんなの今まで出てきてないやろ。なめとんのか。はい。よく見つけましたね。次はそれについてです。
ここまでで、とりあえずサムネイル未対応のファイルにアイコンが付きましたので目的は達しています。けど欲が出てきているんです。
ビデオファイルの画像をキャプチャして保存してサムネイルに使ってしまおうという魂胆です。
ビデオファイルのポスター画像を撮影する(Mac)
上記Caseの計算式にこういうのがあります。
GetAsBoolean ( MediaDB::posterImage ) = 1 ;GetThumbnail ( MediaDB::posterImage ; 150 ; 150 ) ;
これ何でしょう。GetAsBoolean 関数は、オブジェクトがあるかないかをチェックできる関数です。posterImage というフィールドにオブジェクトがあればそれのサムネイルを作るという一文です。posterImage というオブジェクトフィールドを作ったんですよ。ビデオファイルのキャプチャ画像を収めるためのフィールドです。ここに、ビデオのポスターアートを入れるんです。
ビデオのスクリーンキャプチャを格納するオブジェクトフィールド
ビデオファイルはサムネイルを作れません。そこで、OSのスクリーンショット機能を使って無理矢理に作ってしまおうという仕組みです。OSの機能を使うので、多分これはMac専用です。AppleScriptに匹敵するWindowsの機能を知らないのですいません。というかそれ以前にWindowsは弥生の使い方以外何もも知らないので。
この件はすでに書いていますのでリンクしておきます。が、このリンクは見なくていいです。別のもっとスマートなやり方に変更したからです。それを今からここに書きます。
ビデオのキャプチャを撮ってフィールドに保存するスクリプト
ビデオをプレビューしながらここぞというところでキャプチャし、posterImage フィールドに格納します。同時にサムネイルを作成します。
理屈を説明し始めるとえらいことになるので(このブログ内に関連の話題がいっぱいあります)省略しながら行きます。
まずビデオをプレビューする必要があります。プレビューレイアウトはすでにありますので、そのレイアウト上で動かすことを前提としましょう。
プレビューレイアウトの見えないところに posterImage フィールドを置きます。見えなくても良いので必ず配置します。理由は、ペーストするからです。
ボタンを設置し、これから説明するビデオキャプチャのスクリプトをセットします。ビデオをプレビューしながら、ボタンクリックすると、カーソルが十字の形になって撮影準備状態となりますから、ドラッグして範囲を指定します。範囲指定は自動化させていません(いずれするかも)今は自分で画面を括ります。
そうすると画面がposterImage にキャプチャされ、サムネイルが作成されます。この仕組みは素晴らしいですね。自画自賛。
スクリーンキャプチャのスクリプト
ではそのスクリプトを紹介します。
- posterImage フィールドに「準備中」とテキストを入れます。
- 変数 $check に posterImage の内容を保存します。値は「準備中」です。
- posterImage の内容をコピーします。「準備中」がコピーされました。
- 「AppleScriptを実行」スクリプトステップを実行します。実行する AppleScript の内容はこうです。
-
"do shell script \"screencapture -c -i &\""
- do shell script を使ってAppleScriptからコマンド screencapture を送り込んでいます。オプションの -C は結果をコピー、 -i は選択範囲を指示です。
-
- コマンドが実行され、カーソルが十字に変わり、ユーザーが範囲を選択するのを待つモードになります。ドラッグして選択範囲を決定させると、スクリーンキャプチャがクリップボードに収まります。
- さてこちらFileMakerでは、コピーされたスクリーンキャプチャを posterImage フィールドにペーストします。
- ループを回しながら、ひたすら posterImage フィールドにペーストし続けます。もしスクリーンキャプチャがまだ撮られておらずクリップボードが「準備中」のままなら、ループで延々「準備中」とペーストし続けます。
-
- キャプチャがクリップボードに収まった次の瞬間から、クリップボードの内容が「準備中」からキャプチャ画像に変化し、キャプチャ画像がペーストされます。
- ループを抜ける条件は posterImage ≠ $check、つまりフィールド内容が「準備中」ではなくなることです。ループを20回くらい回してもずっと「準備中」をペーストし続けているとすれば、何らかのエラーが考えられるのでスクリプトを中止します。
-
- 無事ループから抜けたらGetThumbnail 関数でサムネイルを作ります。
以上です。このスクリプトには熱血デジタル部 Digitalboo の数年間に及ぶ血と汗とど根性がぎゅーっと込められています。
サムネイルを生成するスクリプト完結編
ここまでおつかれさまでした。サムネイルを生成するスクリプトをやっと完成させることができます。これまで書いてきたことをまとめ、スクリプトはこうなりました。
- FMType がサムネイル作成に対応していて、かつ、参照登録なら、仮フィールドに格納してサムネ作成してポイ
- FMType がサムネイル作成に対応していて、かつ、格納登録なら、普通にオブジェクトからサムネ作成
- FMType がサムネイル作成に対応していないが、posterImage に画像があればそれからサムネ作成
- FMType がサムネイル作成に対応していないその他、audio なら代替オーディオアイコン、videoなら代替ビデオアイコン、それ以外はデフォルト代替アイコンをサムネイルに設置
以上が流れです。この簡単な条件に各々を合致させるため、絶大な労力をつぎ込みました。
後には「サムネイルがエラーを起こしている」レコードを見つけ出すための工夫も入れました。例えばGetAsText とか GetAsBoolean のフィールドを作って、空であったり「?」になってしまっているレコードを特定します。サムネイル生成のスクリプトのループ版を作って対象レコード一斉修理とか、そういうことが実現します。
あとがき
サムネイルの話だけで延々なにをやっとんの。という話ですが。このポストと実作ファイル、更新前の最初の頃は、サムネイルの話に加えて「インポートの仕組み」にまで手を出していました。
元のポストでは以下まだその話を続けていましたが、ばっさり削除しておきます。詰め込みすぎです。インポートの話は壮大すぎて大変なことになりますから、こんなところで片足突っ込んでいる場合ではありません。いずれ、別のところでじっくりと。
リセットしてリスタートしてリメイクしてリブートした実作編改め作成と構築、総合案内のインデックスができました。