【Python】iTunesで聞いてる曲の情報を取得する

## 概要

MacのiTunesで今聞いている曲の情報を取得できないかなぁとふと思い調べたら
[AppleScript](https://ja.wikipedia.org/wiki/AppleScript)でそれが実現するらしいが,
今まで触ったことが一ミリもなく諦めようとしたところ
Pythonの[appscript](https://pypi.org/project/appscript/1.0.1/)というモジュールでそれが可能ということみたいなので試してた


## 環境

Python 3.6.0

## 環境構築

まずはpythonを動かすための環境をvenvで構築をする(別にvenvを使わなくてもいい)

```bash
$ python3 -m venv venv
```

```bash
$ source venv/bin/activate
```

```bash
(venv)$ pip3 install appscript
```


このモジュールの使い方自体の記事もなかったですが[githubのドキュメント](https://github.com/hhas/appscript/tree/master/py-appscript/trunk/appscript_3x/doc)をなんとか見つけたのでまずはベースは参考にしました。
アプリケーションごとのドキュメントが全くないので

あとは[ここ](http://appscript.sourceforge.net/)に書いてある通りAppleScriptをappscriptに落とし込めばいけそうな気がしたので,
だいたいのメソッド等は力技でこんな感じだろうで試して, Pythonの記法に落とし込みました。
※正直かなり疲れた

## 実装
### 前提スクリプト

モジュールのimport等, 大前提として必要なものは切り出しておきます。

```python:getiTunesInfo.py
from aeosa import appscript
it = appscript.app('iTunes')
```

まずはこれを冒頭に入れちゃって下さい。
以下からのpythonスクリプトはこれがある前提で書いていきます。

### プログラムからのちょっとした操作
### 1. iTunesが起動しているかどうかチェック

```python:getiTunesInfo.py
it.isrunning()
```

#### 実行結果
これの戻り値としては以下になります。

- 起動していたら
    - True
- 起動していなかったら
    - False

### 2. iTunesを起動する

```python:getiTunesInfo.py
it.run()
```

#### 実行結果

iTunesが起動します
※但し, 特に前面に出て来たり, アプリの画面に勝手に移動したりはしない

### iTunesを(起動して)最前面にする。

```python:getiTunesInfo.py
it.activate()
```

#### 実行結果
iTunesが起動していなかったら起動して, 最前面になります(iTunesの画面にスライドする)


#### 実行結果
これでiTunesが起動して尚且つ, 最前面にiTunesが開きます。

### 3. 曲を再生する

```python:getiTunesInfo.py
it.play()
```

#### 実行結果

曲が再生されます(音量注意)

### 4. 曲を止める
#### 4-1. 停止

```python:getiTunesInfo.py
it.stop()
```

#### 実行結果

曲が止まります(再生しても途中からは流れない)

#### 4-2. 一時停止

```python:getiTunesInfo.py
it.pause()
```

#### 実行結果

曲が一時停止します(もう一度再生をすると止めたところから再生される)


#### 4-3. 再生・一時停止メソッド

```python:getiTunesInfo.py
it.playpause()
```

#### 実行結果

- 再生していたら一時停止する
- 一時停止していたら再生される(音量注意)
- 停止状態でも再生される(音量注意)


### 再生中の曲情報を取得する
#### テンプレート

曲詳細の取得に関しては以下がテンプレートでプロパティ名を適宜帰る形になる

```python:曲詳細テンプレート
it.current_track.{プロパティ名}.get()
```

<font color=red>**例:**</font> 以下の曲を例として, 画像の曲詳細の情報を取得する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133737/1a0eaed7-a34c-95f0-5c96-1d31e61dc1d3.png)

|取得したい情報|プロパティ名|備考|
|:--:|:--:|:--:|
|曲|**name**|-|
|アーティスト|**artist**|-|
|アルバム|**album**|-|
|アルバムアーティスト|**album_artist**|-|
|作曲者|**composer**|-|
|すべての表示に作曲者を表示|<font color=red>**すみませんこれだけわからなかったです(泣)**🙏💦</font>|-|
|グループ|**grouping**|-|
|ジャンル|**genre**|-|
|年|**year**|多分発売された年?<br>もしかしたらアップルミュージックとかで配信になった年かも?ですが特に深掘りしてないです|
|トラック|**track_number**|そのアルバムで何曲目か|
|ディスク番号|**disk_number**|複数枚組のアルバムとかの場合ディスクによって変わってくるのだと思う|
|コンピレーション|**compilation**|コンピレーションアルバムかどうか(True/False)|
|評価|**rating**|自身で入力したもの<br>☆=20|
|❤️|**loved** または **disliked**|自身で入力したもの<br>lovedの場合、`チェックなし=False, 好き=True, 好きじゃない=False` <br> dislikedの場合, `チェックなし=False, 好き=False, 好きじゃない=True`|
|bpm|**bpm**|-|
|再生回数|**played_count**|自身が再生した回数|
|コメント|**comment**|自身で入力したもの|


#### 実行結果

これらを標準出力する

```python:getiTunesInfo.py
print(it.current_track.name.get())
print(it.current_track.artist.get())
print(it.current_track.album.get())
print(it.current_track.album_artist.get())
print(it.current_track.composer.get())
print(it.current_track.grouping.get())
print(it.current_track.genre.get())
print(it.current_track.year.get())
print(it.current_track.track_number.get())
print(it.current_track.discc_number.get())
print(it.current_track.compilation.get())
print(it.current_track.rating.get())
print(it.current_track.loved.get())
print(it.current_track.disliked.get())
print(it.current_track.bpm.get())
print(it.current_track.played_count.get())
print(it.current_track.comment.get())
```

#### 実行結果

```bash:実行結果
# 曲
Tokyo Invader
# アーティスト
踊Foot Works
# アルバム
ODD FOOT WORKS
# アルバムアーティスト
踊Foot Works
# 作曲者
Odd Foot Works
# グループ
踊Foot Works
# ジャンル
ヒップホップ／ラップ
# 年
2017
# トラック
2
# ディスク番号
1
# コンピレーション
False
# 評価
60
# ❤️
## loved
False
## disliked
False
# bpm
1
# 再生回数
170
# コメント
お気に入り
```

### アートワーク取得

参考にしたのはAppleScriptのiTunesのことが書かれた[記事](https://256stuff.com/gray/docs/misc/itunes_applescript_commands/)です
これが一番苦労した...


先ほどの記事の対象の部分が↓こちら

```applescript:Get_the_artwork_for_the_current_track
tell artwork 1 of current track
   -- get the bytes of the artwork
   set srcBytes to raw data
   -- set the file extension based on the type
   if format is <> then
      set ext to "png"
   else
      set ext to "jpg"
   end if
end tell

-- get the destination filename as cover.ext in a temporary folder
set fileName to (path to temporary items folder from user domain as string) & "cover." & ext)
-- start the output file
set outFile to open for access file fileName with write permission
-- truncate it
set eof outFile to 0
-- write the bytes of the image to the file
write srcBytes to outFile
close access outFile
```

端的に解説すると

1. <font color=red>**画像のバイナリーデータを取得**</font>
2. <font color=red>**落としてきてそれをファイルに出力する**</font>

っていうやり方

では早速pythonに直していきます。

#### バイナリー取得と拡張子判別

```applescript:バイナリー取得と拡張子判別(AppleScript)
tell artwork 1 of current track
   -- get the bytes of the artwork
   set srcBytes to raw data
   -- set the file extension based on the type
   if format is <> then
      set ext to "png"
   else
      set ext to "jpg"
   end if
end tell
```

↑がAppleScriptの記法
↓こちらがpythonの記法で尚且つappscriptを使用したもの

```python:バイナリー取得と拡張子判別(python)
# バイナリーデータ取得
artworkRaw = it.current_track.artworks[1].raw_data.get()

# 拡張子判別
## 一旦jpgかpngしか見られなかったのでキメでやっています。
if it.current_track.artworks[1].format.get().name == 'JPEG_picture':
  ext = 'jpg'
else:
  ext = 'png'
```

<font color=red>※とりあえず拡張子の判別は何曲か再生してjpgはpngしか見られなかったので深追いせずにキメでやっちゃってます。すんません🙏</font>

#### ファイルに書き出し

```applescript:ファイルに書き出し(AppleScript)
-- get the destination filename as cover.ext in a temporary folder
set fileName to (path to temporary items folder from user domain as string) & "cover." & ext)
-- start the output file
set outFile to open for access file fileName with write permission
-- truncate it
set eof outFile to 0
-- write the bytes of the image to the file
write srcBytes to outFile
close access outFile
```

↑がAppleScriptの記法
↓こちらがpythonの記法で尚且つappscriptを使用したもの

```python:ファイルに書き出し(python)
# ファイル名は '曲名+拡張子' にする
## ディレクトリは相対パスでわかりやすくデスクトップに出力されるようにしてます
fileName = f'../../{it.current_track.name.get()}.{ext}'

# バイナリ書き込みモードでファイルを開く
with open(fileName, mode='wb') as f:
  # もし既にあった場合の為に一度中身をクリアする
  pass
  # バイナリーデータを書き込む
  f.write(artworkRaw)
```

以上!

---

繋げるとこうなります。

```python:getiTunesInfo.py
artworkRaw = it.current_track.artworks[1].raw_data.get()
if it.current_track.artworks[1].format.get().name == 'JPEG_picture':
  ext = 'jpg'
else:
  ext = 'png'

fileName = f'../../{it.current_track.name.get()}.{ext}'
with open(fileName, mode='wb') as f:
  pass
  f.write(artworkRaw)
```

#### 実行結果
↓こんな感じでデスクトップにちゃんと画像ファイルが出力されました🎉
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133737/05fd8cf3-b323-b98c-f15a-fdf3e966ee40.png)

実際のデータもこんな感じ↓🎉

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133737/cfbc77fc-d923-55c0-2646-733ff748eed4.png)


## 最後に

今回はお遊び程度にやったのでとりあえず実行できる環境を作ってやりました。
何かに利用してみたい..!!!
これを使ってまたなんか記事書こうと思います!!!

## 参考資料
とにかくappscriptとかAppleScriptの記事が少ないし,
日本語なんてほぼないし,
ましてやiTunesに特化した記事なんてほとんどなかったのでめちゃくちゃ大変でしたので参考にした記事には本当に感謝です🙏

- [appscript](https://pypi.org/project/appscript/1.0.1/)
- [hhas/appscript](https://github.com/hhas/appscript/tree/master/py-appscript/trunk/appscript_3x/doc)
- [What is appscript?](http://appscript.sourceforge.net/)
- [Applescript iTunes Commands](https://256stuff.com/gray/docs/misc/itunes_applescript_commands/)
- [iTunes for Mac まとめ](https://w.atwiki.jp/wiki3_itunes/pages/24.html)


### おわり