diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index 68e7d0686..967c449c2 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -696,7 +696,8 @@ namespace MediaBrowser.Api.Images Item = item, Request = request, CropWhiteSpace = request.Type == ImageType.Logo || request.Type == ImageType.Art, - OriginalImageDateModified = originalFileImageDateModified + OriginalImageDateModified = originalFileImageDateModified, + Enhancers = supportedImageEnhancers }, contentType); } diff --git a/MediaBrowser.Api/Images/ImageWriter.cs b/MediaBrowser.Api/Images/ImageWriter.cs index c130364fb..e77a2f9b0 100644 --- a/MediaBrowser.Api/Images/ImageWriter.cs +++ b/MediaBrowser.Api/Images/ImageWriter.cs @@ -1,5 +1,6 @@ using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Providers; using ServiceStack.Service; using ServiceStack.ServiceHost; using System; @@ -14,6 +15,8 @@ namespace MediaBrowser.Api.Images /// public class ImageWriter : IStreamWriter, IHasOptions { + public List Enhancers; + /// /// Gets or sets the request. /// @@ -67,7 +70,7 @@ namespace MediaBrowser.Api.Images { return Kernel.Instance.ImageManager.ProcessImage(Item, Request.Type, Request.Index ?? 0, CropWhiteSpace, OriginalImageDateModified, responseStream, Request.Width, Request.Height, Request.MaxWidth, - Request.MaxHeight, Request.Quality); + Request.MaxHeight, Request.Quality, Enhancers); } } } diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 8b07145de..9d5f73141 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -340,6 +340,13 @@ namespace MediaBrowser.Api.Playback try { + var parentPath = Path.GetDirectoryName(path); + + if (!Directory.Exists(parentPath)) + { + Directory.CreateDirectory(parentPath); + } + var task = MediaEncoder.ExtractTextSubtitle(inputPath, type, subtitleStream.Index, offset, path, CancellationToken.None); Task.WaitAll(task); @@ -371,6 +378,13 @@ namespace MediaBrowser.Api.Playback { try { + var parentPath = Path.GetDirectoryName(path); + + if (!Directory.Exists(parentPath)) + { + Directory.CreateDirectory(parentPath); + } + var task = MediaEncoder.ConvertTextSubtitleToAss(subtitleStream.Path, path, offset, CancellationToken.None); Task.WaitAll(task); diff --git a/MediaBrowser.Api/SimilarItemsHelper.cs b/MediaBrowser.Api/SimilarItemsHelper.cs index e64ccb11d..bd4aecd0d 100644 --- a/MediaBrowser.Api/SimilarItemsHelper.cs +++ b/MediaBrowser.Api/SimilarItemsHelper.cs @@ -1,6 +1,5 @@ using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Entities; @@ -38,6 +37,29 @@ namespace MediaBrowser.Api /// The limit. [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? Limit { get; set; } + + /// + /// Fields to return within the items, in addition to basic information + /// + /// The fields. + [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: AudioInfo, Budget, Chapters, CriticRatingSummary, DateCreated, DisplayMediaType, EndDate, Genres, HomePageUrl, ItemCounts, IndexOptions, Locations, MediaStreams, Overview, OverviewHtml, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SeriesInfo, SortName, Studios, Taglines, TrailerUrls, UserData", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + public string Fields { get; set; } + + /// + /// Gets the item fields. + /// + /// IEnumerable{ItemFields}. + public IEnumerable GetItemFields() + { + var val = Fields; + + if (string.IsNullOrEmpty(val)) + { + return new ItemFields[] { }; + } + + return val.Split(',').Select(v => (ItemFields)Enum.Parse(typeof(ItemFields), v, true)); + } } /// @@ -64,8 +86,7 @@ namespace MediaBrowser.Api (request.UserId.HasValue ? user.RootFolder : (Folder)libraryManager.RootFolder) : DtoBuilder.GetItemByClientId(request.Id, userManager, libraryManager, request.UserId); - // Get everything - var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)).ToList(); + var fields = request.GetItemFields().ToList(); var dtoBuilder = new DtoBuilder(logger, libraryManager, userDataRepository); diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index 94fb3ce86..ab6b3786d 100644 --- a/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -123,9 +123,14 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks { try { - return JsonSerializer.DeserializeFromFile(GetHistoryFilePath()); + return JsonSerializer.DeserializeFromFile(GetHistoryFilePath(false)); } - catch (IOException) + catch (DirectoryNotFoundException) + { + // File doesn't exist. No biggie + return null; + } + catch (FileNotFoundException) { // File doesn't exist. No biggie return null; @@ -413,63 +418,46 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks } } - /// - /// The _scheduled tasks configuration directory - /// - private string _scheduledTasksConfigurationDirectory; /// /// Gets the scheduled tasks configuration directory. /// - /// The scheduled tasks configuration directory. - private string ScheduledTasksConfigurationDirectory + /// if set to true [create]. + /// System.String. + private string GetScheduledTasksConfigurationDirectory(bool create) { - get - { - if (_scheduledTasksConfigurationDirectory == null) - { - _scheduledTasksConfigurationDirectory = Path.Combine(ApplicationPaths.ConfigurationDirectoryPath, "ScheduledTasks"); + var path = Path.Combine(ApplicationPaths.ConfigurationDirectoryPath, "ScheduledTasks"); - if (!Directory.Exists(_scheduledTasksConfigurationDirectory)) - { - Directory.CreateDirectory(_scheduledTasksConfigurationDirectory); - } - } - return _scheduledTasksConfigurationDirectory; + if (create && !Directory.Exists(path)) + { + Directory.CreateDirectory(path); } + + return path; } - /// - /// The _scheduled tasks data directory - /// - private string _scheduledTasksDataDirectory; /// /// Gets the scheduled tasks data directory. /// - /// The scheduled tasks data directory. - private string ScheduledTasksDataDirectory + /// if set to true [create]. + /// System.String. + private string GetScheduledTasksDataDirectory(bool create) { - get - { - if (_scheduledTasksDataDirectory == null) - { - _scheduledTasksDataDirectory = Path.Combine(ApplicationPaths.DataPath, "ScheduledTasks"); + var path = Path.Combine(ApplicationPaths.DataPath, "ScheduledTasks"); - if (!Directory.Exists(_scheduledTasksDataDirectory)) - { - Directory.CreateDirectory(_scheduledTasksDataDirectory); - } - } - return _scheduledTasksDataDirectory; + if (create && !Directory.Exists(path)) + { + Directory.CreateDirectory(path); } + return path; } /// /// Gets the history file path. /// /// The history file path. - private string GetHistoryFilePath() + private string GetHistoryFilePath(bool createDirectory) { - return Path.Combine(ScheduledTasksDataDirectory, Id + ".js"); + return Path.Combine(GetScheduledTasksDataDirectory(createDirectory), Id + ".js"); } /// @@ -478,7 +466,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks /// System.String. private string GetConfigurationFilePath() { - return Path.Combine(ScheduledTasksConfigurationDirectory, Id + ".js"); + return Path.Combine(GetScheduledTasksConfigurationDirectory(false), Id + ".js"); } /// @@ -493,7 +481,12 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks .Select(ScheduledTaskHelpers.GetTrigger) .ToList(); } - catch (IOException) + catch (FileNotFoundException) + { + // File doesn't exist. No biggie. Return defaults. + return ScheduledTask.GetDefaultTriggers(); + } + catch (DirectoryNotFoundException) { // File doesn't exist. No biggie. Return defaults. return ScheduledTask.GetDefaultTriggers(); @@ -530,7 +523,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks Id = Id }; - JsonSerializer.SerializeToFile(result, GetHistoryFilePath()); + JsonSerializer.SerializeToFile(result, GetHistoryFilePath(true)); LastExecutionResult = result; diff --git a/MediaBrowser.Common/IO/FileSystemRepository.cs b/MediaBrowser.Common/IO/FileSystemRepository.cs index f2fa8ce54..07328d72d 100644 --- a/MediaBrowser.Common/IO/FileSystemRepository.cs +++ b/MediaBrowser.Common/IO/FileSystemRepository.cs @@ -1,6 +1,5 @@ using MediaBrowser.Common.Extensions; using System; -using System.Collections.Concurrent; using System.IO; namespace MediaBrowser.Common.IO @@ -11,12 +10,6 @@ namespace MediaBrowser.Common.IO /// public class FileSystemRepository { - /// - /// Contains the list of subfolders under the main directory - /// The directory entry is created when the item is first added to the dictionary - /// - private readonly ConcurrentDictionary _subFolderPaths = new ConcurrentDictionary(); - /// /// Gets or sets the path. /// @@ -36,18 +29,6 @@ namespace MediaBrowser.Common.IO } Path = path; - Initialize(); - } - - /// - /// Initializes this instance. - /// - protected void Initialize() - { - if (!Directory.Exists(Path)) - { - Directory.CreateDirectory(Path); - } } /// @@ -56,17 +37,18 @@ namespace MediaBrowser.Common.IO /// Name of the unique. /// The file extension. /// System.String. - /// + /// + /// public string GetResourcePath(string uniqueName, string fileExtension) { if (string.IsNullOrEmpty(uniqueName)) { - throw new ArgumentNullException(); + throw new ArgumentNullException("uniqueName"); } if (string.IsNullOrEmpty(fileExtension)) { - throw new ArgumentNullException(); + throw new ArgumentNullException("fileExtension"); } var filename = uniqueName.GetMD5() + fileExtension; @@ -75,7 +57,7 @@ namespace MediaBrowser.Common.IO } /// - /// Gets the full path of where a file should be stored within the repository + /// Gets the resource path. /// /// The filename. /// System.String. @@ -84,41 +66,14 @@ namespace MediaBrowser.Common.IO { if (string.IsNullOrEmpty(filename)) { - throw new ArgumentNullException(); + throw new ArgumentNullException("filename"); } - return GetInternalResourcePath(filename); - } - - /// - /// Takes a filename and returns the full path of where it should be stored - /// - /// The filename. - /// System.String. - private string GetInternalResourcePath(string filename) - { var prefix = filename.Substring(0, 1); - var folder = _subFolderPaths.GetOrAdd(prefix, GetCachePath); - - return System.IO.Path.Combine(folder, filename); - } - - /// - /// Creates a subfolder under the image cache directory and returns the full path - /// - /// The prefix. - /// System.String. - private string GetCachePath(string prefix) - { var path = System.IO.Path.Combine(Path, prefix); - - if (!Directory.Exists(path)) - { - Directory.CreateDirectory(path); - } - - return path; + + return System.IO.Path.Combine(path, filename); } /// @@ -144,8 +99,8 @@ namespace MediaBrowser.Common.IO { throw new ArgumentNullException(); } - - return ContainsFilePath(GetInternalResourcePath(filename)); + + return ContainsFilePath(GetResourcePath(filename)); } /// diff --git a/MediaBrowser.Controller/Drawing/ImageManager.cs b/MediaBrowser.Controller/Drawing/ImageManager.cs index b728fe71f..b8406438c 100644 --- a/MediaBrowser.Controller/Drawing/ImageManager.cs +++ b/MediaBrowser.Controller/Drawing/ImageManager.cs @@ -31,7 +31,7 @@ namespace MediaBrowser.Controller.Drawing /// /// The image enhancers. public IEnumerable ImageEnhancers { get; set; } - + /// /// Gets the image size cache. /// @@ -106,9 +106,10 @@ namespace MediaBrowser.Controller.Drawing /// Use if a max width is required. Aspect ratio will be preserved. /// Use if a max height is required. Aspect ratio will be preserved. /// Quality level, from 0-100. Currently only applies to JPG. The default value should suffice. + /// The enhancers. /// Task. /// entity - public async Task ProcessImage(BaseItem entity, ImageType imageType, int imageIndex, bool cropWhitespace, DateTime dateModified, Stream toStream, int? width, int? height, int? maxWidth, int? maxHeight, int? quality) + public async Task ProcessImage(BaseItem entity, ImageType imageType, int imageIndex, bool cropWhitespace, DateTime dateModified, Stream toStream, int? width, int? height, int? maxWidth, int? maxHeight, int? quality, List enhancers) { if (entity == null) { @@ -127,28 +128,13 @@ namespace MediaBrowser.Controller.Drawing originalImagePath = await GetCroppedImage(originalImagePath, dateModified).ConfigureAwait(false); } - var supportedEnhancers = ImageEnhancers.Where(i => - { - try - { - return i.Supports(entity, imageType); - } - catch (Exception ex) - { - _logger.ErrorException("Error in image enhancer: {0}", ex, i.GetType().Name); - - return false; - } - - }).ToList(); - // No enhancement - don't cache - if (supportedEnhancers.Count > 0) + if (enhancers.Count > 0) { try { // Enhance if we have enhancers - var ehnancedImagePath = await GetEnhancedImage(originalImagePath, dateModified, entity, imageType, imageIndex, supportedEnhancers).ConfigureAwait(false); + var ehnancedImagePath = await GetEnhancedImage(originalImagePath, dateModified, entity, imageType, imageIndex, enhancers).ConfigureAwait(false); // If the path changed update dateModified if (!ehnancedImagePath.Equals(originalImagePath, StringComparison.OrdinalIgnoreCase)) @@ -175,6 +161,19 @@ namespace MediaBrowser.Controller.Drawing var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality.Value, dateModified); + try + { + using (var fileStream = new FileStream(cacheFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) + { + await fileStream.CopyToAsync(toStream).ConfigureAwait(false); + return; + } + } + catch (IOException) + { + // Cache file doesn't exist or is currently being written ro + } + var semaphore = GetLock(cacheFilePath); await semaphore.WaitAsync().ConfigureAwait(false); @@ -262,6 +261,13 @@ namespace MediaBrowser.Controller.Drawing /// The bytes. private async Task CacheResizedImage(string cacheFilePath, byte[] bytes) { + var parentPath = Path.GetDirectoryName(cacheFilePath); + + if (!Directory.Exists(parentPath)) + { + Directory.CreateDirectory(parentPath); + } + // Save to the cache location using (var cacheFileStream = new FileStream(cacheFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) { @@ -323,7 +329,7 @@ namespace MediaBrowser.Controller.Drawing } protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); - + /// /// Gets the size of the image. /// @@ -335,25 +341,53 @@ namespace MediaBrowser.Controller.Drawing // Now check the file system cache var fullCachePath = ImageSizeCache.GetResourcePath(keyName, ".txt"); + try + { + var result = File.ReadAllText(fullCachePath).Split('|').Select(i => double.Parse(i, UsCulture)).ToArray(); + + return new ImageSize { Width = result[0], Height = result[1] }; + } + catch (IOException) + { + // Cache file doesn't exist or is currently being written to + } + var semaphore = GetLock(fullCachePath); await semaphore.WaitAsync().ConfigureAwait(false); try { - try - { - var result = File.ReadAllText(fullCachePath).Split('|').Select(i => double.Parse(i, UsCulture)).ToArray(); + var result = File.ReadAllText(fullCachePath).Split('|').Select(i => double.Parse(i, UsCulture)).ToArray(); - return new ImageSize { Width = result[0], Height = result[1] }; - } - catch (FileNotFoundException) - { - // Cache file doesn't exist no biggie - } + return new ImageSize { Width = result[0], Height = result[1] }; + } + catch (FileNotFoundException) + { + // Cache file doesn't exist no biggie + } + catch (DirectoryNotFoundException) + { + // Cache file doesn't exist no biggie + } + catch + { + semaphore.Release(); + throw; + } + + try + { var size = await ImageHeader.GetDimensions(imagePath, _logger).ConfigureAwait(false); + var parentPath = Path.GetDirectoryName(fullCachePath); + + if (!Directory.Exists(parentPath)) + { + Directory.CreateDirectory(parentPath); + } + // Update the file system cache File.WriteAllText(fullCachePath, size.Width.ToString(UsCulture) + @"|" + size.Height.ToString(UsCulture)); @@ -490,12 +524,12 @@ namespace MediaBrowser.Controller.Drawing await semaphore.WaitAsync().ConfigureAwait(false); // Check again in case of contention - if (CroppedImageCache.ContainsFilePath(croppedImagePath)) + if (File.Exists(croppedImagePath)) { semaphore.Release(); return croppedImagePath; } - + try { using (var fileStream = new FileStream(originalImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, true)) @@ -511,6 +545,13 @@ namespace MediaBrowser.Controller.Drawing using (var croppedImage = originalImage.CropWhitespace()) { + var parentPath = Path.GetDirectoryName(croppedImagePath); + + if (!Directory.Exists(parentPath)) + { + Directory.CreateDirectory(parentPath); + } + using (var outputStream = new FileStream(croppedImagePath, FileMode.Create, FileAccess.Write, FileShare.Read)) { croppedImage.Save(outputFormat, outputStream, 100); @@ -568,7 +609,7 @@ namespace MediaBrowser.Controller.Drawing await semaphore.WaitAsync().ConfigureAwait(false); // Check again in case of contention - if (EnhancedImageCache.ContainsFilePath(enhancedImagePath)) + if (File.Exists(enhancedImagePath)) { semaphore.Release(); return enhancedImagePath; @@ -588,6 +629,13 @@ namespace MediaBrowser.Controller.Drawing //Pass the image through registered enhancers using (var newImage = await ExecuteImageEnhancers(supportedEnhancers, originalImage, item, imageType, imageIndex).ConfigureAwait(false)) { + var parentDirectory = Path.GetDirectoryName(enhancedImagePath); + + if (!Directory.Exists(parentDirectory)) + { + Directory.CreateDirectory(parentDirectory); + } + //And then save it in the cache using (var outputStream = new FileStream(enhancedImagePath, FileMode.Create, FileAccess.Write, FileShare.Read)) { diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index ef1efd8af..f5aed6d21 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -304,7 +304,6 @@ namespace MediaBrowser.Controller.Entities /// /// We attach these to the item so that we only ever have to hit the file system once /// (this includes the children of the containing folder) - /// Use ResolveArgs.FileSystemDictionary to check for the existence of files instead of File.Exists /// /// The resolve args. [IgnoreDataMember] diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs index e186119d6..65884332a 100644 --- a/MediaBrowser.Controller/Entities/User.cs +++ b/MediaBrowser.Controller/Entities/User.cs @@ -19,10 +19,6 @@ namespace MediaBrowser.Controller.Entities public static IUserManager UserManager { get; set; } public static IXmlSerializer XmlSerializer { get; set; } - /// - /// The _root folder path - /// - private string _rootFolderPath; /// /// Gets the root folder path. /// @@ -32,23 +28,19 @@ namespace MediaBrowser.Controller.Entities { get { - if (_rootFolderPath == null) + if (Configuration.UseCustomLibrary) { - if (Configuration.UseCustomLibrary) - { - _rootFolderPath = GetRootFolderPath(Name); + var rootFolderPath = GetRootFolderPath(Name); - if (!Directory.Exists(_rootFolderPath)) - { - Directory.CreateDirectory(_rootFolderPath); - } - } - else + if (!Directory.Exists(rootFolderPath)) { - _rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; + Directory.CreateDirectory(rootFolderPath); } + + return rootFolderPath; } - return _rootFolderPath; + + return ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; } } @@ -261,7 +253,6 @@ namespace MediaBrowser.Controller.Entities // Force these to be lazy loaded again _configurationDirectoryPath = null; - _rootFolderPath = null; RootFolder = null; // Kick off a task to validate the media library @@ -378,7 +369,6 @@ namespace MediaBrowser.Controller.Entities // Force these to be lazy loaded again if (customLibraryChanged) { - _rootFolderPath = null; RootFolder = null; } } diff --git a/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs b/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs index 111f5aff8..91359cd29 100644 --- a/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs +++ b/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs @@ -55,10 +55,6 @@ namespace MediaBrowser.Controller.MediaInfo SubtitleCache = new FileSystemRepository(SubtitleCachePath); } - /// - /// The _video images data path - /// - private string _videoImagesDataPath; /// /// Gets the video images data path. /// @@ -67,24 +63,10 @@ namespace MediaBrowser.Controller.MediaInfo { get { - if (_videoImagesDataPath == null) - { - _videoImagesDataPath = Path.Combine(_appPaths.DataPath, "extracted-video-images"); - - if (!Directory.Exists(_videoImagesDataPath)) - { - Directory.CreateDirectory(_videoImagesDataPath); - } - } - - return _videoImagesDataPath; + return Path.Combine(_appPaths.DataPath, "extracted-video-images"); } } - /// - /// The _audio images data path - /// - private string _audioImagesDataPath; /// /// Gets the audio images data path. /// @@ -93,24 +75,10 @@ namespace MediaBrowser.Controller.MediaInfo { get { - if (_audioImagesDataPath == null) - { - _audioImagesDataPath = Path.Combine(_appPaths.DataPath, "extracted-audio-images"); - - if (!Directory.Exists(_audioImagesDataPath)) - { - Directory.CreateDirectory(_audioImagesDataPath); - } - } - - return _audioImagesDataPath; + return Path.Combine(_appPaths.DataPath, "extracted-audio-images"); } } - /// - /// The _subtitle cache path - /// - private string _subtitleCachePath; /// /// Gets the subtitle cache path. /// @@ -119,17 +87,7 @@ namespace MediaBrowser.Controller.MediaInfo { get { - if (_subtitleCachePath == null) - { - _subtitleCachePath = Path.Combine(_appPaths.CachePath, "subtitles"); - - if (!Directory.Exists(_subtitleCachePath)) - { - Directory.CreateDirectory(_subtitleCachePath); - } - } - - return _subtitleCachePath; + return Path.Combine(_appPaths.CachePath, "subtitles"); } } @@ -177,7 +135,7 @@ namespace MediaBrowser.Controller.MediaInfo var path = VideoImageCache.GetResourcePath(filename, ".jpg"); - if (!VideoImageCache.ContainsFilePath(path)) + if (!File.Exists(path)) { if (extractImages) { @@ -204,6 +162,13 @@ namespace MediaBrowser.Controller.MediaInfo try { + var parentPath = Path.GetDirectoryName(path); + + if (!Directory.Exists(parentPath)) + { + Directory.CreateDirectory(parentPath); + } + await _encoder.ExtractImage(inputPath, type, time, path, cancellationToken).ConfigureAwait(false); chapter.ImagePath = path; changesMade = true; diff --git a/MediaBrowser.Controller/Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Controller/Providers/MediaInfo/AudioImageProvider.cs index 881065ea6..9fd67f477 100644 --- a/MediaBrowser.Controller/Providers/MediaInfo/AudioImageProvider.cs +++ b/MediaBrowser.Controller/Providers/MediaInfo/AudioImageProvider.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.IO; +using System.IO; +using MediaBrowser.Common.IO; using MediaBrowser.Common.MediaInfo; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -156,7 +157,7 @@ namespace MediaBrowser.Controller.Providers.MediaInfo var path = ImageCache.GetResourcePath(filename + "_primary", ".jpg"); - if (!ImageCache.ContainsFilePath(path)) + if (!File.Exists(path)) { var semaphore = GetLock(path); @@ -164,10 +165,17 @@ namespace MediaBrowser.Controller.Providers.MediaInfo await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); // Check again - if (!ImageCache.ContainsFilePath(path)) + if (!File.Exists(path)) { try { + var parentPath = Path.GetDirectoryName(path); + + if (!Directory.Exists(parentPath)) + { + Directory.CreateDirectory(parentPath); + } + await _mediaEncoder.ExtractImage(new[] { item.Path }, InputType.AudioFile, null, path, cancellationToken).ConfigureAwait(false); } finally diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj index 2eb76c731..82193c27c 100644 --- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj +++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj @@ -244,6 +244,9 @@ Querying\PersonsQuery.cs + + Querying\SimilarItemsQuery.cs + Querying\ThemeSongsResult.cs diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs index d2b729ffd..785dfdfd7 100644 --- a/MediaBrowser.Model/ApiClient/IApiClient.cs +++ b/MediaBrowser.Model/ApiClient/IApiClient.cs @@ -72,6 +72,34 @@ namespace MediaBrowser.Model.ApiClient /// query Task GetItemsAsync(ItemQuery query); + /// + /// Gets the similar movies async. + /// + /// The query. + /// Task{ItemsResult}. + Task GetSimilarMoviesAsync(SimilarItemsQuery query); + + /// + /// Gets the similar series async. + /// + /// The query. + /// Task{ItemsResult}. + Task GetSimilarSeriesAsync(SimilarItemsQuery query); + + /// + /// Gets the similar albums async. + /// + /// The query. + /// Task{ItemsResult}. + Task GetSimilarAlbumsAsync(SimilarItemsQuery query); + + /// + /// Gets the similar games async. + /// + /// The query. + /// Task{ItemsResult}. + Task GetSimilarGamesAsync(SimilarItemsQuery query); + /// /// Gets the people async. /// diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 7d56e4743..a13c8ef0c 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -60,6 +60,7 @@ + diff --git a/MediaBrowser.Model/Querying/SimilarItemsQuery.cs b/MediaBrowser.Model/Querying/SimilarItemsQuery.cs new file mode 100644 index 000000000..0dd491550 --- /dev/null +++ b/MediaBrowser.Model/Querying/SimilarItemsQuery.cs @@ -0,0 +1,29 @@ +namespace MediaBrowser.Model.Querying +{ + public class SimilarItemsQuery + { + /// + /// The user to localize search results for + /// + /// The user id. + public string UserId { get; set; } + + /// + /// Gets or sets the id. + /// + /// The id. + public string Id { get; set; } + + /// + /// The maximum number of items to return + /// + /// The limit. + public int? Limit { get; set; } + + /// + /// Fields to return within the items, in addition to basic information + /// + /// The fields. + public ItemFields[] Fields { get; set; } + } +} diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs index ce39ffc06..fbb5a2010 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs +++ b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs @@ -94,29 +94,20 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder } /// - /// The _media tools path + /// Gets the media tools path. /// - private string _mediaToolsPath; - /// - /// Gets the folder path to tools - /// - /// The media tools path. - private string MediaToolsPath + /// if set to true [create]. + /// System.String. + private string GetMediaToolsPath(bool create) { - get + var path = Path.Combine(_appPaths.ProgramDataPath, "ffmpeg"); + + if (create && !Directory.Exists(path)) { - if (_mediaToolsPath == null) - { - _mediaToolsPath = Path.Combine(_appPaths.ProgramDataPath, "ffmpeg"); - - if (!Directory.Exists(_mediaToolsPath)) - { - Directory.CreateDirectory(_mediaToolsPath); - } - } - - return _mediaToolsPath; + Directory.CreateDirectory(path); } + + return path; } /// @@ -185,7 +176,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder var filename = resource.Substring(resource.IndexOf(prefix, StringComparison.OrdinalIgnoreCase) + prefix.Length); - var versionedDirectoryPath = Path.Combine(MediaToolsPath, Path.GetFileNameWithoutExtension(filename)); + var versionedDirectoryPath = Path.Combine(GetMediaToolsPath(true), Path.GetFileNameWithoutExtension(filename)); if (!Directory.Exists(versionedDirectoryPath)) { @@ -570,14 +561,14 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder } var offsetParam = offset.Ticks > 0 ? "-ss " + offset.TotalSeconds + " " : string.Empty; - + var process = new Process { StartInfo = new ProcessStartInfo { RedirectStandardOutput = false, RedirectStandardError = true, - + CreateNoWindow = true, UseShellExecute = false, FileName = FFMpegPath, @@ -744,7 +735,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder RedirectStandardOutput = false, RedirectStandardError = true, - + FileName = FFMpegPath, Arguments = string.Format("{0}-i {1} -map 0:{2} -an -vn -c:s ass \"{3}\"", offsetParam, inputPath, subtitleStreamIndex, outputPath), WindowStyle = ProcessWindowStyle.Hidden, @@ -759,7 +750,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "ffmpeg-sub-extract-" + Guid.NewGuid() + ".txt"); var logFileStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous); - + try { process.Start(); diff --git a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs index 2ba222ca5..ea1b83dec 100644 --- a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs +++ b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs @@ -328,9 +328,7 @@ namespace MediaBrowser.Server.Implementations.Providers public async Task SaveImage(BaseItem item, Stream source, string targetName, bool saveLocally, CancellationToken cancellationToken) { //download and save locally - var localPath = (saveLocally && item.MetaLocation != null) ? - Path.Combine(item.MetaLocation, targetName) : - _remoteImageCache.GetResourcePath(item.GetType().FullName + item.Path.ToLower(), targetName); + var localPath = GetSavePath(item, targetName, saveLocally); if (saveLocally) // queue to media directories { @@ -374,9 +372,18 @@ namespace MediaBrowser.Server.Implementations.Providers /// System.String. public string GetSavePath(BaseItem item, string targetFileName, bool saveLocally) { - return (saveLocally && item.MetaLocation != null) ? + var path = (saveLocally && item.MetaLocation != null) ? Path.Combine(item.MetaLocation, targetFileName) : _remoteImageCache.GetResourcePath(item.GetType().FullName + item.Id.ToString(), targetFileName); + + var parentPath = Path.GetDirectoryName(path); + + if (!Directory.Exists(parentPath)) + { + Directory.CreateDirectory(parentPath); + } + + return path; } /// diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs index 95dc4e7ae..2d9d5abfe 100644 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs +++ b/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs @@ -168,6 +168,14 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks if (!success) { previouslyFailedImages.Add(key); + + var parentPath = Path.GetDirectoryName(failHistoryPath); + + if (!Directory.Exists(parentPath)) + { + Directory.CreateDirectory(parentPath); + } + _jsonSerializer.SerializeToFile(previouslyFailedImages, failHistoryPath); } diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/ImageCleanupTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/ImageCleanupTask.cs index 0c3016552..0e78824c1 100644 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/ImageCleanupTask.cs +++ b/MediaBrowser.Server.Implementations/ScheduledTasks/ImageCleanupTask.cs @@ -165,7 +165,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks var specialFeattures = _itemRepo.GetItems(movie.SpecialFeatureIds).ToList(); images = specialFeattures.Aggregate(images, (current, subItem) => current.Concat(GetPathsInUse(subItem))); } - + return images; } @@ -176,13 +176,20 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks /// IEnumerable{System.String}. private IEnumerable GetFiles(string path) { - return Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories) - .Where(i => - { - var ext = Path.GetExtension(i); + try + { + return Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories) + .Where(i => + { + var ext = Path.GetExtension(i); - return !string.IsNullOrEmpty(ext) && BaseItem.SupportedImageExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase); - }); + return !string.IsNullOrEmpty(ext) && BaseItem.SupportedImageExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase); + }); + } + catch (DirectoryNotFoundException) + { + return new string[] { }; + } } /// diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs index f0afe6358..2233f6fdf 100644 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs +++ b/MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs @@ -8,14 +8,15 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers.MediaInfo; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using MoreLinq; using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Model.Logging; -using MoreLinq; namespace MediaBrowser.Server.Implementations.ScheduledTasks { @@ -263,7 +264,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks var path = ImageCache.GetResourcePath(filename, ".jpg"); - if (!ImageCache.ContainsFilePath(path)) + if (!File.Exists(path)) { var semaphore = GetLock(path); @@ -271,10 +272,17 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); // Check again - if (!ImageCache.ContainsFilePath(path)) + if (!File.Exists(path)) { try { + var parentPath = Path.GetDirectoryName(path); + + if (!Directory.Exists(parentPath)) + { + Directory.CreateDirectory(parentPath); + } + await ExtractImageInternal(item, path, cancellationToken).ConfigureAwait(false); } finally diff --git a/MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs b/MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs index a0acd523e..bead1360b 100644 --- a/MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs @@ -471,20 +471,18 @@ namespace MediaBrowser.Server.Implementations.Sqlite /// /// Gets the critic reviews path. /// - /// The critic reviews path. - private string CriticReviewsPath + /// if set to true [create]. + /// System.String. + private string GetCriticReviewsPath(bool create) { - get + var path = Path.Combine(_appPaths.DataPath, "critic-reviews"); + + if (create && !Directory.Exists(path)) { - var path = Path.Combine(_appPaths.DataPath, "critic-reviews"); - - if (!Directory.Exists(path)) - { - Directory.CreateDirectory(path); - } - - return path; + Directory.CreateDirectory(path); } + + return path; } /// @@ -499,10 +497,14 @@ namespace MediaBrowser.Server.Implementations.Sqlite try { - var path = Path.Combine(CriticReviewsPath, itemId + ".json"); + var path = Path.Combine(GetCriticReviewsPath(false), itemId + ".json"); return _jsonSerializer.DeserializeFromFile>(path); } + catch (DirectoryNotFoundException) + { + return new List(); + } catch (FileNotFoundException) { return new List(); @@ -521,7 +523,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite { return Task.Run(() => { - var path = Path.Combine(CriticReviewsPath, itemId + ".json"); + var path = Path.Combine(GetCriticReviewsPath(true), itemId + ".json"); _jsonSerializer.SerializeToFile(criticReviews.ToList(), path); }); diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 45da5849b..5425e0b9a 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.112 + 3.0.113 MediaBrowser.Common.Internal Luke ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains common components shared by Media Browser Theatre and Media Browser Server. Not intended for plugin developer consumption. Copyright © Media Browser 2013 - + diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 96926c2b9..1b03690ef 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.112 + 3.0.113 MediaBrowser.Common Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index b522a0f1a..48ebcad5f 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.112 + 3.0.113 Media Browser.Server.Core Media Browser Team ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains core components required to build plugins for Media Browser Server. Copyright © Media Browser 2013 - +