From c2537a57ebb4f3b55310234dd9914765da854df7 Mon Sep 17 00:00:00 2001 From: Thomas Gillen Date: Thu, 27 Mar 2014 02:14:06 +0000 Subject: [PATCH 1/6] Support anime series ordering with core providers --- .../MediaBrowser.Controller.csproj | 1 + .../Providers/ISeriesOrderManager.cs | 26 ++++++++++++++ .../Manager/SeriesOrderManager.cs | 36 +++++++++++++++++++ .../MediaBrowser.Providers.csproj | 1 + .../TV/FanArtSeasonProvider.cs | 12 ++++++- .../TV/TvdbEpisodeImageProvider.cs | 3 +- .../TV/TvdbEpisodeProvider.cs | 9 ++--- .../TV/TvdbSeasonImageProvider.cs | 29 ++++++++++----- .../TV/TvdbSeriesImageProvider.cs | 4 +++ .../TV/TvdbSeriesProvider.cs | 30 +++++++++++++++- .../ApplicationHost.cs | 6 ++++ MediaBrowser.sln | 3 -- 12 files changed, 141 insertions(+), 19 deletions(-) create mode 100644 MediaBrowser.Controller/Providers/ISeriesOrderManager.cs create mode 100644 MediaBrowser.Providers/Manager/SeriesOrderManager.cs diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 5e6297d060f..2e0d44e406d 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -187,6 +187,7 @@ + diff --git a/MediaBrowser.Controller/Providers/ISeriesOrderManager.cs b/MediaBrowser.Controller/Providers/ISeriesOrderManager.cs new file mode 100644 index 00000000000..a3adab1b908 --- /dev/null +++ b/MediaBrowser.Controller/Providers/ISeriesOrderManager.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MediaBrowser.Common; + +namespace MediaBrowser.Controller.Providers +{ + public interface ISeriesOrderProvider + { + string OrderType { get; } + Task FindSeriesIndex(string seriesName); + } + + public static class SeriesOrderTypes + { + public const string Anime = "Anime"; + } + + public interface ISeriesOrderManager + { + Task FindSeriesIndex(string orderType, string seriesName); + void AddParts(IEnumerable orderProviders); + } +} diff --git a/MediaBrowser.Providers/Manager/SeriesOrderManager.cs b/MediaBrowser.Providers/Manager/SeriesOrderManager.cs new file mode 100644 index 00000000000..39175d1f947 --- /dev/null +++ b/MediaBrowser.Providers/Manager/SeriesOrderManager.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using MediaBrowser.Common; +using MediaBrowser.Controller.Providers; + +namespace MediaBrowser.Providers.Manager +{ + public class SeriesOrderManager : ISeriesOrderManager + { + private Dictionary _providers; + + public void AddParts(IEnumerable orderProviders) + { + _providers = orderProviders + .GroupBy(p => p.OrderType) + .ToDictionary(g => g.Key, g => g.ToArray()); + } + + public async Task FindSeriesIndex(string orderType, string seriesName) + { + ISeriesOrderProvider[] providers; + if (!_providers.TryGetValue(orderType, out providers)) + return null; + + foreach (ISeriesOrderProvider provider in providers) + { + int? index = await provider.FindSeriesIndex(seriesName); + if (index != null) + return index; + } + + return null; + } + } +} \ No newline at end of file diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index f347e722905..ff39ec70d7e 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -103,6 +103,7 @@ + diff --git a/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs b/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs index 470bd7b3a93..5bc58af915a 100644 --- a/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs +++ b/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs @@ -77,7 +77,8 @@ namespace MediaBrowser.Providers.TV try { - AddImages(list, season.IndexNumber.Value, xmlPath, cancellationToken); + int seasonNumber = AdjustForSeriesOffset(series, season.IndexNumber.Value); + AddImages(list, seasonNumber, xmlPath, cancellationToken); } catch (FileNotFoundException) { @@ -115,6 +116,15 @@ namespace MediaBrowser.Providers.TV .ThenByDescending(i => i.VoteCount ?? 0); } + private int AdjustForSeriesOffset(Series series, int seasonNumber) + { + var offset = TvdbSeriesProvider.GetSeriesOffset(series.ProviderIds); + if (offset != null) + return (int)(seasonNumber + offset); + + return seasonNumber; + } + private void AddImages(List list, int seasonNumber, string xmlPath, CancellationToken cancellationToken) { using (var streamReader = new StreamReader(xmlPath, Encoding.UTF8)) diff --git a/MediaBrowser.Providers/TV/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TvdbEpisodeImageProvider.cs index 36e349f600e..ef377845013 100644 --- a/MediaBrowser.Providers/TV/TvdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbEpisodeImageProvider.cs @@ -62,8 +62,9 @@ namespace MediaBrowser.Providers.TV { // Process images var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, seriesId); + var indexOffset = TvdbSeriesProvider.GetSeriesOffset(series.ProviderIds) ?? 0; - var files = TvdbEpisodeProvider.Current.GetEpisodeXmlFiles(episode.ParentIndexNumber, episode.IndexNumber, episode.IndexNumberEnd, seriesDataPath); + var files = TvdbEpisodeProvider.Current.GetEpisodeXmlFiles(episode.ParentIndexNumber + indexOffset, episode.IndexNumber, episode.IndexNumberEnd, seriesDataPath); var result = files.Select(i => GetImageInfo(i, cancellationToken)) .Where(i => i != null); diff --git a/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs index 5f6bdfa0428..922c29fe970 100644 --- a/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs @@ -52,7 +52,7 @@ namespace MediaBrowser.Providers.TV try { - var item = FetchEpisodeData(searchInfo, seriesDataPath, cancellationToken); + var item = FetchEpisodeData(searchInfo, seriesDataPath, searchInfo.SeriesProviderIds, cancellationToken); if (item != null) { @@ -96,7 +96,7 @@ namespace MediaBrowser.Providers.TV try { - result.Item = FetchEpisodeData(searchInfo, seriesDataPath, cancellationToken); + result.Item = FetchEpisodeData(searchInfo, seriesDataPath, searchInfo.SeriesProviderIds, cancellationToken); result.HasMetadata = result.Item != null; } catch (FileNotFoundException) @@ -213,7 +213,7 @@ namespace MediaBrowser.Providers.TV /// The series data path. /// The cancellation token. /// Task{System.Boolean}. - private Episode FetchEpisodeData(EpisodeInfo id, string seriesDataPath, CancellationToken cancellationToken) + private Episode FetchEpisodeData(EpisodeInfo id, string seriesDataPath, Dictionary seriesProviderIds, CancellationToken cancellationToken) { if (id.IndexNumber == null) { @@ -221,7 +221,8 @@ namespace MediaBrowser.Providers.TV } var episodeNumber = id.IndexNumber.Value; - var seasonNumber = id.ParentIndexNumber; + var seasonOffset = TvdbSeriesProvider.GetSeriesOffset(seriesProviderIds) ?? 0; + var seasonNumber = id.ParentIndexNumber + seasonOffset; if (seasonNumber == null) { diff --git a/MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs index c0c103b7fbb..5a9981c6e6b 100644 --- a/MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs @@ -22,8 +22,9 @@ namespace MediaBrowser.Providers.TV { public class TvdbSeasonImageProvider : IRemoteImageProvider, IHasOrder, IHasChangeMonitor { + private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); + private readonly IServerConfigurationManager _config; - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly IHttpClient _httpClient; private readonly IFileSystem _fileSystem; @@ -77,7 +78,8 @@ namespace MediaBrowser.Providers.TV try { - return GetImages(path, item.GetPreferredMetadataLanguage(), season.IndexNumber.Value, cancellationToken); + int seasonNumber = AdjustForSeriesOffset(series, season.IndexNumber.Value); + return GetImages(path, item.GetPreferredMetadataLanguage(), seasonNumber, cancellationToken); } catch (FileNotFoundException) { @@ -88,7 +90,16 @@ namespace MediaBrowser.Providers.TV return new RemoteImageInfo[] { }; } - private IEnumerable GetImages(string xmlPath, string preferredLanguage, int seasonNumber, CancellationToken cancellationToken) + private int AdjustForSeriesOffset(Series series, int seasonNumber) + { + var offset = TvdbSeriesProvider.GetSeriesOffset(series.ProviderIds); + if (offset != null) + return (int) (seasonNumber + offset); + + return seasonNumber; + } + + internal static IEnumerable GetImages(string xmlPath, string preferredLanguage, int seasonNumber, CancellationToken cancellationToken) { var settings = new XmlReaderSettings { @@ -159,7 +170,7 @@ namespace MediaBrowser.Providers.TV .ToList(); } - private void AddImage(XmlReader reader, List images, int seasonNumber) + private static void AddImage(XmlReader reader, List images, int seasonNumber) { reader.MoveToContent(); @@ -186,7 +197,7 @@ namespace MediaBrowser.Providers.TV double rval; - if (double.TryParse(val, NumberStyles.Any, _usCulture, out rval)) + if (double.TryParse(val, NumberStyles.Any, UsCulture, out rval)) { rating = rval; } @@ -200,7 +211,7 @@ namespace MediaBrowser.Providers.TV int rval; - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) + if (int.TryParse(val, NumberStyles.Integer, UsCulture, out rval)) { voteCount = rval; } @@ -237,12 +248,12 @@ namespace MediaBrowser.Providers.TV { int rval; - if (int.TryParse(resolutionParts[0], NumberStyles.Integer, _usCulture, out rval)) + if (int.TryParse(resolutionParts[0], NumberStyles.Integer, UsCulture, out rval)) { width = rval; } - if (int.TryParse(resolutionParts[1], NumberStyles.Integer, _usCulture, out rval)) + if (int.TryParse(resolutionParts[1], NumberStyles.Integer, UsCulture, out rval)) { height = rval; } @@ -285,7 +296,7 @@ namespace MediaBrowser.Providers.TV CommunityRating = rating, VoteCount = voteCount, Url = TVUtils.BannerUrl + url, - ProviderName = Name, + ProviderName = ProviderName, Language = language, Width = width, Height = height diff --git a/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs index 761c774435a..d1171f34bfd 100644 --- a/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs @@ -77,6 +77,10 @@ namespace MediaBrowser.Providers.TV try { + var seriesOffset = TvdbSeriesProvider.GetSeriesOffset(series.ProviderIds); + if (seriesOffset != null) + return TvdbSeasonImageProvider.GetImages(path, language, seriesOffset.Value + 1, cancellationToken); + return GetImages(path, language, cancellationToken); } catch (FileNotFoundException) diff --git a/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs index 4b78c1a9626..a76e101113f 100644 --- a/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs @@ -25,6 +25,8 @@ namespace MediaBrowser.Providers.TV { public class TvdbSeriesProvider : IRemoteMetadataProvider, IHasOrder { + internal const string TvdbSeriesOffset = "TvdbSeriesOffset"; + internal readonly SemaphoreSlim TvDbResourcePool = new SemaphoreSlim(2, 2); internal static TvdbSeriesProvider Current { get; private set; } private readonly IZipClient _zipClient; @@ -33,14 +35,16 @@ namespace MediaBrowser.Providers.TV private readonly IServerConfigurationManager _config; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly ILogger _logger; + private readonly ISeriesOrderManager _seriesOrder; - public TvdbSeriesProvider(IZipClient zipClient, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager config, ILogger logger) + public TvdbSeriesProvider(IZipClient zipClient, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager config, ILogger logger, ISeriesOrderManager seriesOrder) { _zipClient = zipClient; _httpClient = httpClient; _fileSystem = fileSystem; _config = config; _logger = logger; + _seriesOrder = seriesOrder; Current = this; } @@ -92,11 +96,35 @@ namespace MediaBrowser.Providers.TV result.HasMetadata = true; FetchSeriesData(result.Item, seriesId, cancellationToken); + await FindAnimeSeriesIndex(result.Item, itemId).ConfigureAwait(false); } return result; } + private async Task FindAnimeSeriesIndex(Series series, SeriesInfo info) + { + var index = await _seriesOrder.FindSeriesIndex(SeriesOrderTypes.Anime, series.Name); + if (index == null) + return; + + var offset = info.AnimeSeriesIndex - index; + series.SetProviderId(TvdbSeriesOffset, offset.ToString()); + } + + internal static int? GetSeriesOffset(Dictionary seriesProviderIds) + { + string offsetString; + if (!seriesProviderIds.TryGetValue(TvdbSeriesOffset, out offsetString)) + return null; + + int offset; + if (int.TryParse(offsetString, out offset)) + return offset; + + return null; + } + /// /// Fetches the series data. /// diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index fe7b17d1dde..06ffa37a10b 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -157,6 +157,7 @@ namespace MediaBrowser.ServerApplication private IHttpServer HttpServer { get; set; } private IDtoService DtoService { get; set; } private IImageProcessor ImageProcessor { get; set; } + private ISeriesOrderManager SeriesOrderManager { get; set; } /// /// Gets or sets the media encoder. @@ -453,6 +454,9 @@ namespace MediaBrowser.ServerApplication ProviderManager = new ProviderManager(HttpClient, ServerConfigurationManager, LibraryMonitor, LogManager, FileSystemManager); RegisterSingleInstance(ProviderManager); + SeriesOrderManager = new SeriesOrderManager(); + RegisterSingleInstance(SeriesOrderManager); + RegisterSingleInstance(() => new SearchEngine(LogManager, LibraryManager, UserManager)); SessionManager = new SessionManager(UserDataManager, ServerConfigurationManager, Logger, UserRepository, LibraryManager, UserManager); @@ -680,6 +684,8 @@ namespace MediaBrowser.ServerApplication GetExports(), GetExports()); + SeriesOrderManager.AddParts(GetExports()); + ImageProcessor.AddParts(GetExports()); LiveTvManager.AddParts(GetExports()); diff --git a/MediaBrowser.sln b/MediaBrowser.sln index 7ac1580659a..e05b8ae6ae7 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -251,7 +251,4 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(Performance) = preSolution - HasPerformanceSessions = true - EndGlobalSection EndGlobal From fb22901d38832dcea332136ac748305ba74eb5db Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 27 Mar 2014 15:30:21 -0400 Subject: [PATCH 2/6] create separate media encoding project --- .../Playback/BaseStreamingService.cs | 38 ++++---- .../Playback/Hls/BaseHlsService.cs | 2 +- .../BaseProgressiveStreamingService.cs | 5 + .../MediaEncoding/IMediaEncoder.cs | 13 ++- MediaBrowser.Dlna/DlnaManager.cs | 2 +- MediaBrowser.Dlna/PlayTo/Device.cs | 2 +- MediaBrowser.Dlna/Profiles/DefaultProfile.cs | 18 ---- MediaBrowser.Dlna/Profiles/Xbox360Profile.cs | 14 --- MediaBrowser.Dlna/Profiles/Xml/Default.xml | 7 -- MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml | 7 -- .../Profiles/Xml/Linksys DMA2100.xml | 7 -- MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml | 2 - MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml | 7 -- MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml | 7 -- .../BdInfo/BdInfoExaminer.cs | 2 +- .../Encoder}/MediaEncoder.cs | 97 ++++++++++++------- .../MediaBrowser.MediaEncoding.csproj | 80 +++++++++++++++ .../Properties/AssemblyInfo.cs | 36 +++++++ MediaBrowser.MediaEncoding/packages.config | 4 + .../MediaInfo/AudioImageProvider.cs | 2 +- .../MediaInfo/FFProbeVideoInfo.cs | 2 - .../MediaInfo/VideoImageProvider.cs | 2 +- .../IO/LibraryMonitor.cs | 60 ++++++------ ...MediaBrowser.Server.Implementations.csproj | 10 -- .../MediaEncoder/EncodingManager.cs | 2 +- .../SqliteMediaStreamsRepository.cs | 2 + .../packages.config | 1 - .../ApplicationHost.cs | 8 +- .../MediaBrowser.ServerApplication.csproj | 4 + MediaBrowser.sln | 16 +++ 30 files changed, 276 insertions(+), 183 deletions(-) rename {MediaBrowser.Server.Implementations => MediaBrowser.MediaEncoding}/BdInfo/BdInfoExaminer.cs (99%) rename {MediaBrowser.Server.Implementations/MediaEncoder => MediaBrowser.MediaEncoding/Encoder}/MediaEncoder.cs (91%) create mode 100644 MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj create mode 100644 MediaBrowser.MediaEncoding/Properties/AssemblyInfo.cs create mode 100644 MediaBrowser.MediaEncoding/packages.config diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 2002e594c75..ceb96d226c9 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -316,6 +316,7 @@ namespace MediaBrowser.Api.Playback /// /// The state. /// The video codec. + /// if set to true [is HLS]. /// System.String. protected string GetVideoQualityParam(StreamState state, string videoCodec, bool isHls) { @@ -340,20 +341,17 @@ namespace MediaBrowser.Api.Playback break; } - if (!isHls) + switch (qualitySetting) { - switch (qualitySetting) - { - case EncodingQuality.HighSpeed: - param += " -crf 23"; - break; - case EncodingQuality.HighQuality: - param += " -crf 20"; - break; - case EncodingQuality.MaxQuality: - param += " -crf 18"; - break; - } + case EncodingQuality.HighSpeed: + param += " -crf 23"; + break; + case EncodingQuality.HighQuality: + param += " -crf 20"; + break; + case EncodingQuality.MaxQuality: + param += " -crf 18"; + break; } } @@ -1032,11 +1030,6 @@ namespace MediaBrowser.Api.Playback { var hasFixedResolution = state.VideoRequest.HasFixedResolution; - if (isHls) - { - return string.Format(" -b:v {0} -maxrate ({0}*.80) -bufsize {0}", bitrate.Value.ToString(UsCulture)); - } - if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase)) { if (hasFixedResolution) @@ -1047,7 +1040,6 @@ namespace MediaBrowser.Api.Playback // With vpx when crf is used, b:v becomes a max rate // https://trac.ffmpeg.org/wiki/vpxEncodingGuide return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture)); - //return string.Format(" -minrate:v ({0}*.95) -maxrate:v ({0}*1.05) -bufsize:v {0} -b:v {0}", bitrate.Value.ToString(UsCulture)); } if (string.Equals(videoCodec, "msmpeg4", StringComparison.OrdinalIgnoreCase)) @@ -1055,13 +1047,17 @@ namespace MediaBrowser.Api.Playback return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture)); } - // H264 if (hasFixedResolution) { + if (isHls) + { + return string.Format(" -b:v {0} -maxrate ({0}*.80) -bufsize {0}", bitrate.Value.ToString(UsCulture)); + } + return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture)); } - + return string.Format(" -maxrate {0} -bufsize {1}", bitrate.Value.ToString(UsCulture), (bitrate.Value * 2).ToString(UsCulture)); diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 198376d6a17..96b36ac7fc3 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -200,7 +200,7 @@ namespace MediaBrowser.Api.Playback.Hls builder.AppendLine("#EXTM3U"); // Pad a little to satisfy the apple hls validator - var paddedBitrate = Convert.ToInt32(bitrate * 1.05); + var paddedBitrate = Convert.ToInt32(bitrate * 1.15); // Main stream builder.AppendLine("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + paddedBitrate.ToString(UsCulture)); diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index 78b3f29481f..b4fe9a094f5 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -266,6 +266,11 @@ namespace MediaBrowser.Api.Playback.Progressive return result; } + /// + /// Gets the length of the estimated content. + /// + /// The state. + /// System.Nullable{System.Int64}. private long? GetEstimatedContentLength(StreamState state) { var totalBitrate = 0; diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index 119688fa703..657e52e5a8b 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -24,16 +24,23 @@ namespace MediaBrowser.Controller.MediaEncoding string Version { get; } /// - /// Extracts the image. + /// Extracts the audio image. + /// + /// The path. + /// The cancellation token. + /// Task{Stream}. + Task ExtractAudioImage(string path, CancellationToken cancellationToken); + + /// + /// Extracts the video image. /// /// The input files. /// The type. - /// if set to true [is audio]. /// The threed format. /// The offset. /// The cancellation token. /// Task{Stream}. - Task ExtractImage(string[] inputFiles, InputType type, bool isAudio, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken); + Task ExtractVideoImage(string[] inputFiles, InputType type, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken); /// /// Extracts the text subtitle. diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/MediaBrowser.Dlna/DlnaManager.cs index edccc71c995..ec9ecb9ef2f 100644 --- a/MediaBrowser.Dlna/DlnaManager.cs +++ b/MediaBrowser.Dlna/DlnaManager.cs @@ -197,7 +197,7 @@ namespace MediaBrowser.Dlna throw new ArgumentNullException("headers"); } - return GetProfiles().FirstOrDefault(i => IsMatch(headers, i.Identification)); + return GetProfiles().FirstOrDefault(i => i.Identification != null && IsMatch(headers, i.Identification)); } private bool IsMatch(IDictionary headers, DeviceIdentification profileInfo) diff --git a/MediaBrowser.Dlna/PlayTo/Device.cs b/MediaBrowser.Dlna/PlayTo/Device.cs index fa0bfbca8e0..c0f88f285ef 100644 --- a/MediaBrowser.Dlna/PlayTo/Device.cs +++ b/MediaBrowser.Dlna/PlayTo/Device.cs @@ -631,7 +631,7 @@ namespace MediaBrowser.Dlna.PlayTo RendererCommands = TransportCommands.Create(document); } - internal TransportCommands AvCommands + private TransportCommands AvCommands { get; set; diff --git a/MediaBrowser.Dlna/Profiles/DefaultProfile.cs b/MediaBrowser.Dlna/Profiles/DefaultProfile.cs index 0efe1875549..6b5513e2892 100644 --- a/MediaBrowser.Dlna/Profiles/DefaultProfile.cs +++ b/MediaBrowser.Dlna/Profiles/DefaultProfile.cs @@ -57,24 +57,6 @@ namespace MediaBrowser.Dlna.Profiles Type = DlnaProfileType.Video } }; - - CodecProfiles = new[] - { - new CodecProfile - { - Type = CodecType.VideoCodec, - Conditions = new [] - { - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.VideoLevel, - Value = "3", - IsRequired = false - } - } - } - }; } } } diff --git a/MediaBrowser.Dlna/Profiles/Xbox360Profile.cs b/MediaBrowser.Dlna/Profiles/Xbox360Profile.cs index 640317fbcf7..ed9edeb27ad 100644 --- a/MediaBrowser.Dlna/Profiles/Xbox360Profile.cs +++ b/MediaBrowser.Dlna/Profiles/Xbox360Profile.cs @@ -229,13 +229,6 @@ namespace MediaBrowser.Dlna.Profiles Property = ProfileConditionValue.VideoBitrate, Value = "10240000", IsRequired = false - }, - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.VideoLevel, - Value = "3", - IsRequired = false } } }, @@ -271,13 +264,6 @@ namespace MediaBrowser.Dlna.Profiles Property = ProfileConditionValue.VideoBitrate, Value = "15360000", IsRequired = false - }, - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.VideoLevel, - Value = "3", - IsRequired = false } } }, diff --git a/MediaBrowser.Dlna/Profiles/Xml/Default.xml b/MediaBrowser.Dlna/Profiles/Xml/Default.xml index 52b92c6f355..60f22a64a9d 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Default.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Default.xml @@ -30,12 +30,5 @@ - - - - - - - \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml b/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml index 3332e4d4500..28bb3289e21 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml @@ -34,12 +34,5 @@ - - - - - - - \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml b/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml index b19cb0f7b4a..477fee2dde1 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml @@ -34,12 +34,5 @@ - - - - - - - \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml b/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml index e7839ca7774..ea253919765 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml @@ -73,7 +73,6 @@ - @@ -82,7 +81,6 @@ - diff --git a/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml b/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml index 5243218fb64..ef4fec99ac6 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml @@ -32,13 +32,6 @@ - - - - - - - diff --git a/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml b/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml index 126d7fe73f0..3e6623ac489 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml @@ -36,12 +36,5 @@ - - - - - - - \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/BdInfo/BdInfoExaminer.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs similarity index 99% rename from MediaBrowser.Server.Implementations/BdInfo/BdInfoExaminer.cs rename to MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs index 18f1b92fe74..b15b8d15dd3 100644 --- a/MediaBrowser.Server.Implementations/BdInfo/BdInfoExaminer.cs +++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; -namespace MediaBrowser.Server.Implementations.BdInfo +namespace MediaBrowser.MediaEncoding.BdInfo { /// /// Class BdInfoExaminer diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs similarity index 91% rename from MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs rename to MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index c646d80bceb..7cabf23c48e 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -6,17 +6,15 @@ using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; -using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; -namespace MediaBrowser.Server.Implementations.MediaEncoder +namespace MediaBrowser.MediaEncoding.Encoder { /// /// Class MediaEncoder @@ -53,6 +51,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// The FF probe resource pool /// private readonly SemaphoreSlim _ffProbeResourcePool = new SemaphoreSlim(2, 2); + private readonly IFileSystem _fileSystem; public string FFMpegPath { get; private set; } @@ -62,7 +61,8 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder public string Version { get; private set; } public MediaEncoder(ILogger logger, IApplicationPaths appPaths, - IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, string version, IFileSystem fileSystem) + IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, string version, + IFileSystem fileSystem) { _logger = logger; _appPaths = appPaths; @@ -85,7 +85,8 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// /// The _semaphoreLocks /// - private readonly ConcurrentDictionary _semaphoreLocks = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _semaphoreLocks = + new ConcurrentDictionary(); /// /// Gets the lock. @@ -106,10 +107,10 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// The cancellation token. /// Task. public Task GetMediaInfo(string[] inputFiles, InputType type, bool isAudio, - CancellationToken cancellationToken) + CancellationToken cancellationToken) { return GetMediaInfoInternal(GetInputArgument(inputFiles, type), !isAudio, - GetProbeSizeArgument(type), cancellationToken); + GetProbeSizeArgument(type), cancellationToken); } /// @@ -172,8 +173,8 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// Task{MediaInfoResult}. /// private async Task GetMediaInfoInternal(string inputPath, bool extractChapters, - string probeSizeArgument, - CancellationToken cancellationToken) + string probeSizeArgument, + CancellationToken cancellationToken) { var args = extractChapters ? "{0} -i {1} -threads 0 -v info -print_format json -show_streams -show_chapters -show_format" @@ -191,7 +192,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder RedirectStandardError = true, FileName = FFProbePath, Arguments = string.Format(args, - probeSizeArgument, inputPath).Trim(), + probeSizeArgument, inputPath).Trim(), WindowStyle = ProcessWindowStyle.Hidden, ErrorDialog = false @@ -225,7 +226,8 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder { process.BeginErrorReadLine(); - result = _jsonSerializer.DeserializeFromStream(process.StandardOutput.BaseStream); + result = + _jsonSerializer.DeserializeFromStream(process.StandardOutput.BaseStream); } catch { @@ -295,7 +297,8 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// The language. /// The cancellation token. /// Task. - public async Task ConvertTextSubtitleToAss(string inputPath, string outputPath, string language, CancellationToken cancellationToken) + public async Task ConvertTextSubtitleToAss(string inputPath, string outputPath, string language, + CancellationToken cancellationToken) { var semaphore = GetLock(outputPath); @@ -340,33 +343,35 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder } - var encodingParam = string.IsNullOrEmpty(language) ? string.Empty : - GetSubtitleLanguageEncodingParam(language) + " "; + var encodingParam = string.IsNullOrEmpty(language) + ? string.Empty + : GetSubtitleLanguageEncodingParam(language) + " "; var process = new Process + { + StartInfo = new ProcessStartInfo { - StartInfo = new ProcessStartInfo - { - RedirectStandardOutput = false, - RedirectStandardError = true, + RedirectStandardOutput = false, + RedirectStandardError = true, - CreateNoWindow = true, - UseShellExecute = false, - FileName = FFMpegPath, - Arguments = - string.Format("{0} -i \"{1}\" -c:s ass \"{2}\"", encodingParam, inputPath, outputPath), + CreateNoWindow = true, + UseShellExecute = false, + FileName = FFMpegPath, + Arguments = + string.Format("{0} -i \"{1}\" -c:s ass \"{2}\"", encodingParam, inputPath, outputPath), - WindowStyle = ProcessWindowStyle.Hidden, - ErrorDialog = false - } - }; + WindowStyle = ProcessWindowStyle.Hidden, + ErrorDialog = false + } + }; _logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "ffmpeg-sub-convert-" + Guid.NewGuid() + ".txt"); Directory.CreateDirectory(Path.GetDirectoryName(logFilePath)); - var logFileStream = _fileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true); + var logFileStream = _fileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, + true); try { @@ -525,7 +530,8 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// The cancellation token. /// Task. /// Must use inputPath list overload - public async Task ExtractTextSubtitle(string[] inputFiles, InputType type, int subtitleStreamIndex, bool copySubtitleStream, string outputPath, CancellationToken cancellationToken) + public async Task ExtractTextSubtitle(string[] inputFiles, InputType type, int subtitleStreamIndex, + bool copySubtitleStream, string outputPath, CancellationToken cancellationToken) { var semaphore = GetLock(outputPath); @@ -535,7 +541,9 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder { if (!File.Exists(outputPath)) { - await ExtractTextSubtitleInternal(GetInputArgument(inputFiles, type), subtitleStreamIndex, copySubtitleStream, outputPath, cancellationToken).ConfigureAwait(false); + await + ExtractTextSubtitleInternal(GetInputArgument(inputFiles, type), subtitleStreamIndex, + copySubtitleStream, outputPath, cancellationToken).ConfigureAwait(false); } } finally @@ -559,7 +567,8 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// or /// cancellationToken /// - private async Task ExtractTextSubtitleInternal(string inputPath, int subtitleStreamIndex, bool copySubtitleStream, string outputPath, CancellationToken cancellationToken) + private async Task ExtractTextSubtitleInternal(string inputPath, int subtitleStreamIndex, + bool copySubtitleStream, string outputPath, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(inputPath)) { @@ -571,11 +580,13 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder throw new ArgumentNullException("outputPath"); } - string processArgs = string.Format("-i {0} -map 0:{1} -an -vn -c:s ass \"{2}\"", inputPath, subtitleStreamIndex, outputPath); + string processArgs = string.Format("-i {0} -map 0:{1} -an -vn -c:s ass \"{2}\"", inputPath, + subtitleStreamIndex, outputPath); if (copySubtitleStream) { - processArgs = string.Format("-i {0} -map 0:{1} -an -vn -c:s copy \"{2}\"", inputPath, subtitleStreamIndex, outputPath); + processArgs = string.Format("-i {0} -map 0:{1} -an -vn -c:s copy \"{2}\"", inputPath, + subtitleStreamIndex, outputPath); } var process = new Process @@ -600,7 +611,8 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "ffmpeg-sub-extract-" + Guid.NewGuid() + ".txt"); Directory.CreateDirectory(Path.GetDirectoryName(logFilePath)); - var logFileStream = _fileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true); + var logFileStream = _fileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, + true); try { @@ -715,7 +727,18 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder } } - public async Task ExtractImage(string[] inputFiles, InputType type, bool isAudio, + public Task ExtractAudioImage(string path, CancellationToken cancellationToken) + { + return ExtractImage(new[] { path }, InputType.File, true, null, null, cancellationToken); + } + + public Task ExtractVideoImage(string[] inputFiles, InputType type, Video3DFormat? threedFormat, + TimeSpan? offset, CancellationToken cancellationToken) + { + return ExtractImage(inputFiles, type, false, threedFormat, offset, cancellationToken); + } + + private async Task ExtractImage(string[] inputFiles, InputType type, bool isAudio, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken) { var resourcePool = isAudio ? _audioImageResourcePool : _videoImageResourcePool; @@ -773,7 +796,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder } // Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case. - var args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2},thumbnail\" -f image2 \"{1}\"", inputPath, "-", vf) : + var args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2},thumbnail=80\" -f image2 \"{1}\"", inputPath, "-", vf) : string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, "-", vf); var probeSize = GetProbeSizeArgument(type); @@ -834,7 +857,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder _logger.ErrorException("Error killing process", ex); } } - + resourcePool.Release(); var exitCode = ranToCompletion ? process.ExitCode : -1; diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj new file mode 100644 index 00000000000..d92522bf01b --- /dev/null +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -0,0 +1,80 @@ + + + + + Debug + AnyCPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451} + Library + Properties + MediaBrowser.MediaEncoding + MediaBrowser.MediaEncoding + v4.5 + 512 + ..\ + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\MediaBrowser.BdInfo.1.0.0.10\lib\net35\BDInfo.dll + + + ..\packages\MediaBrowser.BdInfo.1.0.0.10\lib\net35\DvdLib.dll + + + + + + + + + + + + + + + + + {9142eefa-7570-41e1-bfcc-468bb571af2f} + MediaBrowser.Common + + + {17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2} + MediaBrowser.Controller + + + {7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b} + MediaBrowser.Model + + + + + + + + + \ No newline at end of file diff --git a/MediaBrowser.MediaEncoding/Properties/AssemblyInfo.cs b/MediaBrowser.MediaEncoding/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..6616e46acc1 --- /dev/null +++ b/MediaBrowser.MediaEncoding/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MediaBrowser.MediaEncoding")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MediaBrowser.MediaEncoding")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("05f49ab9-2a90-4332-9d41-7817a9cccd90")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MediaBrowser.MediaEncoding/packages.config b/MediaBrowser.MediaEncoding/packages.config new file mode 100644 index 00000000000..6e52b72b8fd --- /dev/null +++ b/MediaBrowser.MediaEncoding/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs index b3c3f278eb0..b2ca97f55cf 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs @@ -94,7 +94,7 @@ namespace MediaBrowser.Providers.MediaInfo { Directory.CreateDirectory(Path.GetDirectoryName(path)); - using (var stream = await _mediaEncoder.ExtractImage(new[] { item.Path }, InputType.File, true, null, null, cancellationToken).ConfigureAwait(false)) + using (var stream = await _mediaEncoder.ExtractAudioImage(item.Path, cancellationToken).ConfigureAwait(false)) { using (var fileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true)) { diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 58fe7f66d4f..cb326c5ad00 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -113,8 +113,6 @@ namespace MediaBrowser.Providers.MediaInfo { cancellationToken.ThrowIfCancellationRequested(); - cancellationToken.ThrowIfCancellationRequested(); - var idString = item.Id.ToString("N"); var cachePath = Path.Combine(_appPaths.CachePath, "ffprobe-video", diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs index 35475849799..70daa3f519b 100644 --- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs @@ -93,7 +93,7 @@ namespace MediaBrowser.Providers.MediaInfo var inputPath = MediaEncoderHelpers.GetInputArgument(item.Path, item.LocationType == LocationType.Remote, item.VideoType, item.IsoType, isoMount, item.PlayableStreamFileNames, out type); - var stream = await _mediaEncoder.ExtractImage(inputPath, type, false, item.Video3DFormat, imageOffset, cancellationToken).ConfigureAwait(false); + var stream = await _mediaEncoder.ExtractVideoImage(inputPath, type, item.Video3DFormat, imageOffset, cancellationToken).ConfigureAwait(false); return new DynamicImageResponse { diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs index 0a0b3f4bcdf..9279fd8d730 100644 --- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs +++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs @@ -249,17 +249,24 @@ namespace MediaBrowser.Server.Implementations.IO // Creating a FileSystemWatcher over the LAN can take hundreds of milliseconds, so wrap it in a Task to do them all in parallel Task.Run(() => { - var newWatcher = new FileSystemWatcher(path, "*") { IncludeSubdirectories = true, InternalBufferSize = 32767 }; - - newWatcher.Created += watcher_Changed; - newWatcher.Deleted += watcher_Changed; - newWatcher.Renamed += watcher_Changed; - newWatcher.Changed += watcher_Changed; - - newWatcher.Error += watcher_Error; - try { + var newWatcher = new FileSystemWatcher(path, "*") + { + IncludeSubdirectories = true, + InternalBufferSize = 32767 + }; + + newWatcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.DirectoryName | + NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.Size; + + newWatcher.Created += watcher_Changed; + newWatcher.Deleted += watcher_Changed; + newWatcher.Renamed += watcher_Changed; + newWatcher.Changed += watcher_Changed; + + newWatcher.Error += watcher_Error; + if (_fileSystemWatchers.TryAdd(path, newWatcher)) { newWatcher.EnableRaisingEvents = true; @@ -272,11 +279,7 @@ namespace MediaBrowser.Server.Implementations.IO } } - catch (IOException ex) - { - Logger.ErrorException("Error watching path: {0}", ex, path); - } - catch (PlatformNotSupportedException ex) + catch (Exception ex) { Logger.ErrorException("Error watching path: {0}", ex, path); } @@ -346,7 +349,9 @@ namespace MediaBrowser.Server.Implementations.IO { try { - OnWatcherChanged(e); + Logger.Debug("Watcher sees change of type " + e.ChangeType + " to " + e.FullPath); + + ReportFileSystemChanged(e.FullPath); } catch (Exception ex) { @@ -354,13 +359,6 @@ namespace MediaBrowser.Server.Implementations.IO } } - private void OnWatcherChanged(FileSystemEventArgs e) - { - Logger.Debug("Watcher sees change of type " + e.ChangeType + " to " + e.FullPath); - - ReportFileSystemChanged(e.FullPath); - } - public void ReportFileSystemChanged(string path) { if (string.IsNullOrEmpty(path)) @@ -370,12 +368,9 @@ namespace MediaBrowser.Server.Implementations.IO var filename = Path.GetFileName(path); - // Ignore certain files - if (!string.IsNullOrEmpty(filename) && _alwaysIgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase)) - { - return; - } + var monitorPath = !(!string.IsNullOrEmpty(filename) && _alwaysIgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase)); + // Ignore certain files var tempIgnorePaths = _tempIgnoredPaths.Keys.ToList(); // If the parent of an ignored path has a change event, ignore that too @@ -416,12 +411,15 @@ namespace MediaBrowser.Server.Implementations.IO })) { - return; + monitorPath = false; } - // Avoid implicitly captured closure - var affectedPath = path; - _affectedPaths.AddOrUpdate(path, path, (key, oldValue) => affectedPath); + if (monitorPath) + { + // Avoid implicitly captured closure + var affectedPath = path; + _affectedPaths.AddOrUpdate(path, path, (key, oldValue) => affectedPath); + } lock (_timerLock) { diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 73a12caf2c6..ea7ef2ed65e 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -48,14 +48,6 @@ ..\packages\Alchemy.2.2.1\lib\net40\Alchemy.dll - - False - ..\packages\MediaBrowser.BdInfo.1.0.0.10\lib\net35\BDInfo.dll - - - False - ..\packages\MediaBrowser.BdInfo.1.0.0.10\lib\net35\DvdLib.dll - False ..\packages\Mono.Nat.1.2.3\lib\Net40\Mono.Nat.dll @@ -105,7 +97,6 @@ Properties\SharedVersion.cs - @@ -191,7 +182,6 @@ - diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs index f74865d2c73..7237ffee5cc 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs +++ b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs @@ -178,7 +178,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder { Directory.CreateDirectory(Path.GetDirectoryName(path)); - using (var stream = await _encoder.ExtractImage(inputPath, type, false, video.Video3DFormat, time, cancellationToken).ConfigureAwait(false)) + using (var stream = await _encoder.ExtractVideoImage(inputPath, type, video.Video3DFormat, time, cancellationToken).ConfigureAwait(false)) { using (var fileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true)) { diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs index f4e7fd0a6b2..fde1e7f216f 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs @@ -37,6 +37,8 @@ namespace MediaBrowser.Server.Implementations.Persistence var createTableCommand = "create table if not exists mediastreams "; + // Add PixelFormat column + createTableCommand += "(ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PRIMARY KEY (ItemId, StreamIndex))"; string[] queries = { diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config index f04536190bd..d82e880c981 100644 --- a/MediaBrowser.Server.Implementations/packages.config +++ b/MediaBrowser.Server.Implementations/packages.config @@ -1,7 +1,6 @@  - diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index 06ffa37a10b..6083158bccc 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -33,13 +33,14 @@ using MediaBrowser.Controller.Sorting; using MediaBrowser.Controller.Themes; using MediaBrowser.Dlna; using MediaBrowser.Dlna.PlayTo; +using MediaBrowser.MediaEncoding.BdInfo; +using MediaBrowser.MediaEncoding.Encoder; using MediaBrowser.Model.Logging; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.System; using MediaBrowser.Model.Updates; using MediaBrowser.Providers.Manager; using MediaBrowser.Server.Implementations; -using MediaBrowser.Server.Implementations.BdInfo; using MediaBrowser.Server.Implementations.Channels; using MediaBrowser.Server.Implementations.Collections; using MediaBrowser.Server.Implementations.Configuration; @@ -821,7 +822,10 @@ namespace MediaBrowser.ServerApplication // Server implementations list.Add(typeof(ServerApplicationPaths).Assembly); - // Dlna implementations + // MediaEncoding + list.Add(typeof(MediaEncoder).Assembly); + + // Dlna list.Add(typeof(PlayToServerEntryPoint).Assembly); list.AddRange(Assemblies.GetAssembliesWithParts()); diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index 227d0dd1d3c..d999841ca7a 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -195,6 +195,10 @@ {734098eb-6dc1-4dd0-a1ca-3140dcd2737c} MediaBrowser.Dlna + + {0bd82fa6-eb8a-4452-8af5-74f9c3849451} + MediaBrowser.MediaEncoding + {7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b} MediaBrowser.Model diff --git a/MediaBrowser.sln b/MediaBrowser.sln index e05b8ae6ae7..c2f9dff59cf 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -41,6 +41,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.ServerApplicat EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Dlna", "MediaBrowser.Dlna\MediaBrowser.Dlna.csproj", "{734098EB-6DC1-4DD0-A1CA-3140DCD2737C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.MediaEncoding", "MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj", "{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -247,6 +249,20 @@ Global {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release|Win32.ActiveCfg = Release|Any CPU {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release|x64.ActiveCfg = Release|Any CPU {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release|x86.ActiveCfg = Release|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Win32.ActiveCfg = Debug|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|x64.ActiveCfg = Debug|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|x86.ActiveCfg = Debug|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Any CPU.Build.0 = Release|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Win32.ActiveCfg = Release|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|x64.ActiveCfg = Release|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 0aec40966e281a1ffefe78ad6461b2f6582ce628 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 27 Mar 2014 19:01:42 -0400 Subject: [PATCH 3/6] add image encoder based on ffmpeg --- .../Playback/BaseStreamingService.cs | 9 +- .../MediaBrowser.Controller.csproj | 1 + .../MediaEncoding/IMediaEncoder.cs | 8 + .../MediaEncoding/ImageEncodingOptions.cs | 18 ++ .../Encoder/ImageEncoder.cs | 158 ++++++++++++++++++ .../Encoder/MediaEncoder.cs | 9 +- .../MediaBrowser.MediaEncoding.csproj | 1 + 7 files changed, 192 insertions(+), 12 deletions(-) create mode 100644 MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs create mode 100644 MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index ceb96d226c9..b510a640e45 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -503,14 +503,13 @@ namespace MediaBrowser.Api.Playback return string.Format("{4} -vf \"{0}scale=trunc({1}/2)*2:trunc({2}/2)*2{3}\"", yadifParam, widthParam, heightParam, assSubtitleParam, copyTsParam); } - // If Max dimensions were supplied - //this makes my brain hurt. For width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size + // If Max dimensions were supplied, for width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size if (request.MaxWidth.HasValue && request.MaxHeight.HasValue) { - var MaxwidthParam = request.MaxWidth.Value.ToString(UsCulture); - var MaxheightParam = request.MaxHeight.Value.ToString(UsCulture); + var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture); + var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture); - return string.Format("{4} -vf \"{0}scale=trunc(min(iw\\,{1})/2)*2:trunc(min((iw/dar)\\,{2})/2)*2{3}\"", yadifParam, MaxwidthParam, MaxheightParam, assSubtitleParam, copyTsParam); + return string.Format("{4} -vf \"{0}scale=trunc(min(iw\\,{1})/2)*2:trunc(min((iw/dar)\\,{2})/2)*2{3}\"", yadifParam, maxWidthParam, maxHeightParam, assSubtitleParam, copyTsParam); } var isH264Output = outputVideoCodec.Equals("libx264", StringComparison.OrdinalIgnoreCase); diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 2e0d44e406d..9915ac04414 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -157,6 +157,7 @@ + diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index 657e52e5a8b..e9081fe8a32 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -88,6 +88,14 @@ namespace MediaBrowser.Controller.MediaEncoding /// The type. /// System.String. string GetInputArgument(string[] inputFiles, InputType type); + + /// + /// Encodes the image. + /// + /// The options. + /// The cancellation token. + /// Task{Stream}. + Task EncodeImage(ImageEncodingOptions options, CancellationToken cancellationToken); } /// diff --git a/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs b/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs new file mode 100644 index 00000000000..c76977f29b2 --- /dev/null +++ b/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs @@ -0,0 +1,18 @@ + +namespace MediaBrowser.Controller.MediaEncoding +{ + public class ImageEncodingOptions + { + public string InputPath { get; set; } + + public int? Width { get; set; } + + public int? Height { get; set; } + + public int? MaxWidth { get; set; } + + public int? MaxHeight { get; set; } + + public string Format { get; set; } + } +} diff --git a/MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs new file mode 100644 index 00000000000..5e4221b0f51 --- /dev/null +++ b/MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs @@ -0,0 +1,158 @@ +using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Model.Logging; +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.MediaEncoding.Encoder +{ + public class ImageEncoder + { + private readonly string _ffmpegPath; + private readonly ILogger _logger; + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + + private static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(5, 5); + + public ImageEncoder(string ffmpegPath, ILogger logger) + { + _ffmpegPath = ffmpegPath; + _logger = logger; + } + + public async Task EncodeImage(ImageEncodingOptions options, CancellationToken cancellationToken) + { + ValidateInput(options); + + var process = new Process + { + StartInfo = new ProcessStartInfo + { + CreateNoWindow = true, + UseShellExecute = false, + FileName = _ffmpegPath, + Arguments = GetArguments(options), + WindowStyle = ProcessWindowStyle.Hidden, + ErrorDialog = false, + RedirectStandardOutput = true, + RedirectStandardError = true + } + }; + + await ResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); + + process.Start(); + + var memoryStream = new MemoryStream(); + +#pragma warning disable 4014 + // Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback + process.StandardOutput.BaseStream.CopyToAsync(memoryStream); +#pragma warning restore 4014 + + // MUST read both stdout and stderr asynchronously or a deadlock may occurr + process.BeginErrorReadLine(); + + var ranToCompletion = process.WaitForExit(5000); + + if (!ranToCompletion) + { + try + { + _logger.Info("Killing ffmpeg process"); + + process.Kill(); + + process.WaitForExit(1000); + } + catch (Exception ex) + { + _logger.ErrorException("Error killing process", ex); + } + } + + ResourcePool.Release(); + + var exitCode = ranToCompletion ? process.ExitCode : -1; + + process.Dispose(); + + if (exitCode == -1 || memoryStream.Length == 0) + { + memoryStream.Dispose(); + + var msg = string.Format("ffmpeg image encoding failed for {0}", options.InputPath); + + _logger.Error(msg); + + throw new ApplicationException(msg); + } + + memoryStream.Position = 0; + return memoryStream; + } + + private string GetArguments(ImageEncodingOptions options) + { + var vfScale = GetFilterGraph(options); + var outputFormat = GetOutputFormat(options); + + return string.Format("-i file:\"{0}\" {1} -f {2}", + options.InputPath, + vfScale, + outputFormat); + } + + private string GetFilterGraph(ImageEncodingOptions options) + { + if (!options.Width.HasValue && + !options.Height.HasValue && + !options.MaxHeight.HasValue && + !options.MaxWidth.HasValue) + { + return string.Empty; + } + + var widthScale = "-1"; + var heightScale = "-1"; + + if (options.MaxWidth.HasValue) + { + widthScale = "min(iw," + options.MaxWidth.Value.ToString(_usCulture) + ")"; + } + else if (options.Width.HasValue) + { + widthScale = options.Width.Value.ToString(_usCulture); + } + + if (options.MaxHeight.HasValue) + { + heightScale = "min(ih," + options.MaxHeight.Value.ToString(_usCulture) + ")"; + } + else if (options.Height.HasValue) + { + heightScale = options.Height.Value.ToString(_usCulture); + } + + var scaleMethod = "lanczos"; + + return string.Format("-vf scale=\"{0}:{1}\" -sws_flags {2}", + widthScale, + heightScale, + scaleMethod); + } + + private string GetOutputFormat(ImageEncodingOptions options) + { + return options.Format; + } + + private void ValidateInput(ImageEncodingOptions options) + { + + } + } +} diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 7cabf23c48e..55035a4caf8 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -948,14 +948,9 @@ namespace MediaBrowser.MediaEncoding.Encoder return GetFileInputArgument(playableStreamFiles[0]); } - /// - /// Gets the bluray input argument. - /// - /// The bluray root. - /// System.String. - private string GetBlurayInputArgument(string blurayRoot) + public Task EncodeImage(ImageEncodingOptions options, CancellationToken cancellationToken) { - return string.Format("bluray:\"{0}\"", blurayRoot); + return new ImageEncoder(FFMpegPath, _logger).EncodeImage(options, cancellationToken); } /// diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index d92522bf01b..fb1041f89ef 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -48,6 +48,7 @@ + From 87c8c429e7d29c94e3722e69d4d31f505c48cfb0 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 27 Mar 2014 23:32:43 -0400 Subject: [PATCH 4/6] added image encoder methods --- .../MediaEncoding/ImageEncodingOptions.cs | 2 + .../Encoder/ImageEncoder.cs | 65 ++++++++++---- .../Encoder/MediaEncoder.cs | 41 +-------- .../Drawing/ImageProcessor.cs | 87 ++++++++++++------- .../ApplicationHost.cs | 14 +-- MediaBrowser.sln | 3 + 6 files changed, 117 insertions(+), 95 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs b/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs index c76977f29b2..a8d1e5a0f11 100644 --- a/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs +++ b/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs @@ -13,6 +13,8 @@ namespace MediaBrowser.Controller.MediaEncoding public int? MaxHeight { get; set; } + public int? Quality { get; set; } + public string Format { get; set; } } } diff --git a/MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs index 5e4221b0f51..d259c631d04 100644 --- a/MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Logging; using System; using System.Diagnostics; @@ -13,20 +14,39 @@ namespace MediaBrowser.MediaEncoding.Encoder { private readonly string _ffmpegPath; private readonly ILogger _logger; + private readonly IFileSystem _fileSystem; + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - private static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(5, 5); + private static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(10, 10); - public ImageEncoder(string ffmpegPath, ILogger logger) + public ImageEncoder(string ffmpegPath, ILogger logger, IFileSystem fileSystem) { _ffmpegPath = ffmpegPath; _logger = logger; + _fileSystem = fileSystem; } public async Task EncodeImage(ImageEncodingOptions options, CancellationToken cancellationToken) { ValidateInput(options); + await ResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); + + try + { + return await EncodeImageInternal(options, cancellationToken).ConfigureAwait(false); + } + finally + { + ResourcePool.Release(); + } + } + + private async Task EncodeImageInternal(ImageEncodingOptions options, CancellationToken cancellationToken) + { + ValidateInput(options); + var process = new Process { StartInfo = new ProcessStartInfo @@ -38,11 +58,12 @@ namespace MediaBrowser.MediaEncoding.Encoder WindowStyle = ProcessWindowStyle.Hidden, ErrorDialog = false, RedirectStandardOutput = true, - RedirectStandardError = true + RedirectStandardError = true, + WorkingDirectory = Path.GetDirectoryName(options.InputPath) } }; - await ResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); + _logger.Debug("ffmpeg " + process.StartInfo.Arguments); process.Start(); @@ -74,8 +95,6 @@ namespace MediaBrowser.MediaEncoding.Encoder } } - ResourcePool.Release(); - var exitCode = ranToCompletion ? process.ExitCode : -1; process.Dispose(); @@ -94,16 +113,21 @@ namespace MediaBrowser.MediaEncoding.Encoder memoryStream.Position = 0; return memoryStream; } - + private string GetArguments(ImageEncodingOptions options) { var vfScale = GetFilterGraph(options); - var outputFormat = GetOutputFormat(options); + var outputFormat = GetOutputFormat(options.Format); - return string.Format("-i file:\"{0}\" {1} -f {2}", - options.InputPath, + var quality = (options.Quality ?? 100) * .3; + quality = 31 - quality; + var qualityValue = Convert.ToInt32(Math.Max(quality, 1)); + + return string.Format("-f image2 -i file:\"{3}\" -q:v {0} {1} -f image2pipe -vcodec {2} -", + qualityValue.ToString(_usCulture), vfScale, - outputFormat); + outputFormat, + Path.GetFileName(options.InputPath)); } private string GetFilterGraph(ImageEncodingOptions options) @@ -121,7 +145,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (options.MaxWidth.HasValue) { - widthScale = "min(iw," + options.MaxWidth.Value.ToString(_usCulture) + ")"; + widthScale = "min(iw\\," + options.MaxWidth.Value.ToString(_usCulture) + ")"; } else if (options.Width.HasValue) { @@ -130,7 +154,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (options.MaxHeight.HasValue) { - heightScale = "min(ih," + options.MaxHeight.Value.ToString(_usCulture) + ")"; + heightScale = "min(ih\\," + options.MaxHeight.Value.ToString(_usCulture) + ")"; } else if (options.Height.HasValue) { @@ -139,15 +163,20 @@ namespace MediaBrowser.MediaEncoding.Encoder var scaleMethod = "lanczos"; - return string.Format("-vf scale=\"{0}:{1}\" -sws_flags {2}", - widthScale, + return string.Format("-vf scale=\"{0}:{1}\" -sws_flags {2}", + widthScale, heightScale, scaleMethod); } - private string GetOutputFormat(ImageEncodingOptions options) + private string GetOutputFormat(string format) { - return options.Format; + if (string.Equals(format, "jpeg", StringComparison.OrdinalIgnoreCase) || + string.Equals(format, "jpg", StringComparison.OrdinalIgnoreCase)) + { + return "mjpeg"; + } + return format; } private void ValidateInput(ImageEncodingOptions options) diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 55035a4caf8..8b41d2105f9 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -879,45 +879,6 @@ namespace MediaBrowser.MediaEncoding.Encoder return memoryStream; } - /// - /// Starts the and wait for process. - /// - /// The process. - /// The timeout. - /// true if XXXX, false otherwise - private bool StartAndWaitForProcess(Process process, int timeout = 10000) - { - process.Start(); - - var ranToCompletion = process.WaitForExit(timeout); - - if (!ranToCompletion) - { - try - { - _logger.Info("Killing ffmpeg process"); - - process.Kill(); - - process.WaitForExit(1000); - } - catch (Win32Exception ex) - { - _logger.ErrorException("Error killing process", ex); - } - catch (InvalidOperationException ex) - { - _logger.ErrorException("Error killing process", ex); - } - catch (NotSupportedException ex) - { - _logger.ErrorException("Error killing process", ex); - } - } - - return ranToCompletion; - } - /// /// Gets the file input argument. /// @@ -950,7 +911,7 @@ namespace MediaBrowser.MediaEncoding.Encoder public Task EncodeImage(ImageEncodingOptions options, CancellationToken cancellationToken) { - return new ImageEncoder(FFMpegPath, _logger).EncodeImage(options, cancellationToken); + return new ImageEncoder(FFMpegPath, _logger, _fileSystem).EncodeImage(options, cancellationToken); } /// diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs index 12d3eadfd3d..408d8c9b1f8 100644 --- a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs +++ b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs @@ -3,6 +3,7 @@ using MediaBrowser.Common.IO; using MediaBrowser.Controller; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Entities; @@ -52,12 +53,14 @@ namespace MediaBrowser.Server.Implementations.Drawing private readonly IFileSystem _fileSystem; private readonly IJsonSerializer _jsonSerializer; private readonly IServerApplicationPaths _appPaths; + private readonly IMediaEncoder _mediaEncoder; - public ImageProcessor(ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem, IJsonSerializer jsonSerializer) + public ImageProcessor(ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder) { _logger = logger; _fileSystem = fileSystem; _jsonSerializer = jsonSerializer; + _mediaEncoder = mediaEncoder; _appPaths = appPaths; _saveImageSizeTimer = new Timer(SaveImageSizeCallback, null, Timeout.Infinite, Timeout.Infinite); @@ -66,7 +69,7 @@ namespace MediaBrowser.Server.Implementations.Drawing try { - sizeDictionary = jsonSerializer.DeserializeFromFile>(ImageSizeFile) ?? + sizeDictionary = jsonSerializer.DeserializeFromFile>(ImageSizeFile) ?? new Dictionary(); } catch (FileNotFoundException) @@ -213,6 +216,39 @@ namespace MediaBrowser.Server.Implementations.Drawing try { + var hasPostProcessing = !string.IsNullOrEmpty(options.BackgroundColor) || options.UnplayedCount.HasValue || options.AddPlayedIndicator || options.PercentPlayed.HasValue; + + if (!hasPostProcessing) + { + using (var outputStream = await _mediaEncoder.EncodeImage(new ImageEncodingOptions + { + InputPath = originalImagePath, + MaxHeight = options.MaxHeight, + MaxWidth = options.MaxWidth, + Height = options.Height, + Width = options.Width, + Quality = options.Quality, + Format = options.OutputFormat == ImageOutputFormat.Original ? Path.GetExtension(originalImagePath).TrimStart('.') : options.OutputFormat.ToString().ToLower() + + }, CancellationToken.None).ConfigureAwait(false)) + { + using (var outputMemoryStream = new MemoryStream()) + { + // Save to the memory stream + await outputStream.CopyToAsync(outputMemoryStream).ConfigureAwait(false); + + var bytes = outputMemoryStream.ToArray(); + + await toStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); + + // kick off a task to cache the result + await CacheResizedImage(cacheFilePath, bytes).ConfigureAwait(false); + } + + return; + } + } + using (var fileStream = _fileSystem.GetFileStream(originalImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, true)) { // Copy to memory stream to avoid Image locking file @@ -241,8 +277,8 @@ namespace MediaBrowser.Server.Implementations.Drawing thumbnailGraph.SmoothingMode = SmoothingMode.HighQuality; thumbnailGraph.InterpolationMode = InterpolationMode.HighQualityBicubic; thumbnailGraph.PixelOffsetMode = PixelOffsetMode.HighQuality; - thumbnailGraph.CompositingMode = string.IsNullOrEmpty(options.BackgroundColor) && !options.UnplayedCount.HasValue && !options.AddPlayedIndicator && !options.PercentPlayed.HasValue ? - CompositingMode.SourceCopy : + thumbnailGraph.CompositingMode = !hasPostProcessing ? + CompositingMode.SourceCopy : CompositingMode.SourceOver; SetBackgroundColor(thumbnailGraph, options); @@ -263,7 +299,7 @@ namespace MediaBrowser.Server.Implementations.Drawing await toStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); // kick off a task to cache the result - CacheResizedImage(cacheFilePath, bytes, semaphore); + await CacheResizedImage(cacheFilePath, bytes).ConfigureAwait(false); } } } @@ -272,11 +308,9 @@ namespace MediaBrowser.Server.Implementations.Drawing } } } - catch + finally { semaphore.Release(); - - throw; } } @@ -285,33 +319,26 @@ namespace MediaBrowser.Server.Implementations.Drawing /// /// The cache file path. /// The bytes. - /// The semaphore. - private void CacheResizedImage(string cacheFilePath, byte[] bytes, SemaphoreSlim semaphore) + /// Task. + private async Task CacheResizedImage(string cacheFilePath, byte[] bytes) { - Task.Run(async () => + try { - try - { - var parentPath = Path.GetDirectoryName(cacheFilePath); + var parentPath = Path.GetDirectoryName(cacheFilePath); - Directory.CreateDirectory(parentPath); + Directory.CreateDirectory(parentPath); - // Save to the cache location - using (var cacheFileStream = _fileSystem.GetFileStream(cacheFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true)) - { - // Save to the filestream - await cacheFileStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); - } - } - catch (Exception ex) + // Save to the cache location + using (var cacheFileStream = _fileSystem.GetFileStream(cacheFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true)) { - _logger.ErrorException("Error writing to image cache file {0}", ex, cacheFilePath); + // Save to the filestream + await cacheFileStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); } - finally - { - semaphore.Release(); - } - }); + } + catch (Exception ex) + { + _logger.ErrorException("Error writing to image cache file {0}", ex, cacheFilePath); + } } /// @@ -519,7 +546,7 @@ namespace MediaBrowser.Server.Implementations.Drawing { filename += "iv=" + IndicatorVersion; } - + if (!string.IsNullOrEmpty(backgroundColor)) { filename += "b=" + backgroundColor; diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index 6083158bccc..b7e9017d6e4 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -473,7 +473,13 @@ namespace MediaBrowser.ServerApplication LocalizationManager = new LocalizationManager(ServerConfigurationManager, FileSystemManager); RegisterSingleInstance(LocalizationManager); - ImageProcessor = new ImageProcessor(LogManager.GetLogger("ImageProcessor"), ServerConfigurationManager.ApplicationPaths, FileSystemManager, JsonSerializer); + var innerProgress = new ActionableProgress(); + innerProgress.RegisterAction(p => progress.Report((.75 * p) + 15)); + + await RegisterMediaEncoder(innerProgress).ConfigureAwait(false); + progress.Report(90); + + ImageProcessor = new ImageProcessor(LogManager.GetLogger("ImageProcessor"), ServerConfigurationManager.ApplicationPaths, FileSystemManager, JsonSerializer, MediaEncoder); RegisterSingleInstance(ImageProcessor); DtoService = new DtoService(Logger, LibraryManager, UserManager, UserDataManager, ItemRepository, ImageProcessor, ServerConfigurationManager, FileSystemManager, ProviderManager); @@ -487,12 +493,6 @@ namespace MediaBrowser.ServerApplication progress.Report(15); - var innerProgress = new ActionableProgress(); - innerProgress.RegisterAction(p => progress.Report((.75 * p) + 15)); - - await RegisterMediaEncoder(innerProgress).ConfigureAwait(false); - progress.Report(90); - EncodingManager = new EncodingManager(ServerConfigurationManager, FileSystemManager, Logger, ItemRepository, MediaEncoder); RegisterSingleInstance(EncodingManager); diff --git a/MediaBrowser.sln b/MediaBrowser.sln index c2f9dff59cf..7dc06fb0ceb 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -267,4 +267,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection EndGlobal From b95f8f5f5cd59d7c2e50e4eee018eccc7fd05216 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 28 Mar 2014 00:24:11 -0400 Subject: [PATCH 5/6] correct dlna param positions --- .../Playback/BaseStreamingService.cs | 17 +++-- .../Encoder/ImageEncoder.cs | 66 ++++++++++++++++--- .../Encoder/MediaEncoder.cs | 2 +- .../Drawing/ImageOutputFormat.cs | 3 +- .../Music/LastfmArtistProvider.cs | 24 ------- .../Drawing/ImageProcessor.cs | 50 +++++++------- 6 files changed, 96 insertions(+), 66 deletions(-) diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index b510a640e45..519ff7947a2 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -967,8 +967,6 @@ namespace MediaBrowser.Api.Playback private async void StreamToStandardInput(Process process, StreamState state) { - state.StandardInputCancellationTokenSource = new CancellationTokenSource(); - try { await StreamToStandardInputInternal(process, state).ConfigureAwait(false); @@ -1263,31 +1261,38 @@ namespace MediaBrowser.Api.Playback { if (videoRequest != null) { - videoRequest.MaxWidth = int.Parse(val, UsCulture); + videoRequest.MaxFramerate = double.Parse(val, UsCulture); } } else if (i == 12) { if (videoRequest != null) { - videoRequest.MaxHeight = int.Parse(val, UsCulture); + videoRequest.MaxWidth = int.Parse(val, UsCulture); } } else if (i == 13) { if (videoRequest != null) { - videoRequest.Framerate = int.Parse(val, UsCulture); + videoRequest.MaxHeight = int.Parse(val, UsCulture); } } else if (i == 14) { if (videoRequest != null) { - request.StartTimeTicks = long.Parse(val, UsCulture); + videoRequest.Framerate = int.Parse(val, UsCulture); } } else if (i == 15) + { + if (videoRequest != null) + { + request.StartTimeTicks = long.Parse(val, UsCulture); + } + } + else if (i == 16) { if (videoRequest != null) { diff --git a/MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs index d259c631d04..e0ca86c41aa 100644 --- a/MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs @@ -1,10 +1,13 @@ -using MediaBrowser.Common.IO; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Logging; using System; using System.Diagnostics; using System.Globalization; using System.IO; +using System.Linq; +using System.Text; using System.Threading; using System.Threading.Tasks; @@ -15,16 +18,18 @@ namespace MediaBrowser.MediaEncoding.Encoder private readonly string _ffmpegPath; private readonly ILogger _logger; private readonly IFileSystem _fileSystem; + private readonly IApplicationPaths _appPaths; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(10, 10); - public ImageEncoder(string ffmpegPath, ILogger logger, IFileSystem fileSystem) + public ImageEncoder(string ffmpegPath, ILogger logger, IFileSystem fileSystem, IApplicationPaths appPaths) { _ffmpegPath = ffmpegPath; _logger = logger; _fileSystem = fileSystem; + _appPaths = appPaths; } public async Task EncodeImage(ImageEncodingOptions options, CancellationToken cancellationToken) @@ -47,6 +52,15 @@ namespace MediaBrowser.MediaEncoding.Encoder { ValidateInput(options); + var inputPath = options.InputPath; + var filename = Path.GetFileName(inputPath); + + if (HasDiacritics(filename)) + { + inputPath = GetTempFile(inputPath); + filename = Path.GetFileName(inputPath); + } + var process = new Process { StartInfo = new ProcessStartInfo @@ -54,12 +68,12 @@ namespace MediaBrowser.MediaEncoding.Encoder CreateNoWindow = true, UseShellExecute = false, FileName = _ffmpegPath, - Arguments = GetArguments(options), + Arguments = GetArguments(options, filename), WindowStyle = ProcessWindowStyle.Hidden, ErrorDialog = false, RedirectStandardOutput = true, RedirectStandardError = true, - WorkingDirectory = Path.GetDirectoryName(options.InputPath) + WorkingDirectory = Path.GetDirectoryName(inputPath) } }; @@ -113,8 +127,19 @@ namespace MediaBrowser.MediaEncoding.Encoder memoryStream.Position = 0; return memoryStream; } + + private string GetTempFile(string path) + { + var extension = Path.GetExtension(path) ?? string.Empty; + + var tempPath = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString("N") + extension); + + File.Copy(path, tempPath); + + return tempPath; + } - private string GetArguments(ImageEncodingOptions options) + private string GetArguments(ImageEncodingOptions options, string inputFilename) { var vfScale = GetFilterGraph(options); var outputFormat = GetOutputFormat(options.Format); @@ -127,7 +152,7 @@ namespace MediaBrowser.MediaEncoding.Encoder qualityValue.ToString(_usCulture), vfScale, outputFormat, - Path.GetFileName(options.InputPath)); + inputFilename); } private string GetFilterGraph(ImageEncodingOptions options) @@ -163,10 +188,9 @@ namespace MediaBrowser.MediaEncoding.Encoder var scaleMethod = "lanczos"; - return string.Format("-vf scale=\"{0}:{1}\" -sws_flags {2}", + return string.Format("-vf scale=\"{0}:{1}\"", widthScale, - heightScale, - scaleMethod); + heightScale); } private string GetOutputFormat(string format) @@ -183,5 +207,29 @@ namespace MediaBrowser.MediaEncoding.Encoder { } + + /// + /// Determines whether the specified text has diacritics. + /// + /// The text. + /// true if the specified text has diacritics; otherwise, false. + private bool HasDiacritics(string text) + { + return !String.Equals(text, RemoveDiacritics(text), StringComparison.Ordinal); + } + + /// + /// Removes the diacritics. + /// + /// The text. + /// System.String. + private string RemoveDiacritics(string text) + { + return String.Concat( + text.Normalize(NormalizationForm.FormD) + .Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) != + UnicodeCategory.NonSpacingMark) + ).Normalize(NormalizationForm.FormC); + } } } diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 8b41d2105f9..fac54ecfff8 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -911,7 +911,7 @@ namespace MediaBrowser.MediaEncoding.Encoder public Task EncodeImage(ImageEncodingOptions options, CancellationToken cancellationToken) { - return new ImageEncoder(FFMpegPath, _logger, _fileSystem).EncodeImage(options, cancellationToken); + return new ImageEncoder(FFMpegPath, _logger, _fileSystem, _appPaths).EncodeImage(options, cancellationToken); } /// diff --git a/MediaBrowser.Model/Drawing/ImageOutputFormat.cs b/MediaBrowser.Model/Drawing/ImageOutputFormat.cs index 6cbe75a7a0e..824970073ac 100644 --- a/MediaBrowser.Model/Drawing/ImageOutputFormat.cs +++ b/MediaBrowser.Model/Drawing/ImageOutputFormat.cs @@ -25,6 +25,7 @@ namespace MediaBrowser.Model.Drawing /// /// The PNG /// - Png + Png, + Webp } } diff --git a/MediaBrowser.Providers/Music/LastfmArtistProvider.cs b/MediaBrowser.Providers/Music/LastfmArtistProvider.cs index a50e5f9d53a..95169aef0c1 100644 --- a/MediaBrowser.Providers/Music/LastfmArtistProvider.cs +++ b/MediaBrowser.Providers/Music/LastfmArtistProvider.cs @@ -130,30 +130,6 @@ namespace MediaBrowser.Providers.Music } } - /// - /// Determines whether the specified text has diacritics. - /// - /// The text. - /// true if the specified text has diacritics; otherwise, false. - private bool HasDiacritics(string text) - { - return !String.Equals(text, RemoveDiacritics(text), StringComparison.Ordinal); - } - - /// - /// Removes the diacritics. - /// - /// The text. - /// System.String. - private string RemoveDiacritics(string text) - { - return String.Concat( - text.Normalize(NormalizationForm.FormD) - .Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) != - UnicodeCategory.NonSpacingMark) - ).Normalize(NormalizationForm.FormC); - } - /// /// Encodes an URL. /// diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs index 408d8c9b1f8..c6c1ec050d4 100644 --- a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs +++ b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs @@ -218,36 +218,36 @@ namespace MediaBrowser.Server.Implementations.Drawing { var hasPostProcessing = !string.IsNullOrEmpty(options.BackgroundColor) || options.UnplayedCount.HasValue || options.AddPlayedIndicator || options.PercentPlayed.HasValue; - if (!hasPostProcessing) - { - using (var outputStream = await _mediaEncoder.EncodeImage(new ImageEncodingOptions - { - InputPath = originalImagePath, - MaxHeight = options.MaxHeight, - MaxWidth = options.MaxWidth, - Height = options.Height, - Width = options.Width, - Quality = options.Quality, - Format = options.OutputFormat == ImageOutputFormat.Original ? Path.GetExtension(originalImagePath).TrimStart('.') : options.OutputFormat.ToString().ToLower() + //if (!hasPostProcessing) + //{ + // using (var outputStream = await _mediaEncoder.EncodeImage(new ImageEncodingOptions + // { + // InputPath = originalImagePath, + // MaxHeight = options.MaxHeight, + // MaxWidth = options.MaxWidth, + // Height = options.Height, + // Width = options.Width, + // Quality = options.Quality, + // Format = options.OutputFormat == ImageOutputFormat.Original ? Path.GetExtension(originalImagePath).TrimStart('.') : options.OutputFormat.ToString().ToLower() - }, CancellationToken.None).ConfigureAwait(false)) - { - using (var outputMemoryStream = new MemoryStream()) - { - // Save to the memory stream - await outputStream.CopyToAsync(outputMemoryStream).ConfigureAwait(false); + // }, CancellationToken.None).ConfigureAwait(false)) + // { + // using (var outputMemoryStream = new MemoryStream()) + // { + // // Save to the memory stream + // await outputStream.CopyToAsync(outputMemoryStream).ConfigureAwait(false); - var bytes = outputMemoryStream.ToArray(); + // var bytes = outputMemoryStream.ToArray(); - await toStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); + // await toStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); - // kick off a task to cache the result - await CacheResizedImage(cacheFilePath, bytes).ConfigureAwait(false); - } + // // kick off a task to cache the result + // await CacheResizedImage(cacheFilePath, bytes).ConfigureAwait(false); + // } - return; - } - } + // return; + // } + //} using (var fileStream = _fileSystem.GetFileStream(originalImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, true)) { From f586c9911dcc930a55c37cf87298b8bd223e2803 Mon Sep 17 00:00:00 2001 From: Tim Hobbs Date: Sun, 30 Mar 2014 16:26:16 -0700 Subject: [PATCH 6/6] Cast updates --- MediaBrowser.WebDashboard/Api/DashboardService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 369fb2a07fb..0908ea0946b 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -376,8 +376,8 @@ namespace MediaBrowser.WebDashboard.Api var files = new[] { "scripts/all.js" + versionString, - "thirdparty/jstree1.0/jquery.jstree.min.js" - //"https://www.gstatic.com/cv/js/sender/v1/cast_sender.js" + "thirdparty/jstree1.0/jquery.jstree.min.js", + "https://www.gstatic.com/cv/js/sender/v1/cast_sender.js" }; var tags = files.Select(s => string.Format("", s)).ToArray();