diff --git a/Assets/RothenburgAR/Scripts/Exhibition/ExhibitionApiDataPreloader.cs b/Assets/RothenburgAR/Scripts/Exhibition/ExhibitionApiDataPreloader.cs index d041a53..fdc5f58 100644 --- a/Assets/RothenburgAR/Scripts/Exhibition/ExhibitionApiDataPreloader.cs +++ b/Assets/RothenburgAR/Scripts/Exhibition/ExhibitionApiDataPreloader.cs @@ -36,14 +36,14 @@ namespace RothenburgAR.Exhibition { //throw new System.NotImplementedException(); - Dictionary> exhibits = new Dictionary>(); + Dictionary> exhibits = new Dictionary>(); var languageDirs = new DirectoryInfo(exhibitionDirectory).GetDirectories().Select(d => d.Name).ToList(); foreach (var lang in languageDirs) { var metaFilePath = PathHelper.CombinePaths(exhibitionDirectory, lang, "meta.json"); if (!File.Exists(metaFilePath)) continue; - var exhibitList = JsonConvert.DeserializeObject>(File.ReadAllText(metaFilePath, System.Text.Encoding.UTF8)); + var exhibitList = JsonConvert.DeserializeObject>(File.ReadAllText(metaFilePath, System.Text.Encoding.UTF8)); exhibits.Add(lang, exhibitList); } @@ -65,7 +65,7 @@ namespace RothenburgAR.Exhibition return resultExhibition; } - private PreloadedExhibit PreloadExhibit(Dictionary> exhibits, string exhibitId) + private PreloadedExhibit PreloadExhibit(Dictionary> exhibits, string exhibitId) { PreloadedExhibit newExhibit = new PreloadedExhibit(); diff --git a/Assets/RothenburgAR/Scripts/PointOfInterest/PoiApiDataPreloader.cs b/Assets/RothenburgAR/Scripts/PointOfInterest/PoiApiDataPreloader.cs index 644a93c..5b47107 100644 --- a/Assets/RothenburgAR/Scripts/PointOfInterest/PoiApiDataPreloader.cs +++ b/Assets/RothenburgAR/Scripts/PointOfInterest/PoiApiDataPreloader.cs @@ -31,20 +31,20 @@ namespace RothenburgAR.PointOfInterest public List PreloadPoi(string poiDirectory) { List preloadedPois = new List(); - Dictionary> exhibits = new Dictionary>(); + Dictionary> exhibits = new Dictionary>(); var languageDirs = new DirectoryInfo(poiDirectory).GetDirectories().Select(d => d.Name).ToList(); foreach (var lang in languageDirs) { var metaFilePath = PathHelper.CombinePaths(poiDirectory, lang, "meta.json"); if (!File.Exists(metaFilePath)) continue; - var exhibitList = JsonConvert.DeserializeObject>(File.ReadAllText(metaFilePath, System.Text.Encoding.UTF8)); + var exhibitList = JsonConvert.DeserializeObject>(File.ReadAllText(metaFilePath, System.Text.Encoding.UTF8)); exhibits.Add(lang, exhibitList); } var poiIds = exhibits.Values.SelectMany(l => l.SelectMany(e => e.Pois.Select(p => p.Id))).Distinct().ToList(); - Dictionary> poisPerLang = new Dictionary>(); + Dictionary> poisPerLang = new Dictionary>(); exhibits.Keys.ToList().ForEach(k => poisPerLang.Add(k, exhibits[k].SelectMany(l => l.Pois.Select(p => p)).Distinct().ToList())); diff --git a/Assets/RothenburgAR/Scripts/Updater/ApiInfo.cs b/Assets/RothenburgAR/Scripts/Updater/ApiInfo.cs index 783ec17..f3c1158 100644 --- a/Assets/RothenburgAR/Scripts/Updater/ApiInfo.cs +++ b/Assets/RothenburgAR/Scripts/Updater/ApiInfo.cs @@ -29,36 +29,39 @@ namespace RothenburgAR.Updater } [Serializable] - public class VersioncheckAnswer + public class ApiVersioncheckAnswer { [JsonProperty("languages")] public List Languages { get; set; } [JsonProperty("data")] - public List Data { get; set; } + public List Data { get; set; } } [Serializable] - public class ExhibitionVersion + public class ApiExhibitionVersion { [JsonProperty("id")] public string Id { get; set; } [JsonProperty("meta")] - public VersionInfo Meta { get; set; } + public ApiVersionInfo Meta { get; set; } [JsonProperty("tracker")] - public VersionInfo Tracker { get; set; } + public ApiVersionInfo Tracker { get; set; } } [Serializable] - public class VersionInfo + public class ApiVersionInfo { [JsonProperty("status")] public VersionStatus Status { get; set; } [JsonProperty("updateUrl")] public string UpdateUrl { get; set; } + + [JsonProperty("updateVersion")] + public long UpdateVersion { get; set; } } public enum VersionStatus @@ -68,7 +71,7 @@ namespace RothenburgAR.Updater deleted } - public class Exhibit + public class ApiExhibit { [JsonProperty("id")] public string Id { get; set; } @@ -95,10 +98,10 @@ namespace RothenburgAR.Updater public long UpdatedTime { get; set; } [JsonProperty("pois")] - public List Pois { get; set; } + public List Pois { get; set; } } - public class Poi + public class ApiPoi { [JsonProperty("id")] public string Id { get; set; } diff --git a/Assets/RothenburgAR/Scripts/Updater/HttpHandler.cs b/Assets/RothenburgAR/Scripts/Updater/HttpHandler.cs index eea8faf..6986f0b 100644 --- a/Assets/RothenburgAR/Scripts/Updater/HttpHandler.cs +++ b/Assets/RothenburgAR/Scripts/Updater/HttpHandler.cs @@ -13,6 +13,8 @@ namespace RothenburgAR.Updater private readonly HttpVerb verb; private string uploadData; + private Dictionary headerEntries = new Dictionary(); + public HttpRequest(String url, HttpVerb verb, String uploadData = null) { this.url = url; @@ -20,17 +22,27 @@ namespace RothenburgAR.Updater this.uploadData = uploadData; } + public HttpRequest WithHeader(string header, string value) + { + headerEntries.Add(header, value); + return this; + } + public HttpHandler Send() { Debug.Log(string.Format("{1}-Downloading {0}", url, Time.realtimeSinceStartup)); DownloadHandler dl = new DownloadHandlerBuffer(); UploadHandlerRaw ul = null; - if (uploadData != null) { ul = new UploadHandlerRaw(Encoding.UTF8.GetBytes(uploadData.ToCharArray())); } - + if (uploadData != null && uploadData != string.Empty) + { + ul = new UploadHandlerRaw(Encoding.UTF8.GetBytes(uploadData.ToCharArray())); + } + string verb = Enum.GetName(typeof(HttpVerb), this.verb); - + UnityWebRequest wr = new UnityWebRequest(url, verb, dl, ul); wr.SetRequestHeader("Content-Type", "application/json;charset=UTF-8"); + foreach (var entry in headerEntries) { wr.SetRequestHeader(entry.Key, entry.Value); } var operation = wr.SendWebRequest(); @@ -38,7 +50,7 @@ namespace RothenburgAR.Updater } } - public enum HttpVerb + public enum HttpVerb { GET, POST, @@ -51,7 +63,7 @@ namespace RothenburgAR.Updater public UploadHandler Upload { get; set; } public DownloadHandler Download { get; set; } public UnityWebRequest Request { get; set; } - public UnityWebRequestAsyncOperation Operation {get;set;} + public UnityWebRequestAsyncOperation Operation { get; set; } public bool IsDone { diff --git a/Assets/RothenburgAR/Scripts/Updater/UpdaterBehaviour.cs b/Assets/RothenburgAR/Scripts/Updater/UpdaterBehaviour.cs index 41cc052..5bf10a2 100644 --- a/Assets/RothenburgAR/Scripts/Updater/UpdaterBehaviour.cs +++ b/Assets/RothenburgAR/Scripts/Updater/UpdaterBehaviour.cs @@ -20,7 +20,7 @@ namespace RothenburgAR.Updater private readonly float afterDownloadWaitTime = 10f; - public VersioncheckAnswer VersionAnswer { get; set; } + public ApiVersioncheckAnswer VersionAnswer { get; set; } private List httpHandlers = new List(); private LogFileHandler logFileHandler; @@ -69,8 +69,7 @@ namespace RothenburgAR.Updater Debug.Log(http.Download.text); - VersionAnswer = JsonConvert.DeserializeObject(http.Download.text); - //VersionAnswer = JsonConvert.DeserializeObject(@"{""languages"":[""de"",""en""],""data"":[{""id"":""006e164c-5e31-4ddf-adf5-df7016c8b3a8"",""meta"":{""status"":""ok"",""updateUrl"":""https://lambdalike.pa.kaim.network/meta/006e164c-5e31-4ddf-adf5-df7016c8b3a7/{lang}""},""tracker"":{""status"":""ok"",""updateUrl"":""https://lambdalike.pa.kaim.network/meta/006e164c-5e31-4ddf-adf5-df7016c8b3a7/{lang}""}},{""id"":""006e164c-5e31-4ddf-adf5-df7016c8b3a7"",""meta"":{""status"":""updated"",""updateUrl"":""https://lambdalike.pa.kaim.network/meta/006e164c-5e31-4ddf-adf5-df7016c8b3a7/{lang}""},""tracker"":{""status"":""ok"",""updateUrl"":""https://lambdalike.pa.kaim.network/meta/006e164c-5e31-4ddf-adf5-df7016c8b3a7/{lang}""}}]}"); + VersionAnswer = JsonConvert.DeserializeObject(http.Download.text); Debug.Log(string.Format("{1}-DONE with {0}", ApiInfo.VersionCheckEndpoint, Time.realtimeSinceStartup)); var answer = VersionAnswer; @@ -106,24 +105,15 @@ namespace RothenburgAR.Updater } var exhibitionDirs = rootDir.GetDirectories().ToList(); - exhibitionDirs.ForEach(dir => + + foreach (var dir in exhibitionDirs) { - var subdir = dir.GetDirectories().ToList().FirstOrDefault(); - if (subdir == null) return; + var versionFilePath = Path.Combine(dir.FullName, "version.txt"); + if (!File.Exists(versionFilePath)) continue; - var metaFilePath = Path.Combine(subdir.FullName, "meta.json"); - if (!File.Exists(metaFilePath)) return; - - var exhibits = JsonConvert.DeserializeObject>(File.ReadAllText(metaFilePath)); - long newestVersion = 0; - foreach (var exhibit in exhibits) - { - newestVersion = Math.Max(newestVersion, exhibit.UpdatedTime); - //result += @"""{id}"":""{version}"",".Replace("{id}", exhibit.Id).Replace("{version}", exhibit.UpdatedTime.ToString()); - } - - result += @"""{id}"":""{version}"",".Replace("{id}", dir.Name).Replace("{version}", newestVersion.ToString()); - }); + var version = long.Parse(File.ReadAllText(versionFilePath)); + result += @"""{id}"":""{version}"",".Replace("{id}", dir.Name).Replace("{version}", version.ToString()); + }; result = result.Substring(0, result.Length - 1) + "}"; return result; @@ -134,7 +124,7 @@ namespace RothenburgAR.Updater /* * dir structure: * data - * exhibitions + * exhibition * id * tracker.dat * tracker.xml @@ -181,20 +171,28 @@ namespace RothenburgAR.Updater deletedData.ForEach(d => DeleteExhibition(d)); } - private void DeleteExhibition(ExhibitionVersion d) + private void DeleteExhibition(ApiExhibitionVersion exhibition) { - //TODO implement DeleteExhibition - throw new NotImplementedException(); + var path = Path.Combine(PathHelper.ExhibitionPath, exhibition.Id); + Directory.Delete(path, true); } - private void UpdateMeta(ExhibitionVersion exhibition) + private void UpdateMeta(ApiExhibitionVersion exhibition) { + var versionData = new UpdateVersionFileCoroutineData + { + path = PathHelper.CombinePaths(PathHelper.ExhibitionPath, exhibition.Id, "version.txt"), + httpHandlers = new List(), + version = exhibition.Meta.UpdateVersion.ToString() + }; + foreach (var lang in VersionAnswer.Languages) { var path = PathHelper.CombinePaths(PathHelper.ExhibitionPath, exhibition.Id, lang); var url = exhibition.Meta.UpdateUrl.Replace("{lang}", lang); var http = new HttpRequest(url, HttpVerb.GET).Send(); httpHandlers.Add(http); + versionData.httpHandlers.Add(http); http.Operation.completed += ar => { @@ -211,7 +209,7 @@ namespace RothenburgAR.Updater File.WriteAllText(Path.Combine(path, "meta.json"), http.Download.text, System.Text.Encoding.UTF8); Debug.Log(string.Format("{1}-DONE with {0}", url, Time.realtimeSinceStartup)); - var exhibits = JsonConvert.DeserializeObject>(http.Download.text); + var exhibits = JsonConvert.DeserializeObject>(http.Download.text); foreach (var exhibit in exhibits) { UpdateMedia(exhibit); @@ -219,26 +217,46 @@ namespace RothenburgAR.Updater }; } + StartCoroutine("UpdateVersionFileCoroutine", versionData); } - private void UpdateMedia(Exhibit exhibit) + private IEnumerator UpdateVersionFileCoroutine(UpdateVersionFileCoroutineData versionData) + { + // updates version file only if all sub files were downloaded successfully + yield return new WaitUntil(() => versionData.httpHandlers.All(h => h.IsDone)); + if (versionData.httpHandlers.Any(http => http.Request.isNetworkError || http.Request.isHttpError)) yield break; + + File.WriteAllText(versionData.path, versionData.version); + } + + private class UpdateVersionFileCoroutineData + { + public List httpHandlers; + public string path; + public string version; + } + + private void UpdateMedia(ApiExhibit exhibit) { var mediaIDs = exhibit.Pois.Select(p => p.MediaId).Except(new List { null }).ToList(); foreach (var mediaId in mediaIDs) { + //System.Security.Cryptography.MD5CryptoServiceProvider + //System.Security.Cryptography.SHA1CryptoServiceProvider + //System.Security.Cryptography.SHA256 + var path = PathHelper.CombinePaths(PathHelper.MediaPath, mediaId); var filepath = Path.Combine(path, "media.zip"); - //TODO cancel download if unnecessary, how to check if unnecessary? - if (File.Exists(filepath)) continue; - - // create file and dir if nonexistent + // create dir if nonexistent if (!Directory.Exists(path)) Directory.CreateDirectory(path); - if (!File.Exists(filepath)) File.Create(filepath).Close(); var url = ApiInfo.FileEndpoint.Replace("{id}", mediaId); - var http = new HttpRequest(url, HttpVerb.GET).Send(); + var http = new HttpRequest(url, HttpVerb.GET) + .WithHeader("ETag", "") + .Send(); + httpHandlers.Add(http); http.Operation.completed += ar => @@ -248,11 +266,12 @@ namespace RothenburgAR.Updater return; } + var x = http; + //TODO unzip var type = http.Request.GetResponseHeader("Content-Type"); File.WriteAllBytes(filepath, http.Download.data); Debug.Log(string.Format("{1}-DONE with {0}", url, Time.realtimeSinceStartup)); - }; } } @@ -280,7 +299,7 @@ namespace RothenburgAR.Updater return false; } - private void UpdateTracker(ExhibitionVersion exhibition) + private void UpdateTracker(ApiExhibitionVersion exhibition) { //TODO test UpdateTracker var path = PathHelper.CombinePaths(PathHelper.ExhibitionPath, exhibition.Id); @@ -299,7 +318,7 @@ namespace RothenburgAR.Updater { Directory.CreateDirectory(path); } - + //TODO unzip, should be tracker.xml and tracker.dat files var type = http.Request.GetResponseHeader("Content-Type"); File.WriteAllBytes(Path.Combine(path, "tracker.zip"), http.Download.data);