public IEnumerator <object> NextPage(List <PolySceneFileInfo> files) { string uri = m_PageToken == null ? m_Uri : String.Format("{0}&page_token={1}", m_Uri, m_PageToken); WebRequest request = new WebRequest(uri, App.GoogleIdentity, UnityWebRequest.kHttpVerbGET); using (var cr = request.SendAsync().AsIeNull()) { while (!request.Done) { try { cr.MoveNext(); } catch (VrAssetServiceException e) { e.UserFriendly = m_ErrorMessage; throw; } yield return(cr.Current); } } Future <JObject> f = new Future <JObject>(() => JObject.Parse(request.Result)); JObject json; while (!f.TryGetResult(out json)) { yield return(null); } var assets = json["assets"]; if (assets != null) { foreach (var asset in assets) { var info = new PolySceneFileInfo(asset); info.Author = asset["displayName"].ToString(); ; files.Add(info); } } JToken jPageToken = json["nextPageToken"]; m_PageToken = jPageToken != null?jPageToken.ToString() : null; }
// Download tilt files and thumbnails (that we don't already have) private IEnumerator DownloadFilesCoroutine(List <PolySketch> sketches) { bool notifyOnError = true; void NotifyCreateError(PolySceneFileInfo sceneFileInfo, string type, Exception ex) { string error = $"Error downloading {type} file for {sceneFileInfo.HumanName}."; ControllerConsoleScript.m_Instance.AddNewLine(error, notifyOnError); notifyOnError = false; Debug.LogException(ex); Debug.LogError($"{sceneFileInfo.HumanName} {sceneFileInfo.TiltPath}"); } void NotifyWriteError(PolySceneFileInfo sceneFileInfo, string type, UnityWebRequest www) { string error = $"Error downloading {type} file for {sceneFileInfo.HumanName}.\n" + "Out of disk space?"; ControllerConsoleScript.m_Instance.AddNewLine(error, notifyOnError); notifyOnError = false; Debug.LogError($"{www.error} {sceneFileInfo.HumanName} {sceneFileInfo.TiltPath}"); } byte[] downloadBuffer = new byte[kDownloadBufferSize]; // Load the icons first, then the thumbnails foreach (PolySketch sketch in sketches) { PolySceneFileInfo sceneFileInfo = sketch.PolySceneFileInfo; // TODO(b/36270116): Check filesizes when Poly can give it to us to detect incomplete downloads if (!sceneFileInfo.IconDownloaded) { if (File.Exists(sceneFileInfo.IconPath)) { sceneFileInfo.IconDownloaded = true; } else { using (UnityWebRequest www = UnityWebRequest.Get(sceneFileInfo.IconUrl)) { DownloadHandlerFastFile downloadHandler; try { downloadHandler = new DownloadHandlerFastFile(sceneFileInfo.IconPath, downloadBuffer); } catch (Exception ex) { NotifyCreateError(sceneFileInfo, "icon", ex); continue; } www.downloadHandler = downloadHandler; yield return(www.SendWebRequest()); if (www.isNetworkError || www.responseCode >= 400 || !string.IsNullOrEmpty(www.error)) { NotifyWriteError(sceneFileInfo, "icon", www); } else { sceneFileInfo.IconDownloaded = true; } } } } yield return(null); } foreach (PolySketch sketch in sketches) { PolySceneFileInfo sceneFileInfo = sketch.PolySceneFileInfo; if (!sceneFileInfo.TiltDownloaded) { if (File.Exists(sceneFileInfo.TiltPath)) { sceneFileInfo.TiltDownloaded = true; } else { using (UnityWebRequest www = UnityWebRequest.Get(sceneFileInfo.TiltFileUrl)) { DownloadHandlerFastFile downloadHandler; try { downloadHandler = new DownloadHandlerFastFile(sceneFileInfo.TiltPath, downloadBuffer); } catch (Exception ex) { NotifyCreateError(sceneFileInfo, "sketch", ex); continue; } www.downloadHandler = downloadHandler; yield return(www.SendWebRequest()); if (www.isNetworkError || www.responseCode >= 400 || !string.IsNullOrEmpty(www.error)) { NotifyWriteError(sceneFileInfo, "sketch", www); } else { sceneFileInfo.TiltDownloaded = true; } } } } yield return(null); } yield return(null); }
public PolySketch(PolySceneFileInfo info) { m_FileInfo = info; }
// Fetch asset metadata from server and populate m_Sketches private IEnumerator PopulateSketchesCoroutine() { // Replacement data structures that will be populated with incoming data. These // temporary structures will be copied to the "live" structures in chunks, so // beware of modifications that may reference incomplete data. List <PolySketch> sketches = new List <PolySketch>(); Dictionary <string, PolySketch> assetIds = new Dictionary <string, PolySketch>(); bool fromEmpty = m_AssetIds.Count == 0; int loadSketchCount = 0; AssetLister lister = null; List <PolySceneFileInfo> infos = new List <PolySceneFileInfo>(); // If we don't have a connection to Poly and we're querying the Showcase, use // the json metadatas stored in resources, instead of trying to get them from Poly. if (VrAssetService.m_Instance.NoConnection && m_Type == SketchSetType.Curated) { TextAsset[] textAssets = Resources.LoadAll <TextAsset>(SketchCatalog.kDefaultShowcaseSketchesFolder); for (int i = 0; i < textAssets.Length; ++i) { JObject jo = JObject.Parse(textAssets[i].text); infos.Add(new PolySceneFileInfo(jo)); } } else { lister = m_AssetService.ListAssets(Type); } bool changed = false; int pagesFetched = 0; while (lister == null || lister.HasMore || assetIds.Count == 0) { if (sketches.Count >= 180) { break; // Don't allow the sketchbook to become too big. } // lister will be null if we can't connect to Poly and we've pre-populated infos // with data from Resources. if (lister != null) { using (var cr = lister.NextPage(infos)) { while (true) { try { if (!cr.MoveNext()) { break; } } catch (VrAssetServiceException e) { ControllerConsoleScript.m_Instance.AddNewLine(e.UserFriendly); Debug.LogException(e); yield break; } yield return(cr.Current); } } } if (infos.Count == 0) { break; } if (m_CacheDir == null) { yield break; } // Only cull the curated sketches. If a user likes a sketch that's very high poly count, // there's no feedback to tell them why it didn't show up in their list. b/123649719 if (m_Type == SketchSetType.Curated) { infos = infos.Where(x => x.GltfTriangleCount < m_MaximumSceneTriangles).ToList(); } if (m_Type == SketchSetType.Curated && !assetIds.Keys.Contains(kIntroSketchAssetId)) { yield return(VrAssetService.m_Instance.InsertSketchInfo( kIntroSketchAssetId, kIntroSketchIndex, infos)); } for (int i = 0; i < infos.Count; i++) { PolySceneFileInfo info = infos[i]; PolySketch sketch; if (m_AssetIds.TryGetValue(info.AssetId, out sketch)) { // We already have this sketch } else { sketch = new PolySketch(info); sketch.m_DownloadIndex = loadSketchCount++; // Set local paths info.TiltPath = Path.Combine(m_CacheDir, String.Format("{0}.tilt", info.AssetId)); info.IconPath = Path.Combine(m_CacheDir, String.Format("{0}.png", info.AssetId)); changed = true; } if (assetIds.ContainsKey(info.AssetId)) { Debug.LogErrorFormat("VR Asset Service has returned two objects with asset id '{0}'.", info.AssetId); } else { sketches.Add(sketch); assetIds.Add(info.AssetId, sketch); } } infos.Clear(); // Only download the files with every other page, otherwise the sketch pages change too often, // Which results in a bad user experience. if ((++pagesFetched & 1) == 0 || lister == null || !lister.HasMore) { if (Type == SketchSetType.Curated) { sketches.Sort(CompareSketchesByTriangleCountAndDownloadIndex); } // If populating the set from new then show stuff as it comes in. // (We don't have to worry about anything being removed) if (fromEmpty) { yield return(DownloadFilesCoroutine(sketches)); RemoveFailedDownloads(sketches); // Copying sketches to m_Sketches before sketches has completed populating is a bit // dangerous, but as long as they're copied and then listeners are notified // immediately afterward with OnChanged(), there data should be stable. m_TotalCount = sketches.Count; m_Sketches = new List <PolySketch>(sketches); m_AssetIds = assetIds; m_Ready = true; OnChanged(); } } } if (!fromEmpty) { // Find anything that was removed int removed = m_Sketches.Count(s => !assetIds.ContainsKey(s.PolySceneFileInfo.AssetId)); if (removed > 0) { changed = true; } // Download new files before we notify our listeners that we've got new stuff for them. if (changed) { yield return(DownloadFilesCoroutine(sketches)); RemoveFailedDownloads(sketches); } // DeleteOldSketchesCoroutine relies on m_AssetIds being up to date, so set these before // we try to cull the herd. m_AssetIds = assetIds; if (changed) { yield return(DeleteOldSketchesCoroutine()); } // Set new data live m_TotalCount = sketches.Count; m_Sketches = new List <PolySketch>(sketches); m_Ready = true; if (changed) { OnChanged(); } } else { // On first run populate, take the time to clean out any cobwebs. // Note that this is particularly important for the curated sketches, which do not have // a periodic refresh, meaning old sketches will never been deleted in the other path. yield return(DeleteOldSketchesCoroutine()); OnChanged(); } }