Archive for 7月, 2015

2015/07/31 ASOCで画像+文字作成テスト

Cocoaの機能を用いて指定サイズの単色塗り画像を作成し、指定の文字列を描画してPNG形式の画像ファイルに書き出すAppleScriptです。

test1.png
▲こんな画像がデスクトップに書き出されます

AppleScript名:ASOCで画像+文字作成テスト
– Created 2015-07-29 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”

set aWidth to 400.0 –幅
set aHeight to 200.0 –高さ
set outPath to “~/Desktop/test.png” –書き出し先のファイルパス
set fillColor to current application’s NSColor’s yellowColor –塗り色
set drawColor to current application’s NSColor’s blackColor –文字色
set aText to “ぴよまるソフトウェア”
set {xPos, yPos} to {1, 5}

–新規画像を作成して背景を塗る
set aImage1 to makeImageWithFilledColor(aWidth, aHeight, outPath, fillColor) of me

–画像に文字を塗る
set aImage2 to drawStringsOnImage(aImage1, aText, “HiraKakuStd-W8″, 36.0, drawColor, xPos, yPos) of me

–ファイル保存
set aRes to saveImageRepAtPathAsPNG(aImage2, outPath) of me

–画像のうえに指定の文字を描画して画像を返す
on drawStringsOnImage(anImage, aText, aFontName, aPoint, drawColor, xPos, yPos)
  
  
set aString to current application’s NSString’s stringWithString:aText
  
set aDict to current application’s NSDictionary’s dictionaryWithObjects:{current application’s NSFont’s fontWithName:aFontName |size|:aPoint, drawColor} forKeys:{current application’s NSFontAttributeName, current application’s NSForegroundColorAttributeName}
  
  
–文字描画開始
  
anImage’s lockFocus()
  
aString’s drawAtPoint:(current application’s NSMakePoint(xPos, yPos)) withAttributes:aDict
  
anImage’s unlockFocus()
  
–文字描画ここまで
  
  
return anImage
  
end drawStringsOnImage

–指定サイズの画像を作成し、背景を指定色で塗る
on makeImageWithFilledColor(aWidth, aHeight, outPath, fillColor)
  
  
–Imageの作成  
  
set anImage to current application’s NSImage’s alloc()’s initWithSize:(current application’s NSMakeSize(aWidth, aHeight))
  
  
–描画開始
  
anImage’s lockFocus()
  
  
set theRect to {{x:0, y:0}, {height:aHeight, width:aWidth}}
  
set theNSBezierPath to current application’s NSBezierPath’s bezierPath
  
theNSBezierPath’s appendBezierPathWithRect:theRect
  
  
fillColor’s |set|() –色設定
  
theNSBezierPath’s fill() –ぬりつぶし
  
  
anImage’s unlockFocus()
  
–描画ここまで
  
  
return anImage –画像を返す
  
end makeImageWithFilledColor

–画像を指定パスに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 

2015/07/31 ASOCで画像作成テスト

Cocoaの機能を用いて指定サイズの単色塗り画像(PNG形式)のファイルを書き出すAppleScriptです。

test.png
▲こんな画像がデスクトップに書き出されます

AppleScript名:画像作成テスト
– Created 2015-07-29 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”

set aWidth to 400.0 –幅
set aHeight to 200.0 –高さ
set outPath to “~/Desktop/test.png” –書き出し先のファイルパス
set fillColor to current application’s NSColor’s greenColor –塗り色

set aRes to makeImageWithFilledWithColor(aWidth, aHeight, outPath, fillColor) of me
–> true

–指定サイズの画像を作成し、指定色で塗ってファイル書き出し
on makeImageWithFilledWithColor(aWidth, aHeight, outPath, fillColor)
  
  
–Imageの作成  
  
set anImage to current application’s NSImage’s alloc()’s initWithSize:(current application’s NSMakeSize(aWidth, aHeight))
  
  
–描画開始
  
anImage’s lockFocus()
  
  
set theRect to {{x:0, y:0}, {height:aHeight, width:aWidth}}
  
set theNSBezierPath to current application’s NSBezierPath’s bezierPath
  
theNSBezierPath’s appendBezierPathWithRect:theRect
  
  
fillColor’s |set|() –色設定
  
theNSBezierPath’s fill() –ぬりつぶし
  
  
anImage’s unlockFocus()
  
–描画ここまで
  
  
–生成した画像のRaw画像を作成
  
set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to current application’s NSBitmapImageRep’s imageRepWithData:imageRep
  
set myNewImageData to (aRawimg’s representationUsingType:(current application’s NSPNGFileType) |properties|:(missing value))
  
  
  
–書き出しファイルパス情報を作成
  
set pathString to current application’s NSString’s stringWithString:outPath
  
set newPath to pathString’s stringByExpandingTildeInPath()
  
  
–書き出し
  
set aRes to (myNewImageData’s writeToFile:newPath atomically:true) as boolean
  
  
return aRes –成功ならtrue、失敗ならfalseが返る
  
end makeImageWithFilledWithColor

★Click Here to Open This Script 

2015/07/29 指定名称のアプリをフルスクリーン表示

指定名称のアプリケーションをフルスクリーン表示するAppleScriptです。フルスクリーン表示させるアプリケーションはあらかじめ起動しておく必要があります。

また、GUI Scriptingを用いているため、実行前にあらかじめ「システム環境設定」>「セキュリティとプライバシー」>「アクセシビリティ」で本Scriptを実行するアプリケーション(おそらく、スクリプトエディタ)にコンピュータの(GUI側からの)制御を許可するよう設定しておく必要があります。

この手のScriptはよく見かけるのですが、自分の環境でしか動かないものを公開している例が多く、少し汎用性と柔軟性をもたせてみました。ただし、フルスクリーン表示の解除はサポートしていません(終了させればよいのでは? ^ー^;)。

フルスクリーン表示はOS X 10.7で搭載されましたが、これをAppleScriptからコントロールするまっとうな手段は用意されてきませんでした。次善の策として(やりたくないけど、仕方なく)GUI Scriptingでコントロールすることになります。

アプローチ方法は主に3つ。

(1)メニューを操作してフルスクリーン化
(2)キーボードショートカットを実行してフルスクリーン化

本Scriptでは、

(3)ウィンドウのフルスクリーン/ズームボタンをクリック

を採用しています。

本Scriptで柔軟性を持たせたのは3点。

(1)アプリケーション名称に、本当の名前(name)でもローカライズされた名称(displayed name)でもどちらでも許容する

(2)フルスクリーン表示がサポートされていないアプリケーションでは、ウィンドウの最大化を行う

(3)メニューの表示項目名などを使っていないため、使用/実行言語環境に左右されない

といったところです。

buttons.png

本来は、アプリケーションのプロパティなり、

–> {frontmost:false, class:application, name:”Safari”, version:”9.0″, full screen:true}

ウィンドウのプロパティにフルスクリーン表示状態を示すものが存在して、

–> {document:document “AS Hole(AppleScriptの穴) By Piyomaru Software”, closeable:true, zoomed:true, class:window, index:1, visible:true, name:”AS Hole(AppleScriptの穴) By Piyomaru Software”, modal:false, miniaturizable:true, titled:true, id:88, miniaturized:false, floating:false, resizable:true, bounds:{26, 23, 901, 1195}, current tab:tab 1 of window id 88, zoomable:true, full screen:true}

その操作によりフルスクリーン/ズーム表示を変更できることが望ましいところです。ただし、最近のUS AppleのAppleScriptエンジニアリングチームは、ASOCでCocoaの機能を使ってScripter側に勝手にやらせようということが多く、Finderの「タグ」にしてもフルスクリーン表示にしても、OS側の新機能にキャッチアップしていない部分が多くて困ります。

printコマンドで出力プリンタ名称を指定できても、肝心のプリンタ名称を取得する手軽でまっとうな方法が標準命令に存在していない点などは、どうかしています。結局、Apple側の対応を待たずにいろいろScripter側でやっているわけで…

AppleScript名:指定名称のアプリをフルスクリーン表示
– Created 2015-07-29 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions

set a to “Safari”
set aRes to dispAppWithFullScreen(a) of me

–指定名称のアプリケーション(起動中のもの)をフルスクリーン表示
on dispAppWithFullScreen(applicationName)
  
  
set appName to returnExactNameOfAnApp(applicationName)
  
if appName = false then return false –指定のアプリがなかった、あるいはアプリケーション名を間違って指定した
  
  
set pID to id of application appName
  
  
tell application appName
    reopen
    
activate
  end tell
  
  
tell application “System Events”
    tell (process 1 whose bundle identifier is pID)
      set b1List to every button of window 1 whose subrole is “AXFullScreenButton” –Full Screen
      
set b2List to every button of window 1 whose subrole is “AXZoomButton” –Zoom
      
      
if b1List = {} then
        set targB to first item of b2List –フルスクリーン表示機能を持たないアプリの場合
      else
        set targB to first item of b1List –フルスクリーン表示機能を持つアプリの場合
      end if
      
      
click targB
    end tell
  end tell
  
  
return true
  
end dispAppWithFullScreen

–Displayed Nameでアプリケーション名が与えられた場合に、正しい名称を返す
on returnExactNameOfAnApp(aName)
  tell application “System Events”
    set ap1List to every process whose name is equal to aName
    
if ap1List = {} then
      set ap1List to every process whose displayed name is equal to aName
      
if ap1List = {} then return false
    end if
    
set anApp to contents of first item of ap1List
    
return name of anApp
  end tell
end returnExactNameOfAnApp

★Click Here to Open This Script 

2015/07/29 ASOCでNSStringをas stringでcastすると問題が出るバグの検証(2)

本問題について、本腰を入れて検証してみました。

昨日試した範囲内では、

「NSStringをas stringでcastするとものすごく頻繁に問題が起きるんだなぁ」(あんまり遭遇してないけど)

と感じていたのですが、いろいろ試してみた結果、

comp.png

「AppleScriptのreadコマンドでUTF-8のテキストファイルを、『read』コマンドでオプションなしで読み込んでいることが問題」

のように見えました(自分のテスト内容自体に問題が?)。

『read as 《utf8》』で読み込んだ場合には、とくに問題はないように見えます。

この「NSStringをas stringでcastした場合に発生する問題」がどのような条件下で発生するのか不明ですが、「念のためにas textでcastしよう」ぐらいの感じでよさそうに思えます。

AppleScript名:ASOCでNSStringをas stringでcastすると問題が出る場合の検証 v3
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aFile to choose file
set aText to read aFile as «class utf8»

set aNSstring to current application’s NSString’s stringWithString:aText

set bStr to aNSstring as Unicode text
set cStr to aNSstring as string

set aRes to (bStr = cStr)
if aRes = false then
  
  
set dtPath to (path to desktop) as string
  
  
set bPath to dtPath & “as_unicode_text.txt”
  
set bStrHex to retHexDumpedStr(bStr, bPath) of me
  
  
set cPath to dtPath & “as_string.txt”
  
set cStrHex to retHexDumpedStr(cStr, cPath) of me
  
else
  
  
return true
  
end if

–与えられたデータ内容をhexdumpして返す
on retHexDumpedStr(aStr, outPath)
  set aRes to do shell script “echo “ & quoted form of aStr & ” | hexdump -v > “ & quoted form of (POSIX path of outPath)
  
return aRes
end retHexDumpedStr

★Click Here to Open This Script 

2015/07/28 ASOCでNSStringをas stringでcastすると問題が出るバグの検証

(必ず、ASOCでNSStringをas stringでcastすると問題が出るバグの検証(2) も合わせてお読みください)

OS X 10.11 El Capitan AppleScript Release Notesに記載されている

Coercing NSString objects to string now functions the same as coercing to text or Unicode text, preserving all characters. [18098890]

について検証してみました。テキストファイルを読み込み、いったんNSStringに変換して、さらに、

 as Unicode textでcastしたもの
 as stringでcastしたもの

の2つを比較してみたところ、100KBオーバー(156KB)の日本語のテキストファイルの処理時にテキストの(AppleScript的な)長さ(文字数)は変わらないものの、両者は等しくないという結果が出ました。

比較検証のためにas textでcastしたものとas Unicode textでcastしたものを同様に比較したところ同一データであるという結果が出ました。

AppleScript名:ASOCでNSStringをas stringでcastすると問題が出る場合の検証
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aFile to choose file
set aText to read aFile

set aNSstring to current application’s NSString’s stringWithString:aText

set bStr to aNSstring as Unicode text
set cStr to aNSstring as string

set aRes to (bStr = cStr)
if aRes = false then
  return {aRes, length of bStr, length of cStr}
  
–>  {​​​​​false, ​​​​​150493, ​​​​​150493​​​}
end if

return aRes

★Click Here to Open This Script 

追加検証

もっと小さいサイズのテキストでも差が出ました。7Kバイト強のサイズの日本語のテキストファイル(文字コード:UTF-8、改行コード:LF)をいったんNSStringに変換して、as string、as Unicode textで処理したものをそれぞれhexdumpして比較してみました。

diff1.png

FileMergeでダンプした結果を比較してみたところ、冒頭部分からかなりの違いがあり、

diff2.png

データの長さ自体も異なるという状態でした。

本来、内部処理がUnicode化されたMac OS X 10.5以降のAppleScriptでは、stringもtextもUnicode textも等しい処理のはずですが、NSStringからの変換時には挙動に差が見られました(as stringのみおかしい)。

OS X 10.10上でNSStringをAppleScriptの文字列に変換する場合には、as stringで変換することを避ける必要があります。

AppleScript名:ASOCでNSStringをas stringでcastすると問題が出る場合の検証 v2
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aFile to choose file
set aText to read aFile

set aNSstring to current application’s NSString’s stringWithString:aText

set bStr to aNSstring as Unicode text
set cStr to aNSstring as string

set aRes to (bStr = cStr)
if aRes = false then
  
  
set dtPath to (path to desktop) as string
  
  
set bPath to dtPath & “as_unicode_text.txt”
  
set bStrHex to retHexDumpedStr(bStr, bPath) of me
  
  
set cPath to dtPath & “as_string.txt”
  
set cStrHex to retHexDumpedStr(cStr, cPath) of me
  
end if

–与えられたデータ内容をhexdumpして返す
on retHexDumpedStr(aStr, outPath)
  set aRes to do shell script “echo “ & quoted form of aStr & ” | hexdump -v > “ & quoted form of (POSIX path of outPath)
  
return aRes
end retHexDumpedStr

★Click Here to Open This Script 

2015/07/28 ASOCでbase64エンコード、デコード v2

とくに掲載するほどのものでもなかった「ASOCでbase64エンコード、デコード」でしたが、それ自体には価値がなかったものの、Shane Stanleyからいろいろ情報をもらえました(Thanks Shane!)。

「deprecated」扱いになっているmethodを使っている

「base64Encoding()」と「initWithBase64Encoding:」がdeprecated扱い

NSStringをas stringでcastするのはトラブルの元

OS X 10.11 El CapitanのAppleScript Release Noteに記載されているバグ修正情報にあるように、NSStringをas stringでcastすると、as textとかas unicode textでcast同様にすべての文字を保護するようになった・・・逆をいえば、as stringでcastするとデータが欠損する可能性があるバグが存在していましたということで・・・なかなか怖いバグです。

少なくとも、OS X 10.10.x上ではNSStringをas stringでcastすることは避けたほうがよさそうです。

AppleScript名:ASOCでbase64エンコード、デコード v2
– Created 2015-07-27 by Takaaki Naganoya
– Updated 2015-07-28 by Shane Stanley

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

–Encode
set aFile to POSIX path of (choose file with prompt “choose text file to test” of type {“public.html”, “public.text”})
set aEncStr to base64StringFromFileAtPath(aFile) of me

–Decode
set aRes to detaFromBase64String(aEncStr) of me

–Base64 Encode
on base64StringFromFileAtPath(aFilePath)
  set aDataFromFile to current application’s NSData’s dataWithContentsOfFile:aFilePath
  
set aBase64EncStr to aDataFromFile’s base64EncodedStringWithOptions:(current application’s NSDataBase64Encoding64CharacterLineLength)
  
return aBase64EncStr as text –stringではなくtext
end base64StringFromFileAtPath

–Base 64 Decode
on detaFromBase64String(aStr)
  set dataFrom64 to current application’s NSData’s alloc()’s initWithBase64EncodedString:aStr options:(current application’s NSDataBase64DecodingIgnoreUnknownCharacters)
  
set aStr to current application’s NSString’s alloc()’s initWithData:dataFrom64 encoding:(current application’s NSUTF8StringEncoding)
  
return aStr as text –stringではなくtext
end detaFromBase64String

★Click Here to Open This Script 

2015/07/27 ASOCでバージョン番号文字列の正確な比較

NSStringのcompare: options:NSNumericSearchでバージョン文字列を比較したところ、

   ”10.10.0″ > “10.10″

という結果が出て、この内容については個人的にたいへん不満に思っておりました(おなじじゃん!)。

そこで、自分が納得できるバージョン文字列比較を行えるルーチンを作ってみました。ASOC的な部分とAS的な書き方がごっちゃになっているので、もう少しスマートに書けそうな気がします。

AppleScript名:ASOCでバージョン番号文字列の正確な比較
– Created 2015-07-27 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set a to “10.10″
set b to “10.10.0″
set aRes to compareVersionNumericStrings(a, b) of me
–>  ”A=B”

set a to “10.10″
set b to “10.10.0.1″
set aRes to compareVersionNumericStrings(a, b) of me
–>  ”B>A”

set a to “10.10.1″
set b to “10.10.0.1″
set aRes to compareVersionNumericStrings(a, b) of me
–>  ”A>B”

set a to “10.10.1″
set b to “10.9.1.1″
set aRes to compareVersionNumericStrings(a, b) of me
–> “A>B”

–バージョン番号文字列の正確な比較
on compareVersionNumericStrings(a, b)
  
  
set aList to parseVersionNumber(a) of me
  
–>  {​​​​​”10″, ​​​​​”10″​​​}
  
  
set bList to parseVersionNumber(b) of me
  
–>  {​​​​​”10″, ​​​​​”10″, ​​​​​”0″​​​}
  
  
set aLen to length of aList
  
set bLen to length of bList
  
  
if aLen = bLen then
    –何もしない
    
set buryTimes to 0
  else if aLen > bLen then
    set buryTimes to (aLen - bLen)
    
set bList to addNullItems(bList, “0″, buryTimes) of me
  else if bLen > aLen then
    set buryTimes to (bLen - aLen)
    
set aList to addNullItems(aList, “0″, buryTimes) of me
  end if
  
  
set cLen to length of aList –aListもbListも同じ長さなのでこれでいい
  
  
repeat with i from 1 to cLen
    
    
set aItem to contents of item i of aList
    
set bItem to contents of item i of bList
    
    
considering numeric strings
      if aItem > bItem then
        return “A>B”
      else if aItem < bItem then
        return “B>A”
      end if
    end considering
    
  end repeat
  
  
return “A=B”
  
end compareVersionNumericStrings

–バージョン番号文字列からメジャーバージョンを取り出し数値として返す
on parseVersionNumber(a)
  set aStr to current application’s NSString’s stringWithString:a
  
set aRes to (aStr’s componentsSeparatedByString:“.”)
  
set bRes to aRes’s allObjects()
  
return bRes as list
end parseVersionNumber

–指定リストの末尾に指定個数、埋め草を追加する
on addNullItems(aList, anItem, aTimes)
  
  
copy aList to bList
  
  
repeat aTimes times
    set the end of bList to anItem
  end repeat
  
  
return bList
  
end addNullItems

★Click Here to Open This Script 

AppleScript名:ASOCでNSStringによりバージョン文字列比較
– Created 2015-07-27 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aVer to “10.10.0″
set bVer to “10.10″

set aVerStr to current application’s NSString’s stringWithString:aVer
set bVerStr to current application’s NSString’s stringWithString:bVer

set aRes to aVerStr’s compare:bVer options:(current application’s NSNumericSearch)
–>  1

–1:aVer > bVer, 0:aVer = bVer, -1:bVer > aVer

★Click Here to Open This Script 

2015/07/27 on idleハンドラで1秒以下の間隔でタイマー割り込み処理を行う

現在ではAppleScriptObjCを使うと普通にできる処理ですが、「0.1秒間隔でのタイマー割り込み処理をやりたい、Mac OS X 10.5上で」という質問を見かけ、その場で思いついたのがこの方法です。

1秒間隔でbeep音を鳴らすアプレットを書く。

AppleScript名:on_idle_sample
on run
  
end run

on idle
  tell current application
    beep
  end tell
  
return 1
end idle

★Click Here to Open This Script 

デスクトップ上に1.app〜9.appまでアプリケーション書き出し or アプリケーションとして保存しておく。

on_idle.png

起動用Scriptがこちら。0.1秒間間隔で上記のアプレットを順次起動する。

AppleScript名:sub_second_idle_applet_launcher
set dtPath to POSIX path of (path to desktop)

repeat with i from 1 to 9
  
  
set aPath to dtPath & (i as string) & “.app”
  
do shell script “open “ & aPath
  
do shell script “sleep 0.1″
  
end repeat

★Click Here to Open This Script 

終了用Scriptがこちら。正確に0.1秒間隔かはわかりませんが、1秒よりも短い間隔でbeep音が鳴ります。つまり、beep処理を行っているところで本来の処理を行えばよいでしょう。アプレットの動的生成による並列処理で培った(無駄な)ノウハウが生きています。

日本の戦国時代には、「長篠の戦い」という有名な(&最近は本当にあった出来事なのか疑問視されていますが)戦いがあり、撃つのに時間がかかる当時の鉄砲を、3交代で撃つことで3倍の速射ができたよ、という出来事を参考にしているものです。

AppleScript名:sub_second_idle_applet_quter
set dtPath to POSIX path of (path to desktop)

repeat with i from 1 to 9
  
  
set aPath to dtPath & (i as string) & “.app”
  
tell application aPath to quit
  
end repeat

★Click Here to Open This Script 

2015/07/27 ASOCでAppleScript設定ファイルの読み込み

Cocoaの機能を用いてScript Editor自身の書式設定ファイル(plist)を読み込むAppleScriptです。

とくに特殊な処理は必要なく、一般的なplistファイルに記録されたDictionaryをrecordに読み込んで、最終的にNSDataから各種データに復元しています。もうちょっとこなれた書き方もありそうですが、とりあえずデータを取り出すレベルまで書いてみた次第です。

Scriptが返している各種Cocoaのオブジェクトについては、Apple純正Script Editorでは表示されず、ASObjC Explorer 4で表示されるものを記載しています。

本当に色とフォント種別(+サイズほか関連情報)しか入っていないのにはびっくりです。

AppleScript名:ASOCでAppleScript設定ファイルの読み込み
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set AppleScriptFontFile to “~/Library/Preferences/com.apple.applescript.plist”
set pathString to current application’s NSString’s stringWithString:AppleScriptFontFile
set newPath to pathString’s stringByExpandingTildeInPath()
set aRec to retDictFromPlist(newPath) of me

set asAttr to AppleScriptSourceAttributes of aRec
set asAttrArray to {}

repeat with i in asAttr
  
  set aCol to NSColor of i
  
set aFont to NSFont of i
  
  set aColO to (current application’s NSUnarchiver’s unarchiveObjectWithData:aCol)
  
set aFonO to (current application’s NSUnarchiver’s unarchiveObjectWithData:aFont)
  
  set the end of asAttrArray to {aColO, aFonO}
  
end repeat

asAttrArray
–>  {​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.568627 0.156863 0.564706 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.372549 0.368627 0.368627 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.054902 0.243137 0.984314 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.470588 0.203922 0.796079 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.988235 0.164706 0.109804 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0 0 0 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.568627 0.321569 0.0666667 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0 0 0 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.152941 0.788235 0.788235 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.0588235 0.243137 0.984314 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.121569 0.713726 0.988235 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.505882 0.227451 0.85098 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.364706 0.211765 0.572549 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.0352941 0.215686 0.733333 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.0313726 0.215686 0.733333 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.0352941 0.215686 0.729412 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.337255 0.215686 0.741176 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.203922 0.12549 0.388235 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}​​​}

–Read plist as record
on retDictFromPlist(aPath)
  set thePath to current application’s NSString’s stringWithString:aPath
  
set thePath to thePath’s stringByExpandingTildeInPath()
  
set theDict to current application’s NSDictionary’s dictionaryWithContentsOfFile:thePath
  
return theDict as record
end retDictFromPlist

★Click Here to Open This Script 

2015/07/23 ASOCでフォルダの連続作成1万個

ASOCでCocoaの機能を用いたフォルダ作成のテストを行いました。比較用にCocoaの機能を用いずにFinder経由で作成したものと、do shell script経由でシェルコマンドの機能を用いたものを用意して比較。

filegraph1.png

Cocoa経由で作成したものが非常に高速(Finder経由の7倍速ぐらい)ということが分かりました。ただし、テスト所要時間が実行するたびに変わり、ブレがあるのが特徴です(Finder経由で作成したときにはあまり見られない現象)。

テストはMacBook Pro Retina 2012 (Core i7 2.6GHz)、OS X 10.10.5上で実施。もちろん、HDDではなくSSDを使っていればこその速度です。

ASOCのテスト1は常識的なフォルダ作成のルーチンを作って呼び出したもの、テスト2は速度をかせぐために「連番のフォルダを作成する」という機能のかたまりを作成して、サブルーチンにはしなかったものです。ねらいどおり、若干後者の方が高速ですが・・・逆に、NSFileManager’s defaultManager() を1万回呼び出して無駄にオブジェクトを生成しても(テスト1)たいしたオーバーヘッドにならないんだなーということが体感的によく分かりました。

念のため、do shell scirpt経由でのフォルダ作成をためしてみたところ、予想外に時間がかかりました。do shell scirptコマンドによるシェルコマンド呼び出しのオーバーヘッドが大きいことが分かります(シェルコマンドが遅いのではなく、呼び出しのためのオーバーヘッドが大きい。シェル側からosascriptコマンドでAppleScriptを呼び出すと同様の現象が起こる)。

あまりにdo shell script使用バージョンが遅かったので、「mkdir -p」ではなく「mkdir」で実行してみたものの、大差はありませんでした。

Shane Stanleyが「do shell scriptコマンドなんか使うのやめようよ」(Cocoaの機能を呼び出したほうがいいぞ)と言っていた理由がよ〜くわかりました。

AppleScript名:ASOCでフォルダ作成テスト1
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

–計時用
set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()

repeat with i from 1 to 10000
  set aDirStr to “Desktop/test/” & (i as string)
  
set aRes to makeDirUnderHome(aDirStr) of me
end repeat

–計時用
set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c1Dat to b1Dat - a1Dat
–> 5.814414978027 (MacBook Pro Retina 2012, Core i7 2.6GHz)

on makeDirUnderHome(dirStr)
  
  
set homeDir to current application’s NSHomeDirectory()
  
set dirPath to homeDir’s stringByAppendingPathComponent:dirStr
  
set fileManager to current application’s NSFileManager’s defaultManager()
  
  
  
–Sample with continuation mark (¬) , similar to Objective-C format
  
set aRes to fileManager’s createDirectoryAtPath:dirPath ¬
    withIntermediateDirectories:true ¬
    
attributes:(missing value) ¬
    
|error|:(reference)
  
  
–Sample without continuation mark (¬)
  
–set aRes to fileManager’s createDirectoryAtPath:dirPath withIntermediateDirectories:true attributes:(missing value) |error|:(reference)
  
  
copy aRes to {aFlag, aError}
  
return aFlag as boolean
  
end makeDirUnderHome

★Click Here to Open This Script 

AppleScript名:ASOCでフォルダ作成テスト2
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

–計時用
set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()

set homeDir to current application’s NSHomeDirectory()
set fileManager to current application’s NSFileManager’s defaultManager()

repeat with i from 1 to 10000
  
  
set aDirText to “Desktop/test/” & (i as string)
  
set dirPath to (homeDir’s stringByAppendingPathComponent:aDirText)
  
  
set aRes to (fileManager’s createDirectoryAtPath:dirPath withIntermediateDirectories:true attributes:(missing value) |error|:(reference))
  
end repeat

–計時用
set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c1Dat to b1Dat - a1Dat
–> 4.124059021473 (MacBook Pro Retina 2012, Core i7 2.6GHz)

★Click Here to Open This Script 

AppleScript名:ASで(Finder経由で)フォルダ作成テスト(比較用)
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

–計時用
set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()

set dtPath to path to desktop
set dtPathStr to dtPath as string
set tFolStr to dtPathStr & “test:”

tell application “Finder”
  
  
tell folder dtPath
    if (exists of folder “test”) = false then
      (make new folder with properties {name:“test”})
    end if
  end tell
  
  
repeat with i from 1 to 10000
    make new folder with properties {name:(i as string)} at folder tFolStr
  end repeat
  
end tell

–計時用
set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c1Dat to b1Dat - a1Dat
–> 37.555700004101

★Click Here to Open This Script 

AppleScript名:ASでフォルダ作成テスト(比較用2)
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

–計時用
set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()

set dtPath to path to desktop
set dtPathStr to dtPath as string
set tFolStr to dtPathStr & “test:”
set tFolPosix to POSIX path of tFolStr

repeat with i from 1 to 10000
  set tPath to tFolPosix & (i as string)
  
do shell script “mkdir -p “ & tPath
end repeat

–計時用
set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c1Dat to b1Dat - a1Dat
–> 272.857131958008

★Click Here to Open This Script 

AppleScript名:ASでフォルダ作成テスト(比較用3)
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

–計時用
set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()

set dtPath to path to desktop
set dtPathStr to dtPath as string
set tFolStr to dtPathStr & “test:”
set tFolPosix to POSIX path of tFolStr

do shell script “mkdir -p “ & tFolPosix

repeat with i from 1 to 10000
  set tPath to tFolPosix & (i as string)
  
do shell script “mkdir “ & tPath
end repeat

–計時用
set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c1Dat to b1Dat - a1Dat
–> 283.687083005905

★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/20 JSON文字列とrecordの相互変換

Cocoaの機能を用いて、JSON文字列をAppleScriptのrecordに変換、あるいはその逆変換を行うAppleScriptです。

recordまで変換しなくても、NSDictionaryとかNSMutableDictionaryで値を保持しておくほうが(入り組んだデータの場合に)アクセスが楽なので、recordにするかどうかは処理する人の自由です。

ただ、こうしてテストScriptを書いて思うのは、ShaneのASObjC Explorer 4の結果表示機能は(ASOCでScriptingを行うのに)必須のものだということです。

AppleScript名:ASOCでrecordをJSON文字列に
– Created 2015-07-20 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aRec to {abc:“test”, bcd:“test2″, cde:“test3″}

set aDict to current application’s NSDictionary’s dictionaryWithDictionary:aRec
–>  (NSDictionary) {abc:”test”, bcd:”test2″, cde:”test3″}

set jsonData to current application’s NSJSONSerialization’s dataWithJSONObject:aDict options:(0 as integer) |error|:(missing value) –0 is NSJSONWritingPrettyPrinted
–> (* (NSData) <7b226162 63223a22 74657374 222c2262 6364223a 22746573 7432222c 22636465 223a2274 65737433 227d> *)

set resString to current application’s NSString’s alloc()’s initWithData:jsonData encoding:(current application’s NSUTF8StringEncoding)
–>  (NSString) “{”abc”:”test”,”bcd”:”test2″,”cde”:”test3″}”

★Click Here to Open This Script 

AppleScript名:ASOCでjson文字列をrecordに
– Created 2015-07-20 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set jsonText to “{\”abc\”:\”test\”,\”bcd\”:\”test2\”,\”cde\”:\”test3\”}”

set jsonString to current application’s NSString’s stringWithString:jsonText
–>  (NSString) “{”abc”:”test”,”bcd”:”test2″,”cde”:”test3″}”

set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
–>  (NSData) <7b226162 63223a22 74657374 222c2262 6364223a 22746573 7432222c 22636465 223a2274 65737433 227d>

set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
–>  (NSDictionary) {abc:”test”, bcd:”test2″, cde:”test3″}

set aRec to aJsonDict as record
–>  {abc:”test”, bcd:”test2″, cde:”test3″}

★Click Here to Open This Script 

2015/07/18 指定のURLのYouTubeのムービーをOffLiberty経由でローカルにダウンロード v2

指定のURLのYouTube上のムービーをOffLiberty経由でダウンロードするAppleScriptのバージョンアップ版です。

yyyeyoyycyaye-2015-07-18-172834.png

先日のScriptを部品として呼び出し、複数のURLのムービーを連続してダウンロードするように処理を行ってみたところ、いくつかのムービーでビデオではなくオーディオのファイルがダウンロードされておりました。

OffLivertyサイト側が対策したのか、あるいは複数回リクエストすると挙動が変わるのか、普通にリクエストしただけではオーディオファイルのリンクが帰ってくるようになっていました。

OffLivertyのサイトがデータ処理結果を返すパターンには、まだ自分が遭遇していないようなケースもあるかもしれないので、「以前は動いていたが、最近動かなくなった」といった報告を求めたいところです(GUI Scriptingを有効にする設定をしていなかったとか、そもそもOS X 10.10を使っていないというケース以外で、、、)。

ret_audio.png

そこで、オーディオだけ帰ってきたとおぼしき場合には、「I want video file」のボタンをクリックして作業を継続させるようにしてみました。これで、テキストから与えた複数のYouTube上のビデオファイルをローカルに順次ダウンロードできるようになりました。

AppleScript名:指定のURLのYouTubeのムービーをOffLiberty経由でローカルにダウンロード v2
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

–ダウンロードしたいYouTubeのムービーのURL
set aURL to “https://www.youtube.com/watch?t=261&v=k95yIQn2Sdg”
set aRes to downloadYoutubeMovieViaOffLiberty(aURL) of me
–> true (OK) / false (NG)

–指定のURLのYouTubeのムービーをOffLiverty経由でローカルにダウンロード(2015/7/18 Version)
on downloadYoutubeMovieViaOffLiberty(aURL)
  
  
set groupList to {6, 3} –Click Link Group in OffLiberty Page
  
  
–OffLibertyの呼び出しブックマークレット
  
set jsText to “var title = window.location.href;
  if (title.indexOf(’http://offliberty.com’) == 0) { (function(){ location.href=’http://offliberty.com/#bookmarklet’; })(); }
  else { (function(){ window.open(’http://offliberty.com/#’+encodeURIComponent(location.href)); })(); }”

  
  
tell application “Safari”
    close every document
  end tell
  
  
  
tell application “Safari”
    make new document with properties {URL:aURL}
    
delay 1
    
do JavaScript jsText in document 1
    
activate
  end tell
  
  
tell current application
    delay 10
  end tell
  
  
–delay後に実行しないとHTML Viewへの参照が取得できなかった(要注意ポイント)
  
set aHTMLView to retSafariHTMLViewReference() of me
  
  
tell application “Safari”
    activate
  end tell
  
  
  
set hitF to false
  
  
repeat with aCount from 1 to 1000
    
    
tell current application
      delay 5
    end tell
    
    
set aTitle to “”
    
repeat with ii in groupList
      
      
set jj to contents of ii
      
      
tell application “System Events”
        tell aHTMLView
          
          
tell group jj –1回変換したものはサイト側でキャッシュしているようなのでループ(6–>3)で対応
            tell UI element 1
              try
                set aTitle to title
              end try
            end tell
          end tell
          
          
if (jj = 3 and aTitle = “Right-click here and ’Save link as…’”) then
            –オーディオファイルをダウンロードさせようとして、ビデオを直接ダウンロードさせない場合の対応
            
tell group 4
              click button 1 –”I want video file”ボタンをクリック
              
              
tell current application
                delay 5
              end tell
              
            end tell
          else if jj = 6 and aTitle = “Right-click here and ’Save link as…’” then
            set hitF to true
            
tell group jj
              tell UI element 1
                click –Download by clicking link
                
                
tell current application
                  delay 5
                end tell
                
                
return hitF
              end tell
            end tell
            
          end if
          
        end tell
      end tell
      
    end repeat
    
  end repeat
  
  
return hitF
  
end downloadYoutubeMovieViaOffLiberty

–SafariのWebViewへのGUI Scripting的な参照を取得する
on retSafariHTMLViewReference()
  
  
set aRes to detectSafariWindow1Status() of me
  
if aRes = false then return false
  
  
tell application “Safari”
    set aVer to version
  end tell
  
  
set majorVer to retMajorVersionNumber(aVer as string) of me
  
  
if majorVer 9 then
    set aRes to getRefOverSafari9() of me
  else
    set aRes to getRefUnderSafari8() of me
  end if
  
  
return aRes
  
end retSafariHTMLViewReference

–Safari 9 Betaに対応
on getRefOverSafari9()
  tell application “System Events”
    tell process “Safari”
      tell window 1
        return UI element 1 of scroll area 1 of group 1 of group 1 of tab group 1
      end tell
    end tell
  end tell
end getRefOverSafari9

–Safari 8に対応
on getRefUnderSafari8()
  tell application “System Events”
    tell process “Safari”
      
      
set wList to every window
      
set wLen to length of wList
      
if wLen = 0 then return false
      
      
tell window 1
        repeat with i from 1 to 10 –Usually from 1 to 5
          try
            tell group i
              tell UI element 1 of scroll area 1 of group 1 of group 1
                set aProp to role
                
if aProp = “AXWebArea” then
                  return (UI element 1 of scroll area 1 of group 1 of group 1 of group i of window 1 of process “Safari” of application “System Events”)
                end if
              end tell
            end tell
          end try
          
        end repeat
      end tell
      
    end tell
  end tell
end getRefUnderSafari8

–Safariでオープン中のURLのローディング完了を待つ
on page_loaded(timeout_value)
  tell current application
    delay 1
  end tell
  
  
repeat with i from 1 to the timeout_value
    tell application “Safari”
      if (do JavaScript “document.readyState” in document 1) is “complete” then
        return true
      else if i is the timeout_value then
        return false
      else
        tell current application
          delay 1
        end tell
      end if
    end tell
  end repeat
  
return false
end page_loaded

–バージョン番号文字列からメジャーバージョンを取り出し数値として返す
on retMajorVersionNumber(a)
  
  
set aStr to current application’s NSString’s stringWithString:a
  
–> “10.0.1″ (NSString)
  
  
set aRes to (aStr’s componentsSeparatedByString:“.”)
  
–> {”10″,”0″,”1″} (NSArray)
  
  
set bRes to aRes’s firstObject()
  
–> “10″ (NSString)
  
  
set cRes to bRes’s integerValue()
  
–> 10
  
  
return cRes as integer
  
end retMajorVersionNumber

–Window 1が通常のWebページを表示しているかどうか判定
on detectSafariWindow1Status()
  
  
–最前面のWindowの表示状態を取得して「処理対象外」のケースを検出する
  
tell application “Safari”
    –Windowの枚数をかぞえる
    
set wCount to count every window
    
if wCount = 0 then return false –Windowなしの場合には処理対象外
    
    
tell window 1
      set aProp to properties
    end tell
    
  end tell
  
  
  
try
    –Window 1に該当するdocumentを取得する
    
set aDoc to document of aProp
    
    
tell application “Safari”
      tell aDoc
        set bProp to properties
      end tell
    end tell
    
  on error
    –エラーが発生するのは通常ページ「ではない」ものを表示しているため、と判断
    
return false –Displaying pages of iCloud connected devices
    
  end try
  
  
  
tell application “Safari”
    set aURL to (URL of bProp)
  end tell
  
  
  
–「お気に入り」か「Pages of iCloud connected devices」表示中なら
  
if aURL = “favorites://” or aURL = missing value then
    return false
  end if
  
  
  
return true
  
end detectSafariWindow1Status

★Click Here to Open This Script 

2015/07/17 Finderで選択中の戦場の絆のリプレイムービーのファイル名を取得して対戦人数を集計する

YouTubeからダウンロードした「戦場の絆」のリプレイムービー(複数)をFinder上で選択しておくと、ファイル名を取得してファイル名に記載されている「4VS4」などの文字列をもとに、各対戦人数(1VS1〜8VS8)の該当個数をカウントするAppleScriptです。

scrn1.png

scrn3.png

res.png

Finder上のファイルを処理するときに、

Finder上で選択中のファイルを取得する
choose fileコマンドで選択する
choose folderコマンドでフォルダを選択してその中に入っているファイルを処理する

といったあたりが定番です。ドラッグ&ドロップを受け付ける「ドロップレット」にすることも可能ですが、ドロップレットにするとデバッグが非常に困難になるため、最初はこのような形態のScriptにすることを強く推奨します。

AppleScript名:Finderで選択中の戦場の絆のリプレイムービーのファイル名を取得して対戦人数を集計する
tell application “Finder”
  set aSel to selection as alias list –Finder上で選択中のファイルを取得する定番の書き方
end tell

if aSel = {} then return –何もFinder上で選ばれていなかった場合

set outStr to “”

repeat with i from 1 to 8
  set aVSstr to makeVSstr(i) of me
  
set aCount to 0
  
  
repeat with ii in aSel
    tell application “System Events”
      set aName to name of ii
      
if aVSstr is in aName then
        set aCount to aCount + 1
      end if
    end tell
  end repeat
  
  
if aCount > 0 then
    set outStr to outStr & aVSstr & “:” & (aCount as string) & return
  end if
  
end repeat

outStr –「結果」への結果出力

– Sub-routine: 4 –> “4VS4″
on makeVSstr(aNum)
  set aNumStr to aNum as string
  
return aNumStr & “VS” & aNumStr
end makeVSstr

★Click Here to Open This Script 

2015/07/17 ASOCでファイル名の文字列から拡張子を削る

NSStringのstringByDeletingPathExtension()を使ってフルパスから拡張子を削るテストは行っていましたが、フルパスではなくファイル名から削ることはできないか試してみたものです。

問題なく削れました。

AppleScript名:ASOCでファイル名の文字列から拡張子を削る
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

set aPath to "aTest.test"
set pathString to current application’s NSString’s stringWithString:aPath
set newPath to pathString’s stringByDeletingPathExtension() as string
–>  "aTest"

★Click Here to Open This Script 

2015/07/16 指定のURLのYouTubeのムービーをOffLiberty経由でローカルにダウンロード

指定のURLのYouTube上のムービーをOffLiberty経由でダウンロードするAppleScriptです。

ab58f773-fd6d-4e02-8054-c54031f24d91.jpg

a6f92604-4a92-4a35-8bc6-4773ae596f1e.jpg

4d180b9b-786f-4195-a4e8-55968aa7aded.jpg

GUI Scriptingを使用しているため、システム環境設定の「セキュリティとプライバシー」>「アクセシビリティ」で実行するプログラム(たぶんScript Editor)にあらかじめ実行許可を与えておく必要があります。

アーケードゲーム「戦場の絆」のリプレイムービー(5分ぐらい)をローカルにダウンロードする目的で作成したので、それよりも長い時間のものについては修正する必要があるかもしれません。

dl_movie.png

また、OffLibertyのサービスが止まったら動作できません。OffLibertyのサイトの応答時間が著しく伸びたような場合にも想定どおりには処理できません。トラブルへの対応を行うために「サイト自体の応答がなかったらエラーにする」「所定の応答待ち時間を超えたらエラーにする」処理が必要になることでしょう。

OffLibertyのサイトにBookmarkletが置いてあったので、これを解析してローカルからSafariのdo javascriptコマンドで実行できるように書き換えてみました。

BookmarkletのJavaScriptはたいした処理を行っていないので、処理内容をAppleScriptに完全に書き換えてしまうことも可能です。

AppleScript名:指定のURLのYouTubeのムービーをOffLiberty経由でローカルにダウンロード.scpt
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

–ダウンロードしたいYouTubeのムービーのURL
set aURL to “https://www.youtube.com/watch?v=OPF2f8HRrmQ&feature=youtu.be”
set aRes to downloadYoutubeMovieViaOffLiberty(aURL) of me
–> true (OK) / false (NG)

–指定のURLのYouTubeのムービーをOffLiverty経由でローカルにダウンロード
on downloadYoutubeMovieViaOffLiberty(aURL)
  
  
set groupList to {6, 3} –Click Link Group in OffLiberty Page
  
  
–OffLibertyの呼び出しブックマークレット
  
set jsText to “var title = window.location.href;
  if (title.indexOf(’http://offliberty.com’) == 0) { (function(){ location.href=’http://offliberty.com/#bookmarklet’; })(); }
  else { (function(){ window.open(’http://offliberty.com/#’+encodeURIComponent(location.href)); })(); }”

  
  
tell application “Safari”
    close every document
  end tell
  
  
  
tell application “Safari”
    make new document with properties {URL:aURL}
    
delay 1
    
do JavaScript jsText in document 1
    
activate
  end tell
  
  
tell current application
    delay 10
  end tell
  
  
–delay後に実行しないとHTML Viewへの参照が取得できなかった(要注意ポイント)
  
set aHTMLView to retSafariHTMLViewReference() of me
  
  
tell application “Safari”
    activate
  end tell
  
  
  
set hitF to false
  
  
repeat with aCount from 1 to 600
    
    
tell current application
      delay 2
    end tell
    
    
set aTitle to “”
    
repeat with ii in groupList
      
      
set jj to contents of ii
      
      
tell application “System Events”
        tell aHTMLView
          tell group jj –1回変換したものはサイト側でキャッシュしているようなのでループ(6–>3)で対応
            
            
tell UI element 1
              try
                set aTitle to title
              end try
              
              
if aTitle = “Right-click here and ’Save link as…’” then
                set hitF to true
                
click –Download by clicking link
                
return hitF
              end if
            end tell
          end tell
        end tell
      end tell
      
    end repeat
    
  end repeat
  
  
return hitF
  
end downloadYoutubeMovieViaOffLiberty

–SafariのWebViewへのGUI Scripting的な参照を取得する
on retSafariHTMLViewReference()
  
  
set aRes to detectSafariWindow1Status() of me
  
if aRes = false then return false
  
  
tell application “Safari”
    set aVer to version
  end tell
  
  
set majorVer to retMajorVersionNumber(aVer as string) of me
  
  
if majorVer 9 then
    set aRes to getRefOverSafari9() of me
  else
    set aRes to getRefUnderSafari8() of me
  end if
  
  
return aRes
  
end retSafariHTMLViewReference

–Safari 9 Betaに対応
on getRefOverSafari9()
  tell application “System Events”
    tell process “Safari”
      tell window 1
        return UI element 1 of scroll area 1 of group 1 of group 1 of tab group 1
      end tell
    end tell
  end tell
end getRefOverSafari9

–Safari 8に対応
on getRefUnderSafari8()
  tell application “System Events”
    tell process “Safari”
      
      
set wList to every window
      
set wLen to length of wList
      
if wLen = 0 then return false
      
      
tell window 1
        repeat with i from 1 to 10 –Usually from 1 to 5
          try
            tell group i
              tell UI element 1 of scroll area 1 of group 1 of group 1
                set aProp to role
                
if aProp = “AXWebArea” then
                  return (UI element 1 of scroll area 1 of group 1 of group 1 of group i of window 1 of process “Safari” of application “System Events”)
                end if
              end tell
            end tell
          end try
          
        end repeat
      end tell
      
    end tell
  end tell
end getRefUnderSafari8

–Safariでオープン中のURLのローディング完了を待つ
on page_loaded(timeout_value)
  tell current application
    delay 1
  end tell
  
  
repeat with i from 1 to the timeout_value
    tell application “Safari”
      if (do JavaScript “document.readyState” in document 1) is “complete” then
        return true
      else if i is the timeout_value then
        return false
      else
        tell current application
          delay 1
        end tell
      end if
    end tell
  end repeat
  
return false
end page_loaded

–バージョン番号文字列からメジャーバージョンを取り出し数値として返す
on retMajorVersionNumber(a)
  
  
set aStr to current application’s NSString’s stringWithString:a
  
–> “10.0.1″ (NSString)
  
  
set aRes to (aStr’s componentsSeparatedByString:“.”)
  
–> {”10″,”0″,”1″} (NSArray)
  
  
set bRes to aRes’s firstObject()
  
–> “10″ (NSString)
  
  
set cRes to bRes’s integerValue()
  
–> 10
  
  
return cRes as integer
  
end retMajorVersionNumber

–Window 1が通常のWebページを表示しているかどうか判定
on detectSafariWindow1Status()
  
  
–最前面のWindowの表示状態を取得して「処理対象外」のケースを検出する
  
tell application “Safari”
    –Windowの枚数をかぞえる
    
set wCount to count every window
    
if wCount = 0 then return false –Windowなしの場合には処理対象外
    
    
tell window 1
      set aProp to properties
    end tell
    
  end tell
  
  
  
try
    –Window 1に該当するdocumentを取得する
    
set aDoc to document of aProp
    
    
tell application “Safari”
      tell aDoc
        set bProp to properties
      end tell
    end tell
    
  on error
    –エラーが発生するのは通常ページ「ではない」ものを表示しているため、と判断
    
return false –Displaying pages of iCloud connected devices
    
  end try
  
  
  
tell application “Safari”
    set aURL to (URL of bProp)
  end tell
  
  
  
–「お気に入り」か「Pages of iCloud connected devices」表示中なら
  
if aURL = “favorites://” or aURL = missing value then
    return false
  end if
  
  
  
return true
  
end detectSafariWindow1Status

★Click Here to Open This Script 

2015/07/14 訂正:Microsoft Office 2016のAppleScript用語辞書は2011とほぼ同レベル

Microsoft OfficeのAppleScript用語辞書のサイズについて、HTML書き出ししたものをバージョンごとに比較。Office 2016について「用語辞書が極端に小さい」という結論を出していましたが、これは誤りでした

Office 2016のWord、Excel、PowerPointではAppleScript用語辞書の形態が変更になったようで、そのままではHTML書き出しすることができませんでしたが、Script Editorで用語辞書をオープンし、その用語辞書ファイルを直接書き出すことで、バージョンごとのサイズの比較を正確に行えました。

office2016_crisis2.png

結果からいえば、Office 2016はOffice 2011と同じレベルの大きさのAppleScript用語辞書を持っています。ただし、Office 2011で行えていた記述がそのまま通じるかどうかについては、個別のScriptのチェックが必要です。

2015/07/14 ASOCでファイルパス関連のじっけん

ShaneがCocoaの有用なmethodを「このあたり、見ておくと便利でいいよ!」と教えてくれました(Many many thanks!)。

さっそく、実際にASOCのscriptを書いて、動作を確認してみました。

ちなみに、こうしたASOCの実行結果はエディタごとに異なり、現時点ではShaneのASObjC Explorer 4のみ、Cocoaのオブジェクトを文字列化して結果表示できます。

asobjcexp.png
▲ASObjC Explorer 4上の結果表示

scripteditor.png
▲Script Editor 2.7上の結果表示

asdebugger.png
▲Script Debugger 5上の結果表示

はたして、OS X 10.11のScript Editor 2.8(多分)は、Cocoaオブジェクトが結果に返ってきたときに、きちんと表示できるようになっているものでしょうか?

AppleScript名:ASOCで指定フォルダ以下の指定ファイル名称リストから、フルパスのリストを作成
–By Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set pathString to POSIX path of (path to desktop)
set pathString to current application’s NSString’s stringWithString:pathString
set newPaths to (pathString’s stringsByAppendingPaths:{“One.txt”, “Two.txt”, “Three.txt”, “Four.txt”}) as list
–>  {​​​​​”/Users/me/Desktop/One.txt”, ​​​​​”/Users/me/Desktop/Two.txt”, ​​​​​”/Users/me/Desktop/Three.txt”, ​​​​​”/Users/me/Desktop/Four.txt”​​​}

★Click Here to Open This Script 

AppleScript名:ASOCで指定ファイルの拡張子を取得する
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set a to choose file
set aPath to POSIX path of a
set pathString to current application’s NSString’s stringWithString:aPath
set newPath to pathString’s pathExtension()
–>  (NSString) “jpg”

★Click Here to Open This Script 

AppleScript名:ASOCで指定パス文字列のチルダを展開したフルパス文字列を取得する
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set a to choose file
set aPath to POSIX path of a
set pathString to current application’s NSString’s stringWithString:aPath
set newPath to pathString’s stringByExpandingTildeInPath()
–>  (NSString) “/Users/me/Desktop/IMG_~3029.jpg”

set bPath to “~/Desktop”
set pathString to current application’s NSString’s stringWithString:bPath
set newPath to pathString’s stringByExpandingTildeInPath()
–>  (NSString) “/Users/me/Desktop”

★Click Here to Open This Script 

AppleScript名:ASOCで指定ファイルのフルパスから拡張子を削除した文字列を取得する
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set a to choose file
set aPath to POSIX path of a
set pathString to current application’s NSString’s stringWithString:aPath
set newPath to pathString’s stringByDeletingPathExtension()
–>  (NSString) “/Users/me/Desktop/aTest”

★Click Here to Open This Script 

AppleScript名:ASOCで指定ファイルの親フォルダを取得する
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set a to choose file
set aPath to POSIX path of a
set pathString to current application’s NSString’s stringWithString:aPath
set newPath to pathString’s stringByDeletingLastPathComponent()
–>  (NSString) “/Users/me/Desktop”

★Click Here to Open This Script 

AppleScript名:ASOCで指定ファイルのファイル名を取得する
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set a to choose file
set aPath to POSIX path of a
set pathString to current application’s NSString’s stringWithString:aPath
set newPath to pathString’s lastPathComponent()
–>  (NSString) “PropDiff.zip”

★Click Here to Open This Script 

2015/07/13 URLByAppendingPathComponentのじっけん

NSURLの「URLByAppendingPathComponent:」がどの程度、想定外なファイルパス形式に対応できるのか実験しておきました。

正しいパス(比較用):
–> (NSURL) file:///Users/me/Library/Application%20Support/test

テスト1:HFSパスの末尾に余計な「:」をつけてみた
–> “Macintosh HD:Users:me:Library:Application Support::”
–> (NSURL) file:///Users/me/Library/test

テスト2:HFSパスの末尾の「:」を削除してみた
–> “Macintosh HD:Users:me:Library:Application Support”
–> (NSURL) file:///Users/maro/Library/Application%20Support/test

テストしたら、HFSパスの末尾に「:」がなかった場合には対応できていますが、HFSパスの末尾に「::」がついた場合には、フォルダがひとつ上の階層が指定されました。一応、HFSパスの仕様に「::」はUNIXの「..」とほぼ等価な挙動をするように定義されているので、この挙動で正しいと思います。

HFSパス末尾の「:」が抜けた場合にはURLByAppendingPathComponentは対応してくれるけれど、末尾が「::」になった場合には1階層上が指定されることになるよ、という挙動を確認できました。

AppleScript名:URLByAppendingPathComponentのじっけん
use AppleScript version "2.4"
use framework "Foundation"
use scripting additions
–use script "BridgePlus" — instead of ASOBjCExtras framework
–load framework — BridgePlus command to load

set folName to "test"

set aPath to (path to application support from user domain)
set bPath to aPath as string

–TEST 間違ったパスを組み立ててみた(1)
set cPath to bPath & ":" –わざと間違ったデータを作成
–>  "Macintosh HD:Users:me:Library:Application Support::"–わざと「::」を末尾につけてみた
set myAppSupDir to POSIX path of cPath
–> "/Users/me/Library/Application Support/"
set folderURL to (current application’s class "NSURL"’s fileURLWithPath:myAppSupDir)’s URLByAppendingPathComponent:folName
–>  (NSURL) file:///Users/me/Library/test

–TEST 間違ったパスを組み立ててみた(2)
set cPath to text 1 thru -2 of bPath –わざと間違ったデータを作成
–>  "Macintosh HD:Users:me:Library:Application Support"
set myAppSupDir2 to POSIX path of cPath
–>  "/Users/me/Library/Application Support"
set folderURL2 to (current application’s class "NSURL"’s fileURLWithPath:myAppSupDir2)’s URLByAppendingPathComponent:folName
–>  (NSURL) file:///Users/maro/Library/Application%20Support/test

–正しいパスを(比較のために)組み立ててみた
set myAppSupDir3 to POSIX path of bPath
set folderURL3 to (current application’s class "NSURL"’s fileURLWithPath:myAppSupDir3)’s URLByAppendingPathComponent:folName
–>  (NSURL) file:///Users/me/Library/Application%20Support/test

★Click Here to Open This Script 

2015/07/13 ASOCでDict書き込み_3(Bridge Plus)

完全に趣味の内容のAppleScriptを、Shane Stanleyがアップデートして送ってくれました(汗)。趣味のScriptというのは、完全に作り捨てに近いというか、あまり後先考えずに作ってしまうことが多いですが・・・

書き換えポイントは、

 (1)ASObjcExtras.frameworkではなくBridgePlusライブラリを使用
 (2)NSFileManagerでディレクトリを作成
 (3)パスの組み立て時に「URLByAppendingPathComponent:」を使うことにより、フォルダを示すパスの末尾に「:」がついていなかったとか、「::」といった誤った表記を行ってしまった場合に備えている

とのこと。do shell scriptなんてダサいぜ、的なShaneのご意見もありましたが、使って簡潔になる箇所(dateコマンドとか)は使ってもいいんじゃないかと。いえ、意見はとってもわかるんですけど、「使えるものはなんでも使う派」なんで(^ー^;;

OS X 10.11ではaliasやfileとNSURLが正確にBridgeされるようになるので、パスの取り扱いについては機能向上(簡潔な記述)が期待ができそうです。”NSURL”を書くと予約語とぶつかる、とかいうダサダサな仕様が直ることは10.11に期待したいです。

しかし、英語圏のShaneから正確な日本語データの入ったプログラムが送られてくると(リスト内のリンクをクリックすればScript Editorに転送されるのはわかっているとはいえ)、かなりビビります(^ー^;;

AppleScript名:ASOCでDict書き込み_3(Bridge Plus)
use AppleScript version “2.4″
use framework “Foundation”
use scripting additions
use script “BridgePlus” – instead of ASOBjCExtras framework

load framework – BridgePlus command to load

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}}

– BridgePlus uses SMSForder instead of SMSFord in ASOBjCExtras, but method is the same
set aArray to current application’s SMSForder’s subarraysIn:b1List asDictionariesUsingLabels:a1List |error|:(missing value)

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

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

on saveRecordToFolAsPlist(theRecord, folName, aName)
  
  
set myAppSupDir to POSIX path of (path to application support from user domain)
  
set folderURL to (current application’s class “NSURL”’s fileURLWithPath:myAppSupDir)’s URLByAppendingPathComponent:folName
  
  
–do shell script(mkdir -p)のかわりに、指定ディレクトリまで作成
  
current application’s NSFileManager’s defaultManager()’s createDirectoryAtURL:folderURL withIntermediateDirectories:true attributes:(missing value) |error|:(missing value)
  
  
set theDict to current application’s NSDictionary’s dictionaryWithDictionary:theRecord
  
set aRes to theDict’s writeToURL:(folderURL’s URLByAppendingPathComponent:aName) atomically:true
  
  
return aRes as boolean
  
end saveRecordToFolAsPlist

★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/07/02 SafariのWebViewへのGUI Scripting的な参照を取得する v2

Safariに表示中のWebコンテンツをGUI Scriptingでコントロールする場合に必要となる、WebView部品へのGUI Scripting的な参照を取得するAppleScriptの、Safari v9 Beta対応版です。

Safariを強引に動かすAppleScriptのプログラムは多々あり(銀行口座へのログインから、オンライン通販サイトのデータダウンロード、戦場の絆の会員サイトからの搭乗履歴の取得などなど)、たいへんに差し迫った必要性があり新バージョンのSafariに対応させたものです。ただし、Safari v9自体がまだBeta段階のものなので今後変更になる可能性もあります。

以前のSafariは、お世辞にも綺麗なオブジェクト階層になっていませんでした。ローカライズも一般のCocoaアプリとはほど遠い状態でした。

新しいバージョンでは、より一般的なOS Xのアプリケーションのように(GUI Scriptingで)見えるようになりました。これに合わせています。

AppleScript名:SafariのWebViewへのGUI Scripting的な参照を取得する v2
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

set aRes to retSafariHTMLViewReference()
–> UI element 1 of scroll area 1 of group 1 of group 1 of tab group 1 of window "戦場の絆" of application process "Safari" of application "System Events"

–SafariのWebViewへのGUI Scripting的な参照を取得する
on retSafariHTMLViewReference()
  tell application "Safari"
    set aVer to version
  end tell
  
  
set majorVer to retMajorVersionNumber(aVer as string) of me
  
  
if majorVer 9 then
    set aRes to getRefOverSafari9() of me
  else
    set aRes to getRefUnderSafari8() of me
  end if
  
  
return aRes
  
end retSafariHTMLViewReference

–Safari 9 Betaに対応
on getRefOverSafari9()
  tell application "System Events"
    tell process "Safari"
      tell window 1
        return UI element 1 of scroll area 1 of group 1 of group 1 of tab group 1
      end tell
    end tell
  end tell
end getRefOverSafari9

–Safari 8に対応
on getRefUnderSafari8()
  tell application "System Events"
    tell process "Safari"
      
      
set wList to every window
      
set wLen to length of wList
      
if wLen = 0 then return false
      
      
tell window 1
        repeat with i from 1 to 10 –Usually from 1 to 5
          try
            tell group i
              tell UI element 1 of scroll area 1 of group 1 of group 1
                set aProp to role
                
if aProp = "AXWebArea" then
                  return (UI element 1 of scroll area 1 of group 1 of group 1 of group i of window 1 of process "Safari" of application "System Events")
                end if
              end tell
            end tell
          end try
          
        end repeat
      end tell
      
    end tell
  end tell
end getRefUnderSafari8

–Safariでオープン中のURLのローディング完了を待つ
on page_loaded(timeout_value)
  tell current application
    delay 1
  end tell
  
  
repeat with i from 1 to the timeout_value
    tell application "Safari"
      if (do JavaScript "document.readyState" in document 1) is "complete" then
        return true
      else if i is the timeout_value then
        return false
      else
        tell current application
          delay 1
        end tell
      end if
    end tell
  end repeat
  
return false
end page_loaded

–バージョン番号文字列からメジャーバージョンを取り出し数値として返す
on retMajorVersionNumber(a)
  
  
set aStr to current application’s NSString’s stringWithString:a
  
–> "10.0.1" (NSString)
  
  
set aRes to (aStr’s componentsSeparatedByString:".")
  
–> {"10","0","1"} (NSArray)
  
  
set bRes to aRes’s firstObject()
  
–> "10" (NSString)
  
  
set cRes to bRes’s integerValue()
  
–> 10
  
  
return cRes as integer
  
end retMajorVersionNumber

★Click Here to Open This Script 

2015/07/02 バージョン番号文字列からメジャーバージョンを取り出し数値として返す v4

バージョン番号文字列を処理するケースが多々あり、似たようなルーチンを作り捨ててきましたが、使い回すために動作状況がよくわかるよう可読性を高めてみました。

 ”10.0.1″→10
 ”9.10″→9

ただそれだけのものです。

AppleScript名:バージョン番号文字列からメジャーバージョンを取り出し数値として返す v4
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

set a to "10.0.1"
set b to retMajorVersionNumber(a) of me
–> 10

set a to "9.10"
set b to retMajorVersionNumber(a) of me
–> 9

–バージョン番号文字列からメジャーバージョンを取り出し数値として返す
on retMajorVersionNumber(a)
  
  
set aStr to current application’s NSString’s stringWithString:a
  
–> "10.0.1" (NSString)
  
  
set aRes to (aStr’s componentsSeparatedByString:".")
  
–> {"10","0","1"} (NSArray)
  
  
set bRes to aRes’s firstObject()
  
–> "10" (NSString)
  
  
set cRes to bRes’s integerValue()
  
–> 10
  
  
return cRes as integer
  
end retMajorVersionNumber

★Click Here to Open This Script 

2015/07/01 iTunes, iBooks Author, XcodeなどOS X 10.10.4に合わせてアップデート

OS X 10.10.4が公開され、同時にiTunes 12.2.0、iBooks Author 2.3、GarageBand 10.1、Xcode 6.4なども公開されました。

OS X 10.10.4では、既存のバグのうち直ったものもあれば直っていないものもあり・・・かつ長期的にテストするとやっぱり動いていないようなもの(Message.appのメッセージ受信などのイベントハンドラで実行するAppleScriptについては、長期間テストしてみると動かないケースもあったりで)もあり、10.10.x系列のバグ取りに協力すべきなのか10.11系列のテストに移行すべきなのかいまひとつはっきりしません。

とりあえず、AppleScript的に見た各アプリケーションの対応度の変化を調べてみました。用語辞書レベルではとくに変化はないように見えます(すべて前バージョンとdiffで比較してみました)。

iTunes 12.2

AppleScript用語辞書の内容について、前バージョンからの変化はありません。

iBooks Author 2.3

用語辞書はついているものの、アプリケーションの機能をほとんどサポートしておらず、「仕事しているフリ」というレベルです。前バージョンと変化はありません。現状では本アプリで行う仕事を自動化するのに役立つ機能はAppleScriptなどのOSA言語からは一切利用できません(書類のオープンとクローズができる程度)。

GarageBand 10.1

ものすごく割り切ったAppleScript用語辞書がついています。renderPreviewという1つの命令のみ実装されており、

renderPreview v : Render an iLife Preview for a project
 renderPreview [file] : Reference to the project

という内容のようですが、これが本当に役立つものかどうかは不明です。あと、説明がそっけなさすぎて「iLife Preview」の指すものが、GarageBandのファイルなのか、iPhoto/Photos、iTunesなどのiLife Applicationのデータなのかがよくわかりません。

Xcode 6.4

前バージョンからの変更点はAppleScript用語辞書的にはありません。Xcodeによる開発作業やプロジェクトの自動解析などに役立つレベルの実装にはなっていません。