Archive for the 'GUI Scripting' Category

2017/04/21 GUI ScriptingによるTouchBarへのアクセス

Bill CheesemanがAppleScript Users MLに投稿したところによると、GUI Scriptingを用いてTouchBarへのアクセスに成功したとのこと。

TouchBar自体は価格が高く、本体価格が3万円近く上昇してしまうためMacBook Pro 15インチモデルにおいても近い将来TouchBarなしモデルの発売が検討されているなど、微妙な存在ではありますが・・・あると便利な気がします。

Bill Cheesemanによる本テストスクリプトは、ハードウェアとしてのTouchBarが存在する、あるいはソフトウェアベースのエミュレータ(TouchBar Serverなど)が存在する環境で実行すると、その情報を取得してくれます。

touchbar_resized.png

{minimum value:missing value, orientation:missing value, position:{80, 0}, class:pop up button, role description:”touchbarポップオーバー”, accessibility description:”文字ピッカー”, focused:false, title:”", size:{72, 30}, value:missing value, help:”特殊文字や絵文字を選択するときにタップします”, enabled:true, maximum value:missing value, role:”AXPopUpButton”, entire contents:{}, subrole:missing value, selected:missing value, name:missing value, description:”文字ピッカー”}

得られた情報を確認してみると、いろいろと興味深い情報が返ってきているようです。

touchbar2.png

TouchBarが存在しない環境で実行すると、

toucherror.png

のように、エラー情報を表示します。ダイアログ表示はテストScript側で行っているものであり、表示せずにスルーするのが実際の使い方になるはずです。

なお、本Scriptを実行する際にはシステム環境設定の「セキュリティとプライバシー」の「プライバシー」でアクセシビリティの項目に「スクリプトエディタ」を登録しておく必要があります(GUI Scriptingのいつものお約束)。

AppleScript名:TouchBarの検出デモ
– Touch Bar GUI Scripting Demo
– 1.0.0 Bill Cheeseman 2017-04-21

– Usage: Run this script in Script Editor or Script Debugger to see its Touch Bar information.

property touchBarRole : “AXFunctionRowTopLevelElement”

tell application “System Events”
  set frontApp to first application process whose frontmost is true
  
try
    set touchBar to first UI element of frontApp whose role is touchBarRole
  on error errMsg number errNum
    display alert “No Touch Bar support” message “This application or the Mac running it does not support the Touch Bar, or accessibility has not been allowed for script runner in the Security & Privacy pane of System Preferences.

& errMsg & space & errNum
    return
  end try
  
set touchBarItems to value of attribute “AXChildren” of touchBar
  
return properties of item 1 of touchBarItems
  
–> {minimum value:missing value, orientation:missing value, position:{80, 0}, class:pop up button, role description:”touchbarポップオーバー”, accessibility description:”文字ピッカー”, focused:false, title:”", size:{72, 30}, value:missing value, help:”特殊文字や絵文字を選択するときにタップします”, enabled:true, maximum value:missing value, role:”AXPopUpButton”, entire contents:{}, subrole:missing value, selected:missing value, name:missing value, description:”文字ピッカー”}
end tell

★Click Here to Open This Script 

2016/05/30 “GUI Scripting”と”UI element Scripting”

Mac OS X上の指定アプリケーションの指定のGUI(メニューやボタンなど)を強制的に動かしたり(そのためのイベントを送ったり)、各種プロパティを取得したりする、強制コントロールのための機能を、一般的に「GUI Scripting」と呼んでいます。

AppleScriptから見れば一部の特殊機能(AppleScriptに対して機能を解放していないメニュー項目など)を利用するための例外的に使用する機能です。Automatorの「記録」機能がこのGUI Scriptingを用いて実装されているため(GUI Scriptingのコードを生成するため)、AutomatorからAppleScriptを覚えたというユーザーにとっては「AppleScriptは部品や座標をクリックする命令で書くものだ」という誤解を与えがちで、実際にTwitter上でそのようなユーザーに何人か遭遇しています。

さて、このGUI Scriptingについて、Appleが公式にどのような名称で呼んでいるかといえば、それは「UI element Scripting」です。この、知っていても自慢にならず、世間一般にも認知されていない言葉が公式に(だけ)使われ続けているのかは不明ですが、実際にMac OS Xにデフォルトインストールされているサンプルスクリプトに、その姿を見つけることができます。

/Library/Scripts/UI Element Scripts

フォルダに(確認はOS X 10.11.5上で実施)、一般的にはGUI Scripting、Apple公式にはUI element Scriptingと呼ばれるもののサンプルScriptが入っています。

uie3.jpg

そして、これらのサンプルScript冒頭にあるCopyright表記部分には、「UI element scripting」と明記されています。WWDCのAppleScript関連のセッションでも「UI element scripting」と呼んでいるのを見かけました。

さらに、System Events.appのAppleScript用語辞書中にも「UI elements enabled」の予約語が確認できます。これは、GUI Scriptingが有効かどうかをAppleScriptのプログラム側から確認するためのプロパティです。

uie1_resized.png

uie2.jpg

ただ、AppleScriptのRelease NotesやWeb上の資料でこの「UI element scripting」の名前に遭遇することはほとんどありません。不思議です。

「GUI Scripting」という言葉では他社の商標にひっかかるとか、自社独自のテクノロジーであることを主張できないとか、おそらくApple社内のリーガル(法務部)が名称を問題視した結果、すでに周知された一般名称とは別に正式名称を作ったと推測するものです(ありがち)。ただ、無理やり作ったので社内外で誰も使っていない、と。

同様の不思議な言葉に、Appleも誰も使っていない「AppleScript/Objective-C」という言葉があります。これは、AppleScriptObjC(ASOC)のことを指す言葉なのですが、何年か(2013年?)のWWDCのセッションでSal Soghoianが突然使い始めたものです。

従来のASOCと何が違うのかと悩んだものでしたが、これも恐らく「新しい言葉を商標登録するのは面倒だし却下されたので、すでに登録してある単語の組み合わせで表記しろ」(ありがち)という指摘がリーガルからあって、仕方なく嫌々使っているものと推測するものです。こちらは幸いなことにWWDCのセッション以外ほとんど遭遇したことはありません。

pathtosamples_resized.png

AppleScript名:Get User Name.applescript
(*
Get User Name

This script uses UI element scripting to get the name for the
current user.

Copyright © 2013 Apple Inc.

You may incorporate this Apple sample code into your program(s) without
restriction. This Apple sample code has been provided “AS IS” and the
responsibility for its operation is yours. You are not permitted to
redistribute this Apple sample code as “Apple sample code” after having
made changes. If you’re going to redistribute the code, we require
that you make it clear that the code was descended from Apple sample
code, but that you’ve made changes.
*)


tell application “System Preferences”
  activate
  
set current pane to pane “com.apple.preferences.users”
end tell

try
  tell application “System Events”
    tell tab group 1 of window “Users & Groups” of process “System Preferences”
      delay 2
      
click radio button “Password”
      
delay 2
      
set theResult to value of text field “Full name:”
    end tell
  end tell
  
activate me
  
display dialog “Full name: “ & theResult
on error errMsg
  display dialog “Error: “ & errMsg
end try

★Click Here to Open This Script 

AppleScript名:Check If UI Element Scripting is enabled
tell application “System Events”
  set aRes to (UI elements enabled)
end tell

★Click Here to Open This Script 

2016/04/27 AppleScriptから電話をかける v2

同一WiFiネットワーク内にある自分の(Macとひもづけした)iPhoneを利用して、Macから電話をかけるAppleScriptの強化版です。

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

単にopen locationで電話番号を呼び出しただけでは、通知センターが表示するウィンドウ上の「発信」ボタンをクリックする必要がありました。それを野蛮なGUI ScriptingやCocoaの怪しい機能を活用して、「発信」ボタンをクリックさせることで自動発信を行わせるものです。

本来なら、「発信」ボタンに対してクリックイベントを送ることになりますが、この「発信」ボタンはどうもUI Element Inspectorで調べてもボタンとして認識されなかったため、ウィンドウの位置とサイズの情報だけ取得して、座標を計算してマウスカーソルを強制移動させ、強制的にクリックさせています。そのため、ユーザーがマウスカーソルを動かしている状態だとクリックできない可能性があります。

座標値の計算結果の照合のため、MacBook Pro Retina Mid 2012(Retina解像度)と、MacBook Air Mid 2011で動作確認を行ってあります。もっと遅いマシンで実行する場合には、冒頭のdelayの秒数を増やすなどして調整してください。

tel1.png
▲open locationの実行直後

tel21.png
▲2秒待ったあと、AppleScriptで「発信」ボタンをクリック。本当に発信されている(この電話番号はたぶん実在しないため、スクリーンショットを撮ったあとで慌てて止めています)

AppleScript名:電話番号を呼び出すv2
– Created 2016-04-27 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ApplicationServices”
use framework “Quartz”

set telNo to “0312345678″ –only numeric characters

open location “tel://” & telNo
delay 2

activate application “NotificationCenter”
tell application “System Events”
  tell process “通知センター” –This name is localized (Japanese)
    set wCount to count every window
    
if wCount is not equal to 1 then return –ウィンドウが表示されていなかった、あるいは多数のウィンドウが表示されていた
    
    
tell window 1
      set {xPos, yPos} to position
      
set {xSize, ySize} to size
    end tell
    
  end tell
end tell

clickAt(xPos + xSize - 20, yPos + 20) of me

–マウスカーソルの強制移動とクリック
on clickAt(newX, newY)
  set pt to current application’s CGPointZero
  
set x of pt to newX
  
set y of pt to newY
  
current application’s CGPostMouseEvent(pt, 1, 1, 1)
  
current application’s CGPostMouseEvent(pt, 1, 1, 0)
end clickAt

★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/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/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/05/30 Radikoで選局v6

SafariでRadiko.jpをオープンして現在の地域で選局可能なラジオ局の一覧を取得し、一覧から選択したあとに、指定したラジオ局をオープンするAppleScriptです。

自作のAppleScriptObjCのアプリケーション「radiRec」(あくまで個人用)に組み込んで、指定時刻に指定ラジオ局の番組を録音するために作成したものです。

Safariアプリケーション内のWebViewへの参照がいろいろ変わるので、そうした動的な変動を吸収するための機構を追加しました。

GUI Scripting経由でSafariにアクセスしているので、システム環境設定からあらかじめ「アクセシビリティ」から「補助装置にアクセスできるようにする」にチェックを入れておくか、Script実行時にパスワードを入力すると設定を変更します。

radiko41.png

OS X 10.10.3+Safari 8.0.7、場所は東京都内で動作確認してあります(他のエリアではチェックしていません)。

AppleScript名:Radikoで選局v6
–WevViewへの参照を動的に取得するように変更
–OS X 10.10.3(本当は4だけど), Safari 8.0.7, Radiko.jpがプレミアム会員サービスを開始した状態に対応

property waitSec : 10 –Safariのページローディングを待つ時間(秒)

–GUI Scriptingの確認
repeat 3 times
  tell application “System Events”
    set aRes to UI elements enabled
  end tell
  
  
if aRes = false then
    display dialog “GUI Scriptingをオンにしてください。” buttons {“OK”} default button 1
    
tell application “System Events”
      activate
      
set UI elements enabled to true
    end tell
  end if
end repeat
if aRes = false then return

set aList to getRadioStationList() of me
–> {”TBSラジオ”, “文化放送”, “ニッポン放送”, “ラジオNIKKEI第1″, “ラジオNIKKEI第2″, “InterFM”, “TOKYO FM”, “J-WAVE”, “ラジオ日本”, “bayfm78″, “NACK5″, “FMヨコハマ”, “放送大学”}

tell current application
  activate
  
set aRes to choose from list aList
end tell

set aaRes to first item of aRes

selectRadioStation(aaRes) of me –選曲

–SafariでRadikoの選局を行う
on selectRadioStation(aStation)
  activate application “Safari”
  
  
tell application “Safari”
    close every document
    
    
tell document 1
      tell current tab
        open location “http://radiko.jp”
      end tell
    end tell
  end tell
  
  
delay waitSec
  
  
tell application “System Events”
    tell process “Safari”
      
      
set webView to retSafariHTMLViewReference() of me
      
tell webView
        tell list 2
          –ログイン/プレミアム会員登録の箇所が増えたのでlist2に
          
set gList to every group
          
          
set uList to {}
          
          
repeat with i in gList
            tell i
              tell group 1 –ここ、1階層増えた
                set a to UI element 1
                
set statName to title of a
                
set the end of uList to statName
                
if statName = aStation then
                  
                  
click a
                  
                end if
              end tell
            end tell
          end repeat
          
        end tell
        
      end tell
    end tell
  end tell
  
end selectRadioStation

–SafariでRadikoのラジオ局一覧を取得する
on getRadioStationList()
  activate application “Safari”
  
  
tell application “Safari”
    close every document
    
    
tell document 1
      tell current tab
        open location “http://radiko.jp”
      end tell
    end tell
  end tell
  
  
delay waitSec
  
  
tell application “System Events”
    tell process “Safari”
      set webView to retSafariHTMLViewReference() of me
      
tell webView
        
        
tell list 2
          –ログイン/プレミアム会員登録の箇所が増えたのでlist2に
          
set gList to every group
          
          
set uList to {}
          
          
repeat with i in gList
            tell i
              tell group 1 –ここ、1階層増えた
                set a to UI element 1
                
set statName to title of a
                
set the end of uList to statName
              end tell
            end tell
          end repeat
          
          
return uList
          
        end tell
      end tell
      
    end tell
  end tell
  
end getRadioStationList

–SafariのWebViewへのGUI Scripting的な参照を取得する
on retSafariHTMLViewReference()
  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 retSafariHTMLViewReference

★Click Here to Open This Script 

2015/02/23 GUI ScriptingでTeam Viewer10の画面上の情報を取得する

TeamViewer経由で遠隔地のMacを動かすために作ったAppleScriptです。PC/Mac/タブレット上で動作するリモート操作アプリケーション「TeamViewer」の画面上のIDとパスワードを取得します。

tv1.png

危ない用途のために作ったものではなく、自分で管理しているMacの遠隔操作のために作成したものです。

離れた場所にあるMacを遠隔操作する際には、OS X標準搭載のMessage.app(旧称:iChat)経由で画面共有を行うことが多いですが、うまくつながらないケースもあります。そうしたケースではTeamViewerを使うことがままあります。

ただ、TeamViewerは遠隔接続のためのIDとPasswordが(起動するたびに)変更になり、お客様にIDとPasswordを教えていただく必要が(汗)。なので、このやりとりをAppleScriptで自動化してしまおう、という話です(以前からやっていました。セキュリティが強化されたYosemiteで効くかどうか実験中)。

tv2.png

Message.appごしに自動応答でAppleScriptを実行させ、TeamViewerを起動してGUI Scriptingを用いてTeamViewerのIDとPasswordを取得。さらにこれをMessage.app経由でテキストチャットで返させる・・・という処理のための部品です。

IDとパスワードの検出のために、ルールにもとづく文字列検出ルーチンを使っています。このあたり、逆にASOCを使ったほうが短く書けそうな気がとてもしています、、、

調べてみたら、YosemiteのMessage.appにバグがあって、AppleScriptによる自動応答ができないことが判明しました(ーー;; バグレポートしておきましたが、Appleは純粋に「数」でバグ対処の優先順位を決めるので、多くの方々のAppleへのバグレポートをお願いしたいです。

mes1.png

mes2.png

AppleScript名:GUI ScirptingでTeam Viewer10の画面上の情報を取得する
activate application “TeamViewer”
delay 1
tell application “Sy stem Events”
  tell process “TeamViewer”
    tell group 2 of group 1 of window 1
      set tList to value of every static text
    end tell
  end tell
end tell

set ruleList to {“999 999 999″, “9999″} –ID, Pass Pattern
set hitList to {}
repeat with i in ruleList
  set j to contents of i
  
  
repeat with ii in tList
    set jj to contents of ii
    
set aRes to chkCode(jj, j) of me
    
set bRes to chkAllTrue(aRes) of me
    
if bRes = true then
      set the end of hitList to jj
      
exit repeat
    end if
  end repeat
end repeat

hitList
–> {”123 456 789″, “1234″}

–超簡易マクロ展開つきのコードチェック
–[99999]の展開を実装(1箇所のみ)。[99999]の場合には、0〜99999の可変桁チェックを実施
on chkCode(aCode, ruleCode)
  set macroPos1 to offset of “[” in ruleCode
  
set macroPos2 to offset of “]” in ruleCode
  
set ruleLen to length of ruleCode
  
  
if {macroPos1, macroPos2} = {0, 0} then
    set aRes to chkCodeSub(aCode, ruleCode) of me
    
  else if (macroPos1 * macroPos2) is not equal to 0 and (macroPos1 < macroPos2) then
    if macroPos1 > 1 then
      –1文字目以外の場合
      
set baseRule1 to text 1 thru (macroPos1 - 1) of ruleCode
    else
      –1文字目に存在する場合
      
set baseRule1 to “”
    end if
    
    
if macroPos2 is not equal to ruleLen then
      –末尾以外の場合
      
set baseRule2 to text (macroPos2 + 1) thru -1 of ruleCode
    else
      –末尾に存在した場合
      
set baseRule2 to “”
    end if
    
    
set paramLen to macroPos2 - macroPos1 - 1
    
set tmpStr to “9″
    
set hitF to false
    
    
repeat with i from 1 to paramLen
      set tmpRule to baseRule1 & tmpStr & baseRule2
      
set aRes to chkCodeSub(aCode, tmpRule) of me
      
if aRes is not equal to false then
        set hitF to true
        
exit repeat
      end if
      
set tmpStr to tmpStr & “9″
    end repeat
    
    
if hitF = false then return false
    
    
return aRes
    
  end if
end chkCode

–コードのチェックを行う(処理本体)
on chkCodeSub(aCode, ruleCode)
  
  
–set ruleCode to “9999Z99″ –コードのルールを記述しておく–> コードのルール自体も外部から供給するようにしてみた
  
set ruleList to characters of ruleCode
  
  
–コード長のチェック
  
set rLen to length of ruleList
  
set aLen to length of aCode
  
if aLen is not equal to rLen then return false
  
  
–与えられたコードをリストに分解
  
set aList to characters of aCode
  
  
set resList to {}
  
  
repeat with i from 1 to rLen
    
    
set j1 to contents of item i of ruleList
    
set j2 to contents of item i of aList
    
    
set {fromID, toID} to getCharRange(j1) of me
    
set aCharCode to ASCII number of j2
    
    
if (aCharCode fromID) and (aCharCode toID) then
      –ルールに合っている場合には何もしない
      
set the end of resList to true
    else
      set the end of resList to false
    end if
    
  end repeat
  
  
return resList
  
end chkCodeSub

–文字レンジを返す
on getCharRange(a)
  set aCode to ASCII number of a
  
  
if aCode 48 and aCode 57 then
    return {48, aCode} –数字(0〜指定数字まで)
  else if aCode 65 and aCode 90 then
    return {65, aCode} –アルファベット(大文字 A〜指定文字まで)
  else if aCode 97 and aCode 122 then
    return {97, aCode} –アルファベット(大文字 a〜指定文字まで)
  else
    return {aCode, aCode} –その他(ハイフンなどの記号を想定。上記とかぶらない文字)
  end if
end getCharRange

on chkAllTrue(aList as list)
  if false is in aList then return false
  
return true
end chkAllTrue

★Click Here to Open This Script 

2015/02/09 ダウンロードできないplist

Appleが「OS X:Mavericks でアクセシビリティとセキュリティ機能を使った AppleScript の使用方法」なるドキュメントをWeb上で公開していました。

saf1.png

Mavericks上のアクセシビリティについての説明と対策ドキュメントであり、けっこう重要です。

試してみたいと考え、掲載されているplistファイルをダウンロードしようとして・・・

saf2.png

一向にダウンロードできません(2015/2/9)。ページのHTMLソースコードを確認してみたところ・・・

saf3.png

何もリンクされていませんでした。そこでオリジナルの英文のページを探し出して、

saf4.png

ダウンロードしようとしたら、またできませんでした。

saf5.png

こちらもソースを確認してみたところ・・・

saf6.png

リンク先が存在していませんでした。英文のオリジナルがダメだったので、それを翻訳した文章もそのままになっていたということで、フィードバックしておきましたが・・・直るものやら。

→ こちらから直接ダウンロードできるそーです

2014/12/20 SafariのWebViewのGUI Scripting的な参照を取得する

SafariのWebViewのGUI Scriptingからのオブジェクト参照を取得するAppleScriptです。

Safariで表示したWebコンテンツを操作する方法はいくつかあり、

(1)GUI Scripting経由で操作

(2)JavaScript(not JXA)経由で(do javascriptで)操作

だいたい、この両方を組み合わせて実行することになります。

safari_view_menu.png

ただし(1)は、Safariの各種要素(お気に入りバー、サイドバー、タブバー、ステータスバー)の表示状態によって参照要素番号が変わるため、これらの部品の表示・非表示に左右されないように対応しておく必要があります。

こうした柔軟な処理を行うためのサンプルです。ただし、Safariのメジャーバージョンが上がったような場合まではカバーしていません。GUI Scriptingの宿命で、バージョンが上がったら手直しせずに動くことは期待できません。

特定GUI部品以下のすべてのGUI部品を取得する処理(entire contents)もできなくはないのですが、安定性がいまひとつなのと、実行時間が読めないのと、グループ化されている部品には無意味なのと、動的にメニューを生成しているアプリなどでは危ないので避けています。

AppleScript名:SafariのWebViewのGUI Scripting的な参照を取得する
– Created 2014-12-20 by Takaaki Naganoya
– 2014 Piyomaru Software

set aRes to retSafariHTMLViewReference() of me
if aRes = false then return

tell aRes
  properties
end tell
–> {minimum value:missing value, orientation:missing value, position:{0, 85}, class:UI element, role description:”HTML コンテンツ”, accessibility description:”AS Hole(AppleScriptの穴) By Piyomaru Software”, focused:false, title:”", size:{900, -25413}, value:”", help:”", enabled:true, maximum value:missing value, role:”AXWebArea”, entire contents:{}, subrole:missing value, selected:false, name:missing value, description:”AS Hole(AppleScriptの穴) By Piyomaru Software”}

–SafariのWebViewへのGUI Scripting的な参照を取得する
on retSafariHTMLViewReference()
  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 retSafariHTMLViewReference

★Click Here to Open This Script 

2014/11/19 UI Browser 2.5が登場

発音不能のpfiddle soft・・・早い話がBill Cheesemanのツール「UI Browser」の新バージョン(v2.5)が出ていました。同ツールは、GUI ScriptingのAppleScriptを対話的に生成するもので、GUI Scriptingをこれなしに記述するのはほぼ不可能と思われるほど欠かせないものです。

AppleScript関連で「なくなると死ぬほど困るサードパーティのツール」を3つ挙げろと言われたら、

Script Debugger・・ステップ実行、ブレークポイントの設定や変数のリアルタイムモニタなど常識的なAppleScriptデバッグ環境を提供。上級者よりもむしろ入門レベルのScripterに有用。個人的には最近使っていないものの、ないと困るツールの筆頭

ASObjC Explorer 4・・・Cocoaオブジェクトへのアクセス時にCocoaオブジェクトの情報をログ表示してくれるため、ASOC記述に必須。この機能はScript Debuggerにはついていない

に並んで、このUI Browserを迷わず挙げることでしょう。

他に操作方法が存在しない場合にやむにやまれず仕方なく書くGUI Scriptingのコード。そのための、オブジェクト階層の取得やアクセスコードの生成、指定オブジェクトの各種属性値のモニタリングなど、UI Browserがないと話になりません。

逆をいえば、これなしでGUI Scriptingを書くのは「時間の無駄」以外のナニモノでもありません。

uib2.png

そんなUI Browserの最新バージョンでは、OS X 10.10に対応しフォーカスモードでのクラッシュなどを低減させたもので、従来バージョンのユーザーには無償アップデートとして提供されます。新規購入は55ドルで、30日間の無料評価版が用意されています。

ちなみに、OS X 10.10にインストーラーからインストールを行ってみたところ、自分の環境にはv2.4がすでにインストールされており、UI Browserの起動時に「システム環境設定でアクセスを許可させろ」と表示され、そのように設定されていることを確認しましたが・・・何度確認しても操作可能になりませんでした(Access is deniedの表示)。

そこで、システム環境設定でいったんUI Browserの登録を削除して、再度v2.5を登録し直したらOK(Access is allowed)に。

uib1.png
▲すでにUI Browserが登録されているのにAccess is deniedのままだったら、いったんUI Browserの登録を削除して再登録

余談:

アクセシビリティ機能へのアプリケーションごとのアクセス許可については、アプリケーションが存在しているフォルダの位置(階層)やバージョンなどをOS側が細かく管理しています。最近はセキュリティ問題へのAppleの取り組みが進んだ結果、強力な機能には一定の歯止めが用意されるようになりました。

アクセシビリティ機能やカスタムURLプロトコルなどは、OS側の監視がきびしくなった機能の筆頭です。昔ほど自由気ままに無茶なプログラムは組めません。

極端な例では・・・・常駐アプレットを10本ぐらい書いて、コントロール側のアプレットからサブプログラムをコントロールするようなシステムを作っていたときに、サブプログラム側で何本かは「このプログラムにはアクセシビリティ機能へのアクセスを許可しない」とOSのメッセージが出て(暗黙の上限があるらしい)、OSからアクセスを許可されませんでした。

1つ1つのプログラムで動作チェックを行って問題はなかったのに、結合段階で文句を言われてしまったわけです(よくある話ではありますが・・・)。

仕方がないので、サブプログラムはメインプログラムのバンドル内に格納し、load scriptで読み込んで順次実行するようにすべて書き換えしました。アクセシビリティ機能は便利ですが、このように思わぬところに落とし穴が存在しているので注意が必要です。

2014/10/06 最前面のQuickTimeムービーの現在のコマをコピーしてPhotoshopでJPEG保存

オープン中のQuickTimeムービーの現在のコマを静止画としてコピーして、PhotoshopでWeb用のJPEG(サイズが小さい)として書き出すAppleScriptです。

QuickTimeムービーの見どころだけをかいつまんでJPEGに書き出していたのですが、画面キャプチャをとったりなんだりで、3ステップぐらい手作業が必要だったので、いっぱつで実行できるようにAppleScriptを書いてみました。

各所にdelayを入れて時間待ちしているところがミソです。入れないとAppleScriptの実行が速すぎて、コピーなどの動作が完了しなかったためです。

また、QuickTime Player(X)の機能がおっそろしく少ないので、仕方なくGUI Scriptingを併用(現在のコマの内容をコピーする)しています。実行には、GUI Scriptingを許可しておく必要があります。

4d31d0bd-9459-478a-8fd5-f941a13f5453.jpg
▲ムービーから1コマだけ抜き出してPhotoshopでWeb用JPEG(サイズが小さい)に書き出した結果

スクリプト名:最前面のQuickTimeムービーの現在のコマをコピーしてPhotoshopでJPEG保存
set dPath to path to desktop
set dPathStr to dPath as string
set fPath to dPathStr & (do shell script “uuidgen”) & “.jpg”

–QuickTime書類の縦横ドットサイズを取得
tell application “QuickTime Player”
  
  
–ムービーを開いていないかチェック
  
set dCount to count every document
  
if dCount < 1 then
    display dialog “ムービーを開いた状態で実行してください” with icon 2 with title “エラー:書き出し対象のムービーが存在しません” buttons {“OK”} default button 1
    
return
  end if
  
  
tell document 1
    set {xWidth, yHeight} to natural dimensions
  end tell
  
end tell

–QuickTime Playerの書類の現在の位置の内容(静止画)をコピー
activate application “QuickTime Player”

delay 0.1

tell application “System Events”
  tell process “QuickTime Player”
    click menu item 5 of menu 1 of menu bar item 4 of menu bar 1 –編集>コピー
  end tell
end tell

delay 0.1

–Photoshopで新規書類を作成してペースト
tell application id “com.adobe.photoshop”
  activate
  
  
delay 0.1
  
  
make new document with properties {width:xWidth, height:yHeight}
  
paste
  
  
delay 0.1
  
  
–Resize & Save
  
tell current document
    
    
set aH to height
    
set aW to width
    
    
set aH1 to aH * 0.25
    
set aW1 to aW * 0.25
    
    
–resize image width aW1 height aH1
    
    
export in file fPath as save for web
    
    
close without saving
    
  end tell
  
  
end tell

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2014/04/07 Radikoで選局 v4

SafariでRadiko.jpをオープンして現在の地域で選局可能なラジオ局の一覧を取得し、一覧から選択したあとに、指定したラジオ局をオープンするAppleScriptです。

自作のAppleScriptObjCのアプリケーション「radiRec」(あくまで個人用)に組み込んで、指定時刻に指定ラジオ局の番組を録音するために作成したものです。

Radiko.jpのプレミアム会員サービス(エリアフリー聴取)がはじまり、Webサイトの構成も若干変更されたため、ラジオ局のリスト作成および選択用のプログラムも変更が必要になりました。

GUI Scripting経由でSafariにアクセスしているので、システム環境設定からあらかじめ「アクセシビリティ」から「補助装置にアクセスできるようにする」にチェックを入れておくか、Script実行時にパスワードを入力すると設定を変更します。

radiko41.png

OS X 10.9.2+Safari 7.0.3、場所は東京都内で動作確認してあります(他のエリアではチェックしていません)。

スクリプト名:Radikoで選局 v4
–東京エリアのRadikoのステーションリストを取得、指定のステーションを選局
–OS X 10.9.2(本当は3だけど), Safari 7.0.3, Radiko.jpがプレミアム会員サービスを開始した状態に対応

property waitSec : 10 –Safariのページローディングを待つ時間(秒)

–GUI Scriptingの確認
repeat 3 times
  tell application “System Events”
    set aRes to UI elements enabled
  end tell
  
  
if aRes = false then
    display dialog “GUI Scriptingをオンにしてください。” buttons {“OK”} default button 1
    
tell application “System Events”
      activate
      
set UI elements enabled to true
    end tell
  end if
end repeat
if aRes = false then return

set aList to getRadioStationList() of me
–> {”TBSラジオ”, “文化放送”, “ニッポン放送”, “ラジオNIKKEI第1″, “ラジオNIKKEI第2″, “InterFM”, “TOKYO FM”, “J-WAVE”, “ラジオ日本”, “bayfm78″, “NACK5″, “FMヨコハマ”, “放送大学”}

tell current application
  activate
  
set aRes to choose from list aList
end tell

set aaRes to first item of aRes

selectRadioStation(aaRes) of me –選曲

–SafariでRadikoの選局を行う
on selectRadioStation(aStation)
  activate application “Safari”
  
  
tell application “Safari”
    close every document
    
    
tell document 1
      tell current tab
        open location “http://radiko.jp”
      end tell
    end tell
  end tell
  
  
delay waitSec
  
  
tell application “System Events”
    tell process “Safari”
      tell window 1
        –おきにいりバーの表示/非表示に対応
        
set countGroup to count every group
        
        
tell group countGroup
          tell group 1
            tell group 1
              tell scroll area 1 –Scrollエリア
                tell UI element 1 –HTMLコンテンツ
                  tell list 2
                    –ログイン/プレミアム会員登録の箇所が増えたのでlist2に
                    
set gList to every group
                    
                    
set uList to {}
                    
                    
repeat with i in gList
                      tell i
                        tell group 1 –ここ、1階層増えた
                          set a to UI element 1
                          
set statName to title of a
                          
set the end of uList to statName
                          
if statName = aStation then
                            
                            
click a
                            
                          end if
                        end tell
                      end tell
                    end repeat
                    
                  end tell
                end tell
              end tell
            end tell
          end tell
        end tell
      end tell
    end tell
  end tell
  
end selectRadioStation

–SafariでRadikoのラジオ局一覧を取得する
on getRadioStationList()
  activate application “Safari”
  
  
tell application “Safari”
    close every document
    
    
tell document 1
      tell current tab
        open location “http://radiko.jp”
      end tell
    end tell
  end tell
  
  
delay waitSec
  
  
tell application “System Events”
    tell process “Safari”
      tell window 1
        –おきにいりバーの表示/非表示に対応
        
set countGroup to count every group
        
        
tell group countGroup
          tell group 1
            tell group 1
              tell scroll area 1 –Scrollエリア
                tell UI element 1 –HTMLコンテンツ
                  tell list 2
                    –ログイン/プレミアム会員登録の箇所が増えたのでlist2に
                    
set gList to every group
                    
                    
set uList to {}
                    
                    
repeat with i in gList
                      tell i
                        tell group 1 –ここ、1階層増えた
                          set a to UI element 1
                          
set statName to title of a
                          
set the end of uList to statName
                        end tell
                      end tell
                    end repeat
                    
                    
return uList
                    
                  end tell
                end tell
              end tell
            end tell
          end tell
        end tell
      end tell
    end tell
  end tell
  
end getRadioStationList

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2013/11/19 MavericksのGUI Scriptingのclick命令にバグ

OS X 10.9, MavericksのGUI Scriptingの一部の命令にバグが確認されました。

System EventsのProcess Suiteの「click」命令にバグがあるようです。

gui_script_bug2.png

click命令で「click at {X座標, Y座標}」と指定すると、エラーになります。10.9.1では……直らないでしょうね、、、

gui_script_bug1.png

2013/08/28 Recording(記録)機能は現在「なかったこと」に

AppleScriptに対する「誤解」の1つに、「ユーザーの操作を記録できないじゃないか」というものがあります。

もう何年もクリックしたことのないボタンが、AppleScript Editor上に、たしかにありますね。「記録」と書いてあるような気がしますが、気のせいでしょう。

rec2.png

一応、Finderはギリギリ「記録」(Recording)に対応しているようですが、Appleからガイドラインが開発者に向けて示されており、「Recordingに対応する必要はない」ということになっています。逆に、いまだに(OS X10.8で)FinderにRecordingが残っているのを知って腰を抜かしたぐらいです。

Finder以外でAppleScriptのRecordingに対応しているアプリケーションは…………ちょっと思い当たらないのですが(TextWranglerが対応しているそうです)、とにかくRecordingは「ないもの」「なかったもの」と見なすのが正しい認識です。

Recording機能の廃止(まだ廃止されてないけど)にあたって、Appleからの説明があり、

「(開発者が)対応するのが大変な割に、Recordingした内容を見ても(逆に難解な記述になるため)初心者の理解の助けに全然ならない。実行環境が異なると動作が再現しなかったりで、使い回せない。」

といった話をされたように覚えています(ものすごい昔の話なのでうるおぼえ)。

Automatorでも操作記録はできるようですが、アレはクリック座標やキーストロークを記録しているだけで……画面サイズや、ウィンドウの位置などもろもろの条件が合わなければ動作内容は再現できないもので、配布しても無意味です(そういう人を見かけたことがあります)。

では、AppleScriptはプログラマー用の言語かといえば……C++とかObjetive-Cとか、記号の塊でもっと読みにくいと思うのですが……逆に読める分だけ「こうなっているに違いない」という思い込みを誘発し、それが違ったときに逆ギレを生むようです。

「操作した内容をそのまま記録」することは、やってもあまり意味がないという結論が見えていますが、逆に「操作するようにプログラミングする」ことは不可能ではありません。

メニューの操作やボタンのクリック、キー入力などをAppleScriptで記述する「GUI Scripting」(UI Element Scripting)というのが、それに該当します。

 GUI Scriptingによる記述(1)
 GUI Scriptingによる記述(2)
 GUI Scriptingによる記述(3)
 GUI Scriptingによる記述(4)
 Preview.appでプリンタと用紙サイズを指定して印刷
 文字入力モードを制御

ただ……これが「やさしい(Easy)」かどうかは意見の分かれるところで……ボタンを押すためには、ボタンが存在するかどうか調べる必要があり、さらにボタンが存在していたとしても、押せる状態かどうかあらかじめ調べる必要があり……そういう意味では、「ちゃんと動くAppleScript」を書くのにGUI Scriptingを利用することは、「楽」だとは思いません。

自分としては、「すでに動作確認がとれているルーチンをコピペしてきて、呼び出すのが一番簡単」だと思っていますが……間もなく、いろいろと状況が変わってくることでしょう。使い回しが、もっと簡単になってくると思います(数ヶ月以内に)。

2013/05/24 Finder上で選択中のファイルをPreviewでオープンして保存してクローズ

Finder上で選択中のファイル(たぶんPDF)をPreview.appでオープンして、保存してクローズ……を繰り返すAppleScriptです。

PhotoshopでPDFを加工したところ、そのままではファイルサイズが大きく、古いOS(Mac OS X 10.4.11とか)では内容を確認できない状態でした。

……ちょっと困りました。そこで、OS X 10.8上のPreview.appでファイルを保存し直したところ、ファイルサイズも小さくなり、古いOSでもオープンできるようになりました。

何か、ほかにも回避方法はありそうですが……とりあえず、本AppleScriptによって、Finder上でPhotoshopによる加工を行ったPDFを選択しておき、Scriptを実行することですべて保存し直します。

なお、本AppleScriptはGUI Scriptingを利用しているため、あらかじめシステム環境設定の「アクセシビリティ」で「補助装置にアクセスできるようにする」にチェックを入れておく必要があります。また、将来のOSバージョンアップによってPreview.appのメニュー内容が変更になった場合には、そのままでは動かず修正(メニューアイテム番号部分のみ)が必要になる可能性が高いです。

スクリプト名:Finder上で選択中のファイルをPreviewでオープンして保存してクローズ
tell application “Finder”
  set aList to selection as alias list
end tell

repeat with i in aList
  
  
set j to contents of i
  
set jj to j as string
  
  
–Preview.appで選択されたファイルをオープンする
  
set appBundleID to “com.apple.Preview”
  
tell application “Finder”
    open file jj using application file id appBundleID
  end tell
  
  
delay 0.1
  
  
–Preview.appでファイル保存→クローズを行う
  
activate application “Preview”
  
tell application “System Events”
    tell process “プレビュー”
      click menu item 8 of menu 1 of menu bar item 3 of menu bar 1 –保存
    end tell
  end tell
  
  
delay 0.1
  
  
tell application “System Events”
    tell process “プレビュー”
      click menu item 5 of menu 1 of menu bar item 3 of menu bar 1 –閉じる
    end tell
  end tell
  
end repeat

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2013/04/05 SafariでRadiko選局を行う 10.6.8+5.1.8版

SafariでRadikoの選局を行うAppleScriptのMac OS X 10.6.8+Safari 5.1.8向けに改修を行ったものです。

OS X 10.8上でSafari 6.0.3を相手にラジオ局選択をできるようにはなったのですが、肝心の自宅ラジオ録音用マシン(Mac mini)はMac OS X 10.6.8で動いているので、ターゲット機に合わせて書き換える必要が出てきました。

仕方なく、UI Browser(のお試し版)を10.6.8のマシンにインストールして、Safari上のWebコンテンツのUIを調べつつ、オブジェクト階層の書き換えを行いました。また、非力なCoreDuo 1.6GHzのマシンに合わせて待ち時間を修正しました。

ちなみに、OS X 10.7.5上ではSafari 6.0.3が動いておりOS X 10.8用のAppleScriptがそのまま動きます。

スクリプト名:SafariでRadiko選局を行う 10.6.8+5.1.8版
–Mac OS X 10.6.8+Safari 5.1.8

property waitSec : 10 –Safariのページローディングを待つ時間(秒)

–GUI Scriptingの確認
repeat 3 times
  tell application “System Events”
    set aRes to UI elements enabled
  end tell
  
  
if aRes = false then
    display dialog “GUI Scriptingをオンにしてください。” buttons {“OK”} default button 1
    
tell application “System Events”
      activate
      
set UI elements enabled to true
    end tell
  end if
end repeat
if aRes = false then return

set aList to getRadioStationList_1068() of me
–> {”TBSラジオ”, “文化放送”, “ニッポン放送”, “ラジオNIKKEI第1″, “ラジオNIKKEI第2″, “InterFM”, “TOKYO FM”, “J-WAVE”, “ラジオ日本”, “bayfm78″, “NACK5″, “FMヨコハマ”, “放送大学”}

tell current application
  activate
  
set aRes to choose from list aList
end tell

set aaRes to first item of aRes

selectRadioStation_1068(aaRes) of me –選局

–SafariでRadikoの選局を行う
on selectRadioStation_1068(aStation)
  activate application “Safari”
  
  
tell application “Safari”
    close every document
    
    
tell document 1
      tell current tab
        open location “http://radiko.jp”
      end tell
    end tell
  end tell
  
  
delay waitSec
  
  
tell application “System Events”
    tell process “Safari”
      tell list 1 of UI element 1 of scroll area 1 of group 1 of group 1 of group 3 of window 1
        set gList to every group
        
        
set uList to {}
        
        
repeat with i in gList
          set j to contents of i
          
tell j
            set a to first item of (every UI element)
            
set statName to title of a
            
            
if statName = aStation then
              tell UI element aStation
                click
                
exit repeat
              end tell
            end if
          end tell
        end repeat
        
      end tell
    end tell
  end tell
  
end selectRadioStation_1068

–SafariでRadikoのラジオ局一覧を取得する
on getRadioStationList_1068()
  activate application “Safari”
  
  
tell application “Safari”
    close every document
    
    
tell document 1
      tell current tab
        open location “http://radiko.jp”
      end tell
    end tell
  end tell
  
  
delay waitSec
  
  
tell application “System Events”
    tell process “Safari”
      tell list 1 of UI element 1 of scroll area 1 of group 1 of group 1 of group 3 of window 1
        set gList to every group
        
        
set uList to {}
        
        
repeat with i in gList
          tell i
            set a to first item of (every UI element)
            
set statName to title of a
            
set the end of uList to statName
          end tell
        end repeat
        
      end tell
    end tell
  end tell
  
  
return uList
  
end getRadioStationList_1068

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2013/04/01 SafariでRadikoの選局を行う v2

SafariでRadiko.jpをオープンして現在の地域で選局可能なラジオ局の一覧を取得し、一覧から選択したあとに、指定したラジオ局をオープンするAppleScriptです。

radi1.png

GUI Scripting経由でSafariにアクセスしているので、システム環境設定からあらかじめ「アクセシビリティ」から「補助装置にアクセスできるようにする」にチェックを入れておくか、Script実行時にパスワードを入力すると設定を変更します。

自作のAppleScriptObjCのアプリケーション「radiRec」に組み込んで動作を確認しました。AppleScriptObjCのプロジェクトに入れても問題ありません。

radirec.png

OS X 10.8.3+Safari 6.0.3、場所は東京都内で動作確認してあります(他のエリアではチェックしていません)。

スクリプト名:SafariでRadikoの選局を行う v2
property waitSec : 5 –Safariのページローディングを待つ時間(秒)

–GUI Scriptingの確認
repeat 3 times
  tell application “System Events”
    set aRes to UI elements enabled
  end tell
  
  
if aRes = false then
    display dialog “GUI Scriptingをオンにしてください。” buttons {“OK”} default button 1
    
tell application “System Events”
      activate
      
set UI elements enabled to true
    end tell
  end if
end repeat
if aRes = false then return

set aList to getRadioStationList() of me
–> {”TBSラジオ”, “文化放送”, “ニッポン放送”, “ラジオNIKKEI第1″, “ラジオNIKKEI第2″, “InterFM”, “TOKYO FM”, “J-WAVE”, “ラジオ日本”, “bayfm78″, “NACK5″, “FMヨコハマ”, “放送大学”}

tell current application
  activate
  
set aRes to choose from list aList
end tell

set aaRes to first item of aRes

selectRadioStation(aaRes) of me –選曲

–SafariでRadikoの選局を行う
on selectRadioStation(aStation)
  activate application “Safari”
  
  
tell application “Safari”
    close every document
    
    
tell document 1
      tell current tab
        open location “http://radiko.jp”
      end tell
    end tell
  end tell
  
  
delay waitSec
  
  
tell application “System Events”
    tell process “Safari”
      tell list 1 of UI element 1 of scroll area 1 of group 1 of group 1 of group 2 of window 1
        set gList to every group
        
        
set uList to {}
        
        
repeat with i in gList
          tell i
            set a to first item of (every UI element)
            
set statName to title of a
            
set the end of uList to statName
            
if statName = aStation then
              
              
click a
              
            end if
          end tell
        end repeat
        
      end tell
    end tell
  end tell
  
end selectRadioStation

–SafariでRadikoのラジオ局一覧を取得する
on getRadioStationList()
  activate application “Safari”
  
  
tell application “Safari”
    close every document
    
    
tell document 1
      tell current tab
        open location “http://radiko.jp”
      end tell
    end tell
  end tell
  
  
delay waitSec
  
  
tell application “System Events”
    tell process “Safari”
      tell list 1 of UI element 1 of scroll area 1 of group 1 of group 1 of group 2 of window 1
        set gList to every group
        
        
set uList to {}
        
        
repeat with i in gList
          tell i
            set a to first item of (every UI element)
            
set statName to title of a
            
set the end of uList to statName
          end tell
        end repeat
        
      end tell
    end tell
  end tell
  
  
return uList
  
end getRadioStationList

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2013/03/10 Safariで指定User Agentで指定URLをオープン

Safariで指定ユーザーエージェント名で、指定のURLをオープンするAppleScriptです。

Safari 6.0.3+OS X 10.8.2で動作確認を行っています。GUI Scriptingを用いているので、あらかじめシステム環境設定の「アクセシビリティ」で「補助装置にアクセスできるようにする」のチェックを入れておいてください。

# GUI側からコントロールしているので、OSおよびSafariのバージョンに依存しています

最初に、ダミーURLをオープンしてから、ユーザーエージェント名を指定して、ターゲットURLをオープンすることになります。

いきなりターゲットのURLをデフォルト設定のままオープンしてしまうと、クッキーなどがデフォルトのブラウザ(Safari)に合わせて書き換えられる可能性があるので、こういう順序で処理させてみました。

スクリプト名:Safariで指定User Agentで指定URLをオープン
–Safari v6.0.3で指定可能なUser Agentの一覧(「その他」で具体的に指定することも可能
set uaList to {“Safari 6.0.3 ― Mac”, “Safari 5.1.7 ― Windows”, “Safari iOS 5.1 ― iPhone”, “Safari iOS 5.1 ― iPod touch”, “Safari iOS 5.1 ― iPad”, “Internet Explorer 9.0″, “Internet Explorer 8.0″, “Internet Explorer 7.0″, “Google Chrome 19.0 ― Mac”, “Google Chrome 19.0 ― Windows”, “Firefox 11.0 ― Mac”, “Firefox 11.0 ― Windows”, “Opera 11.62 ― Mac”, “Opera 11.62 ― Windows”}

–ためしに、リストからUser Agentを選択(普通、こんなかったるい処理はしませんが、動作テスト用)
set aRes to choose from list uaList with prompt “ユーザーエージェントを選択”
if aRes = false then return

set anUserAgent to contents of first item of aRes

set aURL to “http://piyocast.com/as” –オープン対象のURL
set bRes to openAurlBySpecifiedUserAgent(anUserAgent, aURL) of me
–> true(オープンできたとき)
–>false(オープンできなかったとき)

–指定User Agentで指定URLをオープン
on openAurlBySpecifiedUserAgent(aUAstr, aURL)
  
  
–最初にURLをオープンしておいて、あとでURLを変更する
  
set dummyURL to “http://www.google.co.jp” –どこか、適当なページ。ターゲットのURL以外ならどこでもいい? 
  
tell application “Safari”
    try
      make new document with properties {URL:dummyURL}
    on error
      return false
    end try
  end tell
  
  
activate application “Safari”
  
  
tell application “System Events”
    tell process “Safari”
      
      
–指定メニュー項目をクリック
      
repeat 10 times
        try
          click menu item aUAstr of menu 1 of menu item 2 of menu 1 of menu bar item 8 of menu bar 1
        on error
          return false
        end try
        
        
–本当に設定できたかどうか確認
        
set aRes to selected of menu item aUAstr of menu 1 of menu item 2 of menu 1 of menu bar item 8 of menu bar 1
        
if aRes is not equal to false then
          return false
        else
          exit repeat
        end if
        
        
delay 1
        
      end repeat
    end tell
  end tell
  
  
–ひらいておいたウィンドウで指定URLをオープンする
  
tell application “Safari”
    try
      tell document 1
        set URL to aURL
      end tell
    on error
      return false
    end try
  end tell
  
  
return true
  
end openAurlBySpecifiedUserAgent

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2013/03/10 Safariで指定可能なユーザーエージェントのリストを返す

Safariでプリセットで選択可能なユーザーエージェントを取得するAppleScriptです。OS X 10.8.2+Safari 6.0.3上で動作確認しています。

GUI Scriptingを用いているので、あらかじめシステム環境設定の「アクセシビリティ」で「補助装置にアクセスできるようにする」のチェックを入れておいてください。

safari0.png

Safariの「環境設定」>「詳細」で、「メニューバーに”開発”メニューを表示」の項目のチェックをオンにしておくと、メニューバーに「開発」の項目が表示されるようになります(Macユーザーの一般常識)。

この「開発」メニューの「ユーザーエージェント」の下に、Safariがあらかじめ用意しているユーザーエージェントの選択メニューが表示されます。この項目を適宜選択すると、指定ユーザーエージェント名でWebサーバーにアクセスするようになります。

safari1.png

この、Safariがメニューから指定可能なユーザーエージェントの一覧を取得します。

safari2.png

Safariではプリセットのユーザーエージェント名以外にも、「その他」から適宜指定可能なので、別にプリセットのユーザーエージェント名にこだわる必要はありません。

スクリプト名:Safariで指定可能なユーザーエージェントのリストを返す

–Safariでメニューから指定可能なUser Agentのリストを取得する
set uaList to retEnableUserAgentFromSafari() of me
–> {”Safari 6.0.3 ― Mac”, “Safari 5.1.7 ― Windows”, “Safari iOS 5.1 ― iPhone”, “Safari iOS 5.1 ― iPod touch”, “Safari iOS 5.1 ― iPad”, “Internet Explorer 9.0″, “Internet Explorer 8.0″, “Internet Explorer 7.0″, “Google Chrome 19.0 ― Mac”, “Google Chrome 19.0 ― Windows”, “Firefox 11.0 ― Mac”, “Firefox 11.0 ― Windows”, “Opera 11.62 ― Mac”, “Opera 11.62 ― Windows”}

–Safariで指定可能なユーザーエージェントのリストを返す
on retEnableUserAgentFromSafari()
  
  
–除外リスト(This list is for Japanese environment. This depends on each localized menu item string)
  
set exList to {“デフォルト(自動選択)”, “その他…”}
  
  
–activate application “Safari”
  
tell application “System Events”
    tell process “Safari”
      
      
–開発メニューのイネーブルチェック
      
tell menu bar 1
        set tList to title of every menu bar item
      end tell
      
      
if “開発” is not in tList then –(This String is for Japanese environment. This depends on each localized menu item string)
        display dialog “開発メニューがオンになっていません” buttons {“OK”} default button 1 with icon 1
        
return
      end if
      
      
–「開発」>「ユーザーエージェント」
      
tell menu 1 of menu item 2 of menu 1 of menu bar item 8 of menu bar 1
        set t2List to title of every menu item whose title is not “” –セパレーター以外のメニュー項目を取得
      end tell
      
    end tell
  end tell
  
  
set t3List to {}
  
repeat with i in t2List
    set j to contents of i
    
if j is not in exList then
      set the end of t3List to j
    end if
  end repeat
  
  
return t3List
  
end retEnableUserAgentFromSafari

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2012/09/03 通知センター経由でTwitter投稿(10.8用)v2

通知センター経由でTwitter投稿するAppleScriptの改良版です。

最初のバージョンはローカライズされたプロセス名を指定していましたが、その後いろいろ試行錯誤したところ「ローカライズしていない名称でもOK」だということに気付き、結局プロセス名はオリジナルどおり「NotificationCenter」に戻しました(bundle identifierでプロセス名を指定できるかと思っていましたが、ダメでした)。

注:「ダメ」というのは、スマートにtell文で指定することはできなかった、という話でありフィルタ参照でプロセスを抽出する、という地道なやり方では処理できます。

スクリプト名:通知センター経由でTwitter投稿 v2
set aMessage to “AppleScriptによる通知センター経由のTwitter投稿テスト(12)”
postTweetFromNotificationCenter(aMessage) of me

on postTweetFromNotificationCenter(aText)
  
  
set the clipboard to aText
  
  
tell application “System Events”
    tell process “NotificationCenter” –通知センター
      
      
click menu bar item 1 of menu bar 1 –通知センターを表示
      
      
delay 0.5
      
      
–Twitter投稿ボタンをクリック
      
try
        click button 1 of UI element 1 of row 2 of table 1 of scroll area 1 of window 1
      end try
      
      
delay 0.5
      
      
–クリップボードに入れた文字列を
      
keystroke “v” using {command down}
      
keystroke “D” using {command down, shift down}
      
keystroke space
      
      
delay 0.5
      
      
–keystroke (ASCII character 27) –通知センターを消去 [ESC]キー
      
    end tell
  end tell
end postTweetFromNotificationCenter

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2012/09/01 通知センター経由でTwitter投稿(10.8用)

GUI Scripting経由で通知センターをコントロールしてTwitter投稿を行うAppleScriptです(OS X 10.8用)。

海外で紹介されていたAppleScriptが、英語環境下では動くらしいものの、日本語環境ではそのままでは動かなかったので、いろいろ直してみました。だいたい、プロセス名が「Notification Center」(英語環境)から「通知センター」(日本語環境)に変わってしまうのはあんまり感心しません。日本語環境用のAppleScriptに修正するためには、これらも直す必要があります。

GUI Scriptingを用いているため、デフォルト状態では動きません。「システム環境設定」>「アクセシビリティ」で「補助装置にアクセスできるようにする」のチェックボックスをオンにしておく必要があります。

ほかにも、オリジナルは英文専用だったので投稿本文テキストをKeystrokeでそのまま入力していましたが、日本語はそうもいかないのでクリップボード経由でペーストするように変更しました。

通知センターの初期状態が一定ではないので、そのための対処も行っています。Twitter投稿用のボタンが表示されている状態と、すでにTwitter投稿用フィールドが表示されている状態とでGUIの状態が異なるのでそれに対処しています(try文でエラートラップをかけているところ)。

ただ、OS X 10.8の「システム環境設定」>「メール/連絡先/カレンダー」でTwitterのアカウントが設定されていないと、本Scriptはエラーになります。Twitterアカウントの設定の有無を別途確認しておく必要があるので、別途確認用のScriptを用意しておく必要があるでしょう。

通知センターを非表示にするために、後片付けの処理をいろいろ試行錯誤していますが……ちょっとまだうまく行っていません。

スクリプト名:通知センター経由でTwitter投稿
set aMessage to “AppleScriptによる通知センター経由のTwitter投稿テスト(11)”
postTweetFromNotificationCenter(aMessage) of me

on postTweetFromNotificationCenter(aText)
  
  
set the clipboard to aText
  
  
tell application “System Events”
    tell process “通知センター”
      click menu bar item 1 of menu bar 1 –通知センターを表示
      
      
delay 0.5
      
      
–Twitter投稿ボタンをクリック
      
try
        click button 1 of UI element 1 of row 2 of table 1 of scroll area 1 of window 1
      end try
      
      
delay 0.5
      
      
–クリップボードに入れた文字列を
      
keystroke “v” using {command down}
      
keystroke “D” using {command down, shift down}
      
keystroke space
      
      
delay 0.5
      
      
–keystroke (ASCII character 27) –通知センターを消去 [ESC]キー
      
    end tell
  end tell
end postTweetFromNotificationCenter

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2012/08/22 OmniGraffleで日本語版と英語版の画面キャプチャを並べた書類を作成する

2011/04/10 ドロップされたASをdiff表示 v4

ドロップされたASをFileMergeでビジュアルdiff表示v3のバージョンアップ版です。2つのAppleScriptのファイルをFileMerge(Xcodeをインストールすると一緒に入る)でビジュアルdiff表示します。

GUI Scriptingを用いているため、実行のためにはこれをオンにしておく必要があります。また、本Scriptはアプリケーション形式で保存したうえで実行(AppleScriptのファイルを2つ、ドラッグ&ドロップ)することになります。

スクリプト名:ドロップされたASをdiff表示 v4
on run
  –環境確認を行うべき(書いてない)
  
  
–FileMergeの起動を最初にやっておく
  
if running of application “FileMerge” then –Mac OS X 10.5だか10.6で拡張された属性
    –すでに起動している場合には何もしない
  else
    tell application “FileMerge”
      launch
    end tell
  end if
  
end run

on open fileList
  
  
tell application “Finder”
    set sortedList to sort fileList by creation date –オリジナルはmodification dateだったが……趣味の問題?
  end tell
  
  
set sortedList to sortedList’s reverse
  
  
set oldPath to writeASSourceToTempFolder((sortedList’s item 1) as alias)
  
set newPath to writeASSourceToTempFolder((sortedList’s item 2) as alias)
  
  
do shell script “/usr/bin/opendiff “ & oldPath’s POSIX path’s quoted form & ” “ & newPath’s POSIX path’s quoted form & ” > /dev/null 2>&1 &”
  
  
–指定プロセスでDialogが出て処理が停まっている場合には、強制的にダイアログをクローズして処理続行させる
  
clickFrontDialog(“FileMerge”) of me
  
end open

–AppleScriptのソースを取得してファイルに書き出し
on writeASSourceToTempFolder(macPath)
  set aName to (info for macPath size 0)’s name
  
set tmpPath to ((path to temporary items from system domain) as text) & aName & “__” & (do shell script “/usr/bin/uuidgen”) & “.txt”
  
  
set scptText to do shell script “/usr/bin/osadecompile “ & macPath’s POSIX path’s quoted form
  
  
set accessFile to open for access file tmpPath with write permission
  
set eof of accessFile to 0
  
write scptText to accessFile starting at eof as text
  
close access accessFile
  
  
return tmpPath
end writeASSourceToTempFolder

–表示されるダイアログを乗り越えるためのルーチン(「キャンセル」もしくは「Cancel」ではない方のボタンを押す)
on clickFrontDialog(aProcName)
  –余計なプロセスを起動せずにactivate
  
activateAproc(aProcName) of me
  
  
–ダイアログ検出
  
tell application “System Events”
    tell process aProcName
      if (count every window) 1 then
        if subrole of window 1 = “AXDialog” then
          tell window 1
            set bCount to count every button
            
            
–「キャンセル」もしくは「Cancel」ではない方のボタンを取得
            
repeat with i in {“キャンセル”, “Cancel”} –キャンセルに該当するキーワードのリストを各国語分だけ用意しておく???
              set bList to (every button whose title is not equal to (contents of i))
              
if length of bList is not equal to bCount then
                set aButton to first item of bList
                
tell aButton
                  click
                  
exit repeat
                end tell
              end if
            end repeat
          end tell
        end if
      end if
    end tell
  end tell
end clickFrontDialog

on activateAproc(aName)
  tell application “System Events”
    set aList to every process whose name is aName and visible of it is true
    
    
if length of aList 1 then
      set aProc to first item of aList
      
tell aProc
        set frontmost to true
      end tell
      
    else
      tell application aName to activate
    end if
    
  end tell
end activateAproc

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2011/04/10 ドロップされたASをdiff表示 v3

ドロップされたASをFileMergeでビジュアルdiff表示のバージョンアップ版です。GUI Scriptingを用いているため、実行のためにはこれをオンにしておく必要があります。

asdiff.jpg

コメント欄でedama2さんに提案していただいたものがv2。これは、TextWranglerを併用しなくてよくなったのと、shell commandを呼び出したあとできちんと終了するように改良したものです。また、一時ファイルのファイル名にオリジナルが何か分かるように作成されている点も有用な改良点だと思います。

ただ……それをそのまま掲載したのでは能がないので、このv3を作成してみました。

(1)日本語入りAppleScriptをFileMergeでオープンした際に表示される「Files are not ASCII」の警告ダイアログを無理やりGUI Scriptingでスキップ

filem1.jpg

(2)FileMergeのactivate時に、なぜか複数のFileMergeが起動されてしまう、という現象が(一時的に)見られたため、起動中のFileMergeのプロセスを特定して、そいつをactivateするようにした
filem2.jpg

(3)ファイル変更日時でソートされていたものを、ファイル作成日でソート

といった変更を加えています(3つ目のは単なる個人的な趣味の問題です)。

AppleScriptのDiff表示は、AppleScriptエディタの標準機能として搭載してほしいぐらいのものです。

ほかにも、複数のAppleScript間の使用サブルーチン比較表の作成プログラム、構文要素を考慮した置換(変数名のみ置換、定数のみ置換 など)などもすでにAppleScriptで書いたものがあり、日常的に使っていると「なぜこれが標準搭載されていないのか?」と不思議に思えるほどです。

osadecompileコマンドについては、ほとんど使ったことがなかったのですが……こういう分析系のタスクに用いるのがよいのだろうと思っています。あとは、AppleScriptの実行前に「危険な内容」が含まれていないかを事前にチェックするために利用する、ぐらいでしょうか。

スクリプト名:ドロップされたASをdiff表示 v3
on open fileList
  
  
tell application “Finder”
    set sortedList to sort fileList by creation date –オリジナルはmodification dateだったが……趣味の問題?
  end tell
  
  
set sortedList to sortedList’s reverse
  
  
set oldPath to writeASSourceToTempFolder((sortedList’s item 1) as alias)
  
set newPath to writeASSourceToTempFolder((sortedList’s item 2) as alias)
  
  
do shell script “/usr/bin/opendiff “ & oldPath’s POSIX path’s quoted form & ” “ & newPath’s POSIX path’s quoted form & ” > /dev/null 2>&1 &”
  
  
–指定プロセスでDialogが出て処理が停まっている場合には、強制的にダイアログをクローズして処理続行させる
  
clickFrontDialog(“FileMerge”) of me
  
end open

–AppleScriptのソースを取得してファイルに書き出し
on writeASSourceToTempFolder(macPath)
  set aName to (info for macPath size 0)’s name
  
set tmpPath to ((path to temporary items from user domain) as text) & aName & “__” & (do shell script “/usr/bin/uuidgen”) & “.txt”
  
  
set scptText to do shell script “/usr/bin/osadecompile “ & macPath’s POSIX path’s quoted form
  
  
set accessFile to open for access file tmpPath with write permission
  
set eof of accessFile to 0
  
write scptText to accessFile starting at eof as text
  
close access accessFile
  
  
return tmpPath
end writeASSourceToTempFolder

–表示されるダイアログを乗り越えるためのルーチン(「キャンセル」もしくは「Cancel」ではない方のボタンを押す)
on clickFrontDialog(aProcName)
  –余計なプロセスを起動せずにactivate
  
activateAproc(aProcName) of me
  
–tell application aProcName to activate
  
–tell application “FileMerge” to activate
  
  
–ダイアログ検出
  
tell application “System Events”
    tell process aProcName
      if (count every window) 1 then
        if subrole of window 1 = “AXDialog” then
          tell window 1
            set bCount to count every button
            
            
–「キャンセル」もしくは「Cancel」ではない方のボタンを取得
            
repeat with i in {“キャンセル”, “Cancel”} –キャンセルに該当するキーワードのリストを各国語分だけ用意しておく???
              set bList to (every button whose title is not equal to (contents of i))
              
if length of bList is not equal to bCount then
                set aButton to first item of bList
                
tell aButton
                  click
                  
exit repeat
                end tell
              end if
            end repeat
          end tell
        end if
      end if
    end tell
  end tell
end clickFrontDialog

on activateAproc(aName)
  tell application “System Events”
    set aList to every process whose name is aName and visible of it is true
    
if length of aList 1 then
      set aProc to first item of aList
      
tell aProc
        set frontmost to true
      end tell
    end if
  end tell
end activateAproc

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2011/04/05 Mail.appでメールの送信(プレーンテキストへの変換つき)

Mail.appでメールの送信を行うAppleScriptの、Mac OS X 10.6用に機能追加を行ったものです。

Mac OS X 10.6からは、AppleScript経由でMail.appをコントロールしてメールを作成・送信すると、なんとプレーンテキスト(=標準テキスト)ではなく、リッチテキストで送られてしまいます。言われてみないと、なかなか気づきにくい箇所です(コメント欄でのご指摘ありがとうございます)。

ただ……Mail.appの環境設定をいじくっても、挙動に変化はありません。AppleScriptから送信命令を送ると、リッチテキストになってしまいます。

バグ……だと思うのですが、すでにMac OS X 10.6.x上で修正の見込みはなく、その場しのぎにGUI Scriptingでメニューからコマンドを叩き込んで、強制的にプレーンテキストに変換してから送信するようにしてみました。

Mac OS X 10.7に向けて、修正の要望を出しておく必要があります。ただ、Appleはあくまでも統計的にバグの処理を行うため……1人や2人が騒いだところで取りあいません。このバグが直ってほしいと思った方は、Appleに意見を送ってください。

スクリプト名:Mail.appでメールの送信(プレーンテキストへの変換つき)
set aSubject to "メールのサブジェクトです"
set theSender to "maro@sender.com" –送信者アドレス
set recipientAddr to "maro@reciever.jp" –受信者アドレス
set bodyText to "ひよこさんへ
こんにちは。ひよこさん。
ひよこクラブへのおさそいです。

ぴよ〜"

sendMail(aSubject, theSender, recipientAddr, bodyText) of me

–Mail.appでメール送信を行う
on sendMail(theSubject, theSender, recipientAddr, bodyText)
  tell application "Mail"
    set newMessage to make new outgoing message with properties {subject:theSubject, content:bodyText}
    
tell newMessage
      set visible to true
      
set sender to theSender
      
make new to recipient at end of to recipients with properties {address:recipientAddr}
      
      
makeAMessageToPlain() of me
      
      
send
    end tell
  end tell
end sendMail

–作成したメールがリッチテキストなら標準テキスト(プレーンテキスト)に直す
on makeAMessageToPlain()
  activate application "Mail"
  
  
tell application "System Events"
    tell process "Mail"
      –メニュー内容が「Mail.app」>「フォーマット」>「標準テキストにする」だったら現在リッチテキストだと判断し、コマンド実行
      
set aTitle to title of menu item 11 of menu 1 of menu bar item 8 of menu bar 1
      
      
if aTitle = "標準テキストにする" then
        –「標準テキストにする」をクリック
        
click menu item 11 of menu 1 of menu bar item 8 of menu bar 1
      end if
    end tell
  end tell
end makeAMessageToPlain

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2011/03/27 サウンド入出力の選択中の項目名を取得

「システム環境設定」で選択中のサウンド出力先、サウンド入力元の名称を取得するAppleScriptです。

例によってGUI Scriptingを用いているため、ユニバーサルアクセスの「補助装置にアクセスできるようにする」をチェックしておく必要があります。

sound0.jpg

sound00.jpg

また、OSのバージョンが上がると書き換える必要が出てきます。

soud1.jpg

ここで選択されているサウンド入力元の名称を取得します。

soud2.jpg

ここで選択されているサウンド出力先の名称を取得します。

スクリプト名:サウンド入出力の選択中の項目名を取得
set inRes to getSelectedSoundIntputName() of me
–> “内蔵マイク”

set outRes to getSelectedSoundOutputName() of me
–> “内蔵スピーカー”

–サウンド入力元の選択されている名称を取得
on getSelectedSoundIntputName()
  activate application “System Preferences”
  
displaySoundPaneInSystemPrefs() of me
  
  
tell application “System Events”
    tell process “システム環境設定”
      click radio button 3 of tab group 1 of window 1 –「入力」タブを選択
      
set vList to value of attribute “AXVisibleRows” of table 1 of scroll area 1 of tab group 1 of window 1
      
set vCount to count every item of vList
      
      
tell table 1 of scroll area 1 of tab group 1 of window 1
        set aList to every row whose selected is true
        
set anItem to first item of aList
        
tell anItem
          set aStr to value of text field 1
        end tell
      end tell
      
    end tell
  end tell
  
  
return aStr
end getSelectedSoundIntputName

–サウンド出力先の選択されている名称を取得
on getSelectedSoundOutputName()
  activate application “System Preferences”
  
displaySoundPaneInSystemPrefs() of me
  
  
tell application “System Events”
    tell process “システム環境設定”
      click radio button 2 of tab group 1 of window 1 –「出力」タブを選択
      
set vList to value of attribute “AXVisibleRows” of table 1 of scroll area 1 of tab group 1 of window 1
      
set vCount to count every item of vList
      
      
tell table 1 of scroll area 1 of tab group 1 of window 1
        set aList to every row whose selected is true
        
set anItem to first item of aList
        
tell anItem
          set aStr to value of text field 1
        end tell
      end tell
      
    end tell
  end tell
  
  
return aStr
end getSelectedSoundOutputName

–システム環境設定で「サウンド」に表示を切り替える
on displaySoundPaneInSystemPrefs()
  tell application “System Preferences”
    activate
    
set current pane to pane “com.apple.preference.sound”
  end tell
end displaySoundPaneInSystemPrefs

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2011/03/20 サウンド入力元、出力先を設定する

システム環境設定の「サウンド」で入力元、出力先を切り替えるAppleScriptです。

例によってGUI Scriptingのプログラムであるため、OSがアップデートした際には微妙な手直しが発生するはずです。

いいかげん、オーディオ入出力関連の切り替えごときは標準命令で持っていてほしいのですが……10.7で標準搭載してくれないものでしょうか。

スクリプト名:サウンド入力元、出力先を設定する
set aRes to setSountInputSource("ライン入力") of me
–> true(設定成功の場合。失敗の場合にはfalseを返す)

set aRes to setSountOutputSource("内蔵スピーカー") of me
–> true(設定成功の場合。失敗の場合にはfalseを返す)

–サウンド入力先を設定
on setSountInputSource(aName)
  activate application "System Preferences"
  
displaySoundPaneInSystemPrefs() of me
  
  
tell application "System Events"
    tell process "システム環境設定"
      click radio button 3 of tab group 1 of window 1 –「入力」タブを選択
      
set vList to value of attribute "AXVisibleRows" of table 1 of scroll area 1 of tab group 1 of window 1
      
set vCount to count every item of vList
      
      
set outList to {}
      
repeat with i from 1 to vCount
        set aStr to value of text field 1 of row i of table 1 of scroll area 1 of tab group 1 of window 1
        
if aStr = aName then
          set selected of row i of table 1 of scroll area 1 of tab group 1 of window 1 to true
          
return true
        end if
      end repeat
    end tell
  end tell
  
  
return false
end setSountInputSource

–サウンド出力先を設定
on setSountOutputSource(aName)
  activate application "System Preferences"
  
displaySoundPaneInSystemPrefs() of me
  
  
tell application "System Events"
    tell process "システム環境設定"
      click radio button 2 of tab group 1 of window 1 –「入力」タブを選択
      
set vList to value of attribute "AXVisibleRows" of table 1 of scroll area 1 of tab group 1 of window 1
      
set vCount to count every item of vList
      
      
set outList to {}
      
repeat with i from 1 to vCount
        set aStr to value of text field 1 of row i of table 1 of scroll area 1 of tab group 1 of window 1
        
if aStr = aName then
          set selected of row i of table 1 of scroll area 1 of tab group 1 of window 1 to true
          
return true
        end if
      end repeat
    end tell
  end tell
  
  
return false
end setSountOutputSource

–システム環境設定で「サウンド」に表示を切り替える
on displaySoundPaneInSystemPrefs()
  tell application "System Preferences"
    activate
    
set current pane to pane "com.apple.preference.sound"
  end tell
end displaySoundPaneInSystemPrefs

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2011/03/20 サウンド入力元、出力先の名称をリストで取得する

システム環境設定から、サウンドの入力元、出力先の名称をリストで取得するAppleScriptです。

よくあるGUI Scriptingのプログラムなので、Mac OS X 10.7になったら手直しが必要になること必至ではありますが、(個人的に)早急に必要になったので組んでみました。

スクリプト名:サウンド入力元、出力先の名称をリストで取得する
set outList to getEverySoundOutputName() of me
–> {"内蔵スピーカー", "PROPlantronics", "Soundflower (2ch)", "Soundflower (16ch)"}

set inList to getEverySoundInputName() of me
–> {"内蔵マイク", "ライン入力", "PROPlantronics", "Soundflower (2ch)"}

–サウンド出力先のリストを取得
on getEverySoundOutputName()
  activate application "System Preferences"
  
displaySoundPaneInSystemPrefs() of me
  
  
tell application "System Events"
    tell process "システム環境設定"
      click radio button 2 of tab group 1 of window 1 –「出力」タブを選択
      
set vList to value of attribute "AXVisibleRows" of table 1 of scroll area 1 of tab group 1 of window 1
      
set vCount to count every item of vList
      
      
set outList to {}
      
repeat with i from 1 to vCount
        set aStr to value of text field 1 of row i of table 1 of scroll area 1 of tab group 1 of window 1
        
set the end of outList to aStr
      end repeat
    end tell
  end tell
  
  
return outList
end getEverySoundOutputName

–サウンド入力先のリストを取得
on getEverySoundInputName()
  activate application "System Preferences"
  
displaySoundPaneInSystemPrefs() of me
  
  
tell application "System Events"
    tell process "システム環境設定"
      click radio button 3 of tab group 1 of window 1 –「入力」タブを選択
      
set vList to value of attribute "AXVisibleRows" of table 1 of scroll area 1 of tab group 1 of window 1
      
set vCount to count every item of vList
      
      
set outList to {}
      
repeat with i from 1 to vCount
        set aStr to value of text field 1 of row i of table 1 of scroll area 1 of tab group 1 of window 1
        
set the end of outList to aStr
      end repeat
    end tell
  end tell
  
  
return outList
end getEverySoundInputName

–システム環境設定で「サウンド」に表示を切り替える
on displaySoundPaneInSystemPrefs()
  tell application "System Preferences"
    activate
    
set current pane to pane "com.apple.preference.sound"
  end tell
end displaySoundPaneInSystemPrefs

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2011/02/04 フォルダ内の画像ファイルを順次Numbersのセルに貼り付ける

指定フォルダ内に入っている画像を順次Numbersのドキュメントに貼り付けるAppleScriptです。

指定フォルダ内の画像をPhotoshopで順次オープンして、Numbersのドキュメントに貼り付けます。

Numbersには画像を貼り込むような命令は用意されていないので、GUI Scripting経由でペーストを行います。

スクリプト名:フォルダ内の画像ファイルを順次Numbersのセルに貼り付ける
property aRange : “B”

set aFol to choose folder with prompt “Numbersに貼り付ける画像が入っているフォルダを選択してください”

tell application “Finder”
  set aList to (every file of aFol) as alias list
end tell

set aLen to length of aList

tell application “Numbers”
  tell document 1
    tell sheet 1
      tell table 1
        set row count to aLen + 5
      end tell
    end tell
  end tell
end tell

set aCounter to 2

repeat with i in aList
  tell application “Adobe Photoshop CS3″
    activate
    
open i
    
set d1Doc to (a reference to current document)
    
    
tell d1Doc
      select all
      
copy
    end tell
    
    
tell document 1
      close saving no
    end tell
    
  end tell
  
  
  
set aRangeStr to aRange & (aCounter as string) & “:” & aRange & (aCounter as string)
  
  
tell application “Numbers”
    tell document 1
      tell sheet 1
        tell table 1
          set selection range to range aRangeStr
        end tell
      end tell
    end tell
  end tell
  
  
activate application “Numbers”
  
tell application “System Events”
    tell process “Numbers”
      keystroke “v” using {command down}
    end tell
  end tell
  
  
  
set aCounter to aCounter + 1
  
end repeat

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に