Last active
August 30, 2021 17:59
-
-
Save aki-lua87/b33665836c4c330f07968922d86fb2e9 to your computer and use it in GitHub Desktop.
YoutubeRSSPlaylistGenerate
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 && !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