فهرست منبع

Switched all i/o to win32 methods and added protobuf serialization for ffprobe caching

LukePulverenti Luke Pulverenti luke pulverenti 13 سال پیش
والد
کامیت
c80c8c1cfd

+ 4 - 0
MediaBrowser.Common/MediaBrowser.Common.csproj

@@ -33,6 +33,9 @@
   <ItemGroup>
     <Reference Include="PresentationCore" />
     <Reference Include="PresentationFramework" />
+    <Reference Include="protobuf-net">
+      <HintPath>..\packages\protobuf-net.2.0.0.480\lib\net40\protobuf-net.dll</HintPath>
+    </Reference>
     <Reference Include="ServiceStack.Text, Version=3.9.5.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\packages\ServiceStack.Text.3.9.5\lib\net35\ServiceStack.Text.dll</HintPath>
@@ -89,6 +92,7 @@
     <Compile Include="Plugins\BasePlugin.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Serialization\JsvSerializer.cs" />
+    <Compile Include="Serialization\ProtobufSerializer.cs" />
     <Compile Include="Serialization\XmlSerializer.cs" />
     <Compile Include="UI\BaseApplication.cs" />
     <Compile Include="UI\Splash.xaml.cs">

+ 38 - 0
MediaBrowser.Common/Serialization/ProtobufSerializer.cs

@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.IO;
+
+namespace MediaBrowser.Common.Serialization
+{
+    public static class ProtobufSerializer
+    {
+        public static void SerializeToStream<T>(T obj, Stream stream)
+        {
+            ProtoBuf.Serializer.Serialize<T>(stream, obj);
+        }
+
+        public static T DeserializeFromStream<T>(Stream stream)
+        {
+            return ProtoBuf.Serializer.Deserialize<T>(stream);
+        }
+
+        public static void SerializeToFile<T>(T obj, string file)
+        {
+            using (Stream stream = File.Open(file, FileMode.Create))
+            {
+                SerializeToStream<T>(obj, stream);
+            }
+        }
+
+        public static T DeserializeFromFile<T>(string file)
+        {
+            using (Stream stream = File.OpenRead(file))
+            {
+                return DeserializeFromStream<T>(stream);
+            }
+        }
+    }
+}

+ 5 - 0
MediaBrowser.Common/UI/BaseApplication.cs

@@ -48,6 +48,11 @@ namespace MediaBrowser.Common.UI
             }
             catch (Exception ex)
             {
+                if (Logger.LoggerInstance != null)
+                {
+                    Logger.LogException(ex);
+                }
+
                 MessageBox.Show("There was an error launching Media Browser: " + ex.Message);
                 splash.Close();
 

+ 1 - 0
MediaBrowser.Common/packages.config

@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
+  <package id="protobuf-net" version="2.0.0.480" targetFramework="net45" />
   <package id="Rx-Core" version="2.0.20814" targetFramework="net45" />
   <package id="Rx-Interfaces" version="2.0.20814" targetFramework="net45" />
   <package id="Rx-Linq" version="2.0.20814" targetFramework="net45" />

+ 37 - 49
MediaBrowser.Controller/Events/ItemResolveEventArgs.cs

@@ -10,70 +10,47 @@ namespace MediaBrowser.Controller.Events
     /// </summary>
     public class ItemResolveEventArgs : PreBeginResolveEventArgs
     {
-        public LazyFileInfo[] FileSystemChildren { get; set; }
+        public WIN32_FIND_DATA[] FileSystemChildren { get; set; }
 
-        public LazyFileInfo? GetFileSystemEntry(string path, bool? isFolder = null)
+        public WIN32_FIND_DATA? GetFileSystemEntry(string path)
         {
             for (int i = 0; i < FileSystemChildren.Length; i++)
             {
-                LazyFileInfo entry = FileSystemChildren[i];
+                WIN32_FIND_DATA entry = FileSystemChildren[i];
 
                 if (entry.Path.Equals(path, StringComparison.OrdinalIgnoreCase))
                 {
-                    if (isFolder.HasValue)
-                    {
-                        if (isFolder.Value && !entry.FileInfo.IsDirectory)
-                        {
-                            continue;
-                        }
-                        else if (!isFolder.Value && entry.FileInfo.IsDirectory)
-                        {
-                            continue;
-                        }
-                    }
-                    
                     return entry;
                 }
             }
-
+           
             return null;
         }
 
-        public LazyFileInfo? GetFileSystemEntryByName(string name, bool? isFolder = null)
+        public bool ContainsFile(string name)
         {
             for (int i = 0; i < FileSystemChildren.Length; i++)
             {
-                LazyFileInfo entry = FileSystemChildren[i];
-
-                if (System.IO.Path.GetFileName(entry.Path).Equals(name, StringComparison.OrdinalIgnoreCase))
+                if (System.IO.Path.GetFileName(FileSystemChildren[i].Path).Equals(name, StringComparison.OrdinalIgnoreCase))
                 {
-                    if (isFolder.HasValue)
-                    {
-                        if (isFolder.Value && !entry.FileInfo.IsDirectory)
-                        {
-                            continue;
-                        }
-                        else if (!isFolder.Value && entry.FileInfo.IsDirectory)
-                        {
-                            continue;
-                        }
-                    }
-
-                    return entry;
+                    return true;
                 }
             }
 
-            return null;
-        }
-
-        public bool ContainsFile(string name)
-        {
-            return GetFileSystemEntryByName(name, false) != null;
+            return false;
         }
 
         public bool ContainsFolder(string name)
         {
-            return GetFileSystemEntryByName(name, true) != null;
+            for (int i = 0; i < FileSystemChildren.Length; i++)
+            {
+                if (System.IO.Path.GetFileName(FileSystemChildren[i].Path).Equals(name, StringComparison.OrdinalIgnoreCase))
+                {
+                    return true;
+                }
+            }
+
+            return false;
         }
     }
 
@@ -88,21 +65,15 @@ namespace MediaBrowser.Controller.Events
 
         public bool Cancel { get; set; }
 
-        public LazyFileInfo File { get; set; }
+        public WIN32_FIND_DATA FileInfo { get; set; }
 
-        public string Path
-        {
-            get
-            {
-                return File.Path;
-            }
-        }
+        public string Path { get; set; }
 
         public bool IsDirectory
         {
             get
             {
-                return File.FileInfo.dwFileAttributes.HasFlag(FileAttributes.Directory);
+                return FileInfo.dwFileAttributes.HasFlag(FileAttributes.Directory);
             }
         }
 
@@ -133,5 +104,22 @@ namespace MediaBrowser.Controller.Events
                 return vf.CollectionType;
             }
         }
+
+        public bool IsHidden
+        {
+            get
+            {
+                return FileInfo.IsHidden;
+            }
+        }
+
+        public bool IsSystemFile
+        {
+            get
+            {
+                return FileInfo.IsSystemFile;
+            }
+        }
+
     }
 }

+ 23 - 17
MediaBrowser.Controller/FFMpeg/FFProbe.cs

@@ -23,6 +23,10 @@ namespace MediaBrowser.Controller.FFMpeg
             catch (FileNotFoundException)
             {
             }
+            catch (Exception ex)
+            {
+                Logger.LogException(ex);
+            }
 
             FFProbeResult result = Run(item.Path);
 
@@ -34,15 +38,22 @@ namespace MediaBrowser.Controller.FFMpeg
 
         private static FFProbeResult GetCachedResult(string path)
         {
-            return JsvSerializer.DeserializeFromFile<FFProbeResult>(path);
+            return ProtobufSerializer.DeserializeFromFile<FFProbeResult>(path);
         }
 
-        private static void CacheResult(FFProbeResult result, string outputCachePath)
+        private static async void CacheResult(FFProbeResult result, string outputCachePath)
         {
-            Task.Run(() =>
+            await Task.Run(() =>
             {
-                JsvSerializer.SerializeToFile<FFProbeResult>(result, outputCachePath);
-            });
+                try
+                {
+                    ProtobufSerializer.SerializeToFile<FFProbeResult>(result, outputCachePath);
+                }
+                catch (Exception ex)
+                {
+                    Logger.LogException(ex);
+                }
+            }).ConfigureAwait(false);
         }
 
         public static FFProbeResult Run(Video item)
@@ -55,6 +66,10 @@ namespace MediaBrowser.Controller.FFMpeg
             catch (FileNotFoundException)
             {
             }
+            catch (Exception ex)
+            {
+                Logger.LogException(ex);
+            }
 
             FFProbeResult result = Run(item.Path);
 
@@ -93,16 +108,7 @@ namespace MediaBrowser.Controller.FFMpeg
                 // If we ever decide to disable the ffmpeg log then you must uncomment the below line.
                 process.BeginErrorReadLine();
 
-                FFProbeResult result = JsonSerializer.DeserializeFromStream<FFProbeResult>(process.StandardOutput.BaseStream);
-
-                process.WaitForExit();
-
-                if (process.ExitCode != 0)
-                {
-                    Logger.LogInfo("FFProbe exited with code {0} for {1}", process.ExitCode, input);
-                }
-
-                return result;
+                return JsonSerializer.DeserializeFromStream<FFProbeResult>(process.StandardOutput.BaseStream);
             }
             catch (Exception ex)
             {
@@ -129,14 +135,14 @@ namespace MediaBrowser.Controller.FFMpeg
         {
             string outputDirectory = Path.Combine(Kernel.Instance.ApplicationPaths.FFProbeAudioCacheDirectory, item.Id.ToString().Substring(0, 1));
 
-            return Path.Combine(outputDirectory, item.Id + "-" + item.DateModified.Ticks + ".jsv");
+            return Path.Combine(outputDirectory, item.Id + "-" + item.DateModified.Ticks + ".pb");
         }
 
         private static string GetFFProbeVideoCachePath(BaseEntity item)
         {
             string outputDirectory = Path.Combine(Kernel.Instance.ApplicationPaths.FFProbeVideoCacheDirectory, item.Id.ToString().Substring(0, 1));
 
-            return Path.Combine(outputDirectory, item.Id + "-" + item.DateModified.Ticks + ".jsv");
+            return Path.Combine(outputDirectory, item.Id + "-" + item.DateModified.Ticks + ".pb");
         }
     }
 }

+ 57 - 2
MediaBrowser.Controller/FFMpeg/FFProbeResult.cs

@@ -1,4 +1,5 @@
 using System.Collections.Generic;
+using ProtoBuf;
 
 namespace MediaBrowser.Controller.FFMpeg
 {
@@ -7,9 +8,13 @@ namespace MediaBrowser.Controller.FFMpeg
     /// Sample output:
     /// http://stackoverflow.com/questions/7708373/get-ffmpeg-information-in-friendly-way
     /// </summary>
+    [ProtoContract]
     public class FFProbeResult
     {
-        public IEnumerable<MediaStream> streams { get; set; }
+        [ProtoMember(1)]
+        public MediaStream[] streams { get; set; }
+
+        [ProtoMember(2)]
         public MediaFormat format { get; set; }
     }
 
@@ -18,47 +23,97 @@ namespace MediaBrowser.Controller.FFMpeg
     /// A number of properties are commented out to improve deserialization performance
     /// Enable them as needed.
     /// </summary>
+    [ProtoContract]
     public class MediaStream
     {
+        [ProtoMember(1)]
         public int index { get; set; }
+
+        [ProtoMember(2)]
         public string profile { get; set; }
+
+        [ProtoMember(3)]
         public string codec_name { get; set; }
+
+        [ProtoMember(4)]
         public string codec_long_name { get; set; }
+
+        [ProtoMember(5)]
         public string codec_type { get; set; }
+
         //public string codec_time_base { get; set; }
         //public string codec_tag { get; set; }
         //public string codec_tag_string { get; set; }
         //public string sample_fmt { get; set; }
+
+        [ProtoMember(6)]
         public string sample_rate { get; set; }
+
+        [ProtoMember(7)]
         public int channels { get; set; }
+
         //public int bits_per_sample { get; set; }
         //public string r_frame_rate { get; set; }
+
+        [ProtoMember(8)]
         public string avg_frame_rate { get; set; }
+
         //public string time_base { get; set; }
         //public string start_time { get; set; }
+
+        [ProtoMember(9)]
         public string duration { get; set; }
+
+        [ProtoMember(10)]
         public string bit_rate { get; set; }
 
+        [ProtoMember(11)]
         public int width { get; set; }
+
+        [ProtoMember(12)]
         public int height { get; set; }
+
         //public int has_b_frames { get; set; }
         //public string sample_aspect_ratio { get; set; }
+
+        [ProtoMember(13)]
         public string display_aspect_ratio { get; set; }
+
         //public string pix_fmt { get; set; }
         //public int level { get; set; }
-        public Dictionary<string,string> tags { get; set; }
+
+        [ProtoMember(14)]
+        public Dictionary<string, string> tags { get; set; }
    }
 
+    [ProtoContract]
     public class MediaFormat
     {
+        [ProtoMember(1)]
         public string filename { get; set; }
+
+        [ProtoMember(2)]
         public int nb_streams { get; set; }
+
+        [ProtoMember(3)]
         public string format_name { get; set; }
+
+        [ProtoMember(4)]
         public string format_long_name { get; set; }
+
+        [ProtoMember(5)]
         public string start_time { get; set; }
+
+        [ProtoMember(6)]
         public string duration { get; set; }
+
+        [ProtoMember(7)]
         public string size { get; set; }
+
+        [ProtoMember(8)]
         public string bit_rate { get; set; }
+
+        [ProtoMember(9)]
         public Dictionary<string, string> tags { get; set; }
     }
 }

+ 59 - 25
MediaBrowser.Controller/IO/FileData.cs

@@ -2,6 +2,11 @@
 using System.IO;
 using System.Runtime.InteropServices;
 
+using System.Runtime.ConstrainedExecution;
+using Microsoft.Win32.SafeHandles;
+using System.Collections.Generic;
+using System.Linq;
+
 namespace MediaBrowser.Controller.IO
 {
     public static class FileData
@@ -16,16 +21,67 @@ namespace MediaBrowser.Controller.IO
             if (handle == IntPtr.Zero)
                 throw new IOException("FindFirstFile failed");
             FindClose(handle);
+
+            data.Path = fileName;
             return data;
         }
 
-        [DllImport("kernel32")]
+        public static IEnumerable<WIN32_FIND_DATA> GetFileSystemEntries(string path, string searchPattern)
+        {
+            string lpFileName = Path.Combine(path, searchPattern);
+
+            WIN32_FIND_DATA lpFindFileData;
+            var handle = FindFirstFile(lpFileName, out lpFindFileData);
+
+            if (handle == IntPtr.Zero)
+            {
+                int hr = Marshal.GetLastWin32Error();
+                if (hr != 2 && hr != 0x12)
+                {
+                    throw new IOException("GetFileSystemEntries failed");
+                }
+                yield break;
+            }
+
+            if (IsValid(lpFindFileData.cFileName))
+            {
+                yield return lpFindFileData;
+            }
+
+            while (FindNextFile(handle, out lpFindFileData) != IntPtr.Zero)
+            {
+                if (IsValid(lpFindFileData.cFileName))
+                {
+                    lpFindFileData.Path = Path.Combine(path, lpFindFileData.cFileName);
+                    yield return lpFindFileData;
+                }
+            }
+
+            FindClose(handle);
+        }
+
+        private static bool IsValid(string cFileName)
+        {
+            if (cFileName.Equals(".", StringComparison.OrdinalIgnoreCase))
+            {
+                return false;
+            }
+            if (cFileName.Equals("..", StringComparison.OrdinalIgnoreCase))
+            {
+                return false;
+            }
+
+            return true;
+        }
+
+        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
         private static extern IntPtr FindFirstFile(string fileName, out WIN32_FIND_DATA data);
 
+        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+        private static extern IntPtr FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA data);
+
         [DllImport("kernel32")]
         private static extern bool FindClose(IntPtr hFindFile);
-
-
     }
 
     [StructLayout(LayoutKind.Sequential)]
@@ -107,30 +163,8 @@ namespace MediaBrowser.Controller.IO
             highBits = highBits << 32;
             return DateTime.FromFileTime(highBits + (long)filetime.dwLowDateTime);
         }
-    }
 
-    public struct LazyFileInfo
-    {
         public string Path { get; set; }
-
-        private WIN32_FIND_DATA? _FileInfo { get; set; }
-
-        public WIN32_FIND_DATA FileInfo
-        {
-            get
-            {
-                if (_FileInfo == null)
-                {
-                    _FileInfo = FileData.GetFileData(Path);
-                }
-
-                return _FileInfo.Value;
-            }
-            set
-            {
-                _FileInfo = value;
-            }
-        }
     }
 
 }

+ 3 - 3
MediaBrowser.Controller/Kernel.cs

@@ -69,7 +69,7 @@ namespace MediaBrowser.Controller
         public async override Task Init(IProgress<TaskProgress> progress)
         {
             ExtractFFMpeg();
-            
+
             await base.Init(progress).ConfigureAwait(false);
 
             progress.Report(new TaskProgress() { Description = "Loading Users", PercentComplete = 15 });
@@ -91,7 +91,7 @@ namespace MediaBrowser.Controller
 
             // Sort the providers by priority
             MetadataProviders = MetadataProviders.OrderBy(e => e.Priority);
-            
+
             // Initialize the metadata providers
             Parallel.ForEach(MetadataProviders, provider =>
             {
@@ -106,7 +106,7 @@ namespace MediaBrowser.Controller
         /// </summary>
         void ItemController_PreBeginResolvePath(object sender, PreBeginResolveEventArgs e)
         {
-            if (e.File.FileInfo.IsHidden || e.File.FileInfo.IsSystemFile)
+            if (e.IsHidden || e.IsSystemFile)
             {
                 // Ignore hidden files and folders
                 e.Cancel = true;

+ 25 - 46
MediaBrowser.Controller/Library/ItemController.cs

@@ -19,15 +19,8 @@ namespace MediaBrowser.Controller.Library
         /// This gives listeners a chance to cancel the operation and cause the path to be ignored.
         /// </summary>
         public event EventHandler<PreBeginResolveEventArgs> PreBeginResolvePath;
-        private bool OnPreBeginResolvePath(Folder parent, string path, WIN32_FIND_DATA fileData)
+        private bool OnPreBeginResolvePath(PreBeginResolveEventArgs args)
         {
-            PreBeginResolveEventArgs args = new PreBeginResolveEventArgs()
-            {
-                Parent = parent,
-                File = new LazyFileInfo() { Path = path, FileInfo = fileData },
-                Cancel = false
-            };
-
             if (PreBeginResolvePath != null)
             {
                 PreBeginResolvePath(this, args);
@@ -76,35 +69,35 @@ namespace MediaBrowser.Controller.Library
         /// </summary>
         public async Task<BaseItem> GetItem(string path, Folder parent = null, WIN32_FIND_DATA? fileInfo = null)
         {
-            WIN32_FIND_DATA fileData = fileInfo ?? FileData.GetFileData(path);
+            ItemResolveEventArgs args = new ItemResolveEventArgs()
+            {
+                FileInfo = fileInfo ?? FileData.GetFileData(path),
+                Parent = parent,
+                Cancel = false,
+                Path = path
+            };
 
-            if (!OnPreBeginResolvePath(parent, path, fileData))
+            if (!OnPreBeginResolvePath(args))
             {
                 return null;
             }
 
-            LazyFileInfo[] fileSystemChildren;
+            WIN32_FIND_DATA[] fileSystemChildren;
 
             // Gather child folder and files
-            if (fileData.IsDirectory)
+            if (args.IsDirectory)
             {
-                fileSystemChildren = ConvertFileSystemEntries(Directory.GetFileSystemEntries(path, "*", SearchOption.TopDirectoryOnly));
+                fileSystemChildren = FileData.GetFileSystemEntries(path, "*").ToArray();
 
                 bool isVirtualFolder = parent != null && parent.IsRoot;
                 fileSystemChildren = FilterChildFileSystemEntries(fileSystemChildren, isVirtualFolder);
             }
             else
             {
-                fileSystemChildren = new LazyFileInfo[] { };
+                fileSystemChildren = new WIN32_FIND_DATA[] { };
             }
 
-            ItemResolveEventArgs args = new ItemResolveEventArgs()
-            {
-                File = new LazyFileInfo() { Path = path, FileInfo = fileData },
-                FileSystemChildren = fileSystemChildren,
-                Parent = parent,
-                Cancel = false
-            };
+            args.FileSystemChildren = fileSystemChildren;
 
             // Fire BeginResolvePath to see if anyone wants to cancel this operation
             if (!OnBeginResolvePath(args))
@@ -136,7 +129,7 @@ namespace MediaBrowser.Controller.Library
         /// <summary>
         /// Finds child BaseItems for a given Folder
         /// </summary>
-        private Task<BaseItem>[] GetChildren(Folder folder, LazyFileInfo[] fileSystemChildren)
+        private Task<BaseItem>[] GetChildren(Folder folder, WIN32_FIND_DATA[] fileSystemChildren)
         {
             Task<BaseItem>[] tasks = new Task<BaseItem>[fileSystemChildren.Length];
 
@@ -144,7 +137,7 @@ namespace MediaBrowser.Controller.Library
             {
                 var child = fileSystemChildren[i];
 
-                tasks[i] = GetItem(child.Path, folder, child.FileInfo);
+                tasks[i] = GetItem(child.Path, folder, child);
             }
 
             return tasks;
@@ -153,14 +146,14 @@ namespace MediaBrowser.Controller.Library
         /// <summary>
         /// Transforms shortcuts into their actual paths
         /// </summary>
-        private LazyFileInfo[] FilterChildFileSystemEntries(LazyFileInfo[] fileSystemChildren, bool flattenShortcuts)
+        private WIN32_FIND_DATA[] FilterChildFileSystemEntries(WIN32_FIND_DATA[] fileSystemChildren, bool flattenShortcuts)
         {
-            LazyFileInfo[] returnArray = new LazyFileInfo[fileSystemChildren.Length];
-            List<LazyFileInfo> resolvedShortcuts = new List<LazyFileInfo>();
+            WIN32_FIND_DATA[] returnArray = new WIN32_FIND_DATA[fileSystemChildren.Length];
+            List<WIN32_FIND_DATA> resolvedShortcuts = new List<WIN32_FIND_DATA>();
 
             for (int i = 0; i < fileSystemChildren.Length; i++)
             {
-                LazyFileInfo file = fileSystemChildren[i];
+                WIN32_FIND_DATA file = fileSystemChildren[i];
 
                 // If it's a shortcut, resolve it
                 if (Shortcut.IsShortcut(file.Path))
@@ -176,18 +169,18 @@ namespace MediaBrowser.Controller.Library
                         if (flattenShortcuts)
                         {
                             returnArray[i] = file;
-                            LazyFileInfo[] newChildren = ConvertFileSystemEntries(Directory.GetFileSystemEntries(newPath, "*", SearchOption.TopDirectoryOnly));
+                            WIN32_FIND_DATA[] newChildren = FileData.GetFileSystemEntries(newPath, "*").ToArray();
 
                             resolvedShortcuts.AddRange(FilterChildFileSystemEntries(newChildren, false));
                         }
                         else
                         {
-                            returnArray[i] = new LazyFileInfo() { Path = newPath, FileInfo = newPathData };
+                            returnArray[i] = newPathData;
                         }
                     }
                     else
                     {
-                        returnArray[i] = new LazyFileInfo() { Path = newPath, FileInfo = newPathData };
+                        returnArray[i] = newPathData;
                     }
                 }
                 else
@@ -286,26 +279,12 @@ namespace MediaBrowser.Controller.Library
             item.DateModified = Directory.GetLastAccessTime(path);
 
             ItemResolveEventArgs args = new ItemResolveEventArgs();
-            args.File = new LazyFileInfo() { Path = path };
-            args.FileSystemChildren = ConvertFileSystemEntries(Directory.GetFileSystemEntries(path, "*", SearchOption.TopDirectoryOnly));
+            args.FileInfo = FileData.GetFileData(path);
+            args.FileSystemChildren = FileData.GetFileSystemEntries(path, "*").ToArray();
 
             await Kernel.Instance.ExecuteMetadataProviders(item, args).ConfigureAwait(false);
 
             return item;
         }
-
-        private LazyFileInfo[] ConvertFileSystemEntries(string[] files)
-        {
-            LazyFileInfo[] items = new LazyFileInfo[files.Length];
-
-            for (int i = 0; i < files.Length; i++)
-            {
-                string file = files[i];
-
-                items[i] = new LazyFileInfo() { Path = file };
-            }
-
-            return items;
-        }
     }
 }

+ 4 - 0
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -30,6 +30,10 @@
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
   <ItemGroup>
+    <Reference Include="protobuf-net, Version=2.0.0.480, Culture=neutral, PublicKeyToken=257b51d87d2e4d67, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\packages\protobuf-net.2.0.0.480\lib\net40\protobuf-net.dll</HintPath>
+    </Reference>
     <Reference Include="System" />
     <Reference Include="System.ComponentModel.Composition" />
     <Reference Include="System.Core" />

+ 3 - 4
MediaBrowser.Controller/Providers/FolderProviderFromXml.cs

@@ -1,4 +1,5 @@
 using System.ComponentModel.Composition;
+using System.IO;
 using System.Threading.Tasks;
 using MediaBrowser.Controller.Events;
 using MediaBrowser.Controller.Xml;
@@ -21,11 +22,9 @@ namespace MediaBrowser.Controller.Providers
 
         public override Task FetchAsync(BaseEntity item, ItemResolveEventArgs args)
         {
-            var metadataFile = args.GetFileSystemEntryByName("folder.xml");
-
-            if (metadataFile.HasValue)
+            if (args.ContainsFile("folder.xml"))
             {
-                return Task.Run(() => { new FolderXmlParser().Fetch(item as Folder, metadataFile.Value.Path); });
+                return Task.Run(() => { new FolderXmlParser().Fetch(item as Folder, Path.Combine(args.Path, "folder.xml")); });
             }
 
             return Task.FromResult<object>(null);

+ 7 - 12
MediaBrowser.Controller/Providers/LocalTrailerProvider.cs

@@ -3,6 +3,7 @@ using System.ComponentModel.Composition;
 using System.IO;
 using System.Threading.Tasks;
 using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.IO;
 using MediaBrowser.Model.Entities;
 
 namespace MediaBrowser.Controller.Providers
@@ -22,27 +23,21 @@ namespace MediaBrowser.Controller.Providers
 
         public async override Task FetchAsync(BaseEntity item, ItemResolveEventArgs args)
         {
-            var trailerPath = args.GetFileSystemEntryByName("trailers", true);
-
-            if (trailerPath.HasValue)
+            if (args.ContainsFolder("trailers"))
             {
-                string[] allFiles = Directory.GetFileSystemEntries(trailerPath.Value.Path, "*", SearchOption.TopDirectoryOnly);
-
-                List<Video> localTrailers = new List<Video>();
+                List<Video> items = new List<Video>();
 
-                for (int i = 0; i < allFiles.Length; i++)
+                foreach (WIN32_FIND_DATA file in FileData.GetFileSystemEntries(Path.Combine(args.Path, "trailers"), "*"))
                 {
-                    string file = allFiles[i];
-
-                    Video video = await Kernel.Instance.ItemController.GetItem(file).ConfigureAwait(false) as Video;
+                    Video video = await Kernel.Instance.ItemController.GetItem(file.Path, fileInfo: file).ConfigureAwait(false) as Video;
 
                     if (video != null)
                     {
-                        localTrailers.Add(video);
+                        items.Add(video);
                     }
                 }
 
-                (item as BaseItem).LocalTrailers = localTrailers;
+                (item as BaseItem).LocalTrailers = items;
             }
         }
     }

+ 4 - 0
MediaBrowser.Controller/Providers/VideoInfoProvider.cs

@@ -183,6 +183,10 @@ namespace MediaBrowser.Controller.Providers
             base.Init();
 
             AudioInfoProvider.EnsureCacheSubFolders(Kernel.Instance.ApplicationPaths.FFProbeVideoCacheDirectory);
+
+            ProtoBuf.Meta.RuntimeTypeModel.Default.Add(typeof(FFProbeResult), true);
+            ProtoBuf.Meta.RuntimeTypeModel.Default.Add(typeof(MediaStream), true);
+            ProtoBuf.Meta.RuntimeTypeModel.Default.Add(typeof(MediaFormat), true);
         }
     }
 }

+ 8 - 10
MediaBrowser.Controller/Resolvers/BaseItemResolver.cs

@@ -81,30 +81,28 @@ namespace MediaBrowser.Controller.Resolvers
                 return;
             }
 
-            WIN32_FIND_DATA fileData;
-
             // See if a different path came out of the resolver than what went in
             if (!args.Path.Equals(item.Path, StringComparison.OrdinalIgnoreCase))
             {
-                LazyFileInfo? childData = args.GetFileSystemEntry(item.Path);
+                WIN32_FIND_DATA? childData = args.GetFileSystemEntry(item.Path);
 
                 if (childData != null)
                 {
-                    fileData = childData.Value.FileInfo;
+                    item.DateCreated = childData.Value.CreationTime;
+                    item.DateModified = childData.Value.LastWriteTime;
                 }
                 else
                 {
-                    fileData = FileData.GetFileData(item.Path);
+                    WIN32_FIND_DATA fileData = FileData.GetFileData(item.Path);
+                    item.DateCreated = fileData.CreationTime;
+                    item.DateModified = fileData.LastWriteTime;
                 }
             }
             else
             {
-                fileData = args.File.FileInfo;
+                item.DateCreated = args.FileInfo.CreationTime;
+                item.DateModified = args.FileInfo.LastWriteTime;
             }
-
-            item.DateCreated = fileData.CreationTime;
-
-            item.DateModified = fileData.LastWriteTime;
         }
     }
 

+ 1 - 1
MediaBrowser.Controller/Resolvers/VideoResolver.cs

@@ -53,7 +53,7 @@ namespace MediaBrowser.Controller.Resolvers
                 {
                     var folder = args.FileSystemChildren[i];
 
-                    if (!folder.FileInfo.IsDirectory)
+                    if (!folder.IsDirectory)
                     {
                         continue;
                     }

+ 1 - 0
MediaBrowser.Controller/packages.config

@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
+  <package id="protobuf-net" version="2.0.0.480" targetFramework="net45" />
   <package id="Rx-Core" version="2.0.20814" targetFramework="net45" />
   <package id="Rx-Interfaces" version="2.0.20814" targetFramework="net45" />
   <package id="Rx-Linq" version="2.0.20814" targetFramework="net45" />

+ 3 - 4
MediaBrowser.Movies/Providers/MovieProviderFromXml.cs

@@ -1,4 +1,5 @@
 using System.ComponentModel.Composition;
+using System.IO;
 using System.Threading.Tasks;
 using MediaBrowser.Controller.Events;
 using MediaBrowser.Controller.Providers;
@@ -23,11 +24,9 @@ namespace MediaBrowser.Movies.Providers
 
         public override Task FetchAsync(BaseEntity item, ItemResolveEventArgs args)
         {
-            var metadataFile = args.GetFileSystemEntryByName("movie.xml");
-
-            if (metadataFile.HasValue)
+            if (args.ContainsFile("movie.xml"))
             {
-                return Task.Run(() => { new BaseItemXmlParser<Movie>().Fetch(item as Movie, metadataFile.Value.Path); });
+                return Task.Run(() => { new BaseItemXmlParser<Movie>().Fetch(item as Movie, Path.Combine(args.Path, "movie.xml")); });
             }
 
             return Task.FromResult<object>(null);

+ 20 - 18
MediaBrowser.Movies/Resolvers/MovieResolver.cs

@@ -8,6 +8,8 @@ using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.Resolvers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Movies.Entities;
+using System.Collections.Generic;
+using System.Threading.Tasks;
 
 namespace MediaBrowser.Movies.Resolvers
 {
@@ -18,9 +20,7 @@ namespace MediaBrowser.Movies.Resolvers
         {
             if ((args.VirtualFolderCollectionType ?? string.Empty).Equals("Movies", StringComparison.OrdinalIgnoreCase) && args.IsDirectory)
             {
-                var metadataFile = args.GetFileSystemEntryByName("movie.xml");
-
-                if (metadataFile.HasValue || Path.GetFileName(args.Path).IndexOf("[tmdbid=", StringComparison.OrdinalIgnoreCase) != -1)
+                if (args.ContainsFile("movie.xml") || Path.GetFileName(args.Path).IndexOf("[tmdbid=", StringComparison.OrdinalIgnoreCase) != -1)
                 {
                     return GetMovie(args) ?? new Movie();
                 }
@@ -52,8 +52,9 @@ namespace MediaBrowser.Movies.Resolvers
 
                 ItemResolveEventArgs childArgs = new ItemResolveEventArgs()
                 {
-                    File = child,
-                    FileSystemChildren = new LazyFileInfo[] { }
+                    FileInfo = child,
+                    FileSystemChildren = new WIN32_FIND_DATA[] { },
+                    Path = child.Path
                 };
 
                 var item = base.Resolve(childArgs);
@@ -71,23 +72,24 @@ namespace MediaBrowser.Movies.Resolvers
             return null;
         }
 
-        private void PopulateBonusFeatures(Movie item, ItemResolveEventArgs args)
+        /*private void PopulateBonusFeatures(Movie item, ItemResolveEventArgs args)
         {
-            var trailerPath = args.GetFileSystemEntryByName("specials", true);
-
-            if (trailerPath.HasValue)
+            if (args.ContainsFolder("specials"))
             {
-                string[] allFiles = Directory.GetFileSystemEntries(trailerPath.Value.Path, "*", SearchOption.TopDirectoryOnly);
+                List<Video> items = new List<Video>();
 
-                item.SpecialFeatures = allFiles.Select(f => Kernel.Instance.ItemController.GetItem(f)).OfType<Video>();
-            }
-        }
+                foreach (WIN32_FIND_DATA file in FileData.GetFileSystemEntries(Path.Combine(args.Path, "specials"), "*"))
+                {
+                    Video video = await Kernel.Instance.ItemController.GetItem(file.Path, fileInfo: file).ConfigureAwait(false) as Video;
 
-        protected override void SetInitialItemValues(Movie item, ItemResolveEventArgs args)
-        {
-            base.SetInitialItemValues(item, args);
+                    if (video != null)
+                    {
+                        items.Add(video);
+                    }
+                }
 
-            PopulateBonusFeatures(item, args);
-        }
+                (item as BaseItem).LocalTrailers = items;
+            }
+        }*/
     }
 }

+ 3 - 4
MediaBrowser.TV/Providers/SeriesProviderFromXml.cs

@@ -1,4 +1,5 @@
 using System.ComponentModel.Composition;
+using System.IO;
 using System.Threading.Tasks;
 using MediaBrowser.Controller.Events;
 using MediaBrowser.Controller.Providers;
@@ -23,11 +24,9 @@ namespace MediaBrowser.TV.Providers
 
         public override Task FetchAsync(BaseEntity item, ItemResolveEventArgs args)
         {
-            var metadataFile = args.GetFileSystemEntryByName("series.xml");
-
-            if (metadataFile.HasValue)
+            if (args.ContainsFile("series.xml"))
             {
-                return Task.Run(() => { new SeriesXmlParser().Fetch(item as Series, metadataFile.Value.Path); });
+                return Task.Run(() => { new SeriesXmlParser().Fetch(item as Series, Path.Combine(args.Path, "series.xml")); });
             }
 
             return Task.FromResult<object>(null);

+ 1 - 3
MediaBrowser.TV/Resolvers/SeriesResolver.cs

@@ -20,9 +20,7 @@ namespace MediaBrowser.TV.Resolvers
                     return null;
                 }
 
-                var metadataFile = args.GetFileSystemEntryByName("series.xml");
-
-                if (metadataFile.HasValue || Path.GetFileName(args.Path).IndexOf("[tvdbid=", StringComparison.OrdinalIgnoreCase) != -1 || TVUtils.IsSeriesFolder(args.Path, args.FileSystemChildren))
+                if (args.ContainsFile("series.xml") || Path.GetFileName(args.Path).IndexOf("[tvdbid=", StringComparison.OrdinalIgnoreCase) != -1 || TVUtils.IsSeriesFolder(args.Path, args.FileSystemChildren))
                 {
                     return new Series();
                 }

+ 2 - 2
MediaBrowser.TV/TVUtils.cs

@@ -51,13 +51,13 @@ namespace MediaBrowser.TV
             return seasonPathExpressions.Any(r => r.IsMatch(path));
         }
 
-        public static bool IsSeriesFolder(string path, LazyFileInfo[] fileSystemChildren)
+        public static bool IsSeriesFolder(string path, WIN32_FIND_DATA[] fileSystemChildren)
         {
             for (int i = 0; i < fileSystemChildren.Length; i++)
             {
                 var child = fileSystemChildren[i];
 
-                if (child.FileInfo.IsDirectory)
+                if (child.IsDirectory)
                 {
                     if (IsSeasonFolder(child.Path))
                     {