@@ -17,13 +17,30 @@ public static class GetUnityUpdates
1717 private const string CacheFileName = "UnityVersionCache.json" ;
1818 private static readonly HttpClient Client = new HttpClient ( ) ;
1919
20+ static Dictionary < string , string > unofficialReleaseURLs = new Dictionary < string , string > ( ) ;
21+
2022 public static async Task < List < UnityVersion > > FetchAll ( )
2123 {
2224 var cachedVersions = LoadCachedVersions ( ) ;
2325 var newVersions = await FetchNewVersions ( cachedVersions ) ;
2426
25- var allVersions = newVersions . Concat ( cachedVersions ) . ToList ( ) ;
27+ var unofficialVersions = await FetchUnofficialVersions ( cachedVersions ) ;
28+
29+ unofficialReleaseURLs . Clear ( ) ;
30+ // TODO modify FetchUnofficialVersions to put items in this dictionary directly
31+ foreach ( var version in unofficialVersions )
32+ {
33+ //Console.WriteLine("unofficial: " + version.Version + " , " + version.directURL);
34+ if ( unofficialReleaseURLs . ContainsKey ( version . Version ) == false )
35+ {
36+ unofficialReleaseURLs . Add ( version . Version , version . directURL ) ;
37+ }
38+ }
39+
40+ var allVersions = newVersions . Concat ( unofficialVersions ) . Concat ( cachedVersions ) . ToList ( ) ;
41+ //var allVersions = newVersions.Concat(cachedVersions).ToList();
2642
43+ // TODO save unofficial versions to cache also? or maybe not, they will appear in official list later
2744 if ( newVersions . Count > 0 )
2845 {
2946 SaveCachedVersions ( allVersions ) ;
@@ -32,13 +49,89 @@ public static async Task<List<UnityVersion>> FetchAll()
3249 return allVersions ;
3350 }
3451
52+ public static async Task < List < UnityVersion > > FetchUnofficialVersions ( List < UnityVersion > cachedVersions )
53+ {
54+ var unofficialVersions = new List < UnityVersion > ( ) ;
55+ var existingVersions = new HashSet < string > ( cachedVersions . Select ( v => v . Version ) ) ;
56+
57+ try
58+ {
59+ string url = "https://raw.githubusercontent.com/unitycoder/UnofficialUnityReleasesWatcher/refs/heads/main/unity-releases.md" ;
60+
61+ var content = await Client . GetStringAsync ( url ) ;
62+
63+ // Parse the Markdown content
64+ var lines = content . Split ( new [ ] { '\n ' , '\r ' } , StringSplitOptions . RemoveEmptyEntries ) ;
65+ foreach ( var line in lines )
66+ {
67+ if ( line . StartsWith ( "- " ) ) // Identify Markdown list items
68+ {
69+ var urlPart = line . Substring ( 2 ) . Trim ( ) ;
70+ var version = ExtractVersionFromUrl ( urlPart ) ;
71+
72+ if ( ! string . IsNullOrEmpty ( version ) && ! existingVersions . Contains ( version ) )
73+ {
74+ var stream = InferStreamFromVersion ( version ) ;
75+
76+ unofficialVersions . Add ( new UnityVersion
77+ {
78+ Version = version ,
79+ Stream = stream ,
80+ ReleaseDate = DateTime . Now , // NOTE not correct, but we don't have known release date for unofficial versions (its only when they are found..)
81+ //ReleaseDate = DateTime.MinValue // Release date is unavailable in the MD format, TODO add to md as #2021-01-01 ?
82+ directURL = urlPart , // this is available only for unofficial releases
83+ } ) ;
84+ }
85+ }
86+ }
87+ }
88+ catch ( Exception ex )
89+ {
90+ Console . WriteLine ( $ "Error fetching unofficial versions: { ex . Message } ") ;
91+ }
92+
93+ return unofficialVersions ;
94+ }
95+
96+ // TODO fixme, f is not always LTS
97+ private static UnityVersionStream InferStreamFromVersion ( string version )
98+ {
99+ if ( Tools . IsAlpha ( version ) ) return UnityVersionStream . Alpha ;
100+ if ( Tools . IsBeta ( version ) ) return UnityVersionStream . Beta ;
101+ if ( Tools . IsLTS ( version ) ) return UnityVersionStream . LTS ;
102+
103+ //if (version.Contains("a")) return UnityVersionStream.Alpha;
104+ //if (version.Contains("b")) return UnityVersionStream.Beta;
105+ //if (version.Contains("f")) return UnityVersionStream.LTS;
106+ return UnityVersionStream . Tech ; // Default to Tech if no identifier is found
107+ }
108+
109+ /// <summary>
110+ /// Extracts the Unity version from the given URL.
111+ /// </summary>
112+ /// <param name="url">The URL to parse.</param>
113+ /// <returns>The Unity version string.</returns>
114+ private static string ExtractVersionFromUrl ( string url )
115+ {
116+ try
117+ {
118+ var versionStart = url . LastIndexOf ( '#' ) + 1 ;
119+ return versionStart > 0 && versionStart < url . Length ? url . Substring ( versionStart ) : null ;
120+ }
121+ catch
122+ {
123+ return null ;
124+ }
125+ }
126+
35127 public static async Task < string > FetchDownloadUrl ( string unityVersion )
36128 {
37129 if ( string . IsNullOrEmpty ( unityVersion ) )
38130 {
39131 return null ;
40132 }
41133
134+ // unity release api
42135 string apiUrl = $ "{ BaseApiUrl } ?limit=1&version={ unityVersion } &architecture=X86_64&platform=WINDOWS";
43136
44137 try
@@ -53,8 +146,37 @@ public static async Task<string> FetchDownloadUrl(string unityVersion)
53146 }
54147 }
55148
149+ static string ParseHashCodeFromURL ( string url )
150+ {
151+ // https://beta.unity3d.com/download/330fbefc18b7/download.html#6000.1.0a8 > 330fbefc18b7
152+
153+ int hashStart = url . IndexOf ( "download/" ) + 9 ;
154+ int hashEnd = url . IndexOf ( "/download.html" , hashStart ) ;
155+ return url . Substring ( hashStart , hashEnd - hashStart ) ;
156+ }
157+
56158 private static async Task < string > ExtractDownloadUrlAsync ( string json , string unityVersion )
57159 {
160+ //Console.WriteLine("json: " + json + " vers: " + unityVersion);
161+
162+ if ( json . Contains ( "\" results\" :[]" ) )
163+ {
164+ Console . WriteLine ( "No results found from releases API, checking unofficial list (if enabled)" ) ;
165+
166+ if ( unofficialReleaseURLs . ContainsKey ( unityVersion ) )
167+ {
168+ Console . WriteLine ( "Unofficial release found in the list." ) ;
169+
170+ string unityHash = ParseHashCodeFromURL ( unofficialReleaseURLs [ unityVersion ] ) ;
171+ // Console.WriteLine(unityHash);
172+ string downloadURL = Tools . ParseDownloadURLFromWebpage ( unityVersion , unityHash , false , true ) ;
173+ // Console.WriteLine("direct download url: "+downloadURL);
174+ return downloadURL ;
175+ }
176+
177+ return null ;
178+ }
179+
58180 int resultsIndex = json . IndexOf ( "\" results\" :" ) ;
59181 if ( resultsIndex == - 1 ) return null ;
60182
@@ -84,7 +206,7 @@ private static async Task<string> ExtractDownloadUrlAsync(string json, string un
84206
85207 if ( await CheckAssistantUrl ( assistantUrl ) )
86208 {
87- Console . WriteLine ( "Assistant download URL found." ) ;
209+ // Console.WriteLine("ExtractDownloadUrlAsync: Assistant download URL found.");
88210 return assistantUrl ;
89211 }
90212 else
@@ -279,6 +401,7 @@ private static void SaveCachedVersions(List<UnityVersion> versions)
279401 if ( configDirectory == null ) return ;
280402
281403 string cacheFilePath = Path . Combine ( configDirectory , CacheFileName ) ;
404+ //Console.WriteLine("Saving cachedrelease: " + cacheFilePath);
282405 File . WriteAllText ( cacheFilePath , json ) ;
283406 }
284407 }
0 commit comments