public async Task<int> Download(
string outputFilePath,
int firstChunkId,
int lastChunkId,
ChunkDownloadProgressedDelegate chunkDownloadProgressed,
GroupDownloadCompletedDelegate groupDownloadCompleted,
bool saveChunkInfo)
{
return await Task.Run(async () =>
{
const int groupSize = 3;
string streamRootUrl = VodPlaylist.StreamRoot.EndsWith("/") ?
VodPlaylist.StreamRoot : $"{VodPlaylist.StreamRoot}/";
int currentChunkId = firstChunkId;
while (currentChunkId <= lastChunkId && !_cancellationToken.IsCancellationRequested)
{
List<TwitchVodChunk> chunkGroup = new List<TwitchVodChunk>();
for (int i = 0; i < groupSize && i <= lastChunkId; ++i)
{
chunkGroup.Add(VodPlaylist.GetChunk(currentChunkId + i));
}
if (chunkGroup.Count > 0)
{
Dictionary<int, DownloadItem> dict = new Dictionary<int, DownloadItem>();
Progress<DownloadItem> progress = new Progress<DownloadItem>();
progress.ProgressChanged += (s, pi) =>
{
dict[pi.TaskId] = pi;
var items = dict.Values.ToList();
items.Sort((x, y) => x.TaskId < y.TaskId ? -1 : 1);
chunkDownloadProgressed?.Invoke(this, items);
};
var tasks = chunkGroup.Select((chunk, taskId) => Task.Run(() =>
{
Stream chunkStream = null;
IProgress<DownloadItem> reporter = progress;
FileDownloader d = new FileDownloader() { Url = streamRootUrl + chunk.FileName };
d.WorkProgress += (sender, downloadedBytes, contentLength) =>
{
DownloadItem downloadItem = new DownloadItem(
taskId, chunk, contentLength, downloadedBytes, chunkStream,
(sender as FileDownloader).LastErrorCode);
reporter.Report(downloadItem);
};
d.WorkFinished += (sender, downloadedBytes, contentLength, e) =>
{
DownloadItem downloadItem = new DownloadItem(
taskId, chunk, contentLength, downloadedBytes, chunkStream,
(sender as FileDownloader).LastErrorCode);
reporter.Report(downloadItem);
};
chunkStream = new MemoryStream();
return d.Download(chunkStream, 4096, _cancellationTokenSource);
}));
try
{
await Task.WhenAll(tasks);
var items = dict.Values.ToList();
items.Sort((x, y) => x.TaskId < y.TaskId ? -1 : 1);
groupDownloadCompleted?.Invoke(this, items);
} catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
return ex.HResult;
}
}
currentChunkId += groupSize;
}
return 0;
});
}
Иногда выдаёт NullReferenceException
при сортировке.
progress.ProgressChanged += (s, pi) =>
{
dict[pi.TaskId] = pi;
var items = dict.Values.ToList();
items.Sort((x, y) => x.TaskId < y.TaskId ? -1 : 1); //здесь
chunkDownloadProgressed?.Invoke(this, items);
};
Один из элементов равен null
. Я не пойму, откуда он там берётся IProgress
ведь синхронизирует потоки.