From aae259d2cd06dc59d279cf04b0f94a09d485cba0 Mon Sep 17 00:00:00 2001 From: LukePulverenti Luke Pulverenti luke pulverenti Date: Mon, 20 Aug 2012 15:16:51 -0400 Subject: [PATCH] Initial check-in of VideoInfoProvider, although it's currently disabled. --- MediaBrowser.Api/HttpHandlers/VideoHandler.cs | 6 +- .../Configuration/ServerApplicationPaths.cs | 22 +++++ MediaBrowser.Controller/FFMpeg/FFProbe.cs | 22 +++++ MediaBrowser.Controller/Kernel.cs | 10 +++ .../MediaBrowser.Controller.csproj | 1 + .../Providers/AudioInfoProvider.cs | 21 +++-- .../Providers/BaseMetadataProvider.cs | 3 + .../Providers/VideoInfoProvider.cs | 89 +++++++++++++++++++ .../Xml/BaseItemXmlParser.cs | 49 +--------- MediaBrowser.Model/Entities/Video.cs | 3 +- 10 files changed, 166 insertions(+), 60 deletions(-) create mode 100644 MediaBrowser.Controller/Providers/VideoInfoProvider.cs diff --git a/MediaBrowser.Api/HttpHandlers/VideoHandler.cs b/MediaBrowser.Api/HttpHandlers/VideoHandler.cs index 33a1a9c13b..fd08e530d2 100644 --- a/MediaBrowser.Api/HttpHandlers/VideoHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/VideoHandler.cs @@ -279,15 +279,15 @@ namespace MediaBrowser.Api.HttpHandlers } } - if (audio.Format.IndexOf("aac", StringComparison.OrdinalIgnoreCase) != -1) + if (audio.Codec.IndexOf("aac", StringComparison.OrdinalIgnoreCase) != -1) { return false; } - if (audio.Format.IndexOf("ac-3", StringComparison.OrdinalIgnoreCase) != -1 || audio.Format.IndexOf("ac3", StringComparison.OrdinalIgnoreCase) != -1) + if (audio.Codec.IndexOf("ac-3", StringComparison.OrdinalIgnoreCase) != -1 || audio.Codec.IndexOf("ac3", StringComparison.OrdinalIgnoreCase) != -1) { return false; } - if (audio.Format.IndexOf("mpeg", StringComparison.OrdinalIgnoreCase) != -1 || audio.Format.IndexOf("mp3", StringComparison.OrdinalIgnoreCase) != -1) + if (audio.Codec.IndexOf("mpeg", StringComparison.OrdinalIgnoreCase) != -1 || audio.Codec.IndexOf("mp3", StringComparison.OrdinalIgnoreCase) != -1) { return false; } diff --git a/MediaBrowser.Controller/Configuration/ServerApplicationPaths.cs b/MediaBrowser.Controller/Configuration/ServerApplicationPaths.cs index 874e97aed0..50bc1d10ce 100644 --- a/MediaBrowser.Controller/Configuration/ServerApplicationPaths.cs +++ b/MediaBrowser.Controller/Configuration/ServerApplicationPaths.cs @@ -193,6 +193,28 @@ namespace MediaBrowser.Controller.Configuration return _FFProbeAudioCacheDirectory; } } + + private string _FFProbeVideoCacheDirectory = null; + /// + /// Gets the folder path to the ffprobe video cache directory + /// + public string FFProbeVideoCacheDirectory + { + get + { + if (_FFProbeVideoCacheDirectory == null) + { + _FFProbeVideoCacheDirectory = Path.Combine(Kernel.Instance.ApplicationPaths.CacheDirectory, "ffprobe-video"); + + if (!Directory.Exists(_FFProbeVideoCacheDirectory)) + { + Directory.CreateDirectory(_FFProbeVideoCacheDirectory); + } + } + + return _FFProbeVideoCacheDirectory; + } + } private string _FFMpegDirectory = null; /// diff --git a/MediaBrowser.Controller/FFMpeg/FFProbe.cs b/MediaBrowser.Controller/FFMpeg/FFProbe.cs index fd9b2ff438..ea6bda6224 100644 --- a/MediaBrowser.Controller/FFMpeg/FFProbe.cs +++ b/MediaBrowser.Controller/FFMpeg/FFProbe.cs @@ -35,6 +35,28 @@ namespace MediaBrowser.Controller.FFMpeg } } + public async static Task Run(Video item, string outputCachePath) + { + // Use try catch to avoid having to use File.Exists + try + { + using (FileStream stream = File.OpenRead(outputCachePath)) + { + return JsonSerializer.DeserializeFromStream(stream); + } + } + catch (FileNotFoundException) + { + } + + await Run(item.Path, outputCachePath); + + using (FileStream stream = File.OpenRead(outputCachePath)) + { + return JsonSerializer.DeserializeFromStream(stream); + } + } + private async static Task Run(string input, string output) { ProcessStartInfo startInfo = new ProcessStartInfo(); diff --git a/MediaBrowser.Controller/Kernel.cs b/MediaBrowser.Controller/Kernel.cs index 9e31a5d6cd..4acc502338 100644 --- a/MediaBrowser.Controller/Kernel.cs +++ b/MediaBrowser.Controller/Kernel.cs @@ -252,6 +252,16 @@ namespace MediaBrowser.Controller ); } + // Third priority providers + providers = supportedProviders.Where(i => !i.RequiresInternet && i.Priority == MetadataProviderPriority.Third); + + if (providers.Any()) + { + await Task.WhenAll( + providers.Select(i => i.Fetch(item, args)) + ); + } + // Lowest priority providers providers = supportedProviders.Where(i => !i.RequiresInternet && i.Priority == MetadataProviderPriority.Last); diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 6ac2ddd49d..bada34224e 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -64,6 +64,7 @@ + diff --git a/MediaBrowser.Controller/Providers/AudioInfoProvider.cs b/MediaBrowser.Controller/Providers/AudioInfoProvider.cs index 8894f87ed6..0f513f3698 100644 --- a/MediaBrowser.Controller/Providers/AudioInfoProvider.cs +++ b/MediaBrowser.Controller/Providers/AudioInfoProvider.cs @@ -229,21 +229,26 @@ namespace MediaBrowser.Controller.Providers { base.Init(); + EnsureCacheSubFolders(Kernel.Instance.ApplicationPaths.FFProbeAudioCacheDirectory); + } + + internal static void EnsureCacheSubFolders(string root) + { // Do this now so that we don't have to do this on every operation, which would require us to create a lock in order to maintain thread-safety for (int i = 0; i <= 9; i++) { - EnsureDirectory(Path.Combine(Kernel.Instance.ApplicationPaths.FFProbeAudioCacheDirectory, i.ToString())); + EnsureDirectory(Path.Combine(root, i.ToString())); } - EnsureDirectory(Path.Combine(Kernel.Instance.ApplicationPaths.FFProbeAudioCacheDirectory, "a")); - EnsureDirectory(Path.Combine(Kernel.Instance.ApplicationPaths.FFProbeAudioCacheDirectory, "b")); - EnsureDirectory(Path.Combine(Kernel.Instance.ApplicationPaths.FFProbeAudioCacheDirectory, "c")); - EnsureDirectory(Path.Combine(Kernel.Instance.ApplicationPaths.FFProbeAudioCacheDirectory, "d")); - EnsureDirectory(Path.Combine(Kernel.Instance.ApplicationPaths.FFProbeAudioCacheDirectory, "e")); - EnsureDirectory(Path.Combine(Kernel.Instance.ApplicationPaths.FFProbeAudioCacheDirectory, "f")); + EnsureDirectory(Path.Combine(root, "a")); + EnsureDirectory(Path.Combine(root, "b")); + EnsureDirectory(Path.Combine(root, "c")); + EnsureDirectory(Path.Combine(root, "d")); + EnsureDirectory(Path.Combine(root, "e")); + EnsureDirectory(Path.Combine(root, "f")); } - private void EnsureDirectory(string path) + private static void EnsureDirectory(string path) { if (!Directory.Exists(path)) { diff --git a/MediaBrowser.Controller/Providers/BaseMetadataProvider.cs b/MediaBrowser.Controller/Providers/BaseMetadataProvider.cs index 03684e8ee1..0cd7c08d0b 100644 --- a/MediaBrowser.Controller/Providers/BaseMetadataProvider.cs +++ b/MediaBrowser.Controller/Providers/BaseMetadataProvider.cs @@ -47,6 +47,9 @@ namespace MediaBrowser.Controller.Providers // Run this provider after all first priority providers Second, + // Run this provider after all second priority providers + Third, + // Run this provider last Last } diff --git a/MediaBrowser.Controller/Providers/VideoInfoProvider.cs b/MediaBrowser.Controller/Providers/VideoInfoProvider.cs new file mode 100644 index 0000000000..8b140bfc06 --- /dev/null +++ b/MediaBrowser.Controller/Providers/VideoInfoProvider.cs @@ -0,0 +1,89 @@ +using System.ComponentModel.Composition; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using MediaBrowser.Controller.Events; +using MediaBrowser.Controller.FFMpeg; +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Controller.Providers +{ + //[Export(typeof(BaseMetadataProvider))] + public class VideoInfoProvider : BaseMetadataProvider + { + public override bool Supports(BaseEntity item) + { + return item is Video; + } + + public override MetadataProviderPriority Priority + { + // Give this second priority + // Give metadata xml providers a chance to fill in data first + // Then we can skip this step whenever possible + get { return MetadataProviderPriority.Second; } + } + + public override async Task Fetch(BaseEntity item, ItemResolveEventArgs args) + { + Video video = item as Video; + + if (video.VideoType != VideoType.VideoFile) + { + // Not supported yet + return; + } + + if (CanSkip(video)) + { + return; + } + + string outputDirectory = Path.Combine(Kernel.Instance.ApplicationPaths.FFProbeVideoCacheDirectory, item.Id.ToString().Substring(0, 1)); + + string outputPath = Path.Combine(outputDirectory, item.Id + "-" + item.DateModified.Ticks + ".js"); + + FFProbeResult data = await FFProbe.Run(video, outputPath); + } + + /// + /// Determines if there's already enough info in the Video object to allow us to skip running ffprobe + /// + private bool CanSkip(Video video) + { + if (video.AudioStreams == null || !video.AudioStreams.Any()) + { + return false; + } + + if (string.IsNullOrEmpty(video.Codec)) + { + return false; + } + + if (string.IsNullOrEmpty(video.ScanType)) + { + return false; + } + + if (string.IsNullOrEmpty(video.FrameRate)) + { + return false; + } + + if (video.Height == 0 || video.Width == 0 || video.BitRate == 0) + { + return false; + } + + return true; + } + + public override void Init() + { + base.Init(); + + AudioInfoProvider.EnsureCacheSubFolders(Kernel.Instance.ApplicationPaths.FFProbeVideoCacheDirectory); + } + } +} diff --git a/MediaBrowser.Controller/Xml/BaseItemXmlParser.cs b/MediaBrowser.Controller/Xml/BaseItemXmlParser.cs index 78c2241281..cf0c89cba3 100644 --- a/MediaBrowser.Controller/Xml/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Xml/BaseItemXmlParser.cs @@ -378,53 +378,8 @@ namespace MediaBrowser.Controller.Xml break; case "Codec": - { - string codec = await reader.ReadElementContentAsStringAsync(); - - switch (codec.ToLower()) - { - case "dts-es": - case "dts-es matrix": - case "dts-es discrete": - stream.Format = "DTS"; - stream.Profile = "ES"; - break; - case "dts-hd hra": - case "dts-hd high resolution": - stream.Format = "DTS"; - stream.Profile = "HRA"; - break; - case "dts ma": - case "dts-hd ma": - case "dts-hd master": - stream.Format = "DTS"; - stream.Profile = "MA"; - break; - case "dolby digital": - case "dolby digital surround ex": - case "dolby surround": - stream.Format = "AC-3"; - break; - case "dolby digital plus": - stream.Format = "E-AC-3"; - break; - case "dolby truehd": - stream.Format = "AC-3"; - stream.Profile = "TrueHD"; - break; - case "mp2": - stream.Format = "MPEG Audio"; - stream.Profile = "Layer 2"; - break; - case "other": - break; - default: - stream.Format = codec; - break; - } - - break; - } + stream.Codec = await reader.ReadElementContentAsStringAsync(); + break; default: await reader.SkipAsync(); diff --git a/MediaBrowser.Model/Entities/Video.cs b/MediaBrowser.Model/Entities/Video.cs index 688226634c..dca8a2aba0 100644 --- a/MediaBrowser.Model/Entities/Video.cs +++ b/MediaBrowser.Model/Entities/Video.cs @@ -19,8 +19,7 @@ namespace MediaBrowser.Model.Entities public class AudioStream { - public string Format { get; set; } - public string Profile { get; set; } + public string Codec { get; set; } public string Language { get; set; } public int BitRate { get; set; } public int Channels { get; set; }