Switched to low-level io methods for better performance

This commit is contained in:
LukePulverenti Luke Pulverenti luke pulverenti 2012-08-20 19:53:32 -04:00
parent 6fbeee841f
commit 758d18a652
19 changed files with 205 additions and 85 deletions

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using MediaBrowser.Model.Entities;
using MediaBrowser.Controller.IO;
namespace MediaBrowser.Controller.Events
{
@ -10,18 +11,25 @@ namespace MediaBrowser.Controller.Events
/// </summary>
public class ItemResolveEventArgs : PreBeginResolveEventArgs
{
public IEnumerable<KeyValuePair<string, FileAttributes>> FileSystemChildren { get; set; }
public IEnumerable<KeyValuePair<string, WIN32_FIND_DATA>> FileSystemChildren { get; set; }
public KeyValuePair<string, FileAttributes>? GetFolderByName(string name)
public KeyValuePair<string, WIN32_FIND_DATA>? GetFileSystemEntry(string path, bool? isFolder)
{
foreach (KeyValuePair<string, FileAttributes> entry in FileSystemChildren)
foreach (KeyValuePair<string, WIN32_FIND_DATA> entry in FileSystemChildren)
{
if (!entry.Value.HasFlag(FileAttributes.Directory))
if (isFolder.HasValue)
{
continue;
if (isFolder.Value && entry.Value.IsDirectory)
{
continue;
}
else if (!isFolder.Value && !entry.Value.IsDirectory)
{
continue;
}
}
if (System.IO.Path.GetFileName(entry.Key).Equals(name, StringComparison.OrdinalIgnoreCase))
if (entry.Key.Equals(path, StringComparison.OrdinalIgnoreCase))
{
return entry;
}
@ -30,13 +38,20 @@ namespace MediaBrowser.Controller.Events
return null;
}
public KeyValuePair<string, FileAttributes>? GetFileByName(string name)
public KeyValuePair<string, WIN32_FIND_DATA>? GetFileSystemEntryByName(string name, bool? isFolder)
{
foreach (KeyValuePair<string, FileAttributes> entry in FileSystemChildren)
foreach (KeyValuePair<string, WIN32_FIND_DATA> entry in FileSystemChildren)
{
if (entry.Value.HasFlag(FileAttributes.Directory))
if (isFolder.HasValue)
{
continue;
if (isFolder.Value && entry.Value.IsDirectory)
{
continue;
}
else if (!isFolder.Value && !entry.Value.IsDirectory)
{
continue;
}
}
if (System.IO.Path.GetFileName(entry.Key).Equals(name, StringComparison.OrdinalIgnoreCase))
@ -50,12 +65,12 @@ namespace MediaBrowser.Controller.Events
public bool ContainsFile(string name)
{
return GetFileByName(name) != null;
return GetFileSystemEntryByName(name, false) != null;
}
public bool ContainsFolder(string name)
{
return GetFolderByName(name) != null;
return GetFileSystemEntryByName(name, true) != null;
}
}
@ -71,7 +86,8 @@ namespace MediaBrowser.Controller.Events
public bool Cancel { get; set; }
public FileAttributes FileAttributes { get; set; }
public FileAttributes FileAttributes { get { return FileData.dwFileAttributes; } }
public WIN32_FIND_DATA FileData { get; set; }
public bool IsFolder
{

View File

@ -83,7 +83,7 @@ namespace MediaBrowser.Controller.IO
List<string> paths = affectedPaths;
affectedPaths = new List<string>();
await ProcessPathChanges(paths);
await ProcessPathChanges(paths).ConfigureAwait(false);
}
private async Task ProcessPathChanges(IEnumerable<string> paths)
@ -105,11 +105,11 @@ namespace MediaBrowser.Controller.IO
return folder != null && folder.IsRoot;
}))
{
await Kernel.Instance.ReloadRoot();
await Kernel.Instance.ReloadRoot().ConfigureAwait(false);
}
else
{
await Task.WhenAll(itemsToRefresh.Select(i => Kernel.Instance.ReloadItem(i)));
await Task.WhenAll(itemsToRefresh.Select(i => Kernel.Instance.ReloadItem(i))).ConfigureAwait(false);
}
}

View File

@ -0,0 +1,96 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace MediaBrowser.Controller.IO
{
public static class FileData
{
public const int MAX_PATH = 260;
public const int MAX_ALTERNATE = 14;
public static WIN32_FIND_DATA GetFileData(string fileName)
{
WIN32_FIND_DATA data;
IntPtr handle = FindFirstFile(fileName, out data);
if (handle == IntPtr.Zero)
throw new IOException("FindFirstFile failed");
FindClose(handle);
return data;
}
[DllImport("kernel32")]
private static extern IntPtr FindFirstFile(string fileName, out WIN32_FIND_DATA data);
[DllImport("kernel32")]
private static extern bool FindClose(IntPtr hFindFile);
}
[StructLayout(LayoutKind.Sequential)]
public struct FILETIME
{
public uint dwLowDateTime;
public uint dwHighDateTime;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct WIN32_FIND_DATA
{
public FileAttributes dwFileAttributes;
public FILETIME ftCreationTime;
public FILETIME ftLastAccessTime;
public FILETIME ftLastWriteTime;
public int nFileSizeHigh;
public int nFileSizeLow;
public int dwReserved0;
public int dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = FileData.MAX_PATH)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = FileData.MAX_ALTERNATE)]
public string cAlternate;
public bool IsDirectory
{
get
{
return dwFileAttributes.HasFlag(FileAttributes.Directory);
}
}
public DateTime CreationTime
{
get
{
return ParseFileTime(ftCreationTime);
}
}
public DateTime LastAccessTime
{
get
{
return ParseFileTime(ftLastAccessTime);
}
}
public DateTime LastWriteTime
{
get
{
return ParseFileTime(ftLastWriteTime);
}
}
private DateTime ParseFileTime(FILETIME filetime)
{
long highBits = filetime.dwHighDateTime;
highBits = highBits << 32;
return DateTime.FromFileTime(highBits + (long)filetime.dwLowDateTime);
}
}
}

View File

@ -20,13 +20,13 @@ 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, FileAttributes attributes)
private bool OnPreBeginResolvePath(Folder parent, string path, WIN32_FIND_DATA fileData)
{
PreBeginResolveEventArgs args = new PreBeginResolveEventArgs()
{
Path = path,
Parent = parent,
FileAttributes = attributes,
FileData = fileData,
Cancel = false
};
@ -111,39 +111,41 @@ namespace MediaBrowser.Controller.Library
/// </summary>
public async Task<BaseItem> GetItem(Folder parent, string path)
{
return await GetItemInternal(parent, path, File.GetAttributes(path)).ConfigureAwait(false);
WIN32_FIND_DATA fileData = FileData.GetFileData(path);
return await GetItemInternal(parent, path, fileData).ConfigureAwait(false);
}
/// <summary>
/// Resolves a path into a BaseItem
/// </summary>
private async Task<BaseItem> GetItemInternal(Folder parent, string path, FileAttributes attributes)
private async Task<BaseItem> GetItemInternal(Folder parent, string path, WIN32_FIND_DATA fileData)
{
if (!OnPreBeginResolvePath(parent, path, attributes))
if (!OnPreBeginResolvePath(parent, path, fileData))
{
return null;
}
IEnumerable<KeyValuePair<string, FileAttributes>> fileSystemChildren;
IEnumerable<KeyValuePair<string, WIN32_FIND_DATA>> fileSystemChildren;
// Gather child folder and files
if (attributes.HasFlag(FileAttributes.Directory))
if (fileData.IsDirectory)
{
fileSystemChildren = Directory.GetFileSystemEntries(path, "*", SearchOption.TopDirectoryOnly).Select(f => new KeyValuePair<string, FileAttributes>(f, File.GetAttributes(f)));
fileSystemChildren = Directory.GetFileSystemEntries(path, "*", SearchOption.TopDirectoryOnly).Select(f => new KeyValuePair<string, WIN32_FIND_DATA>(f, FileData.GetFileData(f)));
bool isVirtualFolder = parent != null && parent.IsRoot;
fileSystemChildren = FilterChildFileSystemEntries(fileSystemChildren, isVirtualFolder);
}
else
{
fileSystemChildren = new KeyValuePair<string, FileAttributes>[] { };
fileSystemChildren = new KeyValuePair<string, WIN32_FIND_DATA>[] { };
}
ItemResolveEventArgs args = new ItemResolveEventArgs()
{
Path = path,
FileAttributes = attributes,
FileSystemChildren = fileSystemChildren,
FileData = fileData,
Parent = parent,
Cancel = false
};
@ -175,9 +177,9 @@ namespace MediaBrowser.Controller.Library
/// <summary>
/// Finds child BaseItems for a given Folder
/// </summary>
private async Task AttachChildren(Folder folder, IEnumerable<KeyValuePair<string, FileAttributes>> fileSystemChildren)
private async Task AttachChildren(Folder folder, IEnumerable<KeyValuePair<string, WIN32_FIND_DATA>> fileSystemChildren)
{
KeyValuePair<string, FileAttributes>[] fileSystemChildrenArray = fileSystemChildren.ToArray();
KeyValuePair<string, WIN32_FIND_DATA>[] fileSystemChildrenArray = fileSystemChildren.ToArray();
int count = fileSystemChildrenArray.Length;
@ -203,15 +205,15 @@ namespace MediaBrowser.Controller.Library
/// <summary>
/// Transforms shortcuts into their actual paths
/// </summary>
private List<KeyValuePair<string, FileAttributes>> FilterChildFileSystemEntries(IEnumerable<KeyValuePair<string, FileAttributes>> fileSystemChildren, bool flattenShortcuts)
private List<KeyValuePair<string, WIN32_FIND_DATA>> FilterChildFileSystemEntries(IEnumerable<KeyValuePair<string, WIN32_FIND_DATA>> fileSystemChildren, bool flattenShortcuts)
{
List<KeyValuePair<string, FileAttributes>> returnFiles = new List<KeyValuePair<string, FileAttributes>>();
List<KeyValuePair<string, WIN32_FIND_DATA>> returnFiles = new List<KeyValuePair<string, WIN32_FIND_DATA>>();
// Loop through each file
foreach (KeyValuePair<string, FileAttributes> file in fileSystemChildren)
foreach (KeyValuePair<string, WIN32_FIND_DATA> file in fileSystemChildren)
{
// Folders
if (file.Value.HasFlag(FileAttributes.Directory))
if (file.Value.IsDirectory)
{
returnFiles.Add(file);
}
@ -220,28 +222,28 @@ namespace MediaBrowser.Controller.Library
else if (Shortcut.IsShortcut(file.Key))
{
string newPath = Shortcut.ResolveShortcut(file.Key);
FileAttributes newPathAttributes = File.GetAttributes(newPath);
WIN32_FIND_DATA newPathData = FileData.GetFileData(newPath);
// Find out if the shortcut is pointing to a directory or file
if (newPathAttributes.HasFlag(FileAttributes.Directory))
if (newPathData.IsDirectory)
{
// If we're flattening then get the shortcut's children
if (flattenShortcuts)
{
IEnumerable<KeyValuePair<string, FileAttributes>> newChildren = Directory.GetFileSystemEntries(newPath, "*", SearchOption.TopDirectoryOnly).Select(f => new KeyValuePair<string, FileAttributes>(f, File.GetAttributes(f)));
IEnumerable<KeyValuePair<string, WIN32_FIND_DATA>> newChildren = Directory.GetFileSystemEntries(newPath, "*", SearchOption.TopDirectoryOnly).Select(f => new KeyValuePair<string, WIN32_FIND_DATA>(f, FileData.GetFileData(f)));
returnFiles.AddRange(FilterChildFileSystemEntries(newChildren, false));
}
else
{
returnFiles.Add(new KeyValuePair<string, FileAttributes>(newPath, newPathAttributes));
returnFiles.Add(new KeyValuePair<string, WIN32_FIND_DATA>(newPath, newPathData));
}
}
else
{
returnFiles.Add(new KeyValuePair<string, FileAttributes>(newPath, newPathAttributes));
returnFiles.Add(new KeyValuePair<string, WIN32_FIND_DATA>(newPath, newPathData));
}
}
else
@ -335,8 +337,8 @@ namespace MediaBrowser.Controller.Library
ItemResolveEventArgs args = new ItemResolveEventArgs();
args.Path = path;
args.FileAttributes = File.GetAttributes(path);
args.FileSystemChildren = Directory.GetFileSystemEntries(path, "*", SearchOption.TopDirectoryOnly).Select(f => new KeyValuePair<string, FileAttributes>(f, File.GetAttributes(f)));
args.FileData = FileData.GetFileData(path);
args.FileSystemChildren = Directory.GetFileSystemEntries(path, "*", SearchOption.TopDirectoryOnly).Select(f => new KeyValuePair<string, WIN32_FIND_DATA>(f, FileData.GetFileData(f)));
await Kernel.Instance.ExecuteMetadataProviders(item, args).ConfigureAwait(false);

View File

@ -55,6 +55,7 @@
<Compile Include="FFMpeg\FFProbe.cs" />
<Compile Include="FFMpeg\FFProbeResult.cs" />
<Compile Include="IO\DirectoryWatchers.cs" />
<Compile Include="IO\FileData.cs" />
<Compile Include="IO\Shortcut.cs" />
<Compile Include="Library\ItemController.cs" />
<Compile Include="Kernel.cs" />

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
@ -6,7 +7,6 @@ using System.Threading.Tasks;
using MediaBrowser.Controller.Events;
using MediaBrowser.Controller.FFMpeg;
using MediaBrowser.Model.Entities;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Providers
{

View File

@ -21,7 +21,7 @@ namespace MediaBrowser.Controller.Providers
public async override Task Fetch(BaseEntity item, ItemResolveEventArgs args)
{
var metadataFile = args.GetFileByName("folder.xml");
var metadataFile = args.GetFileSystemEntryByName("folder.xml", false);
if (metadataFile.HasValue)
{

View File

@ -6,6 +6,7 @@ using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Controller.Events;
using MediaBrowser.Model.Entities;
using MediaBrowser.Controller.IO;
namespace MediaBrowser.Controller.Providers
{
@ -47,9 +48,9 @@ namespace MediaBrowser.Controller.Providers
/// </summary>
private void PopulateImages(BaseEntity item, ItemResolveEventArgs args)
{
foreach (KeyValuePair<string, FileAttributes> file in args.FileSystemChildren)
foreach (KeyValuePair<string, WIN32_FIND_DATA> file in args.FileSystemChildren)
{
if (file.Value.HasFlag(FileAttributes.Directory))
if (file.Value.IsDirectory)
{
continue;
}
@ -80,9 +81,9 @@ namespace MediaBrowser.Controller.Providers
{
List<string> backdropFiles = new List<string>();
foreach (KeyValuePair<string, FileAttributes> file in args.FileSystemChildren)
foreach (KeyValuePair<string, WIN32_FIND_DATA> file in args.FileSystemChildren)
{
if (file.Value.HasFlag(FileAttributes.Directory))
if (file.Value.IsDirectory)
{
continue;
}

View File

@ -24,7 +24,7 @@ namespace MediaBrowser.Controller.Providers
{
BaseItem baseItem = item as BaseItem;
var trailerPath = args.GetFolderByName("trailers");
var trailerPath = args.GetFileSystemEntryByName("trailers", true);
if (trailerPath.HasValue)
{

View File

@ -66,12 +66,7 @@ namespace MediaBrowser.Controller.Providers
return false;
}
if (string.IsNullOrEmpty(video.FrameRate))
{
return false;
}
if (video.Height == 0 || video.Width == 0 || video.BitRate == 0)
if (video.FrameRate == 0 || video.Height == 0 || video.Width == 0 || video.BitRate == 0)
{
return false;
}

View File

@ -3,6 +3,8 @@ using System.IO;
using System.Threading.Tasks;
using MediaBrowser.Controller.Events;
using MediaBrowser.Model.Entities;
using MediaBrowser.Controller.IO;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Resolvers
{
@ -55,7 +57,7 @@ namespace MediaBrowser.Controller.Resolvers
EnsureName(item);
// Make sure DateCreated and DateModified have values
EnsureDates(item);
EnsureDates(item, args);
}
return item;
@ -74,18 +76,33 @@ namespace MediaBrowser.Controller.Resolvers
/// <summary>
/// Ensures DateCreated and DateModified have values
/// </summary>
private void EnsureDates(T item)
private void EnsureDates(T item, ItemResolveEventArgs args)
{
// If the subclass didn't supply dates, add them here
if (item.DateCreated == DateTime.MinValue)
if (!Path.IsPathRooted(item.Path))
{
item.DateCreated = Path.IsPathRooted(item.Path) ? File.GetCreationTime(item.Path) : DateTime.Now;
return;
}
if (item.DateModified == DateTime.MinValue)
WIN32_FIND_DATA fileData = args.FileData;
// See if a different path came out of the resolver than what went in
if (!args.Path.Equals(item.Path, StringComparison.OrdinalIgnoreCase))
{
item.DateModified = Path.IsPathRooted(item.Path) ? File.GetLastWriteTime(item.Path) : DateTime.Now;
KeyValuePair<string, WIN32_FIND_DATA>? childData = args.GetFileSystemEntry(item.Path, null);
if (childData != null)
{
fileData = childData.Value.Value;
}
else
{
fileData = FileData.GetFileData(item.Path);
}
}
item.DateCreated = fileData.CreationTime;
item.DateModified = fileData.LastWriteTime;
}
}

View File

@ -2,6 +2,7 @@
using System.ComponentModel.Composition;
using System.IO;
using MediaBrowser.Controller.Events;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Controller.Resolvers
@ -50,9 +51,9 @@ namespace MediaBrowser.Controller.Resolvers
}
// Also check the subfolders for bluray or dvd
foreach (KeyValuePair<string, FileAttributes> folder in args.FileSystemChildren)
foreach (KeyValuePair<string, WIN32_FIND_DATA> folder in args.FileSystemChildren)
{
if (!folder.Value.HasFlag(FileAttributes.Directory))
if (!folder.Value.IsDirectory)
{
continue;
}

View File

@ -33,17 +33,6 @@ namespace MediaBrowser.Controller.Xml
}
}
}
// If dates weren't supplied in metadata, use values from the xml file
if (item.DateCreated == DateTime.MinValue)
{
item.DateCreated = File.GetCreationTime(metadataFile);
}
if (item.DateModified == DateTime.MinValue)
{
item.DateModified = File.GetLastWriteTime(metadataFile);
}
}
/// <summary>
@ -414,7 +403,7 @@ namespace MediaBrowser.Controller.Xml
break;
case "FrameRate":
item.FrameRate = reader.ReadElementContentAsString();
item.FrameRate = reader.ReadFloatSafe();
break;
case "ScanType":

View File

@ -12,7 +12,7 @@ namespace MediaBrowser.Model.Entities
public int Height { get; set; }
public int Width { get; set; }
public string ScanType { get; set; }
public string FrameRate { get; set; }
public float FrameRate { get; set; }
public int BitRate { get; set; }
public string Codec { get; set; }
}

View File

@ -23,7 +23,7 @@ namespace MediaBrowser.Movies.Providers
public async override Task Fetch(BaseEntity item, ItemResolveEventArgs args)
{
var metadataFile = args.GetFileByName("movie.xml");
var metadataFile = args.GetFileSystemEntryByName("movie.xml", false);
if (metadataFile.HasValue)
{

View File

@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Events;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Movies.Entities;
@ -24,7 +25,7 @@ namespace MediaBrowser.Movies.Resolvers
return null;
}
var metadataFile = args.GetFileByName("movie.xml");
var metadataFile = args.GetFileSystemEntryByName("movie.xml", false);
if (metadataFile.HasValue || Path.GetFileName(args.Path).IndexOf("[tmdbid=", StringComparison.OrdinalIgnoreCase) != -1)
{
@ -57,8 +58,8 @@ namespace MediaBrowser.Movies.Resolvers
ItemResolveEventArgs childArgs = new ItemResolveEventArgs()
{
Path = child.Key,
FileAttributes = child.Value,
FileSystemChildren = new KeyValuePair<string, FileAttributes>[] { }
FileData = child.Value,
FileSystemChildren = new KeyValuePair<string, WIN32_FIND_DATA>[] { }
};
var item = base.Resolve(childArgs);
@ -78,7 +79,7 @@ namespace MediaBrowser.Movies.Resolvers
private void PopulateBonusFeatures(Movie item, ItemResolveEventArgs args)
{
var trailerPath = args.GetFolderByName("specials");
var trailerPath = args.GetFileSystemEntryByName("specials", true);
if (trailerPath.HasValue)
{

View File

@ -23,7 +23,7 @@ namespace MediaBrowser.TV.Providers
public async override Task Fetch(BaseEntity item, ItemResolveEventArgs args)
{
var metadataFile = args.GetFileByName("series.xml");
var metadataFile = args.GetFileSystemEntryByName("series.xml", false);
if (metadataFile.HasValue)
{

View File

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

View File

@ -1,7 +1,8 @@
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using MediaBrowser.Controller.IO;
namespace MediaBrowser.TV
{
@ -52,11 +53,11 @@ namespace MediaBrowser.TV
return seasonPathExpressions.Any(r => r.IsMatch(path));
}
public static bool IsSeriesFolder(string path, IEnumerable<KeyValuePair<string, FileAttributes>> fileSystemChildren)
public static bool IsSeriesFolder(string path, IEnumerable<KeyValuePair<string, WIN32_FIND_DATA>> fileSystemChildren)
{
foreach (var child in fileSystemChildren)
{
if (child.Value.HasFlag(FileAttributes.Directory))
if (child.Value.IsDirectory)
{
if (IsSeasonFolder(child.Key))
{