iTunesで再生中の曲を自前のサーバに送信するMacアプリ

  • 9
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

なぜ作ったか

社内にBluetoothで接続できるスピーカーが設置されたので、曲をかけている人が何を再生しているのか共有できたら面白いと思って作った。
最終的にHubotにPOSTしてSlackの特定チャンネルに流すことを想定している。

おことわり

普段はJavascriptを書いている人間なので、Macアプリの綺麗な実装法などは理解していない。もちろんSwiftを触ったのもはじめてである。

アプリ実装

TrackInfoController

このクラスはiTunesの通知を受け取る。そのアーティスト名と曲名をViewに送り、またサーバに送信することを担当する。

TrackInfoController.swift
import Foundation

protocol TrackInfoDelegate: class {
    func updateTrackInfo(info: Dictionary<String,String>)
}

class TrackInfoController: NSObject {
    var delegate: TrackInfoDelegate! = nil

    override init() {
    }

    func onPlay (notification: NSNotification?) {
        let artist = notification?.userInfo?["Artist"] as? String ?? "none"
        let name = notification?.userInfo?["Name"] as? String ?? "none"
        let info = ["artist": artist, "name": name]
        delegate.updateTrackInfo(info)

        let str = "artist=" + artist + "&name=" + name
        let strData = str.dataUsingEncoding(NSUTF8StringEncoding)
        var url = NSURL(string: "http://localhost:3000/") //サーバは別に用意する
        var request = NSMutableURLRequest(URL: url!)

        request.HTTPMethod = "POST"
        request.HTTPBody = strData

        NSURLConnection.sendAsynchronousRequest(request, queue: nil, completionHandler: self.onComplete)
    }

    func onComplete (response: NSURLResponse!, data: NSData!, error: NSError!) {
        println(response)
        println(data)
    }
}

Delegateも一緒に定義しておく。
通知がきたらアーティスト名と曲名の辞書を作り、Delegateに渡してあとの処理はViewに任せる。

ViewController

ViewController.swift
import Cocoa

class ViewController: NSViewController, TrackInfoDelegate {

    var trackInfoController = TrackInfoController()
    @IBOutlet weak var artistLabel: NSTextField!
    @IBOutlet weak var nameLabel: NSTextField!
    override func viewDidLoad() {
        super.viewDidLoad()
        trackInfoController.delegate = self
        NSDistributedNotificationCenter.defaultCenter().addObserver(trackInfoController, selector: "onPlay:", name:"com.apple.iTunes.playerInfo", object: nil)
        // iTunesで曲が変わった通知は "com.apple.iTunes.playerInfo"
    }

    override var representedObject: AnyObject? {
        didSet {
        }
    }

    func updateTrackInfo (info:Dictionary<String,String>) {
        artistLabel.stringValue = info["artist"]!
        nameLabel.stringValue = info["name"]!
    }
}

GUIはStoryboardで作ったが、そちらは割愛する。
Viewのロードが完了したときにtrackInfoControllerのDelegateにselfを設定してやる。
また、"com.apple.iTunes.playerInfo"の通知を受け取った時にtrackInfoControlleronPlayメソッドを呼び出すようにする。
DelegateからupdateTrackInfoが呼ばれるとViewのラベルを書き換える。

AppDelegateには特に何も変更を加えていない。

サーバ実装

Node.jsのExpressでサクッと作ってしまう

Node.jsはインストール済みとする。

$ npm install -g express

でExpressが入るのでホームディレクトリなどで

$ express trackinfo

としてExpressアプリを作成する。


trackinfo/routes/index.js
var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res) {
    res.render('index', { title: 'Express' });
});

module.exports = router;

諸々出来上がった中のtrackinfo/routes/index.js は上の感じになっている(Version4.2.0)ので、router.getの下あたりに以下を追加する。

index.js
router.post('/', function(req, res) {
    console.log(req.body);
    res.status(200).send('ok');
});

これでPOSTリクエストを処理できる。


$ npm start

最後にnpm startすれば初期設定ではポート3000番でサーバが立ち上がる。

以上でアプリ・サーバの実装が完了。

結果

t11MLzwq0c.gif

雑感

  • まだObj-CとSwiftの過渡期で、なかなかSwiftで気になったことは調べにくい。特にiOSの情報が多くMacアプリの情報はなかなか見つからない。
  • Delegateを用いたうまい実装法がよくわからず、この形に落ち着いた。AppDelegateを活用したほうがよかっただろうか。
  • !とか?とか??とかなんとなくしかわかってない。

疑問

通信完了時のonCompleteが呼ばれている気配がない。どこか間違っているのだろうが、よくわからない。