GroovyでWALKMAN用のプレイリストコンバーターを作成した

はじめに


自分の楽曲管理なのですが、以下で行っていました

  • Macを使っている&元iPhoneユーザーのためiTunes
  • 現状はAndroidユーザーでXperiaZ3Compactを活用
  • iTunesとの連携にはiSyncrアプリでAndroidアプリを連携

ここ最近、残念なことにXperiaZ3Compactのイヤホンジャックが故障してしまいました
海外版SIMFree版のため、国内で修理することもできません。

部品を調達してきて、別途修理するか悩みましたがリスクが高い
(ネットで調べる限りだいたい、何かしらミスしたという話が多かったです)

仕方ないのでWALKMANを調達するとして、以下の条件で機種が無いかをしらべました

  • WALKMANであること
  • iSyncrアプリの恩恵を預かれること
  • microSDは活用してもいい
  • ある程度の運用は許容するので、何かしらのコンバーター(アプリorスクリプト)は実装してもよい

条件に合致するものであれば・・・

  • 高額機種のWALKMAN
  • Android WALKMAN(3~4年前)で売り出していたFシリーズ
  • microSDが使用しているNW-A20シリーズ

というわけで、機能面でも最新であることたお値段も含めて一番条件に合致するWALKMAN(NW-A25)を新たに新調することとしました
(Fシリーズは別途問題があったようなので却下にしました)

何故コンバーターが必要になったか


iSyncrで作成したプレイリストは、Android向けに最適化されたもののようなので、そのままではNW-A25では活用することができませんでした
(店頭に通って挙動を確認しました)

症状としては以下のような形です

  • プレイリストは認識するが曲が存在しない扱いとなる
  • プレイリストを通さない、ミュージック一覧では楽曲の認識と再生可能

何故こういう状態が起こるかというと、WALKMANはOSがAndroidではない為、ファイルパスの扱いが異なるようです
なので、改善として以下のようにしてあげれば、プレイリストから楽曲再生ができるようになりました
(こちらも店頭で確認)

変更前

1
2
3
#EXTM3U
/hoge/fuga.mp3
/hoge/fugafuga.mp3

変更後

1
2
3
#EXTM3U
hoge/fuga.mp3
hoge/fugafuga.mp3

つまり、うまく認識させるためには相互変換できるようなものがあればよいということになります

何故Groovy?


以下の打算があったので、Groovyでやってみました

  • iSyncrはAndroidアプリなので、コンバーターもAndroidアプリでやれば運用が楽になるのではないか
  • AndroidはJavaで動作し、JVM言語であるGroovyが動作することも確認取れている
  • GroovyはJavaよりもライトに(Clojureを活用してですが)ファイル変更処理を実装することができる
  • ライトにできるので実装モック作成も割りと早めにできるのではないか

が、結局Android上での外部ストレージ周りのファイル操作の挙動が変わってる(DocumentFileを通したファイル作成、変更がスクリプトと相性悪い為)ので
後述のスクリプト処理が活用しづらいのでアプリ内での変換処理は諦めました

なのでコマンドラインツールとして実装&運用する方向としました
(運用できなくなるのが困るので、多少の労力(SDの抜き差し回数が増える)ぐらいなら労力を割いてもいい方向にしました)

iSyncr向けのプレイリストからWALKMAN向けのプレイリストに変換


少し手間ですが、以下のような形をとりました

  1. WALKMAN向けのプレイリストファイルを作成する(この時点では一時ファイルとして)
  2. iSyncr向けのプレイリストファイルを開く
  3. iSyncr向けのプレイリストファイルの内容をWALKMAN向けのプレイリストファイルに書き込む
  4. このとき行の先頭文字が/で始まっている場合、空文字に置換
  5. ファイル名を正しいもの変更する
  6. iSyncr向けプレイリストは、識別可能なファイル名に変更
  7. 一時ファイルのWALKMAN向けのプレイリストファイルは、正しいプレイリストファイル名になるよう変更

これらを実装したものが以下になります

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def isyncrToWalkman() {
  def sdCardDirPath = "/Volumes/WALKMAN/syncr"
  def files = new File(sdCardDirPath).listFiles()

  playList = files.findAll { it.getName() =~ /.*\.m3u$/ }
  if (playList.empty) {
    println "PlayList not found!"
    return
  }

  playList.each { file ->
    def playListName = file.canonicalPath

    def newPlayListName = "${file.canonicalPath}.new"
    def newPlayListFile = new File(newPlayListName)

    newPlayListFile.withWriter('UTF-8') { writer ->
      file.newReader().transformLine(writer) { line ->
        if (line.startsWith("/")){
          line.replaceFirst(/\//,"")
        } else {
          line
        }
      }
    }

    file.renameTo("${file.canonicalPath}.isyncr")
    newPlayListFile.renameTo("${playListName}")
  }
  println "Convert iSyncr To WALKMAN Complete!!"
}

WALKMAN向けのプレイリストからiSyncr向けのプレイリストに変換


こちらは以下を実装するだけでよいので簡単です

  1. WALKMAN向けのプレイリストを削除
  2. iSyncr用のプレイリストの名前を変更

上記を実装したものが以下になります

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def walkmanToIsyncr() {
  def sdCardDirPath = "/Volumes/WALKMAN/syncr"
  def files = new File(sdCardDirPath).listFiles()

  def isyncrPlayList = files.findAll { it.getName() =~ /.*\.m3u.isyncr$/ }
  if (isyncrPlayList.empty) {
    println "PlayList not found!"
    return
  }

  def walkmanPlayList = files.findAll { it.getName() =~ /.*\.m3u$/ }
  walkmanPlayList.each { file -> file.delete() }

  isyncrPlayList.each { file ->
    def name = file.canonicalPath.replaceFirst(/\.isyncr$/, "")
    file.renameTo(name)
  }
  println "Convert WALKMAN TO iSyncr Complete!!"
}

コマンドライン部分


こちらはGroovyのBuilderを実装しているCliBuilderを活用しました これは、コマンドラインから実行する際、どちらの変換を実行するかを選択させるようにしたかった為です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def cli = new CliBuilder(usage:"PlayList Converter")
cli.w("Playlist : iSyncr TO WALKMAN")
cli.i("Playlist : WALKMAN TO iSyncr")


def opt = cli.parse(args)
if (opt.w) {
  isyncrToWalkman()
  return
}

if (opt.i) {
  walkmanToIsyncr()
  return
}
cli.usage()

コードみれば非常に簡単なのですが・・・

オプションにwを付けて実行すると、前述のiSyncrのプレイリストからWALKMAN向けのプレイリストに変換する処理を実施
オプションにiを付けて実行すると、前述のWALKMAN向けのプレイリストからiSyncrのプレイリストに変換する処理を実施
何もつけなければ、実行方法を表示するようになっています

運用


前提

  1. PCが認識するSDカードのパスが/Volume/WALKMANになるように設定
  2. 前述のコードをまとめたGroovyファイルをplaylist_converter.groovyとする
  3. sdkmanを使って、Groovyをインストール

運用手順

  1. iSyncrでAndroid上のSDカードとプレイリストを同期
  2. 同期完了後、AndroidのSDカードを取外しPCにSDを接続
  3. WALKMAN向けプレイリストになるように以下のコマンドで変換
  4. $ groovy playlist_converter.groovy -w
  5. WALKMANにSDカードをさして、ミュージックを楽しむ
  6. WALKMANからSDを取り外し、PCにSDを接続
  7. WALKMAN向けプレイリストになるように以下のコマンドで変換
  8. $ groovy playlist_converter.groovy -i
  9. AndroidにSDをさして、更にプレイリストの同期を実施

少し煩雑な感じもしますが、手作業でプレイリストを修正するよりも多少マシかなぁと思ってます
(実際に自分は運用できてます)