iTunesの再生回数をAppleScript + Ruby + gnuplotで表示してみる
AppleScriptとRubyを使って, iTunesの再生回数を取得しグラフ化してみた.
やろうと思ったきっかけは, 自分がaikoの曲をどれだけ聞いているか知りたかったから. よって, このあと出て来る例はすべてaikoを検索している.
AppleScriptで曲と再生回数を取得
AppleScriptなにもわからない. 雰囲気でAppleScriptを書いている.
tell application "iTunes" # 変数はsetで宣言 set lst to {} # repeat with 変数 in オブジェクト(foreach) repeat with t in (file tracks whose artist contains "aiko") copy ((name of t as string) & " " & played count of t as string) to the end of lst end repeat return lst end tell
分からなさすぎて検索しまくった. これを実行すると, 以下のような出力を得られる.
それっぽい結果が出ている. このスクリプトを次に作るrbファイルと同じ階層においておく.
Rubyでデータを加工
AppleScript辛い…しか言えない体になっていたので, ここでRubyにバトンタッチ.
コマンドを実行
とりあえず先ほどのAppleScriptを実行しないと話にならないので, ``を使って実行し結果を得る.
# scriptの実行 elements = `osascript itunes.scpt`.chomp.split(",").map{ |item| item.strip }
曲名ごとにカウントする
aikoはアルバムを14枚(ベストアルバム込み), シングルも36枚ある. もちろん曲の被りがあるので, それは同じものとしてカウントしたい.
そこでHashを使ってカウントする.
# 曲名ごとにカウント hash = Hash.new(0) elements.each do |elem| next if (elem.index("instrumental")) len = elem.rindex(" ") name = elem[0..len] time = elem[len..elem.length] hash[name] += time.to_i; end
今回instrumentalはカウントしないことにしたので, 以下のように弾く処理を書いている.
next if (elem.index("instrumental"))
また, “Do you think about me?"や"be master of life"のように空白が1つ以上出現する文字列もあるため, rindexで一番最後の空白を基準に曲名と回数を分割することにした.
len = elem.rindex(" ") name = elem[0..len] time = elem[len..elem.length]
そして, その名前をkeyとして回数を加算する.
hash[name] += time.to_i;
再生回数でソートする
先程カウントしたハッシュをソートする必要がある. HashのSortは[key, value] を要素とする配列の配列に変換して,それをソー トした配列を返す。
# 再生数でソート songs = hash.sort do |a, b| b[1] <=> a[1] end
gnuplotで描画
画面に文字列として出力するだけではわかりにくいので, gnuplotを使って表示することにした. ここではgnuplotのインストールについては省略する.
ラベルと回数を取得
ラベルと回数をそれぞれ別の配列として持たないといけないので, それ用の配列を作る.
# Gnuplot用にラベルと回数を別々に配列へ格納 labels = [] times = [] songs.each_with_index do |item, idx| labels << "\"#{item[0]}\"" times << item[1] break if idx > 50 end
あまりに多いと格納できないので, 上位50曲程度で打ち切っている.
描画する
以下のgemを使ってRubyから描画する.
gnuplot | RubyGems.org | your community gem host
# 描画 Gnuplot.open do |gp| Gnuplot::Plot.new(gp) do |plot| plot.terminal "aqua font 'ヒラギノ角ゴ Pro W3,10'" plot.title "視聴回数" plot.yrange "[0:300]" plot.ylabel "再生回数" plot.bmargin "8" plot.style "fill solid border -1" plot.xtics "rotate by -90" plot.unset "key" plot.data << Gnuplot::DataSet.new([labels, times]) do |ds| ds.using = "2:xtic(1)" ds.with = 'boxes lc rgb "orange"' ds.title = "再生回数" end end end
本当は, set terminal png enhanced font ‘GothicBBB-Medium-EUC-H, 10'のような感じにしたかったのだけどできなかったので, terminalに出してスクリーンショットに妥協した.
最終的なソースコード以下のようになる.
require "gnuplot" # scriptの実行 elements = `osascript itunes.scpt`.chomp.split(",").map{ |item| item.strip } # 曲名ごとにカウント hash = Hash.new(0) elements.each do |elem| next if (elem.index("instrumental")) len = elem.rindex(" ") name = elem[0..len] time = elem[len..elem.length] hash[name] += time.to_i; end # 再生数でソート songs = hash.sort do |a, b| b[1] <=> a[1] end # Gnuplot用にラベルと回数を別々に配列へ格納 labels = [] times = [] songs.each_with_index do |item, idx| labels << "\"#{item[0]}\"" times << item[1] break if idx > 50 end # 描画 Gnuplot.open do |gp| Gnuplot::Plot.new(gp) do |plot| plot.terminal "aqua font 'ヒラギノ角ゴ Pro W3,10'" plot.title "視聴回数" plot.yrange "[0:300]" plot.ylabel "再生回数" plot.bmargin "8" plot.style "fill solid border -1" plot.xtics "rotate by -90" plot.unset "key" plot.data << Gnuplot::DataSet.new([labels, times]) do |ds| ds.using = "2:xtic(1)" ds.with = 'boxes lc rgb "orange"' ds.title = "再生回数" end end end
ここまで書いたらGemfileを書く.
$ bundle init
bundle initを実行してGemfileを作る. Gemfileは
# frozen_string_literal: true source "https://rubygems.org" gem 'gnuplot'
とする. ここまできたらinstallをする.
$ bundle install --path vendor/bundle
これでインストールされるので, いよいよ実行.
$ bundle exec ruby ***.rb
実行結果(曲単位)
実行結果は次のようになった.
ランキングを見てみると, 上位5曲は以下のようになっていた.
- 恋のスーパーボール
- シアワセ
- 夢見る隙間
- もっと
- 花火
意外とbe master of lifeとか聞いていないんだなという気持ちになっている. 1位が恋のスーパーボールなのは, いっとき狂ったように聞いていた時があったので適当. 夢見る隙間が3位にいるのは意外すぎる. ちなみに6位は何時何分なので, 発売時期を考慮するとMay Dreamは割りと聞いているということがわかった.
これだけでは面白くないので, 先ほどのスクリプトを改造してアルバムごとの回数を見てみる.
アルバムごとに集計
AppleScriptを以下のように変更し(name→album)
tell application "iTunes" set lst to {} repeat with t in (file tracks whose artist contains "aiko") copy ((album of t as string) & " " & played count of t as string) to the end of lst end repeat return lst end tell
require "gnuplot" # scriptの実行 elements = `osascript itunes.scpt`.chomp.split(",").map{ |item| item.strip } # 曲名ごとにカウント hash = Hash.new(0) albums = ["小さな丸い好日", "桜の木の下", "夏服", "秋 そばにいるよ", "暁のラブレター", "夢の中のまっすぐな道", "彼女", "秘密", "BABY", "まとめ I", "まとめ II", "時のシルエット", "泡のような愛だった", "May Dream"] elements.each do |elem| next if (elem.index("instrumental")) len = elem.rindex(" ") name = elem[0..len-1] time = elem[len+1..elem.length] if albums.include?(name) then # アルバムであるかどうか hash[name] += time.to_i; end end # 再生数でソート songs = hash.sort do |a, b| b[1] <=> a[1] end # グラフの上限を設定 # 1400なら2000にするなど. tmp = songs[0][1].to_s max_val = tmp[0].to_i + 1; max_val *= (10 ** (tmp.size - 1)) # Gnuplot用にラベルと回数を別々に配列へ格納 labels = [] times = [] songs.each_with_index do |item, idx| labels << "\"#{item[0]}\"" times << item[1] break if idx > 50 end # 描画 Gnuplot.open do |gp| Gnuplot::Plot.new(gp) do |plot| plot.terminal "aqua font 'ヒラギノ角ゴ Pro W3,10'" plot.title "視聴回数" plot.yrange "[0:#{max_val}]" plot.ylabel "再生回数" plot.bmargin "8" plot.style "fill solid border -1" plot.xtics "rotate by -90" plot.unset "key" plot.data << Gnuplot::DataSet.new([labels, times]) do |ds| ds.using = "2:xtic(1)" ds.with = 'boxes lc rgb "orange"' ds.title = "再生回数" end end end
まとめⅠとⅡが微妙に違って集計されていなかった. つらいね.
実行結果(アルバム単位)
まとめ2枚と時のシルエット, May Dreamがずば抜けている. 確かによく聞いてるし, なによりまとめから入った人間なのでそれはそう.
暁のラブレター全然聞いていないことに気づく. iTunesのカウントは, おそらく最後まで聞いた曲を1回とカウントしてるので, 終わったと思って飛ばしてしまうと1回とカウントされてないのではないだろうか. 彼の落書きとか結構聞いてると思うのだが…
先日書いたブログでも, 割と力を入れて書いたところが上位に来ているのでそれだけ聞いていたんだと実感した. グラフィカルに見られるのはいいことだ.
あまりにも汎用性の低いコードすぎるので, albumでまとめるのか全部含めるのかなどなど色々更新していきたい.
あとaikoはいいぞ.