前提・実現したいこと
GoogleHomeでYoutubeのおすすめ動画を音声検索し、
検索結果の動画をスマートホームで接続している別端末で再生する自作アプリを開発したいと思っています。
メッセージと再生までのやり取りは以下となります。
①Me:「OK,Google テストアプリに接続して」
②GoogleHome:「接続しました」
③Me:「Youtubeのおすすめ動画を教えて」
④GoogleHome:「1)チャンネル名〇〇・タイトル××、2)~、3)~です」
⑤Me:「1)を端末Aで再生して」
⑥GoogleHome:「再生します」
→端末Aで再生開始、GoogleHomeとの対話は終了
このうち、①~④についてはAction on GoogleやDialogFlow、YoutubeAPI等を用い、
実装できました。(詳細は補足情報に記載)
また⑤~⑥ですが、別端末でのYouTube動画の再生事態は
GoogleHomeの標準機能で実現できることを確認しています。
(命令文は以下のようになります。)
⑤’ Me:「OK,Google Youtubeで1)の動画を端末Aで再生して」
⑥’ GoogleHome:「Youtubeアプリで再生します」
質問
自作のGoogleHomeアプリで再生まで一貫したいと思っており、
自作アプリ上で⑤'のような標準で用意されている命令を呼び出す必要があるかと思います。
この呼び出しの実装は実現可能でしょうか。
手法や参考文献等ございましたら、ご教授いただきたいです。
補足情報
①~④は以下のように実装しています。
・おすすめ動画の検索
GASでYoutubeAPIを活用し、Youtubeへアクセスし、スプレッドシートにリストを書き出す機能を作成。
定期実行させています。
・呼び出し時の挙動
呼び出された際はDialogFlowで、fullfillmentを用い上記のGASへHTTPアクセスする設定としています。
GASではアクセス結果と上記のスプレッドシートの結果をもとに動画情報(チャンネル名やタイトル)を返却し、
conv.close()関数にて結果を読む処理を作成しています。
・その他
実機テストとしてAction on Googleに接続し、実機での動作を確認しています。
追加情報(ソースコード)
index.js(Dialogflowで設定したFulfillment)
Node.js
1'use strict'; 2 3const functions = require('firebase-functions'); 4const { dialogflow } = require('actions-on-google'); 5 6const app = dialogflow(); 7 8const https = require('https'); 9const get = (url) => new Promise((resolve, reject) => { 10 https.get(url, (response) => { 11 let { statusCode } = response; 12 if (statusCode === 302 && response.headers.location ) { //GASの場合一度リダイレクトされるため 13 https.get(response.headers.location, function(res) { 14 let body = ''; 15 res.on('data', (chunk) => body += chunk); 16 res.on('end', () => resolve(body)); 17 return; 18 }); 19 } 20 }).on('error', reject); 21}); 22 23const SEARCH_INTENT = 'Search'; //検索用INTENT(③で発火するINTENT) 24app.intent(SEARCH_INTENT,conv => { 25 const hostUrl = 'script.google.com'; 26 const sheetPath = '******************************'; //GASのシートパス 27 let accessPath = '/macros/s/'+sheetPath+'/exec'; 28 29 const options = { 30 hostname: hostUrl, 31 path: accessPath, 32 followAllRedirects : true, 33 headers: { 34 'Content-Type': 'application/json', 35 'Authorization': 'Bearer ' + '***************************************' 36 } 37 }; 38 39 return get(options).then(response => { 40 conv.ask(JSON.parse(response).fulfillmentText); 41 }); 42}); 43 44const PLAY_INTENT = 'Play'; //再生用INTENT(⑤で発火させるINTENT、未実装) 45app.intent(PLAY_INTENT,conv => { 46 let movie_id = conv.parameters['movie_id']; //⑤の命令文から動画の番号を取得 47 let youtube_url = get_url(movie_id); //youtubeのURLを取得 48 49 /*ここで⑥の返答とともに、端末Aでyoutube_urlの動画の再生を実現したい*/ 50 conv.close("再生します"); 51}); 52 53function get_url(movie_id){ 54 //対象動画のURLを取得する(こちらの機能の実装は目処がついています) 55 return 'https://www.youtube.com/watch?v=XXXXXXXXXX' 56} 57 58exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);
main.gs(index.jsから呼び出されるGAS。呼び出し部分のみ抜粋して記載)
Node.js
1function doGet(e){ 2 let message = '1)チャンネル名〇〇・タイトル××、2)~、3)~です'; //返却用メッセージ。実際はスプレッドシートから情報を取得して生成します。 3 return returnAsJSON({"fulfillmentText":message}); 4} 5 6function returnAsJSON(obj){ 7 return ContentService.createTextOutput(JSON.stringify(obj)).setMimeType(ContentService.MimeType.JSON); 8}