LoginSignup
4
4

More than 3 years have passed since last update.

VRChatのワールドにGooglePhotosと連携するフォトフレームを追加する

Last updated at Posted at 2020-02-15

何ができるの?

VRChatのワールドに置いたフォトフレームに、Google Photosで指定したアルバム内の画像を表示できるようになります。

仕組みとしてはこちら(https://qiita.com/nyakome306/items/bab82e741dd66053b374 )を基に、VRCPanoramaでGoogle Drive上の画像を参照・表示し、Google Apps Scriptで定期的にGoogle Photosの画像を取得、Driveの画像を更新する形となります。

この記事関係なくGoogleの「バックアップと同期」をPCにインストールしておくと
撮ったSSが自動でGoogle PhotosにアップロードされてスマホからSSを管理できて捗るのでお勧め。

この記事では、一定時間で表示されるSSが切り替わるフォトフレームの実装について紹介します。

Google Apps Script側

一部箇所の説明を省いてますので、こちらも参照しながら設定してください
https://qiita.com/nyakome306/items/bab82e741dd66053b374

スクリプトを新規作成

以下のURLにアクセスし、左上の新規スクリプトをクリックするとエディタが開きます
https://script.google.com

Google Cloud Platform(GCP)プロジェクトと連結

以下のURLにアクセスし、左上の▼をクリックして新しいプロジェクトを作成します。
https://console.cloud.google.com
image.png

プロジェクトのダッシュボードを表示し、左側にあるプロジェクトIDを控えておきます。
先ほど作成したスクリプトのエディタ画面よりリソース→Cloud Platformプロジェクトを選択、プロジェクトIDを入力して”プロジェクトを設定”をクリックします。
image.png

Photos Library APIキーを取得

GCPのプロジェクトのダッシュボードの上側の検索窓にPhotos Library APIと入力して検索し、画像の通りにクリックしていきます。
image.png

image.png
名前はそのままでも大丈夫です。
image.png
表示されたクライアントID、クライアントシークレットを控えます。
image.png

コード本体

以下コードを新規作成したスクリプトに貼り付けてください。
またクライアントIDとクライアントシークレットを上で入手したものに書き換え、保存ボタン💾をクリックしてください。
表示する写真の枚数はphotoNumで指定できます。

表示するアルバムの選択は、photoalbum()を実行すると表示→ログにアルバム名とアルバムIDが出力されるため、表示したいアルバムのIDをコピーし、スクリプトのalbumidを書き換えてください。

qiita.js
var clientid = "クライアントID";
var clientsecret = "クライアントシークレット";

//表示枚数
var photoNum = 10;

//表示するアルバムID
var albumid = "表示したいアルバムのID";


var tokenurl = "https://accounts.google.com/o/oauth2/token"
var authurl = "https://accounts.google.com/o/oauth2/auth"
var scope = "https://www.googleapis.com/auth/photoslibrary"


//アクセストークンURLを含んだHTMLを返す関数
function authpage(){
  var service = checkOAuth();
  var authorizationUrl = service.getAuthorizationUrl();
  Logger.log(authorizationUrl);
}

//認証チェック
function checkOAuth() {
  return OAuth2.createService("PhotosAPI")
    .setAuthorizationBaseUrl(authurl)
    .setTokenUrl(tokenurl)
    .setClientId(clientid)
    .setClientSecret(clientsecret)
    .setCallbackFunction("authCallback") //認証を受けたら受け取る関数を指定する
    .setPropertyStore(PropertiesService.getScriptProperties())  //スクリプトプロパティに保存する
    .setScope(scope)
    .setParam('login_hint', Session.getActiveUser().getEmail())
    .setParam('access_type', 'offline')
    .setParam('approval_prompt', 'force');
}

//認証コールバック
function authCallback(request) {
  var service = checkOAuth();
  Logger.log(request);
  var isAuthorized = service.handleCallback(request);
  if (isAuthorized) {
    return HtmlService.createHtmlOutput("認証に成功しました。ページを閉じてください。");
  } else {
    return HtmlService.createHtmlOutput("認証に失敗しました。");
  }
}

//ログアウト
function reset() {
  checkOAuth().reset();
}
//アルバム一覧を取得する
function photoalbum(){
  var service = checkOAuth();

  var accessToken = service.getAccessToken();

  var url = "https://photoslibrary.googleapis.com/v1/albums";
  var response = UrlFetchApp.fetch(url, {
    method: 'GET',
    headers: {
      Authorization: 'Bearer ' + accessToken
    },
    contentType: "application/json",
    muteHttpExceptions: true
  });
  //一覧データを取得する
  var result = JSON.parse(response.getContentText())["albums"];
  for(i=0;i<result.length;i++){
    Logger.log(result[i]["title"]+","+result[i]["coverPhotoMediaItemId"]);
  }
}



//アルバム内の画像一覧取得
function albumPhotolist(albumId){
  var service = checkOAuth();

  var accessToken = service.getAccessToken();

  //アルバムIDの指定など
  var pageSize = photoNum + 10;
  var payload = {
      "pageSize":pageSize.toString(),
      "albumId": albumId,
  };


  var url = "https://photoslibrary.googleapis.com/v1/mediaItems:search";
  var response = UrlFetchApp.fetch(url, {
    method: 'POST',
    headers: {
      Authorization: 'Bearer ' + accessToken
    },
    contentType: "application/json",
    payload : JSON.stringify(payload),
    muteHttpExceptions: true
  });

  //画像Urlを取得
  var result = JSON.parse(response.getContentText())["mediaItems"];
  var photolist = result.filter(function(v) {return v.mimeType == "image/png" || v.mimeType == "image/jpeg"});
  for(i=0;i<photolist.length;i++){
    Logger.log(photolist[i].baseUrl);
  }
  return photolist;
}

//初期化
function photoFilesInit(){

  var albumFolders=DriveApp.getFoldersByName("VRCPhotoAlbum");
  var albumFolder;
  if(!albumFolders.hasNext()){
    albumFolder = DriveApp.createFolder("VRCPhotoAlbum");
  }else{
    albumFolder = albumFolders.next();
  }
  setProp("folderId",albumFolder.getId());
  var strImURL = "Image URL list";
  for(i=0;i<photoNum;i++){
    var photo;
    var photos = DriveApp.getFilesByName("photo"+i+".png");
    if(!photos.hasNext()){
      photo = albumFolder.createFile("photo"+i+".png", "", MimeType.PNG)
    }else{
      photo = photos.next();
    }
    var access = photo.getSharingAccess()    
    //公開範囲の設定
    if (access != DriveApp.Access.ANYONE) photo.setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.VIEW);
    strImURL += "\nhttp://drive.google.com/uc?export=view&id="+photo.getId();
    setProp("photo"+i+"Id",photo.getId());

  }
  Logger.log(strImURL);
}

//定期実行関数
function updatePhotoFiles(){

  //albumIdに指定したアルバムを表示する
 var photoUrls = albumPhotolist();
  for(i=0;i<photoNum;i++){
    var blob = UrlFetchApp.fetch(photoUrls[i]["baseUrl"]+"=d").getBlob();
    updateFile("photo"+i+".png",getProp("photo"+i+"Id"),blob);

  }
}

function setProp(key,val){
  PropertiesService.getScriptProperties().setProperty(key,val);
}
function getProp(key){
  var value = PropertiesService.getScriptProperties().getProperty(key);
  return value;
}

function updateFile(title,id,blob) {
  var body = {
    title: title,
    mimeType: 'image/png'
  };
  file = Drive.Files.update(body,id, blob);
}




API有効化

Drive APIを用いるため、リソース→Googleの拡張サービスより選択して有効化してください。
GASエディタ上の関数の選択よりauthpageを選択して実行▶ボタンを押してください。
表示→ログを開き、表示されているURLにアクセスしてください。
Googleアカウントのログイン画面が出るのでログインし、詳細→photos library(安全でないページに移動)→許可とクリックしてください。

同期ファイル生成、動作テスト

GASエディタ上の関数の選択よりphotoFilesInitを選択して実行▶ボタンを押してください。VRCPanoramaより参照されるファイルがGoogleDrive上に作成されます。(VRCPhotoAlbumフォルダ)
またupdatePhotoFilesを実行するとVRCPhotoAlbumフォルダ内の画像が指定したアルバムのものに更新されます。

定期実行

updatePhotoFiles()を編集→現在のプロジェクトのトリガーからトリガーを作成して定期実行するようにしてください。

VRChat側

パッケージの導入

以下の.unitypackageを導入してphotoFrame.prefabをワールドに置いてください。
https://drive.google.com/open?id=1rQ5jNQfXfhINcihF7A1M3TAev2JUORy_

URLの登録

GASエディタ上の関数の選択よりphotoFilesInitを選択して実行▶ボタンを押してからログを開くと、VRCPanoramaより参照される画像ファイルのURLが一覧で表示されます。
コメント 2020-02-15 153832.jpg

設置したphotoFrame→screen内のVRCPanoramaコンポーネントのsizeを上のphotoNumで指定した値(初期値10)にし、追加されたフォームにURLを順に入力してください。
コメント 2020-02-15 152850.jpg
Unityの再生ボタンを押して、screenにSSが表示されれば正しく導入されています。

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4