2021年9月29日水曜日

Raspberry Piで自宅用音楽ストリーミングサーバーを作った

Raspberry PiにLibreELECというOSを載せて自宅用の音楽ストリーミングサーバーを作りました。ブラウザで開けばサーバーに用意した楽曲をストリーミングで聞くことができます。

発端


2月にリリースしたウマ娘にハマって以来、特典がついてくるウマ娘CDたちが聞けていません。
それだけでなく、妻の持っているアイマスのCDも聞けていません。ライブにも行ってるのに。

妻はYoutube Musicで手持ちの曲をアップロードして管理しているようですが、他人にはシェアできない仕組みのようです。おそらく私的用途から外れるからでしょうか。

自宅にメディアサーバーを作れば、家で気軽に好きなだけ流せそうです。
ちょうど、家には昔買って放置したRaspberry Piがあります。これでメディアサーバーを作ります。

OSのインストール


手元にあるRaspberry Pi 2 B+にメディア用OSを入れます。

Raspberry PiにはいくつかOSの種類があります。
  • 汎用OS
    • Raspberry Pi OS
    • Ubuntu
  • ゲーム用OS
    • RetroPie
    • Recalbox
  • メディア用OS
    • LibreELEC(今回使ったOS)
    • OSMC
  • その他!
Raspberry Pi Imagerを使えば簡単にSDカードにイメージを焼くことができます。
それだけでなく、32GB以上のSDカードのフォーマットをFAT32にしてくれる機能や、自前で用意したイメージを焼いてくれる機能もあります。めちゃくちゃ便利。

ただ、今回はいくらやってもImagerでLibreELECやOSMCのイメージを焼くことができなかった!(これは、なぜか分からないです...。いくつかのディストリビューションで試してもRaspberry Pi OSしか入れられなかった...。)

悲しいですが、昔懐かしいNOOBSを使いました。NOOBSを使ったOSインストールでは、一旦NOOBSをSDカードに入れて起動し、起動後にインストールするOSを選んでインストールします。ちなみに公式ではNOOBSは非推奨のようです。

NOOBSのイメージは下のQiitaの記事を参考にしました。

今回はNOOBSに入っていたLibreELECを入れました。
ちなみにLibreELECとOSMCでどちらを入れるか迷いましたが、そこまで変わらないようです。後述するKodiさえ入れられれば問題ないと思って今回はインストール可能だったLibreELECを入れています。


セットアップ


LibreELECは基本的にKodiをメインで起動します。他にもAdd-Onを追加する形でゲーム機能を追加したりできます。僕の用途は音楽ストリーミングだけなので、ファイルアップロード用にSambaとSSH、Web Interfaceを有効にしました。

基本は下のサイトを見て設定しました。

ストリーミングで楽曲を聞く


Sambaでファイル共有ができるので、手元のPCから音楽ソースに追加されているディレクトリにコピーします。

追加しただけではKodiのWeb Interfaceからはストリーミング配信できません。

右下の三点ボタンから「Scan Audio Library」を押すとMusicに並びます。



アルバムを押すとアルバムの楽曲画面に移ります。
Kodiは右の画面に「Kodi」と「Local」というパネルがあります。
「Kodi」では本体から流れる音楽、つまりRaspberry Piから流れる音楽を表しています。
「Local」はWeb Interface上から流れる音楽を表しています。ストリーミング配信では「Local」を選びます。
アルバムにある「Stream」のボタンを押せば「Local」のプレイリストにすべての楽曲が入り、再生開始します。

茜ちゃんがかわいい of the world


これで自宅内のどこでも音楽を楽しめます!

展望


いくつかやり残しがあります。
  • 楽曲自動バックアップ
    • HDDを繋いでCronで自動的にバックアップさせれば良さそうです。
  • Raspberry Piに光学ドライブを繋いでリップさせる
    • これは試したけどできなかったです。新しいRaspberry Piならできるかも?
  • Amazon Alexaから楽曲再生
    • 夢です。やり方はわかりません。教えて下さい。
というわけで、余っているラズパイを有効活用した話でした。

そして再度放置されるラズパイであった...


2021年3月25日木曜日

Vueのrefを通してリアクティブの雰囲気を理解する

Vueのリアクティブ要素にはrefとreactiveがあります。

たとえばrefをつかったリアクティブの場合、


const count = ref(0);
watchEffect(() => console.log(count.value)); // -> 0
count.value = 1; // -> 1
 

countが変更されるたびにwatchEffect()に登録した関数が実行されます。

countが変更されたらconsole.log()が呼び出される仕組みがわからないので理解していきます。


const count = ref(0)

countにはref(0)が代入されています。このrefはproxyです。

proxyは値とHandlerを持っています。ref(0)だと0が値です。ではHandlerは?

Handlerはrefの中に隠蔽されています。下のコードのような感じです。

Handlerはgetterとsetterを持っています。

getterはtrack()を、setterはtrigger()を持っています。

このtrack()とtrigger()は何をしているのでしょうか?



const dinner = { meal: 'tacos' }
const handler = {
  get(target, prop, receiver) {
    track(target, prop)
    return Reflect.get(...arguments)
  },
  set(target, key, value, receiver) {
    trigger(target, key)
    return Reflect.set(...arguments)
  }
}

const proxy = new Proxy(dinner, handler)
console.log(proxy.meal)

(公式から引用)


track()

trackは

Map<proxyのオブジェクト, proxyを呼び出す関数群>
を保存します。

つまり最初のコードでは、

map.set(count, new Set( {() => console.log(count.value)} ))
を行っています。


const count = ref(0);
watchEffect(() => console.log(count.value));


trigger()

triggerはtrack()で保存したproxyに関連する関数をすべて呼び出します。


const functionSet = map.get(count) // -> Set<Function>
functionSet.forEach( { // 関数を実行 } ) 


ふりかえると

watchEffectの中でcountを呼び出すと、呼び出したラムダが登録されます。

count.valueが変更されると登録されたラムダが実行されます。


const count = ref(0);
watchEffect(() => console.log(count.value)); // -> 0
count.value = 1; // -> 1


リアクティブがどんな感じで実現されているのか雰囲気がつかめてきました。


参考