diff --git a/Assets/Resources/Settings/BackgroundSyncSettings.asset b/Assets/Resources/Settings/BackgroundSyncSettings.asset new file mode 100644 index 00000000..aca408f4 --- /dev/null +++ b/Assets/Resources/Settings/BackgroundSyncSettings.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e62c8b1485e87fad91ea2ffffb70d39ef8e2eb14d1541727fb406b5a1b99422 +size 710 diff --git a/Assets/Resources/Settings/BackgroundSyncSettings.asset.meta b/Assets/Resources/Settings/BackgroundSyncSettings.asset.meta new file mode 100644 index 00000000..f87c9ea6 --- /dev/null +++ b/Assets/Resources/Settings/BackgroundSyncSettings.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 738b59decd79ba245a5ddd6b03d7292c +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/Settings/NotionSyncSettings.asset b/Assets/Resources/Settings/NotionSyncSettings.asset deleted file mode 100644 index e31d14e5..00000000 --- a/Assets/Resources/Settings/NotionSyncSettings.asset +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:18c621c74aaad475f8f0305201cf98e497be47cf7ea066c1e7ce288260168b35 -size 751 diff --git a/Assets/Resources/Settings/NotionSyncSettings.asset.meta b/Assets/Resources/Settings/NotionSyncSettings.asset.meta deleted file mode 100644 index 83de1ff2..00000000 --- a/Assets/Resources/Settings/NotionSyncSettings.asset.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: a1b2c3d4e5f6789012345678abcdef01 -NativeFormatImporter: - externalObjects: {} - mainObjectFileID: 11400000 - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ResourcesData/Background/[아이시아]영재학교 배경/scene/[아이시아]영재학교 배경.png b/Assets/ResourcesData/Background/[아이시아]영재학교 배경/scene/[아이시아]영재학교 배경.png index 2612d752..070e9043 100644 --- a/Assets/ResourcesData/Background/[아이시아]영재학교 배경/scene/[아이시아]영재학교 배경.png +++ b/Assets/ResourcesData/Background/[아이시아]영재학교 배경/scene/[아이시아]영재학교 배경.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f848b0b5a51d8841c7437b20da1bfb8328407cd942369bf882a6742f06020a7 -size 2806900 +oid sha256:422caace36a7c4a088f0fb283857a7dee11b20bac0ec5630f228623b3235ca32 +size 2995718 diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/NotionSyncSettings.cs b/Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/BackgroundSyncSettings.cs similarity index 50% rename from Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/NotionSyncSettings.cs rename to Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/BackgroundSyncSettings.cs index 119800ce..89ab8c2c 100644 --- a/Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/NotionSyncSettings.cs +++ b/Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/BackgroundSyncSettings.cs @@ -4,19 +4,19 @@ using UnityEngine; namespace Streamingle.Background.Editor { /// - /// Notion 동기화 설정을 저장하는 ScriptableObject + /// 배경 동기화 설정을 저장하는 ScriptableObject /// - [CreateAssetMenu(fileName = "NotionSyncSettings", menuName = "Streamingle/Notion Sync Settings")] - public class NotionSyncSettings : ScriptableObject + [CreateAssetMenu(fileName = "BackgroundSyncSettings", menuName = "Streamingle/Background Sync Settings")] + public class BackgroundSyncSettings : ScriptableObject { - [Header("Notion API 설정")] - [Tooltip("Notion Integration Token (ntn_xxx... 또는 secret_xxx...)")] - public string notionApiToken = "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"; + [Header("웹사이트 API 설정")] + [Tooltip("배경 API 엔드포인트 URL (예: https://minglestudio.co.kr/api/backgrounds)")] + public string apiEndpoint = "https://minglestudio.co.kr/api/backgrounds"; - [Tooltip("Notion Database ID (32자리 hex) - 배경 일람")] - public string notionDatabaseId = "25ea7d46b958805f88fcf127979934bf"; + [Tooltip("API 인증 키 (선택사항)")] + public string apiKey = ""; - [Header("Git 설정")] + [Header("Git 설정 (썸네일 URL용)")] [Tooltip("Git 서버 URL (예: https://kindnick-git.duckdns.org)")] public string gitServerUrl = "https://kindnick-git.duckdns.org"; @@ -26,13 +26,6 @@ namespace Streamingle.Background.Editor [Tooltip("Git 브랜치 (예: main)")] public string gitBranch = "main"; - [Header("동기화 설정")] - [Tooltip("썸네일 캡처 시 자동 동기화")] - public bool autoSyncOnCapture = false; - - [Tooltip("배경 씬 로드 시 사용 이력 기록")] - public bool trackUsageHistory = true; - [Header("썸네일 설정")] [Tooltip("썸네일 너비")] public int thumbnailWidth = 1920; @@ -40,13 +33,21 @@ namespace Streamingle.Background.Editor [Tooltip("썸네일 높이 (16:9 비율)")] public int thumbnailHeight = 1080; + [Header("동기화 설정")] + [Tooltip("썸네일 캡처 시 자동 동기화")] + public bool autoSyncOnCapture = false; + + [Header("웹사이트 설정")] + [Tooltip("배경 페이지 URL (브라우저에서 열기용)")] + public string websiteUrl = "https://minglestudio.co.kr/backgrounds"; + /// - /// Git Raw 파일 URL 생성 (Gitea/Gogs 형식) + /// Git Media 파일 URL 생성 (Gitea 형식) /// public string GetGitRawUrl(string assetPath) { - // Assets/ResourcesData/Background/... 형식의 경로를 Git Raw URL로 변환 - // Gitea Raw URL 형식: {serverUrl}/{repoPath}/raw/branch/{branch}/{filePath} + // Assets/ResourcesData/Background/... 형식의 경로를 Git Media URL로 변환 + // Gitea Media URL 형식: {serverUrl}/{repoPath}/media/branch/{branch}/{filePath} string relativePath = assetPath.Replace("\\", "/"); // 경로의 각 세그먼트를 URL 인코딩 (슬래시는 유지) @@ -57,29 +58,17 @@ namespace Streamingle.Background.Editor } string encodedPath = string.Join("/", segments); - return $"{gitServerUrl}/{gitRepoPath}/raw/branch/{gitBranch}/{encodedPath}"; + return $"{gitServerUrl}/{gitRepoPath}/media/branch/{gitBranch}/{encodedPath}"; } /// - /// 설정이 유효한지 확인 + /// API 설정이 유효한지 확인 /// public bool IsValid() { - return !string.IsNullOrEmpty(notionApiToken) && - !string.IsNullOrEmpty(notionDatabaseId) && + return !string.IsNullOrEmpty(apiEndpoint) && !string.IsNullOrEmpty(gitServerUrl) && !string.IsNullOrEmpty(gitRepoPath); } - - /// - /// Notion API Token이 설정되었는지 확인 - /// - public bool HasNotionToken => !string.IsNullOrEmpty(notionApiToken) && - (notionApiToken.StartsWith("secret_") || notionApiToken.StartsWith("ntn_")); - - /// - /// Database ID가 유효한 형식인지 확인 - /// - public bool HasValidDatabaseId => !string.IsNullOrEmpty(notionDatabaseId) && notionDatabaseId.Length >= 32; } } diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/NotionSyncSettings.cs.meta b/Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/BackgroundSyncSettings.cs.meta similarity index 100% rename from Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/NotionSyncSettings.cs.meta rename to Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/BackgroundSyncSettings.cs.meta diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/BackgroundThumbnailCapture.cs b/Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/BackgroundThumbnailCapture.cs index 5dd1474d..f282c028 100644 --- a/Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/BackgroundThumbnailCapture.cs +++ b/Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/BackgroundThumbnailCapture.cs @@ -10,7 +10,7 @@ namespace Streamingle.Background.Editor /// public class BackgroundThumbnailCapture : EditorWindow { - // 16:9 기본값 (Notion용) + // 16:9 기본값 private const int DEFAULT_WIDTH = 1920; private const int DEFAULT_HEIGHT = 1080; @@ -18,7 +18,7 @@ namespace Streamingle.Background.Editor private int _width = DEFAULT_WIDTH; private int _height = DEFAULT_HEIGHT; - private NotionSyncSettings _notionSettings; + private BackgroundSyncSettings _syncSettings; private string _savePath; private string _fileName; private bool _useSceneCamera = true; @@ -42,15 +42,15 @@ namespace Streamingle.Background.Editor _database = AssetDatabase.LoadAssetAtPath( "Assets/Resources/Settings/BackgroundSceneDatabase.asset"); - // Notion 설정 로드 - _notionSettings = AssetDatabase.LoadAssetAtPath( - "Assets/Resources/Settings/NotionSyncSettings.asset"); + // 동기화 설정 로드 + _syncSettings = AssetDatabase.LoadAssetAtPath( + "Assets/Resources/Settings/BackgroundSyncSettings.asset"); // 설정에서 해상도 가져오기 - if (_notionSettings != null) + if (_syncSettings != null) { - _width = _notionSettings.thumbnailWidth; - _height = _notionSettings.thumbnailHeight; + _width = _syncSettings.thumbnailWidth; + _height = _syncSettings.thumbnailHeight; } // 현재 로드된 배경 씬 찾기 @@ -338,7 +338,7 @@ namespace Streamingle.Background.Editor if (GUILayout.Button("960x540")) { _width = 960; _height = 540; } EditorGUILayout.EndHorizontal(); - EditorGUILayout.HelpBox("16:9 비율 썸네일이 권장됩니다.\n에디터에서는 1:1로 크롭되어 표시되고, Notion에는 16:9 원본이 업로드됩니다.", MessageType.Info); + EditorGUILayout.HelpBox("16:9 비율 썸네일이 권장됩니다.\n에디터에서는 1:1로 크롭되어 표시되고, 웹사이트에는 16:9 원본이 사용됩니다.", MessageType.Info); EditorGUILayout.Space(10); @@ -580,33 +580,9 @@ namespace Streamingle.Background.Editor loaderWindow.RefreshThumbnail(assetPath); } - // Notion 자동 동기화 - if (_notionSettings != null && _notionSettings.autoSyncOnCapture && _currentSceneInfo != null) - { - SyncToNotionAsync(_currentSceneInfo); - } - EditorUtility.DisplayDialog("완료", $"썸네일이 저장되었습니다.\n{fullPath}", "확인"); } - private async void SyncToNotionAsync(BackgroundSceneInfo sceneInfo) - { - if (_notionSettings == null || !_notionSettings.IsValid()) - { - UnityEngine.Debug.LogWarning("[Notion] 설정이 올바르지 않아 동기화를 건너뜁니다."); - return; - } - - var sync = new NotionBackgroundSync(_notionSettings, _database); - - sync.OnProgress += (msg) => UnityEngine.Debug.Log($"[Notion] {msg}"); - sync.OnError += (msg) => UnityEngine.Debug.LogError($"[Notion] {msg}"); - sync.OnCompleted += () => UnityEngine.Debug.Log("[Notion] 동기화 완료"); - - await sync.SyncSingleToNotion(sceneInfo); - sync.Dispose(); - } - private void CaptureAllBackgroundScenes() { if (_database == null) diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/NotionBackgroundSync.cs b/Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/NotionBackgroundSync.cs deleted file mode 100644 index 18c1da8e..00000000 --- a/Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/NotionBackgroundSync.cs +++ /dev/null @@ -1,848 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Net.Http; -using System.Text; -using System.Threading.Tasks; -using UnityEditor; -using UnityEngine; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace Streamingle.Background.Editor -{ - /// - /// Notion 배경 씬 동기화 매니저 - /// - public class NotionBackgroundSync - { - private const string NOTION_API_VERSION = "2022-06-28"; - private const string NOTION_API_URL = "https://api.notion.com/v1"; - - private readonly NotionSyncSettings _settings; - private readonly BackgroundSceneDatabase _database; - private readonly HttpClient _httpClient; - - public event Action OnProgress; - public event Action OnError; - public event Action OnCompleted; - - public NotionBackgroundSync(NotionSyncSettings settings, BackgroundSceneDatabase database) - { - _settings = settings; - _database = database; - _httpClient = new HttpClient(); - _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_settings.notionApiToken}"); - _httpClient.DefaultRequestHeaders.Add("Notion-Version", NOTION_API_VERSION); - } - - /// - /// 모든 배경 씬을 Notion에 동기화 - /// - public async Task SyncAllToNotion() - { - if (!_settings.IsValid()) - { - OnError?.Invoke("Notion 설정이 올바르지 않습니다. 설정을 확인하세요."); - return; - } - - try - { - OnProgress?.Invoke("데이터베이스 속성 확인 중..."); - - // 0. 데이터베이스 속성 확인 및 생성 - await EnsureDatabaseProperties(); - - OnProgress?.Invoke("기존 Notion 항목 조회 중..."); - - // 1. 기존 Notion 데이터베이스 항목 조회 - var existingPages = await GetExistingPages(); - - int total = _database.scenes.Count; - int current = 0; - - foreach (var sceneInfo in _database.scenes) - { - current++; - OnProgress?.Invoke($"동기화 중: {sceneInfo.sceneName} ({current}/{total})"); - - // 기존 페이지가 있는지 확인 - string existingPageId = FindExistingPage(existingPages, sceneInfo.scenePath); - - if (existingPageId != null) - { - // 업데이트 - await UpdatePage(existingPageId, sceneInfo); - } - else - { - // 새로 생성 - await CreatePage(sceneInfo); - } - - // API Rate Limit 방지 - await Task.Delay(350); - } - - OnProgress?.Invoke("동기화 완료!"); - OnCompleted?.Invoke(); - } - catch (Exception ex) - { - OnError?.Invoke($"동기화 실패: {ex.Message}"); - UnityEngine.Debug.LogException(ex); - } - } - - /// - /// 단일 배경 씬을 Notion에 동기화 - /// - public async Task SyncSingleToNotion(BackgroundSceneInfo sceneInfo) - { - if (!_settings.IsValid()) - { - OnError?.Invoke("Notion 설정이 올바르지 않습니다."); - return; - } - - try - { - OnProgress?.Invoke("데이터베이스 속성 확인 중..."); - await EnsureDatabaseProperties(); - - OnProgress?.Invoke($"동기화 중: {sceneInfo.sceneName}"); - - var existingPages = await GetExistingPages(); - string existingPageId = FindExistingPage(existingPages, sceneInfo.scenePath); - - if (existingPageId != null) - { - await UpdatePage(existingPageId, sceneInfo); - } - else - { - await CreatePage(sceneInfo); - } - - OnProgress?.Invoke("동기화 완료!"); - OnCompleted?.Invoke(); - } - catch (Exception ex) - { - OnError?.Invoke($"동기화 실패: {ex.Message}"); - } - } - - /// - /// 데이터베이스 속성 확인 및 누락된 속성 생성 - /// - private async Task EnsureDatabaseProperties() - { - // 데이터베이스 정보 조회 - var response = await _httpClient.GetAsync($"{NOTION_API_URL}/databases/{_settings.notionDatabaseId}"); - var responseText = await response.Content.ReadAsStringAsync(); - - if (!response.IsSuccessStatusCode) - { - throw new Exception($"데이터베이스 조회 실패: {responseText}"); - } - - var dbInfo = JObject.Parse(responseText); - var existingProperties = dbInfo["properties"] as JObject; - - // 필요한 속성들 정의 - var requiredProperties = new Dictionary - { - // Title 속성은 이미 존재하므로 이름만 확인 - ["카테고리"] = new JObject { ["select"] = new JObject { ["options"] = new JArray() } }, - ["태그"] = new JObject { ["multi_select"] = new JObject { ["options"] = new JArray() } }, - ["씬 경로"] = new JObject { ["rich_text"] = new JObject() }, - ["폴더 이름"] = new JObject { ["rich_text"] = new JObject() }, - ["썸네일"] = new JObject { ["url"] = new JObject() }, - ["마지막 동기화"] = new JObject { ["date"] = new JObject() }, - ["마지막 사용"] = new JObject { ["date"] = new JObject() } - }; - - // 누락된 속성 찾기 - var propertiesToAdd = new JObject(); - foreach (var prop in requiredProperties) - { - bool found = false; - foreach (var existing in existingProperties) - { - if (existing.Key == prop.Key) - { - found = true; - break; - } - } - - if (!found) - { - propertiesToAdd[prop.Key] = prop.Value; - UnityEngine.Debug.Log($"[Notion] 속성 추가 예정: {prop.Key}"); - } - } - - // 누락된 속성이 있으면 추가 - if (propertiesToAdd.Count > 0) - { - OnProgress?.Invoke($"누락된 속성 {propertiesToAdd.Count}개 생성 중..."); - - var updateBody = new JObject - { - ["properties"] = propertiesToAdd - }; - - var content = new StringContent(updateBody.ToString(), Encoding.UTF8, "application/json"); - var request = new HttpRequestMessage(new HttpMethod("PATCH"), $"{NOTION_API_URL}/databases/{_settings.notionDatabaseId}") - { - Content = content - }; - - var updateResponse = await _httpClient.SendAsync(request); - var updateResponseText = await updateResponse.Content.ReadAsStringAsync(); - - if (!updateResponse.IsSuccessStatusCode) - { - UnityEngine.Debug.LogWarning($"[Notion] 속성 추가 실패 (무시하고 계속): {updateResponseText}"); - } - else - { - UnityEngine.Debug.Log($"[Notion] 데이터베이스 속성 {propertiesToAdd.Count}개 추가됨"); - } - } - - // Title 속성 이름 확인 (기본 "이름" 또는 "Name") - _titlePropertyName = FindTitlePropertyName(existingProperties); - UnityEngine.Debug.Log($"[Notion] Title 속성 이름: {_titlePropertyName}"); - } - - private string _titlePropertyName = "이름"; - - /// - /// Title 타입 속성의 이름 찾기 - /// - private string FindTitlePropertyName(JObject properties) - { - foreach (var prop in properties) - { - var propType = prop.Value["type"]?.Value(); - if (propType == "title") - { - return prop.Key; - } - } - return "이름"; // 기본값 - } - - /// - /// 기존 Notion 페이지 목록 조회 - /// - private async Task> GetExistingPages() - { - var pages = new List(); - string cursor = null; - - do - { - var requestBody = new JObject - { - ["page_size"] = 100 - }; - - if (cursor != null) - { - requestBody["start_cursor"] = cursor; - } - - var content = new StringContent(requestBody.ToString(), Encoding.UTF8, "application/json"); - var response = await _httpClient.PostAsync( - $"{NOTION_API_URL}/databases/{_settings.notionDatabaseId}/query", - content); - - var responseText = await response.Content.ReadAsStringAsync(); - - if (!response.IsSuccessStatusCode) - { - throw new Exception($"Notion API 오류: {responseText}"); - } - - var result = JObject.Parse(responseText); - var results = result["results"] as JArray; - - if (results != null) - { - foreach (var page in results) - { - pages.Add(page as JObject); - } - } - - cursor = result["has_more"]?.Value() == true - ? result["next_cursor"]?.Value() - : null; - - } while (cursor != null); - - return pages; - } - - /// - /// 씬 경로로 기존 페이지 찾기 - /// - private string FindExistingPage(List pages, string scenePath) - { - foreach (var page in pages) - { - var properties = page["properties"] as JObject; - if (properties == null) continue; - - // "씬 경로" 또는 "ScenePath" 속성에서 검색 - var pathProperty = properties["씬 경로"] ?? properties["ScenePath"]; - if (pathProperty == null) continue; - - var richText = pathProperty["rich_text"] as JArray; - if (richText != null && richText.Count > 0) - { - var text = richText[0]["plain_text"]?.Value(); - if (text == scenePath) - { - return page["id"]?.Value(); - } - } - } - - return null; - } - - /// - /// 새 페이지 생성 - /// - private async Task CreatePage(BackgroundSceneInfo sceneInfo) - { - var properties = BuildProperties(sceneInfo); - - var requestBody = new JObject - { - ["parent"] = new JObject { ["database_id"] = _settings.notionDatabaseId }, - ["properties"] = properties - }; - - // 썸네일이 있으면 커버 이미지로 추가 - if (!string.IsNullOrEmpty(sceneInfo.thumbnailPath)) - { - // Git 커밋 상태 확인 - bool isCommitted = CheckIfFileIsCommitted(sceneInfo.thumbnailPath); - if (!isCommitted) - { - UnityEngine.Debug.LogWarning($"[Notion] 썸네일이 Git에 커밋되지 않음: {sceneInfo.thumbnailPath}\n" + - "Notion에서 이미지가 표시되지 않을 수 있습니다. Git commit & push 후 다시 동기화하세요."); - } - - string imageUrl = _settings.GetGitRawUrl(sceneInfo.thumbnailPath); - requestBody["cover"] = new JObject - { - ["type"] = "external", - ["external"] = new JObject { ["url"] = imageUrl } - }; - } - - var content = new StringContent(requestBody.ToString(), Encoding.UTF8, "application/json"); - var response = await _httpClient.PostAsync($"{NOTION_API_URL}/pages", content); - - if (!response.IsSuccessStatusCode) - { - var responseText = await response.Content.ReadAsStringAsync(); - throw new Exception($"페이지 생성 실패: {responseText}"); - } - - UnityEngine.Debug.Log($"[Notion] 페이지 생성됨: {sceneInfo.sceneName}"); - } - - /// - /// 기존 페이지 업데이트 - /// - private async Task UpdatePage(string pageId, BackgroundSceneInfo sceneInfo) - { - var properties = BuildProperties(sceneInfo); - - var requestBody = new JObject - { - ["properties"] = properties - }; - - // 썸네일이 있으면 커버 이미지도 업데이트 - if (!string.IsNullOrEmpty(sceneInfo.thumbnailPath)) - { - // Git 커밋 상태 확인 - bool isCommitted = CheckIfFileIsCommitted(sceneInfo.thumbnailPath); - if (!isCommitted) - { - UnityEngine.Debug.LogWarning($"[Notion] 썸네일이 Git에 커밋되지 않음: {sceneInfo.thumbnailPath}\n" + - "Notion에서 이미지가 표시되지 않을 수 있습니다. Git commit & push 후 다시 동기화하세요."); - } - - string imageUrl = _settings.GetGitRawUrl(sceneInfo.thumbnailPath); - requestBody["cover"] = new JObject - { - ["type"] = "external", - ["external"] = new JObject { ["url"] = imageUrl } - }; - } - - var content = new StringContent(requestBody.ToString(), Encoding.UTF8, "application/json"); - var request = new HttpRequestMessage(new HttpMethod("PATCH"), $"{NOTION_API_URL}/pages/{pageId}") - { - Content = content - }; - - var response = await _httpClient.SendAsync(request); - - if (!response.IsSuccessStatusCode) - { - var responseText = await response.Content.ReadAsStringAsync(); - throw new Exception($"페이지 업데이트 실패: {responseText}"); - } - - UnityEngine.Debug.Log($"[Notion] 페이지 업데이트됨: {sceneInfo.sceneName}"); - } - - /// - /// Notion 속성 빌드 - /// - private JObject BuildProperties(BackgroundSceneInfo sceneInfo) - { - var properties = new JObject(); - - // 이름 (Title) - 동적으로 찾은 속성 이름 사용 - properties[_titlePropertyName] = new JObject - { - ["title"] = new JArray - { - new JObject - { - ["text"] = new JObject { ["content"] = sceneInfo.sceneName } - } - } - }; - - // 카테고리 (Select) - 첫 번째 태그 사용 - string category = ExtractCategory(sceneInfo.categoryName); - properties["카테고리"] = new JObject - { - ["select"] = new JObject { ["name"] = category } - }; - - // 태그 (Multi-Select) - 대괄호 안의 모든 태그 추출 - var tags = ExtractAllTags(sceneInfo.categoryName); - if (tags.Count > 0) - { - var tagArray = new JArray(); - foreach (var tag in tags) - { - tagArray.Add(new JObject { ["name"] = tag }); - } - properties["태그"] = new JObject - { - ["multi_select"] = tagArray - }; - } - - // 씬 경로 (Rich Text) - properties["씬 경로"] = new JObject - { - ["rich_text"] = new JArray - { - new JObject - { - ["text"] = new JObject { ["content"] = sceneInfo.scenePath } - } - } - }; - - // 썸네일 URL (URL) - if (!string.IsNullOrEmpty(sceneInfo.thumbnailPath)) - { - string imageUrl = _settings.GetGitRawUrl(sceneInfo.thumbnailPath); - properties["썸네일"] = new JObject - { - ["url"] = imageUrl - }; - } - - // 폴더 이름 (Rich Text) - properties["폴더 이름"] = new JObject - { - ["rich_text"] = new JArray - { - new JObject - { - ["text"] = new JObject { ["content"] = sceneInfo.categoryName } - } - } - }; - - // 마지막 동기화 (Date) - properties["마지막 동기화"] = new JObject - { - ["date"] = new JObject - { - ["start"] = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss") - } - }; - - return properties; - } - - /// - /// 카테고리 이름에서 첫 번째 태그 부분 추출 (예: "[공용]농구장" → "공용") - /// - private string ExtractCategory(string folderName) - { - if (string.IsNullOrEmpty(folderName)) return "기타"; - - int startBracket = folderName.IndexOf('['); - int endBracket = folderName.IndexOf(']'); - - if (startBracket >= 0 && endBracket > startBracket) - { - return folderName.Substring(startBracket + 1, endBracket - startBracket - 1); - } - - return folderName; - } - - /// - /// 카테고리 이름에서 모든 태그 추출 (예: "[공용][야외]농구장" → ["공용", "야외"]) - /// - private List ExtractAllTags(string folderName) - { - var tags = new List(); - if (string.IsNullOrEmpty(folderName)) return tags; - - int searchStart = 0; - while (searchStart < folderName.Length) - { - int startBracket = folderName.IndexOf('[', searchStart); - if (startBracket < 0) break; - - int endBracket = folderName.IndexOf(']', startBracket); - if (endBracket < 0) break; - - string tag = folderName.Substring(startBracket + 1, endBracket - startBracket - 1); - if (!string.IsNullOrEmpty(tag) && !tags.Contains(tag)) - { - tags.Add(tag); - } - - searchStart = endBracket + 1; - } - - return tags; - } - - /// - /// 파일이 Git에 커밋되어 있는지 확인 - /// - /// Unity Asset 경로 (예: Assets/ResourcesData/...) - /// 커밋 여부 (true: 커밋됨, false: 커밋 안됨 또는 오류) - private bool CheckIfFileIsCommitted(string assetPath) - { - try - { - // Unity 프로젝트 루트 경로 - string projectPath = Path.GetDirectoryName(Application.dataPath); - string filePath = Path.Combine(projectPath, assetPath).Replace("\\", "/"); - - // 파일이 존재하는지 먼저 확인 - if (!File.Exists(filePath)) - { - return false; - } - - // git ls-files로 파일이 추적되고 있는지 확인 - var lsFilesProcess = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = "git", - Arguments = $"ls-files \"{assetPath}\"", - WorkingDirectory = projectPath, - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true, - StandardOutputEncoding = Encoding.UTF8 - } - }; - - lsFilesProcess.Start(); - string lsFilesOutput = lsFilesProcess.StandardOutput.ReadToEnd().Trim(); - lsFilesProcess.WaitForExit(); - - // 파일이 Git에 추적되고 있지 않으면 - if (string.IsNullOrEmpty(lsFilesOutput)) - { - return false; - } - - // git status로 변경 사항 확인 (커밋되지 않은 변경이 있는지) - var statusProcess = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = "git", - Arguments = $"status --porcelain \"{assetPath}\"", - WorkingDirectory = projectPath, - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true, - StandardOutputEncoding = Encoding.UTF8 - } - }; - - statusProcess.Start(); - string statusOutput = statusProcess.StandardOutput.ReadToEnd().Trim(); - statusProcess.WaitForExit(); - - // status 출력이 비어있으면 커밋된 상태 (변경 없음) - // 출력이 있으면 modified/added/untracked 등의 상태 - return string.IsNullOrEmpty(statusOutput); - } - catch (Exception ex) - { - UnityEngine.Debug.LogWarning($"[Notion] Git 상태 확인 실패: {ex.Message}"); - return false; - } - } - - /// - /// 사용 이력 기록 (Notion에) - /// - public async Task RecordUsage(BackgroundSceneInfo sceneInfo) - { - if (!_settings.trackUsageHistory || !_settings.IsValid()) return; - - try - { - var existingPages = await GetExistingPages(); - string pageId = FindExistingPage(existingPages, sceneInfo.scenePath); - - if (pageId != null) - { - // "마지막 사용" 날짜 업데이트 - var properties = new JObject - { - ["마지막 사용"] = new JObject - { - ["date"] = new JObject - { - ["start"] = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss") - } - } - }; - - var requestBody = new JObject { ["properties"] = properties }; - var content = new StringContent(requestBody.ToString(), Encoding.UTF8, "application/json"); - var request = new HttpRequestMessage(new HttpMethod("PATCH"), $"{NOTION_API_URL}/pages/{pageId}") - { - Content = content - }; - - await _httpClient.SendAsync(request); - UnityEngine.Debug.Log($"[Notion] 사용 이력 기록됨: {sceneInfo.sceneName}"); - } - } - catch (Exception ex) - { - UnityEngine.Debug.LogWarning($"[Notion] 사용 이력 기록 실패: {ex.Message}"); - } - } - - public void Dispose() - { - _httpClient?.Dispose(); - } - } - - /// - /// Notion 동기화 에디터 윈도우 - /// - public class NotionSyncWindow : EditorWindow - { - private NotionSyncSettings _settings; - private BackgroundSceneDatabase _database; - private string _statusMessage = ""; - private bool _isSyncing; - private float _progress; - - [MenuItem("Streamingle/Notion Background Sync")] - public static void ShowWindow() - { - var window = GetWindow("Notion 동기화"); - window.minSize = new Vector2(400, 350); - window.Show(); - } - - private void OnEnable() - { - LoadSettings(); - } - - private void LoadSettings() - { - _settings = AssetDatabase.LoadAssetAtPath( - "Assets/Resources/Settings/NotionSyncSettings.asset"); - - _database = AssetDatabase.LoadAssetAtPath( - "Assets/Resources/Settings/BackgroundSceneDatabase.asset"); - } - - private void OnGUI() - { - EditorGUILayout.Space(10); - EditorGUILayout.LabelField("Notion 배경 씬 동기화", EditorStyles.boldLabel); - EditorGUILayout.Space(10); - - // 설정 확인 - if (_settings == null) - { - EditorGUILayout.HelpBox("NotionSyncSettings을 찾을 수 없습니다.\n" + - "Assets/Resources/Settings/NotionSyncSettings.asset을 생성하세요.", MessageType.Warning); - - if (GUILayout.Button("설정 파일 생성")) - { - CreateSettingsFile(); - } - return; - } - - // 설정 표시 - EditorGUILayout.BeginVertical(EditorStyles.helpBox); - EditorGUILayout.LabelField("설정", EditorStyles.boldLabel); - - EditorGUI.BeginChangeCheck(); - - _settings.notionApiToken = EditorGUILayout.PasswordField("Notion API Token", _settings.notionApiToken); - _settings.notionDatabaseId = EditorGUILayout.TextField("Database ID", _settings.notionDatabaseId); - - EditorGUILayout.Space(5); - - _settings.gitServerUrl = EditorGUILayout.TextField("Git Server URL", _settings.gitServerUrl); - _settings.gitRepoPath = EditorGUILayout.TextField("Repo Path", _settings.gitRepoPath); - _settings.gitBranch = EditorGUILayout.TextField("Branch", _settings.gitBranch); - - EditorGUILayout.Space(5); - - _settings.autoSyncOnCapture = EditorGUILayout.Toggle("썸네일 캡처 시 자동 동기화", _settings.autoSyncOnCapture); - _settings.trackUsageHistory = EditorGUILayout.Toggle("사용 이력 추적", _settings.trackUsageHistory); - - if (EditorGUI.EndChangeCheck()) - { - EditorUtility.SetDirty(_settings); - AssetDatabase.SaveAssets(); - } - - EditorGUILayout.EndVertical(); - - // 유효성 검사 - EditorGUILayout.Space(10); - - if (!_settings.HasNotionToken) - { - EditorGUILayout.HelpBox("Notion API Token이 필요합니다.\n" + - "https://www.notion.so/my-integrations 에서 발급하세요.", MessageType.Warning); - } - - if (!_settings.HasValidDatabaseId) - { - EditorGUILayout.HelpBox("Notion Database ID가 필요합니다.\n" + - "데이터베이스 URL에서 32자리 ID를 복사하세요.", MessageType.Warning); - } - - // Git URL 미리보기 - EditorGUILayout.Space(10); - EditorGUILayout.LabelField("이미지 URL 예시:", EditorStyles.boldLabel); - string exampleUrl = _settings.GetGitRawUrl("Assets/ResourcesData/Background/Example/Scene/Example.png"); - EditorGUILayout.SelectableLabel(exampleUrl, EditorStyles.miniLabel, GUILayout.Height(20)); - - // 동기화 버튼 - EditorGUILayout.Space(20); - - GUI.enabled = _settings.IsValid() && !_isSyncing && _database != null; - - if (GUILayout.Button("전체 동기화", GUILayout.Height(35))) - { - SyncAll(); - } - - GUI.enabled = true; - - // 상태 메시지 - if (!string.IsNullOrEmpty(_statusMessage)) - { - EditorGUILayout.Space(10); - EditorGUILayout.HelpBox(_statusMessage, MessageType.Info); - } - - // 데이터베이스 정보 - if (_database != null) - { - EditorGUILayout.Space(10); - EditorGUILayout.LabelField($"배경 씬 수: {_database.scenes.Count}개", EditorStyles.miniLabel); - } - } - - private void CreateSettingsFile() - { - string dirPath = "Assets/Resources/Settings"; - if (!Directory.Exists(dirPath)) - { - Directory.CreateDirectory(dirPath); - } - - _settings = ScriptableObject.CreateInstance(); - AssetDatabase.CreateAsset(_settings, $"{dirPath}/NotionSyncSettings.asset"); - AssetDatabase.SaveAssets(); - - UnityEngine.Debug.Log("NotionSyncSettings 파일이 생성되었습니다."); - } - - private async void SyncAll() - { - _isSyncing = true; - _statusMessage = "동기화 시작..."; - Repaint(); - - var sync = new NotionBackgroundSync(_settings, _database); - - sync.OnProgress += (msg) => - { - _statusMessage = msg; - Repaint(); - }; - - sync.OnError += (msg) => - { - _statusMessage = $"오류: {msg}"; - _isSyncing = false; - Repaint(); - }; - - sync.OnCompleted += () => - { - _statusMessage = "동기화 완료!"; - _isSyncing = false; - Repaint(); - }; - - await sync.SyncAllToNotion(); - - sync.Dispose(); - } - } -} diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/NotionBackgroundSync.cs.meta b/Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/NotionBackgroundSync.cs.meta deleted file mode 100644 index 865884df..00000000 --- a/Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/NotionBackgroundSync.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: e384f532f7d5f744083ecba75388fde0 \ No newline at end of file diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/WebsiteBackgroundExporter.cs b/Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/WebsiteBackgroundExporter.cs new file mode 100644 index 00000000..40eb5b4c --- /dev/null +++ b/Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/WebsiteBackgroundExporter.cs @@ -0,0 +1,406 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; +using UnityEditor; +using UnityEngine; +using UnityEngine.Networking; +using Newtonsoft.Json; + +namespace Streamingle.Background.Editor +{ + /// + /// 배경 씬 데이터를 웹사이트 API로 업로드 + /// 썸네일은 Git URL을 사용 + /// + public class WebsiteBackgroundExporter : EditorWindow + { + private BackgroundSyncSettings _settings; + private BackgroundSceneDatabase _database; + private string _statusMessage = ""; + private MessageType _statusType = MessageType.Info; + private bool _isExporting; + private UnityWebRequestAsyncOperation _currentRequest; + + [MenuItem("Streamingle/Upload Backgrounds to Website")] + public static void ShowWindow() + { + var window = GetWindow("배경 업로드"); + window.minSize = new Vector2(450, 450); + window.Show(); + } + + private void OnEnable() + { + LoadSettings(); + } + + private void LoadSettings() + { + // 새 설정 파일 경로 + _settings = AssetDatabase.LoadAssetAtPath( + "Assets/Resources/Settings/BackgroundSyncSettings.asset"); + + _database = AssetDatabase.LoadAssetAtPath( + "Assets/Resources/Settings/BackgroundSceneDatabase.asset"); + } + + private void OnGUI() + { + EditorGUILayout.Space(10); + EditorGUILayout.LabelField("배경 씬 웹사이트 업로드", EditorStyles.boldLabel); + EditorGUILayout.Space(10); + + // 설정 확인 + if (_settings == null) + { + EditorGUILayout.HelpBox("BackgroundSyncSettings을 찾을 수 없습니다.\nAssets/Resources/Settings/BackgroundSyncSettings.asset 을 생성해주세요.", MessageType.Warning); + + if (GUILayout.Button("설정 파일 생성")) + { + CreateSettingsAsset(); + } + return; + } + + if (_database == null) + { + EditorGUILayout.HelpBox("BackgroundSceneDatabase를 찾을 수 없습니다.", MessageType.Warning); + return; + } + + // API 설정 + EditorGUILayout.BeginVertical(EditorStyles.helpBox); + EditorGUILayout.LabelField("API 설정", EditorStyles.boldLabel); + + EditorGUI.BeginChangeCheck(); + + _settings.apiEndpoint = EditorGUILayout.TextField("API 엔드포인트", _settings.apiEndpoint); + _settings.apiKey = EditorGUILayout.TextField("API 키 (선택)", _settings.apiKey); + + EditorGUILayout.Space(5); + EditorGUILayout.LabelField("Git 설정 (썸네일 URL용)", EditorStyles.boldLabel); + _settings.gitServerUrl = EditorGUILayout.TextField("Git Server URL", _settings.gitServerUrl); + _settings.gitRepoPath = EditorGUILayout.TextField("Repo Path", _settings.gitRepoPath); + _settings.gitBranch = EditorGUILayout.TextField("Branch", _settings.gitBranch); + + EditorGUILayout.Space(5); + _settings.websiteUrl = EditorGUILayout.TextField("웹사이트 URL", _settings.websiteUrl); + + if (EditorGUI.EndChangeCheck()) + { + EditorUtility.SetDirty(_settings); + AssetDatabase.SaveAssets(); + } + + EditorGUILayout.EndVertical(); + + // 설정 유효성 검사 + EditorGUILayout.Space(10); + + if (!_settings.IsValid()) + { + EditorGUILayout.HelpBox("API 엔드포인트와 Git 설정을 입력해주세요.", MessageType.Warning); + } + + // 썸네일 URL 예시 + EditorGUILayout.Space(5); + EditorGUILayout.LabelField("썸네일 URL 예시:", EditorStyles.boldLabel); + string exampleUrl = _settings.GetGitRawUrl("Assets/ResourcesData/Background/[공용]예시/Scene/Example.png"); + EditorGUILayout.SelectableLabel(exampleUrl, EditorStyles.miniLabel, GUILayout.Height(20)); + + // 데이터베이스 정보 + EditorGUILayout.Space(10); + EditorGUILayout.LabelField($"배경 씬 수: {_database.scenes.Count}개", EditorStyles.miniLabel); + + // 업로드 버튼 + EditorGUILayout.Space(20); + + GUI.enabled = _settings.IsValid() && !_isExporting; + + if (GUILayout.Button("웹사이트에 업로드", GUILayout.Height(35))) + { + UploadToWebsite(); + } + + EditorGUILayout.Space(5); + + if (GUILayout.Button("업로드 후 브라우저에서 열기", GUILayout.Height(30))) + { + UploadToWebsite(() => + { + if (!string.IsNullOrEmpty(_settings.websiteUrl)) + { + Application.OpenURL(_settings.websiteUrl); + } + }); + } + + EditorGUILayout.Space(5); + + if (GUILayout.Button("API 연결 테스트", GUILayout.Height(25))) + { + TestApiConnection(); + } + + GUI.enabled = true; + + // 상태 메시지 + if (!string.IsNullOrEmpty(_statusMessage)) + { + EditorGUILayout.Space(10); + EditorGUILayout.HelpBox(_statusMessage, _statusType); + } + } + + private void CreateSettingsAsset() + { + // Settings 폴더 확인 + if (!AssetDatabase.IsValidFolder("Assets/Resources")) + { + AssetDatabase.CreateFolder("Assets", "Resources"); + } + if (!AssetDatabase.IsValidFolder("Assets/Resources/Settings")) + { + AssetDatabase.CreateFolder("Assets/Resources", "Settings"); + } + + // 새 설정 파일 생성 + var settings = CreateInstance(); + AssetDatabase.CreateAsset(settings, "Assets/Resources/Settings/BackgroundSyncSettings.asset"); + AssetDatabase.SaveAssets(); + + _settings = settings; + UnityEngine.Debug.Log("[WebsiteExporter] BackgroundSyncSettings 생성됨"); + } + + private void UploadToWebsite(Action onSuccess = null) + { + _isExporting = true; + _statusMessage = "업로드 준비 중..."; + _statusType = MessageType.Info; + + try + { + // JSON 데이터 생성 + var exportData = new WebsiteBackgroundData + { + lastUpdated = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss"), + backgrounds = new List() + }; + + foreach (var sceneInfo in _database.scenes) + { + var item = new WebsiteBackgroundItem + { + sceneName = sceneInfo.sceneName, + scenePath = sceneInfo.scenePath, + categoryName = sceneInfo.categoryName, + category = ExtractCategory(sceneInfo.categoryName), + tags = ExtractAllTags(sceneInfo.categoryName), + thumbnailUrl = !string.IsNullOrEmpty(sceneInfo.thumbnailPath) + ? _settings.GetGitRawUrl(sceneInfo.thumbnailPath) + : null + }; + + exportData.backgrounds.Add(item); + } + + // JSON 문자열 생성 + string json = JsonConvert.SerializeObject(exportData, Formatting.Indented); + + // HTTP POST 요청 + SendPostRequest(json, onSuccess); + } + catch (Exception ex) + { + _statusMessage = $"데이터 준비 실패: {ex.Message}"; + _statusType = MessageType.Error; + _isExporting = false; + UnityEngine.Debug.LogError($"[WebsiteExporter] 데이터 준비 실패: {ex.Message}"); + } + } + + private void SendPostRequest(string jsonData, Action onSuccess) + { + _statusMessage = "서버에 업로드 중..."; + + var request = new UnityWebRequest(_settings.apiEndpoint, "POST"); + byte[] bodyRaw = Encoding.UTF8.GetBytes(jsonData); + request.uploadHandler = new UploadHandlerRaw(bodyRaw); + request.downloadHandler = new DownloadHandlerBuffer(); + request.SetRequestHeader("Content-Type", "application/json"); + + if (!string.IsNullOrEmpty(_settings.apiKey)) + { + request.SetRequestHeader("X-API-Key", _settings.apiKey); + } + + _currentRequest = request.SendWebRequest(); + _currentRequest.completed += operation => + { + HandleUploadResponse(request, onSuccess); + }; + + // 진행 상황 업데이트를 위한 에디터 갱신 + EditorApplication.update += UpdateProgress; + } + + private void UpdateProgress() + { + if (_currentRequest != null && !_currentRequest.isDone) + { + _statusMessage = $"업로드 중... {(_currentRequest.progress * 100):F0}%"; + Repaint(); + } + else + { + EditorApplication.update -= UpdateProgress; + } + } + + private void HandleUploadResponse(UnityWebRequest request, Action onSuccess) + { + _isExporting = false; + + if (request.result == UnityWebRequest.Result.Success) + { + try + { + var response = JsonConvert.DeserializeObject(request.downloadHandler.text); + if (response.success) + { + _statusMessage = $"업로드 완료!\n{response.message}\n업데이트: {response.lastUpdated}"; + _statusType = MessageType.Info; + UnityEngine.Debug.Log($"[WebsiteExporter] 업로드 성공: {response.message}"); + onSuccess?.Invoke(); + } + else + { + _statusMessage = $"서버 오류: {response.error}"; + _statusType = MessageType.Error; + } + } + catch (Exception ex) + { + _statusMessage = $"응답 파싱 실패: {ex.Message}\n응답: {request.downloadHandler.text}"; + _statusType = MessageType.Error; + } + } + else + { + _statusMessage = $"업로드 실패!\n{request.error}\n상태코드: {request.responseCode}"; + _statusType = MessageType.Error; + UnityEngine.Debug.LogError($"[WebsiteExporter] 업로드 실패: {request.error}"); + } + + request.Dispose(); + Repaint(); + } + + private void TestApiConnection() + { + _statusMessage = "API 연결 테스트 중..."; + _statusType = MessageType.Info; + + var request = UnityWebRequest.Get(_settings.apiEndpoint); + var operation = request.SendWebRequest(); + + operation.completed += op => + { + if (request.result == UnityWebRequest.Result.Success) + { + _statusMessage = $"API 연결 성공!\n응답: {request.downloadHandler.text.Substring(0, Math.Min(200, request.downloadHandler.text.Length))}..."; + _statusType = MessageType.Info; + } + else + { + _statusMessage = $"API 연결 실패!\n{request.error}\n상태코드: {request.responseCode}"; + _statusType = MessageType.Error; + } + + request.Dispose(); + Repaint(); + }; + } + + /// + /// 카테고리 추출 (첫 번째 대괄호) + /// + private string ExtractCategory(string folderName) + { + if (string.IsNullOrEmpty(folderName)) return "기타"; + + int startBracket = folderName.IndexOf('['); + int endBracket = folderName.IndexOf(']'); + + if (startBracket >= 0 && endBracket > startBracket) + { + return folderName.Substring(startBracket + 1, endBracket - startBracket - 1); + } + + return folderName; + } + + /// + /// 모든 태그 추출 (대괄호 안의 모든 텍스트) + /// + private List ExtractAllTags(string folderName) + { + var tags = new List(); + if (string.IsNullOrEmpty(folderName)) return tags; + + var matches = Regex.Matches(folderName, @"\[([^\]]+)\]"); + foreach (Match match in matches) + { + if (match.Groups.Count > 1) + { + string tag = match.Groups[1].Value; + if (!tags.Contains(tag)) + { + tags.Add(tag); + } + } + } + + return tags; + } + } + + /// + /// API 응답 구조 + /// + [Serializable] + public class ApiResponse + { + public bool success; + public string message; + public string error; + public string lastUpdated; + public int count; + } + + /// + /// 웹사이트용 배경 데이터 구조 + /// + [Serializable] + public class WebsiteBackgroundData + { + public string lastUpdated; + public List backgrounds; + } + + /// + /// 웹사이트용 개별 배경 항목 + /// + [Serializable] + public class WebsiteBackgroundItem + { + public string sceneName; + public string scenePath; + public string categoryName; + public string category; + public List tags; + public string thumbnailUrl; + } +} diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/WebsiteBackgroundExporter.cs.meta b/Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/WebsiteBackgroundExporter.cs.meta new file mode 100644 index 00000000..c0f7aadd --- /dev/null +++ b/Assets/Scripts/Streamingle/StreamingleControl/Background/Editor/WebsiteBackgroundExporter.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 7584b72e68020dc499d99b00273dc567 \ No newline at end of file