🌊
Google Home をミュージックジュークボックスにする(Google Drive 型)
Google Home をミュージックジュークボックスにする(Google Drive 型)
前回までの「Google Home をミュージックジュークボックスにする(改良型)」までで、十分に満足できる感じになってたのですが、どうせなら Google Drive からストリーミングできたらいいな、ということで、作成しました。
準備物
前回使用した go-chromecast と go-cast は今回も使います。
MP3 配信サーバ
前回作成した配信サーバを、今回は Google Drive からストリーミングできるように改造しました。
以下のコードをコンパイルして、バイナリを作成し、パスの通ったディレクトリへ設置してください。
package main
import (
"context"
"fmt"
"io"
"log"
"net/http"
"os"
"sort"
"strconv"
"google.golang.org/api/drive/v3"
)
var (
mp3list map[string]string = map[string]string{}
folder string = "./MP3"
)
func list(folderID, folderName string) {
ctx := context.Background()
srv, err := drive.NewService(ctx)
if err != nil {
log.Fatalf("Unable to retrieve Drive client: %v", err)
}
paging := ""
for {
r, err := srv.Files.List().
SupportsAllDrives(true).PageSize(1000).
Fields("nextPageToken, files(id, name, mimeType, parents)").
PageToken(paging).
Q(fmt.Sprintf("'%s' in parents", folderID)). // 特定のフォルダ配下
Context(ctx).Do()
if err != nil {
log.Fatalf("Unable to retrieve files: %v", err)
}
for _, f := range r.Files {
name := folderName + "/" + f.Name
if f.MimeType == "audio/mp3" ||
f.MimeType == "audio/mpeg" {
mp3list[name] = f.Id
} else if f.MimeType == "application/vnd.google-apps.folder" {
list(f.Id, name)
} else {
// println(f.Name, f.MimeType, f.Id)
}
}
paging = r.NextPageToken
if paging == "" {
break
}
}
}
func keys(m map[string]string) []string {
ks := []string{}
for k := range m {
ks = append(ks, k)
}
return ks
}
func nextHandler(rw http.ResponseWriter, r *http.Request) {
number, _ := strconv.Atoi(r.URL.Query().Get("number"))
filelist := keys(mp3list)
if number < 0 {
number = 0
} else if number >= len(filelist) {
rw.Write([]byte(nil))
return
}
sort.Strings(filelist)
target := filelist[number]
// fmt.Println("target: " + target)
// fileID := mp3list[target]
rw.Write([]byte(target))
}
func fileHandler(rw http.ResponseWriter, r *http.Request) {
target := r.URL.Query().Get("target")
fileID := mp3list[target]
ctx := context.Background()
srv, err := drive.NewService(ctx)
if err != nil {
log.Fatalf("Unable to retrieve Drive client: %v", err)
}
resp, err := srv.Files.Get(fileID).
SupportsAllDrives(true).Context(ctx).Download()
if err != nil || resp == nil {
fmt.Printf("get drive file: %v", err)
}
defer resp.Body.Close()
if _, err := io.Copy(rw, resp.Body); err != nil {
fmt.Printf("write file: %v", err)
}
}
func main() {
if len(os.Args) < 2 {
fmt.Println("usage: serve [google drive folder ID]")
os.Exit(0)
}
folderID := os.Args[1]
list(folderID, "")
for name := range mp3list {
fmt.Println(name)
}
port := os.Getenv("PORT")
if port == "" {
port = "8080"
log.Printf("Defaulting to port %s", port)
}
http.HandleFunc("/next", nextHandler)
http.HandleFunc("/mp3", fileHandler)
log.Printf("Listening on port %s", port)
log.Fatal(
http.ListenAndServe(fmt.Sprintf("0.0.0.0:%s", port),
nil))
}
また、 この記事 を参考に、 Google Drive の MP3 ファイルを置いているフォルダへサービスアカウントからの読み込み許可と、 JSON ファイルのダウンロードを行います。
起動スクリプト
今回のミュージックジュークボックスのスクリプトは以下になります。
#!/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}; export GOOGLE_APPLICATION_CREDENTIALS=./hogehoge.json; serve [Google Drive Folder ID] 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}/mp3?target=${file}"
go-cast --host=${castip} media play "http://${myip}/mp3?target=${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 [ -n "`echo ${status} | grep 'YouTube Music'`" ] || [ "$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
以下の行の JSON ファイルをダウンロードした JSON ファイルへ、そして Google Drive の Folder ID への置き換えを忘れずに行ってください。
(export PORT=${port}; export GOOGLE_APPLICATION_CREDENTIALS=./hogehoge.json; serve [Google Drive Folder ID] 2>/dev/null &)
実行
すべて設置出来たら、前回と同じように、 Google Home に向かって 音楽を再生して
などと命令してください。 Google Drive の指定のフォルダからファイル名の辞書順で音楽をループ再生し始めます。フォルダ構造はネストされていても、再帰的に読み込みます。
共有ドライブにも対応している(と思います)ので、活用してください。
Discussion