Archive for the 'NSMetadataQuery' Category

2017/03/15 Spotlightで指定フォルダ以下の指定文字を含むファイル一覧を取得

Cocoaの機能を呼び出すAppleScriptObjCでは、さまざまなCocoaのAPIを呼び出す手法が模索されてきました。その中でもSpotlight検索は「割とまだ決定版の手法が確立していない」ものでありました。

spotlightでタグを指定して検索
http://piyocast.com/as/archives/3731

ASOCでmdfindするじっけん v4(フルパスを返す)
http://piyocast.com/as/archives/4122

などなど、徐々に進化してきて、Shane StanleyのAppleScript Libraries「BridgePlus」にSpotlightの呼び出し命令が実装されたりと、手段が洗練されてきたという経緯があります。

書籍「AppleScript 10大最新技術」にも掲載していますが、その時点のバージョンよりもはるかにこなれた書き方がML上でShane Stanleyから提示されました。いままでよりもはるかに短いので、ちょっと腰を抜かしてしまったほど。しかも、とてもさりげなく、、、

「ああ、こういう書き方でもいいんだ、、、」

と、かなりすごいことになっています。

AppleScript名:Spotlightで指定フォルダ以下の指定文字を含むファイル一覧を取得
– Created 2017-03-15 By Shane Stanley
use AppleScript version “2.4″ – Yosemite (10.10) or later
use framework “Foundation”
use scripting additions
–http://piyocast.com/as/archives/4528

set thePath to POSIX path of (path to desktop) – whatever

–ファイル名で検索
set theKeyword to “スクリーンショット”
set fResList to my searchPath:thePath searchPredicate:“(kMDItemFSName CONTAINS [c]%@)” predicateArgs:{theKeyword}
–>  {”/Users/me/Desktop/2013/03/スクリーンショット 2016-12-15 8.44.28.png”, “/Users/me/Desktop/FromDesktop/samples/list splitted or broken/スクリーンショット 2015-08-31 10.49.30.png”, …… }

–UTI(File Kind)で検索
set theKeyword to “public.png”
set fResList to my searchPath:thePath searchPredicate:“(kMDItemContentType == [c]%@)” predicateArgs:{theKeyword}
–> {”/Users/me/Desktop/2013/03/スクリーンショット 2016-12-15 8.44.28.png”, “/Users/me/Desktop/FromDesktop/IMG_3143_resized.png”, “/Users/me/Desktop/FromDesktop/asoctable.png”….}

–ファイルの内容で検索
set fResList to my searchPath:thePath searchPredicate:“(kMDItemTextContent == [c]%@)” predicateArgs:{“戦場の絆”}
–>  {”/Users/me/Desktop/2013/01/gundam_games_history.txt”, “/Users/me/Desktop/book1_2.0.pdf”}

on searchPath:thePath searchPredicate:predString predicateArgs:argList
  set thePred to current application’s NSPredicate’s predicateWithFormat:predString argumentArray:argList
  
set targetURL to current application’s |NSURL|’s fileURLWithPath:thePath
  
set theQuery to current application’s NSMetadataQuery’s new()
  
  
theQuery’s setPredicate:thePred
  
theQuery’s setSearchScopes:{targetURL}
  
theQuery’s startQuery()
  
  
repeat while theQuery’s isGathering() as boolean
    delay “0.001″ as real
  end repeat
  
theQuery’s stopQuery()
  
  
set theCount to theQuery’s resultCount()
  
set theResults to current application’s NSMutableArray’s array()
  
  
repeat with i from 1 to theCount
    set aResult to (theQuery’s resultAtIndex:(i - 1))
    
set thePath to (aResult’s valueForAttribute:(current application’s NSMetadataItemPathKey))
    (
theResults’s addObject:thePath)
  end repeat
  
  
return (theResults’s sortedArrayUsingSelector:“compare:”) as list
end searchPath:searchPredicate:predicateArgs:

★Click Here to Open This Script 

2016/06/18 指定フォルダ以下にあるMacDownとPages書類をソートしてPDFに書き出す

指定フォルダ以下にあるMacDownで記述したMarkdown書類と、Pages書類をすべての階層からピックアップしてファイル名でソートして、すべてデスクトップにPDFで書き出すAppleScriptです。

コンパイル(構文確認)および実行に際しては、Shane StanleyのScript Library「Bridge Plus」をインストールしておく必要があります。また、GUI Scriptingを利用しているため「システム環境設定」>「セキュリティとプライバシー」>「プライバシー」>「アクセシビリティ」でスクリプトエディタ(アプレットとして実行する場合にはアプレットそのもの)を登録して許可しておく必要があります。

「技術書典」に出す電子ブックのフォーマットがギリギリまで決まらず、しかも縦長のスクロールさせるタイプのものにできないかとあがいていたのですが、結局iPadあたりで読むことを考慮するとiPadのリーダーの仕様にしたがう必要があります。

……あれ?(^ー^; 結局、ページめくりは発生するし、一般的な本と同じような体裁になってしまいますよ → フォーマットがPDFになりました。

Markdown書類とPages書類が混在しているフォルダ構造のトップ階層のフォルダを指定すると、Markdown書類とPages書類をピックアップし、ファイル名でそれらをソートし、順次PDFに書き出すAppleScriptを書いてみました(必要は発明のマザー!)。

book1.png

ただ、MacDownには「書類をPDFに書き出す」という機能がAppleScript側に公開されていません(T_T)。

macdown_dict.png

ないものを「ないない」と嘆いても仕方がないので、さっさとGUI Scriptingで強制的にメニュー操作することにしました。

macdown_gui.png

で、どこに? どこに保存させるのでしょう??

大丈夫! そんなときには、保存ダイアログで幾つかのフォルダに強制的に移動させるキーボードショートカットが存在しており、Command-Dは「カレントディレクトリをデスクトップに移動させる」=「保存先をデスクトップにする」働きをします。

このため、保存先を操作しづらい(不可能とはいいませんけれども)GUI Scriptingにおいて保存先を指定することが、デスクトップフォルダについては可能になっています。

Pagesの方はひじょうに素直に(GUI Scriptingなんて使わずに)PDF書き出しが可能です。

そんなわけで、時間に追い詰められながらもなんとか大量のデータ処理を行っているのでありました。書き出した大量のPDFもAppleScriptでさくっと連結できるので、非常にいい感じです。あとは、本が完成すれば、、、、

AppleScript名:指定フォルダ以下にあるMDとPagesをソートしてPDFに書き出す
– Created 2016-06-17 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”
–use spotLight : script “spotlightLib”

property searchRes : {}

load framework

set origPath to POSIX path of (choose folder with prompt “Markdown/Pages ファイルの入っているフォルダを選択”)

set aResList to (spotlightSearch(origPath, “kMDItemKind == ’Markdown’ || kMDItemKind == ’Pages 一般書類’”) of me) as list –Caution! this parameter is *localized*

–フルパスとファイル名のペアの2D Listを作成
set newList to {}
repeat with i in aResList
  set j to contents of i
  
set aStr to (current application’s NSString’s stringWithString:j)
  
set aFileName to aStr’s lastPathComponent()
  
set the end of newList to {aStr, aFileName}
end repeat

–番号順にソート
set sortIndexes to {1} –Key Item id: begin from 0, Sort by filename
set sortOrders to {true}
set sortTypes to {“compare:”}
set resList to (current application’s SMSForder’s subarraysIn:(newList) sortedByIndexes:sortIndexes ascending:sortOrders sortTypes:sortTypes |error|:(missing value)) as list
if resList = {} then return –No Result

–ソートした順番にMarkdownファイル/Pages書類ファイルをオープンしてデスクトップにPDF生成してクローズ
repeat with i in newList
  copy i to {fullPath, aFileName}
  
  
set apFile to (POSIX file (fullPath as string))
  
set anAlias to apFile as alias
  
set aFileName to aFileName as string
  
  
if aFileName ends with “.md” then
    exportFromMacDown(anAlias) of me –Markdown
  else if aFileName ends with “.pages” then
    exportFromPages(anAlias) of me –Pages
  end if
end repeat

–指定のPagesファイル(alias)をデスクトップ上にPDFで書き出し
on exportFromPages(anAlias)
  tell application “Finder”
    set aName to name of anAlias
  end tell
  
  
set dtPath to (path to desktop) as string
  
set outPath to dtPath & aName & “.pdf”
  
  
tell application “Pages”
    close every document without saving
    
open anAlias
    
export document 1 to file outPath as PDF with properties {image quality:Best}
    
close every document without saving
  end tell
end exportFromPages

–指定のMacDownファイル(alias)をデスクトップ上にPDFで書き出し
on exportFromMacDown(anAlias)
  tell application “MacDown”
    open {anAlias}
  end tell
  
  
tell current application
    delay 1 –ここの時間待ちが少ないと画像抜けが発生?
  end tell
  
macDownForceSave() of me
  
  
tell application “MacDown”
    close every document without saving
  end tell
end exportFromMacDown

–注意!! ここでGUI Scriptingを使用。バージョンが変わったときにメニュー階層などの変更があったら書き換え
on macDownForceSave()
  activate application “MacDown”
  
tell application “System Events”
    tell process “MacDown”
      – File > Export > PDF
      
click menu item 2 of menu 1 of menu item 14 of menu 1 of menu bar item 3 of menu bar 1
      
      
–Go to Desktop Folder
      
keystroke “d” using {command down}
      
      
–Save Button on Sheet
      
click button 1 of sheet 1 of window 1
    end tell
  end tell
end macDownForceSave

–Spotlight Libの内容を引っ張り出してきた
on spotlightSearch(origPOSIXpath, aCondition)
  
  
set searchRes to {} –initialize
  
  
initiateSearchForFullPath(aCondition, origPOSIXpath) –Predicate & Scope Directory
  
  
–Waiting for the result
  
repeat while searchRes = {}
    current application’s NSThread’s sleepForTimeInterval:(“0.001″ as real) –delay 0.001
  end repeat
  
  
set anObj to searchRes’s firstObject() –Pick up the first one for test
  
if anObj = missing value then return {} –No Result
  
  
–set anAttrList to anObj’s attributes() –”mdls” attributes
  
–>  (NSArray) {”kMDItemContentTypeTree”, “kMDItemContentType”, “kMDItemPhysicalSize”, …}
  
  
set resArray to {}
  
repeat with anItem in my searchRes
    set j to contents of anItem
    
set aPath to (j’s valueForAttribute:“kMDItemPath”) as string
    
set the end of resArray to aPath
  end repeat
  
  
return resArray
end spotlightSearch

on initiateSearchForFullPath(aQueryStrings, origPath)
  
  
set aSearch to current application’s NSMetadataQuery’s alloc()’s init()
  
  
current application’s NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:“queryDidUpdate:” |name|:(current application’s NSMetadataQueryDidUpdateNotification) object:aSearch
  
current application’s NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:“initalGatherComplete:” |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:aSearch
  
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aQueryStrings
  
aSearch’s setPredicate:aPredicate
  
  
set aScope to current application’s NSArray’s arrayWithObjects:{origPath}
  
aSearch’s setSearchScopes:aScope
  
  
set sortKeys to current application’s NSSortDescriptor’s sortDescriptorWithKey:“kMDItemFSName” ascending:true
  
aSearch’s setSortDescriptors:(current application’s NSArray’s arrayWithObject:sortKeys)
  
  
aSearch’s startQuery()
  
end initiateSearchForFullPath

on queryDidUpdate:sender
  –  
end queryDidUpdate:

on initalGatherComplete:sender
  set anObject to sender’s object
  
anObject’s stopQuery()
  
current application’s NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(current application’s NSMetadataQueryDidUpdateNotification) object:anObject
  
current application’s NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:anObject
  
set my searchRes to anObject’s results()
end initalGatherComplete:

★Click Here to Open This Script 

2016/06/17 ASOCでmdfindするじっけん v4(フルパスを返す)

Spotlightの機能をCocoa経由で呼び出してmdfindコマンドのクエリーと検索対象フォルダをPOSIX pathで与えると、条件に合致するファイルのPOSIX pathをリストに入れて返してくるAppleScriptです。

以前に掲載したバージョンを実際に使おうとしたら、激しく間違っていることを発見。なんで気がつかなかったんでしょう。いえ、なんでこんな忙しいときに気づいてしまうんでしょう。

AppleScript名:ASOCでmdfindするじっけん v4(フルパスを返す)
– Created 2016-06-17 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

property searchRes : {}

set origPath to POSIX path of (choose folder)
set aList to spotlightSearch(origPath, “kMDItemContentType == ’net.daringfireball.markdown’”) of me

on spotlightSearch(origPath, aMDfindParam)
  
  
set my searchRes to {} –initialize
  
initiateSearchForFullPath(aMDfindParam, origPath) –Predicate & Scope Directory
  
  
–Waiting for the result
  
repeat while my searchRes = {}
    delay 0.01
  end repeat
  
  
set anObj to my searchRes’s firstObject() –Pick up the first one for test
  
if anObj = missing value then return {} –No Result
  
  
set resArray to {}
  
repeat with anItem in my searchRes
    set j to contents of anItem
    
set aPath to (j’s valueForAttribute:“kMDItemPath”) as string
    
set the end of resArray to aPath
  end repeat
  
  
return resArray
  
end spotlightSearch

on initiateSearchForFullPath(aQueryStrings, origPath)
  
  
set aSearch to current application’s NSMetadataQuery’s alloc()’s init()
  
  
current application’s NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:“queryDidUpdate:” |name|:(current application’s NSMetadataQueryDidUpdateNotification) object:aSearch
  
current application’s NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:“initalGatherComplete:” |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:aSearch
  
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aQueryStrings
  
aSearch’s setPredicate:aPredicate
  
  
set aScope to current application’s NSArray’s arrayWithObjects:{origPath}
  
aSearch’s setSearchScopes:aScope
  
  
set sortKeys to current application’s NSSortDescriptor’s sortDescriptorWithKey:“kMDItemFSName” ascending:true
  
aSearch’s setSortDescriptors:(current application’s NSArray’s arrayWithObject:sortKeys)
  
  
aSearch’s startQuery()
  
end initiateSearchForFullPath

on queryDidUpdate:sender
  log sender
  
–> (NSConcreteNotification) NSConcreteNotification 0×7fa618450820 {name = NSMetadataQueryDidFinishGatheringNotification; object = }
end queryDidUpdate:

on initalGatherComplete:sender
  set anObject to sender’s object
  
anObject’s stopQuery()
  
current application’s NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(current application’s NSMetadataQueryDidUpdateNotification) object:anObject
  
current application’s NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:anObject
  
  
set my searchRes to anObject’s results()
end initalGatherComplete:

★Click Here to Open This Script 

2015/09/25 spotlightでタグを指定して検索

Cocoaの機能を用いてSpotlight検索で指定フォルダ以下の指定タグのついているファイルを検索するAppleScriptです。

Shane StanleyがAppleScript Users MLに投稿したもので、Spotlightの検索待ち処理でプロパティをループで監視するやり方が採用されていますが、delayの秒数に0.01と指定して済ますあたりが書きこなれています。

実際0.1秒以下の数値指定は無効ではあるものの、delayを使わないとうまく動きません。動いているし、この書き方でエラーを回避できているので、これでいいんでしょう。

複数のPOSIX pathおよび定数で指定されるフォルダをリストにして与え、一括検索を行っているあたりもみどころです。

AppleScript名:spotlightでタグを指定して検索
– Created 2015-09-25 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

property searchIsDone : false

set aRes to my searchInListOfPaths:{POSIX path of (path to home folder)} forTags:{“レッド”}
–>  {{kMDItemPath:”/Users/me/Xxxxxxxxx/XxxxxXxxxxx/XX出撃回数集計(xxxxx)/XXXX_9xxxx_x9(XXXX_XXXX統合版) _9999.9.9XX変更対応.scptd”, kMDItemUserTags:{”レッド”}}, {kMDItemPath:”/Users/me/Xxxxxxxxx/XxxxxXxxxxx/XxxXxxx上のリプレイのダウンロード(alive)/指定IDの戦場の絆のムービーをOffLiberty経由でローカルにダウンロード v3.scpt”, kMDItemUserTags:{”レッド”}}, {kMDItemPath:”/Users/me/Xxxxxxxxx/XxxxxXxxxxx/クリップボードの内容をRTFとPDFとHTMLで書き出す v2.scptd”, kMDItemUserTags:{”レッド”}},…….}

(* scope can be a list of POSIX paths and/or the following enums:
NSMetadataQueryUserHomeScope
NSMetadataQueryLocalComputerScope
NSMetadataQueryNetworkScope
NSMetadataQueryUbiquitousDocumentsScope
NSMetadataQueryUbiquitousDataScope
NSMetadataQueryAccessibleUbiquitousExternalDocumentsScope
NSMetadataQueryIndexedLocalComputerScope — 10.10 or later
NSMetadataQueryIndexedNetworkScope

Eg:
searchInListOfPaths:{current application’s NSMetadataQueryLocalComputerScope} forTags:tagList
*)

–スコープリストの範囲のフォルダから、指定タグを含むファイルをすべてリストアップ
on searchInListOfPaths:scopeList forTags:tagList – pass empty list for any tags
  
  
– Build the search predicate
  
set tagListCount to count of tagList
  
if tagListCount = 0 then – any tags
    set pred to current application’s NSPredicate’s predicateWithFormat:(“kMDItemUserTags LIKE ’*’”)
  else if tagListCount = 1 then
    set pred to current application’s NSPredicate’s predicateWithFormat:“kMDItemUserTags CONTAINS[c] %@” argumentArray:tagList
  else
    set predList to {}
    
repeat with aTag in tagList
      set end of predList to (current application’s NSPredicate’s predicateWithFormat:“kMDItemUserTags CONTAINS[c] %@” argumentArray:{aTag})
    end repeat
    
set pred to current application’s NSCompoundPredicate’s andPredicateWithSubpredicates:predList
  end if
  
  
– make the query
  
set theQuery to current application’s NSMetadataQuery’s alloc()’s init()
  
theQuery’s setPredicate:pred
  
  
– set its scope
  
theQuery’s setSearchScopes:scopeList
  
  
– tell the notification center to tell us when it’s done
  
set notifCenter to current application’s NSNotificationCenter’s defaultCenter()
  
notifCenter’s addObserver:me selector:“searchDone:” |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:theQuery
  
  
– start searching
  
set my searchIsDone to false
  
theQuery’s startQuery()
  
  
– loop until it’s done
  
repeat
    delay 0.01
    
if searchIsDone then exit repeat
  end repeat
  
  
– get results
  
theQuery’s stopQuery()
  
set theCount to theQuery’s resultCount()
  
set resultsArray to current application’s NSMutableArray’s array() – to hold results
  
repeat with i from 1 to theCount
    (resultsArray’s addObject:((theQuery’s resultAtIndex:(i - 1))’s valuesForAttributes:{current application’s NSMetadataItemPathKey, “kMDItemUserTags”}))
  end repeat
  
  
return resultsArray as list – will be list of records
  
end searchInListOfPaths:forTags:

on searchDone:notification
  set my searchIsDone to true
end searchDone:

★Click Here to Open This Script 

2015/09/05 ASOCでspotlight検索するじっけん v2

Cocoaの機能を用いてspotlight検索を行うAppleScriptです。Script Editor上でControl-Command-RでForeground実行する必要があります。

前バージョンでは、ファイル名しか取得できていませんでしたが、検索でヒットしたアイテムのフルパスを返すようにできたので、ようやくこれで実用レベルに達したと思います。

(1)検索対象フォルダを指定
(2)検索条件を設定

すると、実際にspotlight検索を行って結果を返します。

検索結果待ちループ側でdelayを極小にできるよう、試行錯誤してみましたが、

(1)delayを削除するとうまく動かない
(2)delay 0.01のとおりに本当に0.01秒待っているかはわからない
(3)手元の環境ではうまく動いているが、環境に応じたチューニングが必要?(2台で確認)

という印象を持っています。

ASOCで0.001という数値をプログラム内に記述するのにえっらい苦労しました。そのまま書くと指数表現されてしまい、Pure AppleScriptの世界ではエラーにならないんですが、Cocoaのオブジェクトに渡されるときにうまくブリッジされないのか、エラーに。NSSNumberでfloatValueを設定しようとしてもうまく行かないわで、結局、文字列で書いて強制的にcastするという強引な方法で解決。もっといい方法があるとよいのですが、相当苦労しました。

いろいろ条件を変えて検索の実験をしたところ、与えた検索条件でヒットがない場合には早々に{}を返してくるため、無限ループで処理が終わらないということはないようです。

これでもう、spotlight検索のためにshell commandのmdfindを呼び出す必要はなくなりました。

AppleScript Users MLの過去ログや、ShaneのEveryday AppleScriptObjC添付のサンプルコード、ASObjC Explorer 4添付のサンプルコードなども探し回ってspotlight検索のサンプルを探してみましたが、検索結果のフルパスを返すものはなかったので(ついでに言うとObjective-Cのプログラムを検索エンジンで探しても、けっこう見つからない)、これを書いておいた意義はあるものと思います。

AppleScript名:ASOCでmdfindするじっけん v2(フルパスを返す)
– Created 2015-09-03 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

property searchRes : {}

set searchRes to {} –initialize

set origPath to POSIX path of (choose folder)
initiateSearchForFullPath(“kMDItemDisplayName == ’ICON0.PNG’”, origPath) –Predicate & Scope Directory

–Waiting for the result
repeat while searchRes = {}
  current application’s NSThread’s sleepForTimeInterval:(“0.001″ as real) –delay 0.001
end repeat

set anObj to searchRes’s firstObject() –Pick up the first one for test
if anObj = missing value then return {} –No Result

–set anAttrList to anObj’s attributes() –”mdls” attributes
–>  (NSArray) {”kMDItemContentTypeTree”, “kMDItemContentType”, “kMDItemPhysicalSize”, …}

set resArray to current application’s NSMutableArray’s alloc()’s init()
repeat with anItem in searchRes
  (resArray’s addObject:(anObj’s valueForAttribute:“kMDItemPath”))
  
–>  (NSString) “/Users/me/Documents/機動戦士ガンダム戦場の絆 Folder/ULJS00181USERID_0000/ICON0.PNG”
end repeat

resArray
–>  (NSArray) {”/Users/me/Documents/機動戦士ガンダム戦場の絆 Folder/ULJS00181USERID_0000/ICON0.PNG”,……}

on initiateSearchForFullPath(aQueryStrings, origPath)
  
  
set aSearch to current application’s NSMetadataQuery’s alloc()’s init()
  
  
current application’s NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:“queryDidUpdate:” |name|:(current application’s NSMetadataQueryDidUpdateNotification) object:aSearch
  
current application’s NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:“initalGatherComplete:” |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:aSearch
  
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aQueryStrings
  
aSearch’s setPredicate:aPredicate
  
  
set aScope to current application’s NSArray’s arrayWithObjects:{origPath}
  
aSearch’s setSearchScopes:aScope
  
  
set sortKeys to current application’s NSSortDescriptor’s sortDescriptorWithKey:“kMDItemFSName” ascending:true
  
aSearch’s setSortDescriptors:(current application’s NSArray’s arrayWithObject:sortKeys)
  
  
aSearch’s startQuery()
  
end initiateSearchForFullPath

on queryDidUpdate:sender
  log sender
  
–> (NSConcreteNotification) NSConcreteNotification 0×7fa618450820 {name = NSMetadataQueryDidFinishGatheringNotification; object = }
end queryDidUpdate:

on initalGatherComplete:sender
  set anObject to sender’s object
  
anObject’s stopQuery()
  
current application’s NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(current application’s NSMetadataQueryDidUpdateNotification) object:anObject
  
current application’s NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:anObject
  
  
set my searchRes to anObject’s results()
end initalGatherComplete:

★Click Here to Open This Script 

2015/08/31 ASOCでspotlight検索するじっけん

Cocoaの機能を用いてspotlightによる検索を行うAppleScriptの実験的な実装です。

オリジナルは、Appleのオンラインドキュメント「File Metadata Search Programming Guide」にあった、「Listing 2-1 Static Spotlight search implementation」です。これをAppleScriptObjCに(少しアレンジを加えつつ)翻訳したものです。

Script Editor上でCommand-Control-RによるForeground実行する必要があります。プログラムリスト中におけるログ内容はASObjC Explorer 4で表示させたもので、同様の内容を確認したい場合にはASObjC Explorer 4が必要です。

海外のユーザーから「どうやって表示してんの、それ?(Cocoa Objectのログ)」と、問い合わせがあって「通常のScript Editorでは表示できないよ」と回答。意外と知られていないようで、、、(もったいないので、ここで宣伝しておくことにしました)。

ASObjC Explorer 4のログ表示は、AppleScriptのプログラムの間にログ表示用のコードを挟んでいるとのことなので、必ずしも実際のオブジェクトとイコールの内容が表示されているわけではありませんが、実際の内容をつかみやすいものになっています。この機能を持つAppleScript系のエディタは、目下のところASObjC Explorer 4だけです。

Objective-CのプログラムをさらっとAppleScriptObjCのプログラムに書き換えできるのは、ASObjC ExplorerのCocoaオブジェクトログ機能があればこそです。ログを出しつつ属性値を確認できるため、おおいに役立っています。逆に、Cocoaオブジェクトログ機能がないと、全く歯が立ちません(AppleがShaneのプログラムを買い取って、標準のScript Editorに機能をマージすればいいのに、、、)。

AppleScript名:ASOCでspotlight検索するじっけん
– Created 2015-08-31 by Takaaki Naganoya
– 2015 Piyomaru Software

use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

property searchRes : {}

set searchRes to {} –initialize
initiateSearch(“kMDItemContentTypeTree == ’public.image’”)

–Waiting for the result
repeat while searchRes = {} –Inifinity loop, danger!!
  delay 0.1
end repeat

set anObj to searchRes’s firstObject() –Pick up the first one for test
set anAttrList to anObj’s attributes() –”mdls” attributes
–>  (NSArray) {”kMDItemContentTypeTree”, “kMDItemContentType”, “kMDItemPhysicalSize”, …}

set aFileName to anObj’s valueForKey:“kMDItemDisplayName”
–>  (NSString) ” 1 - 95.7.16.jpg” — example

on initiateSearch(aQueryStrings)
  
  
set aSearch to current application’s NSMetadataQuery’s alloc()’s init()
  
–>  (NSMetadataQuery)
  
  
current application’s NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:“queryDidUpdate:” |name|:(current application’s NSMetadataQueryDidUpdateNotification) object:aSearch
  
current application’s NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:“initalGatherComplete:” |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:aSearch
  
  
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aQueryStrings
  
–> (NSComparisonPredicate) kMDItemContentTypeTree == “public.image”
  
aSearch’s setPredicate:aPredicate
  
  
  
set aScope to current application’s NSArray’s arrayWithObjects:{current application’s NSMetadataQueryUserHomeScope, current application’s NSMetadataQueryUbiquitousDocumentsScope}
  
–> (NSArray) {{”kMDQueryScopeHome”, “NSMetadataQueryUbiquitousDocumentsScope”}
  
aSearch’s setSearchScopes:aScope
  
  
  
set sortKeys to current application’s NSSortDescriptor’s sortDescriptorWithKey:“kMDItemDisplayName” ascending:true
  
aSearch’s setSortDescriptors:(current application’s NSArray’s arrayWithObject:sortKeys)
  
  
  
aSearch’s startQuery()
  
end initiateSearch

on queryDidUpdate:sender
  log sender
  
–> (NSConcreteNotification) NSConcreteNotification 0×7fa618450820 {name = NSMetadataQueryDidFinishGatheringNotification; object = }
end queryDidUpdate:

on initalGatherComplete:sender
  log sender
  
–> (NSConcreteNotification) NSConcreteNotification 0×7fa618450820 {name = NSMetadataQueryDidFinishGatheringNotification; object = }
  
  
set anObject to sender’s object
  
anObject’s stopQuery()
  
  
set aRes to anObject’s resultCount()
  
–>  173352 –different in each environment. Just a test
  
  
set my searchRes to anObject’s results()
  
–>  (NSArray) {(NSMetadataItem) , (NSMetadataItem) , ……
  
  
current application’s NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(current application’s NSMetadataQueryDidUpdateNotification) object:anObject
  
current application’s NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:anObject
  
  
set anObject to missing value –really needed?
  
end initalGatherComplete:

★Click Here to Open This Script