動画のプレイリストの内容をスプレッドシートで管理する

はじめに

※※※信頼されたドメインは動的に作成できるようになるらしいのでAWS部分内容を割愛しました※※※
※※※上記機能実装後AWSを挟まない形で実装しなおす予定※※※

本記事はVRChat内の動画プレイリストの内容をスプレッドシートで管理する方法についての解説です。
対象のVideoPlayerはKineL式VideoPlayerです。

スプレッドシートの内容はAPIを通じて公開されることになります。
また、スプレッドシートのみでは解決できない部分があるため、サーバサイドとしてAWSを利用しています。

アーキテクチャ図
スプレッドシートイメージ

このようなスプレッドシートが
スプレッドシートイメージ

こんな感じでVRChat内で表示されるようになります。
VRChatイメージ

利用ツール、技術など

  • 言語
    • Python、C#(Udon)
  • バックエンド
    • Googleスプレッドシート、AWS
  • その他
    • Unity、Serverless Framework

概念さえ理解できてれば、PythonやAWSの知識は必要なく他のもので代用は利くので必須じゃないです。

手順

1. スプレッドシートの作成

以下をAppsScriptで作成します。
デプロイしてURLを取得しておきます。
https://script.google.com/macros/s/AKhogehohehogehohehogehohehogehohe/exec
のAKhogehohehogehohehogehohehogehohe部分が分かれば大丈夫です。
詳細なスプレッドシートの内容は本記事では割愛します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
function doGet(e) {
const mode = (typeof e === "undefined" || typeof e.parameter.mode === "undefined")?"NONE":e.parameter.mode;
const sheet = SpreadsheetApp.getActive().getSheetByName("main");
var res;
if(mode == "titles") res = getAllTitles(sheet);// Create
if(mode == "url") res = getURL(sheet, e.parameter.id);
if(mode == "NONE") res = JSON.stringify(getAll(sheet), null, 2);
console.log(res);
return ContentService.createTextOutput(res).setMimeType(ContentService.MimeType.JSON);
}

function getAllTitles(sheet){
console.log("getaAllTitles!!");
const start_row = 2,start_col = 1 // 開始行,開始列 --> A1なら(1,1)、A2なら(2,1)
const last_col = 1;
const last_row = 21; // 変なとこに変な値とかあるとおっもくなるので決め打つ
const datas = sheet.getRange(start_row, start_col, last_row, last_col).getValues();
var list = "";
for(let a of datas){
console.log(a);
for(let b of a){
if(b != ""){
if(list != "") list += ",";
list += b;
}
}
}
return list;
}

function getURL(sheet, id){
console.log("getTitleForID!!");
values = sheet.getDataRange().getValues();
return values[id][1]
}

function getAll(sheet) {
console.log("getTitleForID!!");
const rows = sheet.getDataRange().getValues();
const keys = rows.splice(0, 1)[0];
return rows.map(row => {
const obj = {};
row.map((item, index) => {
obj[String(keys[index])] = String(item);
});
return obj;
});
}

「はじめに」の画像にあるようなスプレッドシートを入力します。
A2からA11までに動画のタイトルを入力します。
B2からB11までに動画のURLを入力します。

2. AWSリソースの作成

以下の2つのLambdaを作成しています。作成済みで利用できるものを用意してるため割愛して大丈夫です。
https://github.com/aki-lua87/vrc_video_playlist_server/blob/master/src/lambda/get_ss_video/handler.py
https://github.com/aki-lua87/vrc_video_playlist_server/blob/master/src/lambda/get_ss_video_titles/handler.py

3. Unity側の実装

Unityでスクリプトを作成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
using System.Globalization;
using UdonSharp;
using UnityEngine;
using UnityEngine.UI;
using VRC.SDKBase;
using VRC.Udon;
using VRC.Udon.Common.Interfaces;
using VRC.SDK3.StringLoading;

public class VideoTitleLoader : UdonSharpBehaviour
{
[SerializeField] private GameObject targetPlaylist;
[SerializeField] private VRCUrl stringURL;

// NOTE: KVPPlaylist/Canvas/PlaylistImpl/Scroll View/Viewport/Content
// NOTE: Video (0)/Description.Text.text

public void LoadString()
{
VRCStringDownloader.LoadUrl(stringURL, (IUdonEventReceiver)this);
}

public override void OnStringLoadSuccess(IVRCStringDownload download)
{
Debug.Log("VideoTitleLoader: OnstringLoadSuccess");
var titlesString = download.Result;
var titles = titlesString.Split(',');
// text1.text = titles[0];
// contentを取得
var content = targetPlaylist.transform.Find("KVPPlaylist/Canvas/PlaylistImpl/Scroll View/Viewport/Content");
// Debug.Log(content.gameObject.name);
// 配下のvideo(N)を取得
var videos = GetChildren(content);
// Debug.Log(videos[0].name);
Debug.Log("titlesString" + titlesString);
for (var i = 0; i < videos.Length; i++)
{
if (titles.Length < i)
{
break;
}
var descriptionText = videos[i].Find("Description").gameObject.GetComponent<Text>();
descriptionText.text = titles[i];
Debug.Log(videos[i].name + " => " + titles[i]);
if (titles[i].Length < 25)
{
descriptionText.fontSize = 40;
}
else
{
descriptionText.fontSize = 23;
}
}
}

public override void OnStringLoadError(IVRCStringDownload result)
{
Debug.Log($"VideoTitleLoader: OnStringLoadError {result.ErrorCode} {result.Url} {result.Error}");
}

// parent直下の子オブジェクトをforループで取得する
private Transform[] GetChildren(Transform parent)
{
// 子オブジェクトを格納する配列作成
var children = new Transform[parent.childCount];
// 0~個数-1までの子を順番に配列に格納
for (var i = 0; i < children.Length; ++i)
{
children[i] = parent.GetChild(i);
}
// 子オブジェクトが格納された配列
return children;
}
}

GameObjectにVideoTitleLoaderをアタッチ、targetPlaylistにはプレイリストのGameObjectをアタッチします。
URL欄には以下を入力します。AKhogehohehogehohehogehohehogehoheはデプロイしたスプレッドシートのURLの一部です。
https://vrc.akakitune87.net/ss/titles?ssid=AKhogehohehogehohehogehohehogehohe

Unityイメージ

プレイリストのURLを入力

Unityイメージ

タイトルは何でもいいです
https://vrc.akakitune87.net/ss/video?ssid=AKhogehohehogehohehogehohehogehohe&n=1
https://vrc.akakitune87.net/ss/video?ssid=AKhogehohehogehohehogehohehogehohe&n=2
https://vrc.akakitune87.net/ss/video?ssid=AKhogehohehogehohehogehohehogehohe&n=3
https://vrc.akakitune87.net/ss/video?ssid=AKhogehohehogehohehogehohehogehohe&n=4
https://vrc.akakitune87.net/ss/video?ssid=AKhogehohehogehohehogehohehogehohe&n=5
https://vrc.akakitune87.net/ss/video?ssid=AKhogehohehogehohehogehohehogehohe&n=6
https://vrc.akakitune87.net/ss/video?ssid=AKhogehohehogehohehogehohehogehohe&n=7
https://vrc.akakitune87.net/ss/video?ssid=AKhogehohehogehohehogehohehogehohe&n=8
https://vrc.akakitune87.net/ss/video?ssid=AKhogehohehogehohehogehohehogehohe&n=9
https://vrc.akakitune87.net/ss/video?ssid=AKhogehohehogehohehogehohehogehohe&n=10

プレイリストのタブを修正

Unityイメージ

KineLVP Playlist(tab)/KinelVP TabList Impl/Tablist/Scroll View/Viewport/Content[プレイリスト名]/Dummy
のインスペクタよりOnValueChangedに画像のようにスクリプトをアタッチします。

Unityイメージ

アタッチするスクリプトは「 Unityでスクリプトを作成」で作成したものです。

4. VRChat内で確認

  • 当該のタブを選択すると、スプレッドシートの内容が表示されることを確認
  • プレイリスト押下でスプレッドシートで入力した動画が再生されることを確認

おわりに

弊環境では問題なく動いてますが複数環境での動作確認はしていません、問題などありましたらご報告いただけると助かります。
上記システムを利用する際は自己責任でお願いします。