AppleScriptによるiTunesのスリープタイマー

AppleScriptの勉強をかねて、iTunes11でのスリープタイマーアプリケーションを作成してみました。

おヒマな方、AppleScriptに詳しい方には、ぜひお試しいただいて改善点などアドバイスをいただけたら幸いです。

当方、プログラム経験(COBOL,Visual Basic等)はあるものの、AppleScriptは全くの素人です。どんな事でも結構ですので何とぞご指導ください。




なお、iTunes11には「AppleScriptでシャッフルとリピートの情報を取得、設定できない」というバグがあるそうです。こちらにその報告があります。

iTunes 11 AppleScript bug « Doug's AppleScripts for iTunes

iTunes10.7では問題なく、iTunes11にて発生したバグであることは当方でも確認いたしました。


また、その対応策を考えられた方がいらっしゃいましたので、そちらからサンプルサブルーチンを拝借いたしました。それがこちらに公開されています。

How to set iTunes 11 in shuffle or repeat mode via applescript ...

このサブルーチンはiTunes11専用です。また、下記ソースコードでは日本語環境用に少々カスタマイズしています。




以下がソースコードです。(先頭のデフォルト値は、各々の好みで変更してください)

set myShuffleList to {"曲別", "アルバム別", "グループ別"}

set myTimerList to {"00:05:00", "00:15:00", "00:30:00", "01:00:00", "01:30:00", "02:00:00"}

set myDelayTime to 10


set myDefaultShuffle to "アルバム別"

set myDefaultPlayList to "お気に入りのラジオ番組"

set myDefaultTrack to "Jazz Wyoming"

set myDefaultTimer to "01:00:00"


try



-- iTunesが起動されているか?

if application "iTunes" is not running then

display alert "iTunesが起動されていません:" message "起動しますか?" as warning buttons {"No", "Yes"} default button "No" cancel button "No"

if result is {"No"} then

error number -128

else

tell application "iTunes" to run

end if

end if


tell application "iTunes"



-- Windowを表示


activate

set visible of front window to true



-- プレイリストを選択

set mySelectPlaylist to (choose from list (name of every playlist as list) with prompt "プレイリストを選択して下さい:" & return & "[キャンセル]で、再生を中止します。" default items myDefaultPlayList)

if mySelectPlaylist is false then

error number -128

else

set myPlaylist to playlist (mySelectPlaylist as string)


revealmyPlaylist

end if



-- トラックを選択

set mySelectTrack to (choose from list (name of every track of myPlaylist as list) with prompt "トラックを選択して下さい:" & return & "[キャンセル]で、全曲を再生します。" default items myDefaultTrack)

if mySelectTrack is false then


-- プレイリストを再生

set myTrack to myPlaylist

set myKind to ""


-- シャッフル再生?

set mySelectShuffle to (choose from list myShuffleList with prompt "シャッフル再生をしますか:" & return & "[キャンセル]で、先頭から再生します。" default items myDefaultShuffle)

if mySelectShuffle is false then

set myShuffle to "オフ"

else

set myShuffle to (mySelectShuffle as string)

end if

my setShuffleType(myShuffle)

else


-- トラックを再生

set myTrack to track (mySelectTrack as string) of myPlaylist

set myKind to kind of myTrack


revealmyTrack

end if



-- タイマーを選択:プレイリスト再生 or ラジオ再生

if (myKind is "") or (myKind is "インターネットオーディオストリーム") then

set myTimer to (choose from list myTimerList with prompt "再生時間を選択して下さい:" & return & "[キャンセル]で、再生を中止します。" default items myDefaultTimer)

if myTimer is false then error number -128

else

set myTimer to my retSecondsToTimer(((duration of myTrack) as integer) + 1)

end if



-- 再生開始

play myTrack with once


revealcurrent track



-- 再生時間の監視

set mySeconds to my retTimerToSeconds(myTimer as string)

repeat (mySeconds div myDelayTime + 1) times

if player state is not playing then exit repeat


delaymyDelayTime

end repeat


repeat with myVolume from 100 to 0 by -10

set sound volume to myVolume

delay 1

end repeat


stop

my setShuffleType("オフ")

set sound volume to 100

set visible of front window to false


end tell



-- エラー出口

on error wCaptionnumberwErrNum

if wErrNum = -128 then


-- "Cancelled!!"

else

error wCaptionnumberwErrNum

end if


end try


-- HH:MM:SSを秒に変換

on retTimerToSeconds(hhmmss)

set hh to (text from character 1 to character 2 of hhmmss) as integer

set mm to (text from character 4 to character 5 of hhmmss) as integer

set ss to (text from character 7 to character 8 of hhmmss) as integer

return (hh * hours + mm * minutes + ss)

end retTimerToSeconds


-- シャッフル状態の取得

on getShuffleType() -- the return value is a string: Off/By Songs/By Albums/By Groupings

tell application "System Events"

tell process "iTunes"

set menuItems to menu items of menu bar 1's menu bar item "制御"'s menu 1's menu item "シャッフル"'s menu 1

set onOffItemName to name of item 1 of menuItems

end tell

end tell



-- is shuffle off

ignoring case

if onOffItemName contains "シャッフルをオン" then return "オフ"

end ignoring



-- shuffle is on so find how we are shuffling

set currentChoice to "Unknown"

tell application "System Events"

tell process "iTunes"

repeat with i from 2 to count of menuItems

set anItem to item i of menuItems

try

set theResult to value of attribute "AXMenuItemMarkChar" of anItem

if theResult is not "" then

set currentChoice to name of anItem

exit repeat

end if

end try

end repeat

end tell

end tell

return currentChoice

end getShuffleType


-- シャッフルの設定

on setShuffleType(shuffleType) -- shuffleType is a string: Off/By Songs/By Albums/By Groupings

set currentValue to my getShuffleType()


script subs

on toggleShuffleOnOff(OnOff)

tell application "System Events" to perform action "AXPress" of (first menu item of process "iTunes"'s menu bar 1's menu bar item "制御"'s menu 1's menu item "シャッフル"'s menu 1 whose name ends with "シャッフルを" & OnOff)

end toggleShuffleOnOff


on pressBySongs()

tell application "System Events" to perform action "AXPress" of (first menu item of process "iTunes"'s menu bar 1's menu bar item "制御"'s menu 1's menu item "シャッフル"'s menu 1 whose name ends with "曲別")

end pressBySongs


on pressByAlbums()

tell application "System Events" to perform action "AXPress" of (first menu item of process "iTunes"'s menu bar 1's menu bar item "制御"'s menu 1's menu item "シャッフル"'s menu 1 whose name ends with "アルバム別")

end pressByAlbums


on pressByGroupings()

tell application "System Events" to perform action "AXPress" of (first menu item of process "iTunes"'s menu bar 1's menu bar item "制御"'s menu 1's menu item "シャッフル"'s menu 1 whose name ends with "グループ別")

end pressByGroupings

end script


ignoring case

if shuffleType contains "オフ" then -- we have to make sure it's off

if currentValue does not contain "オフ" then subs's toggleShuffleOnOff("オフ")

else


-- make sure it's on

if currentValue contains "オフ" then subs's toggleShuffleOnOff("オン")



-- select the shuffle menu item for the type

if shuffleType contains "曲別" and currentValue does not contain "曲別" then


subs's pressBySongs()

else if shuffleType contains "アルバム別" and currentValue does not contain "アルバム別" then


subs's pressByAlbums()

else if shuffleType contains "グループ別" and currentValue does not contain "グループ別" then


subs's pressByGroupings()

end if

end if

end ignoring

end setShuffleType


なお、リピート再生については、実用的でないことから対応しておりません。

MacBook Pro (15-inch Early 2008), OS X Mountain Lion, Core2Duo 2.4GHz , 4G RAM

投稿日 2013/05/02 19:47

返信
返信: 47

2013/05/02 20:20 Pajerow への返信

夜の就寝時に使うと便利かな...と思います。



あっ、それと、

「システム環境設定>アクセシビリティ>補助装置にアクセスできるようにする」をオンにしておく必要があります。


* タイトルを変更し、スレッドを移動しました。 Apple Inc.

2013/05/03 00:14 Pajerow への返信

Mac OS X 10.6.8 で動きました。


まず、不具合報告ですが、ライブラリ選択 → トラック選択 → 「«script» は retSecondsToTimer メッセージを認識できません。」となりエラー終了します。トラック選択で「キャンセル = 全曲再生」にすると大丈夫です。


それと、「エラー出口」は エラーで終了するのではなく、アラートダイアログを出すか、return wCaption ... として終わらせた方が良いような気がします。


その他は大丈夫のようです。強いて言えば、ignoring case は不要かなと。あと、現在の音量は Max とは限らないので「現在の音量 → フェードアウト → 元の音量」とした方が良いかなと思います。


# スリープタイマー作ろうかなと思ってたので、ありがたく使わせていただきます。

2013/05/03 00:32 Hiro__S への返信

あと、現在の音量は Max とは限らないので「現在の音量 → フェードアウト → 元の音量」とした方が良いかなと思います。


初めにテストしたとき、音量がフェードアウトする直前に音量が大きくなってびっくりしたんですがその後のテストでは再現できず。たまたまかと思っていたのですがこれが原因のようですね。

iTunesの音量設定がmaxでない場合、フェードアウト前に音量がmaxに設定されるので音量が上がり、フェードアウト後に音量がmaxに戻されるのでその後のテストでは再現できなかった・・・と。

フェードアウト前に音量が上がるとびっくりして飛び起きてしまうので改善してもらえるとより実用的になるかと思います (^^)

2013/05/03 02:47 Hiro__S への返信

Hiro.Sさん、こんにちは。早速のご報告、誠にありがとうございました。

Hiro.S さんによる書き込み:


まず、不具合報告ですが、ライブラリ選択 → トラック選択 → 「«script» は retSecondsToTimer メッセージを認識できません。」となりエラー終了します。トラック選択で「キャンセル = 全曲再生」にすると大丈夫です。

上記の不具合ですが、投稿したときに下記のサブルーチンが抜けておりました。

申し訳ありません。追加貼付けをお願いします。

-- 秒をHH:MM:SSに変換

on retSecondsToTimer(ssNum)

set hhStr to retZeroPaddingText((ssNum div hours) as string, 2)

set mmStr to retZeroPaddingText((ssNum mod hours div minutes) as string, 2)

set ssStr to retZeroPaddingText((ssNum mod hours mod minutes) as string, 2)

return hhStr & ":" & mmStr & ":" & ssStr

end retSecondsToTimer


-- ゼロパディング

on retZeroPaddingText(aNum, aLen)

do shell script "printf '%0" & aLen & "d' " & aNum

end retZeroPaddingText


なお、ゼロパディングのルーチンは、iTunesの player positionの情報をコピーできない。[Applescript]Hiro.Sさんにご指導いただいたものです。(^^;


Hiro.S さんによる書き込み:


それと、「エラー出口」は エラーで終了するのではなく、アラートダイアログを出すか、return wCaption ... として終わらせた方が良いような気がします。


その他は大丈夫のようです。強いて言えば、ignoring case は不要かなと。あと、現在の音量は Max とは限らないので「現在の音量 → フェードアウト → 元の音量」とした方が良いかなと思います。

アドバイス、ありがとうございます。勉強してみます。

できたらまた報告させていただきますので、ご指導よろしくお願いいたします。m(_ _)m

2013/05/03 02:55 HAL への返信

HALさん、こんにちは。早速のご報告、誠にありがとうございました。


HAL さんによる書き込み:


フェードアウト前に音量が上がるとびっくりして飛び起きてしまうので改善してもらえるとより実用的になるかと思います (^^)

思わぬ笑い話になってしまいましたね。スリープタイマーで飛び起きちゃうなんて...(^^)

早速、対応を考えたいと思います。


私は普段、iTunesのボリュームは常にmaxの状態で全く触らないので、ぜんぜん気が付きませんでした。

2013/05/03 03:35 Hiro__S への返信

Hiro.S さんによる書き込み:


それと「ゼロパディング」使っていただいたのですね。嬉しいです。

そもそも、私がこのスリープタイマーを作ろうとしたきっかけが先のスレッドでした。(^^)

やっぱりプログラミングって本当に楽しいですね。

職場ではもうプログラマとしてはお払い箱になってしまいましたけど、趣味として続けていこうと思っています。

2013/05/03 04:48 Pajerow への返信

終了(フェードアウト)部分を改善しました。

repeatwithmyVolumefrom 100 to 0 by -10

set sound volume to myVolume

delay 1

end repeat

stop

my setShuffleType("オフ")

set sound volume to 100

set visible of front window to false

上記の部分を、以下のロジックに変更してください。

現在の音量を記憶、復元するとともに、約10秒程度でフェードアウトするようにしてあります。

もっと良い方法があればご教示ねがいます。


-- フェードアウトして停止

set myVolume to sound volume

if myVolume > 0 then

repeat with myMuting from myVolume to 0 by ((myVolume div 10 + 1) * -1) as integer

-- if myMuting < 0 then exit repeat

set sound volume to myMuting

delay 1

end repeat

end if

stop

my setShuffleType("オフ")

set sound volume to myVolume

set visible of front window to false


-- if myMuting < 0 then exit repeat

この判定はいらないものでしょうか?

(myMutingがマイナスになることはない?)

なくてもうまく動いているようですが...

2013/05/03 05:31 Pajerow への返信

ステップが細かくなりますが、こんな感じでどうでしょうか?


set myVolume to sound volume


repeat while sound volume > 0

set sound volume to sound volume - 1

delay 0.1

end repeat


-- 省略


set sound volume to myVolume


こうするとマイナスになる心配はなし。ちょっとマシンへの負担は大きいですが、まあ大丈夫のような気がします。多分...。

2013/05/03 05:53 Hiro__S への返信

Hiro.S さんによる書き込み:


こうするとマイナスになる心配はなし。ちょっとマシンへの負担は大きいですが、まあ大丈夫のような気がします。多分...。

そうですね、すごくシンプルに出来るんですね。

私チョット考えすぎてました。(現行音量に関わらず一定時間でフェードアウトする...ことばかり考えてました)

これだと、絞り込む速度も一定だし、Maxからでも10秒でフェードアウトできるってことですね。


頂戴いたします。ありがとうございました。m( _ _ )m

2013/05/03 06:12 Pajerow への返信

マシンへの負担をちょっとでも少なく...というのであれば、こんな感じでも良いかもしれません。


set myVolume to sound volume


repeat while sound volume > 0

if sound volume < 10 then -- 値が10未満のときは0にして repeat を抜ける

set sound volume to 0

exit repeat

end if

set sound volume to sound volume - 10

delay 1

end repeat


set sound volume to myVolume


これだと CPU 占有率は Pajerow さんの同じぐらい。まあ、この辺は好みですかね。

2013/05/03 06:24 Hiro__S への返信

どうも、度々ありがとうございます。


ふむふむ...

やはり、このあたりは頭の柔らかさでしょうね。

私は年も年なのでもうガチガチでこんな発想が浮かびません。トホホ...(笑)


ちなみに、先の「delay 0.1」でもそんなにCPU負荷はかかっていなかったみたいですよ。

見た目のシンプルさとボリュームコントロールの動きのスムーズさで、「0.1」のほうにしておきます。

2013/05/03 07:03 Hiro__S への返信

重要なのは HAL さんが飛び上がらないようにすることです。(笑)


お気遣いありがとうございます (^^;;

ただいま細部調整中ですが、間をとってsound volume - 1, delay 0.5くらいが好みでしょうか。

夜静かだと眠れないので就寝時の音楽は必須、普段はAuroraを使っているのですがAppleScriptのいいところは細部を自由にカスタマイズできることですね。

もらうばかりで申し訳ないな・・・と思いつつ、ありがたく使用させていただきます m(_ _)m

2013/05/03 07:31 HAL への返信

HAL さんによる書き込み:


夜静かだと眠れないので就寝時の音楽は必須、

あっ、私と同じだっ!


私も毎晩、iTunesでインターネットラジオ(ジャズたまにクラシック)を聴きながら眠ります。

演奏が終わるとMacは勝手にスリープしてくれますが、その前に私の方が先にスリープしてしまっています。(笑)

今まではDashboard Widgets - Minutesを使ってましたが、これからはこのアプリでいけそうです。

このスレッドはシステム、またはAppleコミュニティチームによってロックされました。 問題解決の参考になる情報であれば、どの投稿にでも投票いただけます。またコミュニティで他の回答を検索することもできます。

AppleScriptによるiTunesのスリープタイマー

Apple サポートコミュニティへようこそ
Apple ユーザ同士でお使いの製品について助け合うフォーラムです。Apple Account を使ってご参加ください。