Last active
August 14, 2020 05:10
-
-
Save esnya/0f021e7656e9cafdba6ff0ff4e63845d to your computer and use it in GitHub Desktop.
Google Drive のフォルダかファイルの共有URLを指定すると VRChat の VRC_Panorama を自動的に設定するツール
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#if UNITY_EDITOR | |
using System.Linq; | |
using UnityEngine; | |
using UnityEditor; | |
using System; | |
using System.Text.RegularExpressions; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using Google.Apis.Auth.OAuth2; | |
using Google.Apis.Drive.v3; | |
using Google.Apis.Services; | |
using Google.Apis.Util.Store; | |
using VRCSDK2; | |
namespace EsnyaFactory { | |
public class GoogleDrivePanoramaUtility : EditorWindow { | |
[MenuItem("EsnyaFactory/Google Drive Panorama Utility")] | |
private static void ShowWindow() { | |
var window = GetWindow<GoogleDrivePanoramaUtility>(); | |
window.Show(); | |
} | |
private string fileIdPatternString = @"(file/d|folders)/(?<fileId>[a-zA-Z0-9_-]+)"; | |
private Vector2 _scrollPosition = Vector2.zero; | |
private string clientId; | |
private string clientSecret; | |
private long fileSizeLimit = 2 * 1024 * 1024; | |
private UserCredential credential; | |
private MeshRenderer target; | |
private string url; | |
private Google.Apis.Drive.v3.Data.File file; | |
private Google.Apis.Drive.v3.Data.File[] files; | |
private bool isRunning = false; | |
private DriveService drive { | |
get { | |
return new DriveService(new BaseClientService.Initializer() { | |
HttpClientInitializer = credential, | |
ApplicationName = "EsnyaFactory.GoogleDrivePanoramaUtility", | |
}); | |
} | |
} | |
private Regex fileIdPattern { | |
get { | |
return new Regex(fileIdPatternString); | |
} | |
} | |
private string fileId { | |
get { | |
if (url == null) { | |
return null; | |
} | |
var match = fileIdPattern.Match(url); | |
return match.Groups["fileId"].Value; | |
} | |
} | |
private bool isFolder { | |
get { | |
var b = file?.Capabilities?.CanListChildren; | |
return b == true; | |
} | |
} | |
private MonoBehaviour panorama { | |
get { | |
if (target == null) return null; | |
return target.GetComponents<MonoBehaviour>().FirstOrDefault(c => c.GetType().Name.EndsWith("VRC_Panorama")); | |
} | |
} | |
private bool isReady { | |
get { | |
if (target == null) return false; | |
if (file == null) return false; | |
if (isFolder) { | |
if (files == null || files.Length == 0) return false; | |
} else { | |
if (file.WebContentLink == null) return false; | |
} | |
if (panorama == null) return false; | |
return true; | |
} | |
} | |
private void ReadonlyTextField(string label, string value) | |
{ | |
EditorGUILayout.BeginHorizontal(); | |
EditorGUILayout.LabelField(label, GUILayout.Width(EditorGUIUtility.labelWidth - 4)); | |
EditorGUILayout.SelectableLabel(value, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight)); | |
EditorGUILayout.EndHorizontal(); | |
} | |
public GoogleDrivePanoramaUtility() | |
{ | |
Initialize(); | |
} | |
private async void AsyncAction(Func<Task> action) | |
{ | |
isRunning = true; | |
try { | |
await action(); | |
} finally { | |
isRunning = false; | |
} | |
} | |
private void OnGUI() { | |
_scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition); | |
EditorGUI.BeginDisabledGroup(isRunning); | |
var headerStyle = new GUIStyle() { fontStyle = FontStyle.Bold }; | |
titleContent = new GUIContent("Google Drive Panorama Utility"); | |
EditorGUILayout.LabelField("Google Drive SDK", headerStyle); | |
if (credential == null) { | |
clientId = EditorGUILayout.TextField("Client Id", clientId); | |
clientSecret = EditorGUILayout.PasswordField("Client Secret", clientSecret); | |
EditorGUI.BeginDisabledGroup(clientId == null || clientSecret == null); | |
if (GUILayout.Button("Authorize")) { | |
AsyncAction(Authorize); | |
} | |
EditorGUI.EndDisabledGroup(); | |
} else { | |
EditorGUILayout.BeginHorizontal(); | |
EditorGUILayout.LabelField("Authorized"); | |
if (GUILayout.Button("Clear Credential")) { | |
AsyncAction(ClearCredential); | |
} | |
EditorGUILayout.EndHorizontal(); | |
EditorGUILayout.Space(); | |
EditorGUILayout.LabelField("File(s)", headerStyle); | |
url = EditorGUILayout.TextField("URL", url); | |
fileIdPatternString = EditorGUILayout.TextField("File Id Pattern", fileIdPatternString); | |
EditorGUI.BeginDisabledGroup(file == null); | |
EditorGUILayout.LabelField("Name", file?.Name ?? ""); | |
EditorGUILayout.LabelField("Type", $"{file?.MimeType}"); | |
EditorGUILayout.LabelField("Size", $"{file?.Size}"); | |
EditorGUILayout.LabelField("Shared", $"{file?.Shared == true}"); | |
EditorGUILayout.LabelField("Web Content Link", $"{file?.WebContentLink}"); | |
EditorGUILayout.LabelField("Is Folder", $"{file?.Capabilities?.CanListChildren}"); | |
if (files != null) { | |
EditorGUILayout.LabelField("Children"); | |
files.ToList().ForEach(childFile => { | |
EditorGUILayout.BeginVertical(GUI.skin.box); | |
EditorGUILayout.LabelField("Name", childFile?.Name ?? ""); | |
EditorGUILayout.LabelField("Type", $"{childFile?.MimeType}"); | |
EditorGUILayout.LabelField("Size", $"{childFile?.Size}"); | |
EditorGUILayout.LabelField("Web Content Link", $"{childFile?.WebContentLink}"); | |
EditorGUILayout.EndVertical(); | |
}); | |
} | |
EditorGUI.EndDisabledGroup(); | |
EditorGUI.BeginDisabledGroup(url == null); | |
if (GUILayout.Button("Update")) { | |
AsyncAction(UpdateFileList); | |
} | |
EditorGUI.EndDisabledGroup(); | |
EditorGUILayout.Space(); | |
EditorGUILayout.LabelField("Panorama", headerStyle); | |
target = EditorGUILayout.ObjectField("Target Mesh Renderer", target, typeof(MeshRenderer), true) as MeshRenderer; | |
EditorGUILayout.Space(); | |
if (panorama == null) { | |
EditorGUILayout.LabelField("Add VRC_Panorama into Target Mesh Renderer"); | |
} | |
fileSizeLimit = EditorGUILayout.LongField("File Size Limit (Bytes)", fileSizeLimit); | |
EditorGUI.BeginDisabledGroup(!isReady); | |
if (GUILayout.Button("Generate VRCPanorama")) { | |
GeneratePanorama(); | |
} | |
EditorGUI.EndDisabledGroup(); | |
} | |
EditorGUI.EndDisabledGroup(); | |
EditorGUILayout.EndScrollView(); | |
} | |
private async Task Authorize() | |
{ | |
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync( | |
new ClientSecrets | |
{ | |
ClientId = clientId, | |
ClientSecret = clientSecret, | |
}, | |
new[] { DriveService.Scope.DriveReadonly }, | |
"user", | |
CancellationToken.None, | |
new FileDataStore("google.token") | |
); | |
if (credential == null || fileId == null) return; | |
await UpdateFileList(); | |
} | |
private async Task ClearCredential() | |
{ | |
await credential.RevokeTokenAsync(CancellationToken.None); | |
credential = null; | |
} | |
private async Task UpdateFileList() | |
{ | |
var fields = "name,shared,capabilities,mimeType,webContentLink,size"; | |
var getReq = drive.Files.Get(fileId); | |
getReq.Fields = fields; | |
file = await getReq.ExecuteAsync(); | |
if (file?.Capabilities?.CanListChildren != true) { | |
files = null; | |
} else { | |
var listReq = drive.Files.List(); | |
listReq.Q = $"'{fileId}' in parents"; | |
var list = await listReq.ExecuteAsync() ; | |
var allFiles = await Task.WhenAll(list.Files.Select(async f => { | |
var req = drive.Files.Get(f.Id); | |
req.Fields = fields; | |
return await req.ExecuteAsync(); | |
})); | |
files = allFiles.Where(f => f.Shared == true && f.MimeType.StartsWith("image/") && f.WebContentLink != null).ToArray(); | |
} | |
} | |
private void GeneratePanorama() | |
{ | |
var dataStorage = target.GetComponent<VRC_DataStorage>(); | |
var display = dataStorage.data.FirstOrDefault(d => d.name == "display"); | |
if (display == null) { | |
dataStorage.data = dataStorage.data.Append(new VRC_DataStorage.VrcDataElement() { | |
name = "display", | |
type = VRC_DataStorage.VrcDataType.Int, | |
valueInt = 0, | |
}).ToArray(); | |
} else { | |
display.valueInt = 0; | |
} | |
var urls = (isFolder ? files : new [] { file }).Where(f => f.Size < fileSizeLimit).Select(f => f.WebContentLink.Replace("export=download", "export=view").Replace("https://", "http://")); | |
var panoramas = urls.Select(url => new VRC.SDKBase.VRC_Panorama.PanoSpec() { url = url }).ToList(); | |
panorama.GetType().GetField("panoramas").SetValue(panorama, panoramas); | |
} | |
private async void Initialize() { | |
if (clientId == null || clientSecret == null) return; | |
await Authorize(); | |
} | |
} | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment