Edited at

iTunesで再生した楽曲をIFTTT経由でDiscordに投げてみた話

恥ずかしながら、最近までWeb系のプロダクト(最近主流のキラキラしてるやつ)にはほとほと疎く

この間ようやくIFTTTなるものを知った。

で、いろいろ弄っているうちになんか面白そう!と思って表題のアプリを作ってみようと思い立ったのが昨日。

大変雑な感じの組み方をしてしまったのだけれど、せっかくなので記事にしてしまおうと思ってみたり。


用語定義

一応わかりやすさのために用語を簡単にまとめておきたい。

用語
ザックリ説明

IFTTT
web経由でいろんなサービス間の橋渡しをしてくれるサービス。
webhookからwebhookをつなげてくれる子。

iTunes
appleが提供してる音楽再生アプリ。
いや、音楽再生のみじゃないんだけど、windowsだと基本こんな認識

Discord
チャットアプリ。ゲーム配信に特化してるイメージがある。
会議通話も配信もいける。アプリの説明文大好き。


基本的なイメージ

2018y11m14d_022626602.jpg

うーん、シンプル。

ちなみにwebhookを利用してるので、IFTTT経由しなくてもDiscordにメッセージ投げるのは出来ちゃいます。

IFTTT弄らない分手間もかからないかも。

ただ、IFTTTを経由することで、IFTTTから出力先に指定できる400超のサービスに拡張できるってのは間違いなく魅力かと。

さて・・・しっかり書くのは明日にして、とりあえず成果だけ挙げてお休み。

気がついたら2時回ってたので・・・

ちゃんと計測してないけど、4時間くらいで出来る簡単なアプリ。


成果

2018y11m14d_014343048.jpg

{再生したトラックのタイトル} - {アルバム名}/{アーティスト名}

の書式。この書式はWindowsアプリで整える。


実装


DiscordのWebhook用アドレスを取得する

まずはDiscord。

Discordのサーバーアイコンを右クリック。

コンテキストメニューから[サーバー設定]-[webhooks]を選択。

サーバーの設定画面に遷移するので、ここで[webhookを作成]ボタンを押下。

2018y11m14d_145553053.jpg

webhook契機でメッセージを送る際のデフォルト名と、投稿先のチャンネル、アイコンなどを設定し、

WEBHOOK URLをコピーする。

Discord_Webhook.jpg


!注意!

Webhook URLを公開すると、匿名の誰かが好き勝手メッセージ投げられるようになる。控えよう。


後はコピーしたURLにjson形式のデータをPOSTしてやればDiscordへの投稿はOK。

jsonのKeyは色々あるけど、とりあえず使うならcontent(本文)さえ入れておけばOK。

お手軽なキーと内容の表はこんな感じ

Key
説明

content
Discordの本文。メッセージ部分とも。

username
投稿者の名前。指定しない場合、DiscordのWebhoook設定で指定したデフォルト名が使われる

avatar_url
投稿者のアイコンを指すURL。指定しない場合、DiscordのWebhook設定で指定したデフォルトアイコンが使われる。

なお、Content-Typeはapplication/jsonをちゃんと指定しないとおむずがる模様。

参考にさせていただいたもの:DiscordにWebhookでいろいろ投稿する

https://qiita.com/Eai/items/1165d08dce9f183eac74




IFTTTでAppletを作る。

今回一番使いたかったところ。

先にも述べたとおり、IFTTTを介さなくてもjson形式のbodyを書き込んだHTTPリクエストを送ればDiscordに投稿は出来る。

ただし、別のサービスに送りたくなったときにソースを直す必要があるわけですよ。

あ、ハードコーディングする場合ね?

もちろんそこはいくらでもやりようがあるんだけど。

とにかく、IFTTTを使いたかったので、いいんですぅ!(ゴリ押し)

で、IFTTT。

もうこれ説明割愛してもいいんじゃないの?ってくらいサンプルにあふれてるんだけど、

一応webhook入力の例は見当たらなかった気がするから書いてみよう。

まずはIFTTTにアクセス。

※アカウントの登録はしてある前提。

New Appletを選択し、

2018y11m15d_010757519.jpg

をクリック。

[Choose a service]画面の検索窓に"webhook"と打ち込み、出てきた[webhook]をクリック。

2018y11m15d_011801112.jpg

Choose trigger画面に遷移するので、[Receive a web request]を選択。

2018y11m15d_012148157.jpg

すると、triggerに必要な情報を埋めてね!画面に遷移するので、

必要なEvent Nameテキストボックスに任意の文字列を設定する。

ちなみに、気になったので試してみたら、日本語も指定できました。

エンコードによる差異とか出そうで怖いからascii文字のみの方がよさそうだけども。

2018y11m15d_012720295.jpg

ここまででtriggerの設定はOK。

次に

2018y11m15d_014036002.jpg

この画面の

2018y11m15d_014055893.jpg

をクリックしてDiscordに投げるwebhookの設定をしていきます。

[Choose action service]画面に遷移したら、triggerの設定時と同じように、検索窓に"webhook"と入力し、

出てきた[webhook]をクリック。

[Choose action]画面で[Make a web request]をクリックし、次の画面へ

2018y11m15d_014914686.jpg

[Make a web request]をクリックし、遷移した[Complete action fields]画面で送信するHTTPリクエストの内容を設定。

設定項目と対応は以下の通り

2018y11m15d_020506865.jpg

項目
説明

URL
送信先のURLをここに指定する。
Discordにメッセージを投げる場合、
ここにDiscordのwebhook先URLを貼り付ける。

Method
HTTPリクエストにおけるメソッドを指定する。
データを投げる場合、基本的にはPOSTでいいはず。

Content Type
HTTPリクエストのBody部分の内容を指定する。
今回はjson形式なので、"application/json"を指定

Body
HTTPリクエストのBody部。
今回はjson形式でデータを送る。

今回のBodyはシンプルにこんな感じ


{

"username" : "IFTTT music",

"avatar_url": "https://img.icons8.com/color/1600/ifttt.png",

"content" : " {{Value1}}"

}


{{Value1}}の部分にはtrigger側のwebhookで投げられてきたHTTPリクエストのvalue1キーの値がそのまま入るイメージ。

入力が終わったら画面下部の[Create action]をクリック。

[Review and finish]画面で[Finish]ボタンを押下してappletの作成を完了しましょう。

これでIFTTTの設定は終わり。

ちなみにIFTTTのwebhookによるトリガは、[webhook service]の[Documentation]ページから動作を確認することが出来ます。

Service一覧から[webhooks]を選択し、Webhooks画面へ。

または、ここから移動。

画面右上にあるDocumentationボタンを押下してDocumentation画面へ移動。

Documentation画面の[To trigger an Event]以下の値を書き換えて[Test It]ボタンを押下することで

作成したHTTPリクエストを送ることが出来ます。

ちなみに、この画面にはアカウント固有のキーが表示されてるので

例によって公開しないほうがいいです。

悪戯し放題!




iTunesで再生した楽曲を取得し、HTTPリクエストを投げるアプリを作る

さて、ここまできてようやく大詰め。

iTunesで再生した楽曲を取得するお時間です。

参考にさせていただいたサイトさんの内容とかなり被るので要点を


  1. iTunesLib(COMオブジェクト)を参照に加える。

  2. using iTunesLib;をコードの先頭に足しておく。

  3. iTunesAppインスタンスを取得し、イベントハンドラを登録する。

    ※イベントは以下の表.iTunesAppイベント一覧を参照のこと

  4. 引数として渡されるObjectをIITTrack型にキャストし、必要なデータを取得

  5. データを整形してHTTPリクエストにまとめる

  6. IFTTTにポイっ!って

  7. ※使わなくなったらiTunesAppオブジェクトを解放する。登録したイベントハンドラの削除も忘れずに

表. iTunesAppイベント一覧

イベント名

引数
概要

OnDatabaseChangedEvent
void
object deletedObjectIDs,
object changedObjectIDs
iTunesデータベースに変更が起きたときに発生。

OnPlayerPlayEvent
void
object iTrack
iTunesでトラックを再生するときに発生。
引数としてトラック情報が渡される。
コード内で利用する場合、IITTrack型にキャストして利用する。
IITTrack型については表.IITTrack型プロパティを参照のこと

OnPlayerStopEvent
void
object iTrack
iTunesでトラックの再生を停止するときに発生。
引数としてトラック情報が渡される。
コード内で利用する場合、IITTrack型にキャストして利用する。
IITTrack型については表.IITTrack型プロパティを参照のこと

OnPlayerPlayingTrackChangedEvent
void
object iTrack
iTunesで再生中のトラック情報が変更されるときに発生。
次楽曲の再生や、前楽曲の再生を行った場合にも発生する。
引数としてトラック情報が渡される。
コード内で利用する場合、IITTrack型にキャストして利用する。
IITTrack型については表.IITTrack型プロパティを参照のこと

OnCOMCallsDisabledEvent
void
[ComAliasName("iTunesLib.ITCOMDisabledReason")] ITCOMDisabledReason reason
COM(iTuneslib)へのアクセスが繰延(differ)される場合に発生。
説明文読む限りモーダルダイアログが出たときとかじゃないかな?

OnCOMCallsEnabledEvent
void
-
COM(iTuneslib)への繰延(differ)状態が解除された場合に発生?
なんかモーダルダイアログが出てると遅延が起きるらしい。
その状態が解除されると発生するっぽい

OnQuittingEvent
void
-
iTunesが終了されるときに発生。

OnAboutToPromptUserToQuitEvent
void
-
iTunesが”ユーザーに終了するか問い合わせるプロンプトを表示する”時に発生
iTunesに絡めたアプリ立ち上げた状態でiTunes終了しようとすると"連携アプリあるけど本当にiTunes閉じる?"って聞いてくるアレ

OnSoundVolumeChangedEvent
void
int newVolume
音量変更時に発生。

表.IITTrack型プロパティ

プロパティ

説明

Album
string
アルバム名

Artist
string
読んで字のごとく、アーティスト名。

Artwork
IITArtworkCollection
アートワークを格納してるとかなんとか。そうなると画像データなんだろうなぁ。
今回はノータッチ

Bitrate
int
ビットレート。これ説明要らないのでは?

BPM
int
BPM。Beats Per Minuitの略なんだとか。テンポの単位なんですって!

Comment
string
コメント。基本的にiTunesで楽曲のコンテキストメニューからプロパティ開かない限り見る機会ないやつ。

Compilation
bool
Compilation albumからのトラックかどうかを返却。
・・・これ要るん・・・?

Composer
string
コンポーザー。作曲家のことだそうな。

DateAdded
Datetime
プレイリストに追加された日。

DiskCount
int
元アルバムの総枚数が格納されてるそうな。

DiskNumber
int
えー、元アルバムの何枚目に収録されてるかを格納・・・これ使ってるのかな?

Duration
int
トラックの長さを秒単位で返してくれるらしい。処理に使うならこっちかな?

Enabled
bool
再生可否を示すプロパティ。

EQ
string
EQ。心の知能指数・・・ではなく、イコライザのことっぽい。
EQプリセットの名前を示すプロパティだそうな

Finish
int
トラックの停止時間を秒で格納しているとかなんとか。
レジューム再生用?
ノータッチでした

Genre
string
ジャンル。これ難しいよね。特にメタルとロックとパンク。

Grouping
string
グルーピングタグ。なんかタグでまとめておけるっぽいよ、トラック。そのためのタグ?

KindAsString
string
”AAC audio file”みたいなファイルの種類を文字列で返してくれるそうな。

Kind
ITTrackKind
音声ファイルの種別情報みたい。enum。ファイル、CD、URL、デバイス、ライブラリとかを判別してる?

ModificationDate
DateTime
更新日。

Name
string
トラックの名前。大抵の場合楽曲名。

PlayedCount
int
再生回数。

PlayedDate
DateTime
最後に再生した日付を記録してるみたい。
古い順に並べると懐メロがいろいろ聞けるかも?

Playlist
IITPlaylist
トラックが登録されてるプレイリストを返却してくれるっぽい。

PlaylistID
int
多分playListの中の番号・・・かな?

PlayOrderIndex
int
オーナープレイリストにおける再生順序、だそうな。

Rating
int
トラックのレーティング。一応0から100の値で設定できるみたい。
0は未設定値として扱ってて、アルバムのレーティングが適用されるんだって

SampleRate
int
サンプリングレート。Hz単位だそうな

Size
int
トラックの情報量(byte)。

Start
int
トラックの開始時間を秒で返却・・・もしかして使ってない領域があるってこと?

Time
string
プレイ時間。mm:ssみたいな書式でくる。

TrackCount
int
アルバムの総トラック数

TrackNumber
int
アルバムのトラック番号

VolumeAdjustment
int
トラックごとのボリューム調整情報。-100~100の値で設定してるそうな。
加減値?

Year
int
録音、リリース年を格納してるそうな

えー、つらつら書いたんですが

上の成果を見てもらってわかるとおり、今回触ったのは

Album、Name、Artistくらいなので、ほかは一応調べたけど使う場合はちょっと疑ってくださいな

ソースは・・・どうしよう?

勢いで作ったから雑なんだよなぁ・・・

まあ、部分部分上げます。起きたらね!


・・・起きた!(19時)

ってことでちょろちょろソース挙げていきます。

参照の加え方とかは参考にさせていただいたページをご覧あれ。

iTunesをインストールしてるPCなら問題なくiTunesLibは見つかるハズ。


ソース

今回はなんとなくFormアプリケーションで組みましたが・・・ぶっちゃけコンソールアプリで十分だった気がする。

コンストラクタでiTunesAppオブジェクトを取得し、イベントを登録する。

iTunes.(Event名) += ... って部分ね。

・・・そういえばDesigner.csの読み方に関する記事書こうとしてガッツリ忘れてたなぁ。

再生中のトラックが有る場合、トラック情報が更新されたか判定するためのメソッドに処理を投げるようにしてます。

わざわざObject型にキャストしてるのは、iTunesAppのイベントと共通の処理にしたかったから。


Form1.cs_iTunesAppオブジェクト設定部分

    public partial class Form1 : Form {

delegate void SetTextCallback(string text);

private iTunesApp iTunes;
private IITTrack cTrack;

public Form1() {
InitializeComponent();

/* iTunesCOMオブジェクトの取得とイベントハンドラの登録 */
iTunes = new iTunesApp();
iTunes.OnPlayerPlayEvent += ITunes_OnPlayerPlayEvent;
iTunes.OnPlayerPlayingTrackChangedEvent += ITunes_OnPlayerPlayingTrackChangedEvent;

if ( iTunes.CurrentTrack != null ) {
/* 再生中のトラックが存在する場合 */
isNewTrack( (Object)iTunes.CurrentTrack );

}else {
/* 再生中のトラックが存在しない場合 */
/* nop */

}

return;
}


イベントハンドラ内の処理として


  1. 保存してるトラックと同一のものか判定

  2. 新しいトラックである場合、クラス内に保存しているトラック情報を更新

  3. 必要なトラック情報を取得してHTTPリクエストを作成してポイッ!ってする

HTTPリクエスト投げるにあたって、using System.Net;が必要


Form1.cs_イベントハンドラ内の処理


/// <summary>
/// Track再生開始時にコール
/// </summary>
/// <param name="iTrack"></param>
private void ITunes_OnPlayerPlayEvent(object iTrack) {

isNewTrack(iTrack);
return;
}

/// <summary>
/// Trackが変更された場合にコール
/// </summary>
/// <param name="iTrack"></param>
private void ITunes_OnPlayerPlayingTrackChangedEvent(object iTrack) {

isNewTrack(iTrack);
return;
}

/// <summary>
/// 引数に指定されたトラックが新規トラックであるか判定する
/// </summary>
/// <param name="iTrack"></param>
/// <returns></returns>
private bool isNewTrack( object iTrack) {
bool ret = false;
IITTrack paramTrack = (IITTrack)iTrack;

if ( (cTrack == null) || ( cTrack.Name != paramTrack.Name ) ) {
/* 保存しているトラックが引数のトラックと異なる場合 */
cTrack = paramTrack;
refreshInfomation();
ret = true;

}else {
/* 保存しているトラックが引数のトラックと一致する場合 */
/* nop */
}

return ret;
}

/// <summary>
/// フォームの情報を更新する。
/// </summary>
private void refreshInfomation() {

string trackInfo;
string album;
string trackNo;
string trackName;
string artist;
string tracktime;
string sendMsg;

album = cTrack.Album;
trackNo = cTrack.TrackNumber + "/" + cTrack.TrackCount;
trackName = cTrack.Name;
artist = cTrack.Artist;
tracktime = cTrack.Time;

/* iTunes.PlayerPositionは秒単位での動作ポジションを取得する。MSはたぶんミリセク単位でとるやつ。 */
trackInfo = album + "\r" + trackNo + "\r" + trackName + "\r" + artist + "\r" + iTunes.PlayerPosition + "\r" + tracktime;

updateText(trackInfo);

/* webhook処理 */
sendMsg = trackName + "- " + album + "/" + artist;
webhook2IFTTT(sendMsg);

return;
}

/// <summary>
/// IFTTTへのwebhook処理
/// </summary>
/// <param name=""></param>
private void webhook2IFTTT(string pStr) {

updateHTTPreq("Request ready");

/* POST ペイロードの調整 */
string payload = " { \"value1\" : \"" + pStr + " \" } ";
byte[] byteArray = Encoding.UTF8.GetBytes(payload);

/* HTTPリクエストの送信処理 */
/**
* Method : POST
* ContentType : application/json
* IFTTTのwebhookに投げるHTTPリクエストの調整
*/

WebRequest wReq = WebRequest.Create("https://maker.ifttt.com/trigger/playMusic/with/key/!ここはアカウント別のキー!");
wReq.Method = "POST";
wReq.ContentType = "application/json";
wReq.ContentLength = byteArray.Length;

Stream dataStream = wReq.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();

WebResponse wRes = wReq.GetResponse();

updateHTTPreq( "Request send" );

/* リソースの解放 */
wRes.Close();

return;
}


で、終了時はCOMオブジェクトを解放しないといけませんよ、と

登録したイベントハンドラはiTunes.(イベント名) -= ...で削除してます。

COMを解放するにあたってusing System.Runtime.InteropServices;を追加する必要あり。


Form1.cs_終了時の処理


/// <summary>
/// Formクローズ時の処理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_FormClosed(object sender, FormClosedEventArgs e) {
if (iTunes != null) {
iTunes.OnPlayerPlayEvent -= ITunes_OnPlayerPlayEvent;
iTunes.OnPlayerPlayingTrackChangedEvent -= ITunes_OnPlayerPlayingTrackChangedEvent;

Marshal.ReleaseComObject(iTunes);
iTunes = null;
} else {
/* nop */
}

}


ちょっとその、きったないソースだけど勢いで作ったから多少はね?

しっかり作る場合はちゃんと例外処理とかログとかいい塩梅で入れなきゃだけど。

参考にさせていただいたもの :

from Agonymous coward - C#でiTunes COM SDKを叩いて曲情報を表示したりアートワークを埋め込んだりしてみる

_liTunesEventsInterfaceReference(英語)

IITTrack Interface Reference


お借りしたもの

IFTTT : https://ifttt.com/discover

IFTTTアイコン : https://icons8.jp/icon/new-icons/all