private void calculateTimeRemaining(DownloadProgress progress)
        {
            DownloadItem item = DownloadSessionMetadata.GetDownloadItem(progress.Url.ToString());

            if (item == null)
            {
                return;
            }

            if (!item.TimeStarted.HasValue)
            {
                item.TimeStarted = DateTime.UtcNow;
            }

            try
            {
                var timeElapsed = DateTime.UtcNow.Subtract(item.TimeStarted.Value);
                var lastSpeed   = progress.DownloadedLength / timeElapsed.TotalSeconds;
                progress.ExpectedDuration   = (long)((progress.TotalLength / lastSpeed) * 1000);
                progress.DownloadedDuration = (long)timeElapsed.TotalMilliseconds;
                progress.Percent            = (double)progress.DownloadedLength / progress.TotalLength;
                progress.Id = item.Id;

                DownloadSessionMetadata.SaveDownloadItem(item);
            }
            catch (Exception ex)
            {
                Logger.Log("ERROR: calculateTimeRemaining: " + ex);
            }
        }
        public override void DidCompleteWithError(NSUrlSession session, NSUrlSessionTask task, NSError error)
        {
            try
            {
                Logger.Log("INFO: DidCompleteWithError: " + error);
                if (error != null)
                {
                    if (task.OriginalRequest == null)
                    {
                        return;
                    }

                    string itemID       = task.OriginalRequest.Url.ToString();
                    string title        = task.OriginalRequest.Url.ToString();
                    var    downloadItem = DownloadSessionMetadata.GetDownloadItem(task.OriginalRequest.Url.ToString());
                    if (downloadItem != null)
                    {
                        itemID = downloadItem.Id;
                        if (!string.IsNullOrEmpty(downloadItem.FileName))
                        {
                            title = downloadItem.FileName.Replace(".mp4", string.Empty);
                        }

                        DownloadSessionMetadata.RemoveDownloadItem(task.OriginalRequest.Url.ToString());
                    }

                    var isCanceled            = (error.Code == -999);
                    DownloadProgress progress = new DownloadProgress()
                    {
                        Status = isCanceled ? DownloadStatus.Canceled : DownloadStatus.Failed,
                        Id     = itemID,
                        Title  = title,
                        Url    = task.OriginalRequest.Url.ToString()
                    };

                    if (!isCanceled && (downloadItem != null) && (downloadItem.RetryCount <= 3))
                    {
                        progress.WillRetryOnError = true;
                    }

                    Logger.Log("INFO: DidCompleteWithError: ID: " + progress.Id + ". Error: " + error);
                    if (itemDownloader != null)
                    {
                        InvokeOnMainThread(() => itemDownloader.Fire_DownloadComplete(new AsyncCompletedEventArgs(new NSErrorException(error), isCanceled, progress)));
                    }

                    if (!isCanceled && (downloadItem != null) && (downloadItem.RetryCount <= 3))
                    {
                        downloadItem.RetryCount++;
#pragma warning disable 4014
                        itemDownloader.Download(downloadItem);
#pragma warning restore 4014
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Log("ERROR: DidCompleteWithError: " + ex);
            }
        }
 public void Cancel(string id)
 {
     Logger.Log("Canceling download item: ID: " + id);
     lock (syncRoot)
         if (session != null)
         {
             session.GetAllTasks((NSUrlSessionTask[] tasks) =>
             {
                 var runningTasks = tasks.Where(t => t.State == NSUrlSessionTaskState.Running).ToArray();
                 foreach (var runningTask in runningTasks)
                 {
                     var downloadItem = DownloadSessionMetadata.GetDownloadItem(runningTask.OriginalRequest.Url.ToString());
                     if ((downloadItem != null) && (downloadItem.Id == id))
                     {
                         runningTask.Cancel();
                         Logger.Log("Download item " + id + " canceled");
                     }
                 }
             });
         }
 }
        public override void DidFinishDownloading(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, NSUrl location)
        {
            try
            {
                Logger.Log("INFO: DidFinishDownloading: " + downloadTask.OriginalRequest.Url.ToString());
                string itemID          = string.Empty;
                string destinationPath = ItemDownloader.DownloaderPath + Path.DirectorySeparatorChar + downloadTask.OriginalRequest.Url.LastPathComponent;
                string fileName        = downloadTask.Response.SuggestedFilename;
                string title           = downloadTask.Response.SuggestedFilename;

                var downloadItem = DownloadSessionMetadata.GetDownloadItem(downloadTask.OriginalRequest.Url.ToString());
                if ((downloadItem != null) && (downloadItem.FileName != null))
                {
                    if (string.IsNullOrEmpty(fileName))
                    {
                        fileName = downloadItem.FileName;
                    }

                    itemID = downloadItem.Id;
                    if (!string.IsNullOrEmpty(downloadItem.FileName))
                    {
                        title = downloadItem.FileName.Replace(".mp4", string.Empty);
                    }

                    DownloadSessionMetadata.RemoveDownloadItem(downloadTask.OriginalRequest.Url.ToString());
                }

                string filter           = "\\/:*?\"<>|#";
                string filteredFileName = new string(fileName.Where(c => filter.IndexOf(c) < 0).ToArray());
                destinationPath = ItemDownloader.DownloaderPath + Path.DirectorySeparatorChar + filteredFileName;

                NSError   error     = null;
                Exception exception = null;

                if (NSFileManager.DefaultManager.FileExists(destinationPath))
                {
                    NSFileManager.DefaultManager.Remove(destinationPath, out error);
                }

                bool isCustomError = false;
                var  success       = NSFileManager.DefaultManager.Move(location, NSUrl.FromFilename(destinationPath), out error);
                if (!success)
                {
                    exception = new Exception(error.LocalizedDescription);
                }
                else
                {
                    NSFileManager.SetSkipBackupAttribute(destinationPath, true);
                    if (!LocalLibraryService.Instance.CanReadRecordingMetadata(destinationPath))
                    {
                        isCustomError = true;
                        Logger.Log("ERROR: DidFinishDownloading: Unable to read the file metadata");
                        exception = new Exception("We are sorry, but there is a technical issue preventing PlayOn Cloud from downloading or playing your recording. Please report this issue to our support team through this app's Account tab.");
                    }
                }

                DownloadProgress progress = new DownloadProgress()
                {
                    Status        = success ? DownloadStatus.Completed : DownloadStatus.Failed,
                    Id            = itemID,
                    Title         = title,
                    LocalFilePath = destinationPath,
                    Url           = downloadTask.OriginalRequest.Url.ToString(),
                    IsCustomError = isCustomError
                };

                Logger.Log("INFO: DidFinishDownloading: ID: " + progress.Id + ". Exception: " + exception);
                if (itemDownloader != null)
                {
                    InvokeOnMainThread(() => itemDownloader.Fire_DownloadComplete(new AsyncCompletedEventArgs(exception, false, progress)));
                }
            }
            catch (Exception ex)
            {
                Logger.Log("ERROR: DidFinishDownloading: " + ex);
            }
        }
        public async Task <Tuple <bool, string> > Download(DownloadItem item)
        {
            Logger.Log("Adding item for downloading: ID: " + item.Id);
            var downloadItem = await LibraryClient.GetDownload(item.Id);

            Logger.Log("Item download requested from server");

            if (downloadItem != null)
            {
                Logger.Log("Checking available storage");
                var canStore = await canStoreOnDevice(downloadItem);

                Logger.Log("Available storage checked");

                if (!canStore)
                {
                    return(Tuple.Create(false, downloadItem.Url));
                }

                item.Url     = downloadItem.Url;
                item.Headers = downloadItem.Headers;
                using (var url = NSUrl.FromString(item.Url))
                    using (var request = new NSMutableUrlRequest(url))
                    {
                        List <NSHttpCookie> cookies = new List <NSHttpCookie>();
                        foreach (var header in item.Headers)
                        {
                            cookies.Add(new NSHttpCookie(header.Key, header.Value));
                        }

                        request.Headers = NSHttpCookie.RequestHeaderFieldsWithCookies(cookies.ToArray());
                        DownloadSessionMetadata.SaveDownloadItem(item);
                        if ((RestService.Instance.User != null) && !string.IsNullOrEmpty(RestService.Instance.User.Email))
                        {
                            LocalLibraryService.Instance.CreateCloudItemId(item.Id, RestService.Instance.User.Email);
                        }

                        CreateSession(sessionID);
                        lock (syncRoot)
                        {
                            if (session != null)
                            {
                                var task = session.CreateDownloadTask(request);
                                task.Resume();
                            }
                            else
                            {
                                Logger.Log("ERROR: Failed to start downloading because session is NULL");
                            }
                        }

                        Logger.Log("Item added to the download queue: ID: " + item.Id + " FileName: " + item.FileName + " URL:" + item.Url);

                        return(Tuple.Create(true, item.Url));
                    }
            }
            else
            {
                Logger.Log("ERROR: Failed to start downloading of item: ID: " + item.Id + ". FileName: " + item.FileName);
            }

            return(Tuple.Create(true, string.Empty));
        }