/// <summary> /// Reads all tag images and caches them in the <see cref="IFanArtCache"/> service. /// </summary> /// <param name="lfsra"><see cref="ILocalFsResourceAccessor>"/> for the file.</param> /// <param name="mediaItemId">Id of the media item.</param> /// <param name="title">Title of the media item.</param> /// <returns><see cref="Task"/> that completes when the images have been cached.</returns> protected async Task ExtractTagFanArt(ILocalFsResourceAccessor lfsra, Guid mediaItemId, string title) { TagLib.File tag; if (!TryCreateTagReader(lfsra, out tag)) { return; } using (tag) { IFanArtCache fanArtCache = ServiceRegistration.Get <IFanArtCache>(); IPicture[] pics = tag.Tag.Pictures; if (pics.Length > 0) { string filename = Path.GetFileNameWithoutExtension(lfsra.LocalFileSystemPath); bool imageFound = false; foreach (var pic in pics) { if (pic.Type == PictureType.FrontCover || pic.Type == PictureType.Other) { imageFound = true; await fanArtCache.TrySaveFanArt(mediaItemId, title, FanArtTypes.Thumbnail, p => TrySaveFileImage(pic.Data.Data, p, filename)).ConfigureAwait(false); } } if (!imageFound) //If no matching image type found, use first image { await fanArtCache.TrySaveFanArt(mediaItemId, title, FanArtTypes.Thumbnail, p => TrySaveFileImage(pics[0].Data.Data, p, filename)).ConfigureAwait(false); } } } }
protected virtual async Task <int> SaveFanArtImagesAsync(string id, IEnumerable <TImg> images, TLang language, Guid mediaItemId, string name, string fanArtType) { if (images == null || !images.Any()) { return(0); } var validImages = images.Where(i => VerifyFanArtImage(i, language, fanArtType)).ToList(); IFanArtCache fanArtCache = ServiceRegistration.Get <IFanArtCache>(); int count = await fanArtCache.TrySaveFanArt(mediaItemId, name, fanArtType, validImages, (p, i) => SaveFanArtImageAsync(id, i, p, mediaItemId, name)).ConfigureAwait(false); Logger.Debug(_id + @" Download: Saved {0} for media item {1} ({2}) of type {3}", count, mediaItemId, name, fanArtType); return(count); }
protected async Task <bool> TrySaveFanArt(IFanArtCache fanArtCache, string fanArtType, string fanArtName, HashSet <byte[]> fanArtData, Guid mediaItemId, string mediaName) { if (fanArtData == null || fanArtData.Count == 0) { return(false); } bool addCount = fanArtData.Count > 1; int count = 0; foreach (var data in fanArtData) { await fanArtCache.TrySaveFanArt(mediaItemId, mediaName, fanArtType, p => TrySaveFileImage(data, p, $"{fanArtName}{(addCount ? (count++).ToString() : "")}", "Nfo.")); } return(true); }
/// <summary> /// Reads all tag images and caches them in the <see cref="IFanArtCache"/> service. /// </summary> /// <param name="lfsra"><see cref="ILocalFsResourceAccessor>"/> for the file.</param> /// <param name="mediaItemId">Id of the media item.</param> /// <param name="title">Title of the media item.</param> /// <returns><see cref="Task"/> that completes when the images have been cached.</returns> protected async Task ExtractTagFanArt(ILocalFsResourceAccessor lfsra, Guid mediaItemId, string title) { TagLib.File tag; if (!TryCreateTagReader(lfsra, out tag)) { return; } using (tag) { IFanArtCache fanArtCache = ServiceRegistration.Get <IFanArtCache>(); IPicture[] pics = tag.Tag.Pictures; if (pics.Length > 0) { string filename = Path.GetFileNameWithoutExtension(lfsra.LocalFileSystemPath); await fanArtCache.TrySaveFanArt(mediaItemId, title, FanArtTypes.Thumbnail, p => TrySaveFileImage(pics[0].Data.Data, p, filename)).ConfigureAwait(false); } } }
/// <summary> /// Reads all mkv tag images and caches them in the <see cref="IFanArtCache"/> service. /// </summary> /// <param name="lfsra"><see cref="ILocalFsResourceAccessor>"/> for the file.</param> /// <param name="mediaItemId">Id of the media item.</param> /// <param name="title">Title of the media item.</param> /// <returns><see cref="Task"/> that completes when the images have been cached.</returns> protected async Task ExtractMkvFanArt(ILocalFsResourceAccessor lfsra, Guid mediaItemId, string title) { if (lfsra == null) { return; } MatroskaBinaryReader mkvReader = new MatroskaBinaryReader(lfsra); IFanArtCache fanArtCache = ServiceRegistration.Get <IFanArtCache>(); foreach (var pattern in MKV_PATTERNS) { byte[] binaryData = await mkvReader.GetAttachmentByNameAsync(pattern.Item1).ConfigureAwait(false); if (binaryData == null) { continue; } string filename = pattern + Path.GetFileNameWithoutExtension(lfsra.LocalFileSystemPath); await fanArtCache.TrySaveFanArt(mediaItemId, title, pattern.Item2, p => TrySaveFileImage(binaryData, p, filename)).ConfigureAwait(false); } }
protected async Task <bool> TrySaveThumbStubs(IFanArtCache fanArtCache, HashSet <SeriesThumbStub> thumbs, int?season, Guid mediaItemId, string mediaItemName) { if (thumbs == null || thumbs.Count == 0) { return(false); } HashSet <byte[]> posters = new HashSet <byte[]>(); HashSet <byte[]> banners = new HashSet <byte[]>(); HashSet <byte[]> fanart = new HashSet <byte[]>(); foreach (var thumbStub in thumbs) { if ((!season.HasValue && thumbStub.Season == null) || (season.HasValue && thumbStub.Season == season)) { if (thumbStub.Aspect == SeriesThumbStub.ThumbAspect.Poster || !thumbStub.Aspect.HasValue) { posters.Add(thumbStub.Thumb); } else if (thumbStub.Aspect == SeriesThumbStub.ThumbAspect.Banner) { banners.Add(thumbStub.Thumb); } else if (thumbStub.Aspect == SeriesThumbStub.ThumbAspect.Fanart) { fanart.Add(thumbStub.Thumb); } } } await TrySaveFanArt(fanArtCache, FanArtTypes.Poster, "Poster", posters, mediaItemId, mediaItemName); await TrySaveFanArt(fanArtCache, FanArtTypes.Banner, "Banner", banners, mediaItemId, mediaItemName); await TrySaveFanArt(fanArtCache, FanArtTypes.FanArt, "FanArt", fanart, mediaItemId, mediaItemName); return(true); }
public override async Task CollectFanArtAsync(Guid mediaItemId, IDictionary <Guid, IList <MediaItemAspect> > aspects) { IResourceLocator mediaItemLocator = null; if (!BaseInfo.IsVirtualResource(aspects)) { mediaItemLocator = GetResourceLocator(aspects); } if (!aspects.ContainsKey(EpisodeAspect.ASPECT_ID) || mediaItemLocator == null) { return; } IFanArtCache fanArtCache = ServiceRegistration.Get <IFanArtCache>(); using (IResourceAccessor mediaItemAccessor = mediaItemLocator.CreateAccessor()) { EpisodeInfo episodeInfo = new EpisodeInfo(); if (!episodeInfo.FromMetadata(aspects)) { return; } //Episode fanart if (AddToCache(mediaItemId)) { var existingThumbs = fanArtCache.GetFanArtFiles(mediaItemId, FanArtTypes.Thumbnail); int?season = episodeInfo.SeasonNumber; int?episode = episodeInfo.EpisodeNumbers != null && episodeInfo.EpisodeNumbers.Any() ? episodeInfo.EpisodeNumbers.First() : (int?)null; if (!existingThumbs.Any()) //Only get thumb if needed for better performance { NfoSeriesEpisodeReader episodeReader = await SERIES_EXTRACTOR.TryGetNfoSeriesEpisodeReaderAsync(mediaItemAccessor, season, episode, true).ConfigureAwait(false); if (episodeReader != null) { var stubs = episodeReader.GetEpisodeStubs(); var mainStub = stubs?.FirstOrDefault(); if (mainStub?.Thumb != null) { await fanArtCache.TrySaveFanArt(mediaItemId, episodeInfo.ToString(), FanArtTypes.Thumbnail, p => TrySaveFileImage(mainStub.Thumb, p, "Thumb", "Nfo.")).ConfigureAwait(false); } } } } //Series fanart if (RelationshipExtractorUtils.TryGetLinkedId(SeriesAspect.ROLE_SERIES, aspects, out Guid seriesMediaItemId)) { IList <Tuple <Guid, string> > actors = GetActors(aspects); RelationshipExtractorUtils.TryGetLinkedId(SeasonAspect.ROLE_SEASON, aspects, out Guid seasonMediaItemId); //Check if loading nfo is needed if ((actors?.All(a => IsInCache(a.Item1)) ?? true) && IsInCache(seriesMediaItemId) && (seasonMediaItemId == Guid.Empty || IsInCache(seasonMediaItemId))) { return; //Everything was already saved } NfoSeriesReader seriesNfoReader = await SERIES_EXTRACTOR.TryGetNfoSeriesReaderAsync(mediaItemAccessor, true).ConfigureAwait(false); if (seriesNfoReader != null) { var stubs = seriesNfoReader.GetSeriesStubs(); var mainStub = stubs?.FirstOrDefault(); if (AddToCache(seriesMediaItemId)) { var series = episodeInfo.CloneBasicInstance <SeriesInfo>(); if (mainStub?.Thumbs?.Count > 0) { await TrySaveThumbStubs(fanArtCache, mainStub.Thumbs, null, seriesMediaItemId, series.ToString()); } } if (seasonMediaItemId != Guid.Empty && episodeInfo.SeasonNumber.HasValue && AddToCache(seasonMediaItemId)) { var season = episodeInfo.CloneBasicInstance <SeasonInfo>(); if (mainStub?.Thumbs?.Count > 0) { await TrySaveThumbStubs(fanArtCache, mainStub.Thumbs, episodeInfo.SeasonNumber, seasonMediaItemId, season.ToString()); } } //Actor fanart //We only want the series actors because thumb loading is disabled on episode actors for performance reasons, so we might need to //load the series nfo multiple time before we have all actors depending on what actors are in the episode foreach (var actor in actors) { if (!IsInCache(actor.Item1)) { var existingThumbs = fanArtCache.GetFanArtFiles(actor.Item1, FanArtTypes.Thumbnail); var actorStub = mainStub?.Actors?.FirstOrDefault(a => string.Equals(a?.Name, actor.Item2, StringComparison.InvariantCultureIgnoreCase)); if (actorStub != null || existingThumbs.Any()) //We have a thumb already or no thumb is available, so no need to check again { AddToCache(actor.Item1); } if (actorStub?.Thumb != null) { await fanArtCache.TrySaveFanArt(actor.Item1, actor.Item2, FanArtTypes.Thumbnail, p => TrySaveFileImage(actorStub.Thumb, p, "Thumb", "Nfo.")).ConfigureAwait(false); } } } } } } }
/// <summary> /// Gets a list of <see cref="FanArtImage"/>s for a requested <paramref name="mediaType"/>, <paramref name="fanArtType"/> and <paramref name="name"/>. /// The name can be: Series name, Actor name, Artist name depending on the <paramref name="mediaType"/>. /// </summary> /// <param name="mediaType">Requested FanArtMediaType</param> /// <param name="fanArtType">Requested FanArtType</param> /// <param name="name">Requested name of Series, Actor, Artist...</param> /// <param name="maxWidth">Maximum width for image. <c>0</c> returns image in original size.</param> /// <param name="maxHeight">Maximum height for image. <c>0</c> returns image in original size.</param> /// <param name="singleRandom">If <c>true</c> only one random image URI will be returned</param> /// <param name="result">Result if return code is <c>true</c>.</param> /// <returns><c>true</c> if at least one match was found.</returns> public bool TryGetFanArt(string mediaType, string fanArtType, string name, int maxWidth, int maxHeight, bool singleRandom, out IList <IResourceLocator> result) { result = null; if (!VALID_MEDIA_TYPES.Contains(mediaType) || !VALID_FANART_TYPES.Contains(fanArtType)) { return(false); } if (string.IsNullOrWhiteSpace(name)) { return(false); } Guid mediaItemId; if (Guid.TryParse(name, out mediaItemId) == false) { return(false); } IFanArtCache fanArtCache = ServiceRegistration.Get <IFanArtCache>(); List <string> fanArtFiles = new List <string>(); fanArtFiles.AddRange(fanArtCache.GetFanArtFiles(mediaItemId, fanArtType)); if (fanArtFiles.Count == 0 && fanArtType == FanArtTypes.Poster) { fanArtFiles.AddRange(fanArtCache.GetFanArtFiles(mediaItemId, FanArtTypes.Cover)); } // Try fallback if (fanArtFiles.Count == 0 && (mediaType == FanArtMediaTypes.Audio || mediaType == FanArtMediaTypes.Album)) { IMediaLibrary mediaLibrary = ServiceRegistration.Get <IMediaLibrary>(false); if (mediaLibrary == null) { return(false); } IFilter filter = new MediaItemIdFilter(mediaItemId); IList <MediaItem> items = mediaLibrary.Search(new MediaItemQuery(NECESSARY_MIAS, OPTIONAL_MIAS, filter), false, null, true); if (items == null || items.Count == 0) { return(false); } MediaItem mediaItem = items.First(); IList <MultipleMediaItemAspect> relationAspects; if (mediaType == FanArtMediaTypes.Audio && fanArtType == FanArtTypes.FanArt) { //No FanArt exists for Audio so use Artists if (MediaItemAspect.TryGetAspects(mediaItem.Aspects, RelationshipAspect.Metadata, out relationAspects)) { //Artist fallback foreach (MultipleMediaItemAspect relation in relationAspects) { if ((Guid?)relation[RelationshipAspect.ATTR_LINKED_ROLE] == PersonAspect.ROLE_ARTIST) { fanArtFiles.AddRange(fanArtCache.GetFanArtFiles((Guid)relation[RelationshipAspect.ATTR_LINKED_ID], fanArtType)); if (fanArtFiles.Count > 0) { break; } } } } } else if (mediaType == FanArtMediaTypes.Album && fanArtType == FanArtTypes.FanArt) { //No FanArt exists for Album so use Artists if (MediaItemAspect.TryGetAspects(mediaItem.Aspects, RelationshipAspect.Metadata, out relationAspects)) { //Album artist fallback foreach (MultipleMediaItemAspect relation in relationAspects) { if ((Guid?)relation[RelationshipAspect.ATTR_LINKED_ROLE] == PersonAspect.ROLE_ALBUMARTIST) { fanArtFiles.AddRange(fanArtCache.GetFanArtFiles((Guid)relation[RelationshipAspect.ATTR_LINKED_ID], fanArtType)); if (fanArtFiles.Count > 0) { break; } } } } } else if (mediaItem.Aspects.ContainsKey(AudioAspect.ASPECT_ID)) { if (MediaItemAspect.TryGetAspects(mediaItem.Aspects, RelationshipAspect.Metadata, out relationAspects)) { //Album fallback foreach (MultipleMediaItemAspect relation in relationAspects) { if ((Guid?)relation[RelationshipAspect.ATTR_LINKED_ROLE] == AudioAlbumAspect.ROLE_ALBUM) { fanArtFiles.AddRange(fanArtCache.GetFanArtFiles((Guid)relation[RelationshipAspect.ATTR_LINKED_ID], fanArtType)); if (fanArtFiles.Count > 0) { break; } } } } } } List <IResourceLocator> files = new List <IResourceLocator>(); try { files.AddRange(fanArtFiles .Select(fileName => new ResourceLocator(ResourcePath.BuildBaseProviderPath(LocalFsResourceProviderBase.LOCAL_FS_RESOURCE_PROVIDER_ID, fileName))) ); result = files; return(result.Count > 0); } catch (Exception) { } return(false); }
public override async Task CollectFanArtAsync(Guid mediaItemId, IDictionary <Guid, IList <MediaItemAspect> > aspects) { IResourceLocator mediaItemLocator = null; if (!BaseInfo.IsVirtualResource(aspects)) { mediaItemLocator = GetResourceLocator(aspects); } if (!aspects.ContainsKey(AudioAspect.ASPECT_ID) || mediaItemLocator == null) { return; } IFanArtCache fanArtCache = ServiceRegistration.Get <IFanArtCache>(); using (IResourceAccessor mediaItemAccessor = mediaItemLocator.CreateAccessor()) { //Album fanart if (RelationshipExtractorUtils.TryGetLinkedId(AudioAlbumAspect.ROLE_ALBUM, aspects, out Guid albumMediaItemId) && AddToCache(albumMediaItemId)) { var existingCovers = fanArtCache.GetFanArtFiles(albumMediaItemId, FanArtTypes.Cover); if (!existingCovers.Any()) //Only get album cover if needed for better performance { NfoAlbumReader albumNfoReader = await AUDIO_EXTRACTOR.TryGetNfoAlbumReaderAsync(mediaItemAccessor, true).ConfigureAwait(false); if (albumNfoReader != null) { var stubs = albumNfoReader.GetAlbumStubs(); var mainStub = stubs?.FirstOrDefault(); if (mainStub?.Thumb != null) { await fanArtCache.TrySaveFanArt(albumMediaItemId, mainStub.Title, FanArtTypes.Cover, p => TrySaveFileImage(mainStub.Thumb, p, "Thumb", "Nfo.")).ConfigureAwait(false); } } } } //Artist fanart IList <Tuple <Guid, string> > artists = GetArtists(aspects); if (artists?.Count > 0) { foreach (var artist in artists) { var existingThumbs = fanArtCache.GetFanArtFiles(artist.Item1, FanArtTypes.Thumbnail); if (!existingThumbs.Any() && AddToCache(artist.Item1)) //Only get artist thumbnail if needed for better performance { NfoArtistReader artistReader = await AUDIO_EXTRACTOR.TryGetNfoArtistReaderAsync(mediaItemAccessor, artist.Item2, true).ConfigureAwait(false); if (artistReader != null) { var stubs = artistReader.GetArtistStubs(); var mainStub = stubs?.FirstOrDefault(); if (string.Equals(mainStub?.Name, artist.Item2, StringComparison.InvariantCultureIgnoreCase)) { if (mainStub?.Thumb != null) { await fanArtCache.TrySaveFanArt(artist.Item1, artist.Item2, FanArtTypes.Thumbnail, p => TrySaveFileImage(mainStub.Thumb, p, "Thumb", "Nfo.")).ConfigureAwait(false); } } } } } } } }
public override async Task CollectFanArtAsync(Guid mediaItemId, IDictionary <Guid, IList <MediaItemAspect> > aspects) { IResourceLocator mediaItemLocator = null; if (!BaseInfo.IsVirtualResource(aspects)) { mediaItemLocator = GetResourceLocator(aspects); } if (!aspects.ContainsKey(MovieAspect.ASPECT_ID) || mediaItemLocator == null) { return; } IFanArtCache fanArtCache = ServiceRegistration.Get <IFanArtCache>(); using (IResourceAccessor mediaItemAccessor = mediaItemLocator.CreateAccessor()) { IList <Tuple <Guid, string> > actors = GetActors(aspects); //Check if loading nfo is needed if ((actors?.All(a => IsInCache(a.Item1)) ?? true) && IsInCache(mediaItemId)) { return; //Everything was already saved } NfoMovieReader movieNfoReader = await MOVIE_EXTRACTOR.TryGetNfoMovieReaderAsync(mediaItemAccessor, true).ConfigureAwait(false); if (movieNfoReader != null) { //Movie fanart var stubs = movieNfoReader.GetMovieStubs(); var mainStub = stubs?.FirstOrDefault(); if (AddToCache(mediaItemId)) { if (mainStub?.Thumb != null) { await fanArtCache.TrySaveFanArt(mediaItemId, mainStub.Title, FanArtTypes.Poster, p => TrySaveFileImage(mainStub.Thumb, p, "Thumb", "Nfo.")).ConfigureAwait(false); } await TrySaveFanArt(fanArtCache, FanArtTypes.FanArt, "FanArt", mainStub?.FanArt, mediaItemId, mainStub?.Title).ConfigureAwait(false); await TrySaveFanArt(fanArtCache, FanArtTypes.DiscArt, "DiscArt", mainStub?.DiscArt, mediaItemId, mainStub?.Title).ConfigureAwait(false); await TrySaveFanArt(fanArtCache, FanArtTypes.Logo, "Logo", mainStub?.Logos, mediaItemId, mainStub?.Title).ConfigureAwait(false); await TrySaveFanArt(fanArtCache, FanArtTypes.ClearArt, "ClearArt", mainStub?.ClearArt, mediaItemId, mainStub?.Title).ConfigureAwait(false); await TrySaveFanArt(fanArtCache, FanArtTypes.Banner, "Banner", mainStub?.Banners, mediaItemId, mainStub?.Title).ConfigureAwait(false); await TrySaveFanArt(fanArtCache, FanArtTypes.Thumbnail, "Landscape", mainStub?.Landscape, mediaItemId, mainStub?.Title).ConfigureAwait(false); } //Actor fanart if (actors != null) { foreach (var actor in actors) { if (!IsInCache(actor.Item1)) { var existingThumbs = fanArtCache.GetFanArtFiles(actor.Item1, FanArtTypes.Thumbnail); var actorStub = mainStub?.Actors?.FirstOrDefault(a => string.Equals(a?.Name, actor.Item2, StringComparison.InvariantCultureIgnoreCase)); if (actorStub != null || existingThumbs.Any()) //We have a thumb already or no thumb is available, so no need to check again { AddToCache(actor.Item1); } if (actorStub?.Thumb != null) { await fanArtCache.TrySaveFanArt(actor.Item1, actor.Item2, FanArtTypes.Thumbnail, p => TrySaveFileImage(actorStub.Thumb, p, "Thumb", "Nfo.")).ConfigureAwait(false); } } } } } } }
/// <summary> /// Asynchronously saves all images contained in the specified paths to the <see cref="IFanArtCache"/> service. /// </summary> /// <param name="nativeSystemId">The native system id of the paths contained in the paths collection.</param> /// <param name="paths">Collection of image paths to save.</param> /// <param name="fanArtType">The fanart type of the images.</param> /// <param name="mediaItemId">The id of the media item to which the images belong.</param> /// <param name="title">The title of the media item to which the images belong.</param> /// <returns>A <see cref="Task"/> that completes when the images have been saved to the <see cref="IFanArtCache"/> service.</returns> protected async Task SaveFolderImagesToCache(string nativeSystemId, ICollection <ResourcePath> paths, string fanArtType, Guid mediaItemId, string title) { IFanArtCache fanArtCache = ServiceRegistration.Get <IFanArtCache>(); await fanArtCache.TrySaveFanArt(mediaItemId, title, fanArtType, paths, (p, f) => TrySaveFolderImage(nativeSystemId, f, p)).ConfigureAwait(false); }
/// <summary> /// Extracts a frame image and caches them in the <see cref="IFanArtCache"/> service. /// </summary> /// <param name="lfsra"><see cref="ILocalFsResourceAccessor>"/> for the file.</param> /// <param name="mediaItemId">Id of the media item.</param> /// <param name="title">Title of the media item.</param> /// <returns><see cref="Task"/> that completes when the images have been cached.</returns> protected async Task ExtractThumbnailFanArt(ILocalFsResourceAccessor lfsra, Guid mediaItemId, string title, IDictionary <Guid, IList <MediaItemAspect> > aspects) { IFanArtCache fanArtCache = ServiceRegistration.Get <IFanArtCache>(); string filename = $"OpenCv.{Path.GetFileNameWithoutExtension(lfsra.LocalFileSystemPath)}"; // Check for a reasonable time offset int defaultVideoOffset = 720; long videoDuration; double width = 0; double height = 0; double downscale = 7.5; // Reduces the HD video frame size to a quarter size to around 256 IList <MultipleMediaItemAspect> videoAspects; if (MediaItemAspect.TryGetAspects(aspects, VideoStreamAspect.Metadata, out videoAspects)) { if ((videoDuration = videoAspects[0].GetAttributeValue <long>(VideoStreamAspect.ATTR_DURATION)) > 0) { if (defaultVideoOffset > videoDuration * DEFAULT_OPENCV_THUMBNAIL_OFFSET) { defaultVideoOffset = Convert.ToInt32(videoDuration * DEFAULT_OPENCV_THUMBNAIL_OFFSET); } } width = videoAspects[0].GetAttributeValue <int>(VideoStreamAspect.ATTR_WIDTH); height = videoAspects[0].GetAttributeValue <int>(VideoStreamAspect.ATTR_HEIGHT); downscale = width / 256.0; //256 is max size of large thumbnail aspect } var sw = Stopwatch.StartNew(); using (VideoCapture capture = new VideoCapture()) { capture.Open(lfsra.LocalFileSystemPath); int capturePos = defaultVideoOffset * 1000; if (capture.FrameCount > 0 && capture.Fps > 0) { var duration = capture.FrameCount / capture.Fps; if (defaultVideoOffset > duration) { capturePos = Convert.ToInt32(duration * DEFAULT_OPENCV_THUMBNAIL_OFFSET * 1000); } } if (capture.FrameWidth > 0) { downscale = capture.FrameWidth / 256.0; //256 is max size of large thumbnail aspect } capture.PosMsec = capturePos; using (var mat = capture.RetrieveMat()) { if (mat.Height > 0 && mat.Width > 0) { width = mat.Width; height = mat.Height; Logger.Debug("VideoFanArtHandler: Scaling thumbnail of size {1}x{2} for resource '{0}'", lfsra.LocalFileSystemPath, width, height); using (var scaledMat = mat.Resize(new OpenCvSharp.Size(width / downscale, height / downscale))) { var binary = scaledMat.ToBytes(); await fanArtCache.TrySaveFanArt(mediaItemId, title, FanArtTypes.Thumbnail, p => TrySaveFileImage(binary, p, filename)).ConfigureAwait(false); Logger.Debug("VideoFanArtHandler: Successfully created thumbnail for resource '{0}' ({1} ms)", lfsra.LocalFileSystemPath, sw.ElapsedMilliseconds); } } else { Logger.Warn("VideoFanArtHandler: Failed to create thumbnail for resource '{0}'", lfsra.LocalFileSystemPath); } } } }