diff options
7 files changed, 324 insertions, 408 deletions
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 865b4af5f..0d4f18f3a 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -231,6 +231,8 @@ namespace MediaBrowser.Model.Configuration /// <value><c>true</c> if [enable tv db updates]; otherwise, <c>false</c>.</value> public bool EnableTvDbUpdates { get; set; } + public bool EnableVideoImageExtraction { get; set; } + /// <summary> /// Initializes a new instance of the <see cref="ServerConfiguration" /> class. /// </summary> @@ -241,6 +243,7 @@ namespace MediaBrowser.Model.Configuration LegacyWebSocketPortNumber = 8945; EnableHttpLevelLogging = true; EnableDashboardResponseCaching = true; + EnableVideoImageExtraction = true; #if (DEBUG) EnableDeveloperTools = true; diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index d62c2f27f..139c622fc 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -57,6 +57,7 @@ <Compile Include="MediaInfo\BaseFFProbeProvider.cs" /> <Compile Include="MediaInfo\FFProbeAudioInfoProvider.cs" /> <Compile Include="MediaInfo\FFProbeVideoInfoProvider.cs" /> + <Compile Include="MediaInfo\VideoImageProvider.cs" /> <Compile Include="Movies\BoxSetProviderFromXml.cs" /> <Compile Include="Movies\MovieXmlParser.cs" /> <Compile Include="Movies\FanArtMovieProvider.cs" /> diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs new file mode 100644 index 000000000..b202e4866 --- /dev/null +++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs @@ -0,0 +1,315 @@ +using MediaBrowser.Common.IO; +using MediaBrowser.Common.MediaInfo; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.MediaInfo; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Logging; +using System; +using System.Collections.Concurrent; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Providers.MediaInfo +{ + class VideoImageProvider : BaseMetadataProvider + { + /// <summary> + /// Gets or sets the image cache. + /// </summary> + /// <value>The image cache.</value> + public FileSystemRepository ImageCache { get; set; } + + /// <summary> + /// The _locks + /// </summary> + private readonly ConcurrentDictionary<string, SemaphoreSlim> _locks = new ConcurrentDictionary<string, SemaphoreSlim>(); + + /// <summary> + /// The _media encoder + /// </summary> + private readonly IMediaEncoder _mediaEncoder; + private readonly IIsoManager _isoManager; + + public VideoImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IMediaEncoder mediaEncoder, IIsoManager isoManager) + : base(logManager, configurationManager) + { + _mediaEncoder = mediaEncoder; + _isoManager = isoManager; + + ImageCache = new FileSystemRepository(Kernel.Instance.FFMpegManager.AudioImagesDataPath); + } + + /// <summary> + /// Gets a value indicating whether [refresh on version change]. + /// </summary> + /// <value><c>true</c> if [refresh on version change]; otherwise, <c>false</c>.</value> + protected override bool RefreshOnVersionChange + { + get + { + return true; + } + } + + /// <summary> + /// Gets the provider version. + /// </summary> + /// <value>The provider version.</value> + protected override string ProviderVersion + { + get + { + return "1"; + } + } + + /// <summary> + /// Supportses the specified item. + /// </summary> + /// <param name="item">The item.</param> + /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> + public override bool Supports(BaseItem item) + { + return item.LocationType == LocationType.FileSystem && item is Video; + } + + /// <summary> + /// Needses the refresh internal. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="providerInfo">The provider info.</param> + /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> + protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) + { + var video = (Video)item; + + if (!QualifiesForExtraction(video)) + { + return false; + } + + return base.NeedsRefreshInternal(item, providerInfo); + } + + /// <summary> + /// Qualifieses for extraction. + /// </summary> + /// <param name="item">The item.</param> + /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> + private bool QualifiesForExtraction(Video item) + { + if (!ConfigurationManager.Configuration.EnableVideoImageExtraction) + { + return false; + } + + if (!string.IsNullOrEmpty(item.PrimaryImagePath)) + { + return false; + } + + // No support for this + if (item.VideoType == VideoType.HdDvd) + { + return false; + } + + // Can't extract from iso's if we weren't unable to determine iso type + if (item.VideoType == VideoType.Iso && !item.IsoType.HasValue) + { + return false; + } + + // Can't extract if we didn't find a video stream in the file + if (item.MediaStreams.All(m => m.Type != MediaStreamType.Video)) + { + return false; + } + + return true; + } + + /// <summary> + /// Override this to return the date that should be compared to the last refresh date + /// to determine if this provider should be re-fetched. + /// </summary> + /// <param name="item">The item.</param> + /// <returns>DateTime.</returns> + protected override DateTime CompareDate(BaseItem item) + { + return item.DateModified; + } + + /// <summary> + /// Gets the priority. + /// </summary> + /// <value>The priority.</value> + public override MetadataProviderPriority Priority + { + get { return MetadataProviderPriority.Last; } + } + + public override ItemUpdateType ItemUpdateType + { + get + { + return ItemUpdateType.ImageUpdate; + } + } + + /// <summary> + /// Fetches metadata and returns true or false indicating if any work that requires persistence was done + /// </summary> + /// <param name="item">The item.</param> + /// <param name="force">if set to <c>true</c> [force].</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task{System.Boolean}.</returns> + public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) + { + var video = (Video)item; + + // Double check this here in case force was used + if (QualifiesForExtraction(video)) + { + try + { + await ExtractImage(video, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + // Swallow this so that we don't keep on trying over and over again + + Logger.ErrorException("Error extracting image for {0}", ex, item.Name); + } + } + + SetLastRefreshed(item, DateTime.UtcNow); + return true; + } + + /// <summary> + /// Extracts the image. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + private async Task ExtractImage(Video item, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var filename = item.Path + "_" + item.DateModified.Ticks + "_primary"; + + var path = ImageCache.GetResourcePath(filename, ".jpg"); + + if (!File.Exists(path)) + { + var semaphore = GetLock(path); + + // Acquire a lock + await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + + // Check again + if (!File.Exists(path)) + { + try + { + var parentPath = Path.GetDirectoryName(path); + + if (!Directory.Exists(parentPath)) + { + Directory.CreateDirectory(parentPath); + } + + await ExtractImageInternal(item, path, cancellationToken).ConfigureAwait(false); + } + finally + { + semaphore.Release(); + } + } + else + { + semaphore.Release(); + } + } + + // Image is already in the cache + item.PrimaryImagePath = path; + } + + /// <summary> + /// Extracts the image. + /// </summary> + /// <param name="video">The video.</param> + /// <param name="path">The path.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + private async Task ExtractImageInternal(Video video, string path, CancellationToken cancellationToken) + { + var isoMount = await MountIsoIfNeeded(video, cancellationToken).ConfigureAwait(false); + + try + { + // If we know the duration, grab it from 10% into the video. Otherwise just 10 seconds in. + // Always use 10 seconds for dvd because our duration could be out of whack + var imageOffset = video.VideoType != VideoType.Dvd && video.RunTimeTicks.HasValue && + video.RunTimeTicks.Value > 0 + ? TimeSpan.FromTicks(Convert.ToInt64(video.RunTimeTicks.Value * .1)) + : TimeSpan.FromSeconds(10); + + InputType type; + + var inputPath = MediaEncoderHelpers.GetInputArgument(video, isoMount, out type); + + await _mediaEncoder.ExtractImage(inputPath, type, video.Video3DFormat, imageOffset, path, cancellationToken).ConfigureAwait(false); + + video.PrimaryImagePath = path; + } + finally + { + if (isoMount != null) + { + isoMount.Dispose(); + } + } + } + + /// <summary> + /// The null mount task result + /// </summary> + protected readonly Task<IIsoMount> NullMountTaskResult = Task.FromResult<IIsoMount>(null); + + /// <summary> + /// Mounts the iso if needed. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task{IIsoMount}.</returns> + protected Task<IIsoMount> MountIsoIfNeeded(Video item, CancellationToken cancellationToken) + { + if (item.VideoType == VideoType.Iso) + { + return _isoManager.Mount(item.Path, cancellationToken); + } + + return NullMountTaskResult; + } + + /// <summary> + /// Gets the lock. + /// </summary> + /// <param name="filename">The filename.</param> + /// <returns>SemaphoreSlim.</returns> + private SemaphoreSlim GetLock(string filename) + { + return _locks.GetOrAdd(filename, key => new SemaphoreSlim(1, 1)); + } + } +} diff --git a/MediaBrowser.Providers/TV/RemoteEpisodeProvider.cs b/MediaBrowser.Providers/TV/RemoteEpisodeProvider.cs index 5cf1651a9..88116eb54 100644 --- a/MediaBrowser.Providers/TV/RemoteEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/RemoteEpisodeProvider.cs @@ -1,8 +1,4 @@ -using System.Collections.Generic; -using System.Text.RegularExpressions; -using System.Xml.Linq; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; @@ -11,13 +7,16 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net; +using MediaBrowser.Providers.Extensions; using System; +using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Xml; -using MediaBrowser.Providers.Extensions; +using System.Xml.Linq; namespace MediaBrowser.Providers.TV { diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 4e1e6bb3f..22de1e898 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -171,7 +171,6 @@ <Compile Include="ScheduledTasks\ChapterImagesTask.cs" /> <Compile Include="ScheduledTasks\PluginUpdateTask.cs" /> <Compile Include="ScheduledTasks\RefreshMediaLibraryTask.cs" /> - <Compile Include="ScheduledTasks\VideoImagesTask.cs" /> <Compile Include="ServerApplicationPaths.cs" /> <Compile Include="ServerManager\ServerManager.cs" /> <Compile Include="ServerManager\WebSocketConnection.cs" /> diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs deleted file mode 100644 index 447bb4956..000000000 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs +++ /dev/null @@ -1,387 +0,0 @@ -using MediaBrowser.Common.IO; -using MediaBrowser.Common.MediaInfo; -using MediaBrowser.Common.ScheduledTasks; -using MediaBrowser.Controller; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.MediaInfo; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Logging; -using MoreLinq; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Server.Implementations.ScheduledTasks -{ - /// <summary> - /// Class VideoImagesTask - /// </summary> - public class VideoImagesTask : IScheduledTask - { - /// <summary> - /// Gets or sets the image cache. - /// </summary> - /// <value>The image cache.</value> - public FileSystemRepository ImageCache { get; set; } - - /// <summary> - /// The _library manager - /// </summary> - private readonly ILibraryManager _libraryManager; - /// <summary> - /// The _media encoder - /// </summary> - private readonly IMediaEncoder _mediaEncoder; - - /// <summary> - /// The _iso manager - /// </summary> - private readonly IIsoManager _isoManager; - - private readonly IItemRepository _itemRepo; - - private readonly ILogger _logger; - - /// <summary> - /// The _locks - /// </summary> - private readonly ConcurrentDictionary<string, SemaphoreSlim> _locks = new ConcurrentDictionary<string, SemaphoreSlim>(); - - private readonly List<BaseItem> _newlyAddedItems = new List<BaseItem>(); - - private const int NewItemDelay = 60000; - - /// <summary> - /// The current new item timer - /// </summary> - /// <value>The new item timer.</value> - private Timer NewItemTimer { get; set; } - - /// <summary> - /// Initializes a new instance of the <see cref="AudioImagesTask" /> class. - /// </summary> - /// <param name="libraryManager">The library manager.</param> - /// <param name="logManager">The log manager.</param> - /// <param name="mediaEncoder">The media encoder.</param> - /// <param name="isoManager">The iso manager.</param> - public VideoImagesTask(ILibraryManager libraryManager, ILogManager logManager, IMediaEncoder mediaEncoder, IIsoManager isoManager, IItemRepository itemRepo) - { - _libraryManager = libraryManager; - _mediaEncoder = mediaEncoder; - _isoManager = isoManager; - _itemRepo = itemRepo; - _logger = logManager.GetLogger(GetType().Name); - - ImageCache = new FileSystemRepository(Kernel.Instance.FFMpegManager.VideoImagesDataPath); - - libraryManager.ItemAdded += libraryManager_ItemAdded; - libraryManager.ItemUpdated += libraryManager_ItemAdded; - } - - /// <summary> - /// Handles the ItemAdded event of the libraryManager control. - /// </summary> - /// <param name="sender">The source of the event.</param> - /// <param name="e">The <see cref="ItemChangeEventArgs"/> instance containing the event data.</param> - void libraryManager_ItemAdded(object sender, ItemChangeEventArgs e) - { - lock (_newlyAddedItems) - { - _newlyAddedItems.Add(e.Item); - - if (NewItemTimer == null) - { - NewItemTimer = new Timer(NewItemTimerCallback, null, NewItemDelay, Timeout.Infinite); - } - else - { - NewItemTimer.Change(NewItemDelay, Timeout.Infinite); - } - } - } - - /// <summary> - /// News the item timer callback. - /// </summary> - /// <param name="state">The state.</param> - private async void NewItemTimerCallback(object state) - { - List<BaseItem> newItems; - - // Lock the list and release all resources - lock (_newlyAddedItems) - { - newItems = _newlyAddedItems.DistinctBy(i => i.Id).ToList(); - _newlyAddedItems.Clear(); - - NewItemTimer.Dispose(); - NewItemTimer = null; - } - - foreach (var item in GetItemsForExtraction(newItems.Take(5))) - { - try - { - await ExtractImage(item, CancellationToken.None).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.ErrorException("Error creating image for {0}", ex, item.Name); - } - } - } - - /// <summary> - /// Gets the name of the task - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return "Video image extraction"; } - } - - /// <summary> - /// Gets the description. - /// </summary> - /// <value>The description.</value> - public string Description - { - get { return "Extracts images from video files that do not have external images."; } - } - - /// <summary> - /// Gets the category. - /// </summary> - /// <value>The category.</value> - public string Category - { - get { return "Library"; } - } - - /// <summary> - /// Executes the task - /// </summary> - /// <param name="cancellationToken">The cancellation token.</param> - /// <param name="progress">The progress.</param> - /// <returns>Task.</returns> - public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress) - { - var items = GetItemsForExtraction(_libraryManager.RootFolder.RecursiveChildren).ToList(); - - progress.Report(0); - - var numComplete = 0; - - foreach (var item in items) - { - try - { - await ExtractImage(item, cancellationToken).ConfigureAwait(false); - } - catch - { - // Already logged at lower levels. - // Just don't let the task fail - } - - numComplete++; - double percent = numComplete; - percent /= items.Count; - - progress.Report(100 * percent); - } - - progress.Report(100); - } - - /// <summary> - /// Gets the items for extraction. - /// </summary> - /// <param name="sourceItems">The source items.</param> - /// <returns>IEnumerable{BaseItem}.</returns> - private IEnumerable<Video> GetItemsForExtraction(IEnumerable<BaseItem> sourceItems) - { - var allItems = sourceItems.ToList(); - - var localTrailers = allItems.SelectMany(i => i.LocalTrailerIds.Select(_itemRepo.RetrieveItem)).Cast<Video>(); - - var themeVideos = allItems.SelectMany(i => i.ThemeVideoIds.Select(_itemRepo.RetrieveItem)).Cast<Video>(); - - var videos = allItems.OfType<Video>().ToList(); - - var items = videos.ToList(); - - items.AddRange(localTrailers); - - items.AddRange(themeVideos); - - items.AddRange(videos.SelectMany(i => i.AdditionalPartIds).Select(_itemRepo.RetrieveItem).Cast<Video>()); - items.AddRange(videos.OfType<Movie>().SelectMany(i => i.SpecialFeatureIds).Select(_itemRepo.RetrieveItem).Cast<Video>()); - - return items.Where(i => - { - if (!string.IsNullOrEmpty(i.PrimaryImagePath)) - { - return false; - } - - if (i.LocationType != LocationType.FileSystem) - { - return false; - } - - if (i.VideoType == VideoType.HdDvd) - { - return false; - } - - if (i.VideoType == VideoType.Iso && !i.IsoType.HasValue) - { - return false; - } - - return i.MediaStreams != null && i.MediaStreams.Any(m => m.Type == MediaStreamType.Video); - }); - } - - /// <summary> - /// Extracts the image. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - private async Task ExtractImage(Video item, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var filename = item.Path + "_" + item.DateModified.Ticks + "_primary"; - - var path = ImageCache.GetResourcePath(filename, ".jpg"); - - if (!File.Exists(path)) - { - var semaphore = GetLock(path); - - // Acquire a lock - await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - - // Check again - if (!File.Exists(path)) - { - try - { - var parentPath = Path.GetDirectoryName(path); - - if (!Directory.Exists(parentPath)) - { - Directory.CreateDirectory(parentPath); - } - - await ExtractImageInternal(item, path, cancellationToken).ConfigureAwait(false); - } - finally - { - semaphore.Release(); - } - } - else - { - semaphore.Release(); - } - } - - // Image is already in the cache - item.PrimaryImagePath = path; - - await _libraryManager.UpdateItem(item, ItemUpdateType.ImageUpdate, cancellationToken).ConfigureAwait(false); - } - - /// <summary> - /// Extracts the image. - /// </summary> - /// <param name="video">The video.</param> - /// <param name="path">The path.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - private async Task ExtractImageInternal(Video video, string path, CancellationToken cancellationToken) - { - var isoMount = await MountIsoIfNeeded(video, cancellationToken).ConfigureAwait(false); - - try - { - // If we know the duration, grab it from 10% into the video. Otherwise just 10 seconds in. - // Always use 10 seconds for dvd because our duration could be out of whack - var imageOffset = video.VideoType != VideoType.Dvd && video.RunTimeTicks.HasValue && - video.RunTimeTicks.Value > 0 - ? TimeSpan.FromTicks(Convert.ToInt64(video.RunTimeTicks.Value * .1)) - : TimeSpan.FromSeconds(10); - - InputType type; - - var inputPath = MediaEncoderHelpers.GetInputArgument(video, isoMount, out type); - - await _mediaEncoder.ExtractImage(inputPath, type, video.Video3DFormat, imageOffset, path, cancellationToken).ConfigureAwait(false); - - video.PrimaryImagePath = path; - } - finally - { - if (isoMount != null) - { - isoMount.Dispose(); - } - } - } - - /// <summary> - /// The null mount task result - /// </summary> - protected readonly Task<IIsoMount> NullMountTaskResult = Task.FromResult<IIsoMount>(null); - - /// <summary> - /// Mounts the iso if needed. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{IIsoMount}.</returns> - protected Task<IIsoMount> MountIsoIfNeeded(Video item, CancellationToken cancellationToken) - { - if (item.VideoType == VideoType.Iso) - { - return _isoManager.Mount(item.Path, cancellationToken); - } - - return NullMountTaskResult; - } - - /// <summary> - /// Gets the default triggers. - /// </summary> - /// <returns>IEnumerable{BaseTaskTrigger}.</returns> - public IEnumerable<ITaskTrigger> GetDefaultTriggers() - { - return new ITaskTrigger[] - { - new DailyTrigger { TimeOfDay = TimeSpan.FromHours(2) } - }; - } - - /// <summary> - /// Gets the lock. - /// </summary> - /// <param name="filename">The filename.</param> - /// <returns>System.Object.</returns> - private SemaphoreSlim GetLock(string filename) - { - return _locks.GetOrAdd(filename, key => new SemaphoreSlim(1, 1)); - } - } -} diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index 2cc907144..d7e228b18 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -697,20 +697,6 @@ namespace MediaBrowser.ServerApplication public override async Task<CheckForUpdateResult> CheckForApplicationUpdate(CancellationToken cancellationToken, IProgress<double> progress) { - var result = await CheckForApplicationUpdateInternal(cancellationToken, progress).ConfigureAwait(false); - - return result; - } - - /// <summary> - /// Checks for application update internal. - /// </summary> - /// <param name="cancellationToken">The cancellation token.</param> - /// <param name="progress">The progress.</param> - /// <returns>Task{CheckForUpdateResult}.</returns> - private async Task<CheckForUpdateResult> CheckForApplicationUpdateInternal(CancellationToken cancellationToken, - IProgress<double> progress) - { var availablePackages = await InstallationManager.GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false); var version = InstallationManager.GetLatestCompatibleVersion(availablePackages, Constants.MbServerPkgName, ConfigurationManager.CommonConfiguration.SystemUpdateLevel); |
