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