【AppleScript】【JavaScript for Automation(JXA)】QuickTime Playerの画面収録を時間指定で実行するスクリプトについて

# 概要
MacのQuickTime Playerでは画面収録機能を使うことで画面上の動作を記録し、動画ファイルとして保存することができます。本記事では画面収録を時間指定で実行するスクリプトを紹介します。

# スクリプトについて
スクリプトはMacのスクリプトエディタ使用して、AppleScript、および、JavaScript for Automation(JXA)のscptファイルを作成して実行します。
使い方はソースコード先頭のコメントを参照してください。

# 注意点
スクリプトではSystem Eventsを使用します。System Eventsを実行するためにはスクリプトエディタに対してセキュリティ設定を許可してください。
「システム環境設定」の「セキュリティーとプライバシー」＞「プライバシー」＞「アクセシビリティ」＞ 「スクリプトエディタ」のコンピューター制御を許可する。

# ソースコード
ソースコードはGitHubにもコミットしています。
また、本記事とは別にQuickTime Playerで起動した1つ以上の動画ファイル対して同時に再生処理をさせて、時間指定で画面収録するスクリプトをコミットしています。こちらも参考にしてください。

https://github.com/7rikazhexde/AppleScript-JXA-Collection

## AppleScript
```QuickTimePlayerAutoScreenRecordingAS.scpt
(********************************************************
 Summary:
	MacOSのQuickTime Playerの画面収録機能を使用して
	指定された時間で画面収録するAppleScript
 
 SupportVersion:
	macOS Monterey 
	version 12.4
 
 Usage:
	1.QuickTime Playerの設定
		command+shift+5でスクリーンショットのツールバーを起動し、
		選択部分を収録を選択し、収録したい範囲と
		ファイルの保存先（オプション→保存先）を設定する。
	2.収録時間入力
		収録時間を秒指定で起動後に表示されるダイアログに入力する
		ex)3600(秒) = 60(分) = 1(時間)
		※収録時間はプロセスに依存するため数秒〜数十秒の時間のずれが発生します。
		 収録時間には収録したい時間よりも大きい値を設定してください。

 reference:
	1.AppleScript のサンプルコード
	https://dev.classmethod.jp/articles/applescript-sample/
	2.QuickTime Playerでオープン中の全ムービーを頭出しして同時再生
	http://piyocast.com/as/archives/tag/quicktime-player
********************************************************)

(******************************************************** 
 関数名
    ask_recording_time():
    収録時間を入力指示するダイアログを表示して入力された値を返す関数
 引数
    なし
 戻り値
    recording_time: 収録時間
 説明
    収録時間は数値のみ対象として処理する
********************************************************)
on ask_recording_time()
    repeat
        set results to (display dialog "Please enter the recording time(sec)" default answer "" with icon note buttons {"Cancel", "Continue"} default button "Continue")
        set input_returned to text returned of results
        set button_returned to button returned of results
        
        if button_returned = "Cancel" then
            (display alert "Pushed cancel button. Quit script.")
            quit {}
        end if
        
        try
            set recording_time to input_returned as number
            exit repeat
        end try
        
    end repeat
    log "[ask_recording_time()]Recording time" & recording_time & "sec"
    return recording_time
end ask_recording_time

(********************************************************
 関数名
    screen_recording(input_time)
    説明： 画面記録処理関数
 引数
    input_time: 収録時間
 戻り値
    なし
 説明
    システムイベントからのショートカットキー入力により、
　　　　　　　　スクリーンショットツールバーが起動され、記録が開始される。
    記録する経過時間は、データオブジェクトからcurrent dateコマンドで取得する。
    収録時間経過後、記録開始と同様にショートカットキー入力で記録を停止する。
********************************************************)
on screen_recording(input_time)
    tell application "System Events"
        -- command+shift+5 activates the screenshot toolbar
        key code 23 using {command down, shift down}
        -- Allow 0.5 seconds to activate the record button (adjust by number of screens)
        delay 0.5
        -- return
        key code 36
        log "[screen_recording()]Recording start"
    end tell
    
    set cDate to (current date)
    delay input_time
    set elapsedTime to ((current date) - cDate)
    
    tell application "System Events"
        -- command+control+esc to complete recording
        key code 53 using {command down, control down}
        -- Commentout the dialog display if it is not needed.
        log "[screen_recording()]Recording end:Elapsed time" & elapsedTime & "sec"
    end tell
    
    return
end screen_recording

(******************************************************** 
　関数名
    quit_quicktimeplayer():
    QuickTime Playerを終了させる関数
　引数
    なし
　戻り値
    なし
　説明
    QuickTime Playerをquitコマンドで終了させる。
********************************************************)
on quit_quicktimeplayer()
    tell application "QuickTime Player"
        log "[quit_quicktimeplayer()]QuickTime Player quit"
        quit
    end tell
end quit_quicktimeplayer

(********************************************************
　関数名:
    main():
    QuickTime Playerで画面収録処理するmain関数
　引数
    なし
　戻り値
    なし
　説明
    QuickTime Playerで入力された指定時間で画面収録する(開始〜停止)
    指定時間経過後、画面収録を終了し、QuickTime Playerを終了し、ダイアログを表示する。
********************************************************)
on main()
    set input_num to ask_recording_time()
    if input_num is greater than 0 then
        screen_recording(input_num)
        quit_quicktimeplayer()
        (display dialog "Screen recording is complete. Quit script." buttons {"Cancel", "OK"} default button "OK")
    else
        (display dialog "Please enter a positive integer for the elapsed time.Quit script." buttons {"Cancel", "OK"} default button "OK")
    end if
    log "[main()]Script end"
    return
end main

-- main function call
main()
```
## JXA
```QuickTimePlayerAutoScreenRecordingJS.scpt
/********************************************************
 Summary:
	MacOSのQuickTime Playerの画面収録機能を使用して
	指定された時間で画面収録するJavaScript for Automation(JXA)

 SupportVersion:
	macOS Monterey 
	version 12.4

 Usage:
	1.QuickTime Playerの設定
		command+shift+5でスクリーンショットのツールバーを起動し、
		選択部分を収録を選択し、収録したい範囲と
		ファイルの保存先（オプション→保存先）を設定する。
	2.収録時間設定
		収録時間を秒指定で起動後に表示されるダイアログに入力する
		ex)3600(秒) = 60(分) = 1(時間)
		※収録時間はプロセスに依存するため数秒〜数十秒の時間のずれが発生します。
		 収録時間には収録したい時間よりも大きい値を設定してください。
		 
 reference
	1.JavaScript for Automation Release Notes
	https://developer.apple.com/library/archive/releasenotes/InterapplicationCommunication/RN-JavaScriptForAutomation/Articles/Introduction.html#//apple_ref/doc/uid/TP40014508-CH111-SW1
	2.Mac : JavaScript for Automation (JXA) 例文辞典
	http://www.openspc2.org/reibun/JXA/Yosemite/
	3.JavaScript for Automation (JXA)
	http://tonbi.jp/AppleScript/JavaScript/
	4.JavaScript for Automation Cookbook
	https://github.com/JXA-Cookbook/JXA-Cookbook/wiki/
********************************************************/

/******************************************************** 
 関数名
    ask_recording_time():
    収録時間を入力指示するダイアログを表示して入力された値を返す関数
 引数
    なし
 戻り値
    recording_time: 収録時間
 説明
    収録時間は数値のみ対象として処理する
********************************************************/
function ask_recording_time(){
	var app = Application.currentApplication()
	app.includeStandardAdditions = true
 	var str_check = true;
	
	// 数値以外繰り返し
	while(str_check == true){
		var response = app.displayDialog("Please enter the recording time(sec)", {
    	defaultAnswer: "",
    	withIcon: "note",
   		buttons: ["Cancel", "OK"],
    	defaultButton: "OK"
		})
	
		if(!(isNaN(response.textReturned))){
			// 数字ならループを抜ける
			str_check = false;
		}

		if(response.buttonReturned == "Cancel"){
			app.displayAlert('Pushed cancel button. Quit script.');
			app.quit()
		}
	}
	console.log('[ask_recording_time()]Recording time%isec', response.textReturned);
	return response.textReturned
}

/********************************************************
 関数名
    screen_recording(input_time)
    説明： 画面記録処理関数
 引数
    input_time: 収録時間
 戻り値
    なし
 説明
    System Eventsからショートカットキー入力でスクリーンショットのツールバーを起動し収録開始する
	収録する経過時間はDateオブジェクトから取得、計算して設定する
	収録時間経過後は収録開始と同様にショートカットキー入力で収録停止する
********************************************************/
function screen_recording(input_time){
	var sys = Application('System Events');
	// command+shift+5
	sys.keyCode('23', { using: ['command down', 'shift down']});
	delay(0.5);
	// return
	sys.keyCode('36');
	console.log('[screen_recording()]Recording start')
	
	const startTime = Date.now();
	delay(input_time);
	const elapsedTime = (Date.now() - startTime) / 1000;
	
	// command+control+esc
	sys.keyCode('53', { using: ['command down', 'control down']});
	console.log('[screen_recording()]Recording end:Elapsed time%isec', elapsedTime);
	return
}

/******************************************************** 
 関数名:
	quit_quicktimeplayer():
	QuickTime Playerを終了する関数
 引数:
	なし
 戻り値：
	なし
 説明:
	quitコマンドでQuickTime Playerを終了する
********************************************************/
function quit_quicktimeplayer(){
	var qt = Application('QuickTime Player')
	console.log('[quit_quicktimeplayer()]QuickTime Player quit');
	qt.quit()
	return
}

/******************************************************** 
 関数名:
    main():
    QuickTime Playerで画面収録処理するmain関数
 引数
    なし
 戻り値
    なし
 説明
    QuickTime Playerで入力された指定時間で画面収録する(開始〜停止)
    指定時間経過後、画面収録を終了し、QuickTime Playerを終了し、ダイアログを表示する。
********************************************************/
function main(){
	var input_num = ask_recording_time()
	var app = Application.currentApplication();
	app.includeStandardAdditions = true;
		
	if(input_num > 0){
		console.log(input_num)
		screen_recording(input_num);
		quit_quicktimeplayer();
		app.displayAlert('Screen recording is complete. Quit script.');
		console.log('[main()]Script end')
	}
	else{
		app.displayAlert('Please enter a positive integer for the elapsed time.\n Quit script.');
	}
	return
}

//main function call
main()
```

# まとめ
本記事ではQuickTimeの画面収録を指定時間指定で実行するスクリプトを紹介しました。

本スクリプトはscptファイルでスクリプトエディタから実行する形式ですが、ファイルフォーマットでアプリケーション指定で書き出し処理をすれば`.appファイル`を直接実行する事ができます。(※初回起動時はセキュリティとプライバシー設定よりコンピューター制御を許可する必要があります。）

また、ApplescriptとJXAはautomatorとショートカットアプリからも実行することができます。automatorでは例えばカレンダーアプリと連携することで指定の時間でスクリプトを実行するということもできるようです。
ショートカットアプリからスクリプトを実行する方法としては以下2つの方法がありますが、
  1. AppleScript、もしくは、Javascript for Automationを実行
  2. SSH経由でスクリプトを実行するレシピから[osascriptコマンド](https://support.apple.com/ja-jp/guide/terminal/trml1003/mac)から実行する

2.についてはリモートログインを許可して、公開鍵認証もしくはパスワード認証の設定することで実行できますが、1.についてはセキュリティの問題があるのか、私のPCではショートカットからセキュリティ設定を許可してもSystem Eventsを実行できせんでした。一度エラーになるとautomatorとスクリプトエディタも実行できなくなりました。(automatorとスクリプトエディタは再度コンピューター制御を許可すれば実行できました。）
もしかするとバグがあるのかも知れません。

Applescriptでスクリプトを作成すれば、大抵のMac上の操作はスクリプト化して自動化できるのではないかと思いますが、ドキュメントが少ない（特にJXA）ので作成するのに時間がかかってしまう印象です。
ただ、スクリプトからアプリを操作できる点や[フォルダアクション](https://sites.google.com/site/zzaatrans/home/applescriptlangguide/reference/aslr_folder_actions-html)など、他にも活用できる特徴があるため、自動化したい操作があればまた作成したいと思います。

# 参考
* [AppleScript のサンプルコード](https://dev.classmethod.jp/articles/applescript-sample/)
* [mytrans マニュアル等の個人的な翻訳](https://sites.google.com/site/zzaatrans/home/applescriptlangguide)
* [JavaScript for Automation Release Notes](https://developer.apple.com/library/archive/releasenotes/InterapplicationCommunication/RN-JavaScriptForAutomation/Articles/Introduction.html#//apple_ref/doc/uid/TP40014508-CH111-SW1)
* [Mac : JavaScript for Automation例文辞典](http://www.openspc2.org/reibun/JXA/Yosemite/)
* [JavaScript for Automation(JXA)](http://tonbi.jp/AppleScript/JavaScript/)
* [JavaScript for Automation(JXA) Cookbook](https://github.com/JXA-Cookbook/JXA-Cookbook/wiki/)