public static bool UpdateMetadata(BaseItem item, MetadataRefreshOptions options) { bool force = (options & MetadataRefreshOptions.Force) == MetadataRefreshOptions.Force; bool fastOnly = (options & MetadataRefreshOptions.FastOnly) == MetadataRefreshOptions.FastOnly; bool changed = false; if (force) { ClearItem(item); } bool neverSavedProviderInfo; var providers = GetSupportedProviders(item, out neverSavedProviderInfo); var itemClone = (BaseItem)Serializer.Clone(item); // Parent is not serialized so its not cloned itemClone.Parent = item.Parent; foreach (var provider in providers) { provider.Item = itemClone; } if (force || NeedsRefresh(providers, fastOnly)) { // something changed clear the item before pulling metadata if (!force) { ClearItem(item); ClearItem(itemClone); } // we must clear the provider data as well in case it is bad or out of date! foreach (var provider in providers) { ClearItem(provider); } Logger.ReportInfo("Metadata changed for the following item {0} (first pass : {1} forced via UI : {2})", item.Name, fastOnly, force); changed = UpdateMetadata(item, true, fastOnly, providers); } if (!changed && neverSavedProviderInfo) { Kernel.Instance.ItemRepository.SaveProviders(item.Id, providers); } return changed; }
public static bool UpdateMetadata(BaseItem item, MetadataRefreshOptions options) { bool force = (options & MetadataRefreshOptions.Force) == MetadataRefreshOptions.Force; bool fastOnly = (options & MetadataRefreshOptions.FastOnly) == MetadataRefreshOptions.FastOnly; bool changed = false; if (force) { ClearItem(item); } var providers = GetSupportedProviders(item); var itemClone = (BaseItem)Serializer.Clone(item); // Parent is not serialized so its not cloned itemClone.Parent = item.Parent; foreach (var provider in providers) { provider.Item = itemClone; } if (force || NeedsRefresh(providers, fastOnly)) { changed = UpdateMetadata(item, force, fastOnly, providers); } return changed; }
public Task <ItemUpdateType> FetchAsync(Photo item, MetadataRefreshOptions options, CancellationToken cancellationToken) { item.SetImagePath(ImageType.Primary, item.Path); // Examples: https://github.com/mono/taglib-sharp/blob/a5f6949a53d09ce63ee7495580d6802921a21f14/tests/fixtures/TagLib.Tests.Images/NullOrientationTest.cs try { using (var file = TagLib.File.Create(item.Path)) { var image = file as TagLib.Image.File; var tag = file.GetTag(TagTypes.TiffIFD) as IFDTag; if (tag != null) { var structure = tag.Structure; if (structure != null) { var exif = structure.GetEntry(0, (ushort)IFDEntryTag.ExifIFD) as SubIFDEntry; if (exif != null) { var exifStructure = exif.Structure; if (exifStructure != null) { var entry = exifStructure.GetEntry(0, (ushort)ExifEntryTag.ApertureValue) as RationalIFDEntry; if (entry != null) { double val = entry.Value.Numerator; val /= entry.Value.Denominator; item.Aperture = val; } entry = exifStructure.GetEntry(0, (ushort)ExifEntryTag.ShutterSpeedValue) as RationalIFDEntry; if (entry != null) { double val = entry.Value.Numerator; val /= entry.Value.Denominator; item.ShutterSpeed = val; } } } } } item.CameraMake = image.ImageTag.Make; item.CameraModel = image.ImageTag.Model; item.Width = image.Properties.PhotoWidth; item.Height = image.Properties.PhotoHeight; var rating = image.ImageTag.Rating; if (rating.HasValue) { item.CommunityRating = rating; } else { item.CommunityRating = null; } item.Overview = image.ImageTag.Comment; if (!string.IsNullOrWhiteSpace(image.ImageTag.Title)) { item.Name = image.ImageTag.Title; } var dateTaken = image.ImageTag.DateTime; if (dateTaken.HasValue) { item.DateCreated = dateTaken.Value; item.PremiereDate = dateTaken.Value; item.ProductionYear = dateTaken.Value.Year; } item.Genres = image.ImageTag.Genres.ToList(); item.Tags = image.ImageTag.Keywords.ToList(); item.Software = image.ImageTag.Software; if (image.ImageTag.Orientation == TagLib.Image.ImageOrientation.None) { item.Orientation = null; } else { Model.Drawing.ImageOrientation orientation; if (Enum.TryParse(image.ImageTag.Orientation.ToString(), true, out orientation)) { item.Orientation = orientation; } } item.ExposureTime = image.ImageTag.ExposureTime; item.FocalLength = image.ImageTag.FocalLength; item.Latitude = image.ImageTag.Latitude; item.Longitude = image.ImageTag.Longitude; item.Altitude = image.ImageTag.Altitude; if (image.ImageTag.ISOSpeedRatings.HasValue) { item.IsoSpeedRating = Convert.ToInt32(image.ImageTag.ISOSpeedRatings.Value); } else { item.IsoSpeedRating = null; } } } catch (Exception e) { _logger.ErrorException("Image Provider - Error reading image tag for {0}", e, item.Path); } const ItemUpdateType result = ItemUpdateType.ImageUpdate | ItemUpdateType.MetadataImport; return(Task.FromResult(result)); }
public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress <double> progress, CancellationToken cancellationToken) { // Refresh bottom up, children first, then the boxset // By then hopefully the movies within will have Tmdb collection values var items = GetRecursiveChildren(); var totalItems = items.Count; var numComplete = 0; // Refresh seasons foreach (var item in items) { if (!(item is Season)) { continue; } cancellationToken.ThrowIfCancellationRequested(); if (refreshOptions.RefreshItem(item)) { await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); } numComplete++; double percent = numComplete; percent /= totalItems; progress.Report(percent * 100); } // Refresh episodes and other children foreach (var item in items) { if ((item is Season)) { continue; } cancellationToken.ThrowIfCancellationRequested(); var skipItem = false; var episode = item as Episode; if (episode != null && refreshOptions.MetadataRefreshMode != MetadataRefreshMode.FullRefresh && !refreshOptions.ReplaceAllMetadata && episode.IsMissingEpisode && episode.LocationType == LocationType.Virtual && episode.PremiereDate.HasValue && (DateTime.UtcNow - episode.PremiereDate.Value).TotalDays > 30) { skipItem = true; } if (!skipItem) { if (refreshOptions.RefreshItem(item)) { await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); } } numComplete++; double percent = numComplete; percent /= totalItems; progress.Report(percent * 100); } refreshOptions = new MetadataRefreshOptions(refreshOptions); await ProviderManager.RefreshSingleItem(this, refreshOptions, cancellationToken).ConfigureAwait(false); }
private void FetchEmbeddedInfo(Video video, Model.MediaInfo.MediaInfo data, MetadataRefreshOptions options) { var isFullRefresh = options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh; if (!video.LockedFields.Contains(MetadataFields.OfficialRating)) { if (!string.IsNullOrWhiteSpace(data.OfficialRating) || isFullRefresh) { video.OfficialRating = data.OfficialRating; } } if (!video.LockedFields.Contains(MetadataFields.Genres)) { if (video.Genres.Count == 0 || isFullRefresh) { video.Genres.Clear(); foreach (var genre in data.Genres) { video.AddGenre(genre); } } } if (!video.LockedFields.Contains(MetadataFields.Studios)) { if (video.Studios.Count == 0 || isFullRefresh) { video.Studios.Clear(); foreach (var studio in data.Studios) { video.AddStudio(studio); } } } if (data.ProductionYear.HasValue) { if (!video.ProductionYear.HasValue || isFullRefresh) { video.ProductionYear = data.ProductionYear; } } if (data.PremiereDate.HasValue) { if (!video.PremiereDate.HasValue || isFullRefresh) { video.PremiereDate = data.PremiereDate; } } if (data.IndexNumber.HasValue) { if (!video.IndexNumber.HasValue || isFullRefresh) { video.IndexNumber = data.IndexNumber; } } if (data.ParentIndexNumber.HasValue) { if (!video.ParentIndexNumber.HasValue || isFullRefresh) { video.ParentIndexNumber = data.ParentIndexNumber; } } // If we don't have a ProductionYear try and get it from PremiereDate if (video.PremiereDate.HasValue && !video.ProductionYear.HasValue) { video.ProductionYear = video.PremiereDate.Value.ToLocalTime().Year; } if (!video.LockedFields.Contains(MetadataFields.Overview)) { if (string.IsNullOrWhiteSpace(video.Overview) || isFullRefresh) { video.Overview = data.Overview; } } }
public Task <ItemUpdateType> FetchAsync(Video item, MetadataRefreshOptions options, CancellationToken cancellationToken) { return(FetchInternal(item, options, cancellationToken)); }
private async Task RunCustomProvider(ICustomMetadataProvider <TItemType> provider, TItemType item, string logName, MetadataRefreshOptions options, RefreshResult refreshResult, CancellationToken cancellationToken) { Logger.LogDebug("Running {Provider} for {Item}", provider.GetType().Name, logName); try { refreshResult.UpdateType |= await provider.FetchAsync(item, options, cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) { throw; } catch (Exception ex) { refreshResult.ErrorMessage = ex.Message; Logger.LogError(ex, "Error in {Provider}", provider.Name); } }
protected IEnumerable <IMetadataProvider> GetProviders(BaseItem item, LibraryOptions libraryOptions, MetadataRefreshOptions options, bool isFirstRefresh, bool requiresRefresh) { // Get providers to refresh var providers = ((ProviderManager)ProviderManager).GetMetadataProviders <TItemType>(item, libraryOptions).ToList(); var metadataRefreshMode = options.MetadataRefreshMode; // Run all if either of these flags are true var runAllProviders = options.ReplaceAllMetadata || metadataRefreshMode == MetadataRefreshMode.FullRefresh || (isFirstRefresh && metadataRefreshMode >= MetadataRefreshMode.Default) || (requiresRefresh && metadataRefreshMode >= MetadataRefreshMode.Default); if (!runAllProviders) { var providersWithChanges = providers .Where(i => { if (i is IHasItemChangeMonitor hasFileChangeMonitor) { return(HasChanged(item, hasFileChangeMonitor, options.DirectoryService)); } return(false); }) .ToList(); if (providersWithChanges.Count == 0) { providers = new List <IMetadataProvider <TItemType> >(); } else { var anyRemoteProvidersChanged = providersWithChanges.OfType <IRemoteMetadataProvider>() .Any(); var anyLocalProvidersChanged = providersWithChanges.OfType <ILocalMetadataProvider>() .Any(); var anyLocalPreRefreshProvidersChanged = providersWithChanges.OfType <IPreRefreshProvider>() .Any(); providers = providers.Where(i => { // If any provider reports a change, always run local ones as well if (i is ILocalMetadataProvider) { return(anyRemoteProvidersChanged || anyLocalProvidersChanged || anyLocalPreRefreshProvidersChanged); } // If any remote providers changed, run them all so that priorities can be honored if (i is IRemoteMetadataProvider) { if (options.MetadataRefreshMode == MetadataRefreshMode.ValidationOnly) { return(false); } return(anyRemoteProvidersChanged); } // Run custom refresh providers if they report a change or any remote providers change return(anyRemoteProvidersChanged || providersWithChanges.Contains(i)); }).ToList(); } } return(providers); }
public Task <ItemUpdateType> FetchAsync(AudioBook item, MetadataRefreshOptions options, CancellationToken cancellationToken) { return(FetchAudioInfo(item, cancellationToken)); }
/// <summary> /// Compare our current children (presumably just read from the repo) with the current state of the file system and adjust for any changes /// ***Currently does not contain logic to maintain items that are unavailable in the file system*** /// </summary> /// <param name="progress">The progress.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="recursive">if set to <c>true</c> [recursive].</param> /// <param name="refreshChildMetadata">if set to <c>true</c> [refresh child metadata].</param> /// <param name="refreshOptions">The refresh options.</param> /// <param name="directoryService">The directory service.</param> /// <returns>Task.</returns> protected override Task ValidateChildrenInternal(IProgress <double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService) { CreateResolveArgs(directoryService); ResetDynamicChildren(); return(NullTaskResult); }
protected async Task <ItemUpdateType> FetchAsync(IHasMetadata item, ImageType imageType, MetadataRefreshOptions options, CancellationToken cancellationToken) { var image = item.GetImageInfo(imageType, 0); if (image != null) { if (!image.IsLocalFile) { return(ItemUpdateType.None); } if (!FileSystem.ContainsSubPath(item.GetInternalMetadataPath(), image.Path)) { return(ItemUpdateType.None); } } var items = GetItemsWithImages(item); return(await FetchToFileInternal(item, items, imageType, cancellationToken).ConfigureAwait(false)); }
private async Task RunCustomProvider(ICustomMetadataProvider <TItemType> provider, TItemType item, MetadataRefreshOptions options, RefreshResult refreshResult, CancellationToken cancellationToken) { Logger.Debug("Running {0} for {1}", provider.GetType().Name, item.Path ?? item.Name); try { refreshResult.UpdateType = refreshResult.UpdateType | await provider.FetchAsync(item, options, cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) { throw; } catch (Exception ex) { refreshResult.Status = ProviderRefreshStatus.CompletedWithErrors; refreshResult.ErrorMessage = ex.Message; Logger.ErrorException("Error in {0}", ex, provider.Name); } }
private async Task RefreshItem(BaseItem item, MetadataRefreshOptions refreshOptions, IProgress <double> progress, CancellationToken cancellationToken) { await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); progress.Report(100); }
public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress <double> progress, CancellationToken cancellationToken) { var items = RecursiveChildren.ToList(); var songs = items.OfType <Audio>().ToList(); var others = items.Except(songs).ToList(); var totalItems = songs.Count + others.Count; var percentages = new Dictionary <Guid, double>(totalItems); var tasks = new List <Task>(); // Refresh songs foreach (var item in songs) { if (tasks.Count >= 3) { await Task.WhenAll(tasks).ConfigureAwait(false); tasks.Clear(); } cancellationToken.ThrowIfCancellationRequested(); var innerProgress = new ActionableProgress <double>(); // Avoid implicitly captured closure var currentChild = item; innerProgress.RegisterAction(p => { lock (percentages) { percentages[currentChild.Id] = p / 100; var percent = percentages.Values.Sum(); percent /= totalItems; percent *= 100; progress.Report(percent); } }); var taskChild = item; tasks.Add(Task.Run(async() => await RefreshItem(taskChild, refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false), cancellationToken)); } await Task.WhenAll(tasks).ConfigureAwait(false); tasks.Clear(); // Refresh current item await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); // Refresh all non-songs foreach (var item in others) { if (tasks.Count >= 3) { await Task.WhenAll(tasks).ConfigureAwait(false); tasks.Clear(); } cancellationToken.ThrowIfCancellationRequested(); var innerProgress = new ActionableProgress <double>(); // Avoid implicitly captured closure var currentChild = item; innerProgress.RegisterAction(p => { lock (percentages) { percentages[currentChild.Id] = p / 100; var percent = percentages.Values.Sum(); percent /= totalItems; percent *= 100; progress.Report(percent); } }); // Avoid implicitly captured closure var taskChild = item; tasks.Add(Task.Run(async() => await RefreshItem(taskChild, refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false), cancellationToken)); } await Task.WhenAll(tasks).ConfigureAwait(false); progress.Report(100); }
/// <summary> /// Validates the children internal. /// </summary> /// <param name="progress">The progress.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="recursive">if set to <c>true</c> [recursive].</param> /// <param name="refreshChildMetadata">if set to <c>true</c> [refresh child metadata].</param> /// <param name="refreshOptions">The refresh options.</param> /// <param name="directoryService">The directory service.</param> /// <returns>Task.</returns> protected async virtual Task ValidateChildrenInternal(IProgress <double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService) { var locationType = LocationType; cancellationToken.ThrowIfCancellationRequested(); var validChildren = new List <BaseItem>(); var allLibraryPaths = LibraryManager .GetVirtualFolders() .SelectMany(i => i.Locations) .ToList(); if (locationType != LocationType.Remote && locationType != LocationType.Virtual) { IEnumerable <BaseItem> nonCachedChildren; try { nonCachedChildren = GetNonCachedChildren(directoryService); } catch (IOException ex) { nonCachedChildren = new BaseItem[] { }; Logger.ErrorException("Error getting file system entries for {0}", ex, Path); } if (nonCachedChildren == null) { return; //nothing to validate } progress.Report(5); //build a dictionary of the current children we have now by Id so we can compare quickly and easily var currentChildren = GetActualChildrenDictionary(); //create a list for our validated children var newItems = new List <BaseItem>(); cancellationToken.ThrowIfCancellationRequested(); foreach (var child in nonCachedChildren) { BaseItem currentChild; if (currentChildren.TryGetValue(child.Id, out currentChild) && IsValidFromResolver(currentChild, child)) { validChildren.Add(currentChild); continue; } // Brand new item - needs to be added child.SetParent(this); newItems.Add(child); validChildren.Add(child); } // If any items were added or removed.... if (newItems.Count > 0 || currentChildren.Count != validChildren.Count) { // That's all the new and changed ones - now see if there are any that are missing var itemsRemoved = currentChildren.Values.Except(validChildren).ToList(); var actualRemovals = new List <BaseItem>(); foreach (var item in itemsRemoved) { var itemLocationType = item.LocationType; if (itemLocationType == LocationType.Virtual || itemLocationType == LocationType.Remote) { } else if (!string.IsNullOrEmpty(item.Path) && IsPathOffline(item.Path, allLibraryPaths)) { } else { actualRemovals.Add(item); } } if (actualRemovals.Count > 0) { foreach (var item in actualRemovals) { Logger.Debug("Removed item: " + item.Path); item.SetParent(null); await LibraryManager.DeleteItem(item, new DeleteOptions { DeleteFileLocation = false }).ConfigureAwait(false); LibraryManager.ReportItemRemoved(item); } } await LibraryManager.CreateItems(newItems, cancellationToken).ConfigureAwait(false); } } progress.Report(10); cancellationToken.ThrowIfCancellationRequested(); if (recursive) { await ValidateSubFolders(ActualChildren.OfType <Folder>().ToList(), directoryService, progress, cancellationToken).ConfigureAwait(false); } progress.Report(20); if (refreshChildMetadata) { var container = this as IMetadataContainer; var innerProgress = new ActionableProgress <double>(); innerProgress.RegisterAction(p => progress.Report(.80 * p + 20)); if (container != null) { await container.RefreshAllMetadata(refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false); } else { await RefreshMetadataRecursive(refreshOptions, recursive, innerProgress, cancellationToken); } } progress.Report(100); }
void FullRefresh(Folder folder, MetadataRefreshOptions options) { Kernel.Instance.MajorActivity = true; Information.AddInformationString(CurrentInstance.StringData("FullRefreshMsg")); folder.RefreshMetadata(options); using (new Profiler(CurrentInstance.StringData("FullValidationProf"))) { RunActionRecursively(folder, item => { Folder f = item as Folder; if (f != null) f.ValidateChildren(); }); } using (new Profiler(CurrentInstance.StringData("FastRefreshProf"))) { RunActionRecursively(folder, item => item.RefreshMetadata(MetadataRefreshOptions.FastOnly)); } using (new Profiler(CurrentInstance.StringData("SlowRefresh"))) { RunActionRecursively(folder, item => item.RefreshMetadata(MetadataRefreshOptions.Default)); } Information.AddInformationString(CurrentInstance.StringData("FullRefreshFinishedMsg")); Kernel.Instance.MajorActivity = false; }
protected async Task Fetch(Video video, CancellationToken cancellationToken, InternalMediaInfoResult data, IIsoMount isoMount, BlurayDiscInfo blurayInfo, MetadataRefreshOptions options) { var mediaInfo = MediaEncoderHelpers.GetMediaInfo(data); var mediaStreams = mediaInfo.MediaStreams; video.TotalBitrate = mediaInfo.TotalBitrate; video.FormatName = (mediaInfo.Format ?? string.Empty) .Replace("matroska", "mkv", StringComparison.OrdinalIgnoreCase); if (data.format != null) { // For dvd's this may not always be accurate, so don't set the runtime if the item already has one var needToSetRuntime = video.VideoType != VideoType.Dvd || video.RunTimeTicks == null || video.RunTimeTicks.Value == 0; if (needToSetRuntime && !string.IsNullOrEmpty(data.format.duration)) { video.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(data.format.duration, _usCulture)).Ticks; } if (video.VideoType == VideoType.VideoFile) { var extension = (Path.GetExtension(video.Path) ?? string.Empty).TrimStart('.'); video.Container = extension; } else { video.Container = null; } if (!string.IsNullOrEmpty(data.format.size)) { video.Size = long.Parse(data.format.size, _usCulture); } else { video.Size = null; } } var mediaChapters = (data.Chapters ?? new MediaChapter[] { }).ToList(); var chapters = mediaChapters.Select(GetChapterInfo).ToList(); if (video.VideoType == VideoType.BluRay || (video.IsoType.HasValue && video.IsoType.Value == IsoType.BluRay)) { FetchBdInfo(video, chapters, mediaStreams, blurayInfo); } await AddExternalSubtitles(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); FetchWtvInfo(video, data); video.IsHD = mediaStreams.Any(i => i.Type == MediaStreamType.Video && i.Width.HasValue && i.Width.Value >= 1270); var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video); video.VideoBitRate = videoStream == null ? null : videoStream.BitRate; video.DefaultVideoStreamIndex = videoStream == null ? (int?)null : videoStream.Index; video.HasSubtitles = mediaStreams.Any(i => i.Type == MediaStreamType.Subtitle); ExtractTimestamp(video); UpdateFromMediaInfo(video, videoStream); await _itemRepo.SaveMediaStreams(video.Id, mediaStreams, cancellationToken).ConfigureAwait(false); if (options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || options.MetadataRefreshMode == MetadataRefreshMode.Default) { var chapterOptions = _chapterManager.GetConfiguration(); try { var remoteChapters = await DownloadChapters(video, chapters, chapterOptions, cancellationToken).ConfigureAwait(false); if (remoteChapters.Count > 0) { chapters = remoteChapters; } } catch (Exception ex) { _logger.ErrorException("Error downloading chapters", ex); } if (chapters.Count == 0 && mediaStreams.Any(i => i.Type == MediaStreamType.Video)) { AddDummyChapters(video, chapters); } NormalizeChapterNames(chapters); await _encodingManager.RefreshChapterImages(new ChapterImageRefreshOptions { Chapters = chapters, Video = video, ExtractImages = chapterOptions.ExtractDuringLibraryScan, SaveChapters = false }, cancellationToken).ConfigureAwait(false); await _chapterManager.SaveChapters(video.Id.ToString(), chapters, cancellationToken).ConfigureAwait(false); } }
/// <inheritdoc/> public Task RefreshFullItem(BaseItem item, MetadataRefreshOptions options, CancellationToken cancellationToken) { return(RefreshItem(item, options, cancellationToken)); }
public override bool RefreshMetadata(MetadataRefreshOptions options) { // do nothing, metadata is assigned external to the provider framework return false; }
protected virtual async Task <RefreshResult> RefreshWithProviders( MetadataResult <TItemType> metadata, TIdType id, MetadataRefreshOptions options, ICollection <IMetadataProvider> providers, ItemImageProvider imageService, CancellationToken cancellationToken) { var refreshResult = new RefreshResult { UpdateType = ItemUpdateType.None }; var item = metadata.Item; var customProviders = providers.OfType <ICustomMetadataProvider <TItemType> >().ToList(); var logName = !item.IsFileProtocol ? item.Name ?? item.Path : item.Path ?? item.Name; foreach (var provider in customProviders.Where(i => i is IPreRefreshProvider)) { await RunCustomProvider(provider, item, logName, options, refreshResult, cancellationToken).ConfigureAwait(false); } var temp = new MetadataResult <TItemType> { Item = CreateNew() }; temp.Item.Path = item.Path; var userDataList = new List <UserItemData>(); // If replacing all metadata, run internet providers first if (options.ReplaceAllMetadata) { var remoteResult = await ExecuteRemoteProviders(temp, logName, id, providers.OfType <IRemoteMetadataProvider <TItemType, TIdType> >(), cancellationToken) .ConfigureAwait(false); refreshResult.UpdateType |= remoteResult.UpdateType; refreshResult.ErrorMessage = remoteResult.ErrorMessage; refreshResult.Failures += remoteResult.Failures; } var hasLocalMetadata = false; foreach (var provider in providers.OfType <ILocalMetadataProvider <TItemType> >().ToList()) { var providerName = provider.GetType().Name; Logger.LogDebug("Running {Provider} for {Item}", providerName, logName); var itemInfo = new ItemInfo(item); try { var localItem = await provider.GetMetadata(itemInfo, options.DirectoryService, cancellationToken).ConfigureAwait(false); if (localItem.HasMetadata) { foreach (var remoteImage in localItem.RemoteImages) { try { await ProviderManager.SaveImage(item, remoteImage.Url, remoteImage.Type, null, cancellationToken).ConfigureAwait(false); refreshResult.UpdateType |= ItemUpdateType.ImageUpdate; } catch (HttpRequestException ex) { Logger.LogError(ex, "Could not save {ImageType} image: {Url}", Enum.GetName(remoteImage.Type), remoteImage.Url); } } if (imageService.MergeImages(item, localItem.Images)) { refreshResult.UpdateType |= ItemUpdateType.ImageUpdate; } if (localItem.UserDataList != null) { userDataList.AddRange(localItem.UserDataList); } MergeData(localItem, temp, Array.Empty <MetadataField>(), !options.ReplaceAllMetadata, true); refreshResult.UpdateType |= ItemUpdateType.MetadataImport; // Only one local provider allowed per item if (item.IsLocked || localItem.Item.IsLocked || IsFullLocalMetadata(localItem.Item)) { hasLocalMetadata = true; } break; } Logger.LogDebug("{Provider} returned no metadata for {Item}", providerName, logName); } catch (OperationCanceledException) { throw; } catch (Exception ex) { Logger.LogError(ex, "Error in {Provider}", provider.Name); // If a local provider fails, consider that a failure refreshResult.ErrorMessage = ex.Message; } } // Local metadata is king - if any is found don't run remote providers if (!options.ReplaceAllMetadata && (!hasLocalMetadata || options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || !item.StopRefreshIfLocalMetadataFound)) { var remoteResult = await ExecuteRemoteProviders(temp, logName, id, providers.OfType <IRemoteMetadataProvider <TItemType, TIdType> >(), cancellationToken) .ConfigureAwait(false); refreshResult.UpdateType |= remoteResult.UpdateType; refreshResult.ErrorMessage = remoteResult.ErrorMessage; refreshResult.Failures += remoteResult.Failures; } if (providers.Any(i => i is not ICustomMetadataProvider)) { if (refreshResult.UpdateType > ItemUpdateType.None) { if (hasLocalMetadata) { MergeData(temp, metadata, item.LockedFields, true, true); } else { if (!options.RemoveOldMetadata) { MergeData(metadata, temp, Array.Empty <MetadataField>(), false, false); } MergeData(temp, metadata, item.LockedFields, true, false); } } } // var isUnidentified = failedProviderCount > 0 && successfulProviderCount == 0; foreach (var provider in customProviders.Where(i => i is not IPreRefreshProvider)) { await RunCustomProvider(provider, item, logName, options, refreshResult, cancellationToken).ConfigureAwait(false); } // ImportUserData(item, userDataList, cancellationToken); return(refreshResult); }
public Task <ItemUpdateType> FetchAsync(LiveTvAudioRecording item, MetadataRefreshOptions options, CancellationToken cancellationToken) { return(FetchAudioInfo(item, cancellationToken)); }
public async Task <ItemUpdateType> RefreshMetadata(BaseItem item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken) { var itemOfType = (TItemType)item; var updateType = ItemUpdateType.None; var libraryOptions = LibraryManager.GetLibraryOptions(item); var requiresRefresh = libraryOptions.AutomaticRefreshIntervalDays > 0 && (DateTime.UtcNow - item.DateLastRefreshed).TotalDays >= libraryOptions.AutomaticRefreshIntervalDays; if (!requiresRefresh && refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None) { // TODO: If this returns true, should we instead just change metadata refresh mode to Full? requiresRefresh = item.RequiresRefresh(); if (requiresRefresh) { Logger.LogDebug("Refreshing {Type} {Item} because item.RequiresRefresh() returned true", typeof(TItemType).Name, item.Path ?? item.Name); } } var localImagesFailed = false; var allImageProviders = ((ProviderManager)ProviderManager).GetImageProviders(item, refreshOptions).ToList(); if (refreshOptions.RemoveOldMetadata && refreshOptions.ReplaceAllImages) { if (ImageProvider.RemoveImages(item)) { updateType |= ItemUpdateType.ImageUpdate; } } // Start by validating images try { // Always validate images and check for new locally stored ones. if (ImageProvider.ValidateImages(item, allImageProviders.OfType <ILocalImageProvider>(), refreshOptions.DirectoryService)) { updateType |= ItemUpdateType.ImageUpdate; } } catch (Exception ex) { localImagesFailed = true; Logger.LogError(ex, "Error validating images for {Item}", item.Path ?? item.Name ?? "Unknown name"); } var metadataResult = new MetadataResult <TItemType> { Item = itemOfType }; bool hasRefreshedMetadata = true; bool hasRefreshedImages = true; var isFirstRefresh = item.DateLastRefreshed == default; // Next run metadata providers if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None) { var providers = GetProviders(item, libraryOptions, refreshOptions, isFirstRefresh, requiresRefresh) .ToList(); if (providers.Count > 0 || isFirstRefresh || requiresRefresh) { if (item.BeforeMetadataRefresh(refreshOptions.ReplaceAllMetadata)) { updateType |= ItemUpdateType.MetadataImport; } } if (providers.Count > 0) { var id = itemOfType.GetLookupInfo(); if (refreshOptions.SearchResult != null) { ApplySearchResult(id, refreshOptions.SearchResult); } // await FindIdentities(id, cancellationToken).ConfigureAwait(false); id.IsAutomated = refreshOptions.IsAutomated; var result = await RefreshWithProviders(metadataResult, id, refreshOptions, providers, ImageProvider, cancellationToken).ConfigureAwait(false); updateType |= result.UpdateType; if (result.Failures > 0) { hasRefreshedMetadata = false; } } } // Next run remote image providers, but only if local image providers didn't throw an exception if (!localImagesFailed && refreshOptions.ImageRefreshMode != MetadataRefreshMode.ValidationOnly) { var providers = GetNonLocalImageProviders(item, allImageProviders, refreshOptions).ToList(); if (providers.Count > 0) { var result = await ImageProvider.RefreshImages(itemOfType, libraryOptions, providers, refreshOptions, cancellationToken).ConfigureAwait(false); updateType |= result.UpdateType; if (result.Failures > 0) { hasRefreshedImages = false; } } } var beforeSaveResult = BeforeSave(itemOfType, isFirstRefresh || refreshOptions.ReplaceAllMetadata || refreshOptions.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || requiresRefresh || refreshOptions.ForceSave, updateType); updateType |= beforeSaveResult; // Save if changes were made, or it's never been saved before if (refreshOptions.ForceSave || updateType > ItemUpdateType.None || isFirstRefresh || refreshOptions.ReplaceAllMetadata || requiresRefresh) { if (item.IsFileProtocol) { var file = TryGetFile(item.Path, refreshOptions.DirectoryService); if (file != null) { item.DateModified = file.LastWriteTimeUtc; } } // If any of these properties are set then make sure the updateType is not None, just to force everything to save if (refreshOptions.ForceSave || refreshOptions.ReplaceAllMetadata) { updateType |= ItemUpdateType.MetadataDownload; } if (hasRefreshedMetadata && hasRefreshedImages) { item.DateLastRefreshed = DateTime.UtcNow; } else { item.DateLastRefreshed = default; } // Save to database await SaveItemAsync(metadataResult, updateType, cancellationToken).ConfigureAwait(false); } await AfterMetadataRefresh(itemOfType, refreshOptions, cancellationToken).ConfigureAwait(false); return(updateType); }
private void AddToCollection(Guid collectionId, IEnumerable <string> ids, bool fireEvent, MetadataRefreshOptions refreshOptions) { var collection = _libraryManager.GetItemById(collectionId) as BoxSet; if (collection == null) { throw new ArgumentException("No collection exists with the supplied Id"); } var list = new List <LinkedChild>(); var itemList = new List <BaseItem>(); var linkedChildrenList = collection.GetLinkedChildren(); var currentLinkedChildrenIds = linkedChildrenList.Select(i => i.Id).ToList(); foreach (var id in ids) { var guidId = new Guid(id); var item = _libraryManager.GetItemById(guidId); if (item == null) { throw new ArgumentException("No item exists with the supplied Id"); } if (!currentLinkedChildrenIds.Contains(guidId)) { itemList.Add(item); list.Add(LinkedChild.Create(item)); linkedChildrenList.Add(item); } } if (list.Count > 0) { var newList = collection.LinkedChildren.ToList(); newList.AddRange(list); collection.LinkedChildren = newList.ToArray(); collection.UpdateRatingToItems(linkedChildrenList); collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); refreshOptions.ForceSave = true; _providerManager.QueueRefresh(collection.Id, refreshOptions, RefreshPriority.High); if (fireEvent) { ItemsAddedToCollection?.Invoke(this, new CollectionModifiedEventArgs { Collection = collection, ItemsChanged = itemList }); } } }
protected async Task Fetch(Video video, CancellationToken cancellationToken, Model.MediaInfo.MediaInfo mediaInfo, IIsoMount isoMount, BlurayDiscInfo blurayInfo, MetadataRefreshOptions options) { var mediaStreams = mediaInfo.MediaStreams; video.TotalBitrate = mediaInfo.Bitrate; //video.FormatName = (mediaInfo.Container ?? string.Empty) // .Replace("matroska", "mkv", StringComparison.OrdinalIgnoreCase); // For dvd's this may not always be accurate, so don't set the runtime if the item already has one var needToSetRuntime = video.VideoType != VideoType.Dvd || video.RunTimeTicks == null || video.RunTimeTicks.Value == 0; if (needToSetRuntime) { video.RunTimeTicks = mediaInfo.RunTimeTicks; } if (video.VideoType == VideoType.VideoFile) { var extension = (Path.GetExtension(video.Path) ?? string.Empty).TrimStart('.'); video.Container = extension; } else { video.Container = null; } var chapters = mediaInfo.Chapters ?? new List <ChapterInfo>(); if (video.VideoType == VideoType.BluRay || (video.IsoType.HasValue && video.IsoType.Value == IsoType.BluRay)) { FetchBdInfo(video, chapters, mediaStreams, blurayInfo); } await AddExternalSubtitles(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); FetchEmbeddedInfo(video, mediaInfo, options); await FetchPeople(video, mediaInfo, options).ConfigureAwait(false); video.IsHD = mediaStreams.Any(i => i.Type == MediaStreamType.Video && i.Width.HasValue && i.Width.Value >= 1270); var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video); video.VideoBitRate = videoStream == null ? null : videoStream.BitRate; video.DefaultVideoStreamIndex = videoStream == null ? (int?)null : videoStream.Index; video.HasSubtitles = mediaStreams.Any(i => i.Type == MediaStreamType.Subtitle); video.Timestamp = mediaInfo.Timestamp; await _itemRepo.SaveMediaStreams(video.Id, mediaStreams, cancellationToken).ConfigureAwait(false); if (options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || options.MetadataRefreshMode == MetadataRefreshMode.Default) { var chapterOptions = _chapterManager.GetConfiguration(); try { var remoteChapters = await DownloadChapters(video, chapters, chapterOptions, cancellationToken).ConfigureAwait(false); if (remoteChapters.Count > 0) { chapters = remoteChapters; } } catch (Exception ex) { _logger.ErrorException("Error downloading chapters", ex); } if (chapters.Count == 0 && mediaStreams.Any(i => i.Type == MediaStreamType.Video)) { AddDummyChapters(video, chapters); } NormalizeChapterNames(chapters); await _encodingManager.RefreshChapterImages(new ChapterImageRefreshOptions { Chapters = chapters, Video = video, ExtractImages = chapterOptions.ExtractDuringLibraryScan, SaveChapters = false }, cancellationToken).ConfigureAwait(false); await _chapterManager.SaveChapters(video.Id.ToString(), chapters, cancellationToken).ConfigureAwait(false); } }
public Task <ItemUpdateType> FetchAsync(Movie item, MetadataRefreshOptions options, CancellationToken cancellationToken) { return(FetchVideoInfo(item, options, cancellationToken)); }
public async Task <ItemUpdateType> ProbeVideo <T>(T item, MetadataRefreshOptions options, CancellationToken cancellationToken) where T : Video { if (item.IsArchive) { var ext = Path.GetExtension(item.Path) ?? string.Empty; item.Container = ext.TrimStart('.'); return(ItemUpdateType.MetadataImport); } var isoMount = await MountIsoIfNeeded(item, cancellationToken).ConfigureAwait(false); BlurayDiscInfo blurayDiscInfo = null; try { if (item.VideoType == VideoType.BluRay || (item.IsoType.HasValue && item.IsoType == IsoType.BluRay)) { var inputPath = isoMount != null ? isoMount.MountedPath : item.Path; blurayDiscInfo = GetBDInfo(inputPath); } OnPreFetch(item, isoMount, blurayDiscInfo); // If we didn't find any satisfying the min length, just take them all if (item.VideoType == VideoType.Dvd || (item.IsoType.HasValue && item.IsoType == IsoType.Dvd)) { if (item.PlayableStreamFileNames.Count == 0) { _logger.Error("No playable vobs found in dvd structure, skipping ffprobe."); return(ItemUpdateType.MetadataImport); } } if (item.VideoType == VideoType.BluRay || (item.IsoType.HasValue && item.IsoType == IsoType.BluRay)) { if (item.PlayableStreamFileNames.Count == 0) { _logger.Error("No playable vobs found in bluray structure, skipping ffprobe."); return(ItemUpdateType.MetadataImport); } } var result = await GetMediaInfo(item, isoMount, cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); await Fetch(item, cancellationToken, result, isoMount, blurayDiscInfo, options).ConfigureAwait(false); } finally { if (isoMount != null) { isoMount.Dispose(); } } return(ItemUpdateType.MetadataImport); }
/// <summary> /// Compare our current children (presumably just read from the repo) with the current state of the file system and adjust for any changes /// ***Currently does not contain logic to maintain items that are unavailable in the file system*** /// </summary> /// <param name="progress">The progress.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="recursive">if set to <c>true</c> [recursive].</param> /// <param name="refreshChildMetadata">if set to <c>true</c> [refresh child metadata].</param> /// <param name="refreshOptions">The refresh options.</param> /// <param name="directoryService">The directory service.</param> /// <returns>Task.</returns> protected override Task ValidateChildrenInternal(IProgress <double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService) { var list = PhysicalLocationsList.ToList(); CreateResolveArgs(directoryService); if (!list.SequenceEqual(PhysicalLocationsList)) { return(UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken)); } return(Task.FromResult(true)); }
protected override async Task ValidateChildrenInternal(IProgress <double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService) { ClearCache(); await base.ValidateChildrenInternal(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService) .ConfigureAwait(false); ClearCache(); }
/// <summary> /// Gets the providers. /// </summary> /// <param name="item">The item.</param> /// <param name="status">The status.</param> /// <param name="options">The options.</param> /// <returns>IEnumerable{`0}.</returns> protected IEnumerable <IMetadataProvider> GetProviders(IHasMetadata item, MetadataStatus status, MetadataRefreshOptions options) { // Get providers to refresh var providers = ((ProviderManager)ProviderManager).GetMetadataProviders <TItemType>(item).ToList(); // Run all if either of these flags are true var runAllProviders = options.ReplaceAllMetadata || options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || !status.DateLastMetadataRefresh.HasValue; if (!runAllProviders) { // Avoid implicitly captured closure var currentItem = item; var providersWithChanges = providers .Where(i => { var hasChangeMonitor = i as IHasChangeMonitor; if (hasChangeMonitor != null) { return(HasChanged(item, hasChangeMonitor, currentItem.DateLastSaved, options.DirectoryService)); } var hasFileChangeMonitor = i as IHasItemChangeMonitor; if (hasFileChangeMonitor != null) { return(HasChanged(item, hasFileChangeMonitor, status, options.DirectoryService)); } return(false); }) .ToList(); if (providersWithChanges.Count == 0) { providers = new List <IMetadataProvider <TItemType> >(); } else { providers = providers.Where(i => { // If any provider reports a change, always run local ones as well if (i is ILocalMetadataProvider) { return(true); } var anyRemoteProvidersChanged = providersWithChanges.OfType <IRemoteMetadataProvider>() .Any(); // If any remote providers changed, run them all so that priorities can be honored if (i is IRemoteMetadataProvider) { return(anyRemoteProvidersChanged); } // Run custom providers if they report a change or any remote providers change return(anyRemoteProvidersChanged || providersWithChanges.Contains(i)); }).ToList(); } } return(providers); }
/// <summary> /// Validates that the children of the folder still exist /// </summary> /// <param name="progress">The progress.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="metadataRefreshOptions">The metadata refresh options.</param> /// <param name="recursive">if set to <c>true</c> [recursive].</param> /// <returns>Task.</returns> public Task ValidateChildren(IProgress <double> progress, CancellationToken cancellationToken, MetadataRefreshOptions metadataRefreshOptions, bool recursive = true) { return(ValidateChildrenInternal(progress, cancellationToken, recursive, true, metadataRefreshOptions, metadataRefreshOptions.DirectoryService)); }
protected virtual async Task <RefreshResult> RefreshWithProviders(MetadataResult <TItemType> metadata, TIdType id, MetadataRefreshOptions options, List <IMetadataProvider> providers, ItemImageProvider imageService, CancellationToken cancellationToken) { var refreshResult = new RefreshResult { UpdateType = ItemUpdateType.None, Providers = providers.Select(i => i.GetType().FullName.GetMD5()).ToList() }; var item = metadata.Item; var customProviders = providers.OfType <ICustomMetadataProvider <TItemType> >().ToList(); var logName = item.LocationType == LocationType.Remote ? item.Name ?? item.Path : item.Path ?? item.Name; foreach (var provider in customProviders.Where(i => i is IPreRefreshProvider)) { await RunCustomProvider(provider, item, logName, options, refreshResult, cancellationToken).ConfigureAwait(false); } var temp = new MetadataResult <TItemType> { Item = CreateNew() }; temp.Item.Path = item.Path; var successfulProviderCount = 0; var failedProviderCount = 0; var userDataList = new List <UserItemData>(); // If replacing all metadata, run internet providers first if (options.ReplaceAllMetadata) { var remoteResult = await ExecuteRemoteProviders(temp, logName, id, providers.OfType <IRemoteMetadataProvider <TItemType, TIdType> >(), cancellationToken) .ConfigureAwait(false); refreshResult.UpdateType = refreshResult.UpdateType | remoteResult.UpdateType; refreshResult.Status = remoteResult.Status; refreshResult.ErrorMessage = remoteResult.ErrorMessage; successfulProviderCount += remoteResult.Successes; failedProviderCount += remoteResult.Failures; } var hasLocalMetadata = false; foreach (var provider in providers.OfType <ILocalMetadataProvider <TItemType> >().ToList()) { var providerName = provider.GetType().Name; Logger.Debug("Running {0} for {1}", providerName, logName); var itemInfo = new ItemInfo(item); try { var localItem = await provider.GetMetadata(itemInfo, options.DirectoryService, cancellationToken).ConfigureAwait(false); if (localItem.HasMetadata) { if (imageService.MergeImages(item, localItem.Images)) { refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.ImageUpdate; } if (localItem.UserDataList != null) { userDataList.AddRange(localItem.UserDataList); } MergeData(localItem, temp, new List <MetadataFields>(), !options.ReplaceAllMetadata, true); refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataImport; // Only one local provider allowed per item if (IsFullLocalMetadata(localItem.Item)) { hasLocalMetadata = true; } successfulProviderCount++; break; } Logger.Debug("{0} returned no metadata for {1}", providerName, logName); } catch (OperationCanceledException) { throw; } catch (Exception ex) { failedProviderCount++; Logger.ErrorException("Error in {0}", ex, provider.Name); // If a local provider fails, consider that a failure refreshResult.Status = ProviderRefreshStatus.Failure; refreshResult.ErrorMessage = ex.Message; if (options.MetadataRefreshMode != MetadataRefreshMode.FullRefresh) { // If the local provider fails don't continue with remote providers because the user's saved metadata could be lost return(refreshResult); } } } // Local metadata is king - if any is found don't run remote providers if (!options.ReplaceAllMetadata && (!hasLocalMetadata || options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh)) { var remoteResult = await ExecuteRemoteProviders(temp, logName, id, providers.OfType <IRemoteMetadataProvider <TItemType, TIdType> >(), cancellationToken) .ConfigureAwait(false); refreshResult.UpdateType = refreshResult.UpdateType | remoteResult.UpdateType; if (remoteResult.Status != ProviderRefreshStatus.Success) { refreshResult.Status = remoteResult.Status; refreshResult.ErrorMessage = remoteResult.ErrorMessage; } successfulProviderCount += remoteResult.Successes; } if (providers.Any(i => !(i is ICustomMetadataProvider))) { if (refreshResult.UpdateType > ItemUpdateType.None) { // If no local metadata, take data from item itself if (!hasLocalMetadata) { // TODO: If the new metadata from above has some blank data, this can cause old data to get filled into those empty fields MergeData(metadata, temp, new List <MetadataFields>(), false, true); } MergeData(temp, metadata, item.LockedFields, true, true); } } var isUnidentified = failedProviderCount > 0 && successfulProviderCount == 0; if (item.IsUnidentified != isUnidentified) { item.IsUnidentified = isUnidentified; refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataImport; } foreach (var provider in customProviders.Where(i => !(i is IPreRefreshProvider))) { await RunCustomProvider(provider, item, logName, options, refreshResult, cancellationToken).ConfigureAwait(false); } await ImportUserData(item, userDataList, cancellationToken).ConfigureAwait(false); return(refreshResult); }
protected override Task ValidateChildrenInternal(IProgress <double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService) { if (IsAccessedByName) { // Should never get in here anyway return(_cachedTask); } return(base.ValidateChildrenInternal(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService)); }
public async Task <ItemUpdateType> RefreshMetadata(IHasMetadata item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken) { var itemOfType = (TItemType)item; var config = ProviderManager.GetMetadataOptions(item); var updateType = ItemUpdateType.None; var refreshResult = GetLastResult(item); refreshResult.LastErrorMessage = string.Empty; refreshResult.LastStatus = ProviderRefreshStatus.Success; var itemImageProvider = new ItemImageProvider(Logger, ProviderManager, ServerConfigurationManager, FileSystem); var localImagesFailed = false; var allImageProviders = ((ProviderManager)ProviderManager).GetImageProviders(item).ToList(); // Start by validating images try { // Always validate images and check for new locally stored ones. if (itemImageProvider.ValidateImages(item, allImageProviders.OfType <ILocalImageProvider>(), refreshOptions.DirectoryService)) { updateType = updateType | ItemUpdateType.ImageUpdate; } } catch (Exception ex) { localImagesFailed = true; Logger.ErrorException("Error validating images for {0}", ex, item.Path ?? item.Name ?? "Unknown name"); refreshResult.AddStatus(ProviderRefreshStatus.Failure, ex.Message); } var metadataResult = new MetadataResult <TItemType> { Item = itemOfType }; // Next run metadata providers if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None) { var providers = GetProviders(item, refreshResult, refreshOptions) .ToList(); if (providers.Count > 0 || !refreshResult.DateLastMetadataRefresh.HasValue) { if (item.BeforeMetadataRefresh()) { updateType = updateType | ItemUpdateType.MetadataImport; } } if (providers.Count > 0) { var id = await CreateInitialLookupInfo(itemOfType, cancellationToken).ConfigureAwait(false); var result = await RefreshWithProviders(metadataResult, id, refreshOptions, providers, itemImageProvider, cancellationToken).ConfigureAwait(false); updateType = updateType | result.UpdateType; refreshResult.AddStatus(result.Status, result.ErrorMessage); refreshResult.SetDateLastMetadataRefresh(DateTime.UtcNow); MergeIdentities(itemOfType, id); } } // Next run remote image providers, but only if local image providers didn't throw an exception if (!localImagesFailed && refreshOptions.ImageRefreshMode != ImageRefreshMode.ValidationOnly) { var providers = GetNonLocalImageProviders(item, allImageProviders, refreshResult, refreshOptions).ToList(); if (providers.Count > 0) { var result = await itemImageProvider.RefreshImages(itemOfType, providers, refreshOptions, config, cancellationToken).ConfigureAwait(false); updateType = updateType | result.UpdateType; refreshResult.AddStatus(result.Status, result.ErrorMessage); refreshResult.SetDateLastImagesRefresh(DateTime.UtcNow); } } var beforeSaveResult = await BeforeSave(itemOfType, item.DateLastSaved == default(DateTime) || refreshOptions.ReplaceAllMetadata || refreshOptions.MetadataRefreshMode == MetadataRefreshMode.FullRefresh, updateType).ConfigureAwait(false); updateType = updateType | beforeSaveResult; // Save if changes were made, or it's never been saved before if (refreshOptions.ForceSave || updateType > ItemUpdateType.None || item.DateLastSaved == default(DateTime) || refreshOptions.ReplaceAllMetadata) { // If any of these properties are set then make sure the updateType is not None, just to force everything to save if (refreshOptions.ForceSave || refreshOptions.ReplaceAllMetadata) { updateType = updateType | ItemUpdateType.MetadataDownload; } // Save to database await SaveItem(metadataResult, updateType, cancellationToken).ConfigureAwait(false); } if (updateType > ItemUpdateType.None || refreshResult.IsDirty) { await SaveProviderResult(itemOfType, refreshResult, refreshOptions.DirectoryService).ConfigureAwait(false); } await AfterMetadataRefresh(itemOfType, refreshOptions, cancellationToken).ConfigureAwait(false); return(updateType); }
public override bool RefreshMetadata(MetadataRefreshOptions options) { bool changed = false; string path = Plugin.proxy == null ? this.Path : Plugin.proxy.ProxyUrl(this); if (this.Path != path) { this.Path = path; changed = true; } MediaType = MediaTypeResolver.DetermineType(Path); if ((options & MetadataRefreshOptions.FastOnly) != MetadataRefreshOptions.FastOnly && Plugin.PluginOptions.Instance.FetchBackdrops && string.IsNullOrEmpty(this.BackdropImagePath)) { if (this.BackdropImagePath == null) { // use our own movieDBProvider to grab just the backdrops var provider = new BackdropProvider(); provider.Item = (Movie)Serializer.Clone(this); provider.Fetch(); this.BackdropImagePaths = provider.Item.BackdropImagePaths; foreach (var image in this.BackdropImages) { try { if (image != null) { image.ClearLocalImages(); MediaBrowser.Library.Factories.LibraryImageFactory.Instance.ClearCache(image.Path); var ignore = image.GetLocalImagePath(); } } catch (Exception ex) { MediaBrowser.Library.Logging.Logger.ReportException("Failed to clear local image (its probably in use)", ex); } } changed = true; } } if ((options & MetadataRefreshOptions.Force) == MetadataRefreshOptions.Force) { //force images to refresh var images = new List<MediaBrowser.Library.ImageManagement.LibraryImage>(); images.Add(PrimaryImage); images.Add(SecondaryImage); images.Add(BannerImage); foreach (var image in images) { try { if (image != null) { image.ClearLocalImages(); MediaBrowser.Library.Factories.LibraryImageFactory.Instance.ClearCache(image.Path); } } catch (Exception ex) { MediaBrowser.Library.Logging.Logger.ReportException("Failed to clear local image (its probably in use)", ex); } } changed = true; } if (changed) MediaBrowser.Library.Kernel.Instance.ItemRepository.SaveItem(this); return changed; }
protected virtual Task AfterMetadataRefresh(TItemType item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken) { item.AfterMetadataRefresh(); return(Task.CompletedTask); }
bool FullRefresh(AggregateFolder folder, MetadataRefreshOptions options, ServiceRefreshOptions manualOptions) { int phases = manualOptions.AnyImageOptionsSelected || manualOptions.MigrateOption ? 3 : 2; double totalIterations = 0; UpdateProgress("Determining Library Size", 0); //this will trap any circular references in the library tree try { Async.RunWithTimeout(() => { totalIterations = folder.AllRecursiveChildren.Count() * phases; }, 600000); } catch (TimeoutException) { Logger.ReportError("ERROR DURING REFRESH. Timed out attempting to retrieve count of all items. Most likely there is a circular reference in your library. Look for *.lnk files that might be pointing back to a parent of the folder that contatians that link."); _config.RefreshFailed = true; _config.Save(); return false; } if (totalIterations == 0) return true; //nothing to do int currentIteration = 0; UpdateProgress("Validating Root", 0); folder.RefreshMetadata(options); using (new Profiler(Kernel.Instance.GetString("FullValidationProf"))) { if (!RunActionRecursively(folder, item => { currentIteration++; UpdateProgress("Validating",currentIteration / totalIterations); var f = item as Folder; if (f != null) { f.ValidateChildren(); } })) return false; } string msg = (options & MetadataRefreshOptions.FastOnly) == MetadataRefreshOptions.FastOnly ? "Not allowing internet and other slow providers." : "Allowing internet and other slow providers."; Logger.ReportInfo(msg); var processedItems = new HashSet<Guid>(); using (new Profiler(Kernel.Instance.GetString("SlowRefresh"))) { if (!RunActionRecursively(folder, item => { currentIteration++; UpdateProgress("All Metadata",(currentIteration / totalIterations)); if (!processedItems.Contains(item.Id)) //only process any given item once (could be multiple refs to same item) { if (manualOptions.MigrateOption) { MigratePlayState(item); } item.RefreshMetadata(options); processedItems.Add(item.Id); //Logger.ReportInfo(item.Name + " id: " + item.Id); //test //throw new InvalidOperationException("Test Error..."); } })) return false; } if (manualOptions.AnyImageOptionsSelected || manualOptions.MigrateOption) { processedItems.Clear(); using (new Profiler(Kernel.Instance.GetString("ImageRefresh"))) { var studiosProcessed = new List<string>(); var genresProcessed = new List<string>(); var peopleProcessed = new List<string>(); var yearsProcessed = new List<string>(); if (!RunActionRecursively(folder, item => { currentIteration++; UpdateProgress("Images",(currentIteration / totalIterations)); if (!processedItems.Contains(item.Id)) { if (manualOptions.IncludeImagesOption) //main images { Logger.ReportInfo("Caching all images for " + item.Name ); item.ReCacheAllImages(); } if (manualOptions.MigrateOption) //migrate main images { item.MigrateAllImages(); } // optionally cause genre, poeple, year and studio images to cache as well if (item is Show) { var show = item as Show; if ((manualOptions.IncludeGenresOption || manualOptions.MigrateOption) && show.Genres != null) { foreach (var genre in show.Genres) { if (!genresProcessed.Contains(genre)) { Genre g = Genre.GetGenre(genre); g.RefreshMetadata(); if (g.PrimaryImage != null) { if (manualOptions.IncludeGenresOption) { Logger.ReportInfo("Caching image for genre: " + genre); g.PrimaryImage.ClearLocalImages(); g.PrimaryImage.GetLocalImagePath(); } if (manualOptions.MigrateOption) { Logger.ReportInfo("Migrating image for genre: " + genre); g.PrimaryImage.MigrateFromOldID(); } } foreach (MediaBrowser.Library.ImageManagement.LibraryImage image in g.BackdropImages) { if (manualOptions.IncludeGenresOption) { image.GetLocalImagePath(); } if (manualOptions.MigrateOption) { image.MigrateFromOldID(); } } genresProcessed.Add(genre); } } } if ((manualOptions.IncludeStudiosOption || manualOptions.MigrateOption) && show.Studios != null) { foreach (var studio in show.Studios) { if (!studiosProcessed.Contains(studio)) { Logger.ReportInfo("Caching image for studio: " + studio); Studio st = Studio.GetStudio(studio); st.RefreshMetadata(); if (st.PrimaryImage != null) { if (manualOptions.IncludeStudiosOption) { Logger.ReportInfo("Caching image for studio: " + studio); st.PrimaryImage.ClearLocalImages(); st.PrimaryImage.GetLocalImagePath(); } if (manualOptions.MigrateOption) { Logger.ReportInfo("Migrating image for studio: " + studio); st.PrimaryImage.MigrateFromOldID(); } } foreach (MediaBrowser.Library.ImageManagement.LibraryImage image in st.BackdropImages) { if (manualOptions.IncludeStudiosOption) { image.ClearLocalImages(); image.GetLocalImagePath(); } if (manualOptions.MigrateOption) { image.MigrateFromOldID(); } } studiosProcessed.Add(studio); } } } if ((manualOptions.IncludePeopleOption || manualOptions.MigrateOption) && show.Actors != null) { foreach (var actor in show.Actors) { if (!peopleProcessed.Contains(actor.Name)) { Person p = Person.GetPerson(actor.Name); p.RefreshMetadata(); if (p.PrimaryImage != null) { if (manualOptions.IncludePeopleOption) { Logger.ReportInfo("Caching image for person: " + actor.Name); p.PrimaryImage.ClearLocalImages(); p.PrimaryImage.GetLocalImagePath(); } if (manualOptions.MigrateOption) { Logger.ReportInfo("Migrating image for person: " + actor.Name); p.PrimaryImage.MigrateFromOldID(); } } foreach (MediaBrowser.Library.ImageManagement.LibraryImage image in p.BackdropImages) { if (manualOptions.IncludePeopleOption) { image.ClearLocalImages(); image.GetLocalImagePath(); } if (manualOptions.MigrateOption) { image.MigrateFromOldID(); } } peopleProcessed.Add(actor.Name); } } } if (manualOptions.IncludePeopleOption && show.Directors != null) { foreach (var director in show.Directors) { if (!peopleProcessed.Contains(director)) { Person p = Person.GetPerson(director); p.RefreshMetadata(); if (p.PrimaryImage != null) { Logger.ReportInfo("Caching image for person: " + director); p.PrimaryImage.ClearLocalImages(); p.PrimaryImage.GetLocalImagePath(); } foreach (MediaBrowser.Library.ImageManagement.LibraryImage image in p.BackdropImages) { image.ClearLocalImages(); image.GetLocalImagePath(); } peopleProcessed.Add(director); } } } if (manualOptions.IncludeYearOption && show.ProductionYear != null) { if (!yearsProcessed.Contains(show.ProductionYear.ToString())) { Year yr = Year.GetYear(show.ProductionYear.ToString()); yr.RefreshMetadata(); if (yr.PrimaryImage != null) { Logger.ReportInfo("Caching image for year: " + yr); yr.PrimaryImage.ClearLocalImages(); yr.PrimaryImage.GetLocalImagePath(); } foreach (MediaBrowser.Library.ImageManagement.LibraryImage image in yr.BackdropImages) { image.ClearLocalImages(); image.GetLocalImagePath(); } yearsProcessed.Add(show.ProductionYear.ToString()); } } } processedItems.Add(item.Id); } else Logger.ReportInfo("Not processing " + item.Name + " again."); })) return false; } } return true; }
static void FullRefresh(Folder folder, MetadataRefreshOptions options) { int totalItems = 0; int fastMetadataChanged = 0; int slowMetadataChanged = 0; folder.RefreshMetadata(options); Console.Out.WriteLine(); Console.Out.WriteLine("===[Validate]============================================"); Console.Out.WriteLine(); var validationTime = TimeAction(() => { RunActionRecursively("validate", folder, item => { Folder f = item as Folder; if (f != null) f.ValidateChildren(); }); }); Console.Out.WriteLine(); Console.Out.WriteLine("===[Fast Metadata]======================================="); Console.Out.WriteLine(); var fastMetadataTime = TimeAction(() => { RunActionRecursively("fast metadata", folder, item => { fastMetadataChanged += item.RefreshMetadata(MetadataRefreshOptions.FastOnly) ? 1 : 0; totalItems++; }); }); if (rebuildImageCache) { Console.Out.WriteLine(); Console.Out.WriteLine("===[Recreate ImageCache]================================="); Console.Out.WriteLine(); Console.Out.WriteLine("/i specified - Clearing Image Cache for re-build.."); Console.Out.WriteLine(); try { Console.Out.WriteLine("Deleting ImageCache folder."); Directory.Delete(ApplicationPaths.AppImagePath, true); } catch (Exception ex) { Console.Out.WriteLine("Error trying to delete ImageCache folder. " + ex.Message); } Console.Out.WriteLine("Sleeping 2 seconds."); System.Threading.Thread.Sleep(2000); // give it time to fully delete Console.Out.WriteLine("Continuing."); try { Console.Out.WriteLine("Creating ImageCache folder."); Directory.CreateDirectory(ApplicationPaths.AppImagePath); } catch (Exception ex) { Console.Out.WriteLine("Error trying to create ImageCache folder. " + ex.Message); } Console.Out.WriteLine("Sleeping 2 seconds."); System.Threading.Thread.Sleep(2000); // give it time to fully create Console.Out.WriteLine("Continuing."); } Console.Out.WriteLine(); Console.Out.WriteLine("===[Slow Metadata]======================================="); Console.Out.WriteLine(); var slowMetadataTime = TimeAction(() => { RunActionRecursively("slow metadata", folder, item => { slowMetadataChanged += item.RefreshMetadata(MetadataRefreshOptions.Default) ? 1 : 0; if (rebuildImageCache) { //touch all the images - causing them to be re-cached Console.Out.WriteLine("Caching images for " + item.Name); string ignore = null; if (item.PrimaryImage != null) ignore = item.PrimaryImage.GetLocalImagePath(); if (item.SecondaryImage != null) ignore = item.SecondaryImage.GetLocalImagePath(); if (item.BackdropImages != null) foreach (var image in item.BackdropImages) { ignore = image.GetLocalImagePath(); } if (item.BannerImage != null) ignore = item.BannerImage.GetLocalImagePath(); } }); }); Console.Out.WriteLine(); Console.Out.WriteLine("===[Saving LastFullRefresh]=============================="); Console.Out.WriteLine(); Console.Out.WriteLine("Saving LastFullRefresh in config"); Kernel.Instance.ConfigData.LastFullRefresh = DateTime.Now; Kernel.Instance.ConfigData.Save(); Console.Out.WriteLine(); Console.Out.WriteLine("===[Results]============================================="); Console.Out.WriteLine(); Console.Out.WriteLine("We are done"); Console.Out.WriteLine(); Console.Out.WriteLine("Validation took: " + (new DateTime(validationTime.Ticks)).ToString("HH:mm:ss")); Console.Out.WriteLine("Fast metadata took: " + (new DateTime(fastMetadataTime.Ticks)).ToString("HH:mm:ss")); Console.Out.WriteLine("Slow metadata took: " + (new DateTime(slowMetadataTime.Ticks)).ToString("HH:mm:ss")); Console.Out.WriteLine("Total items in your library: {0}", totalItems); Console.Out.WriteLine(); Console.Out.WriteLine("Fast metadata changed on {0} item's", fastMetadataChanged); Console.Out.WriteLine("Slow metadata changed on {0} item's", slowMetadataChanged); Console.Out.WriteLine(); Console.Out.WriteLine("===[EOF]=================================================="); }