/// <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); handle.set_sequential_download(true); var alreadyBuffered = false; var bandwidth = new Progress <BandwidthRate>(); var prog = new Progress <double>(); while (!cts.IsCancellationRequested) { using (var status = handle.status()) { var progress = status.progress * 100d; 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); bandwidthRate.Report(new BandwidthRate { DownloadRate = downRate, UploadRate = upRate }); ((IProgress <double>)prog).Report(progress); ((IProgress <BandwidthRate>)bandwidth).Report(new BandwidthRate { DownloadRate = downRate, UploadRate = upRate }); handle.flush_cache(); if (handle.need_save_resume_data()) { handle.save_resume_data(1); } double minimumBuffering; switch (type) { case MediaType.Movie: minimumBuffering = Constants.MinimumMovieBuffering; break; case MediaType.Show: minimumBuffering = Constants.MinimumShowBuffering; break; case MediaType.Unkown: minimumBuffering = Constants.MinimumMovieBuffering; break; default: minimumBuffering = Constants.MinimumMovieBuffering; break; } if (progress >= minimumBuffering && !alreadyBuffered) { buffered.Invoke(); foreach ( var filePath in Directory .GetFiles(status.save_path, "*.*", SearchOption.AllDirectories) .Where(s => s.Contains(handle.torrent_file().name()) && (s.EndsWith(".mp4") || s.EndsWith(".mkv") || s.EndsWith(".mov") || s.EndsWith(".avi"))) ) { alreadyBuffered = true; media.FilePath = filePath; BroadcastMediaBuffered(media, prog, bandwidth); break; } if (!alreadyBuffered) { session.remove_torrent(handle, 0); if (type == MediaType.Unkown) { Messenger.Default.Send( new UnhandledExceptionMessage( new PopcornException( "There is no media file in the torrent you dropped in the UI."))); } else { Messenger.Default.Send( new UnhandledExceptionMessage( new PopcornException("There is no media file in the torrent."))); } break; } } if (status.is_finished) { session.remove_torrent(handle, 0); break; } try { await Task.Delay(1000, cts.Token); } catch (TaskCanceledException) { cancelled.Invoke(); break; } } } }
/// <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; } } } }
/// <summary> /// Download media /// </summary> /// <param name="media">Media file <see cref="IMediaFile"/></param> /// <param name="savePath">Save path of the media</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, string savePath, 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); handle.set_sequential_download(true); var alreadyBuffered = false; var bandwidth = new Progress <BandwidthRate>(); var prog = new Progress <double>(); var playingProgress = new Progress <double>(); var sw = new Stopwatch(); sw.Start(); var mediaIndex = -1; long maxSize = 0; var filePath = string.Empty; while (!cts.IsCancellationRequested) { using (var status = handle.status()) { var progress = 0d; if (status.has_metadata) { var numFiles = handle.torrent_file().files().num_files(); var totalSizeExceptIgnoredFiles = handle.torrent_file().total_size(); handle.flush_cache(); if (mediaIndex == -1 || string.IsNullOrEmpty(filePath)) { for (var i = 0; i < numFiles; i++) { var currentSize = handle.torrent_file().files().file_size(i); if (currentSize > maxSize) { maxSize = currentSize; mediaIndex = i; } } for (var i = 0; i < numFiles; i++) { if (i != mediaIndex) { handle.file_priority(i, 0); totalSizeExceptIgnoredFiles -= handle.torrent_file().files().file_size(i); } } filePath = handle.torrent_file().files().file_path(mediaIndex, savePath); } var fileProgressInBytes = handle.file_progress(1)[mediaIndex]; progress = (double)fileProgressInBytes / (double)totalSizeExceptIgnoredFiles * 100d; 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 eta = sw.GetEta(fileProgressInBytes, totalSizeExceptIgnoredFiles); 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 }); } double minimumBuffering; switch (type) { case MediaType.Show: minimumBuffering = Constants.MinimumShowBuffering; break; default: minimumBuffering = Constants.MinimumMovieBuffering; break; } if (mediaIndex != -1 && progress >= minimumBuffering && !alreadyBuffered) { buffered.Invoke(); if (!string.IsNullOrEmpty(filePath)) { alreadyBuffered = true; media.FilePath = filePath; BroadcastMediaBuffered(media, prog, bandwidth, playingProgress); } 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 (Exception ex) when(ex is TaskCanceledException || ex is ObjectDisposedException) { cancelled.Invoke(); sw.Stop(); try { session.remove_torrent(handle, 1); } catch (Exception) { // ignored } break; } } } }