Archive for the 'Framework (third party)' Category

2017/10/25 MagicKitで指定ファイルのUTI情報を取得

オープンソースのFramework「MagicKit」(By Aidan Steele)を呼び出して、指定ファイルのUTI(Uniform Type Identifiers)の情報を取得するAppleScriptです。

本Scriptの実行にはGithub上のプロジェクトをダウンロードして各自でXcode上でFrameworkをビルドし、MagicKit.frameworkを~/Library/Frameworksフォルダ以下にインストールする必要があります。

ちょうどUTIについてこういうツールがないかと思っていたところでした(BridgePlusがなくても調べられることがのぞましい)。指定ファイルのUTIやMIME Typeを調べられるのは便利です。

とくに、NSDataから直接UTIを得られる「magicForData:」 メソッドがとても便利そうで、コレが使いたかったのでテストしてみた次第です(まだそこまで試してないですけれど)。

本当のところは、UTIの文字列(例:”public.image”)を与えると、その下位に所属するUTIの一覧をArrayとかlistとかで返してくれるAPIがあるとよいのですが、、、うまく探しきれていないだけで「存在しないのはおかしい」感じではあります。

AppleScriptでも、choose file of typeコマンドでUTIの世界を垣間見ることはありますが、その階層構造を調べられないのは少々ストレスが溜まるところです。

AppleScript名:MagicKitで指定ファイルのUTI情報を取得
– Created 2017-10-25 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “MagicKit”
–https://github.com/aidansteele/magickit
–http://piyocast.com/as/archives/4922

set aFile to POSIX path of (choose file)
set aRes to current application’s GEMagicKit’s magicForFileAtPath:aFile
–>  (GEMagicResult) ISO Media, Apple QuickTime movie

set aMime to (aRes’s mimeType()) as string
–>  ”video/quicktime; charset=binary”

set utiList to (aRes’s uniformTypeHierarchy()) as list
–>  {”com.apple.quicktime-movie”, “public.movie”, “public.audiovisual-content”, “public.data”, “public.content”, “public.item”}

set aDesc to (aRes’s |description|()) as string
–>  ”ISO Media, Apple QuickTime movie”

★Click Here to Open This Script 

2017/10/10 なろう小説APIで各カテゴリごとの集計を実行

「小説家になろう」サイトのAPI「なろう小説API」を呼び出して、カテゴリごとの該当件数を集計するAppleScriptです。

「なろう小説API」は事前にAPI Keyの取得も不要で、簡単に呼び出せるのでお手軽に使えます。

本AppleScriptは、「小説家になろう」掲載作品を、大カテゴリと小カテゴリでコードを指定して、ループで存在件数の集計を行います。カテゴリごとに分布が偏っているようなので、該当件数が0件のカテゴリは結果出力しないようにしています。筆者の環境では集計に22〜25秒ぐらいかかっています(インターネット接続回線速度に依存)。

http headerにgzip転送リクエスト要求を書きつつ、実際のデータ自体もgzipで圧縮されているので、二重に圧縮している状態です。実測したところ、http headerでgzip指定を行なったほうがトータルで1秒程度速かったので「そんなもんかな」と思いつつ、そのままにしています。

Web APIからのデータ受信時のNSDataからのZip展開にオープンソースのフレームワーク「GZIP」(By Nick Lockwood)を利用しています。同プロジェクトはGithub上のXcodeプロジェクトをXcodeでビルドするとFrameworkが得られるので、ビルドして~/Library/FrameworksフォルダにGZIP.frameworkを入れてください。

ジャンルは数値で指定するようになっていますが、その数値が何を示しているかという情報はAPI側からの出力にはないので、Webサイト上から文字情報をコピペで取得し、AppleScript内に記載して(ハードコーディング)カテゴリコードリストと照合して出力しています。

実際に集計してみると、ノンカテゴリが53%ということで、カテゴリ分けの機能が有効に活用されていないことが見てとれます。

そのことについては運営側も重々承知しているようで、APIの検索オプションに「キーワードに異世界転生があるものを含む」といったものがあるなど、ジャンルよりもキーワード重視するようにしているようです。

そういいつつも、使われているキーワードについては若干の表記ゆらぎがあるようで、単純にこのオプションを指定しても「異世界転生もの」をすべて抽出できていないように見えます。キーワード自体にどの程度「表記揺れ」が存在しているのかを調べてみるとよいのかもしれません。

APIの仕様上、2,000件しか詳細データを取得できないように見えるので、そのあたりがちょっと気になります(どうも全数調査を行いにくい仕様)。

分析するまでもなく、異世界転生ものが多く、ノンジャンル作品でも異世界転生ものばっかりという印象です。掘り出しもので「ソ連の宇宙技術は最強過ぎたのだが、それを西側諸国が完全に理解したのはつい最近だった」という作品に行き当たり、これが強烈に面白いです。

AppleScript名:なろう小説APIで各カテゴリごとの集計を実行
– Created 2017-10-10 by Takaaki Naganoya
– 2017 Piyomaru Software
–http://piyocast.com/as/archives/4891
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “GZIP”
–https://github.com/nicklockwood/GZIP
–http://dev.syosetu.com/man/api/
–1日の利用上限は80,000または転送量上限400MByte???

property |NSURL| : a reference to current application’s |NSURL|
property NSString : a reference to current application’s NSString
property NSArray : a reference to current application’s NSArray
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
property NSMutableDictionary : a reference to current application’s NSMutableDictionary
property NSURLQueryItem : a reference to current application’s NSURLQueryItem
property NSURLComponents : a reference to current application’s NSURLComponents
property NSJSONSerialization : a reference to current application’s NSJSONSerialization
property NSMutableURLRequest : a reference to current application’s NSMutableURLRequest
property NSURLConnection : a reference to current application’s NSURLConnection
property NSSortDescriptor : a reference to current application’s NSSortDescriptor
property NSNumber : a reference to current application’s NSNumber
property NSNumberFormatter : a reference to current application’s NSNumberFormatter
property NSNumberFormatterRoundUp : a reference to current application’s NSNumberFormatterRoundUp
property NSNumberFormatterRoundDown : a reference to current application’s NSNumberFormatterRoundDown

set invList to {}

set bgList to {1, 2, 3, 4, 99, 98}
set bigGnereLabel to {“恋愛”, “ファンタジー”, “文芸”, “SF”, “その他”, “ノンジャンル”}

set gList to {101, 102, 201, 202, 301, 302, 303, 304, 305, 306, 307, 401, 402, 403, 404, 9901, 9902, 9903, 9904, 9999, 9801}
set smlGenreLabel to {“異世界〔恋愛〕”, “現実世界〔恋愛〕”, “ハイファンタジー〔ファンタジー〕”, “ローファンタジー〔ファンタジー〕”, “純文学〔文芸〕”, “ヒューマンドラマ〔文芸〕”, “歴史〔文芸〕”, “推理〔文芸〕”, “ホラー〔文芸〕”, “アクション〔文芸〕”, “コメディー〔文芸〕”, “VRゲーム〔SF〕”, “宇宙〔SF〕”, “空想科学〔SF〕”, “パニック〔SF〕”, “童話〔その他〕”, “詩〔その他〕”, “エッセイ〔その他〕”, “リプレイ〔その他〕”, “その他〔その他〕”, “ノンジャンル〔ノンジャンル〕”}

–全体の件数取得
set aRec to {gzip:“5″, out:“json”, lim:“1″}
set aRESTres to callNarouAPI(aRec, “1″, “1″) of me
set wholeCount to (allCount of first item of aRESTres)

–カテゴリごとの集計
repeat with i in bgList
  repeat with ii in gList
    set aRec to {gzip:“5″, biggenre:i as string, genre:ii as string, out:“json”, lim:“1″}
    
set aRESTres to callNarouAPI(aRec, “1″, “1″) of me
    
set aTotal to allCount of first item of aRESTres
    
    
if aTotal is not equal to 0 then
      set big to contents of i
      
set small to contents of ii
      
set bigLabel to getLabelFromNum(bgList, bigGnereLabel, big) of me
      
set smlLabel to getLabelFromNum(gList, smlGenreLabel, small) of me
      
set aPerCentatge to roundingDownNumStr(((aTotal / wholeCount) * 100), 1) of me
      
set the end of invList to {biggenre:bigLabel, genre:smlLabel, totalNum:aTotal, percentage:aPerCentatge}
    end if
  end repeat
end repeat

set bList to sortRecListByLabel(invList, “totalNum”, false) of me –降順ソート
–> {{totalNum:274072, biggenre:”ノンジャンル”, percentage:53.1, genre:”ノンジャンル〔ノンジャンル〕”}, {totalNum:47121, biggenre:”ファンタジー”, percentage:9.1, genre:”ハイファンタジー〔ファンタジー〕”}, {totalNum:28883, biggenre:”恋愛”, percentage:5.6, genre:”現実世界〔恋愛〕”}, {totalNum:23217, biggenre:”文芸”, percentage:4.5, genre:”ヒューマンドラマ〔文芸〕”}, {totalNum:21320, biggenre:”ファンタジー”, percentage:4.1, genre:”ローファンタジー〔ファンタジー〕”}, {totalNum:17079, biggenre:”恋愛”, percentage:3.3, genre:”異世界〔恋愛〕”}, {totalNum:16798, biggenre:”その他”, percentage:3.2, genre:”その他〔その他〕”}, {totalNum:13892, biggenre:”その他”, percentage:2.6, genre:”詩〔その他〕”}, {totalNum:13341, biggenre:”文芸”, percentage:2.5, genre:”コメディー〔文芸〕”}, {totalNum:10120, biggenre:”文芸”, percentage:1.9, genre:”ホラー〔文芸〕”}, {totalNum:9502, biggenre:”その他”, percentage:1.8, genre:”エッセイ〔その他〕”}, {totalNum:8486, biggenre:”文芸”, percentage:1.6, genre:”純文学〔文芸〕”}, {totalNum:7211, biggenre:”文芸”, percentage:1.3, genre:”アクション〔文芸〕”}, {totalNum:6199, biggenre:”SF”, percentage:1.2, genre:”空想科学〔SF〕”}, {totalNum:5780, biggenre:”その他”, percentage:1.1, genre:”童話〔その他〕”}, {totalNum:3295, biggenre:”文芸”, percentage:0.6, genre:”推理〔文芸〕”}, {totalNum:3217, biggenre:”文芸”, percentage:0.6, genre:”歴史〔文芸〕”}, {totalNum:2606, biggenre:”SF”, percentage:0.5, genre:”VRゲーム〔SF〕”}, {totalNum:1471, biggenre:”SF”, percentage:0.2, genre:”パニック〔SF〕”}, {totalNum:1454, biggenre:”SF”, percentage:0.2, genre:”宇宙〔SF〕”}, {totalNum:190, biggenre:”その他”, percentage:0.0, genre:”リプレイ〔その他〕”}}

on callNarouAPI(aRec, callFrom, callNum)
  set reqURLStr to “http://api.syosetu.com/novelapi/api/” –通常API
  
  
–set aRec to {gzip:”5″, |st|:callFrom as string, out:”json”, lim:callNum as string}
  
set aURL to retURLwithParams(reqURLStr, aRec) of me
  
set aRes to callRestGETAPIAndParseResults(aURL) of me
  
  
set aRESCode to (responseCode of aRes) as integer
  
if aRESCode is not equal to 200 then return false
  
  
set aRESHeader to responseHeader of aRes
  
set aRESTres to (json of aRes) as list
  
end callNarouAPI

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to NSMutableURLRequest’s requestWithURL:(|NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setValue:“gzip” forHTTPHeaderField:“Content-Encoding”
  
  
set aRes to NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
  
set rRes to bRes’s gunzippedData() –From GZIP.framework
  
  
set resStr to NSString’s alloc()’s initWithData:rRes encoding:(NSUTF8StringEncoding)
  
  
set jsonString to NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(NSUTF8StringEncoding)
  
set aJsonDict to NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

–リストに入れたレコードを、指定の属性ラベルの値でソート
on sortRecListByLabel(aRecList as list, aLabelStr as string, ascendF as boolean)
  set aArray to NSArray’s arrayWithArray:aRecList
  
  
set sortDesc to NSSortDescriptor’s alloc()’s initWithKey:aLabelStr ascending:ascendF
  
set sortDescArray to NSArray’s arrayWithObjects:sortDesc
  
set sortedArray to aArray’s sortedArrayUsingDescriptors:sortDescArray
  
  
set bList to sortedArray as list
  
return bList
end sortRecListByLabel

on getLabelFromNum(aList, labelLIst, aNum)
  set aInd to offsetOf(aList, aNum) of me
  
set anItem to contents of item aInd of labelLIst
  
return anItem
end getLabelFromNum

on offsetOf(aList as list, aTarg)
  set aArray to current application’s NSArray’s arrayWithArray:aList
  
set aIndex to aArray’s indexOfObjectIdenticalTo:aTarg
  
return (aIndex + 1)
end offsetOf

on roundingDownNumStr(aNum as string, aDigit as integer)
  set a to NSString’s stringWithString:aNum
  
set aa to a’s doubleValue()
  
set aFormatter to NSNumberFormatter’s alloc()’s init()
  
aFormatter’s setMaximumFractionDigits:aDigit
  
aFormatter’s setRoundingMode:(NSNumberFormatterRoundDown)
  
set aStr to aFormatter’s stringFromNumber:aa
  
return (aStr as text) as real
end roundingDownNumStr

on roundingUpNumStr(aNum as string, aDigit as integer)
  set a to NSString’s stringWithString:aNum
  
set aa to a’s doubleValue()
  
set aFormatter to NSNumberFormatter’s alloc()’s init()
  
aFormatter’s setMaximumFractionDigits:aDigit
  
aFormatter’s setRoundingMode:(NSNumberFormatterRoundUp)
  
set aStr to aFormatter’s stringFromNumber:aa
  
return (aStr as text) as real
end roundingUpNumStr

★Click Here to Open This Script 

2017/08/23 指定フォルダ以下のLocalのHTMLのテキストエンコーディングをUTF-8に書き換えて保存

Localの指定フォルダ以下にあるHTMLをすべてSpotlightで抽出し、見つかったすべてのHTMLのテキストエンコーディングをUTF-8に変更して上書き保存するAppleScriptです。

本Script単体では動作確認できないので、確認のためにアプレットを用意しておきました。HTMLへの要素アクセスにはオープンソースのHTMLReader.framework(By Nolan Waite)を、Spotlight検索にはShane Stanleyの「Metadata Lib」を、日本語テキストファイルのエンコーディング自動検出には自作の「japaneseTextEncodingDetector」を用いています。これらをすべて含んだアプレット「htmlutf8rewriter.app」を用意しておきました(Code Signずみ)。

–> Download htmlutf8rewriter.zip

実際にテストデータ(7,979 files)で実験してみたところ、MacBook Air 2011(Core i5, 1.6GHz)上で702 Seconds、12分で処理できました(macOS 10.13beta 7で実行)。

バスが遅くて、非力なUプロセッサで、放熱機構が弱い、鈍足なMacBook Air 2011でこの程度の速度なので、手元のMacBook Pro 2012で実行すると半分ぐらいの時間で実行できるものと思われます。

Dual Core(4 thread)のMacBook Airでも1〜2thread分は処理に余裕があったので、よりコア数の多いMac上で実行する場合には並列実行するとマシンパワーを絞り出せるものと思われます。

用意したテストデータには行儀の悪いものも混在していたので、300程度のエラーが発生。こうしたエラーデータに対しては、別途何らかのテキストエディタをコントロールして書き換え&保存を行なってもよいのかもしれません。

最初からテキストエディタをコントロールして書き換えを行うと並列処理できないですし、AppleScript単体で実行するよりも処理時間がかかります(現状だと秒間11ファイルぐらい処理できていますが、外部のテキストエディタを制御して処理すると秒間2ファイルぐらいまで落ちるはずです)。

AppleScript名:指定フォルダ以下のLocalのHTMLのテキストエンコーディングをUTF-8に書き換えて保存
– Created 2017-08-23 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “HTMLReader” –https://github.com/nolanw/HTMLReader
use jLib : script “japaneseTextEncodingDetector”
use mdLib : script “Metadata Lib” version “1.0.0″
–http://piyocast.com/as/archives/4789

property NSString : a reference to current application’s NSString
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
property HTMLDocument : a reference to current application’s HTMLDocument

set theFolder to choose folder
set theRecord to mdLib’s searchFolders:{theFolder} searchString:“kMDItemFSName ENDSWITH[c] ’.html’” searchArgs:{}
set aCount to 1

repeat with i in theRecord
  set hRes to overWriteHTMLWithUTF8Encoding(i) of me
  
if hRes = true then
    set aCount to aCount + 1
  end if
end repeat

return aCount

on overWriteHTMLWithUTF8Encoding(aPOSIX)
  set aRes to readJapanesTextFileWithGuessingEncoding(aPOSIX) of jLib
  
set bRes to retSpecifiedElementsFromHTMLString(aRes, “meta”) of me
  
–>  {(HTMLElement) 0 children>, (HTMLElement) 0 children>, ….
  
  
–Search Element
  
set aCount to 0
  
set hitF to false
  
repeat with i in bRes
    set tRes to i’s attributes()
    
–> (NSDictionary) {http-equiv:”Content-Type”, content:”text/html; charset=euc-jp”}
    
    
set kList to tRes’s allKeys() as list
    
–> {”http-equiv”, “content”}
    
    
if kList contains “http-equiv” then
      set hitF to true
      
exit repeat
    end if
    
    
set aCount to aCount + 1
  end repeat
  
  
if hitF = false then return false –Element Not Found
  
  
–Text Encoding を書き換えたHTMLElementを作成する
  
set aHTML to HTMLDocument’s documentWithString:aRes
  
set anElement to (aHTML’s nodesMatchingSelector:“meta”)’s objectAtIndex:aCount
  
anElement’s setObject:“text/html; charset=UTF-8″ forKeyedSubscript:“content”
  
set aRootHTMLStr to anElement’s |document|()’s serializedFragment()
  
  
–Overwrite HTML
  
set aRes to (aRootHTMLStr’s writeToFile:aPOSIX atomically:false encoding:(NSUTF8StringEncoding) |error|:(missing value)) as boolean
  
return aRes
end overWriteHTMLWithUTF8Encoding

–与えられたHTML文字列のうち、指定されたタグ要素で囲まれた文字要素を返す
on retSpecifiedElementsFromHTMLString(anNSString, aTag)
  set aHTML to HTMLDocument’s documentWithString:anNSString
  
set anElement to (aHTML’s nodesMatchingSelector:aTag)
  
return anElement as list
end retSpecifiedElementsFromHTMLString

–与えられたHTML文字列のうち、指定されたタグ要素で囲まれた文字要素を返す
on retSpecifiedElementStringFromHTMLString(anNSString, aTag)
  set aHTML to HTMLDocument’s documentWithString:anNSString
  
set anElement to (aHTML’s nodesMatchingSelector:aTag)’s firstObject()’s textContent()
  
return anElement as string
end retSpecifiedElementStringFromHTMLString

–指定されたローカルのHTMLファイルを、指定文字エンコーディングで読み込んで、指定されたタグ要素で囲まれた文字要素を返す
on retSpecifiedElementStringFromLocalHTML(aPOSIX, anEncoding, aTag)
  set aPath to NSString’s stringWithString:aPOSIX
  
set aData to NSString’s stringWithContentsOfFile:aPath encoding:(anEncoding) |error|:(missing value)
  
if aData = missing value then return false
  
  
set aHTML to HTMLDocument’s documentWithString:aData
  
set anElement to (aHTML’s nodesMatchingSelector:aTag)’s firstObject()’s textContent()
  
return anElement as string
end retSpecifiedElementStringFromLocalHTML

★Click Here to Open This Script 

2017/08/07 LocalのHTMLからJavaScriptを除去する

オープンソースのHTMLReader.framework(By Nolan Waite)を用いて、ローカルのHTML書類からJavaScriptの記述(タグ要素)を削除するAppleScriptです。

JavaScriptを削除しているのは単に処理サンプル掲載のためであり、実際にはJavaScriptにかぎらずタグ名で要素を指定できるようになっているので、とくにtitleだろうがtableだろうがcssだろうが、問題なく削除できます。

また、サンプル掲載のためにわざとHTMLのテキストエンコーディングを固定で呼び出していますが、こちらもAppleScript単独で日本語テキストのエンコーディング自動判別できるライブラリを整備してあるので、そちらを利用するとよいでしょう(実際にやっています)。

HTMLReader.frameworkについては、実際に使いきれないぐらいの機能が入っているので、いろいろ調べていますが、処理サンプルでいちばん参考にしているのはHTMLReader.framework自体のソースコードです。Objective-Cで書かれたソース自体を読みつつ、AppleScriptで呼び出しています。

本Script実行のためにはHTMLReader.frameworkをXcode上でビルドして、~/Library/Frameworksフォルダに入れて実行する必要があります。

本Scriptを実際に処理すると、与えたHTMLからJavaScript要素を削除したHTMLのテキストを返してきます。適宜、ファイルに保存するなりしてください。

HTMLに対して単に文字列処理して加工するのではなく、こうしたHTMLReaderのようなフレームワークを併用して高度な処理が行えるのはとても便利です。

AppleScript名:LocalのHTMLからJavaScriptを除去する
– Created 2017-08-04 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “HTMLReader” –https://github.com/nolanw/HTMLReader
–http://piyocast.com/as/archives/4765

property NSString : a reference to current application’s NSString
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
property NSJapaneseEUCStringEncoding : a reference to current application’s NSJapaneseEUCStringEncoding
property HTMLDocument : a reference to current application’s HTMLDocument

set aPOSIX to POSIX path of (choose file)
set cRec to removeSpecifiedElementFromLocalHTML(aPOSIX, NSJapaneseEUCStringEncoding, “script”) of me

–指定されたローカルのHTMLファイルを、指定文字エンコーディングで読み込んで、指定されたタグ要素をすべて削除する
on removeSpecifiedElementFromLocalHTML(aPOSIX, anEncoding, aTag)
  set aPath to NSString’s stringWithString:aPOSIX
  
set aData to NSString’s stringWithContentsOfFile:aPath encoding:(anEncoding) |error|:(missing value)
  
if aData = missing value then return false
  
  
set aHTML to HTMLDocument’s documentWithString:aData
  
set elemList to (aHTML’s nodesMatchingSelector:aTag) as list
  
repeat with i in elemList
    i’s removeFromParentNode()
  end repeat
  
  
return aHTML’s serializedFragment() as string
end removeSpecifiedElementFromLocalHTML

★Click Here to Open This Script 

2017/07/21 指定のPNG画像をASCII ART化してTextEditでオープン v2

jp2aを利用して指定のPNG画像を色付きHTML形式のアスキーアート化して出力し、HTMLをRTFに変換してTextEditでオープンしてフォントを変更するAppleScriptです。

指定のJPEG画像をアスキーアート化するjp2aは、Homebrew経由でインストールしてください。

asciiart4_resized.png

asciiart3_resized.png
▲このように縦長で余白の多い画像から、自動で余白部分をトリミングして処理

asciiart_z.png
▲左側はPNG画像の余白トリミング処理を行ったもの、右側はトリミング処理を行わなかったもの

アスキーアート化するにあたって、元画像の余白部分をトリミングできたほうが良好な結果が得られるため、処理対象をJPEG画像からPNG画像(背景透過)に変更し、以前に使った画像余白トリミングフレームワーク「KGPixelBoundsClipKit」を用いてトリミングしてJPEG画像に変換。

トリミングしたJPEG画像をjp2aでHTMLのアスキーアートに変換し、さらにHTMLをスタイル付きテキスト(NSAttributedString)に変換。

スタイル付きテキストをRTF(リッチテキストフォーマット)に変換してファイル出力。RTFになればTextEditで扱えるので、TextEditでオープンして本文のフォントを等幅フォント(Osaka-mono)に指定してウィンドウのサイズを変更しています。

テストする際には、KGPixelBoundsClipKitフレームワークのバイナリを~/Library/Frameworksフォルダに入れてお試しください。

–> Download Framework Binary

AppleScript名:指定のPNG画像をASCII ART化してTextEditでオープン v2
– Created 2017-07-21 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “KGPixelBoundsClipKit” –https://github.com/kgn/KGPixelBoundsClip
–http://piyocast.com/as/archives/4736

set aFile to choose file of type {“public.png”}

–PNG画像の余白をトリミング
set anImage to (current application’s NSImage’s alloc()’s initWithContentsOfFile:(POSIX path of aFile))
set bImage to anImage’s imageClippedToPixelBounds()

–トリミング結果をデスクトップにJPEG形式で保存
set aDesktopPath to (current application’s NSProcessInfo’s processInfo()’s environment()’s objectForKey:(“HOME”))’s stringByAppendingString:“/Desktop/”
set savePath to aDesktopPath’s stringByAppendingString:((current application’s NSUUID’s UUID()’s UUIDString())’s stringByAppendingString:“.jpg”)
set fRes to saveNSImageAtPathAsJPG(bImage, savePath, 100) of me

–ASCII ARTに変換してHTMLとして出力
set anAlias to (POSIX file (savePath as string)) as alias
set htmlRes to jpegToAsciiArt(anAlias, 120) of me

–出力されたHTMLデータを評価してスタイル付きテキストに変換
set htmlData to current application’s NSString’s stringWithString:htmlRes
set keyList to {current application’s NSDocumentTypeDocumentAttribute, current application’s NSCharacterEncodingDocumentAttribute}
set valList to {current application’s NSHTMLTextDocumentType, current application’s NSUTF8StringEncoding}
set optDict to current application’s NSMutableDictionary’s dictionaryWithObjects:valList forKeys:keyList
set aStyledStr to current application’s NSAttributedString’s alloc()’s initWithData:(htmlData’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)) options:optDict documentAttributes:(missing value) |error|:(missing value)

–スタイル付きテキストをRTFとしてデスクトップに保存
set targFol to POSIX path of (path to desktop)
set aUUID to current application’s NSUUID’s UUID()’s UUIDString() as text
set bRes to my saveStyledTextAsRTF(aUUID, targFol, aStyledStr) –PDFで書き出す
set newPath to targFol & aUUID & “.rtf”

–保存したRTFをTextEditでオープンして等幅フォント設定して、Window横幅を変更
tell application “TextEdit”
  activate
  
open (POSIX file newPath) as alias
  
  
tell text of document 1
    set font of every attribute run to “Osaka-Mono”
  end tell
  
  
tell window 1
    set {x1, y1, x2, y2} to bounds
    
set bounds to {x1, y1, (x1 + 800), y2}
  end tell
end tell

–NSImageを指定パスにJPEG形式で保存
on saveNSImageAtPathAsJPG(anImage, outPath, qulityNum as real)
  set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to current application’s NSBitmapImageRep’s imageRepWithData:imageRep
  
set pathString to current application’s NSString’s stringWithString:outPath
  
set newPath to pathString’s stringByExpandingTildeInPath()
  
set myNewImageData to (aRawimg’s representationUsingType:(current application’s NSJPEGFileType) |properties|:{NSImageCompressionFactor:qulityNum})
  
set aRes to (myNewImageData’s writeToFile:newPath atomically:true) as boolean
  
return aRes –true/false
end saveNSImageAtPathAsJPG

–与えられたファイルパスのJPEG画像をASCII ARTに変換して返す
on jpegToAsciiArt(anImageAlias, digitNum as integer)
  set sText to “/usr/local/bin/jp2a -f –html-raw –colors –width=” & (digitNum as string) & ” -i “ & quoted form of POSIX path of anImageAlias
  
set tRes to do shell script sText
  
return tRes
end jpegToAsciiArt

–スタイル付きテキストを指定フォルダ(POSIX path)にRTFで書き出し
on saveStyledTextAsRTF(aFileName, targFol, aStyledString)
  –Convert NSMutableStyledStrings to RTF
  
set bstyledLength to aStyledString’s |string|()’s |length|()
  
set bDict to current application’s NSDictionary’s dictionaryWithObject:“NSRTFTextDocumentType” forKey:(current application’s NSDocumentTypeDocumentAttribute)
  
set bRTF to aStyledString’s RTFFromRange:(current application’s NSMakeRange(0, bstyledLength)) documentAttributes:bDict
  
  
– build path based on title
  
set theName to current application’s NSString’s stringWithString:aFileName
  
set theName to theName’s stringByReplacingOccurrencesOfString:“/” withString:“_”
  
set theName to theName’s stringByReplacingOccurrencesOfString:“:” withString:“_”
  
set thePath to current application’s NSString’s stringWithString:targFol
  
set thePath to (thePath’s stringByAppendingPathComponent:theName)’s stringByAppendingPathExtension:“rtf”
  
  
return (bRTF’s writeToFile:thePath atomically:true) as boolean
end saveStyledTextAsRTF

★Click Here to Open This Script 

2017/07/09 指定URLにリンクされているPDFをすべて指定のフォルダにダウンロードする

指定URLのHTMLにリンクされているPDFをすべて指定のフォルダにダウンロードするAppleScriptです。

従来、この手の処理はSafariに対してdo javascript命令を実行していましたが、呼び出しにそこそこ時間がかかります。

そこで、SafariまかせにせずにAppleScript側でオープンソースのフレームワーク「HTMLReader」を呼び出してHTMLを解析したところ、圧倒的に高速になりました。

同じページ内のリンク箇所の抽出処理だと、

  Safari+do javascript:89 seconds
  HTMLReader.framework:0.064 seconds (First Run Time:0.831 seconds)

と、Safariに対してdo javascriptコマンドを実行しないAppleScriptのほうが100〜1,400倍高速に処理できています。本Script全体の処理時間については、PDFのダウンロード処理をともなうためネットワークの速さに依存しますが、1分かからない程度で終わることでしょう。Safari+do javascriptだとリンク先の抽出がまだ終わっていないぐらいの時間です。

実行にあたっては、HTMLReaderのプロジェクトをダウンロードしてXcode上でビルドし、出来上がったフレームワークのバイナリを~/Library/Frameworksにインストールしておく必要があります。

ただ、PDFのURLが厳密にわかっている連番のファイルならshellのcurlコマンドを呼び出してダウンロードさせれば1行で終わってしまう内容ではあります。

AppleScript名:指定URLにリンクされているPDFをすべて指定のフォルダにダウンロードする
– Created 2017-07-09 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “HTMLReader” –https://github.com/nolanw/HTMLReader
–http://piyocast.com/as/archives/4720

set aTargFol to POSIX path of (choose folder with prompt “PDFダウンロード先のフォルダを選択”)
set aTargPath to current application’s NSString’s stringWithString:aTargFol

set aStr to “http://yakumo-tajimi.com/dl.html” –Safariの最前面のウィンドウからとってきてもよい
set aList to getWebLinkURLs(aStr, “pdf”) of me

repeat with i in aList
  set j to contents of i
  
set jURL to (current application’s |NSURL|’s URLWithString:j)
  
set {exRes, headerRes, aData} to checkURLResourceExistence(jURL, 10) of me
  
  
if exRes = true then
    set cURL to (current application’s |NSURL|’s URLWithString:j)
    
set cFileName to (cURL’s |lastPathComponent|()) as string
    
set savePath to (aTargPath’s stringByAppendingPathComponent:cFileName)
    
set wRes to (aData’s writeToFile:savePath atomically:true)
  end if
end repeat

–指定のURLのページのHTMLソースからリンクを抽出して、指定拡張子に合うものだけをフルパスのURL化して返す
on getWebLinkURLs(anURLstr, linkFileExt)
  –URLの妥当性チェック(存在チェック)
  
set aURL to (current application’s |NSURL|’s URLWithString:anURLstr)
  
set {exRes, headerRes, aData} to checkURLResourceExistence(aURL, 3) of me
  
if exRes = false then error “Illegal URL Error” –エラー発生時に処理打ち切り
  
  
–HTMLのソースを取得する
  
set conType to headerRes’s valueForKeyPath:“Content-Type”
  
set aHTML to current application’s HTMLDocument’s documentWithData:aData contentTypeHeader:conType
  
  
–リンク箇所を抽出
  
set aTextArray to ((aHTML’s nodesMatchingSelector:“a”)’s textContent) as list –リンク文字
  
set aLinkArray to ((aHTML’s nodesMatchingSelector:“a”)’s attributes’s valueForKeyPath:“href”) as list –URL
  
  
  
–取得したリンクを拡張子で絞り込みつつ、それぞれフルパスのURLを組み立てる
  
set urlList to {}
  
set aaURL to aURL’s URLByDeletingLastPathComponent()
  
  
repeat with i in aLinkArray
    set bURL to (current application’s |NSURL|’s URLWithString:i)
    
set aRes to (bURL’s |scheme|()) as string
    
set aExt to (bURL’s |pathExtension|()) as string
    
    
if aRes = “missing value” and aExt = linkFileExt then
      –想定URL(指定サイト内)のファイルへのリンクの処理
      
set aaaURL to (aaURL’s URLByAppendingPathComponent:i)
      
set aaaURLstr to (aaaURL’s absoluteString()) as string
      
set the end of urlList to aaaURLstr
    else if aRes is not “missing value” and aExt = linkFileExt then
      –指定外のURL(想定サイト外のファイルへのリンクなど)の処理
      
set the end of urlList to (aaURL’s absoluteString()) as string
    end if
  end repeat
  
  
–重複部分を除去してユニークなリストにして返す
  
return uniquify1DList(urlList, true) of me
end getWebLinkURLs

– 指定URLにファイル(画像など)が存在するかチェック
–> {存在確認結果(boolean), レスポンスヘッダー(NSDictionary), データ(NSData)}
on checkURLResourceExistence(aURL, timeOutSec as real)
  set aRequest to (current application’s NSURLRequest’s requestWithURL:aURL cachePolicy:(current application’s NSURLRequestUseProtocolCachePolicy) timeoutInterval:timeOutSec)
  
set aRes to (current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value))
  
set dRes to (first item of (aRes as list))
  
set bRes to (second item of (aRes as list))
  
if bRes is not equal to missing value then
    set hRes to (bRes’s allHeaderFields())
    
set aResCode to (bRes’s statusCode()) as integer
  else
    set hRes to {}
    
set aResCode to -1 –error
  end if
  
return {(aResCode = 200), hRes, dRes}
end checkURLResourceExistence

–1D/2D Listをユニーク化
on uniquify1DList(theList as list, aBool as boolean)
  set aArray to current application’s NSArray’s arrayWithArray:theList
  
set bArray to aArray’s valueForKeyPath:“@distinctUnionOfObjects.self”
  
return bArray as list
end uniquify1DList

★Click Here to Open This Script 

2017/04/26 指定画像の余白の自動トリミング

オープンソースのKGPixelBoundsClip(By David Keegan)をフレームワーク化した「KGPixelBoundsClipKit」を呼び出して指定画像を自動でトリミングするAppleScriptです。

OS X 10.10以降用にビルドしたフレームワークのバイナリを用意しておきました。興味のある方は自己責任で~/Library/Frameworksフォルダに入れてお試しください。

–> Download Framework Binary

指定画像(背景を透明に指定したPNGで実行)を自動トリミングして、デスクトップに結果を書き出します。

Photoshopにも同様の機能があったような記憶がありますが、フレームワーク経由で実行することで並列処理を行いやすい部品を作っておくことが目的です。

original_and_trimed.png

AppleScript名:指定画像の余白の自動トリミング
– Created 2017-04-26 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “KGPixelBoundsClipKit” –https://github.com/kgn/KGPixelBoundsClip
–http://piyocast.com/as/archives/4611

set aFile to POSIX path of (choose file of type {“public.image”})
set anImage to (current application’s NSImage’s alloc()’s initWithContentsOfFile:aFile)

set bImage to anImage’s imageClippedToPixelBounds()

set aDesktopPath to (current application’s NSProcessInfo’s processInfo()’s environment()’s objectForKey:(“HOME”))’s stringByAppendingString:“/Desktop/”
set savePath to aDesktopPath’s stringByAppendingString:((current application’s NSUUID’s UUID()’s UUIDString())’s stringByAppendingString:“.png”)
set fRes to saveNSImageAtPathAsPNG(bImage, savePath) of me

–NSImageを指定パスにPNG形式で保存
on saveNSImageAtPathAsPNG(anImage, outPath)
  set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to current application’s NSBitmapImageRep’s imageRepWithData:imageRep
  
set pathString to current application’s NSString’s stringWithString:outPath
  
set newPath to pathString’s stringByExpandingTildeInPath()
  
set myNewImageData to (aRawimg’s representationUsingType:(current application’s NSPNGFileType) |properties|:(missing value))
  
set aRes to (myNewImageData’s writeToFile:newPath atomically:true) as boolean
  
return aRes
end saveNSImageAtPathAsPNG

★Click Here to Open This Script 

2017/03/02 指定の画像が真っ白かどうか判定する

オープンソースのフレームワーク「GPUImage」(By Brad Larson)を利用して、指定の画像が真っ白かどうかを判定するAppleScriptです。指定画像が真っ白の場合にはtrueを、そうでない場合にはfalseを返します。

テストにあたっては、GPUImageフレームワークをXcodeでビルドして、~/Library/Frameworksフォルダに入れておいてください。

GPUImage.frameworkには特殊なフィルタが存在しており、その中で最もユニークなのが指定画像(NSImage)のヒストグラムを求めるものです。つまり、画像をフィルタ加工するのではなく、分析した結果を画像(NSImage)に出力します。

そのうちの「明度ヒストグラム」のフィルタ(?)を指定して、指定画像の明度分布を求めます(一瞬で)。明度分布のヒストグラムは、横256ドット・高さ3ドットの画像(NSImage)として出力されます。うち、中央の1ドットが左側が0(暗い=黒)右側が255(明るい=白)を表現しています。

white.png
▲GPUImage.frameworkで出力した明度ヒストグラム(真っ白な画像を処理した場合)。幅256ドット、高さ3ドットの画像(高さを90ドットに拡大)。右端(明度=255)のみ白く反応しており、他に明度分布は存在しないことが見て取れる

test2.png

test1.png
▲GPUImage.frameworkで出力した明度ヒストグラム(真っ白ではない画像を処理した場合の処理例)

つまり、画像が真っ白かどうかを判定するという、気が遠くなるほど重そうな画像処理が、わずか256箇所の色情報を調べるだけで、しかもGPUの機能を活用して一瞬で処理できることになります。しかも、AppleScriptで。

  1024×768@72DPIの画像=786,432ドット
  1920×1200@72DPIの画像=2,304,000ドット

これらを全部ループで色検出処理すると、相当の時間がかかるはずです。230万アイテム超のループ処理なんて想像したくもありません。

一応、テストのために、

  1024×768@72DPIの画像で、1ドットだけ黒くしたもの
  1920×1200@72DPIの画像で、1ドットだけ黒くしたもの

の2つを用意して、黒くした1ドットの存在を検出できるかテストしてみたところ、無事検出できました(しかも一瞬で)。

ただし、このことが4Kとか5Kの白い画像に1ドットだけ黒くした場合でも同様に検出できるかどうかは保証のかぎりではありません。

実際に利用するさいには事前にターゲットサイズの白い画像に1ドットだけ黒くして実験して検出可能かどうかを確認。検出できなかった場合には事前に解像度を落とす処理を行うなどの対応が必要です。

AppleScript名:指定の画像が真っ白かどうか判定する
– Created 2017-02-12 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “GPUImage”
–http://piyocast.com/as/archives/4492

set aFile to POSIX path of (choose file of type {“public.image”} with prompt “Select a Image to check it is white (or not) “)
set anNSImage to current application’s NSImage’s alloc()’s initWithContentsOfFile:aFile
set wRes to my detectAllWhite:anNSImage

–指定のNSImageが真っ白かどうかをチェック
on detectAllWhite:anNSImage
  
  
–明度ヒストグラム画像を取得
  
set imgRes to getHistogramFromImage(anNSImage, 4) of me
  
  
set aRawimg to current application’s NSBitmapImageRep’s imageRepWithData:(imgRes’s TIFFRepresentation())
  
  
–白い画像のデータパターン
  
set aWhitePattern to current application’s NSMutableArray’s arrayWithArray:{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0}
  
  
set resArray to current application’s NSMutableArray’s alloc()’s init()
  
repeat with i from 0 to 255
    set origColor to (aRawimg’s colorAtX:i y:1)
    
set srgbColSpace to current application’s NSColorSpace’s genericGrayColorSpace
    
set aColor to (origColor’s colorUsingColorSpace:srgbColSpace)
    
set aWhite to aColor’s whiteComponent()
    (
resArray’s addObject:aWhite)
  end repeat
  
  
set aRes to (resArray’s isEqualTo:aWhitePattern) as boolean
  
return aRes
  
end detectAllWhite:

–指定のNSImageをGPUImage.frameworkで明度ヒストグラム化してNSImageで返す
on getHistogramFromImage(aNSImage, histogramType)
  set aFilter to current application’s GPUImageHistogramFilter’s alloc()’s initWithHistogramType:histogramType
  
set aProcImg to (aFilter’s imageByFilteringImage:aNSImage)
  
return aProcImg
end getHistogramFromImage

–NSImageをGPUImage.frameworkの指定フィルタで処理してNSImageを返す
on filterWithNSImage(aNSImage, filterName as string)
  set aClass to current application’s NSClassFromString(filterName)
  
set aImageFilter to aClass’s alloc()’s init()
  
set aProcImg to (aImageFilter’s imageByFilteringImage:aNSImage)
  
return aProcImg
end filterWithNSImage

★Click Here to Open This Script 

2017/02/16 YAMLのじっけん

オープンソースのフレームワーク「YAML.framework」(Mirek Rusin)を用いて、構造化データ記述言語であるYAMLとオブジェクト(listやrecord)との間の相互変換を行うAppleScriptです。

これまでYAMLとは縁のない生活を送ってきましたが、とくに問題はありませんでした。構造を持つデータの分量が増えた場合には、プログラム中に直接記述せず、Excelの表からデータを読み取って処理したり、データベースなど他のデータソースからデータを取得していました。

ただ、listとかrecordの内容が込み入ってくる(フィールド数が多いとか、入れ子構造の段数が深いとか)と、こういう仕組みがあったほうが便利なんだろうな、ということは理解できます。

YAML.frameworkのプロジェクトをXcodeでビルドし、出来上がったフレームワークを~/Library/Frameworksに入れてテストしてみてください。

途中、Githubに掲載されているObjective-CのサンプルプログラムがNSInputStreamを使っており、これをAppleScriptに書き換えて呼び出すとAppleScriptの処理系が100%クラッシュ。NSDataを経由して変換する方法なども試してみましたがNSInputStreamを作りにいくとクラッシュ。

結局、Stringから直接変換するメソッドがあったため、これを使用することで安定して処理できるようになりました。

当初は機械学習フレームワークの「YCML」をいじくっていたのですが、その過程で本フレームワークを発見。なかなか有用性が高そうなので試してみた次第です。

AppleScript名:YAMLのじっけん
– Created 2017-02-16 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “YAML” –https://github.com/mirek/YAML.framework
–http://piyocast.com/as/archives/4464

–YAMLの文字列からオブジェクトを生成する
set aYAMLstr to
items:
- name: Foo
- name: Bar

set aStr to current application’s NSString’s stringWithString:aYAMLstr
set aData to aStr’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)

set aData to current application’s YAMLSerialization’s objectsWithYAMLString:aStr options:(4096) |error|:(missing value)
–>  (NSArray) {{items:{{name:”Foo”}, {name:”Bar”}}}}

–オブジェクトからYAMLの文字列を取得する
set aString to (current application’s YAMLSerialization’s createYAMLStringWithObject:aData options:(1) |error|:(missing value)) as string
(* –>
“—
- items:
- name: Foo
- name: Bar
…”
*)

★Click Here to Open This Script 

AppleScript名:YAMLのじっけん4
– Created 2017-02-16 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “YAML” –https://github.com/mirek/YAML.framework
–http://piyocast.com/as/archives/4464

–YAMLの文字列からオブジェクトを生成する
set aYAMLstr to
- name: Smith
email: smith@mail.com
- name: Shelton
email: shelton@mail.com
- name: Kelly
email: kelly@mail.com

set aRes to retObjectFromYAMLString(aYAMLstr) of me
–>  {{{name:”Smith”, email:”smith@mail.com”}, {name:”Shelton”, email:”shelton@mail.com”}, {name:”Kelly”, email:”kelly@mail.com”}}}

set bYAMLstr to
names: [Smith, Shelton, Kelly]
emails: [smith@mail.com, shelton@mail.com, kelly@mail.com]

set bRes to retObjectFromYAMLString(bYAMLstr) of me
–>  {{names:{”Smith”, “Shelton”, “Kelly”}, emails:{”smith@mail.com”, “shelton@mail.com”, “kelly@mail.com”}}}

–オブジェクト(list)からYAMLの文字列を生成する
set bStr to retYAMLStringFromObject(bRes)
(*  
“—
- names:
- Smith
- Shelton
- Kelly
emails:
- smith@mail.com
- shelton@mail.com
- kelly@mail.com


*)

on retObjectFromYAMLString(aYAMLstr as string)
  set aStr to current application’s NSString’s stringWithString:aYAMLstr
  
set aData to aStr’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aData to current application’s YAMLSerialization’s objectsWithYAMLString:aStr options:(4096) |error|:(missing value)
  
return aData as list
end retObjectFromYAMLString

on retYAMLStringFromObject(anObject)
  set aString to (current application’s YAMLSerialization’s createYAMLStringWithObject:anObject options:(1) |error|:(missing value)) as string
  
return aString
end retYAMLStringFromObject

★Click Here to Open This Script 

2017/02/12 GPUImageで輪郭抽出フィルタを実行

オープンソースのフレームワーク「GPUImage」(By Brad Larson)を呼び出して、指定画像に輪郭抽出フィルタを実行して、デスクトップにフィルタ名でPNGに保存するAppleScriptです。

テストにあたっては、GPUImageフレームワークをXcodeでビルドして、~/Library/Frameworksフォルダに入れておいてください。

実際にはもっと凝った処理に使っていますが、その過程でフィルタ名を文字列で間接指定して実行するルーチンが出来てきて、なかなか使い勝手もよくなってきています。

気になる処理速度ですが、MacBook Pro Mid 2012 Core i7 2.6GHzの環境で、

 3,264 × 2,448 pixel、1.2MB → 0.7sec
 3,024 × 4,032 pixel、2.8MB → 1.2sec

ぐらいでした(10回計測時の平均。2回目以降はキャッシュが効いている可能性もあります)。処理中にはこの4Core/8Threadの環境でCPUのロードアベレージが20%ぐらいでまだ余裕があり、AppleScriptで並列処理するともうちょっと速度を稼げるかもしれません。

sample1.png

sample2.png

AppleScript名:GPUImageで輪郭抽出フィルタを実行
– Created 2017-02-12 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “GPUImage” –https://github.com/BradLarson/GPUImage
use framework “AppKit”
–http://piyocast.com/as/archives/4451

set aFile to POSIX path of (choose file of type {“public.image”})
set anImage to current application’s NSImage’s alloc()’s initWithContentsOfFile:aFile
set imgRes to filterWithNSImage(anImage, “GPUImageSobelEdgeDetectionFilter”) of me
set newPath to retUUIDfilePath(aFile, “png”) of me
set sRes to saveNSImageAtPathAsPNG(imgRes, newPath) of me

on retUUIDfilePath(aPath, aEXT)
  set aUUIDstr to (current application’s NSUUID’s UUID()’s UUIDString()) as string
  
set aPath to ((current application’s NSString’s stringWithString:aPath)’s stringByDeletingLastPathComponent()’s stringByAppendingPathComponent:aUUIDstr)’s stringByAppendingPathExtension:aEXT
  
return aPath
end retUUIDfilePath

on filterWithNSImage(aNSImage, filterName as string)
  set aClass to current application’s NSClassFromString(filterName)
  
set aImageFilter to aClass’s alloc()’s init()
  
set aProcImg to (aImageFilter’s imageByFilteringImage:aNSImage)
  
return aProcImg
end filterWithNSImage

–NSImageを指定パスにPNG形式で保存
on saveNSImageAtPathAsPNG(anImage, outPath)
  set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to current application’s NSBitmapImageRep’s imageRepWithData:imageRep
  
  
set pathString to current application’s NSString’s stringWithString:outPath
  
set newPath to pathString’s stringByExpandingTildeInPath()
  
  
set myNewImageData to (aRawimg’s representationUsingType:(current application’s NSPNGFileType) |properties|:(missing value))
  
set aRes to (myNewImageData’s writeToFile:newPath atomically:true) as boolean
  
  
return aRes –true/false
end saveNSImageAtPathAsPNG

★Click Here to Open This Script 

2017/02/05 GPUImageで画像にすべてのフィルタを実行してデスクトップにPNG形式で保存

オープンソースのフレームワーク「GPUImage」(By Brad Larson)を呼び出して、指定画像にGPUImageのすべてのフィルタを実行して、デスクトップにフィルタ名でPNGに保存するAppleScriptです。

テストにあたっては、GPUImageフレームワークをXcodeでビルドして、~/Library/Frameworksフォルダに入れておいてください。

ドキュメントによれば125種類ものフィルタを内蔵しているというGPUImageですが、どれがどのような処理を行ってくれるのかは、実際に呼び出してみないとわかりません。また、単に呼び出して画像にフィルタを実行しただけでは記述内容が不足しているというものもありそうです(パラメータを指定しないと意味がないケース、フィルタが仕様の都合で複数の画像を要求するものも)。

そこで、ヘッダーファイルからすべてのフィルタ名称を取り出して、ループで順次実行させてみました。デスクトップにフィルター名称でPNG画像を書き出します。

ヘッダーファイルに記載されていたフィルターとおぼしきものが145、うち実行可能だったものが100、エラーが45となりました。また、エラーは出なかったものの何らかのパラメータを指定しないとおそらく意味がない(オリジナル画像と変わらない)ものもありました。

samples_out.png

フィルタをallocしてinitした段階でエラーが出るものもあったり、エラーをキャッチできるまでに時間がかかるものもありましたが、with timedoutでタイムアウト時間を指定してもエラーとして検出することはできませんでした。

フレームワークを呼び出すのはAppleEventの枠組みの中でやっているわけではないはずなので、タイムアウトを仕掛けて適用されなくても仕方ありません。

AppleScript名:GPUImageで画像にすべてのフィルタを実行してデスクトップにPNG形式で保存
– Created 2017-02-05 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “GPUImage”
–https://github.com/BradLarson/GPUImage
–http://piyocast.com/as/archives/4441

–Read JPEG file
set aFile to POSIX path of (choose file of type {“public.image”})
set anImage to current application’s NSImage’s alloc()’s initWithContentsOfFile:aFile

–Select Filter
set erroredFilter to {}
set aList to {“GPUImageFilter”, “GPUImageTwoPassFilter”, “GPUImage3×3TextureSamplingFilter”, “GPUImageContrastFilter”, “GPUImageSaturationFilter”, “GPUImageBrightnessFilter”, “GPUImageLevelsFilter”, “GPUImageExposureFilter”, “GPUImageRGBFilter”, “GPUImageHueFilter”, “GPUImageWhiteBalanceFilter”, “GPUImageMonochromeFilter”, “GPUImagePixellateFilter”, “GPUImageSobelEdgeDetectionFilter”, “GPUImageSketchFilter”, “GPUImageToonFilter”, “GPUImageGrayscaleFilter”, “GPUImageKuwaharaFilter”, “GPUImageFalseColorFilter”, “GPUImageSharpenFilter”, “GPUImageUnsharpMaskFilter”, “GPUImageTwoInputFilter”, “GPUImageGaussianBlurFilter”, “GPUImageTwoPassTextureSamplingFilter”, “GPUImageFilterGroup”, “GPUImageTransformFilter”, “GPUImageCropFilter”, “GPUImageGaussianBlurPositionFilter”, “GPUImageGaussianSelectiveBlurFilter”, “GPUImageBilateralFilter”, “GPUImageBoxBlurFilter”, “GPUImageSingleComponentGaussianBlurFilter”, “GPUImageMedianFilter”, “GPUImageMotionBlurFilter”, “GPUImageZoomBlurFilter”, “GPUImageAddBlendFilter”, “GPUImageColorBurnBlendFilter”, “GPUImageDarkenBlendFilter”, “GPUImageDivideBlendFilter”, “GPUImageLightenBlendFilter”, “GPUImageMultiplyBlendFilter”, “GPUImageOverlayBlendFilter”, “GPUImageColorDodgeBlendFilter”, “GPUImageLinearBurnBlendFilter”, “GPUImageScreenBlendFilter”, “GPUImageColorBlendFilter”, “GPUImageExclusionBlendFilter”, “GPUImageHueBlendFilter”, “GPUImageLuminosityBlendFilter”, “GPUImageNormalBlendFilter”, “GPUImagePoissonBlendFilter”, “GPUImageSaturationBlendFilter”, “GPUImageSoftLightBlendFilter”, “GPUImageHardLightBlendFilter”, “GPUImageSubtractBlendFilter”, “GPUImageTwoInputCrossTextureSamplingFilter”, “GPUImageDifferenceBlendFilter”, “GPUImageDissolveBlendFilter”, “GPUImageChromaKeyBlendFilter”, “GPUImageMaskFilter”, “GPUImageOpacityFilter”, “GPUImageAlphaBlendFilter”, “GPUImageColorMatrixFilter”, “GPUImageSepiaFilter”, “GPUImageGammaFilter”, “GPUImageHazeFilter”, “GPUImageToneCurveFilter”, “GPUImageHighlightShadowFilter”, “GPUImageLookupFilter”, “GPUImageAmatorkaFilter”, “GPUImageMissEtikateFilter”, “GPUImageSoftEleganceFilter”, “GPUImage3×3ConvolutionFilter”, “GPUImageEmbossFilter”, “GPUImageLaplacianFilter”, “GPUImageLanczosResamplingFilter”, “GPUImageThreeInputFilter”, “GPUImageFourInputFilter”, “GPUImageColorInvertFilter”, “GPUImageHistogramFilter”, “GPUImageHistogramGenerator”, “GPUImageAverageColor”, “GPUImageLuminosity”, “GPUImageSolidColorGenerator”, “GPUImageAdaptiveThresholdFilter”, “GPUImageAverageLuminanceThresholdFilter”, “GPUImageLuminanceThresholdFilter”, “GPUImageSolarizeFilter”, “GPUImageHalftoneFilter”, “GPUImagePixellatePositionFilter”, “GPUImagePolarPixellateFilter”, “GPUImagePolkaDotFilter”, “GPUImageCrosshatchFilter”, “GPUImageXYDerivativeFilter”, “GPUImageDirectionalNonMaximumSuppressionFilter”, “GPUImageDirectionalSobelEdgeDetectionFilter”, “GPUImageCannyEdgeDetectionFilter”, “GPUImagePrewittEdgeDetectionFilter”, “GPUImageThresholdEdgeDetectionFilter”, “GPUImageHarrisCornerDetectionFilter”, “GPUImageNobleCornerDetectionFilter”, “GPUImageShiTomasiFeatureDetectionFilter”, “GPUImageThresholdedNonMaximumSuppressionFilter”, “GPUImageColorPackingFilter”, “GPUImageHoughTransformLineDetector”, “GPUImageParallelCoordinateLineTransformFilter”, “GPUImageCrosshairGenerator”, “GPUImageLineGenerator”, “GPUImageBuffer”, “GPUImageLowPassFilter”, “GPUImageHighPassFilter”, “GPUImageMotionDetector”, “GPUImageThresholdSketchFilter”, “GPUImageSmoothToonFilter”, “GPUImageTiltShiftFilter”, “GPUImageCGAColorspaceFilter”, “GPUImagePosterizeFilter”, “GPUImageKuwaharaRadius3Filter”, “GPUImageChromaKeyFilter”, “GPUImageVignetteFilter”, “GPUImageBulgeDistortionFilter”, “GPUImagePinchDistortionFilter”, “GPUImageStretchDistortionFilter”, “GPUImageClosingFilter”, “GPUImageRGBClosingFilter”, “GPUImageDilationFilter”, “GPUImageRGBDilationFilter”, “GPUImageErosionFilter”, “GPUImageRGBErosionFilter”, “GPUImageOpeningFilter”, “GPUImageRGBOpeningFilter”, “GPUImageSphereRefractionFilter”, “GPUImageGlassSphereFilter”, “GPUImageSwirlFilter”, “GPUImageJFAVoronoiFilter”, “GPUImageVoronoiConsumerFilter”, “GPUImageLocalBinaryPatternFilter”, “GPUImageColorLocalBinaryPatternFilter”, “GPUImageMosaicFilter”, “GPUImagePerlinNoiseFilter”, “GPUImageWeakPixelInclusionFilter”, “GPUImageNonMaximumSuppressionFilter”, “GPUImageSourceOverBlendFilter”, “GPUImageColourFASTFeatureDetector”, “GPUImageColourFASTSamplingOperation”}

repeat with i in aList
  set j to contents of i
  
set aClass to current application’s NSClassFromString(j)
  
  
–Filter Image
  
set errorFlag to true
  
try
    with timeout of 5 seconds
      set stillImageFilter to aClass’s alloc()’s init()
      
set aProcImg to (stillImageFilter’s imageByFilteringImage:anImage)
    end timeout
  on error erM
    set the end of erroredFilter to {j, erM, 1}
    
set errorFlag to false
  end try
  
  
if errorFlag = true then
    –Make New File Name
    
set aPath to (((current application’s NSString’s stringWithString:aFile)’s stringByDeletingLastPathComponent()’s stringByAppendingPathComponent:j)’s stringByAppendingPathExtension:“png”)
    
try
      set sRes to saveNSImageAtPathAsPNG(aProcImg, aPath as string) of me
    on error erM
      set the end of erroredFilter to {j, erM, 2}
    end try
  end if
end repeat

return erroredFilter
–> {{”GPUImageTwoInputFilter”, “missing valueは“representationUsingType_properties_”メッセージを認識できません。”, 2}, …..

–NSImageを指定パスにPNG形式で保存
on saveNSImageAtPathAsPNG(anImage, outPath)
  set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to current application’s NSBitmapImageRep’s imageRepWithData:imageRep
  
  
set pathString to current application’s NSString’s stringWithString:outPath
  
set newPath to pathString’s stringByExpandingTildeInPath()
  
  
set myNewImageData to (aRawimg’s representationUsingType:(current application’s NSPNGFileType) |properties|:(missing value))
  
set aRes to (myNewImageData’s writeToFile:newPath atomically:true) as boolean
  
  
return aRes –true/false
end saveNSImageAtPathAsPNG

★Click Here to Open This Script 

2017/02/05 GPUImageで画像にGPUImageMonochromeFilterを実行してデスクトップにPNG形式で保存

オープンソースのフレームワーク「GPUImage」(By Brad Larson)を呼び出して、指定画像にフィルタ(モノクローム画像化)を実行して、デスクトップにPNGで保存するAppleScriptです。

テストにあたっては、GPUImageフレームワークをXcodeでビルドして、~/Library/Frameworksフォルダに入れておいてください。

GPUImageはもともとiOSデバイス用に作られたようですが、Macもサポートしており、さまざまな(125種類もの)画像フィルタが用意されており、手軽に利用できます。CoreImageをAppleScriptから呼び出してフィルタを実行してみたこともありますが、CoreImageよりも手軽に感じられました。

フィルタ実行部分は2行だけで、ファイルを選択したり画像を保存する部分がほとんどなので、やることはほとんどありません。ある意味、Photoshopを呼び出すよりも手軽です。

img_0007_resized.png
▲実行前のオリジナル画像(Haneda Airport, Tokyo, Japan)

a5dfc34c-d1e4-47e6-8379-f46412dbc30d_resized.png
▲GPUImageMonochromeFilterを実行した画像

AppleScript名:GPUImageで画像にGPUImageMonochromeFilterを実行してデスクトップにPNG形式で保存
– Created 2017-02-05 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “GPUImage”
–https://github.com/BradLarson/GPUImage
–http://piyocast.com/as/archives/4438

–Read JPEG file
set aFile to POSIX path of (choose file of type {“public.image”})
set anImage to current application’s NSImage’s alloc()’s initWithContentsOfFile:aFile

–Filter Image
set stillImageFilter to current application’s GPUImageMonochromeFilter’s alloc()’s init()
set aProcImg to stillImageFilter’s imageByFilteringImage:anImage

–Make New File Name
set aUUIDstr to (current application’s NSUUID’s UUID()’s UUIDString()) as string
set aPath to ((current application’s NSString’s stringWithString:aFile)’s stringByDeletingLastPathComponent()’s stringByAppendingPathComponent:aUUIDstr)’s stringByAppendingPathExtension:“png”
set sRes to saveNSImageAtPathAsPNG(aProcImg, aPath as string) of me

–NSImageを指定パスにPNG形式で保存
on saveNSImageAtPathAsPNG(anImage, outPath)
  set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to current application’s NSBitmapImageRep’s imageRepWithData:imageRep
  
  
set pathString to current application’s NSString’s stringWithString:outPath
  
set newPath to pathString’s stringByExpandingTildeInPath()
  
  
set myNewImageData to (aRawimg’s representationUsingType:(current application’s NSPNGFileType) |properties|:(missing value))
  
set aRes to (myNewImageData’s writeToFile:newPath atomically:true) as boolean
  
  
return aRes –true/false
end saveNSImageAtPathAsPNG

★Click Here to Open This Script 

2016/11/21 HTMLReaderでWeb上のHTMLからリンクを取得する

オープンソースのフレームワーク「HTMLReader」を用いて、指定のURLのHTMLをダウンロードして指定要素を取り出すAppleScriptです。

実行にあたっては、HTMLReaderをダウンロードしてビルドし、~/Library/Frameworksにインストールしておく必要があります。

AppleScript名:HTMLReaderでWeb上のHTMLからリンクを取得する
– Created 2016-11-21 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “HTMLReader” –https://github.com/nolanw/HTMLReader
–http://piyocast.com/as/archives/4325

set aStr to “https://github.com/nolanw/HTMLReader”
set aURL to (current application’s |NSURL|’s URLWithString:aStr)
set {exRes, headerRes, aData} to checkURLResourceExistence(aURL, 3) of me
if exRes = false then return –エラー発生時に処理打ち切り

set conType to headerRes’s valueForKeyPath:“Content-Type”
set aHTML to current application’s HTMLDocument’s documentWithData:aData contentTypeHeader:conType

set aTextArray to ((aHTML’s nodesMatchingSelector:“a”)’s textContent) as list –リンク文字
set aLinkArray to ((aHTML’s nodesMatchingSelector:“a”)’s attributes’s valueForKeyPath:“href”) as list –URL
–> {”#start-of-content”, “https://github.com/”, “/personal”, “/open-source”, “/business”, “/explore”, “/join?source=header-repo”, “/login?return_to=%2Fnolanw%2FHTMLReader”, “/pricing”, “/blog”, …….}

– 指定URLにファイル(画像など)が存在するかチェック
–> {存在確認結果(boolean), レスポンスヘッダー(NSDictionary), データ(NSData)}
on checkURLResourceExistence(aURL, timeOutSec as real)
  set aRequest to (current application’s NSURLRequest’s requestWithURL:aURL cachePolicy:(current application’s NSURLRequestUseProtocolCachePolicy) timeoutInterval:timeOutSec)
  
set aRes to (current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value))
  
set dRes to (first item of (aRes as list))
  
set bRes to (second item of (aRes as list))
  
if bRes is not equal to missing value then
    set hRes to (bRes’s allHeaderFields())
    
set aResCode to (bRes’s statusCode()) as integer
  else
    set hRes to {}
    
set aResCode to -1 –error
  end if
  
return {(aResCode = 200), hRes, dRes}
end checkURLResourceExistence

★Click Here to Open This Script 

AppleScript名:HTMLReaderでWeb上のHTMLから画像リンクを取得する
– Created 2016-11-21 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “HTMLReader” –https://github.com/nolanw/HTMLReader
–http://piyocast.com/as/archives/4325

set aStr to “https://github.com/nolanw/HTMLReader”
set aURL to (current application’s |NSURL|’s URLWithString:aStr)
set {exRes, headerRes, aData} to checkURLResourceExistence(aURL, 3) of me
if exRes = false then return –エラー発生時に処理打ち切り

set conType to headerRes’s valueForKeyPath:“Content-Type”
set aHTML to current application’s HTMLDocument’s documentWithData:aData contentTypeHeader:conType

set aLinkArray to ((aHTML’s nodesMatchingSelector:“img”)’s attributes’s valueForKeyPath:“src”) as list –画像リンクURLを取得
–> {”https://avatars2.githubusercontent.com/u/177228?v=3&s=40″, “https://assets-cdn.github.com/images/spinners/octocat-spinner-32.gif”, “https://assets-cdn.github.com/images/spinners/octocat-spinner-32.gif”, ….

– 指定URLにファイル(画像など)が存在するかチェック
–> {存在確認結果(boolean), レスポンスヘッダー(NSDictionary), データ(NSData)}
on checkURLResourceExistence(aURL, timeOutSec as real)
  set aRequest to (current application’s NSURLRequest’s requestWithURL:aURL cachePolicy:(current application’s NSURLRequestUseProtocolCachePolicy) timeoutInterval:timeOutSec)
  
set aRes to (current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value))
  
set dRes to (first item of (aRes as list))
  
set bRes to (second item of (aRes as list))
  
if bRes is not equal to missing value then
    set hRes to (bRes’s allHeaderFields())
    
set aResCode to (bRes’s statusCode()) as integer
  else
    set hRes to {}
    
set aResCode to -1 –error
  end if
  
return {(aResCode = 200), hRes, dRes}
end checkURLResourceExistence

★Click Here to Open This Script 

2016/11/21 HTMLReaderでWeb上のHTMLを取得する

オープンソースのフレームワーク「HTMLReader」を用いて、指定のURLのHTMLをダウンロードして指定要素を取り出すAppleScriptです。

実行にあたっては、HTMLReaderをダウンロードしてビルドし、~/Library/Frameworksにインストールしておく必要があります。

Github上のページで紹介されているサンプルを翻訳したもので、そのうち「Load a web page.」をblocks構文を使わずに大幅に書き換えてみました。

AppleScript名:HTMLReaderのじっけん
– Created 2016-06-10 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “HTMLReader” –https://github.com/nolanw/HTMLReader
–http://piyocast.com/as/archives/4324

–Parse a string and find an element
set aMarkUp to current application’s NSString’s stringWithString:

ぴよぴよ!ぴよー


set aDocument to current application’s HTMLDocument’s documentWithString:aMarkUp
set anArray to ((aDocument’s nodesMatchingSelector:“b”)’s textContent) as list
–>  {”ぴよぴよ!”, “ぴよー”}

–Wrap one element in another.
set b to aDocument’s firstNodeMatchingSelector:“b”
set childArray to b’s parentNode()’s mutableChildren()
set aWrapper to current application’s HTMLElement’s alloc()’s initWithTagName:“div” attributes:{|class|:“special”}
childArray’s insertObject:aWrapper atIndex:(childArray’s indexOfObject:b)
b’s setParentNode:aWrapper
set htmlRes to (aDocument’s rootElement’s serializedFragment()) as string
–>  ”

ぴよぴよ!

ぴよー

★Click Here to Open This Script 

AppleScript名:HTMLReaderでWeb上のHTMLを取得する
– Created 2016-11-21 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “HTMLReader” –https://github.com/nolanw/HTMLReader
–http://piyocast.com/as/archives/4324

set aStr to “https://github.com/nolanw/HTMLReader”
set aURL to (current application’s |NSURL|’s URLWithString:aStr)
set {exRes, headerRes, aData} to checkURLResourceExistence(aURL, 3) of me
if exRes = false then return –エラー発生時に処理打ち切り

–>  (NSDictionary) {Content-Encoding:”gzip”, X-Runtime:”0.074184″, Set-Cookie:”_gh_sess=eyJz…..”,…….. X-Frame-Options:”deny”, Content-Type:”text/html; charset=utf-8″, X-Content-Type-Options:”nosniff”, X-UA-Compatible:”IE=Edge,chrome=1″}

set conType to headerRes’s valueForKeyPath:“Content-Type”
–>  (NSString) “text/html; charset=utf-8″

set aHome to current application’s HTMLDocument’s documentWithData:aData contentTypeHeader:conType
set htmlSource to aHome’s rootElement()’s serializedFragment() –HTMLソース文字列

–(処理対象部分のみ掲載)
–>

set aDiv to aHome’s firstNodeMatchingSelector:“.repository-meta-content”
set aWhiteSpace to current application’s NSCharacterSet’s whitespaceAndNewlineCharacterSet()
set dRes to (aDiv’s textContent()’s stringByTrimmingCharactersInSet:aWhiteSpace) as string
–>  ”A WHATWG-compliant HTML parser in Objective-C.”

– 指定URLにファイル(画像など)が存在するかチェック
–> {存在確認結果(boolean), レスポンスヘッダー(NSDictionary), データ(NSData)}
on checkURLResourceExistence(aURL, timeOutSec as real)
  set aRequest to (current application’s NSURLRequest’s requestWithURL:aURL cachePolicy:(current application’s NSURLRequestUseProtocolCachePolicy) timeoutInterval:timeOutSec)
  
set aRes to (current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value))
  
set dRes to (first item of (aRes as list))
  
set bRes to (second item of (aRes as list))
  
if bRes is not equal to missing value then
    set hRes to (bRes’s allHeaderFields())
    
set aResCode to (bRes’s statusCode()) as integer
  else
    set hRes to {}
    
set aResCode to -1 –error
  end if
  
return {(aResCode = 200), hRes, dRes}
end checkURLResourceExistence

★Click Here to Open This Script 

2016/02/16 FBEncryptorで文字列の暗号化、復号化(ASOC版)

xcatsanさんが公開されている「AES128暗号化ライブラリ FBEncryptor」を少々書き変えて(ARC対応&フレームワーク化。単なる作業レベル)作ったFBEncryptorKitを呼び出して文字列の暗号化、復号化を行うAppleScriptです。

以前にXcode上のAppleScriptObjCでこれを呼び出すものを書いてみましたが、通常のScript Editorで利用できるようにした方が応用が効くため、Cocoa Frameworkとして別途ビルドしてみました。

OS X 10.10以降用にビルドしたバイナリを用意しましたので、試したい方は(自己責任で)~/Library/Frameworksフォルダに入れて呼び出してください。

–> Download Binary

AppleScript名:FBEncryptorで文字列の暗号化、復号化
– Created 2016-02-16 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “FBEncryptorKit” –https://github.com/dev5tec/FBEncryptor

set aStr to “ABCDEFあいうえお
かきくけこGHIJKLMN”

set aKey to “piyomaru”

–Encryption
set aEnc to current application’s FBEncryptorAES’s encryptBase64String:aStr keyString:aKey separateLines:true
–>  (NSString) “N0/E5FB97DY+qOFtfKK9CCsAMKznyej94Ons1lC90V/9vMJIaBw5R+mbaxaTm711″

–Decription
set aDec to current application’s FBEncryptorAES’s decryptBase64String:aEnc keyString:aKey
(*
(NSString) “ABCDEFあいうえお
かきくけこGHIJKLMN”
*)

★Click Here to Open This Script 

2015/11/19 度量衡の変換

オープンソースの「DDUnitConverter」(By Dave DeLong)を用いて、度量衡(measurement unit)の変換を行うAppleScriptです。AppleScriptから呼び出すために、DDUnitConverterをフレームワーク化した「ddUnitConversionKit」を作成しています。

もともと、AppleScriptには内蔵の度量衡変換機能があり、

AppleScript名:インチからセンチへの変換
set a to 1
set a to a as inches
–> inches 1.0

set b to a as centimeters
–> centimeters 2.54

set c to a as meters
–> meters 0.0254

set d to a as kilometers
–> kilometers 2.54E-5

★Click Here to Open This Script 

とか、

AppleScript名:液体の容積の単位変換
–液体の容積の単位変換
set a to 10 as liters
–> liters 10.0

set b to a as gallons
–> gallons 2.641720372842

set c to a as quarts
–> quarts 10.566881491367

★Click Here to Open This Script 

ぐらいは簡単にできます。AppleScriptがサポートしている度量衡は、長さ、重さ、温度、液体の容積です。

ただ、mmの下のμmとか、オングストロームとか、そういう単位には対応していませんし、重量でいえばKgに対応していてもt(トン)の単位はサポートしていなかったりと、日常的な度量衡の単位の世界でも不十分な感じが否めません(数値の有効桁数が10桁と小さいことも影響?)。

Cocoaの世界で度量衡変換機能について調べてみたところ、km→m→cm→mmといった変換はしてくれるものの、他の単位への変換は行ってくれていなかったりして、標準機能だけではいまひとつな印象です。

AppleScript名:NSLengthFormatterのじっけん
– Created 2015-11-18 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
–http://dev.classmethod.jp/references/ios8-new-formatter/

set aLengthFormatter to current application’s NSLengthFormatter’s alloc()’s init()
set aMeters to 5000
set aRes to (aLengthFormatter’s stringFromMeters:aMeters) as text
–>  "5 km"
set bRes to (aLengthFormatter’s stringFromMeters:500) as text
–>  "500 m"
set cRes to (aLengthFormatter’s stringFromMeters:0.5) as text
–>  "50 cm"
set dRes to (aLengthFormatter’s stringFromMeters:0.005) as text
–>  "5 mm"
set eRes to (aLengthFormatter’s stringFromMeters:("0.000005" as real)) as text
–>  "0.005 mm"

set res2 to aLengthFormatter’s getObjectValue:(missing value) forString:aRes errorDescription:(missing value)
–>  false

set res3 to (aLengthFormatter’s stringFromValue:aMeters unit:(current application’s NSLengthFormatterUnitMillimeter)) as text
–>  "5,000 mm"
set res4 to (aLengthFormatter’s stringFromValue:aMeters unit:(current application’s NSLengthFormatterUnitCentimeter)) as text
–>  "5,000 cm"
set res5 to (aLengthFormatter’s stringFromValue:aMeters unit:(current application’s NSLengthFormatterUnitMeter)) as text
–>  "5,000 m"
set res6 to (aLengthFormatter’s stringFromValue:aMeters unit:(current application’s NSLengthFormatterUnitKilometer)) as text
–>  "5,000 km"
set res7 to (aLengthFormatter’s stringFromValue:aMeters unit:(current application’s NSLengthFormatterUnitInch)) as text
–>  "5,000 in"
set res8 to (aLengthFormatter’s stringFromValue:aMeters unit:(current application’s NSLengthFormatterUnitFoot)) as text
–>  "5,000 ft"
set res9 to (aLengthFormatter’s stringFromValue:aMeters unit:(current application’s NSLengthFormatterUnitYard)) as text
–>  "5,000 yd"
set res10 to (aLengthFormatter’s stringFromValue:aMeters unit:(current application’s NSLengthFormatterUnitMile)) as text
–>  "5,000 mi"

★Click Here to Open This Script 

AppleScript名:NSMassFormatterのじっけん
– Created 2015-11-18 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
–http://dev.classmethod.jp/references/ios8-new-formatter/

set aMasFormatter to current application’s NSMassFormatter’s alloc()’s init()
set aKilloGrams to 60.0
set aRes to (aMasFormatter’s stringFromKilograms:aKilloGrams) as text
–>  "60 kg"
set a2Res to (aMasFormatter’s stringFromKilograms:(0.5)) as text
–>  "500 g"
set a3Res to (aMasFormatter’s stringFromKilograms:(5000000)) as text
–>  "5,000,000 kg"
set a4Res to (aMasFormatter’s stringFromKilograms:("0.0005" as real)) as text
–>  "0.5 g"

set bRes to (aMasFormatter’s stringFromValue:aKilloGrams unit:(current application’s NSMassFormatterUnitGram)) as text
–>  "60 g"
set cRes to (aMasFormatter’s stringFromValue:aKilloGrams unit:(current application’s NSMassFormatterUnitKilogram)) as text
–>  "60 kg"
set dRes to (aMasFormatter’s stringFromValue:aKilloGrams unit:(current application’s NSMassFormatterUnitOunce)) as text
–>  "60 oz"
set eRes to (aMasFormatter’s stringFromValue:aKilloGrams unit:(current application’s NSMassFormatterUnitPound)) as text
–>  "60 lb"
set fRes to (aMasFormatter’s stringFromValue:aKilloGrams unit:(current application’s NSMassFormatterUnitStone)) as text
–>  "60 st"

set gRes to (aMasFormatter’s unitStringFromValue:aKilloGrams unit:(current application’s NSMassFormatterUnitGram)) as text
–>  "g"
set hRes to (aMasFormatter’s unitStringFromValue:aKilloGrams unit:(current application’s NSMassFormatterUnitKilogram)) as text
–>  "kg"
set iRes to (aMasFormatter’s unitStringFromValue:aKilloGrams unit:(current application’s NSMassFormatterUnitOunce)) as text
–>  "oz"
set jRes to (aMasFormatter’s unitStringFromValue:aKilloGrams unit:(current application’s NSMassFormatterUnitPound)) as text
–>  "lb"
set kRes to (aMasFormatter’s unitStringFromValue:aKilloGrams unit:(current application’s NSMassFormatterUnitStone)) as text
–>  "st"

set res01 to aMasFormatter’s getObjectValue:(missing value) forString:fRes errorDescription:(missing value)
–>  false

set res02 to (aMasFormatter’s stringFromKilograms:80) as text
–>  "80 kg"

set res03 to aMasFormatter’s unitStyle()
–>  2 = NSFormattingUnitStyleMedium

★Click Here to Open This Script 

そこで、冒頭に紹介したようにDDUnitConverterを導入してみたわけですが、これはこれで問題がないわけでもありません。本来、通貨同士の変換をサポートしており、通貨レートの更新機能を持っているのですが、実際に呼び出してみると更新されていない雰囲気が濃厚です(呼び出し方を間違っているのか?)。

なので、「通貨レート以外」の度量衡変換にのみ用いるのが安全な使い方なのかもしれない、というところです。

例によって、OS X 10.10以降用にビルドしたバイナリを用意しておきましたので、興味のある方はアーカイブを展開したあとで、~/Library/Frameworksに入れておためしください。

–> Download ddUnitConversionKit.framework binary

AppleScript名:DDUnitConverterのじっけん1
– Created 2015-11-19 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ddUnitConversionKit” –davedelong/DDUnitConverter
–https://github.com/davedelong/DDUnitConverter

–enums in DDVelocityUnitConverter.h
property DDVelocityUnitCentimetersPerHour : 0
property DDVelocityUnitCentimetersPerMinute : 1
property DDVelocityUnitCentimetersPerSecond : 2
property DDVelocityUnitFeetPerHour : 3
property DDVelocityUnitFeetPerMinute : 4
property DDVelocityUnitFeetPerSecond : 5
property DDVelocityUnitInchesPerHour : 6
property DDVelocityUnitInchesPerMinute : 7
property DDVelocityUnitInchesPerSecond : 8
property DDVelocityUnitKilometersPerHour : 9
property DDVelocityUnitKilometersPerMinute : 10
property DDVelocityUnitKilometersPerSecond : 11
property DDVelocityUnitKnots : 12
property DDVelocityUnitLight : 13
property DDVelocityUnitMach : 14
property DDVelocityUnitMetersPerHour : 15
property DDVelocityUnitMetersPerMinute : 16
property DDVelocityUnitMetersPerSecond : 17
property DDVelocityUnitMilesPerHour : 18
property DDVelocityUnitMilesPerMinute : 19
property DDVelocityUnitMilesPerSecond : 20
property DDVelocityUnitFurlongsPerFortnight : 21

–時速100kmを秒速kmに変換
set aVal to ((current application’s DDUnitConverter’s velocityUnitConverter())’s convertNumber:100 fromUnit:(DDVelocityUnitKilometersPerHour) toUnit:(DDVelocityUnitKilometersPerSecond)) as real
–>  0.027777777778

★Click Here to Open This Script 

AppleScript名:DDCurrencyUnitConverterのじっけん
– Created 2015-11-19 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ddUnitConversionKit” –davedelong/DDUnitConverter
–https://github.com/davedelong/DDUnitConverter

–enums in DDCurrencyUnitConverter.h
property DDCurrencyUnitEuro : 0
property DDCurrencyUnitJapaneseYen : 1
property DDCurrencyUnitUKPoundSterling : 2
property DDCurrencyUnitUSDollar : 3
property DDCurrencyUnitAlgerianDinar : 4
property DDCurrencyUnitArgentinePeso : 5
property DDCurrencyUnitAustralianDollar : 6
property DDCurrencyUnitBahrainDinar : 7
property DDCurrencyUnitBotswanaPula : 8
property DDCurrencyUnitBrazilianReal : 9
property DDCurrencyUnitBruneiDollar : 10
property DDCurrencyUnitCanadianDollar : 11
property DDCurrencyUnitChileanPeso : 12
property DDCurrencyUnitChineseYuan : 13
property DDCurrencyUnitColombianPeso : 14
property DDCurrencyUnitCzechKoruna : 15
property DDCurrencyUnitDanishKrone : 16
property DDCurrencyUnitHungarianForint : 17
property DDCurrencyUnitIcelandicKrona : 18
property DDCurrencyUnitIndianRupee : 19
property DDCurrencyUnitIndonesianRupiah : 20
property DDCurrencyUnitIranianRial : 21
property DDCurrencyUnitIsraeliNewSheqel : 22
property DDCurrencyUnitKazakhstaniTenge : 23
property DDCurrencyUnitKoreanWon : 24
property DDCurrencyUnitKuwaitiDinar : 25
property DDCurrencyUnitLibyanDinar : 26
property DDCurrencyUnitMalaysianRinggit : 27
property DDCurrencyUnitMauritianRupee : 28
property DDCurrencyUnitMexicanPeso : 29
property DDCurrencyUnitNepaleseRupee : 30
property DDCurrencyUnitNewZealandDollar : 31
property DDCurrencyUnitNorwegianKrone : 32
property DDCurrencyUnitRialOmani : 33
property DDCurrencyUnitPakistaniRupee : 34
property DDCurrencyUnitNuevoSol : 35
property DDCurrencyUnitPhilippinePeso : 36
property DDCurrencyUnitPolishZloty : 37
property DDCurrencyUnitQatarRiyal : 38
property DDCurrencyUnitRussianRuble : 39
property DDCurrencyUnitSaudiArabianRiyal : 40
property DDCurrencyUnitSingaporeDollar : 41
property DDCurrencyUnitSouthAfricanRand : 42
property DDCurrencyUnitSriLankaRupee : 43
property DDCurrencyUnitSwedishKrona : 44
property DDCurrencyUnitSwissFranc : 45
property DDCurrencyUnitThaiBaht : 46
property DDCurrencyUnitTrinidadAndTobagoDollar : 47
property DDCurrencyUnitTunisianDinar : 48
property DDCurrencyUnitUAEDirham : 49
property DDCurrencyUnitPesoUruguayo : 50
property DDCurrencyUnitBolivarFuerte : 51
property DDCurrencyUnitSDR : 52

–最初に通貨レートの更新を行う必要があるが、実行しても変わらないのはなぜ????
set aConv to current application’s DDUnitConverter’s currencyUnitConverter()
aConv’s refreshExchangeRates()
set aVal to (aConv’s convertNumber:100 fromUnit:(DDCurrencyUnitUSDollar) toUnit:(DDCurrencyUnitJapaneseYen))
–>  (NSNumber) 1.006839721743

★Click Here to Open This Script 

2015/11/13 MIKMIDIでMIDIデバイス一覧を取得

MIDIで接続されたデバイス一覧を取得するAppleScriptです。

まず、MIDIデバイスとMacの接続を行うわけですが、これについては安価なUSB-MIDI I/Fが市販されているため、こうしたものを利用するのが手軽でよいでしょう。自分はRolandのUM-ONEを試してみました。

top_l_resized.png

UM-Oneは専用のデバイスドライバをインストールすれば使えるようになります。

次に、AppleScriptからMIDIにアクセスする方法を模索することになります。現時点では3つの方法を見つけています。

1つめは、Apple純正のCoreMIDIを利用する方法。CのAPIなのでAppleScript-Cocoa Bridgeには手に負えない箇所が出てきそうですし、仕様を読んでいると何から何まで自分で記述しないといけないようなので、ちょっとウンザリします。

2つめは、オープンソースのフレームワーク「MIDIKit」(By Randy Weinstein)を利用する方法。いろいろAppleScriptからこづき回してみたところ、接続確認やMIDIデバイスのオンライン状態は調べられたものの、それ以上のことができません。たとえば、MIDIデバイスの名称(name)をAppleScriptから取得しようとするとすべてmissing valueが返ってくるなど、相性の悪さを感じました(日本語環境なのでなおさら未検証だったりするのかも)。

3つめが、今回紹介する同じくオープンソースのフレームワーク「MIKMIDI」(By Andrew Madsen)を利用する方法です。

midistudio.png
▲実験時の機器構成をOS X標準装備の「Audio MIDI設定」アプリで確認したところ。IAC Driverに「GarageBand」という名前のポートを宣言している。

自分がいろいろ試した中では、MIKMIDIが一番各種情報にアクセスしやすい印象を持っています。まだ、MIDI機器の接続状態を調べたぐらいなので、指定のデバイスにメッセージを送ったりできているレベルではありませんが、いろいろ調べてみるとできそうな感じがします。

img_3175.jpg
▲LAN経由でMIDI音源(Sound Canvas for iOS)をつないでみた。まだ、AppleScriptからメッセージを送れてはいない状況

AppleScript名:MIKMIDIでデバイス一覧を取得
– Created 2015-11-13 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “MIKMIDI” –mixedinkey-opensource/MIKMIDI
–https://github.com/mixedinkey-opensource/MIKMIDI

set availableMIDIDevices to current application’s MIKMIDIDeviceManager’s sharedDeviceManager()’s availableDevices()
(*
–>  (NSArray) {(MIKMIDIDevice) <MIKMIDIDevice: 0×7f8a5e972a80> IAC Driver:
Entities: {
GarageBand:
Sources: {
<MIKMIDISourceEndpoint: 0×7f8a63a65610> IAC Driver GarageBand,
}
Destinations: {
<MIKMIDIDestinationEndpoint: 0×7f8a61bbc130> IAC Driver GarageBand,
},
}, (MIKMIDIDevice) <MIKMIDIDevice: 0×7f8a5e8d4320>ネットワーク:
Entities: {
}, (MIKMIDIDevice) <MIKMIDIDevice: 0×7f8a638a5fc0> Bluetooth:
Entities: {
}, (MIKMIDIDevice) <MIKMIDIDevice: 0×7f8a59e16360> UM-ONE:
Entities: {
<MIKMIDIEntity: 0×7f8a59ed8990> UM-ONE:
Sources: {
<MIKMIDISourceEndpoint: 0×7f8a61d47950> 新しい外部装置,
}
Destinations: {
<MIKMIDIDestinationEndpoint: 0×7f8a5ecadcf0> 新しい外部装置,
},
}}
*)

set aCount to availableMIDIDevices’s |count|()
–> 4

set availableVDevices to current application’s MIKMIDIDeviceManager’s sharedDeviceManager()’s virtualSources()
–>  (NSArray) {(MIKMIDISourceEndpoint) IAC Driver GarageBand, (MIKMIDISourceEndpoint) 新しい外部装置}

★Click Here to Open This Script 

2015/11/09 ムービーからオーディオのみ抽出してm4aで保存

Cocoaの機能を用いて、指定のQuickTimeムービーから音声のみ抽出してM4A(MPEG-4 Audio)で保存して指定のメタデータを付与するAppleScriptです。

Alex Nichol氏の「MP4Audio」をFramework化した「mp4AudioExportKit」を作って、それをAppleScriptから呼び出しています。

ただ、ビルド時に「それはもうdeprecatedだよー」というwarningが大量に出ているので、OS X 10.12以降で使えるかどうかは未知数です。

meta2.png
▲出力した音声データ(audiotest1.m4a)のmetadataをTerminal.app上で確認したところ。たしかに指定のデータが入っている

本Scriptでは、常識的な処理をひととおり試しているだけです。Sampling Rateを変更したりといったことは行っていません。

例によって、OS X 10.10以降用にビルドしたバイナリをあげておきますので、興味のある方は、アーカイブを展開して~/Library/Frameworksに入れてお試しください。

→ Download mp4AudioExportKit.framework

AppleScript名:MP4Audioのじっけん(ムービーからオーディオのみ抽出してM4Aで保存)
– Created 2015-11-09 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “mp4AudioExportKit” –unixpickle/MP4Audio
–https://github.com/unixpickle/MP4Audio

–Open Original Movie
set aFile to POSIX path of (choose file of type {“com.apple.quicktime-movie”} with prompt “Choose a QuickTime movie to export as MP4 audio”)
set aPath to current application’s NSString’s stringWithString:aFile
set aMovie to current application’s ANMovie’s alloc()’s initWithFile:aPath

–Select Export File Name
set bFile to POSIX path of (choose file name with prompt “Input Export file name”)
set bPath to current application’s NSString’s stringWithString:bFile
if (bPath’s hasSuffix:“.m4a”) as boolean = false then
  set bPath to bPath’s appendString:“.m4a”
end if

–Export as MP4 audio
with timeout of 3600 seconds
  aMovie’s exportAACToFile:bPath
  
aMovie’s |close|()
end timeout

–Add Metadata
set aMetaData to current application’s ANMetadata’s alloc()’s init()
aMetaData’s setTitle:“戦場の絆ポータブル リプレイムービーNY音声編(1)”
aMetaData’s setAlbum:“戦場の絆ポータブル”
aMetaData’s setYear:“2015″
aMetaData’s setArtist:“Bandai Namco Entertainment”
aMetaData’s setTrackNumber:(current application’s ANMetadataTrack’s alloc()’s initWithTrackNumber:5 tracks:12)

set bMovie to current application’s ANMovie’s alloc()’s initWithFile:bPath
bMovie’s setMovieMetadata:aMetaData
bMovie’s |close|()

★Click Here to Open This Script 

2015/10/24 シリアルコードの生成と検証

オープンソースのSerialKeyGeneratorをもとに作成した「serialKeyGenKit」フレームワークを用いて、シリアルコードの生成(Generation)と検証(Validation)を行うAppleScriptです。

ソフトウェアの販売時などに、シリアルコードを発行することがあります。Mac App Storeのソフトウェア販売ではあまり見られませんが(自社ストア販売バージョンからMac App Store販売バージョンへのアップグレードでOmniGraffleあたりで見かけました)、Mac App Store以外でオンライン販売するような場合には必要になってきます。

キーの重複がなく、偽造されにくく、必要な情報を含んでいるようなシリアルコード体系が理想的です。しかも、使用する文字はA〜Z、0〜9の文字だけで、「XXXXX-XXXXX-XXXXX-XXXXX-XXXXX」のフォーマットで・・・・

そんなシリアルコードを手軽かつ高速に生成できるのがSerialKeyGeneratorです。シークレットキーをもとに乱数要素や日時の要素を勘案したシリアルコードを生成してくれます。

仕事で何度かシリアルコードの作成を行ったことがありますが、なかなかこれが大変でした。「SerialKeyGenerator」は非常に簡単なシリアルコードを生成するためのものですが、シリアルコードによって動作許可する機能を作ったり、指定日時まで動作許可するコードだったりと・・・それだけで相当の手間になりました。

また、流失したシリアルコードについてはオンラインで失効手続きを行えるようにしたりと、本気で作り出すと「ソフトウェア本体よりもでかい処理」になったりします(頭痛が、、、)。

例によって、serialKeyGenKit.frameworkをOS X 10.10以降用にビルドしておきましたので、興味のある方は~/Library/Frameworksフォルダに入れてお試しください。

–> Download Framework Binary

AppleScript名:SerialKeyGeneratorサンプル1
– Created 2015-10-24 18:27:16 +0900 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “serialKeyGenKit” –danielvy/SerialKeyGenerator
–https://github.com/danielvy/SerialKeyGenerator

set aGen to current application’s SerialKeyGenerator’s alloc()’s initWithSecret:“Piyomaru”
–>  (SerialKeyGenerator)

set aList to {}
repeat with i from 1 to 10
  set aRes to (aGen’s generateSerialWithSequence:i |data|:10)
  
set the end of aList to (aRes as text)
end repeat

aList
–>  {”1CHH8-BJY89-TR9Z9-AKSTL-70JZF”, “4KXCF-LS6P5-7BKYN-CUMPY-NM072″, “41GO2-OGZ3T-83YP9-2LS4J-9W3MI”, “CA3WV-C7FU7-PSPJ3-LNGT7-N6AJT”, “3OG0B-MCFUM-7YKHT-EE533-LNJEJ”, “4SKWQ-9G8XE-ZCE6K-APSAL-R0T8S”, “CTEZT-H4AQQ-BM15H-FH2DG-P7K7O”, “4BZ1X-2OHKF-5FWND-7SZVA-EBR89″, “BDKRI-9JU7T-G6N20-YPU9K-6FXQW”, “9EWW5-O3FMV-SDEZA-77JZE-OY8FL”}

★Click Here to Open This Script 

AppleScript名:SerialKeyGeneratorサンプル2
– Created 2015-10-24 18:27:16 +0900 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “serialKeyGenKit” –danielvy/SerialKeyGenerator
–https://github.com/danielvy/SerialKeyGenerator

–シリアルコードの生成(エンコード)

–シークレットキー「Piyomaru」をもとにシリアルコードジェネレータを生成
set aGen to current application’s SerialKeyGenerator’s alloc()’s initWithSecret:“Piyomaru”
–>  (SerialKeyGenerator)

–指定個数のシリアルコードを生成。生成するたびに毎回シリアルコードは異なる
set anArray to (aGen’s generateSerials:10 start:1 |data|:10) as list
–>  {”0YR0Q-Y5FZP-A25EI-TK3YI-78L5U”, “7VZSX-S87LJ-812L7-A2XC6-Q9DBQ”, “C3HYG-H4HPP-FPP6A-1PH1L-MVVF0″, “B7FHO-AQ4E0-Y0AQG-YYFVX-XSEWA”, “0C4GP-M46PY-BD7J9-878SG-FSZP3″, “5OLK7-C3R3R-IVKX5-7FUIT-GJA07″, “2KA8T-PFIC4-PII60-QBRWN-ZPJWE”, “325M7-8KR60-2X1G8-EF7OA-UP06I”, “CIH21-H6H1O-31AA4-12ZSD-0ZBT3″, “8NA54-0W8R5-0VI34-X1L5F-5E0GS”}

–シリアルコードの検証(デコード)

–シークレットキー「Piyomaru」をもとにシリアルコードジェネレータを生成
set bGen to current application’s SerialKeyGenerator’s alloc()’s initWithSecret:“Piyomaru”

set bRes to bGen’s decodeSerial:(contents of first item of anArray)

set aDate to bRes’s |date| as date
–>  date “2015年10月24日土曜日 18:37:25″
set aSeq to bRes’s sequence as integer
–>  1
set aData to bRes’s |data| as integer
–>  10

★Click Here to Open This Script 

2015/10/23 RFKeychain経由でKeychainアクセス

オープンソースのRFKeychain(By Rheinfabrik)をframework化したもの(rfKeychainKit.framework)を呼び出して、キーチェーンへのアクセスを行うAppleScriptです。

Keychainについては、かつてはKeychain AccessというAppleScript専用のヘルパーアプリが存在していましたが、いまはshell commandの「security」が用意されているので、そちら経由でアクセスしてくれという状況です。

OS X標準搭載のそれらヘルパーアプリのメンテナンス状況は正直言って期待できないので、AppleScriptがScriptring Bridge対応したタイミングで、Apple純正ヘルパーアプリ群への依存度を下げるための布石が必要だと思っていました。

たまたま見つけたRFKeychainですが、正直機能が足りているとも思えないので(すでに登録されているキーチェーン項目を検索するような機能がない)、ほかにもいろいろ探してみたほうがよさそうです。

例によって、フレームワークをOS X 10.10以降用にビルドしたものを用意しておきました。興味のある方は~/Library/Frameworksフォルダに入れてためしてみてください。

→ Download Framework Binary

AppleScript名:ASOCでRFKeychain経由でKeychainアクセス
– Created 2015-10-23 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “rfKeychainKit” –rheinfabrik/RFKeychain
–https://github.com/rheinfabrik/RFKeychain

–Register Password
set aRes to (current application’s RFKeychain’s setPassword:“piyopiyo” account:“piyomaru” service:“piyocast.com” accessGroup:“other”) as boolean
–> true

–Get Password
set bRes to (current application’s RFKeychain’s passwordForAccount:“piyomaru” service:“piyocast.com”) as text
–>  ”piyopiyo”

–Delete Password
set cRes to (current application’s RFKeychain’s deletePasswordForAccount:“piyomaru” service:“piyocast.com” accessGroup:“other”) as boolean
–>  true

★Click Here to Open This Script 

2015/10/22 辞書.appの指定名称の辞書でキーワード検索

オープンソースのプログラムDictionaryKit(By Mattt Thompson)をフレームワークに入れた「dictKit」を介して、OS X内蔵の辞書でキーワード検索を行うAppleScriptです。

DictionaryKitの内部機能を直接AppleScriptから呼ぶことはできませんが、DictionaryKitのプログラムを突っ込んだFramework「dictKit.framework」を作成し、これをインストールしてAppleScriptから呼べるようにしました。

注意点:DictionaryKitの説明にもあるのですが、OS XのPrivate APIを呼んでいるため、本プログラム(dictKit)を用いて作ったアプリケーションをMac App Store向けに申請することはできません。

でも、超〜便利なんで(^ー^;;; 日常的なScriptで使わない手はありません。

例によって、dictKitのバイナリをOS X 10.10以降用にビルドしておきましたので、興味のある方は~/Library/Frameworksフォルダに入れておためしください。

–> Download Framework Binary

AppleScript名:ASOCで辞書.appで検索可能な辞書名称一覧を取得する
– Created 2015-10-22 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “dictKit” –mattt/DictionaryKit
–https://github.com/mattt/DictionaryKit

set dSet to current application’s TTTDictionary’s availableDictionaries()
set dList to dSet’s allObjects()
set dNameList to {}
repeat with i in dList
  set the end of dNameList to (i’s |name|()) as text
end repeat
dNameList
–>  {”뉴에이스 영한사전 / 뉴에이스 한영사전“, “Apple 用語辞典”, “Multidictionnaire de la langue française”, “राजपाल हिन्दी शब्दकोश“, “Dizionario italiano da un affiliato di Oxford University Press”, “Oxford-Hachette French Dictionary”, “NE Ordbok”, “牛津英汉汉典”, “Oxford Thesaurus of English”, “スーパー大辞林”, “Oxford Dictionary of English”, “Oxford American Writer’s Thesaurus”, “Norsk Ordbok”, “Gran Diccionario Oxford - Español-Inglés Inglés-Español”, “Wikipedia”, “Duden-Wissensnetz deutsche Sprache”, “Толковый словарь русского языка”, “뉴에이스 국어사전“, “Prisma woordenboek Nederlands”, “New Oxford American Dictionary”, “Dicionário de Português licenciado para Oxford University Press”, “Oxford German Dictionary”, “Diccionario General de la Lengua Española Vox”, “ウィズダム英和辞典 / ウィズダム和英辞典”, “Arkadaş Türkçe Sözlük”, “พจนานุกรมไทย ฉบับทันสมัยและสมบูรณ์“, “汉语规典”}

★Click Here to Open This Script 

AppleScript名:ASOCで辞書検索じっけん
– Created 2015-10-22 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “dictKit” –mattt/DictionaryKit
–https://github.com/mattt/DictionaryKit

set aDictionary to current application’s TTTDictionary’s dictionaryNamed:“Apple 用語辞典”
set dRes to aDictionary’s |name|()
set dRes to dRes as text
–>  ”Apple 用語辞典”

set aTerm to “AppleScript”
set hitEntryList to (aDictionary’s entriesForSearchTerm:aTerm) as list
if hitEntryList = {missing value} then return “” –ヒットしなかった場合

repeat with i in hitEntryList
  set j to contents of i
  
  
set headW to (j’s headword)
  
set headW to headW as text
  
–>  ”AppleScript”
  
  
set aText to (j’s |text|)
  
set aText to aText as text
  
(*)
  –>  ”AppleScript
OS X に内蔵されたスクリプト言語です。AppleScript 言語のコマンドを使用すれば、“メール”、Safari、“カレンダー”など、さまざまなアプリケーションで繰り返しの作業や複雑な作業を自動化できます。
*)

  
end repeat

★Click Here to Open This Script 

2015/10/21 ステガノグラフィーを用いて画像に情報を埋め込む

オープンソースのステガノグラフィーのプログラム「ISStego」(By Isaac Stevao Sena氏)を用いて、PNG画像に文字情報を埋め込む/取り出すAppleScriptです。

ISStegoは普通にObjective-Cで書かれたGUIベースのアプリケーションだったので、そのままではAppleScriptから呼び出せませんでした。

そこで、中身をそのままそっくり移し替えた新規フレームワーク「stegLib.framework」をでっちあげてビルドし、AppleScriptから呼び出してみました(フレームワークをはじめて作ってみましたが、えっらい簡単で驚きました)。

PNG画像にUTF-8の文字情報(日本語文字列)を埋め込んで別のPNG画像に書き出し、書き出した画像からUTF-8の文字情報を取り出す実験を行ってみました。エンコードもデコードもうまく行っているようなので、うまく処理できていると思います。

ステガノグラフィーについて初めて聞いたのは20年ぐらい前のことと記憶していますが、こんなに手軽に使えるようになっていたとは驚きです。

Twitterにプログラムを投稿するのに、(140文字制限を回避するため)文字を画像化して投稿しているのを見て、「そこまでやるなら、画像にプログラムの文字データを埋め込めばいいのに」と思い、「ステガノグラフィーで埋め込めばいいんじゃないか?」ということで、埋め込めるようになったのですが、肝心のTwitterクライアントから画像をダウンロードする手段がなかったのがダメダメでした(Webブラウザ経由ならOKです)。

custf11.png
▲ステガノグラフィー処理前の画像

custf1_stego.png
▲ステガノグラフィーによって文字情報を埋め込んだ画像(本当に埋め込んであります^ー^;)

stegLib.framework(ISStegoをFramework化したもの)のバイナリを例によってOS X 10.10以降用にビルドしておきましたので、興味のある方は~/Library/Frameworksに入れて使ってみてください。

–> Download Framework

AppleScript名:ASOCで画像にステガノグラフィーで情報を埋め込む
– Created 2015-10-21 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “stegLib” –ISStegoをフレームワーク化
–https://github.com/isena/ISStego

set aFile to POSIX path of (choose file of type {“public.image”})
set encString to “長野谷隆昌/ぴよまるソフトウェア/Piyomaru Software”

set aFilePath to current application’s NSString’s stringWithString:aFile
set aExt to aFilePath’s pathExtension()

set newPath to aFilePath’s stringByDeletingPathExtension()
set newPath2 to newPath’s stringByAppendingString:“_stego”
set newPath3 to newPath2’s stringByAppendingPathExtension:aExt

set aURL to current application’s |NSURL|’s fileURLWithPath:aFilePath
set aImage to current application’s NSImage’s alloc()’s initWithContentsOfURL:aURL
set strData to current application’s NSString’s stringWithString:encString

set aEncimage to current application’s ISStegoEncoder’s alloc()’s init()’s stegoImageForImage:aImage |data|:strData |error|:(missing value)
my saveImageRepAtPathAsPNG(aEncimage, newPath3)

–画像を指定パスにPNG形式で保存
on saveImageRepAtPathAsPNG(anImage, outPath)
  
  
–画像のRaw画像を作成
  
set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to current application’s NSBitmapImageRep’s imageRepWithData:imageRep
  
  
–書き出しファイルパス情報を作成
  
set pathString to current application’s NSString’s stringWithString:outPath
  
set newPath to pathString’s stringByExpandingTildeInPath()
  
  
–書き出し
  
set myNewImageData to (aRawimg’s representationUsingType:(current application’s NSPNGFileType) |properties|:(missing value))
  
set aRes to (myNewImageData’s writeToFile:newPath atomically:true) as boolean
  
  
return aRes –成功ならtrue、失敗ならfalseが返る
  
end saveImageRepAtPathAsPNG

★Click Here to Open This Script 

AppleScript名:ASOCで画像にステガノグラフィーで埋め込まれた文字列を取り出す
– Created 2015-10-21 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “stegLib” –ISStegoをフレームワーク化
–https://github.com/isena/ISStego

set aFile to POSIX path of (choose file of type {“public.image”})
set aFilePath to current application’s NSString’s stringWithString:aFile
set aURL to current application’s |NSURL|’s fileURLWithPath:aFilePath
set aImage to current application’s NSImage’s alloc()’s initWithContentsOfURL:aURL

set aDecodedData to current application’s ISStegoDecoder’s alloc()’s init()’s decodeStegoImage:aImage |error|:(missing value)
set resStr to current application’s NSString’s alloc()’s initWithData:aDecodedData encoding:(current application’s NSUTF8StringEncoding)
–>  (NSString) “長野谷隆昌/ぴよまるソフトウェア/Piyomaru Software”

★Click Here to Open This Script 

2015/10/11 ZipZap frameworkを使ってZipアーカイブ内の情報を取得

オープンソースのZipZapフレームワーク(By Pixelglow Software)を利用して、選択したZipアーカイブ中の情報を取得するAppleScriptです。

たまたまCocoa dev MLの過去ログを漁っていたら、ZipZapフレームワークを見つけました。GitHubのサイトはキーワード検索がいまひとつで、以前に検索したときにはひっかかってこなかったものです(ひととおり、めぼしいのは調べたので)。

それはともかく、GitHubのサイトからClone in DesktopでMacにXcodeプロジェクトをクローンして、Xcode 7.0.1でプロジェクトをビルド。生成したフレームワークをAppleScriptから呼び出すテストを行ってみたものです。

ほぼ、READMEに書いてある通り書いてそのまま動きました。ヘッダーファイルに書いてあるいくつかのプロパティも試してみたところ納得の結果が(READMEどおりに書いて動かないものも、他にいろいろみかけますが・・・本プロジェクトは実に素直でした)。

さまざまなプロパティをAppleScriptの型にcastしてログ表示しているため、OS X 10.11用(AppleScript version 2.5が必要)になっていますが、castしたりlog表示しなければOS X 10.10でも動きます(安全のために10.11専用に)。

例によって、OS X 10.10以降用にビルドしたZipZapフレームワークを置いておきます。興味のある方はお試しください(~/Library/Frameworks/にインストール)。

–> Download Framework Binary (OS X 10.10 or later)

AppleScript名:ASOCでZipZap frameworkを使ってZipアーカイブ内の情報を取得
– Created 2015-10-11 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
use framework “ZipZap”
–https://github.com/pixelglow/zipzap

set aPath to POSIX path of (choose file of type {“public.zip-archive”})
set oldArchive to current application’s ZZArchive’s archiveWithURL:(current application’s |NSURL|’s fileURLWithPath:aPath) |error|:(missing value)
–>  (ZZArchive)

set entryCount to oldArchive’s entries()’s |count|()
–>  180 –this zip archive includes 180 file or folders inside

set firstArchEntry to oldArchive’s entries()’s firstObject()
–>  (ZZOldArchiveEntry)

set aList to oldArchive’s entries() as list

repeat with i in aList
  set uSize to i’s uncompressedSize() as real
  
set cSize to i’s compressedSize() as real
  
set comF to i’s compressed() as boolean
  
set encF to i’s encrypted() as boolean
  
set modD to i’s lastModified() as date
  
set chkSum to i’s crc32() as text –The CRC32 code of the entry file: 0 for new entries.
  
set aFileMode to i’s fileMode() as text – The UNIX file mode for the entry: 0 for new or non-UNIX entries. This includes the file type bits.
  
set aFileName to i’s fileName() as text
  
  
log {aFileName, aFileMode, chkSum, modD, encF, comF, cSize, uSize}
  
–> 21:28:01.817 (* {”ZXingObjC.framework/”, “16877d”, “0″, date “2015年10月10日土曜日 10:07:16″, false, false, 0.0, 0.0} *)
  
–> 21:28:01.818 (* {”ZXingObjC.framework/Headers”, “41453d”, “2.393740531E+9″, date “2015年10月10日土曜日 10:07:10″, false, false, 24.0, 24.0} *)
  
–> 21:28:01.820 (* {”ZXingObjC.framework/Versions/”, “16877d”, “0″, date “2015年10月10日土曜日 10:07:10″, false, false, 0.0, 0.0} *)
  
–> 21:28:01.821 (* {”ZXingObjC.framework/Versions/3.1.0/”, “16877d”, “0″, date “2015年10月10日土曜日 10:07:16″, false, false, 0.0, 0.0} *)
  
–> 21:28:01.822 (* {”ZXingObjC.framework/Versions/3.1.0/Headers/”, “16877d”, “0″, date “2015年10月10日土曜日 10:07:16″, false, false, 0.0, 0.0} *)
  
  
–set aData to (i’s newDataWithError:false)–Uncompressed raw data
  
–log aData
end repeat

★Click Here to Open This Script 

2015/10/08 Excelデータを組み立てて書き出し(フォント指定、カラー指定)

オープンソースのExcelデータ書き出しフレームワーク「JXLS」(By David Hoerl)を利用して、Excelのない環境でもExcelデータを作成するAppleScriptです。

デフォルトのままではあまりにも味気なかったので、フォント名、フォントサイズ、カラーを指定してみたものです。

サンプルがほとんど存在していないので手探り状態でしたが、なんとかなりました。

OS XのFinder上でもXLSファイルのプレビュー機能があるため、類似のフレームワークはOS X自体に搭載していそうです(というより、機能があるからプレビューできているんでしょう)。

xls.png

AppleScript名:ASOCでExcelファイル生成テスト v2
– Created 2015-10-08 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “JXLS”

set aFile to POSIX path of (choose file name)
set filePath to current application’s NSString’s stringWithString:aFile

set workBook to current application’s JXLSWorkBook’s new()
set workSheet to workBook’s workSheetWithName:“ぴよぴよシート”
workSheet’s setWidth:1000 forColumn:0 defaultFormat:(missing value)

repeat with i from 0 to 64
  set aCell to (workSheet’s setCellAtRow:i column:0 toString:(current application’s NSString’s stringWithString:(“ぴよまる “ & (i as text))))
  (
aCell’s setFontName:“HiraKakuStd-W8″)
  (
aCell’s setFontHeight:320) –this is point * 20
  (
aCell’s setFontColorIndex:i)
end repeat

workBook’s writeToFile:filePath

★Click Here to Open This Script 

2015/10/08 Excelデータを組み立てて書き出し

オープンソースのExcelデータ書き出しフレームワーク「JXLS」(By David Hoerl)を利用して、Excelのない環境でもExcelデータを作成するAppleScriptです。

正直、Excelデータを書き出せるNumbers.appがあるので、そこまで頑張らなくてもよさそうな気配がしていますが、たまたま「Excelデータを読むフレームワーク」を探していたら見つけたものです(読み込む方は、iOS用のみだったという)。

無人島に流されてExcelがない極限環境であっても、AppleScriptだけでExcelデータを生成できます。

何も考えずに、サンプル中に書かれていたObjective-CのプログラムをAppleScriptで再現してみたものですが、出来上がったExcel書類(のフォント)がちょっと気持ち悪いです。フォントについては、しかるべきもの(ヒラギノとか)を指定する必要性を感じます。

excel1.png

OS X 10.10以降用にビルドしたJXLSフレームワークを置いておきます。興味のある方はお試しください(~/Library/Frameworks/にインストール)。

→ Download Framework (Binary)

AppleScript名:ASOCでExcelファイル生成テスト
– Created 2015-10-08 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “JXLS”

set aFile to POSIX path of (choose file name)
set filePath to current application’s NSString’s stringWithString:aFile

set workBook to current application’s JXLSWorkBook’s new()

set workSheet to workBook’s workSheetWithName:“ぴよぴよシート”
workSheet’s setWidth:1000 forColumn:0 defaultFormat:(missing value)

repeat with i from 0 to 10
  set aCell to (workSheet’s setCellAtRow:i column:0 toString:(current application’s NSString’s stringWithFormat_(“ぴよまる %d”, i + 1)))
  (
aCell’s setHorizontalAlignment:1) —-HALIGN_LEFT
  (
aCell’s setIndentation:(0 + i)) —– INDENT_0
end repeat

workBook’s writeToFile:filePath

★Click Here to Open This Script 

2015/10/06 XML-RPCのテスト

オープンソースのXML-RPCフレームワークを利用して、Web上のXML-RPCのサービスを呼び出すAppleScriptです。

AppleScriptにはcall soapやcall xmlrpc命令などが標準で実装されており、割と便利に使っています。

ただし、複雑な処理を行った場合に問題が見られました。たとえばWordPressに対して新規記事作成をリクエストした場合に、call xmlrpc命令自体が実行途中で100%クラッシュするなど、信頼性については疑問符がつき、安定性については「出たとこ勝負」なヤケッパチ感が漂っておりました。

AppleScriptの処理系がSOAPやXML-RPCの命令を実装したのは比較的早く、Mac OS X登場後間もないタイミングであったと記憶しています。ただし、この「早すぎた登場」にのちのち苦しめられることになります。

のちにAppleがOS XのIntel移行+64bitへの移行を決定。これに伴い、Carbon系の機能を使っているプログラムは事実上アップデートされないことになりました。

AppleScriptの処理系自体はIntel対応→Cocoa化→64bit化→マルチスレッド化+Scripting Bridge対応など進化して行ったのですが、call xmlrpcの機能モジュールについては「Carbon APIを使っているためアップデートしない(=バグ修正しない)」との明確な回答がAppleのエンジニアからありました。

XML-RPCの利用頻度自体はそれほど高くないため、致命的な問題にはなってはいないものの「いつか、Apple標準のcall xmlrpc命令の代わりになるものを見つけなくては」という問題意識は持っていました。

そんな中、オープンソースのXML-RPCのフレームワークをいくつか見つけ、AppleScriptから呼び出す実験を実施。その中で同期通信の機能を持っていたEric Czarny氏とNikolay Kasyanov氏のフレームワークにたどりつきました。

いろいろ試行錯誤して、AppleScriptからこのオープンソースのXML-RPCフレームワークを呼び出して、インターネット上のサービスを呼び出して使えることを確認したのが本AppleScriptです。Script Editor+ASObjC Explorer 4+OS X 10.11.0、Script Editor+OS X 10.10.5上での動作を確認しています。

XML-RPC frameworkについて、OS X 10.10以降をターゲットOSにしてビルドしたものも用意しておきました。興味のある方はおためしを(~/Library/Frameworks/にインストール)。

→ Download Framework(binary)

AppleScript名:ASOCでXML-RPCのテスト v3
– Created 2015-10-06 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “XMLRPC”
–XMLRPC.framework
–https://cocoapods.org/pods/xmlrpc
–Cocoa XML-RPC Framework © 2011 Divisible by Zero
–v2.3.4

set aRes to (my callXMLRPC(“http://yubin.senmon.net/service/xmlrpc/”, “yubin.getMaxFetchCount”, missing value)) as integer
–>  100

set bRes to (my callXMLRPC(“http://yubin.senmon.net/service/xmlrpc/”, “yubin.getVersion”, missing value)) as text
–> “15.09a”

on callXMLRPC(paramURL, aMethod, aParamList)
  
  
set aURL to current application’s |NSURL|’s URLWithString:paramURL
  
set aReq to current application’s XMLRPCRequest’s alloc()’s initWithURL:aURL
  
aReq’s setMethod:aMethod withParameter:aParamList
  
set aRes to current application’s XMLRPCConnection’s sendSynchronousXMLRPCRequest:aReq |error|:(missing value)
  
  
set errF to (aRes’s isFault()) as boolean
  
  
if errF = true then
    set xmlRPCres to faultCode of aRes
    
–set xmlRPCbody to faultString of aRes
  else
    set xmlRPCres to aRes’s object()
    
–set xmlRPCbody to aRes’s body()
  end if
  
  
return xmlRPCres
  
end callXMLRPC

★Click Here to Open This Script 

2015/10/03 UniversalDetectorで文字コード判定

オープンソースの文字コード判別フレームワーク「UniversalDetector」の機能を用いて与えられたテキストファイルの文字コードを判定するAppleScriptです。

たまたまGitHubでUniversalDetectorを見つけたので、ダウンロードしてビルドしてAppleScriptから呼び出して試してみました。AppleScriptで自動判定したもの(左側)との比較を行った結果がこれです(↓)。

btable.png

日本語のテキストに限定して試してみると、なかなかつらいです。AppleScriptだけで判定したほうが良好な結果が出ています。

AppleScript名:UniversalDetectorで文字コード判定
– Created 2015-10-03 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
use framework “UniversalDetector”
–https://github.com/JanX2/UniversalDetector

set aPath to (POSIX path of (choose file))
set aStr to current application’s NSString’s stringWithString:aPath
set aDetector to current application’s UniversalDetector’s new()
aDetector’s analyzeContentsOfFile:aStr
set aStr to current application’s NSString’s localizedNameOfStringEncoding:(aDetector’s encoding())

–>  (NSString) “日本語(EUC)”
–>  (NSString) “日本語(ISO 2022-JP)”
–>  (NSString) “日本語(Shift JIS)”
–>  (NSString) “Unicode(UTF-8)”
–>  (NSString) “キリル文字(Windows)” –NG。本当はUTF-16 no BOM
–>  (NSString) “中国語(GB 18030)”–NG。本当はUTF-16BE
–>  (NSString) “Unicode(UTF-16)”

set bStr to aDetector’s MIMECharset()

–>  (NSString) “EUC-JP”
–>  (NSString) “ISO-2022-JP”
–>  (NSString) “Shift_JIS”
–>  (NSString) “UTF-8″
–>  (NSString) “windows-1251″–NG
–>  (NSString) “gb18030″–NG
–>  (NSString) “UTF-16″

set aNum to (aDetector’s confidence()) * 100
–>  100.0–”EUC-JP”
–>  100.0–”ISO-2022-JP”
–>  100.0–”Shift_JIS”
–>  100.0–”UTF-8″
–>  5.271286144853–UTF-16 no BOM
–>  100.0–NGだが100%といっている
–>  100.0– “UTF-16″

★Click Here to Open This Script 

2015/07/23 ASOCでレコードのリストをユニーク化

レコードのリスト(例:{{namae:”Piyomaru”,age:16}, {namae:”Piyoko”, age:13}})で、重複データ項目があった場合にそなえ、重複項目を削除する「ユニーク化」を行うAppleScriptObjCのScriptです。

BridgePlusを用いたバージョンと、ASObjCExtras.frameworkを用いたバージョンを掲載します。現在ではBridgePlusの使用が強く推奨されています。

AppleScript名:ASOCでレコードのリストをユニーク化(BridgePlus)
use AppleScript version “2.4″
use framework “Foundation”
use scripting additions
use script “BridgePlus”

load framework

set msRecList to {{msName:“格 陸戦型ジム 獲得済 COST: 120″, sortieTimes:12}, {msName:“狙 ジム・スナイパーカスタム 獲得済 COST: 200″, sortieTimes:6}, {msName:“狙 ジム・スナイパーカスタム 獲得済 COST: 200″, sortieTimes:6}}

set newMsList to uniquefyList(msRecList)
–>  {{sortieTimes:6, msName:”狙 ジム・スナイパーカスタム 獲得済 COST: 200″}, {sortieTimes:12, msName:”格 陸戦型ジム 獲得済 COST: 120″}}

–レコードのリストをユニーク化
on uniquefyList(aList)
  set msArray to Cocoaify aList
  
set aRes to current application’s NSSet’s setWithArray:(msArray’s allObjects())
  
set bRes to aRes’s allObjects()
  
set cRes to ASify from bRes as list
  
return cRes
end uniquefyList

★Click Here to Open This Script 

AppleScript名:ASOCでレコードのリストをユニーク化(ASObjCExtras.framework)
use AppleScript version “2.4″
use framework “Foundation”
use framework “ASObjCExtras”
use scripting additions

set msRecList to {{msName:“格 陸戦型ジム 獲得済 COST: 120″, sortieTimes:12}, {msName:“狙 ジム・スナイパーカスタム 獲得済 COST: 200″, sortieTimes:6}, {msName:“狙 ジム・スナイパーカスタム 獲得済 COST: 200″, sortieTimes:6}}

set newMsList to uniquefyList(msRecList)
–>  {{sortieTimes:6, msName:”狙 ジム・スナイパーカスタム 獲得済 COST: 200″}, {sortieTimes:12, msName:”格 陸戦型ジム 獲得済 COST: 120″}}

–レコードのリストをユニーク化
on uniquefyList(aList)
  set msArray to current application’s SMSFord’s Cocoaify:aList
  
set aRes to current application’s NSSet’s setWithArray:(msArray’s allObjects())
  
set bRes to aRes’s allObjects()
  
set cRes to bRes’s ASify() as list
  
return cRes
end uniquefyList

★Click Here to Open This Script 

2015/07/10 ASOCでDict書き込み_2

アーケードゲーム「戦場の絆」では、PC/携帯電話などで会員ページにログインすると、各MSの出撃回数を調べられるようになっています。この出撃回数を取得するAppleScriptで、出撃回数ランキングの中に出撃回数が加算されたMSについて、「▲」表記を行う処理を作成してみました。

ms1.png

ms2.png

■1位 近 装甲強化型ジム COST: 200 = 66回
▲2位 近 ザクII(F2) COST: 160 = 50回
■3位 遠 ジム・キャノン COST: 160 = 43回
■4位 近 ジム・コマンド COST: 200 = 32回
■5位 近 ジム(WD隊) COST: 160 = 28回
■6位 近 陸戦型ガンダム COST: 220 = 24回
■7位 近 ジム改 COST: 240 = 22回
■7位 遠 ガンタンク COST: 200 = 22回
■9位 格 ジム(指揮官機) COST: 160 = 20回
■10位 近 ジム COST: 120 = 19回
■11位 遠 量産型ガンタンク COST: 160 = 14回
■12位 格 陸戦型ジム COST: 120 = 12回
■13位 格 ガンダム COST: 280 = 11回
■14位 近 ジム・トレーナー COST: 120 = 9回
■14位 射 ジム・スナイパーII(WD隊) COST: 220 = 9回
■16位 射 陸戦型ガンダム(ジム頭) COST: 200 = 7回
■17位 格 ガンダムEz8 COST: 240 = 6回
■17位 近 ジム・寒冷地仕様 COST: 200 = 6回
■17位 狙 ジム・スナイパーカスタム COST: 200 = 6回
■20位 格 ジム・ストライカー COST: 180 = 4回
■21位 格 ガンキャノン重装型 COST: 160 = 3回
■22位 近 アクア・ジム COST: 160 = 2回
■22位 射 ガンキャノン COST: 200 = 2回
■24位 近 ジム・コマンドライトアーマー COST: 160 = 1回

■25位 格 ボールK型 COST: 120 = 0回
■25位 格 B.D.2号機 COST: 260 = 0回
■25位 格 プロトタイプガンダム COST: 280 = 0回
■25位 近 パワード・ジム COST: 240 = 0回
■25位 射 デザート・ジム COST: 160 = 0回
■25位 遠 量産型ガンキャノン COST: 200 = 0回

このScriptの中で、AppleScriptのrecordをplistとして保存するルーチンを作成したので紹介します。そのまま実行すると、

~/Library/Application Support/戦場の絆/

ディレクトリ内にefsf.plistというplistファイルを作成します。

plist1.png

フォルダが存在しない場合には作成します。

plist2.png

いったん、かなりの数のサブルーチンをASObjC Extras.framework用に書いたので、なかなかBridgePlusライブラリ用に書き換えるということはできないところですが、このあたりの切り替えを柔軟に行うためにも自作のScriptの機能をライブラリ化しておくとよいのかもしれません。

ただ、AppleScript系の開発で毎回ターゲットOSがOS X 10.10.xになっているわけでもなくて、少し前のバージョンだったり(10.8とか)することもあるので、なかなかライブラリ化に踏み切れないところもあります。

AppleScript名:ASOCでDict書き込み_2
use AppleScript version “2.4″
use framework “Foundation”
use framework “ASObjCExtras”
use scripting additions

set a1List to {“msName”, “sortieTimes”}
set b1List to {{“近 装甲強化型ジム 獲得済 COST: 200″, 66}, {“遠 ジム・キャノン 獲得済 COST: 160″, 43}, {“近 ザクII(F2) 獲得済 COST: 160″, 42}, {“近 ジム・コマンド 獲得済 COST: 200″, 32}, {“近 ジム(WD隊) 獲得済 COST: 160″, 28}, {“近 陸戦型ガンダム 獲得済 COST: 220″, 24}, {“近 ジム改 獲得済 COST: 240″, 22}, {“遠 ガンタンク 獲得済 COST: 200″, 22}, {“格 ジム(指揮官機) 獲得済 COST: 160″, 20}, {“近 ジム 獲得済 COST: 120″, 19}, {“遠 量産型ガンタンク 獲得済 COST: 160″, 14}, {“格 陸戦型ジム 獲得済 COST: 120″, 12}, {“格 ガンダム 獲得済 COST: 280″, 11}, {“近 ジム・トレーナー 獲得済 COST: 120″, 9}, {“射 ジム・スナイパーII(WD隊) 獲得済 COST: 220″, 9}, {“射 陸戦型ガンダム(ジム頭) 獲得済 COST: 200″, 7}, {“格 ガンダムEz8 獲得済 COST: 240″, 6}, {“近 ジム・寒冷地仕様 獲得済 COST: 200″, 6}, {“狙 ジム・スナイパーカスタム 獲得済 COST: 200″, 6}, {“格 ジム・ストライカー 獲得済 COST: 180″, 4}, {“格 ガンキャノン重装型 獲得済 COST: 160″, 3}, {“近 アクア・ジム 獲得済 COST: 160″, 2}, {“射 ガンキャノン 獲得済 COST: 200″, 2}, {“近 ジム・コマンドライトアーマー 獲得済 COST: 160″, 1}, {“格 ボールK型 獲得済 COST: 120″, 0}, {“格 B.D.2号機 獲得済 COST: 260″, 0}, {“格 プロトタイプガンダム 獲得済 COST: 280″, 0}, {“近 パワード・ジム 獲得済 COST: 240″, 0}, {“射 デザート・ジム 獲得済 COST: 160″, 0}, {“遠 量産型ガンキャノン 獲得済 COST: 200″, 0}}

set aArray to current application’s SMSFord’s subarraysIn:b1List asDictionariesUsingLabels:a1List |error|:(missing value)
set b1List to aArray’s ASify() as list

–>  {​​​​​{​​​​​​​sortieTimes:66, ​​​​​​​msName:”近 装甲強化型ジム 獲得済 COST: 200″​​​​​}, ​​​​​{​​​​​​​sortieTimes:43, ​​​​​​​msName:”遠 ジム・キャノン 獲得済 COST: 160″​​​​​}, ​​​​​{​​​​​​​sortieTimes:42, ​​​​​​​msName:”近 ザクII(F2) 獲得済 COST: 160″​​​​​}, ​​​​​{​​​​​​​sortieTimes:32, ​​​​​​​msName:”近 ジム・コマンド 獲得済 COST: 200″​​​​​}, ​​​​​{​​​​​​​sortieTimes:28, ​​​​​​​msName:”近 ジム(WD隊) 獲得済 COST: 160″​​​​​}, ​​​​​{​​​​​​​sortieTimes:24, ​​​​​​​msName:”近 陸戦型ガンダム 獲得済 COST: 220″​​​​​}, ​​​​​{​​​​​​​sortieTimes:22, ​​​​​​​msName:”近 ジム改 獲得済 COST: 240″​​​​​}, ​​​​​{​​​​​​​sortieTimes:22, ​​​​​​​msName:”遠 ガンタンク 獲得済 COST: 200″​​​​​}, ​​​​​{​​​​​​​sortieTimes:20, ​​​​​​​msName:”格 ジム(指揮官機) 獲得済 COST: 160″​​​​​}, ​​​​​{​​​​​​​sortieTimes:19, ​​​​​​​msName:”近 ジム 獲得済 COST: 120″​​​​​}, ​​​​​{​​​​​​​sortieTimes:14, ​​​​​​​msName:”遠 量産型ガンタンク 獲得済 COST: 160″​​​​​}, ​​​​​{​​​​​​​sortieTimes:12, ​​​​​​​msName:”格 陸戦型ジム 獲得済 COST: 120″​​​​​}, ​​​​​{​​​​​​​sortieTimes:11, ​​​​​​​msName:”格 ガンダム 獲得済 COST: 280″​​​​​}, ​​​​​{​​​​​​​sortieTimes:9, ​​​​​​​msName:”近 ジム・トレーナー 獲得済 COST: 120″​​​​​}, ​​​​​{​​​​​​​sortieTimes:9, ​​​​​​​msName:”射 ジム・スナイパーII(WD隊) 獲得済 COST: 220″​​​​​}, ​​​​​{​​​​​​​sortieTimes:7, ​​​​​​​msName:”射 陸戦型ガンダム(ジム頭) 獲得済 COST: 200″​​​​​}, ​​​​​{​​​​​​​sortieTimes:6, ​​​​​​​msName:”格 ガンダムEz8 獲得済 COST: 240″​​​​​}, ​​​​​{​​​​​​​sortieTimes:6, ​​​​​​​msName:”近 ジム・寒冷地仕様 獲得済 COST: 200″​​​​​}, ​​​​​{​​​​​​​sortieTimes:6, ​​​​​​​msName:”狙 ジム・スナイパーカスタム 獲得済 COST: 200″​​​​​}, ​​​​​{​​​​​​​sortieTimes:4, ​​​​​​​msName:”格 ジム・ストライカー 獲得済 COST: 180″​​​​​}, ​​​​​{​​​​​​​sortieTimes:3, ​​​​​​​msName:”格 ガンキャノン重装型 獲得済 COST: 160″​​​​​}, ​​​​​{​​​​​​​sortieTimes:2, ​​​​​​​msName:”近 アクア・ジム 獲得済 COST: 160″​​​​​}, ​​​​​{​​​​​​​sortieTimes:2, ​​​​​​​msName:”射 ガンキャノン 獲得済 COST: 200″​​​​​}, ​​​​​{​​​​​​​sortieTimes:1, ​​​​​​​msName:”近 ジム・コマンドライトアーマー 獲得済 COST: 160″​​​​​}, ​​​​​{​​​​​​​sortieTimes:0, ​​​​​​​msName:”格 ボールK型 獲得済 COST: 120″​​​​​}, ​​​​​{​​​​​​​sortieTimes:0, ​​​​​​​msName:”格 B.D.2号機 獲得済 COST: 260″​​​​​}, ​​​​​{​​​​​​​sortieTimes:0, ​​​​​​​msName:”格 プロトタイプガンダム 獲得済 COST: 280″​​​​​}, ​​​​​{​​​​​​​sortieTimes:0, ​​​​​​​msName:”近 パワード・ジム 獲得済 COST: 240″​​​​​}, ​​​​​{​​​​​​​sortieTimes:0, ​​​​​​​msName:”射 デザート・ジム 獲得済 COST: 160″​​​​​}, ​​​​​{​​​​​​​sortieTimes:0, ​​​​​​​msName:”遠 量産型ガンキャノン 獲得済 COST: 200″​​​​​}​​​}

set cRec to {msList:b1List, sortieDate:date string of (current date)}

–>  {​​​​​msList:{​​​​​​​{​​​​​​​​​sortieTimes:66, ​​​​​​​​​msName:”近 装甲強化型ジム 獲得済 COST: 200″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:43, ​​​​​​​​​msName:”遠 ジム・キャノン 獲得済 COST: 160″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:42, ​​​​​​​​​msName:”近 ザクII(F2) 獲得済 COST: 160″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:32, ​​​​​​​​​msName:”近 ジム・コマンド 獲得済 COST: 200″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:28, ​​​​​​​​​msName:”近 ジム(WD隊) 獲得済 COST: 160″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:24, ​​​​​​​​​msName:”近 陸戦型ガンダム 獲得済 COST: 220″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:22, ​​​​​​​​​msName:”近 ジム改 獲得済 COST: 240″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:22, ​​​​​​​​​msName:”遠 ガンタンク 獲得済 COST: 200″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:20, ​​​​​​​​​msName:”格 ジム(指揮官機) 獲得済 COST: 160″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:19, ​​​​​​​​​msName:”近 ジム 獲得済 COST: 120″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:14, ​​​​​​​​​msName:”遠 量産型ガンタンク 獲得済 COST: 160″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:12, ​​​​​​​​​msName:”格 陸戦型ジム 獲得済 COST: 120″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:11, ​​​​​​​​​msName:”格 ガンダム 獲得済 COST: 280″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:9, ​​​​​​​​​msName:”近 ジム・トレーナー 獲得済 COST: 120″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:9, ​​​​​​​​​msName:”射 ジム・スナイパーII(WD隊) 獲得済 COST: 220″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:7, ​​​​​​​​​msName:”射 陸戦型ガンダム(ジム頭) 獲得済 COST: 200″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:6, ​​​​​​​​​msName:”格 ガンダムEz8 獲得済 COST: 240″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:6, ​​​​​​​​​msName:”近 ジム・寒冷地仕様 獲得済 COST: 200″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:6, ​​​​​​​​​msName:”狙 ジム・スナイパーカスタム 獲得済 COST: 200″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:4, ​​​​​​​​​msName:”格 ジム・ストライカー 獲得済 COST: 180″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:3, ​​​​​​​​​msName:”格 ガンキャノン重装型 獲得済 COST: 160″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:2, ​​​​​​​​​msName:”近 アクア・ジム 獲得済 COST: 160″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:2, ​​​​​​​​​msName:”射 ガンキャノン 獲得済 COST: 200″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:1, ​​​​​​​​​msName:”近 ジム・コマンドライトアーマー 獲得済 COST: 160″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:0, ​​​​​​​​​msName:”格 ボールK型 獲得済 COST: 120″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:0, ​​​​​​​​​msName:”格 B.D.2号機 獲得済 COST: 260″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:0, ​​​​​​​​​msName:”格 プロトタイプガンダム 獲得済 COST: 280″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:0, ​​​​​​​​​msName:”近 パワード・ジム 獲得済 COST: 240″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:0, ​​​​​​​​​msName:”射 デザート・ジム 獲得済 COST: 160″​​​​​​​}, ​​​​​​​{​​​​​​​​​sortieTimes:0, ​​​​​​​​​msName:”遠 量産型ガンキャノン 獲得済 COST: 200″​​​​​​​}​​​​​}, ​​​​​sortieDate:”2015年7月10日金曜日”​​​}

set aName to “efsf.plist”
saveRecordToFolAsPlist(cRec, “戦場の絆”, aName) of me

on saveRecordToFolAsPlist(theRecord, folName, aName)
  
  
set myAppSupDir to (path to application support from user domain) as string
  
set myAppSupDir to myAppSupDir & folName & “:”
  
set aPath to (POSIX path of myAppSupDir)
  
try
    do shell script “mkdir -p” & space & (quoted form of aPath)
  end try
  
  
set theDict to current application’s NSDictionary’s dictionaryWithDictionary:theRecord
  
  
set thePath to aPath & aName –Full Path
  
set cPath to current application’s NSString’s stringWithString:thePath
  
–set cPath to cPath’s stringByExpandingTildeInPath()—(OSからフルパスを取得しているので)チルダなどの展開処理は省略
  
  
set aRes to theDict’s writeToFile:cPath atomically:true
  
  
return aRes as boolean
  
end saveRecordToFolAsPlist

★Click Here to Open This Script 

2015/05/29 BridgePlus v1.1.0 Readme日本語翻訳版

(Written By Shane Stanley、Translated By Takaaki Naganoya〜翻訳・掲載はShaneの許諾済み)

BridgePlusはAppleScriptで記述した「Script Library」で、バージョン10.9以降のOS Xで動作します。

典型的なScripterにとって、BridgePlusは「ASObjC Runner.app」の機能をもとにしたハンドラ群を含んだライブラリです。

AppleScriptObjCユーザーにとって、AppleScriptとCocoaオブジェクトの相互変換機能を向上させたコマンド群で、(AppleScript用語辞書によるラッピングを介さず)拡張機能に直接アクセスが可能なものです。

bridge.png

BridgePlusは、いずれかのScript Librariesフォルダ(~/Library/Script Libraries/ など)にインストールしておく必要があります。また、Scriptアプリケーションのバンドル内に直接埋め込むことも可能です(後述)。

基本的な利用方法

もし、あなたがAppleScriptObjCを使うつもりがない場合、BridgePlusは1つの適切なコマンドをAppleScript用語辞書の中に持っています。それが、「open lib description」コマンドです。本コマンドを実行すると、このReadme書類(英文)を自動でオープンします。

AppleScript名:sample1
use BridgePlus : script “BridgePlus”
open lib description

★Click Here to Open This Script 

BridgePlusライブラリ内のハンドラ(サブルーチン)を呼び出すためには、ライブラリ経由で呼び出すことになります。

たとえば、ソーティング(データ並べ替え)のハンドラ(サブルーチン)、

on sortListOfNumbers:listOfNumbers

を呼び出すためには、

AppleScript名:sample2
use AppleScript version “2.4″
use scripting additions
use BridgePlus : script “BridgePlus”
set sortedList to BridgePlus’s sortListOfNumbers:{9, 2, 7, 3, 6, 4}
–> {2, 3, 4, 6, 7, 9}

★Click Here to Open This Script 

のように書くことになります。もし、OS X 10.6.x上でのAppleScriptObjCハンドラ風に、

AppleScript名:sample3
use AppleScript version “2.4″
use scripting additions
use BridgePlus : script “BridgePlus”
set sortedList to BridgePlus’s sortListOfNumbers_({9, 2, 7, 3, 6, 4})

★Click Here to Open This Script 

と書いたとしても、コンパイル(構文確認)すると、

AppleScript名:sample4
use AppleScript version “2.4″
use scripting additions
use BridgePlus : script “BridgePlus”
set sortedList to BridgePlus’s sortListOfNumbers:{9, 2, 7, 3, 6, 4}

★Click Here to Open This Script 

のように書き換えられます。

(注記:オリジナルではここで各種ハンドラの実例紹介。サンプル部分をすべて日本語訳していると時間がかかりすぎるので、Shaneと協議の結果、まずは日本語訳も実例以外の部分を掲載ということで話がまとまった)

基本的な利用方法

BridgePlusは2つのメインコマンドを持っており、これらはAppleScriptObjC記述時に、CocoaとAppleScriptのオブジェクトの変換をシンプルにし、機能向上させるものです。

 Cocoaify:AppleScript側の値をCocoa側の等しい型の値(例:integer→NSInteger)に変換します
 ASify from:Cocoa側の値をAppleScript側の等しい型の値(例:NSInteger→integer)に変換します

BridgePlus Script LibraryのAppleScript用語辞書を見るには、

 スクリプトエディタ:BridgePlusを~/Library/Script Libraries.フォルダにコピーしたのちに、「ファイル」>「用語説明を開く…」を実行
 Script Debugger:Optionキーを押しながらScript Debuggerアイコンにドラッグ

と操作してください。

多くのケースにおいて、AppleScriptObjCによる処理は以下の3ステップから構成されます。

 AppleScript側の値をCocoa側の等しい型に変換
 Cocoaオブジェクトのinstance methodを呼び出す
 7覯漫Cocoaの値)をAppleScript側の値に変換して返す

たとえば、以下のようなAppleScriptObjCのコードがあったとして、

AppleScript名:sample5
use framework “Foundation”

set someList to {1, 3, 2} –I added :-)
set anArray to current application’s NSArray’s arrayWithArray:someList
set anArray to anArray’s sortedArrayUsingSelector:“compare:”
set aList to anArray as list

★Click Here to Open This Script 

これはBridgePlusを使ってこのように書き換えられます。

AppleScript名:sample6
use framework “Foundation”
use script “BridgePlus”

set someList to {1, 3, 2} I added :-)
set anArray to Cocoaify someList
set anArray to anArray’s sortedArrayUsingSelector:“compare:”
set aList to ASify from anArray

★Click Here to Open This Script 

ほんのちょっとシンプルになりましたが、劇的なほどではありませんね。しかしながら、「Cocoaify」「ASify from」コマンドを使うことによる利点は、AppleScriptとCocoaの間の型変換をより完全に行えることにあります。

・Cocoaifyコマンド:AppleScript側の値がdateオブジェクト、もしくはlist型やRecord型でdateオブジェクトを含む場合に、dateはNSDateに変換されます。alias型やfileはNSURLに変換されます

・ASify fromコマンド:Cocoa側の値がNSDateの場合、およびNSDateを含むNSArrayやNSDictionaryの場合、AppleScript側のdateに変換されます。同様に、NSURLはfile («class furl»)に変換されます。

Cocoa側の値がFloating Pointの数値を含む場合、それらはAppleScript側のrealに変換されますし、listやrecord内に含まれるrealとして変換されます。実際に、OS X 10.10.3上でCocoaのFloating Pointの数値1.2をAppleScript側のrealに変換すると1.200000047684になるなど、AppleScriptの標準機能を使うと精度が失われてしまいます。でも、BridgePlusの「ASify from」コマンドを使えば、こんな問題は起きません。Cocoaの1.2を変換しても、AppleScript側でも1.2が得られます。

なので、たとえば上記のサンプルScriptにAppleScriptのdate型のデータをlist型データに入れても、きちんとソートされます。

AppleScript名:sample7
use framework “Foundation”
use scripting additions
use script “BridgePlus”

set someList to {current date, (current date) - (2 * days), (current date) - (1 * days)} Today, The Day before Yesterday, Yesterday
set anArray to Cocoaify someList
set anArray to anArray’s sortedArrayUsingSelector:“compare:”
set aList to ASify from anArray
–> {date “2015年5月28日木曜日 0:22:39″, date “2015年5月29日金曜日 0:22:39″, date “2015年5月30日土曜日 0:22:39″}

★Click Here to Open This Script 

BridgePlusなしでAppleScriptの標準機能を使った場合には、サブルーチンを用いてひとつひとつのdateの値をNSDateに変換してから並べ替える必要があります。さらに、並べ替えたあとに元の型に変換し直す必要も出てきます。そのさいのAppleScriptObjCのコードはより長く複雑なものになってしまいます。

高度なAppleScriptObjCでの利用方法

「BridgePlus.scptd」のバンドルは、多くのScripterにとって有用なフレームワーク(BridgePlus.framework)を含んでいます。本ライブラリBridgePlusはこの内部フレームワークが持つすべての機能をサポートする用語は持っていませんが、あなたのScriptに「use script “BridgePlus”」の1行を加えるだけで、フレームワークのmethodに直接アクセスできるようになります。

フレームワーク内のmethodのほとんどは、「SMSForder」クラスから呼び出せるmethodとして実装されており、これらは「ASObjC Runner.app」(現在では廃止)をもとに機能拡張されたものです。「SMSForder」クラスのヘッダーファイルを確認するには、「open framework header」コマンドを実行します。

AppleScript名:sample8
use script “BridgePlus”
open framework header

★Click Here to Open This Script 

BridgePlusのフレームワークは、実際のところ「ASObjCExtras.framework」のアップデート版であり、ASObjCExtras.frameworkがすでにインストールされている環境でのコンフリクトやクラッシュを避けるために名称変更されたものです。

SMSForder内のmethodを直接呼び出す前に、最低でも1つのBridgePlus.scptdライブラリ内のコマンドを実行する必要があります。それが「load framework」コマンドです(もしくは何かBridgePlusの持つコマンド・・・たとえば、Cocoaify “” を実行することでもOKです)。

かんたんなサンプル:

AppleScript名:sample9
use framework “Foundation”
use scripting additions
use script “BridgePlus”

load framework
set newList to ASify from (current application’s SMSForder’s colsToRowsIn:{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}} |error|:(missing value))
>  {{1, 4, 7, 10}, {2, 5, 8, 11}, {3, 6, 9, 12}}

★Click Here to Open This Script 

やってはいけないことがあります。たとえあなたがBridgePlusライブラリをインストールしてあって使っていても、Script中に「use framework “BridgePlus”」とは書いてはいけません。その書き方ではBridgePlusバンドル内部のフレームワークは見つかりません。「use script “BridgePlus” 」の行とBridgePlusの持つコマンドを1つ実行するとフレームワークが(自動で)読み込まれるようになっています。

SMSForderはCocoa的に0からはじまるインデックスを用います、一方でBridgePlusライブラリの「AppleScriptObjC」で直接内部機能を呼び出さないハンドラを呼び出した場合にはAppleScriptの世界で標準的な1からはじまるインデックスで値を返します。

実行環境へのインストール(Deployment)

「~/Library/Script Libraries」フォルダに「BridgePlus.scptd」ファイルを入れることで、あなたのすべてのAppleScript中でBridgePlusの機能を利用できるようになります。

BridgePlusライブラリをあなたの書いたScriptと一緒に配布する際には、BridgePlus.scptdファイルをあなたのScriptのBundle内の「/Contents/Resources/Script Libraries」フォルダに入れておけば大丈夫です(アプレットの場合でも、.scptdファイルの場合でも)。

もし、あなたがScript Bundle(AppleScriptアプレット/.scptd)をCodesignすることを検討している場合、まず最初にライブラリ中に入っているBridgePlus.frameworkをcodesignする必要があります。

BridgePlusをXcodeプロジェクト内でお使いの場合、「BridgePlus.framework」をBridgePlusライブラリのバンドル内から取り出して、他のframeworkと同様にXcodeプロジェクトに登録し、「BridgePlus.scptd」バンドル内から削除する必要があります。このようにすれば、BridgePlus.frameworkのcodesignはXcodeがめんどうを見てくれます。

BridgePlusのダウンロードはこちら!
http://www.macosxautomation.com/applescript/apps/BridgePlus.html

著名なScripterでありBlogerであるTakaaki Naganoyaによる(表現原文ママ)ASObjCExtras.frameworkの総合マニュアル(英文)はこちらです。
同マニュアルは、もしあなたがBridgePlusの上級者向けの(AppleScriptObjCの)機能を利用したい場合には有用なものです。

http://piyocast.com/as/asinyaye/free-asobjcextras-scripting-guide/

本ライブラリの利用にあたって、ライブラリ全体、内部フレームワークのみの場合の両方で自由に再配布できます(再配布時の料金は発生しません)。
利用料金は発生しませんが、動作保証もいたしません。「At your own risk」でご利用ください。