気の向くままに辿るIT/ICT/IoT
webzoit.net
IoT・電子工作

ラズパイ自作スマートスピーカーでYouTube音楽のストリーミング

ウェブ造ホーム前へ次へ
サイト内検索
カスタム検索
Raspberry Piって?

ラズパイ自作スマートスピーカーでYouTube音楽のストリーミング

ラズパイ自作スマートスピーカーでYouTube音楽のストリーミング

2019/10/24

 以前、作って運用しつつもブラッシュアップ中のRaspberry Pi 3 Model B+とJuliusOpen JTalkベースの自作スマートスピーカーがあります。

 主な機能は、

 尚、ラズパイ用ACアダプタを挿したスイッチ付きコンセントでのON/OFFとは別にラズパイ用boot/reboot/shutdown物理ボタン付き。

 ちなみに便利なのでラズパイだけでなく、PC/Debianにも自作スマートスピーカー機能を搭載しています。

YouTubeの音源をストリーミング再生

 今回は、ラズパイ・Julius・Open JTalk自作スマートスピーカーやPCに入れた自作スマートスピーカー機能でログインや動画のダウンロードせずにYouTubeミュージック動画の楽曲をストリーミングしつつ、プレイリストのシャッフル再生、スキップ、停止といった操作をできるようにしてみました。

 PC版はともかくラズパイスマートスピーカーにはディスプレイを搭載していないので動画は不要、特定の楽曲ではなく、BGMとして音楽をかけ流ししたい、新/旧・邦楽/洋楽問わずなら平日8時〜21時30分に限られるものの、ラジオNIKKEI第2(RN2)や有志であることもあり、突如消滅することはあるもICECASTでジャズやクラシック、ロック等々既に取り入れていますが、最新の音楽や邦楽というくくりでも聴きたいとなれば、YouTubeやAmazon Music、Spotify、LINEミュージックなどストリーミングサービスが候補に。

 ただ、クラウドに個人情報や利用状況が上がることに抵抗があるからこそ、敢えてローカルで完結するJulius/Open JTalkを使ったスマートスピーカーを自作した自身としては、自分の音楽的趣味嗜好や視聴時間帯なども日々晒したくはないので会員登録・ログインはしたくない...となると視聴可能動画の数に差はあるにせよ、YouTube一択かなと。

 スマートスピーカーでも特定の動画・音声だけとか、1曲、2曲だとすぐに終わってしまいますし、それをリピートというのは、目的とは異なる...、気分が変わる度にスクリプト上の動画URLを書き換えるのもアナログ過ぎる、再生リストも50件、100件とあまり多いと取得するだけで時間がかかり、実用的ではない、メドレー動画なら相当時間聴取できますが、mplayerでshuffleできない為、毎回同じ曲から開始となり微妙...。

 が、一瞬、実用的ではないかとも思ったものの、違法になる余地のないYouTube公式のプレイリストをダウンロードするか、仮にこれがYouTube規約違反ならWebスクレイピングして相当するリストを作る、プレイリストをシャッフル、ループして動画を順次再生させればいいのか...と思うに至りました。

構想

 現在のYouTubeにおいて視聴可能な動画の数は異なるにしても会員登録やログインをしなくともブラウザでのストリーミングは、利用可能なサービスであることは言うまでもなく、YouTubeの特徴であり、人気の秘密であると言えるべきものであることは間違いないでしょう。

 また、今の世の中、普通にPCなどからYouTubeコンテンツを含む動画コンテンツをCLI操作できるツールも多々流通しており、YouTubeのCLI操作も少なくともストリーミング配信なら問題ないと考えてよさ気(判断は自己責任)。

$ youtube-dl 動画URL -o - | mplayer - -novideo
...

 コマンドラインからyoutubeを再生するにもあるように、youtube-dlなどを使わせて頂くとYouTubeの動画+音声はもちろん、音声だけを取得することもでき、mplayerなどを併用するとストリーミング再生できます。

 となれば、スクリプトに書ける、スマートスピーカーに利用できるわけです。

 尚、YouTube Music(旧Google Play Music)/YouTube Music Premium会員なら可能となった模様もその後も(YouTube)利用規約には、「いかなるコンテンツもダウンロード禁止」とありますが、これなら問題ないでしょう。

 ただ、ログインしない場合、プレイリストにおけるシャッフル再生含め、1から再生する以外の機能のほとんどは使えないらしく、再生・停止はどうにでもなるとしてスマートスピーカーで呼び出し都度同じ曲順というのはちょっと...、というわけで後述のようにすることで実質シャッフル機能と更に副産物的な発見があってスキップ機能を実装してみました。

$ sudo apt install python3-pip mplayer
$ pip3 install youtube-dl
$ pip3 install --upgrade youtube-dl
$ mkdir -p ~/tmp/sound/youtube
$ ~/.local/bin/youtube-dl --ignore-errors --no-warnings --get-id "https://www.youtube.com/watch?list=RDCLAK5uy_nbK9qSkqYZvtMXH1fLCMmC1yn8HEm0W90" > /home/xxx/tmp/sound/youtube/jpophotlist.id
$ ~/.local/bin/youtube-dl --ignore-errors --no-warnings --get-id "https://www.youtube.com/watch?list=PL4fGSI1pDJn5FhDrWnRp2NLzJCoPliNgT" > /home/xxx/tmp/sound/youtube/jpoptop100.id

 予め複数曲PlayList ID(符号化されたような文字列ながら実はファイル名っぽい)だけをダウンロードしておき、スクリプト内でプレイリストをシャッフル、ループさせつつ、URI/URLを作ってyoutube-dlとmplayerで順次再生してみるという目論見。

 尚、自身は、Debian(Linux)のリポジトリからではなく、pipでinstallしたyoutube-dlを一応アップグレードしつつ、使用、これによる実行ファイルパス[~/.local/bin]を通していない(環境変数[PATH]に追加していない)為、フルパス指定してあります。

[注意1] ここでは、URLにlistパラメータだけ渡していますが、当初listのみでできたものが、翌日やってみると併せてvパラメータもないとダウンロードできなくなっているリストも存在したりと、ちょっと謎な挙動(対策打たれてる!?)があったので要注意、ただ、更に謎なのは、端末から実行するとvパラメータが要るのにスクリプトからだとvパラメータがなくてもいけること...です。

[注意2] 更に注意すべきは、仮にスクリプトでもそうなった場合、vパラメータに指定したid(短縮?符号化?ファイル名)がリストの更新でなくなるようなことがあった場合、URLが有効になるのか否かで、無効なら後述のようなcronやスマートスピーカー機能起動スクリプトを使う場合、書き換えを要することになることです。

[注意3] ある日突然、プレイリスト自体がなくなる可能性も...。

 仮にPlaylistもコンテンツであり、ダウンロードはダメということなら、先日ICECASTでやってみた通り、WebスクレイピングでプレイリストIDもしくは、プレイリストURLを取得するという方法もあるかと。

 プレイリストについては、違法アップロードの余地がないYouTube公式の『音楽チャンネル』、『邦楽プレイリスト』から、とりあえず、『J-Pop Hotlist』(59曲)と『人気のミュージック ビデオ トップ 100 - 日本』をチョイス、曲数からして後者が有力ですが、何れにするか、はたまたこれ以外のものにするかは後で考えることにしました。

[2019/11/08]

 昨日まで何ごともなく、ノートPCでもラズパイでも先のコマンドラインオプションで実行できていたyoutube-dlによるダウンロードですが、今日になって、ラズパイのみ、なぜか、[--force-ipv4]オプションを付加しないとダウンロードできない事態になりました。

 プレイリストだけでなく、後述のスクリプト~/tmp/sound/script/youtubejpop.shにおける個別動画ストリーミング時のyoutube-dlコマンドについても同様です。

 このオプションがないと[HTTP Error 429: Too Many Requests]とか、[Unable to extract video data]といったエラーメッセージが表示され、結果何もダウンロードできませんでした。

 なぜ、今日突然、しかも、ラズパイのみ...謎...。

 尚、ブラウザで確認してみると動画によっては、[Music Premiumが必要です]と表示されているものもあり、会員登録してログインしない場合には、プレイリスト内の動画であっても実質視聴可不可がある場合があり、視聴可能数に影響があるので要注意です。

 idの取得においても取得できるものとできないものがあるらしく、CLIの場合、何も対策せず、エラーとなると一切、または、その先のリストを取得できない為、エラーや警告を無視するオプション[--ignore-errors]や[--no-warnings]を付けて取得しています。

 実際、前述のような会員登録を要する表示がないものを選んだつもりのプレイリスト用IDリスト取得後、十分時間を置いて[wl -l]してみると、やはり、数曲分程度は、ダウンロードできないものもあるようです。

$ cat ~/tmp/sound/script/youtubejpop.sh
#!/bin/sh
 
while :
do
  for id in `sort -R /home/xxx/tmp/sound/youtube/jpophotlist.id`
  do
    httpcd=`curl -v https://www.youtube.com/watch?v=$id 2>&1 1>/dev/null | awk '{if($2~"HTTP") print $3}'`
 
    if [ $httpcd -eq "200" ]
    then
      /home/xxx/.local/bin/youtube-dl -i "https://www.youtube.com/watch?v=$id" -f bestaudio -o - | mplayer - -novideo
    fi
  done
done

 それはさておき、やることは単純なのでスクリプトにはshellを選択、サンプルスクリプトにおける想定プレイリストは、『J-POP Hotlist』。

 曲順をシャッフルすべく、shellスクリプト内で予めダウンロード済みのプレイリストを[sort -R(--radom-sort)]でランダムに並べ替えつつ、ループ、URI/URL加工してyoutube-dlに、パイプ経由でmplayerに渡すことに、一巡してしまう場合に備え、これらを無限ループすることにしました。

 複数のプレイリストを無限ループ内でそれぞれループで回すか、catで連結したバッファをsort -Rして曲数を嵩増しするのもありですね。

 ただ、動画(ファイル)によってデフォルトのボリューム(音量)が異なるようで曲によって稀に極端に音が小さくなったり(微妙に大きくなったりも?)することがあります。

 ポイントは、プレイリストそのものではなく、プレイリストに含まれる動画URLを直接指定し、ループで回す点でしょうか。

 これは、ブラウザであれば、vパラメータでID(ファイル名)、listパラメータでプレイリスト、(indexパラメータで曲順)指定すると該当する楽曲から再生できるのですが、リファラチェック?によるのか、CLIからだと指定がエラーになり、常に1曲めから再生されてしまうので、これを回避する為です。

 尚、少なくとも自身の現行の環境(光ONUから10base/100base-Tのスイッチングハブや無線LANルーター経由)だと有線・無線に関わらず、timeコマンドで計測してみるとプレイリストのダウンロードにとあるリスト55曲で1m33s、100曲で2m30sなど相応の時間がかかり、そのタイムラグは無視できない為、スクリプト内でダウンロードはしていませんが、公式プレイリストの更新もある可能性を考慮するとcronで一定の期間ごとにダウンロードするのが賢明でしょうね。

 まだ、cron対応していませんし、したとしても、ダウンロード済みプレイリストとの差異は排除しきれないと思われるため、URLの画像やページが存在するかシェル上で確認を参考にファイル存在チェックにcurlとHTTPリターンコードを使うことにしました。

 あ、特に自身の場合、PC版はもちろんのこと、ラズパイスマートスピーカーも常時電源ONではない(使用都度電源を入れている)為、cronよりもスマートスピーカー自動起動設定スクリプト上にバックグランドでダウンロードするよう追記しておけば、使用都度、常に最新のプレイリストに更新できますね。

 何れにしてもダウンロード完了前にプレイリストを再生してしまうとプレイリストファイルにアクセスすることになり、ダウンロードが途中でもプロセスが強制終了してしまうようなので、どこかでプレイリストのダウンロード時間を十分に消費というか吸収というか確保する必要がありますが。

 スマートスピーカーの辞書や応答スクリプトを編集し、ウェイクワード有効時間内のコールワードは、『JPOP』とか、『邦楽』にしてみました。

 この実装に伴い、『音楽消して』や『ラジオ消して』に応答するstop_radio.shに[ps aux | grep youtubejpop.*.sh | awk '{ print "kill -9", $2 }' | sh]と[pkill -f youtube-dl]を追記しました。

 [pkill -f youtube-dl]だけだと、その時流れている曲は停止するものの、ループ中のスクリプトがプロセスとして存在している為、次の曲が再生されてしまうからです。

 というか、せっかく気づいたので、これをスキップ機能として使うべく、Julius辞書と応答スクリプトを編集、プレイリスト再生中にコールワード『スキップ』や『次の曲』で[pkill -f youtube-dl]だけ書いたshellスクリプトを呼ぶことにしました。

 [youtubejpop.*.sh]としたのは、他の選択肢のスクリプト名を[youtubejpophotlist.sh]にしたから、仮に今後増やす場合もこの命名規則?でいこうと考えている為ですが、catで連結するならスクリプト1つでよいので、その場合は、特定のファイル名を指定してもよいですね。

 ともかく、これでYouTubeにログインしたり、動画や音源をダウンロードする必要もなく、CLIによるストリーミング配信で、再生都度シャッフルをかけつつ、YouTubeのプレイリストに沿った音楽を聴取、再生時にスキップもできるようになりました。

Juliusオリジナル辞書の編集

$ vi mysmartspeaker.list
...
JPOP  [JPOP]  j e i p o q p u
邦楽  [JPOP]  h o u g a k u
...
スキップ  [SKIP]  s u k i q p u
次の曲  [SKIP]  t u g i n o ky o k u
...
$ iconv -f utf8 -t eucjp mysmartspeaker.list > mysmartspeaker.eucjp
$

 コールワードをこんな感じでJuliusの辞書に登録、(Julius 4.4)では、eucjpに変換しておきます。

スマートスピーカー応答スクリプトの編集

$ pwd
/home/xxx/sound/
$ vi ./voicerecieve.pl
#!/usr/bin/env perl
...
      # 【邦楽】
      when("JPOP"){
       system("/home/xxx/sound/script/stop_radio.sh &");
       system("/home/xxx/sound/script/play_youtube_jpop.sh &");
      }
      # 【スキップ】
      when("SKIP"){
       system("/home/xxx/sound/script/skip_playlist.sh &");
      }
 ...
 

 これらを踏まえ、自作スマートスピーカー用スクリプトvoicerecieve.plに適宜追記。

変更の反映

 辞書に影響がある(辞書を編集した)場合は、少なくともJulius 4.4においては、iconvでeucjpに変換が必要です。

 構成ファイル変更の反映については、systemd/systemctlならsystemd自動起動設定参照。

ウェブ造ホーム前へ次へ