🌊

Google Home をミュージックジュークボックスにする(改良型)

2022/10/12に公開

Google Home をミュージックジュークボックスにする(改良型)

前回の「Google Home をミュージックジュークボックスにする」だと、再生のたびに ffmepg によるトランスコードが発生し、なかなか再生されずにストレスが溜まっていました。でも、 Chromecast 系はトランスコードしなくても、直接 MP3 などの再生機能を持っているはずなので、ということで、改良型を作成しました。

準備物

前回使用した go-chromecast は今回も使います。こっちのほうが、1曲1曲の再生終了タイミングなど、取れる情報が多いです。代わりに、今回は ffmpeg は必要なくなります。

今回の肝は go-cast です。上の go-chromecast のもともとの成果物だと思うのですが、こちらは MP3 をトランスコードせずにダイレクトに Google Home に指令することが出来ました。

それぞれバイナリも用意されていますので、それぞれ環境にあったバイナリを取ってくるなりコンパイルするなりして、パスの通ったディレクトリへ設置します。

MP3 配信サーバ

go-cast は go-chromecast と違って、自分で HTTP サーバとはならないので、簡易的な HTTP サーバを golang で作成しました。

以下のコードをコンパイルして、バイナリを作成し、パスの通ったディレクトリへ設置してください。

package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"sort"
	"strconv"
)

var (
	folder string = "./MP3"
)

func nextHandler(rw http.ResponseWriter, r *http.Request) {
	number, _ := strconv.Atoi(r.URL.Query().Get("number"))

	filelist := []string{}
	err := filepath.Walk(folder,
		func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}

			if info.IsDir() {
				return nil
			}
			filelist = append(filelist, path)
			return nil
		})
	if err != nil {
		panic(err)
	}
	sort.Strings(filelist)
	if number < 0 {
		number = 0
	} else if number >= len(filelist) {
		rw.Write([]byte(nil))
		return
	}
	target := filelist[number]
	// fmt.Println("target: " + target)
	rw.Write([]byte(target))
}

func main() {
	if len(os.Args) < 2 {
		fmt.Println("usage: serve [MP3 folder]")
		os.Exit(0)
	}
	folder = os.Args[1]

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
		log.Printf("Defaulting to port %s", port)
	}

	http.Handle("/MP3/",
		http.StripPrefix("/MP3/",
			http.FileServer(http.Dir(folder+"/"))))
	http.HandleFunc("/next", nextHandler)

	log.Printf("Listening on port %s", port)
	log.Fatal(
		http.ListenAndServe(fmt.Sprintf("0.0.0.0:%s", port),
			nil))
}

肝心の MP3 ファイルは MP3 フォルダ以下へ展開しておいてください。このとき、階層が深くなっても、再帰的に探してくるので、適当に整理して配置してください。曲の順番は 012ABC 順になります。

起動スクリプト

今回のミュージックジュークボックスのスクリプトは以下になります。

myip は設置するマシンの IP を、 castip には Google Home の IP アドレスを設定してください。分からない場合には、 go-cast discover などとすると探してきてくれます。

#!/bin/sh

# port number
port="8808"
# my ip address
myip="192.168.??.??:${port}"
# chromecast ip address
castip="192.168.??.??"

trap "exit" INT TERM
trap "kill 0" EXIT
(export PORT=${port}; serve ./MP3/ 2>/dev/null &)

play () {
	file="`wget -O - http://${myip}/next?number=$index 2> /dev/null`"
	if [ -z "$file" ]; then
		index=0
		file="`wget -O - http://${myip}/next?number=$index 2> /dev/null`"
	fi
	echo ${file}
	#echo go-cast --host=${castip} media play "http://${myip}/${file}"
	go-cast --host=${castip} media play "http://${myip}/${file}" >/dev/null
	index=$((index+1))
}

index=0
go-chromecast watch --addr "${castip}" |
        (\
        while read line
        do
		#echo $line
		status="`echo $line | sed 's/^.*"playerState":"//g' | sed 's/".*$//g'`"
                #echo $status
                if [ "$status" = "BUFFERING" ] || [ "$status" = "IDLE" ]; then
			#echo "START!"
			#go-cast --host=${castip} quit
			play
                elif [ "$status" = "CLOSE" ]; then
                        #echo "STOP!"
			go-cast --host=${castip} quit
                fi
        done
        )

wait

実行

すべて設置出来たら、上のスクリプトを実行してから、 Google Home に向かって 音楽を再生して などと命令してください。 YouTube Music の再生時のバッファリングのタイミングを検出して、 MP3 の再生が始まります。

YouTube Music の再生が始まり、なかなか MP3 ファイルが再生されないときには、 次の曲を再生して などとしてください。バッファリングを検出してからの MP3 ファイルの再生になります。

また、 MP3 再生中に 音楽を再生して などとして、 YouTube Music を再生させようとすると、次の曲へスキップします。たまに2,3曲飛ばしてしまいますが、そこは御愛嬌。各自で修正してください。

音楽を止めて と命令すれば、再生は終了します。

今回の改良型で、十分実用的なミュージックジュークボックスになったと思います。

Discussion