diff options
7 files changed, 159 insertions, 74 deletions
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 338edd568..ae34621cb 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -116,6 +116,11 @@ namespace MediaBrowser.Controller.Library Task ValidateMediaLibrary(IProgress<double> progress, CancellationToken cancellationToken); /// <summary> + /// Queues the library scan. + /// </summary> + void QueueLibraryScan(); + + /// <summary> /// Gets the default view. /// </summary> /// <returns>IEnumerable{VirtualFolderInfo}.</returns> diff --git a/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs b/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs index e53acfc02..fad204c69 100644 --- a/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs +++ b/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs @@ -1,7 +1,8 @@ -using MediaBrowser.Common.IO; +using System.Globalization; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.IO; using MediaBrowser.Common.MediaInfo; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; @@ -19,18 +20,6 @@ namespace MediaBrowser.Controller.MediaInfo /// </summary> public class FFMpegManager { - /// <summary> - /// Gets or sets the video image cache. - /// </summary> - /// <value>The video image cache.</value> - internal FileSystemRepository VideoImageCache { get; set; } - - /// <summary> - /// Gets or sets the subtitle cache. - /// </summary> - /// <value>The subtitle cache.</value> - internal FileSystemRepository SubtitleCache { get; set; } - private readonly IServerApplicationPaths _appPaths; private readonly IMediaEncoder _encoder; private readonly ILogger _logger; @@ -53,32 +42,17 @@ namespace MediaBrowser.Controller.MediaInfo _logger = logger; _itemRepo = itemRepo; _fileSystem = fileSystem; - - VideoImageCache = new FileSystemRepository(VideoImagesDataPath); - SubtitleCache = new FileSystemRepository(SubtitleCachePath); } /// <summary> - /// Gets the video images data path. + /// Gets the chapter images data path. /// </summary> - /// <value>The video images data path.</value> - public string VideoImagesDataPath + /// <value>The chapter images data path.</value> + public string ChapterImagesPath { get { - return Path.Combine(_appPaths.DataPath, "extracted-video-images"); - } - } - - /// <summary> - /// Gets the audio images data path. - /// </summary> - /// <value>The audio images data path.</value> - public string AudioImagesDataPath - { - get - { - return Path.Combine(_appPaths.DataPath, "extracted-audio-images"); + return Path.Combine(_appPaths.DataPath, "chapter-images"); } } @@ -86,7 +60,7 @@ namespace MediaBrowser.Controller.MediaInfo /// Gets the subtitle cache path. /// </summary> /// <value>The subtitle cache path.</value> - public string SubtitleCachePath + private string SubtitleCachePath { get { @@ -122,6 +96,8 @@ namespace MediaBrowser.Controller.MediaInfo var runtimeTicks = video.RunTimeTicks ?? 0; + var currentImages = GetSavedChapterImages(video); + foreach (var chapter in chapters) { if (chapter.StartPositionTicks >= runtimeTicks) @@ -130,11 +106,9 @@ namespace MediaBrowser.Controller.MediaInfo break; } - var filename = video.Path + "_" + video.DateModified.Ticks + "_" + chapter.StartPositionTicks; - - var path = VideoImageCache.GetResourcePath(filename, ".jpg"); + var path = GetChapterImagePath(video, chapter.StartPositionTicks); - if (!File.Exists(path)) + if (!currentImages.Contains(path, StringComparer.OrdinalIgnoreCase)) { if (extractImages) { @@ -188,9 +162,35 @@ namespace MediaBrowser.Controller.MediaInfo await _itemRepo.SaveChapters(video.Id, chapters, cancellationToken).ConfigureAwait(false); } + DeleteDeadImages(currentImages, chapters); + return success; } + private void DeleteDeadImages(IEnumerable<string> images, IEnumerable<ChapterInfo> chapters) + { + var deadImages = images + .Except(chapters.Select(i => i.ImagePath), StringComparer.OrdinalIgnoreCase) + .Where(i => BaseItem.SupportedImageExtensions.Contains(Path.GetExtension(i), StringComparer.OrdinalIgnoreCase)) + .ToList(); + + foreach (var image in deadImages) + { + _logger.Debug("Deleting dead chapter image {0}", image); + + try + { + File.Delete(image); + } + catch (IOException ex) + { + _logger.ErrorException("Error deleting {0}.", ex, image); + } + } + } + + private readonly CultureInfo UsCulture = new CultureInfo("en-US"); + /// <summary> /// Gets the subtitle cache path. /// </summary> @@ -220,7 +220,39 @@ namespace MediaBrowser.Controller.MediaInfo ticksParam += _fileSystem.GetLastWriteTimeUtc(stream.Path).Ticks; } - return SubtitleCache.GetResourcePath(input.Id + "_" + subtitleStreamIndex + "_" + input.DateModified.Ticks + ticksParam, outputExtension); + var filename = (input.Id + "_" + subtitleStreamIndex.ToString(UsCulture) + "_" + input.DateModified.Ticks.ToString(UsCulture) + ticksParam).GetMD5() + outputExtension; + + var prefix = filename.Substring(0, 1); + + return Path.Combine(SubtitleCachePath, prefix, filename); + } + + public string GetChapterImagePath(Video video, long chapterPositionTicks) + { + var filename = video.DateModified.Ticks.ToString(UsCulture) + "_" + chapterPositionTicks.ToString(UsCulture) + ".jpg"; + + var videoId = video.Id.ToString(); + var prefix = videoId.Substring(0, 1); + + return Path.Combine(ChapterImagesPath, prefix, videoId, filename); + } + + public List<string> GetSavedChapterImages(Video video) + { + var videoId = video.Id.ToString(); + var prefix = videoId.Substring(0, 1); + + var path = Path.Combine(ChapterImagesPath, prefix, videoId); + + try + { + return Directory.EnumerateFiles(path) + .ToList(); + } + catch (DirectoryNotFoundException) + { + return new List<string>(); + } } } } diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs index 264b24b87..5782e3e63 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs @@ -1,6 +1,5 @@ -using MediaBrowser.Common.IO; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.MediaInfo; -using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -23,12 +22,6 @@ namespace MediaBrowser.Providers.MediaInfo public class AudioImageProvider : BaseMetadataProvider { /// <summary> - /// Gets or sets the image cache. - /// </summary> - /// <value>The image cache.</value> - public FileSystemRepository ImageCache { get; set; } - - /// <summary> /// The _locks /// </summary> private readonly ConcurrentDictionary<string, SemaphoreSlim> _locks = new ConcurrentDictionary<string, SemaphoreSlim>(); @@ -48,8 +41,6 @@ namespace MediaBrowser.Providers.MediaInfo : base(logManager, configurationManager) { _mediaEncoder = mediaEncoder; - - ImageCache = new FileSystemRepository(Kernel.Instance.FFMpegManager.AudioImagesDataPath); } /// <summary> @@ -113,7 +104,7 @@ namespace MediaBrowser.Providers.MediaInfo return ItemUpdateType.ImageUpdate; } } - + /// <summary> /// Fetches metadata and returns true or false indicating if any work that requires persistence was done /// </summary> @@ -154,13 +145,7 @@ namespace MediaBrowser.Providers.MediaInfo { cancellationToken.ThrowIfCancellationRequested(); - var album = item.Parent as MusicAlbum; - - var filename = item.Album ?? string.Empty; - filename += item.Artists.FirstOrDefault() ?? string.Empty; - filename += album == null ? item.Id.ToString("N") + item.DateModified.Ticks : album.Id.ToString("N") + album.DateModified.Ticks; - - var path = ImageCache.GetResourcePath(filename + "_primary", ".jpg"); + var path = GetAudioImagePath(item); if (!File.Exists(path)) { @@ -196,6 +181,38 @@ namespace MediaBrowser.Providers.MediaInfo } /// <summary> + /// Gets the audio image path. + /// </summary> + /// <param name="item">The item.</param> + /// <returns>System.String.</returns> + private string GetAudioImagePath(Audio item) + { + var album = item.Parent as MusicAlbum; + + var filename = item.Album ?? string.Empty; + filename += item.Artists.FirstOrDefault() ?? string.Empty; + filename += album == null ? item.Id.ToString("N") + item.DateModified.Ticks : album.Id.ToString("N") + album.DateModified.Ticks + "_primary"; + + filename = filename.GetMD5() + ".jpg"; + + var prefix = filename.Substring(0, 1); + + return Path.Combine(AudioImagesPath, prefix, filename); + } + + /// <summary> + /// Gets the audio images data path. + /// </summary> + /// <value>The audio images data path.</value> + public string AudioImagesPath + { + get + { + return Path.Combine(ConfigurationManager.ApplicationPaths.DataPath, "extracted-audio-images"); + } + } + + /// <summary> /// Gets the lock. /// </summary> /// <param name="filename">The filename.</param> diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs index 2864983ce..b35d5e07e 100644 --- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs @@ -1,6 +1,5 @@ -using MediaBrowser.Common.IO; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.MediaInfo; -using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -12,7 +11,6 @@ using MediaBrowser.Model.Logging; using System; using System.Collections.Concurrent; using System.IO; -using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -21,12 +19,6 @@ namespace MediaBrowser.Providers.MediaInfo class VideoImageProvider : BaseMetadataProvider { /// <summary> - /// Gets or sets the image cache. - /// </summary> - /// <value>The image cache.</value> - public FileSystemRepository ImageCache { get; set; } - - /// <summary> /// The _locks /// </summary> private readonly ConcurrentDictionary<string, SemaphoreSlim> _locks = new ConcurrentDictionary<string, SemaphoreSlim>(); @@ -42,8 +34,6 @@ namespace MediaBrowser.Providers.MediaInfo { _mediaEncoder = mediaEncoder; _isoManager = isoManager; - - ImageCache = new FileSystemRepository(Kernel.Instance.FFMpegManager.VideoImagesDataPath); } /// <summary> @@ -206,9 +196,7 @@ namespace MediaBrowser.Providers.MediaInfo { cancellationToken.ThrowIfCancellationRequested(); - var filename = item.Path + "_" + item.DateModified.Ticks + "_primary"; - - var path = ImageCache.GetResourcePath(filename, ".jpg"); + var path = GetVideoImagePath(item); if (!File.Exists(path)) { @@ -310,5 +298,33 @@ namespace MediaBrowser.Providers.MediaInfo { return _locks.GetOrAdd(filename, key => new SemaphoreSlim(1, 1)); } + + /// <summary> + /// Gets the video images data path. + /// </summary> + /// <value>The video images data path.</value> + public string VideoImagesPath + { + get + { + return Path.Combine(ConfigurationManager.ApplicationPaths.DataPath, "extracted-video-images"); + } + } + + /// <summary> + /// Gets the audio image path. + /// </summary> + /// <param name="item">The item.</param> + /// <returns>System.String.</returns> + private string GetVideoImagePath(Video item) + { + var filename = item.Path + "_" + item.DateModified.Ticks + "_primary"; + + filename = filename.GetMD5() + ".jpg"; + + var prefix = filename.Substring(0, 1); + + return Path.Combine(VideoImagesPath, prefix, filename); + } } } diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 791beb941..11c99a32c 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -899,6 +899,15 @@ namespace MediaBrowser.Server.Implementations.Library } /// <summary> + /// Queues the library scan. + /// </summary> + public void QueueLibraryScan() + { + // Just run the scheduled task so that the user can see it + _taskManager.QueueScheduledTask<RefreshMediaLibraryTask>(); + } + + /// <summary> /// Validates the media library internal. /// </summary> /// <param name="progress">The progress.</param> diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 37794cb3d..e06b99999 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -420,14 +420,20 @@ namespace MediaBrowser.Server.Implementations.LiveTv { var info = _tvDtoService.GetTimerInfo(timer); - return ActiveService.UpdateTimerAsync(info, cancellationToken); + var service = GetServices(timer.ServiceName, null) + .First(); + + return service.UpdateTimerAsync(info, cancellationToken); } public Task UpdateSeriesTimer(SeriesTimerInfoDto timer, CancellationToken cancellationToken) { var info = _tvDtoService.GetSeriesTimerInfo(timer); - return ActiveService.UpdateSeriesTimerAsync(info, cancellationToken); + var service = GetServices(timer.ServiceName, null) + .First(); + + return service.UpdateSeriesTimerAsync(info, cancellationToken); } public async Task<QueryResult<SeriesTimerInfoDto>> GetSeriesTimers(SeriesTimerQuery query, CancellationToken cancellationToken) diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs index 9270b879a..7af077785 100644 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs +++ b/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs @@ -145,7 +145,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks var numComplete = 0; - var failHistoryPath = Path.Combine(_kernel.FFMpegManager.VideoImagesDataPath, "failures.txt"); + var failHistoryPath = Path.Combine(_kernel.FFMpegManager.ChapterImagesPath, "failures.txt"); List<string> previouslyFailedImages; |
