日々精進

aikoと旅行とプログラミング

iTunesの再生回数をAppleScript + Ruby + gnuplotで表示してみる

AppleScriptRubyを使って, iTunesの再生回数を取得しグラフ化してみた.
 やろうと思ったきっかけは, 自分がaikoの曲をどれだけ聞いているか知りたかったから. よって, このあと出て来る例はすべてaikoを検索している.

developer.apple.com

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

分からなさすぎて検索しまくった. これを実行すると, 以下のような出力を得られる.

f:id:bath_poo:20170703140028p:plain

それっぽい結果が出ている. このスクリプトを次に作る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

実行結果(曲単位)

実行結果は次のようになった.

f:id:bath_poo:20170703143619p:plain

ランキングを見てみると, 上位5曲は以下のようになっていた.

  1. 恋のスーパーボール
  2. シアワセ
  3. 夢見る隙間
  4. もっと
  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

集計部分のソースコード(Ruby)を多少変更した.

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

まとめⅠとⅡが微妙に違って集計されていなかった. つらいね.

実行結果(アルバム単位)

f:id:bath_poo:20170703151533p:plain  まとめ2枚と時のシルエット, May Dreamがずば抜けている. 確かによく聞いてるし, なによりまとめから入った人間なのでそれはそう.
 暁のラブレター全然聞いていないことに気づく. iTunesのカウントは, おそらく最後まで聞いた曲を1回とカウントしてるので, 終わったと思って飛ばしてしまうと1回とカウントされてないのではないだろうか. 彼の落書きとか結構聞いてると思うのだが…  先日書いたブログでも, 割と力を入れて書いたところが上位に来ているのでそれだけ聞いていたんだと実感した. グラフィカルに見られるのはいいことだ.

muttan1203.hatenablog.com

 あまりにも汎用性の低いコードすぎるので, albumでまとめるのか全部含めるのかなどなど色々更新していきたい.
 あとaikoはいいぞ.