protected async Task Fetch(Video video, CancellationToken cancellationToken, InternalMediaInfoResult data, IIsoMount isoMount, BlurayDiscInfo blurayInfo, IDirectoryService directoryService) { 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; } } var mediaStreams = MediaEncoderHelpers.GetMediaInfo(data).MediaStreams; var chapters = data.Chapters ?? new List <ChapterInfo>(); if (video.VideoType == VideoType.BluRay || (video.IsoType.HasValue && video.IsoType.Value == IsoType.BluRay)) { FetchBdInfo(video, chapters, mediaStreams, blurayInfo); } AddExternalSubtitles(video, mediaStreams, directoryService); FetchWtvInfo(video, data); video.IsHD = mediaStreams.Any(i => i.Type == MediaStreamType.Video && i.Width.HasValue && i.Width.Value >= 1270); if (chapters.Count == 0 && mediaStreams.Any(i => i.Type == MediaStreamType.Video)) { AddDummyChapters(video, chapters); } 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); await _encodingManager.RefreshChapterImages(new ChapterImageRefreshOptions { Chapters = chapters, Video = video, ExtractImages = false, SaveChapters = false }, cancellationToken).ConfigureAwait(false); await _itemRepo.SaveMediaStreams(video.Id, mediaStreams, cancellationToken).ConfigureAwait(false); await _itemRepo.SaveChapters(video.Id, chapters, cancellationToken).ConfigureAwait(false); }
private async void NewItemTimerCallback(object state) { List <Video> newItems; // Lock the list and release all resources lock (_newlyAddedItems) { newItems = _newlyAddedItems.DistinctBy(i => i.Id).ToList(); _newlyAddedItems.Clear(); NewItemTimer.Dispose(); NewItemTimer = null; } // Limit to video files to reduce changes of ffmpeg crash dialog foreach (var item in newItems .Where(i => i.LocationType == LocationType.FileSystem && i.VideoType == VideoType.VideoFile && string.IsNullOrEmpty(i.PrimaryImagePath) && i.DefaultVideoStreamIndex.HasValue) .Take(1)) { try { var chapters = _itemRepo.GetChapters(item.Id).ToList(); await _encodingManager.RefreshChapterImages(new ChapterImageRefreshOptions { SaveChapters = true, ExtractImages = true, Video = item, Chapters = chapters }, CancellationToken.None); } catch (Exception ex) { _logger.ErrorException("Error creating image for {0}", ex, item.Name); } } }
protected async Task Fetch(Video video, CancellationToken cancellationToken, Model.MediaInfo.MediaInfo mediaInfo, BlurayDiscInfo blurayInfo, MetadataRefreshOptions options) { List <MediaStream> mediaStreams; List <ChapterInfo> chapters; if (mediaInfo != null) { 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; } video.Size = mediaInfo.Size; if (video.VideoType == VideoType.VideoFile) { var extension = (Path.GetExtension(video.Path) ?? string.Empty).TrimStart('.'); video.Container = extension; } else { video.Container = null; } video.Container = mediaInfo.Container; chapters = mediaInfo.Chapters == null ? new List <ChapterInfo>() : mediaInfo.Chapters.ToList(); if (blurayInfo != null) { FetchBdInfo(video, chapters, mediaStreams, blurayInfo); } } else { mediaStreams = new List <MediaStream>(); chapters = new List <ChapterInfo>(); } await AddExternalSubtitles(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); var libraryOptions = _libraryManager.GetLibraryOptions(video); if (mediaInfo != null) { FetchEmbeddedInfo(video, mediaInfo, options, libraryOptions); FetchPeople(video, mediaInfo, options); video.Timestamp = mediaInfo.Timestamp; video.Video3DFormat = video.Video3DFormat ?? mediaInfo.Video3DFormat; } var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video); video.Height = videoStream == null ? 0 : videoStream.Height ?? 0; video.Width = videoStream == null ? 0 : videoStream.Width ?? 0; video.DefaultVideoStreamIndex = videoStream == null ? (int?)null : videoStream.Index; video.HasSubtitles = mediaStreams.Any(i => i.Type == MediaStreamType.Subtitle); _itemRepo.SaveMediaStreams(video.Id, mediaStreams, cancellationToken); if (options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || options.MetadataRefreshMode == MetadataRefreshMode.Default) { if (chapters.Count == 0 && mediaStreams.Any(i => i.Type == MediaStreamType.Video)) { AddDummyChapters(video, chapters); } NormalizeChapterNames(chapters); var extractDuringScan = false; if (libraryOptions != null) { extractDuringScan = libraryOptions.ExtractChapterImagesDuringLibraryScan; } await _encodingManager.RefreshChapterImages(video, options.DirectoryService, chapters, extractDuringScan, false, cancellationToken).ConfigureAwait(false); _chapterManager.SaveChapters(video.Id.ToString(), chapters); } }
/// <summary> /// Returns the task to be executed /// </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 videos = _libraryManager.GetItemList(new InternalItemsQuery { MediaTypes = new[] { MediaType.Video }, IsFolder = false, Recursive = true, DtoOptions = new DtoOptions(false) { EnableImages = false }, SourceTypes = new SourceType[] { SourceType.Library }, HasChapterImages = false, IsVirtualItem = false }) .OfType <Video>() .ToList(); var numComplete = 0; var failHistoryPath = Path.Combine(_appPaths.CachePath, "chapter-failures.txt"); List <string> previouslyFailedImages; if (File.Exists(failHistoryPath)) { try { previouslyFailedImages = File.ReadAllText(failHistoryPath) .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) .ToList(); } catch (IOException) { previouslyFailedImages = new List <string>(); } } else { previouslyFailedImages = new List <string>(); } var directoryService = new DirectoryService(_logger, _fileSystem); foreach (var video in videos) { cancellationToken.ThrowIfCancellationRequested(); var key = video.Path + video.DateModified.Ticks; var extract = !previouslyFailedImages.Contains(key, StringComparer.OrdinalIgnoreCase); try { var chapters = _itemRepo.GetChapters(video); var success = await _encodingManager.RefreshChapterImages(video, directoryService, chapters, extract, true, cancellationToken).ConfigureAwait(false); if (!success) { previouslyFailedImages.Add(key); var parentPath = Path.GetDirectoryName(failHistoryPath); Directory.CreateDirectory(parentPath); string text = string.Join("|", previouslyFailedImages); File.WriteAllText(failHistoryPath, text); } numComplete++; double percent = numComplete; percent /= videos.Count; progress.Report(100 * percent); } catch (ObjectDisposedException) { //TODO Investigate and properly fix. break; } } }
/// <summary> /// Returns the task to be executed /// </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 videos = _libraryManager.GetItemList(new InternalItemsQuery { MediaTypes = new[] { MediaType.Video }, IsFolder = false, Recursive = true, DtoOptions = new DtoOptions(false) }) .OfType <Video>() .ToList(); var numComplete = 0; var failHistoryPath = Path.Combine(_appPaths.CachePath, "chapter-failures.txt"); List <string> previouslyFailedImages; try { previouslyFailedImages = _fileSystem.ReadAllText(failHistoryPath) .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) .ToList(); } catch (FileNotFoundException) { previouslyFailedImages = new List <string>(); } catch (IOException) { previouslyFailedImages = new List <string>(); } foreach (var video in videos) { cancellationToken.ThrowIfCancellationRequested(); var key = video.Path + video.DateModified.Ticks; var extract = !previouslyFailedImages.Contains(key, StringComparer.OrdinalIgnoreCase); try { var chapters = _itemRepo.GetChapters(video.Id); var success = await _encodingManager.RefreshChapterImages(video, chapters, extract, true, CancellationToken.None); if (!success) { previouslyFailedImages.Add(key); var parentPath = _fileSystem.GetDirectoryName(failHistoryPath); _fileSystem.CreateDirectory(parentPath); _fileSystem.WriteAllText(failHistoryPath, string.Join("|", previouslyFailedImages.ToArray(previouslyFailedImages.Count))); } numComplete++; double percent = numComplete; percent /= videos.Count; progress.Report(100 * percent); } catch (ObjectDisposedException) { break; } } }
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); } }
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 (blurayInfo != null) { 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); } }
protected async Task Fetch( Video video, CancellationToken cancellationToken, Model.MediaInfo.MediaInfo mediaInfo, BlurayDiscInfo blurayInfo, MetadataRefreshOptions options) { List <MediaStream> mediaStreams; IReadOnlyList <MediaAttachment> mediaAttachments; ChapterInfo[] chapters; if (mediaInfo != null) { mediaStreams = mediaInfo.MediaStreams.ToList(); mediaAttachments = mediaInfo.MediaAttachments; 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; } video.Size = mediaInfo.Size; if (video.VideoType == VideoType.VideoFile) { var extension = (Path.GetExtension(video.Path) ?? string.Empty).TrimStart('.'); video.Container = extension; } else { video.Container = null; } video.Container = mediaInfo.Container; chapters = mediaInfo.Chapters ?? Array.Empty <ChapterInfo>(); if (blurayInfo != null) { FetchBdInfo(video, ref chapters, mediaStreams, blurayInfo); } } else { mediaStreams = new List <MediaStream>(); mediaAttachments = Array.Empty <MediaAttachment>(); chapters = Array.Empty <ChapterInfo>(); } await AddExternalSubtitles(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); await AddExternalAudioAsync(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); var libraryOptions = _libraryManager.GetLibraryOptions(video); if (mediaInfo != null) { FetchEmbeddedInfo(video, mediaInfo, options, libraryOptions); FetchPeople(video, mediaInfo, options); video.Timestamp = mediaInfo.Timestamp; video.Video3DFormat ??= mediaInfo.Video3DFormat; } if (libraryOptions.AllowEmbeddedSubtitles == EmbeddedSubtitleOptions.AllowText || libraryOptions.AllowEmbeddedSubtitles == EmbeddedSubtitleOptions.AllowNone) { _logger.LogDebug("Disabling embedded image subtitles for {Path} due to DisableEmbeddedImageSubtitles setting", video.Path); mediaStreams.RemoveAll(i => i.Type == MediaStreamType.Subtitle && !i.IsExternal && !i.IsTextSubtitleStream); } if (libraryOptions.AllowEmbeddedSubtitles == EmbeddedSubtitleOptions.AllowImage || libraryOptions.AllowEmbeddedSubtitles == EmbeddedSubtitleOptions.AllowNone) { _logger.LogDebug("Disabling embedded text subtitles for {Path} due to DisableEmbeddedTextSubtitles setting", video.Path); mediaStreams.RemoveAll(i => i.Type == MediaStreamType.Subtitle && !i.IsExternal && i.IsTextSubtitleStream); } var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video); video.Height = videoStream?.Height ?? 0; video.Width = videoStream?.Width ?? 0; video.DefaultVideoStreamIndex = videoStream?.Index; video.HasSubtitles = mediaStreams.Any(i => i.Type == MediaStreamType.Subtitle); _itemRepo.SaveMediaStreams(video.Id, mediaStreams, cancellationToken); _itemRepo.SaveMediaAttachments(video.Id, mediaAttachments, cancellationToken); if (options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || options.MetadataRefreshMode == MetadataRefreshMode.Default) { if (chapters.Length == 0 && mediaStreams.Any(i => i.Type == MediaStreamType.Video)) { chapters = CreateDummyChapters(video); } NormalizeChapterNames(chapters); var extractDuringScan = false; if (libraryOptions != null) { extractDuringScan = libraryOptions.ExtractChapterImagesDuringLibraryScan; } await _encodingManager.RefreshChapterImages(video, options.DirectoryService, chapters, extractDuringScan, false, cancellationToken).ConfigureAwait(false); _chapterManager.SaveChapters(video.Id, chapters); } }
/// <summary> /// Returns the task to be executed /// </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 videos = _libraryManager.RootFolder.GetRecursiveChildren(i => i is Video) .Cast <Video>() .ToList(); var numComplete = 0; var failHistoryPath = Path.Combine(_appPaths.CachePath, "chapter-failures.txt"); List <string> previouslyFailedImages; try { previouslyFailedImages = File.ReadAllText(failHistoryPath) .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) .ToList(); } catch (FileNotFoundException) { previouslyFailedImages = new List <string>(); } catch (DirectoryNotFoundException) { previouslyFailedImages = new List <string>(); } foreach (var video in videos) { cancellationToken.ThrowIfCancellationRequested(); var key = video.Path + video.DateModified.Ticks; var extract = !previouslyFailedImages.Contains(key, StringComparer.OrdinalIgnoreCase); var chapters = _itemRepo.GetChapters(video.Id).ToList(); var success = await _encodingManager.RefreshChapterImages(new ChapterImageRefreshOptions { SaveChapters = true, ExtractImages = extract, Video = video, Chapters = chapters }, CancellationToken.None); if (!success) { previouslyFailedImages.Add(key); var parentPath = Path.GetDirectoryName(failHistoryPath); Directory.CreateDirectory(parentPath); File.WriteAllText(failHistoryPath, string.Join("|", previouslyFailedImages.ToArray())); } numComplete++; double percent = numComplete; percent /= videos.Count; progress.Report(100 * percent); } }