diff --git a/Emby.Drawing.Skia/SkiaEncoder.cs b/Emby.Drawing.Skia/SkiaEncoder.cs index 7469d167e1..a89e1f2db8 100644 --- a/Emby.Drawing.Skia/SkiaEncoder.cs +++ b/Emby.Drawing.Skia/SkiaEncoder.cs @@ -10,22 +10,27 @@ using System.IO; using System.Linq; using System.Reflection; using System.Threading.Tasks; +using MediaBrowser.Controller.Extensions; +using System.Globalization; +using MediaBrowser.Model.Globalization; namespace Emby.Drawing.Skia { public class SkiaEncoder : IImageEncoder { private readonly ILogger _logger; - private readonly IApplicationPaths _appPaths; + private static IApplicationPaths _appPaths; private readonly Func _httpClientFactory; private readonly IFileSystem _fileSystem; + private static ILocalizationManager _localizationManager; - public SkiaEncoder(ILogger logger, IApplicationPaths appPaths, Func httpClientFactory, IFileSystem fileSystem) + public SkiaEncoder(ILogger logger, IApplicationPaths appPaths, Func httpClientFactory, IFileSystem fileSystem, ILocalizationManager localizationManager) { _logger = logger; _appPaths = appPaths; _httpClientFactory = httpClientFactory; _fileSystem = fileSystem; + _localizationManager = localizationManager; LogVersion(); } @@ -190,14 +195,53 @@ namespace Emby.Drawing.Skia } } + private static bool HasDiacritics(string text) + { + return !String.Equals(text, text.RemoveDiacritics(), StringComparison.Ordinal); + } + + private static bool RequiresSpecialCharacterHack(string path) + { + if (_localizationManager.HasUnicodeCategory(path, UnicodeCategory.OtherLetter)) + { + return true; + } + + if (HasDiacritics(path)) + { + return true; + } + + return false; + } + + private static string NormalizePath(string path, IFileSystem fileSystem) + { + if (!RequiresSpecialCharacterHack(path)) + { + return path; + } + + var tempPath = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid() + Path.GetExtension(path) ?? string.Empty); + + fileSystem.CopyFile(path, tempPath, true); + + return tempPath; + } + private static string[] TransparentImageTypes = new string[] { ".png", ".gif", ".webp" }; - internal static SKBitmap Decode(string path, bool forceCleanBitmap, out SKCodecOrigin origin) + internal static SKBitmap Decode(string path, bool forceCleanBitmap, IFileSystem fileSystem, out SKCodecOrigin origin) { + if (!fileSystem.FileExists(path)) + { + throw new FileNotFoundException("File not found", path); + } + var requiresTransparencyHack = TransparentImageTypes.Contains(Path.GetExtension(path) ?? string.Empty); if (requiresTransparencyHack || forceCleanBitmap) { - using (var stream = new SKFileStream(path)) + using (var stream = new SKFileStream(NormalizePath(path, fileSystem))) { using (var codec = SKCodec.Create(stream)) { @@ -227,11 +271,11 @@ namespace Emby.Drawing.Skia } } - var resultBitmap = SKBitmap.Decode(path); + var resultBitmap = SKBitmap.Decode(NormalizePath(path, fileSystem)); if (resultBitmap == null) { - return Decode(path, true, out origin); + return Decode(path, true, fileSystem, out origin); } // If we have to resize these they often end up distorted @@ -239,7 +283,7 @@ namespace Emby.Drawing.Skia { using (resultBitmap) { - return Decode(path, true, out origin); + return Decode(path, true, fileSystem, out origin); } } @@ -251,13 +295,13 @@ namespace Emby.Drawing.Skia { if (cropWhitespace) { - using (var bitmap = Decode(path, forceAnalyzeBitmap, out origin)) + using (var bitmap = Decode(path, forceAnalyzeBitmap, _fileSystem, out origin)) { return CropWhiteSpace(bitmap); } } - return Decode(path, forceAnalyzeBitmap, out origin); + return Decode(path, forceAnalyzeBitmap, _fileSystem, out origin); } private SKBitmap GetBitmap(string path, bool cropWhitespace, bool autoOrient) diff --git a/Emby.Drawing.Skia/StripCollageBuilder.cs b/Emby.Drawing.Skia/StripCollageBuilder.cs index 624245ee79..d562bb4be3 100644 --- a/Emby.Drawing.Skia/StripCollageBuilder.cs +++ b/Emby.Drawing.Skia/StripCollageBuilder.cs @@ -83,7 +83,7 @@ namespace Emby.Drawing.Skia for (int i = 0; i < 4; i++) { SKCodecOrigin origin; - using (var currentBitmap = SkiaEncoder.Decode(paths[imageIndex], false, out origin)) + using (var currentBitmap = SkiaEncoder.Decode(paths[imageIndex], false, _fileSystem, out origin)) { // resize to the same aspect as the original int iWidth = (int)Math.Abs(iHeight * currentBitmap.Width / currentBitmap.Height); @@ -165,7 +165,7 @@ namespace Emby.Drawing.Skia for (var y = 0; y < 2; y++) { SKCodecOrigin origin; - using (var currentBitmap = SkiaEncoder.Decode(paths[imageIndex], false, out origin)) + using (var currentBitmap = SkiaEncoder.Decode(paths[imageIndex], false, _fileSystem, out origin)) { using (var resizedBitmap = new SKBitmap(cellWidth, cellHeight, currentBitmap.ColorType, currentBitmap.AlphaType)) { diff --git a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs index 4d9bf0624d..e73a69892c 100644 --- a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs +++ b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs @@ -195,52 +195,49 @@ namespace Emby.Server.Implementations.Configuration } } - public void DisableMetadataService(string service) + public bool SetOptimalValues() { - DisableMetadataService(typeof(Movie), Configuration, service); - DisableMetadataService(typeof(Episode), Configuration, service); - DisableMetadataService(typeof(Series), Configuration, service); - DisableMetadataService(typeof(Season), Configuration, service); - DisableMetadataService(typeof(MusicArtist), Configuration, service); - DisableMetadataService(typeof(MusicAlbum), Configuration, service); - DisableMetadataService(typeof(MusicVideo), Configuration, service); - DisableMetadataService(typeof(Video), Configuration, service); - } + var config = Configuration; - private void DisableMetadataService(Type type, ServerConfiguration config, string service) - { - var options = GetMetadataOptions(type, config); + var changed = false; - if (!options.DisabledMetadataSavers.Contains(service, StringComparer.OrdinalIgnoreCase)) + if (!config.EnableCaseSensitiveItemIds) { - var list = options.DisabledMetadataSavers.ToList(); - - list.Add(service); - - options.DisabledMetadataSavers = list.ToArray(list.Count); + config.EnableCaseSensitiveItemIds = true; + changed = true; } - } - private MetadataOptions GetMetadataOptions(Type type, ServerConfiguration config) - { - var options = config.MetadataOptions - .FirstOrDefault(i => string.Equals(i.ItemType, type.Name, StringComparison.OrdinalIgnoreCase)); + if (!config.SkipDeserializationForBasicTypes) + { + config.SkipDeserializationForBasicTypes = true; + changed = true; + } - if (options == null) + if (!config.EnableSimpleArtistDetection) { - var list = config.MetadataOptions.ToList(); + config.EnableSimpleArtistDetection = true; + changed = true; + } - options = new MetadataOptions - { - ItemType = type.Name - }; + if (!config.EnableNormalizedItemByNameIds) + { + config.EnableNormalizedItemByNameIds = true; + changed = true; + } - list.Add(options); + if (!config.DisableLiveTvChannelUserDataName) + { + config.DisableLiveTvChannelUserDataName = true; + changed = true; + } - config.MetadataOptions = list.ToArray(list.Count); + if (!config.EnableNewOmdbSupport) + { + config.EnableNewOmdbSupport = true; + changed = true; } - return options; + return changed; } } } diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 01416a3079..eb0f5150f9 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -5298,7 +5298,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type OfficialRatings = query.OfficialRatings, GenreIds = query.GenreIds, Genres = query.Genres, - Years = query.Years + Years = query.Years, + NameContains = query.NameContains }; var outerWhereClauses = GetWhereClauses(outerQuery, null); diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 94cafa3e2d..d69a2b2404 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -160,7 +160,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies ProductionYear = video.Year, Name = parseName ? video.Name : - Path.GetFileName(video.Files[0].Path), + Path.GetFileNameWithoutExtension(video.Files[0].Path), AdditionalParts = video.Files.Skip(1).Select(i => i.Path).ToArray(), LocalAlternateVersions = video.AlternateVersions.Select(i => i.Path).ToArray() }; diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs index b1ed034ca2..df21c14091 100644 --- a/Emby.Server.Implementations/Library/SearchEngine.cs +++ b/Emby.Server.Implementations/Library/SearchEngine.cs @@ -99,8 +99,6 @@ namespace Emby.Server.Implementations.Library var terms = GetWords(searchTerm); - var hints = new List>(); - var excludeItemTypes = query.ExcludeItemTypes.ToList(); var includeItemTypes = (query.IncludeItemTypes ?? new string[] { }).ToList(); @@ -161,8 +159,15 @@ namespace Emby.Server.Implementations.Library AddIfMissing(excludeItemTypes, typeof(CollectionFolder).Name); AddIfMissing(excludeItemTypes, typeof(Folder).Name); + var mediaTypes = query.MediaTypes.ToList(); + + if (includeItemTypes.Count > 0) + { + excludeItemTypes.Clear(); + mediaTypes.Clear(); + } - var mediaItems = _libraryManager.GetItemList(new InternalItemsQuery(user) + var searchQuery = new InternalItemsQuery(user) { NameContains = searchTerm, ExcludeItemTypes = excludeItemTypes.ToArray(excludeItemTypes.Count), @@ -178,7 +183,7 @@ namespace Emby.Server.Implementations.Library IsNews = query.IsNews, IsSeries = query.IsSeries, IsSports = query.IsSports, - MediaTypes = query.MediaTypes, + MediaTypes = mediaTypes.ToArray(), DtoOptions = new DtoOptions { @@ -189,17 +194,33 @@ namespace Emby.Server.Implementations.Library ItemFields.ChannelInfo } } - }); + }; + + List mediaItems; + + if (searchQuery.IncludeItemTypes.Length == 1 && string.Equals(searchQuery.IncludeItemTypes[0], "MusicArtist", StringComparison.OrdinalIgnoreCase)) + { + if (searchQuery.ParentId.HasValue) + { + searchQuery.AncestorIds = new string[] { searchQuery.ParentId.Value.ToString("N") }; + } + searchQuery.ParentId = null; + searchQuery.IncludeItemsByName = true; + searchQuery.IncludeItemTypes = new string[] { }; + mediaItems = _libraryManager.GetArtists(searchQuery).Items.Select(i => i.Item1).ToList(); + } + else + { + mediaItems = _libraryManager.GetItemList(searchQuery); + } - // Add search hints based on item name - hints.AddRange(mediaItems.Select(item => + var returnValue = mediaItems.Select(item => { var index = GetIndex(item.Name, searchTerm, terms); return new Tuple(item, index.Item1, index.Item2); - })); - var returnValue = hints.Where(i => i.Item3 >= 0).OrderBy(i => i.Item3).ThenBy(i => i.Item1.SortName).Select(i => new SearchHintInfo + }).OrderBy(i => i.Item3).ThenBy(i => i.Item1.SortName).Select(i => new SearchHintInfo { Item = i.Item1, MatchedTerm = i.Item2 diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 650f388a1b..2eb4743cd0 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -308,6 +308,19 @@ namespace Emby.Server.Implementations.Localization return value == null ? (int?)null : value.Value; } + public bool HasUnicodeCategory(string value, UnicodeCategory category) + { + foreach (var chr in value) + { + if (char.GetUnicodeCategory(chr) == category) + { + return true; + } + } + + return false; + } + public string GetLocalizedString(string phrase) { return GetLocalizedString(phrase, _configurationManager.Configuration.UICulture); diff --git a/MediaBrowser.Api/ConfigurationService.cs b/MediaBrowser.Api/ConfigurationService.cs index 643ecd9c86..0023c13d78 100644 --- a/MediaBrowser.Api/ConfigurationService.cs +++ b/MediaBrowser.Api/ConfigurationService.cs @@ -134,8 +134,6 @@ namespace MediaBrowser.Api public void Post(AutoSetMetadataOptions request) { - _configurationManager.DisableMetadataService("Emby Xml"); - _configurationManager.SaveConfiguration(); } /// diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs index 1ccb683204..1b185b073b 100644 --- a/MediaBrowser.Api/StartupWizardService.cs +++ b/MediaBrowser.Api/StartupWizardService.cs @@ -63,7 +63,7 @@ namespace MediaBrowser.Api public void Post(ReportStartupWizardComplete request) { _config.Configuration.IsStartupWizardCompleted = true; - SetWizardFinishValues(_config.Configuration); + _config.SetOptimalValues(); _config.SaveConfiguration(); } @@ -87,16 +87,6 @@ namespace MediaBrowser.Api return result; } - private void SetWizardFinishValues(ServerConfiguration config) - { - config.EnableCaseSensitiveItemIds = true; - config.SkipDeserializationForBasicTypes = true; - config.EnableSimpleArtistDetection = true; - config.EnableNormalizedItemByNameIds = true; - config.DisableLiveTvChannelUserDataName = true; - config.EnableNewOmdbSupport = true; - } - public void Post(UpdateStartupConfiguration request) { _config.Configuration.UICulture = request.UICulture; diff --git a/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs b/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs index 13c9f8d84b..af57149322 100644 --- a/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs +++ b/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs @@ -20,10 +20,6 @@ namespace MediaBrowser.Controller.Configuration /// The configuration. ServerConfiguration Configuration { get; } - /// - /// Sets the preferred metadata service. - /// - /// The service. - void DisableMetadataService(string service); + bool SetOptimalValues(); } } diff --git a/MediaBrowser.Model/Dlna/PlaybackException.cs b/MediaBrowser.Model/Dlna/PlaybackException.cs deleted file mode 100644 index 761fa1c904..0000000000 --- a/MediaBrowser.Model/Dlna/PlaybackException.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace MediaBrowser.Model.Dlna -{ - public class PlaybackException : Exception - { - public PlaybackErrorCode ErrorCode { get; set;} - } -} diff --git a/MediaBrowser.Model/Globalization/ILocalizationManager.cs b/MediaBrowser.Model/Globalization/ILocalizationManager.cs index 2356a2fa19..61f0ebfd31 100644 --- a/MediaBrowser.Model/Globalization/ILocalizationManager.cs +++ b/MediaBrowser.Model/Globalization/ILocalizationManager.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using MediaBrowser.Model.Entities; +using System.Globalization; namespace MediaBrowser.Model.Globalization { @@ -54,5 +55,7 @@ namespace MediaBrowser.Model.Globalization string RemoveDiacritics(string text); string NormalizeFormKD(string text); + + bool HasUnicodeCategory(string value, UnicodeCategory category); } } diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index b36a773eb4..dd9b7ff75b 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -98,7 +98,6 @@ - diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs index bce4219019..e79aec33c1 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs @@ -163,7 +163,11 @@ namespace MediaBrowser.Providers.MediaInfo private void FetchShortcutInfo(Video video) { - video.ShortcutPath = _fileSystem.ReadAllText(video.Path); + video.ShortcutPath = _fileSystem.ReadAllText(video.Path) + .Replace("\t", string.Empty) + .Replace("\r", string.Empty) + .Replace("\n", string.Empty) + .Trim(); } public Task FetchAudioInfo(T item, CancellationToken cancellationToken) diff --git a/MediaBrowser.Server.Mono/ImageEncoderHelper.cs b/MediaBrowser.Server.Mono/ImageEncoderHelper.cs index 5112c64eda..4f8451c0d1 100644 --- a/MediaBrowser.Server.Mono/ImageEncoderHelper.cs +++ b/MediaBrowser.Server.Mono/ImageEncoderHelper.cs @@ -9,6 +9,7 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using Emby.Drawing.Skia; using MediaBrowser.Model.System; +using MediaBrowser.Model.Globalization; namespace MediaBrowser.Server.Startup.Common { @@ -20,13 +21,14 @@ namespace MediaBrowser.Server.Startup.Common StartupOptions startupOptions, Func httpClient, IApplicationPaths appPaths, - IEnvironmentInfo environment) + IEnvironmentInfo environment, + ILocalizationManager localizationManager) { if (!startupOptions.ContainsOption("-enablegdi")) { try { - return new SkiaEncoder(logManager.GetLogger("Skia"), appPaths, httpClient, fileSystem); + return new SkiaEncoder(logManager.GetLogger("Skia"), appPaths, httpClient, fileSystem, localizationManager); } catch (Exception ex) { diff --git a/MediaBrowser.Server.Mono/Program.cs b/MediaBrowser.Server.Mono/Program.cs index 3267a77b95..7a3a968ade 100644 --- a/MediaBrowser.Server.Mono/Program.cs +++ b/MediaBrowser.Server.Mono/Program.cs @@ -127,7 +127,7 @@ namespace MediaBrowser.Server.Mono Task.WaitAll(task); - appHost.ImageProcessor.ImageEncoder = ImageEncoderHelper.GetImageEncoder(_logger, logManager, fileSystem, options, () => appHost.HttpClient, appPaths, environmentInfo); + appHost.ImageProcessor.ImageEncoder = ImageEncoderHelper.GetImageEncoder(_logger, logManager, fileSystem, options, () => appHost.HttpClient, appPaths, environmentInfo, appHost.LocalizationManager); Console.WriteLine("Running startup tasks"); diff --git a/MediaBrowser.ServerApplication/ImageEncoderHelper.cs b/MediaBrowser.ServerApplication/ImageEncoderHelper.cs index c86e85785b..7c95a25ded 100644 --- a/MediaBrowser.ServerApplication/ImageEncoderHelper.cs +++ b/MediaBrowser.ServerApplication/ImageEncoderHelper.cs @@ -7,6 +7,7 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller.Drawing; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Globalization; namespace MediaBrowser.Server.Startup.Common { @@ -17,11 +18,12 @@ namespace MediaBrowser.Server.Startup.Common IFileSystem fileSystem, StartupOptions startupOptions, Func httpClient, - IApplicationPaths appPaths) + IApplicationPaths appPaths, + ILocalizationManager localizationManager) { try { - return new SkiaEncoder(logManager.GetLogger("Skia"), appPaths, httpClient, fileSystem); + return new SkiaEncoder(logManager.GetLogger("Skia"), appPaths, httpClient, fileSystem, localizationManager); } catch { diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs index 70b03aa44b..91a949921e 100644 --- a/MediaBrowser.ServerApplication/MainStartup.cs +++ b/MediaBrowser.ServerApplication/MainStartup.cs @@ -304,6 +304,19 @@ namespace MediaBrowser.ServerApplication } } + private static string UpdatePackageFileName + { + get + { + if (Environment.Is64BitOperatingSystem) + { + return "embyserver-win-x64-{version}.zip"; + } + + return "embyserver-win-x86-{version}.zip"; + } + } + /// /// Runs the application. /// @@ -324,7 +337,7 @@ namespace MediaBrowser.ServerApplication options, fileSystem, new PowerManagement(), - "emby.windows.zip", + UpdatePackageFileName, environmentInfo, new NullImageEncoder(), new SystemEvents(logManager.GetLogger("SystemEvents")), @@ -355,7 +368,7 @@ namespace MediaBrowser.ServerApplication } // set image encoder here - appHost.ImageProcessor.ImageEncoder = ImageEncoderHelper.GetImageEncoder(_logger, logManager, fileSystem, options, () => appHost.HttpClient, appPaths); + appHost.ImageProcessor.ImageEncoder = ImageEncoderHelper.GetImageEncoder(_logger, logManager, fileSystem, options, () => appHost.HttpClient, appPaths, appHost.LocalizationManager); task = task.ContinueWith(new Action(a => appHost.RunStartupTasks()), TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.AttachedToParent);