/// <summary> /// Download media /// </summary> /// <param name="media">Media file <see cref="IMediaFile"/></param> /// <param name="type">Media type <see cref="MediaType"/></param> /// <param name="uploadLimit">Upload limit</param> /// <param name="downloadLimit">Download limit</param> /// <param name="downloadProgress">Download progress</param> /// <param name="bandwidthRate">Download rate</param> /// <param name="nbSeeds">Number of seeders</param> /// <param name="nbPeers">Number of peers</param> /// <param name="handle"><see cref="torrent_handle"/></param> /// <param name="session"><see cref="session"/></param> /// <param name="buffered">Action to execute when media has been buffered</param> /// <param name="cancelled">Action to execute when media download has been cancelled</param> /// <param name="cts"><see cref="CancellationTokenSource"/></param> /// <returns><see cref="Task"/></returns> private async Task HandleDownload(T media, MediaType type, int uploadLimit, int downloadLimit, IProgress <double> downloadProgress, IProgress <BandwidthRate> bandwidthRate, IProgress <int> nbSeeds, IProgress <int> nbPeers, torrent_handle handle, session session, Action buffered, Action cancelled, CancellationTokenSource cts) { handle.set_upload_limit(uploadLimit * 1024); handle.set_download_limit(downloadLimit * 1024); var alreadyBuffered = false; var bandwidth = new Progress <BandwidthRate>(); var prog = new Progress <double>(); var playingProgress = new Progress <double>(); var playingProgression = 0d; playingProgress.ProgressChanged += (sender, d) => { playingProgression = d; }; IProgress <PieceAvailability> pieceAvailability = new Progress <PieceAvailability>(); Stopwatch sw = new Stopwatch(); sw.Start(); while (!cts.IsCancellationRequested) { using (var status = handle.status()) { var filePath = string.Empty; var progress = status.progress * 100d; if (status.has_metadata) { var downRate = Math.Round(status.download_rate / 1024d, 0); var upRate = Math.Round(status.upload_rate / 1024d, 0); nbSeeds.Report(status.num_seeds); nbPeers.Report(status.num_peers); downloadProgress.Report(progress); var numFiles = handle.torrent_file().num_files(); var fileIndex = -1; for (var i = 0; i < numFiles; i++) { var path = handle.torrent_file().file_at(i); if (path.EndsWith(".mp4") || path.EndsWith(".mkv") || path.EndsWith(".mov") || path.EndsWith(".avi")) { fileIndex = i; filePath = $@"{Directory.GetParent(status.save_path)}\{path}"; } } var fileProgress = handle.file_progress(1)[fileIndex]; var eta = sw.GetEta(fileProgress, handle.torrent_file().total_size()); bandwidthRate.Report(new BandwidthRate { DownloadRate = downRate, UploadRate = upRate, ETA = eta }); ((IProgress <double>)prog).Report(progress); ((IProgress <BandwidthRate>)bandwidth).Report(new BandwidthRate { DownloadRate = downRate, UploadRate = upRate, ETA = eta }); var numPieces = handle.torrent_file().num_pieces() - 1; var cursor = Math.Floor((numPieces - 3 * numPieces / 100d) * playingProgression); var pieces = handle.piece_priorities() .Select((piece, index) => new { Piece = piece, Index = index }) .ToList(); var lastPieceAvailableIndex = 0; foreach (var piece in pieces.Where(a => a.Index >= cursor)) { if (!handle.have_piece(piece.Index)) { handle.set_piece_deadline(piece.Index, 50); foreach (var otherPiece in pieces.Where(a => a.Index != piece.Index)) { handle.reset_piece_deadline(otherPiece.Index); } break; } else { lastPieceAvailableIndex = piece.Index; handle.reset_piece_deadline(piece.Index); } } pieceAvailability.Report(new PieceAvailability(numPieces, pieces.FirstOrDefault(a => a.Index >= cursor - 3 * numPieces / 100d).Index, lastPieceAvailableIndex)); } handle.flush_cache(); if (handle.need_save_resume_data()) { handle.save_resume_data(1); } double minimumBuffering; switch (type) { case MediaType.Show: minimumBuffering = Constants.MinimumShowBuffering; break; default: minimumBuffering = Constants.MinimumMovieBuffering; break; } if (progress >= minimumBuffering && !alreadyBuffered) { buffered.Invoke(); if (!string.IsNullOrEmpty(filePath)) { alreadyBuffered = true; media.FilePath = filePath; BroadcastMediaBuffered(media, prog, bandwidth, playingProgress, (Progress <PieceAvailability>)pieceAvailability); } if (!alreadyBuffered) { session.remove_torrent(handle, 0); if (type == MediaType.Unkown) { Messenger.Default.Send( new UnhandledExceptionMessage( new PopcornException( LocalizationProviderHelper.GetLocalizedValue <string>( "NoMediaInDroppedTorrent")))); } else { Messenger.Default.Send( new UnhandledExceptionMessage( new PopcornException( LocalizationProviderHelper.GetLocalizedValue <string>("NoMediaInTorrent")))); } break; } } try { await Task.Delay(1000, cts.Token); } catch (TaskCanceledException) { cancelled.Invoke(); sw.Stop(); try { session.remove_torrent(handle, 1); } catch (Exception) { } break; } } } }