2023-05-18 19:06:40 +08:00
|
|
|
using System;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using Assets.Scenes.Ride.Scripts;
|
|
|
|
|
using Assets.Scripts.Apis.Models;
|
|
|
|
|
using Cysharp.Threading.Tasks;
|
2023-06-28 15:59:34 +08:00
|
|
|
using UnityEngine;
|
2023-05-18 19:06:40 +08:00
|
|
|
using UnityEngine.Networking;
|
|
|
|
|
|
|
|
|
|
namespace Assets.AR
|
|
|
|
|
{
|
|
|
|
|
public class ARDownloader
|
|
|
|
|
{
|
|
|
|
|
private MapRoute _route;
|
|
|
|
|
|
|
|
|
|
private readonly string _localARDataPath;
|
|
|
|
|
private readonly string _localARRoutePath;
|
|
|
|
|
private readonly string _localVideoPath;
|
|
|
|
|
private readonly string _serverARDataPath;
|
|
|
|
|
private readonly string _serverARRoutePath;
|
|
|
|
|
private readonly string _serverVideoPath;
|
|
|
|
|
|
2023-06-28 15:59:34 +08:00
|
|
|
private bool IsVideoNeedDownload { get; set; }
|
|
|
|
|
private bool IsARDataNeedDownload { get; set; }
|
|
|
|
|
private bool IsARRouteNeedDownload { get; set; }
|
|
|
|
|
public bool IsDone => !IsVideoNeedDownload && !IsARDataNeedDownload && !IsARRouteNeedDownload;
|
|
|
|
|
|
|
|
|
|
private float _total { get; set; }
|
|
|
|
|
private float _videoProgress;
|
|
|
|
|
public float VideoProgress
|
|
|
|
|
{
|
|
|
|
|
get => _videoProgress;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_videoProgress = value;
|
|
|
|
|
ComputeProgress();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private float _dataProgress;
|
|
|
|
|
public float DataProgress
|
|
|
|
|
{
|
|
|
|
|
get => _dataProgress;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_dataProgress = value;
|
|
|
|
|
ComputeProgress();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private float _routeProgress;
|
|
|
|
|
public float RouteProgress
|
|
|
|
|
{
|
|
|
|
|
get => _routeProgress;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_routeProgress = value;
|
|
|
|
|
ComputeProgress();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Action<float> ReportProgress { get; set; }
|
2023-05-18 19:06:40 +08:00
|
|
|
|
|
|
|
|
public ARDownloader(MapRoute route)
|
|
|
|
|
{
|
|
|
|
|
this._route = route;
|
|
|
|
|
|
|
|
|
|
var dataPath = $"{PFConstants.ARFolder}/{_route.Id}";
|
|
|
|
|
var path = PFConstants.VideoFolder;
|
|
|
|
|
Helper.CreateDirectoryIfNotExsit(dataPath);
|
|
|
|
|
|
|
|
|
|
_localARDataPath = $"{dataPath}/{_route.Id}.json";
|
|
|
|
|
_localARRoutePath = $"{dataPath}/route-{_route.Id}.json";
|
|
|
|
|
_localVideoPath = $"{path}/{_route.FileName}";
|
|
|
|
|
|
|
|
|
|
_serverARDataPath = _route.ARConfig;
|
|
|
|
|
_serverARRoutePath = _route.VideoRoute;
|
|
|
|
|
_serverVideoPath = _route.Url;
|
|
|
|
|
}
|
2023-06-28 15:59:34 +08:00
|
|
|
|
|
|
|
|
private void ComputeProgress()
|
|
|
|
|
{
|
|
|
|
|
var progress = (_videoProgress + _routeProgress + _dataProgress) / _total;
|
|
|
|
|
ReportProgress?.Invoke(progress);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task DownLoadDefault(Action<float> progress = null,CancellationTokenSource cancellation = default(CancellationTokenSource))
|
|
|
|
|
{
|
|
|
|
|
ReportProgress = progress;
|
|
|
|
|
|
|
|
|
|
if(IsVideoNeedDownload)
|
|
|
|
|
await DownLoadVideoAsync(Progress.Create<float>(x => VideoProgress = x),cancellation);
|
|
|
|
|
if(IsARRouteNeedDownload)
|
|
|
|
|
await DownLoadARRouteAsync(Progress.Create<float>(x=> RouteProgress = x),cancellation);
|
|
|
|
|
if(IsARDataNeedDownload)
|
|
|
|
|
await DownLoadARDataAsync(Progress.Create<float>(x=> DataProgress = x),cancellation);
|
|
|
|
|
}
|
2023-05-18 19:06:40 +08:00
|
|
|
|
2023-06-28 15:59:34 +08:00
|
|
|
private async UniTask<UnityWebRequest> DownLoadVideoAsync(IProgress<float> progress = null,CancellationTokenSource cancellation = default(CancellationTokenSource))
|
2023-05-18 19:06:40 +08:00
|
|
|
{
|
2023-06-28 15:59:34 +08:00
|
|
|
var result = await DownloadToFileAsync(_serverVideoPath, _localVideoPath, progress, cancellation);
|
|
|
|
|
IsVideoNeedDownload = !result.isDone;
|
|
|
|
|
return result;
|
2023-05-18 19:06:40 +08:00
|
|
|
}
|
|
|
|
|
|
2023-06-28 15:59:34 +08:00
|
|
|
private async UniTask<UnityWebRequest> DownLoadARRouteAsync(IProgress<float> progress = null,CancellationTokenSource cancellation = default(CancellationTokenSource))
|
2023-05-18 19:06:40 +08:00
|
|
|
{
|
2023-06-28 15:59:34 +08:00
|
|
|
var result = await DownloadToFileAsync(_serverARRoutePath, _localARRoutePath, progress, cancellation);
|
|
|
|
|
IsARRouteNeedDownload = !result.isDone;
|
|
|
|
|
return result;
|
2023-05-18 19:06:40 +08:00
|
|
|
}
|
|
|
|
|
|
2023-06-28 15:59:34 +08:00
|
|
|
private async UniTask<UnityWebRequest> DownLoadARDataAsync(IProgress<float> progress = null,
|
2023-05-18 19:06:40 +08:00
|
|
|
CancellationTokenSource cancellation = default(CancellationTokenSource))
|
|
|
|
|
{
|
2023-06-28 15:59:34 +08:00
|
|
|
var result = await DownloadToFileAsync(_serverARDataPath, _localARDataPath, progress, cancellation);
|
|
|
|
|
IsARDataNeedDownload = !result.isDone;
|
|
|
|
|
return result;
|
2023-05-18 19:06:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<bool> CheckAllAsync()
|
|
|
|
|
{
|
2023-06-28 15:59:34 +08:00
|
|
|
IsARRouteNeedDownload = await CheckARRouteAsync();
|
|
|
|
|
IsARDataNeedDownload = await CheckARDataAsync();
|
|
|
|
|
IsVideoNeedDownload = await CheckVideoAsync();
|
|
|
|
|
|
|
|
|
|
if (IsVideoNeedDownload)
|
|
|
|
|
_total += 1f;
|
|
|
|
|
if (IsARRouteNeedDownload)
|
|
|
|
|
_total += 1f;
|
|
|
|
|
if (IsARDataNeedDownload)
|
|
|
|
|
_total += 1f;
|
|
|
|
|
|
|
|
|
|
return IsVideoNeedDownload || IsARRouteNeedDownload || IsARDataNeedDownload;
|
2023-05-18 19:06:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// return true if the video is needed to download
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns></returns>
|
2023-06-28 15:59:34 +08:00
|
|
|
private async Task<bool> CheckVideoAsync()
|
2023-05-18 19:06:40 +08:00
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (!File.Exists(_localVideoPath)) return true;
|
2023-06-28 15:59:34 +08:00
|
|
|
return await HeadFileAsync(_serverVideoPath, _localVideoPath);
|
2023-05-18 19:06:40 +08:00
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2023-06-28 15:59:34 +08:00
|
|
|
/// return true if the video need to update or download
|
2023-05-18 19:06:40 +08:00
|
|
|
/// </summary>
|
|
|
|
|
/// <returns></returns>
|
2023-06-28 15:59:34 +08:00
|
|
|
private async Task<bool> CheckARRouteAsync()
|
2023-05-18 19:06:40 +08:00
|
|
|
{
|
|
|
|
|
if (!File.Exists(_localARRoutePath)) return true;
|
2023-06-28 15:59:34 +08:00
|
|
|
return await HeadFileAsync(_serverARRoutePath, _localARRoutePath);
|
2023-05-18 19:06:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// return true if the video is needed to download
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns></returns>
|
2023-06-28 15:59:34 +08:00
|
|
|
private async Task<bool> CheckARDataAsync()
|
2023-05-18 19:06:40 +08:00
|
|
|
{
|
|
|
|
|
if (!File.Exists(_localARDataPath)) return true;
|
2023-06-28 15:59:34 +08:00
|
|
|
return await HeadFileAsync(_serverARDataPath, _localARDataPath);
|
2023-05-18 19:06:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 下载文件到本地(持续存入磁盘减少内存占用)
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="downloadUrl">文件连接</param>
|
|
|
|
|
/// <param name="fullPath">本地文件全路径</param>
|
|
|
|
|
/// <param name="progress">下载进度</param>
|
2023-06-28 15:59:34 +08:00
|
|
|
/// <param name="cancellation"></param>
|
|
|
|
|
private static async UniTask<UnityWebRequest> DownloadToFileAsync(string downloadUrl, string fullPath,
|
2023-05-18 19:06:40 +08:00
|
|
|
IProgress<float> progress = null, CancellationTokenSource cancellation = default(CancellationTokenSource))
|
|
|
|
|
{
|
|
|
|
|
var dh = new DownloadHandlerFile(fullPath)
|
|
|
|
|
{
|
|
|
|
|
removeFileOnAbort = true
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var request = UnityWebRequest.Get(downloadUrl);
|
|
|
|
|
request.downloadHandler = dh;
|
|
|
|
|
request.method = UnityWebRequest.kHttpVerbGET;
|
|
|
|
|
|
|
|
|
|
var result = await request.SendWebRequest().ToUniTask(progress, PlayerLoopTiming.Update,
|
|
|
|
|
(cancellation?.Token ?? default(CancellationToken)));
|
|
|
|
|
|
2023-06-28 15:59:34 +08:00
|
|
|
var etag = result.GetResponseHeader("x-oss-hash-crc64ecma");
|
2023-05-18 19:06:40 +08:00
|
|
|
var path = fullPath + ".etag";
|
|
|
|
|
File.WriteAllText(path, etag);
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// head request for file meta info such as etag.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="downloadUrl">download url</param>
|
|
|
|
|
/// <param name="fullPath">local path for storage</param>
|
|
|
|
|
/// <returns></returns>
|
2023-06-28 15:59:34 +08:00
|
|
|
private static async Task<bool> HeadFileAsync(string downloadUrl, string fullPath,
|
2023-05-18 19:06:40 +08:00
|
|
|
IProgress<float> progress = null, CancellationTokenSource cancellation = default(CancellationTokenSource))
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var etagFile = fullPath + ".etag";
|
|
|
|
|
var etag = string.Empty;
|
|
|
|
|
if (File.Exists(etagFile))
|
|
|
|
|
{
|
|
|
|
|
etag = File.ReadAllText(etagFile);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var request = UnityWebRequest.Head(downloadUrl);
|
2023-06-28 15:59:34 +08:00
|
|
|
var result = await request.SendWebRequest().ToUniTask(progress, PlayerLoopTiming.Update, (cancellation?.Token ?? default(CancellationToken)));
|
|
|
|
|
var responseTag = result.GetResponseHeader("x-oss-hash-crc64ecma");
|
|
|
|
|
return !responseTag.Equals(etag);
|
2023-05-18 19:06:40 +08:00
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
2023-06-28 15:59:34 +08:00
|
|
|
return false;
|
2023-05-18 19:06:40 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|