using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Networking; using Newtonsoft.Json; using Unity.EditorCoroutines.Editor; using UnityEditor; namespace AlicizaX.PackageManager.Editor { public enum PackageState { UnInstall, InstallLocal, Install, Update } [Serializable] public class RepositoryPackageData { // Repository info public string name; public string description; public string cloneUrl; public string htmlUrl; public string updatedAt; // Package info public string displayName; public string version; public string unityVersion; public string category; public string packageDescription; public string authorName; public PackageState PackageState; } [Serializable] public class RepoApiResponse { public bool ok; public List data; } [Serializable] public class RepoItem { public string name; public string description; public string clone_url; public string html_url; public string updated_at; public Owner owner; [Serializable] public class Owner { public string login; } } [Serializable] public class PackageJsonResponse { public string name; public string displayName; public string category; public string description; public string version; public string unity; public Author author; [Serializable] public class Author { public string name; } } public static class RepositoryDataFetcher { private const string BaseApiUrl = "http://101.34.252.46:3000/api/v1/repos/search?q=unity&topic=false"; private const string PackageJsonPath = "/raw/branch/main/package.json"; public static void FetchRepositoryData(Action> callback) { EditorPrefs.SetString("PackageUpdateDate", DateTime.Now.ToString()); EditorCoroutineUtility.StartCoroutineOwnerless(FetchDataRoutine(callback)); } private static IEnumerator FetchDataRoutine(Action> callback) { // 第一阶段:获取仓库列表 using (var request = CreateWebRequest(BaseApiUrl)) { yield return request.SendWebRequest(); if (!HandleRequestError(request, "Repository request failed")) { callback?.Invoke(null); yield break; } var repoResponse = JsonConvert.DeserializeObject(request.downloadHandler.text); if (!repoResponse.ok || repoResponse.data == null) { Debug.LogError("Invalid repository response data"); callback?.Invoke(null); yield break; } var results = new List(); var pendingRequests = new Queue(repoResponse.data); // 第二阶段:逐个获取package.json信息 while (pendingRequests.Count > 0) { var repo = pendingRequests.Dequeue(); var packageData = CreateBaseRepositoryData(repo); var packageUrl = repo.html_url + PackageJsonPath; using (var packageRequest = CreateWebRequest(packageUrl)) { yield return packageRequest.SendWebRequest(); if (HandleRequestError(packageRequest, $"Package request failed for {repo.name}")) { try { var packageInfo = JsonConvert.DeserializeObject( packageRequest.downloadHandler.text); UpdateWithPackageInfo(ref packageData, packageInfo); } catch (Exception e) { Debug.LogError($"JSON parse error for {repo.name}: {e.Message}"); } } results.Add(packageData); } // 防止同时发起太多请求,每帧处理一个 yield return null; } callback?.Invoke(results); } } private static UnityWebRequest CreateWebRequest(string url) { var request = new UnityWebRequest(url, "GET"); request.downloadHandler = new DownloadHandlerBuffer(); request.SetRequestHeader("accept", "application/json"); return request; } private static bool HandleRequestError(UnityWebRequest request, string errorMessage) { if (request.result != UnityWebRequest.Result.Success) { Debug.LogError($"{errorMessage}: {request.error}"); return false; } return true; } private static RepositoryPackageData CreateBaseRepositoryData(RepoItem repo) { return new RepositoryPackageData { name = repo.name, description = repo.description, cloneUrl = repo.clone_url, htmlUrl = repo.html_url, updatedAt = repo.updated_at }; } private static void UpdateWithPackageInfo(ref RepositoryPackageData data, PackageJsonResponse package) { if (package == null) return; data.name = package.name; data.displayName = package.displayName; data.version = package.version; data.unityVersion = package.unity; data.category = package.category; data.packageDescription = package.description; if (package.author != null) { data.authorName = package.author.name; } } } }