Skip to content

Instantly share code, notes, and snippets.

@aki-lua87
Last active August 30, 2021 17:59
Show Gist options
  • Save aki-lua87/b33665836c4c330f07968922d86fb2e9 to your computer and use it in GitHub Desktop.
Save aki-lua87/b33665836c4c330f07968922d86fb2e9 to your computer and use it in GitHub Desktop.
YoutubeRSSPlaylistGenerate
#if UNITY_EDITOR && !COMPILER_UDONSHARP
// using Internal;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UdonSharp;
using UdonSharpEditor;
using UnityEditor;
using UnityEditor.Experimental.UIElements;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.UI;
using VRC.Udon;
using VRC.Udon.Serialization.OdinSerializer.Utilities;
using System.Net.Http;
using UnityEngine.Networking;
using System.Xml.Linq;
using HarmonyLib;
// using System.Text.Json;
namespace Kinel.VideoPlayer.Scripts.Playlist
{
public class KinelPlayListGenerator : MonoBehaviour
{
public GameObject playlist;
public List<string> tags = new List<string>();
public List<KinelPlaylist> playlistList = new List<KinelPlaylist>();
public int index = 0;
public string[] playerList = { "KinelVideoPlayer", "iwasync v3", "USharpVideo"};
public string channelID = "";
public string youtubeAPIKey = "";
public string searchWord = "";
public string playlistID = "";
public void OnEnable()
{
}
}
[CustomEditor(typeof(KinelPlayListGenerator))]
public class KinelPlayListGeneratorInspector : Editor
{
private KinelPlayListGenerator instance;
private GameObject targetGameObject;
private SerializedProperty playlistSerializedProperty;
private ReorderableList list;
private ScriptableObject kinelPlaylistEditor;
private SerializedProperty tagsSerializedProperty;
private SerializedProperty indexSerializedProperty;
private SerializedProperty channnelSerializedProperty;
private SerializedProperty apikeySerializedProperty;
private SerializedProperty searchWordSerializedProperty;
private SerializedProperty playlistIDSerializedProperty;
private void OnEnable()
{
instance = target as KinelPlayListGenerator;
playlistSerializedProperty = serializedObject.FindProperty(nameof(KinelPlayListGenerator.playlist));
tagsSerializedProperty = serializedObject.FindProperty(nameof(KinelPlayListGenerator.tags));
indexSerializedProperty = serializedObject.FindProperty(nameof(KinelPlayListGenerator.index));
channnelSerializedProperty = serializedObject.FindProperty(nameof(KinelPlayListGenerator.channelID));
apikeySerializedProperty = serializedObject.FindProperty(nameof(KinelPlayListGenerator.youtubeAPIKey));
searchWordSerializedProperty = serializedObject.FindProperty(nameof(KinelPlayListGenerator.searchWord));
playlistIDSerializedProperty = serializedObject.FindProperty(nameof(KinelPlayListGenerator.playlistID));
list = new ReorderableList(serializedObject, tagsSerializedProperty);
list.drawElementCallback = (rect, index, active, focused) =>
{
Rect uiRect = new Rect(rect.x, rect.y + 5, rect.width, EditorGUIUtility.singleLineHeight);
tagsSerializedProperty.GetArrayElementAtIndex(index).stringValue = EditorGUI.TextField(uiRect,
$"Tag name {index + 1}", tagsSerializedProperty.GetArrayElementAtIndex(index).stringValue);
};
list.elementHeightCallback = reorderableList => (EditorGUIUtility.singleLineHeight + 10);
list.drawHeaderCallback = rect => EditorGUI.LabelField(rect, "Playlist tab initializer");
}
public override void OnInspectorGUI()
{
EditorGUI.BeginChangeCheck();
serializedObject.Update();
EditorGUILayout.Space();
// instance.playlist = EditorGUILayout.ObjectField("Playlist_tab Object", instance.playlist, typeof(GameObject), true) as GameObject;
EditorGUILayout.PropertyField(playlistSerializedProperty);
EditorGUILayout.Space();
// var index = EditorGUILayout.Popup("using player", instance.index, instance.playerList);
EditorGUILayout.PropertyField(indexSerializedProperty);
EditorGUILayout.Space();
list.DoLayoutList();
EditorGUILayout.Space();
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(instance, "playlist");
}
if (GUILayout.Button("Generate"))
CreateTabPrefabs();
// if (GUILayout.Button("Reset Position"))
// ResetPosition();
if (GUILayout.Button("Delete"))
{
bool isOK = EditorUtility.DisplayDialog("警告", "すべてのデータが消去されます。続行しますか?", "続行", "中止");
if (!isOK)
return;
Delete();
}
if (GUILayout.Button("Import"))
{
if(instance.playlistList.Count > 0)
{
var overwrite = EditorUtility.DisplayDialogComplex("上書き", "既にListが存在します。", "続けて追加", "中止", "削除して追加");
switch (overwrite)
{
case 0:
ImportAll(true);
break;
case 2:
ImportAll(false);
break;
default:
return;
}
return;
}
ImportAll(false);
}
if (GUILayout.Button("Export")) // to do
ExportAll();
// aki_lua87 modify
EditorGUILayout.Space();
EditorGUILayout.PropertyField(channnelSerializedProperty);
// list.DoLayoutList();
if (GUILayout.Button("Create Youtube Channel to PlayList"))
CreatePlaylistForYoutubeChannel();
EditorGUILayout.Space();
// EditorGUILayout.PropertyField(apikeySerializedProperty);
apikeySerializedProperty.stringValue = EditorGUILayout.PasswordField("API Key", apikeySerializedProperty.stringValue);
EditorGUILayout.Space();
EditorGUILayout.PropertyField(searchWordSerializedProperty);
if (GUILayout.Button("Create Youtube Search Word to PlayList"))
CreatePlaylistForYoutubeSearchWord();
EditorGUILayout.Space();
EditorGUILayout.PropertyField(playlistIDSerializedProperty);
if (GUILayout.Button("Create Youtube Playrist to PlayList"))
CreatePlaylistForYoutubePlaylist();
serializedObject.ApplyModifiedProperties();
}
public void CreatePlaylistForYoutubeChannel()
{
Debug.Log("Call CreatePlaylistForYoutubeChannel");
try
{
var channel = channnelSerializedProperty.stringValue;
string URL = $"https://www.youtube.com/feeds/videos.xml?channel_id={channel}";
var xml = XDocument.Load(URL);
var root = xml.Root;
var auther = root.Elements().Where(p => p.Name.LocalName == "title").FirstOrDefault();
Debug.Log("auther => "+ auther.Value);
var entrys = root.Elements().Where(p => p.Name.LocalName == "entry");
if( entrys != null )
{
var videoTitles = new List<string>();
var descriptions = new List<string>();
var urls = new List<string>();
var dummys = new List<string>();
var playMode = new List<int>();
foreach( var entry in entrys )
{
var mediaGroup = entry.Elements().Where(p => p.Name.LocalName == "group").FirstOrDefault();
var videoTitle = mediaGroup.Elements().Where(p => p.Name.LocalName == "title").Select(p => p.Value).FirstOrDefault();
var description = mediaGroup.Elements().Where(p => p.Name.LocalName == "description").Select(p => p.Value).FirstOrDefault();
var url = entry.Elements().Where(p => p.Name.LocalName == "link").Select(p => p.Attribute("href").Value).FirstOrDefault();
Debug.Log($"videoTitle:{videoTitle} description:{description} url:{url}");
videoTitles.Add(videoTitle);
descriptions.Add(description);
urls.Add(url);
dummys.Add("");
playMode.Add(0);
}
CreatePlaylist(auther.Value,videoTitles,descriptions,urls,dummys,playMode);
}
}
catch(Exception e)
{
Debug.Log($"プレイリスト作成に失敗しました: {e.Message}");
}
return;
}
[Serializable]
public class YoutubeWebResponse
{
public string kind;
public string etag;
public string nextPageToken;
public string regionCode;
public YoutubeWebResponsePageInfo pageInfo;
public YoutubeWebResponseItems[] items;
[Serializable]
public class YoutubeWebResponsePageInfo
{
public int totalResults;
public int resultsPerPage;
}
[Serializable]
public class YoutubeWebResponseItems
{
public string kind;
public string etag;
public YoutubeWebResponseItemsId id;
public YoutubeWebResponseItemsSnippet snippet;
[Serializable]
public class YoutubeWebResponseItemsId
{
public string kind;
public string videoId;
}
[Serializable]
public class YoutubeWebResponseItemsResourceId
{
public string kind;
public string videoId;
}
[Serializable]
public class YoutubeWebResponseItemsSnippet
{
public string publishedAt;
public string channelId;
public string title;
public string description;
public string channelTitle;
public string liveBroadcastContent;
public string publishTime;
public YoutubeWebResponseItemsResourceId resourceId;
}
}
}
public void CreatePlaylistForYoutubeSearchWord()
{
Debug.Log("Call CreatePlaylistForYoutubeSearchWord");
try
{
var apiKey = apikeySerializedProperty.stringValue;
var word = searchWordSerializedProperty.stringValue;
var maxResults = 15;
string URL = $"https://www.googleapis.com/youtube/v3/search?q={word}&key={apiKey}&part=snippet&maxResults={maxResults}&type=video";
string json = new HttpClient().GetStringAsync(URL).Result;
Debug.Log("response json:"+json);
var data = JsonUtility.FromJson<YoutubeWebResponse>(json);
Debug.Log("data:"+JsonUtility.ToJson(data));
var urlleft = "https://www.youtube.com/watch?v=";
var tagTitle = "検索結果:"+word;
var videoTitles = new List<string>();
var descriptions = new List<string>();
var urls = new List<string>();
var dummys = new List<string>();
var playMode = new List<int>();
foreach(var item in data.items)
{
var targetURL = $"{urlleft}{item.id.videoId}";
Debug.Log($"videoTitle:{item.snippet.title} description:{item.snippet.description} url:{targetURL}");
videoTitles.Add(item.snippet.title);
descriptions.Add(item.snippet.description);
urls.Add(targetURL);
dummys.Add("");
playMode.Add(0);
}
CreatePlaylist(tagTitle,videoTitles,descriptions,urls,dummys,playMode);
}
catch(Exception e)
{
Debug.Log($"プレイリスト作成に失敗しました: {e.Message}");
}
}
public void CreatePlaylistForYoutubePlaylist()
{
Debug.Log("Call CreatePlaylistForYoutubePlaylist");
try
{
var apiKey = apikeySerializedProperty.stringValue;
var playlistID = playlistIDSerializedProperty.stringValue;
var maxResults = 50;
// タイトル取得(少し雑にだけど)
string tURL = $"https://www.googleapis.com/youtube/v3/playlists?id={playlistID}&key={apiKey}&part=snippet";
string tjson = new HttpClient().GetStringAsync(tURL).Result;
var tdata = JsonUtility.FromJson<YoutubeWebResponse>(tjson);
var tagTitle = "";
foreach(var item in tdata.items)
{
tagTitle = item.snippet.title;
break;
}
// 内容取得
string URL = $"https://www.googleapis.com/youtube/v3/playlistItems?playlistId={playlistID}&key={apiKey}&part=snippet&maxResults={maxResults}";
string json = new HttpClient().GetStringAsync(URL).Result;
Debug.Log("response json:"+json);
var data = JsonUtility.FromJson<YoutubeWebResponse>(json);
Debug.Log("data:"+JsonUtility.ToJson(data));
var urlleft = "https://www.youtube.com/watch?v=";
var videoTitles = new List<string>();
var descriptions = new List<string>();
var urls = new List<string>();
var dummys = new List<string>();
var playMode = new List<int>();
foreach(var item in data.items)
{
var targetURL = $"{urlleft}{item.snippet.resourceId.videoId}";
Debug.Log($"videoTitle:{item.snippet.title} description:{item.snippet.description} url:{targetURL}");
videoTitles.Add(item.snippet.title);
descriptions.Add(item.snippet.description);
urls.Add(targetURL);
dummys.Add("");
playMode.Add(0);
}
CreatePlaylist(tagTitle,videoTitles,descriptions,urls,dummys,playMode);
}
catch(Exception e)
{
Debug.Log($"プレイリスト作成に失敗しました: {e.Message}");
}
}
private void CreatePlaylist(string tagTitle,List<string> videoTitles,List<string> descriptions,List<string> urls,List<string> dummys,List<int> playMode)
{
var tabParent = instance.playlist.transform.Find("Canvas/Playlist/Tag/Viewport/Content").gameObject;
var tabPrefab = tabParent.transform.Find("TabPrefab").gameObject;
var listParent = instance.playlist.transform.Find("Canvas/Playlist/List").gameObject;
var listPrefab = listParent.transform.Find("ListPrefab").gameObject;
var index = instance.tags.Count;
instance.tags.Add(tagTitle);
var playlistObject = CreateTabPrefab(tagTitle, index, tabPrefab, listPrefab, tabParent, listParent, isActive: false);
var playlist = playlistObject.GetUdonSharpComponent<KinelPlaylist>();
playlist.titles = videoTitles.ToArray();
playlist.descriptions = descriptions.ToArray();
playlist.urlString = urls.ToArray();
playlist.dummy = dummys.ToArray();
playlist.playMode = playMode.ToArray();
playlist.autoPlay = false;
UdonSharpEditorUtility.CopyProxyToUdon(playlist);
Debug.Log("prefabを作成しました");
}
private bool CreateTabPrefabs()
{
if (!instance.playlist)
{
Debug.LogError("instance.playlist is null");
return false;
}
if (instance.tags.Count < 0){
Debug.LogError("tag count is 0");
return false;
}
var tabParent = instance.playlist.transform.Find("Canvas/Playlist/Tag/Viewport/Content").gameObject;
var tabPrefab = tabParent.transform.Find("TabPrefab").gameObject;
var listParent = instance.playlist.transform.Find("Canvas/Playlist/List").gameObject;
var listPrefab = listParent.transform.Find("ListPrefab").gameObject;
if(tabParent.transform.childCount > 1)
{
bool isOK = EditorUtility.DisplayDialog("警告", "既にListが存在します。続行すると上書きされます。", "続行", "中止");
if (!isOK)
return false;
this.Delete();
}
for (int i = 0; i < instance.tags.Count; i++)
{
_ = CreateTabPrefab(tagsSerializedProperty.GetArrayElementAtIndex(i).stringValue, i,
tabPrefab, listPrefab, tabParent, listParent, isActive: i
== indexSerializedProperty.intValue - 1);
}
tabPrefab.gameObject.SetActive(false);
listPrefab.gameObject.SetActive(false);
return true;
}
private GameObject CreateTabPrefab(string tagName, int index, GameObject tabPrefab, GameObject listPrefab, GameObject tabParent, GameObject listParent, bool isActive)
{
var tab = Instantiate(tabPrefab);
var playlist = Instantiate(listPrefab);
tab.transform.SetParent(tabParent.transform);
playlist.transform.SetParent(listParent.transform);
tab.transform.localPosition = Vector3.zero;
tab.transform.localRotation = Quaternion.identity;
tab.transform.localScale = Vector3.one;
tab.name = $"Tag {index + 1} {tagName}";
tab.SetActive(true);
playlist.transform.localPosition = new Vector3(-325, -450, 0);
playlist.transform.localRotation = Quaternion.identity;
playlist.transform.localScale = Vector3.one;
var toggleComponet = tab.GetComponent<Toggle>();
toggleComponet.isOn = isActive;
var playlistComponent = playlist.GetComponent<KinelPlaylist>();
var text = tab.transform.GetChild(1).GetComponent<Text>();
text.text = $"{tagName}";
playlist.name = $"List {index + 1} {tagName}";
instance.playlistList.Add(playlistComponent);
playlist.SetActive(isActive);
return playlist;
}
private void ResetPosition()
{
var listParent = instance.playlist.transform.Find("Canvas/Playlist/List");
for (int i = 0; i < listParent.childCount; i++)
{
var transform = listParent.GetChild(i);
transform.localPosition = Vector3.zero;
transform.localRotation = Quaternion.identity;
}
}
private void Delete()
{
var tagParent = instance.playlist.transform.Find("Canvas/Playlist/Tag/Viewport/Content");
var listParebt = instance.playlist.transform.Find("Canvas/Playlist/List");
var count = tagParent.childCount;
for (int i = 1; i < count; i++)
{
DestroyImmediate(tagParent.GetChild(1).gameObject);
DestroyImmediate(listParebt.GetChild(1).gameObject);
}
tagParent.GetChild(0).gameObject.SetActive(true);
listParebt.GetChild(0).gameObject.SetActive(true);
instance.playlistList.RemoveAll(s => true);
}
public void ExportAll()
{
var filePath = EditorUtility.SaveFolderPanel("", "", "Playlist");
if (string.IsNullOrEmpty(filePath))
return;
filePath = $"{filePath}/{DateTime.Now:yyyy_MM_dd_HH_mm_ss}";
Directory.CreateDirectory(filePath);
for (int i = 0; i < instance.playlistList.Count; i++)
{
var playList = instance.playlistList[i];
var jsonString = JsonUtility.ToJson(playList);
Debug.Log("jsonString:"+jsonString);
// Debug.Log(playList.name);
File.WriteAllText($"{filePath}/{i}.json", jsonString);
}
}
public void ExportPlaylist(string name)
{
}
public void ImportAll(bool Overwrite)
{
var directoryPath = EditorUtility.SaveFolderPanel("Import", "", "Playlist");
if (String.IsNullOrEmpty(directoryPath))
return;
if (!Overwrite)
{
Delete();
}
var tabParent = instance.playlist.transform.Find("Canvas/Playlist/Tag/Viewport/Content").gameObject;
var tabPrefab = tabParent.transform.Find("TabPrefab").gameObject;
var listParent = instance.playlist.transform.Find("Canvas/Playlist/List").gameObject;
var listPrefab = listParent.transform.Find("ListPrefab").gameObject;
foreach (var path in Directory.GetFiles(directoryPath))
{
if (!path.Contains("json"))
{
Debug.LogError($"[KineL] Import error! please check your file extention {path}");
return;
}
if(path.Contains("meta"))
continue;
var jsonString = File.ReadAllText(path);
var splitedPath = path.Split(Path.DirectorySeparatorChar);
var str = splitedPath[splitedPath.Length - 1].Split(' ');
var tag = "";
for (int i = 2; i < str.Length; i++)
{
tag = tag + " " + str[i];
}
tag = tag.Replace(".json", "");
bool active = false;
if(!Overwrite)
if (instance.playlistList.Count - 1 == indexSerializedProperty.intValue - 2)
active = true;
var playlistObject = CreateTabPrefab(tag, instance.playlistList.Count, tabPrefab, listPrefab, tabParent, listParent, active);
var playlist = playlistObject.GetComponents<KinelPlaylist>();
JsonUtility.FromJsonOverwrite(jsonString, playlist[0]);
UdonSharpEditorUtility.ConvertToUdonBehaviours(playlist, true);
DestroyImmediate(playlistObject.GetComponents<UdonBehaviour>()[0]);
}
if (!Overwrite)
{
tabPrefab.SetActive(false);
listPrefab.SetActive(false);
}
}
public void Import(string fileName)
{
}
private void ConvertUdonSharpScript(GameObject obj)
{
var playlist = obj.GetComponents<KinelPlaylist>();
UdonSharpEditorUtility.ConvertToUdonBehaviours(playlist, true);
Debug.Log($"{playlist.Length}");
var playlistSerializedObject = new SerializedObject(playlist[0]);
// playlist[0].content =
playlistSerializedObject.FindProperty(nameof(KinelPlaylist.content)).objectReferenceValue
= obj.transform.Find("Viewport/Content").transform as RectTransform;
playlistSerializedObject.FindProperty(nameof(KinelPlaylist.videoPrefab)).objectReferenceValue
= obj.transform.Find("Viewport/VideoPrefab").gameObject;
playlistSerializedObject.FindProperty(nameof(KinelPlaylist.warningUI)).objectReferenceValue
= instance.gameObject.transform.Find("Canvas/WarningImage").gameObject;
playlistSerializedObject.Update();
playlistSerializedObject.FindProperty(nameof(KinelPlaylist.videoPrefab));
playlistSerializedObject.FindProperty(nameof(KinelPlaylist.warningUI));
// ugokanai
// var oldUdon = obj.GetComponents<KinelPlaylist>();
// foreach (var udonSharp in oldUdon)
// {
// DestroyImmediate(udonSharp);
// }
}
}
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment