Added initial implementation of the metadata provider network, along with the first few providers
This commit is contained in:
parent
803ce0968e
commit
d794eecec4
|
@ -12,6 +12,7 @@ using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Common.Plugins;
|
using MediaBrowser.Common.Plugins;
|
||||||
using MediaBrowser.Common.Serialization;
|
using MediaBrowser.Common.Serialization;
|
||||||
using MediaBrowser.Model.Progress;
|
using MediaBrowser.Model.Progress;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MediaBrowser.Common.Kernel
|
namespace MediaBrowser.Common.Kernel
|
||||||
{
|
{
|
||||||
|
@ -51,7 +52,9 @@ namespace MediaBrowser.Common.Kernel
|
||||||
ApplicationPaths = new TApplicationPathsType();
|
ApplicationPaths = new TApplicationPathsType();
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Init(IProgress<TaskProgress> progress)
|
public virtual Task Init(IProgress<TaskProgress> progress)
|
||||||
|
{
|
||||||
|
return Task.Run(() =>
|
||||||
{
|
{
|
||||||
ReloadLogger();
|
ReloadLogger();
|
||||||
|
|
||||||
|
@ -63,6 +66,7 @@ namespace MediaBrowser.Common.Kernel
|
||||||
|
|
||||||
progress.Report(new TaskProgress() { Description = "Loading Plugins", PercentComplete = 10 });
|
progress.Report(new TaskProgress() { Description = "Loading Plugins", PercentComplete = 10 });
|
||||||
ReloadComposableParts();
|
ReloadComposableParts();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -151,6 +151,50 @@ namespace MediaBrowser.Controller.Configuration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string _CacheDirectory = null;
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the folder path to the cache directory
|
||||||
|
/// </summary>
|
||||||
|
public string CacheDirectory
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_CacheDirectory == null)
|
||||||
|
{
|
||||||
|
_CacheDirectory = Path.Combine(Kernel.Instance.ApplicationPaths.ProgramDataPath, "cache");
|
||||||
|
|
||||||
|
if (!Directory.Exists(_CacheDirectory))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(_CacheDirectory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _CacheDirectory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _FFProbeAudioCacheDirectory = null;
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the folder path to the ffprobe audio cache directory
|
||||||
|
/// </summary>
|
||||||
|
public string FFProbeAudioCacheDirectory
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_FFProbeAudioCacheDirectory == null)
|
||||||
|
{
|
||||||
|
_FFProbeAudioCacheDirectory = Path.Combine(Kernel.Instance.ApplicationPaths.CacheDirectory, "ffprobe-audio");
|
||||||
|
|
||||||
|
if (!Directory.Exists(_FFProbeAudioCacheDirectory))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(_FFProbeAudioCacheDirectory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _FFProbeAudioCacheDirectory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private string _FFMpegDirectory = null;
|
private string _FFMpegDirectory = null;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the folder path to ffmpeg
|
/// Gets the folder path to ffmpeg
|
||||||
|
@ -221,7 +265,7 @@ namespace MediaBrowser.Controller.Configuration
|
||||||
|
|
||||||
_FFProbePath = Path.Combine(FFMpegDirectory, filename);
|
_FFProbePath = Path.Combine(FFMpegDirectory, filename);
|
||||||
|
|
||||||
// Always re-extract the first time to handle new versions
|
/*// Always re-extract the first time to handle new versions
|
||||||
if (File.Exists(_FFProbePath))
|
if (File.Exists(_FFProbePath))
|
||||||
{
|
{
|
||||||
File.Delete(_FFProbePath);
|
File.Delete(_FFProbePath);
|
||||||
|
@ -234,7 +278,7 @@ namespace MediaBrowser.Controller.Configuration
|
||||||
{
|
{
|
||||||
stream.CopyTo(fileStream);
|
stream.CopyTo(fileStream);
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
return _FFProbePath;
|
return _FFProbePath;
|
||||||
|
|
|
@ -1,13 +1,41 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Common.Logging;
|
using MediaBrowser.Common.Logging;
|
||||||
using MediaBrowser.Common.Serialization;
|
using MediaBrowser.Common.Serialization;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.FFMpeg
|
namespace MediaBrowser.Controller.FFMpeg
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Runs FFProbe against a media file and returns metadata.
|
||||||
|
/// </summary>
|
||||||
public static class FFProbe
|
public static class FFProbe
|
||||||
{
|
{
|
||||||
public static FFProbeResult Run(string path)
|
public async static Task<FFProbeResult> Run(Audio item, string outputCachePath)
|
||||||
|
{
|
||||||
|
// Use try catch to avoid having to use File.Exists
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (FileStream stream = File.OpenRead(outputCachePath))
|
||||||
|
{
|
||||||
|
return JsonSerializer.DeserializeFromStream<FFProbeResult>(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
await Run(item.Path, outputCachePath);
|
||||||
|
|
||||||
|
using (FileStream stream = File.OpenRead(outputCachePath))
|
||||||
|
{
|
||||||
|
return JsonSerializer.DeserializeFromStream<FFProbeResult>(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async static Task Run(string input, string output)
|
||||||
{
|
{
|
||||||
ProcessStartInfo startInfo = new ProcessStartInfo();
|
ProcessStartInfo startInfo = new ProcessStartInfo();
|
||||||
|
|
||||||
|
@ -21,13 +49,15 @@ namespace MediaBrowser.Controller.FFMpeg
|
||||||
|
|
||||||
startInfo.FileName = Kernel.Instance.ApplicationPaths.FFProbePath;
|
startInfo.FileName = Kernel.Instance.ApplicationPaths.FFProbePath;
|
||||||
startInfo.WorkingDirectory = Kernel.Instance.ApplicationPaths.FFMpegDirectory;
|
startInfo.WorkingDirectory = Kernel.Instance.ApplicationPaths.FFMpegDirectory;
|
||||||
startInfo.Arguments = string.Format("\"{0}\" -v quiet -print_format json -show_streams -show_format", path);
|
startInfo.Arguments = string.Format("\"{0}\" -v quiet -print_format json -show_streams -show_format", input);
|
||||||
|
|
||||||
Logger.LogInfo(startInfo.FileName + " " + startInfo.Arguments);
|
//Logger.LogInfo(startInfo.FileName + " " + startInfo.Arguments);
|
||||||
|
|
||||||
Process process = new Process();
|
Process process = new Process();
|
||||||
process.StartInfo = startInfo;
|
process.StartInfo = startInfo;
|
||||||
|
|
||||||
|
FileStream stream = new FileStream(output, FileMode.Create);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
process.Start();
|
process.Start();
|
||||||
|
@ -36,18 +66,23 @@ namespace MediaBrowser.Controller.FFMpeg
|
||||||
// If we ever decide to disable the ffmpeg log then you must uncomment the below line.
|
// If we ever decide to disable the ffmpeg log then you must uncomment the below line.
|
||||||
process.BeginErrorReadLine();
|
process.BeginErrorReadLine();
|
||||||
|
|
||||||
FFProbeResult result = JsonSerializer.DeserializeFromStream<FFProbeResult>(process.StandardOutput.BaseStream);
|
await process.StandardOutput.BaseStream.CopyToAsync(stream);
|
||||||
|
|
||||||
process.WaitForExit();
|
process.WaitForExit();
|
||||||
|
|
||||||
Logger.LogInfo("FFMpeg exited with code " + process.ExitCode);
|
stream.Dispose();
|
||||||
|
|
||||||
return result;
|
if (process.ExitCode != 0)
|
||||||
|
{
|
||||||
|
Logger.LogInfo("FFProbe exited with code {0} for {1}", process.ExitCode, input);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.LogException(ex);
|
Logger.LogException(ex);
|
||||||
|
|
||||||
|
stream.Dispose();
|
||||||
|
|
||||||
// Hate having to do this
|
// Hate having to do this
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -56,8 +91,7 @@ namespace MediaBrowser.Controller.FFMpeg
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
File.Delete(output);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,6 +13,11 @@ namespace MediaBrowser.Controller.FFMpeg
|
||||||
public MediaFormat format { get; set; }
|
public MediaFormat format { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a stream within the output
|
||||||
|
/// A number of properties are commented out to improve deserialization performance
|
||||||
|
/// Enable them as needed.
|
||||||
|
/// </summary>
|
||||||
public class MediaStream
|
public class MediaStream
|
||||||
{
|
{
|
||||||
public int index { get; set; }
|
public int index { get; set; }
|
||||||
|
@ -20,28 +25,28 @@ namespace MediaBrowser.Controller.FFMpeg
|
||||||
public string codec_name { get; set; }
|
public string codec_name { get; set; }
|
||||||
public string codec_long_name { get; set; }
|
public string codec_long_name { get; set; }
|
||||||
public string codec_type { get; set; }
|
public string codec_type { get; set; }
|
||||||
public string codec_time_base { get; set; }
|
//public string codec_time_base { get; set; }
|
||||||
public string codec_tag { get; set; }
|
//public string codec_tag { get; set; }
|
||||||
public string codec_tag_string { get; set; }
|
//public string codec_tag_string { get; set; }
|
||||||
public string sample_fmt { get; set; }
|
//public string sample_fmt { get; set; }
|
||||||
public string sample_rate { get; set; }
|
public string sample_rate { get; set; }
|
||||||
public int channels { get; set; }
|
public int channels { get; set; }
|
||||||
public int bits_per_sample { get; set; }
|
//public int bits_per_sample { get; set; }
|
||||||
public string r_frame_rate { get; set; }
|
//public string r_frame_rate { get; set; }
|
||||||
public string avg_frame_rate { get; set; }
|
//public string avg_frame_rate { get; set; }
|
||||||
public string time_base { get; set; }
|
//public string time_base { get; set; }
|
||||||
public string start_time { get; set; }
|
//public string start_time { get; set; }
|
||||||
public string duration { get; set; }
|
public string duration { get; set; }
|
||||||
public string bit_rate { get; set; }
|
public string bit_rate { get; set; }
|
||||||
|
|
||||||
public int width { get; set; }
|
public int width { get; set; }
|
||||||
public int height { get; set; }
|
public int height { get; set; }
|
||||||
public int has_b_frames { get; set; }
|
//public int has_b_frames { get; set; }
|
||||||
public string sample_aspect_ratio { get; set; }
|
//public string sample_aspect_ratio { get; set; }
|
||||||
public string display_aspect_ratio { get; set; }
|
//public string display_aspect_ratio { get; set; }
|
||||||
public string pix_fmt { get; set; }
|
//public string pix_fmt { get; set; }
|
||||||
public int level { get; set; }
|
//public int level { get; set; }
|
||||||
public MediaTags tags { get; set; }
|
public Dictionary<string,string> tags { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MediaFormat
|
public class MediaFormat
|
||||||
|
@ -54,23 +59,6 @@ namespace MediaBrowser.Controller.FFMpeg
|
||||||
public string duration { get; set; }
|
public string duration { get; set; }
|
||||||
public string size { get; set; }
|
public string size { get; set; }
|
||||||
public string bit_rate { get; set; }
|
public string bit_rate { get; set; }
|
||||||
public MediaTags tags { get; set; }
|
public Dictionary<string, string> tags { get; set; }
|
||||||
}
|
|
||||||
|
|
||||||
public class MediaTags
|
|
||||||
{
|
|
||||||
public string title { get; set; }
|
|
||||||
public string comment { get; set; }
|
|
||||||
public string artist { get; set; }
|
|
||||||
public string album { get; set; }
|
|
||||||
public string album_artist { get; set; }
|
|
||||||
public string composer { get; set; }
|
|
||||||
public string copyright { get; set; }
|
|
||||||
public string publisher { get; set; }
|
|
||||||
public string track { get; set; }
|
|
||||||
public string disc { get; set; }
|
|
||||||
public string genre { get; set; }
|
|
||||||
public string date { get; set; }
|
|
||||||
public string language { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,7 +88,7 @@ namespace MediaBrowser.Controller.IO
|
||||||
|
|
||||||
private void ProcessPathChanges(IEnumerable<string> paths)
|
private void ProcessPathChanges(IEnumerable<string> paths)
|
||||||
{
|
{
|
||||||
List<BaseItem> itemsToRefresh = new List<BaseItem>();
|
/*List<BaseItem> itemsToRefresh = new List<BaseItem>();
|
||||||
|
|
||||||
foreach (BaseItem item in paths.Select(p => GetAffectedBaseItem(p)))
|
foreach (BaseItem item in paths.Select(p => GetAffectedBaseItem(p)))
|
||||||
{
|
{
|
||||||
|
@ -113,7 +113,7 @@ namespace MediaBrowser.Controller.IO
|
||||||
{
|
{
|
||||||
Kernel.Instance.ReloadItem(itemsToRefresh[i]);
|
Kernel.Instance.ReloadItem(itemsToRefresh[i]);
|
||||||
});
|
});
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
private BaseItem GetAffectedBaseItem(string path)
|
private BaseItem GetAffectedBaseItem(string path)
|
||||||
|
|
|
@ -5,11 +5,13 @@ using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Common.Kernel;
|
using MediaBrowser.Common.Kernel;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Events;
|
using MediaBrowser.Controller.Events;
|
||||||
using MediaBrowser.Controller.IO;
|
using MediaBrowser.Controller.IO;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Controller.Resolvers;
|
using MediaBrowser.Controller.Resolvers;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Progress;
|
using MediaBrowser.Model.Progress;
|
||||||
|
@ -35,6 +37,12 @@ namespace MediaBrowser.Controller
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the list of currently registered metadata prvoiders
|
||||||
|
/// </summary>
|
||||||
|
[ImportMany(typeof(BaseMetadataProvider))]
|
||||||
|
public IEnumerable<BaseMetadataProvider> MetadataProviders { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the list of currently registered entity resolvers
|
/// Gets the list of currently registered entity resolvers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -56,35 +64,63 @@ namespace MediaBrowser.Controller
|
||||||
ItemController.BeginResolvePath += ItemController_BeginResolvePath;
|
ItemController.BeginResolvePath += ItemController_BeginResolvePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Init(IProgress<TaskProgress> progress)
|
public async override Task Init(IProgress<TaskProgress> progress)
|
||||||
{
|
{
|
||||||
base.Init(progress);
|
await base.Init(progress);
|
||||||
|
|
||||||
progress.Report(new TaskProgress() { Description = "Loading Users", PercentComplete = 15 });
|
progress.Report(new TaskProgress() { Description = "Loading Users", PercentComplete = 15 });
|
||||||
ReloadUsers();
|
ReloadUsers();
|
||||||
|
|
||||||
progress.Report(new TaskProgress() { Description = "Loading Media Library", PercentComplete = 20 });
|
progress.Report(new TaskProgress() { Description = "Loading Media Library", PercentComplete = 20 });
|
||||||
ReloadRoot();
|
await ReloadRoot();
|
||||||
|
|
||||||
progress.Report(new TaskProgress() { Description = "Loading Complete", PercentComplete = 100 });
|
progress.Report(new TaskProgress() { Description = "Loading Complete", PercentComplete = 100 });
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnComposablePartsLoaded()
|
protected override void OnComposablePartsLoaded()
|
||||||
{
|
{
|
||||||
List<IBaseItemResolver> resolvers = EntityResolvers.ToList();
|
AddCoreResolvers();
|
||||||
|
AddCoreProviders();
|
||||||
// Add the internal resolvers
|
|
||||||
resolvers.Add(new VideoResolver());
|
|
||||||
resolvers.Add(new AudioResolver());
|
|
||||||
resolvers.Add(new VirtualFolderResolver());
|
|
||||||
resolvers.Add(new FolderResolver());
|
|
||||||
|
|
||||||
EntityResolvers = resolvers;
|
|
||||||
|
|
||||||
// The base class will start up all the plugins
|
// The base class will start up all the plugins
|
||||||
base.OnComposablePartsLoaded();
|
base.OnComposablePartsLoaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AddCoreResolvers()
|
||||||
|
{
|
||||||
|
List<IBaseItemResolver> list = EntityResolvers.ToList();
|
||||||
|
|
||||||
|
// Add the core resolvers
|
||||||
|
list.AddRange(new IBaseItemResolver[]{
|
||||||
|
new AudioResolver(),
|
||||||
|
new VideoResolver(),
|
||||||
|
new VirtualFolderResolver(),
|
||||||
|
new FolderResolver()
|
||||||
|
});
|
||||||
|
|
||||||
|
EntityResolvers = list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddCoreProviders()
|
||||||
|
{
|
||||||
|
List<BaseMetadataProvider> list = MetadataProviders.ToList();
|
||||||
|
|
||||||
|
// Add the core resolvers
|
||||||
|
list.InsertRange(0, new BaseMetadataProvider[]{
|
||||||
|
new ImageFromMediaLocationProvider(),
|
||||||
|
new LocalTrailerProvider(),
|
||||||
|
new AudioInfoProvider(),
|
||||||
|
new FolderProviderFromXml()
|
||||||
|
});
|
||||||
|
|
||||||
|
MetadataProviders = list;
|
||||||
|
|
||||||
|
Parallel.ForEach(MetadataProviders, provider =>
|
||||||
|
{
|
||||||
|
provider.Init();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fires when a path is about to be resolved, but before child folders and files
|
/// Fires when a path is about to be resolved, but before child folders and files
|
||||||
/// have been collected from the file system.
|
/// have been collected from the file system.
|
||||||
|
@ -129,7 +165,7 @@ namespace MediaBrowser.Controller
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reloads the root media folder
|
/// Reloads the root media folder
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void ReloadRoot()
|
public async Task ReloadRoot()
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(MediaRootFolderPath))
|
if (!Directory.Exists(MediaRootFolderPath))
|
||||||
{
|
{
|
||||||
|
@ -138,7 +174,7 @@ namespace MediaBrowser.Controller
|
||||||
|
|
||||||
DirectoryWatchers.Stop();
|
DirectoryWatchers.Stop();
|
||||||
|
|
||||||
RootFolder = ItemController.GetItem(MediaRootFolderPath) as Folder;
|
RootFolder = await ItemController.GetItem(null, MediaRootFolderPath) as Folder;
|
||||||
|
|
||||||
DirectoryWatchers.Start();
|
DirectoryWatchers.Start();
|
||||||
}
|
}
|
||||||
|
@ -152,23 +188,23 @@ namespace MediaBrowser.Controller
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReloadItem(BaseItem item)
|
public async Task ReloadItem(BaseItem item)
|
||||||
{
|
{
|
||||||
Folder folder = item as Folder;
|
Folder folder = item as Folder;
|
||||||
|
|
||||||
if (folder != null && folder.IsRoot)
|
if (folder != null && folder.IsRoot)
|
||||||
{
|
{
|
||||||
ReloadRoot();
|
await ReloadRoot();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(item.Path) && !File.Exists(item.Path))
|
if (!Directory.Exists(item.Path) && !File.Exists(item.Path))
|
||||||
{
|
{
|
||||||
ReloadItem(item.Parent);
|
await ReloadItem(item.Parent);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseItem newItem = ItemController.GetItem(item.Parent, item.Path);
|
BaseItem newItem = await ItemController.GetItem(item.Parent, item.Path);
|
||||||
|
|
||||||
List<BaseItem> children = item.Parent.Children.ToList();
|
List<BaseItem> children = item.Parent.Children.ToList();
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,6 @@ using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Common.Configuration;
|
|
||||||
using MediaBrowser.Common.Events;
|
|
||||||
using MediaBrowser.Controller.Events;
|
using MediaBrowser.Controller.Events;
|
||||||
using MediaBrowser.Controller.IO;
|
using MediaBrowser.Controller.IO;
|
||||||
using MediaBrowser.Controller.Resolvers;
|
using MediaBrowser.Controller.Resolvers;
|
||||||
|
@ -58,67 +56,12 @@ namespace MediaBrowser.Controller.Library
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region BaseItem Events
|
private async Task<BaseItem> ResolveItem(ItemResolveEventArgs args)
|
||||||
/// <summary>
|
|
||||||
/// Called when an item is being created.
|
|
||||||
/// This should be used to fill item values, such as metadata
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<GenericItemEventArgs<BaseItem>> BaseItemCreating;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called when an item has been created.
|
|
||||||
/// This should be used to process or modify item values.
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<GenericItemEventArgs<BaseItem>> BaseItemCreated;
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called when an item has been created
|
|
||||||
/// </summary>
|
|
||||||
private void OnBaseItemCreated(BaseItem item, Folder parent)
|
|
||||||
{
|
|
||||||
GenericItemEventArgs<BaseItem> args = new GenericItemEventArgs<BaseItem> { Item = item };
|
|
||||||
|
|
||||||
if (BaseItemCreating != null)
|
|
||||||
{
|
|
||||||
BaseItemCreating(this, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BaseItemCreated != null)
|
|
||||||
{
|
|
||||||
BaseItemCreated(this, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FireCreateEventsRecursive(Folder folder, Folder parent)
|
|
||||||
{
|
|
||||||
OnBaseItemCreated(folder, parent);
|
|
||||||
|
|
||||||
int count = folder.Children.Length;
|
|
||||||
|
|
||||||
Parallel.For(0, count, i =>
|
|
||||||
{
|
|
||||||
BaseItem item = folder.Children[i];
|
|
||||||
|
|
||||||
Folder childFolder = item as Folder;
|
|
||||||
|
|
||||||
if (childFolder != null)
|
|
||||||
{
|
|
||||||
FireCreateEventsRecursive(childFolder, folder);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OnBaseItemCreated(item, folder);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private BaseItem ResolveItem(ItemResolveEventArgs args)
|
|
||||||
{
|
{
|
||||||
// If that didn't pan out, try the slow ones
|
// If that didn't pan out, try the slow ones
|
||||||
foreach (IBaseItemResolver resolver in Kernel.Instance.EntityResolvers)
|
foreach (IBaseItemResolver resolver in Kernel.Instance.EntityResolvers)
|
||||||
{
|
{
|
||||||
var item = resolver.ResolvePath(args);
|
var item = await resolver.ResolvePath(args);
|
||||||
|
|
||||||
if (item != null)
|
if (item != null)
|
||||||
{
|
{
|
||||||
|
@ -132,39 +75,15 @@ namespace MediaBrowser.Controller.Library
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resolves a path into a BaseItem
|
/// Resolves a path into a BaseItem
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public BaseItem GetItem(string path)
|
public async Task<BaseItem> GetItem(Folder parent, string path)
|
||||||
{
|
{
|
||||||
return GetItem(null, path);
|
return await GetItemInternal(parent, path, File.GetAttributes(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resolves a path into a BaseItem
|
/// Resolves a path into a BaseItem
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public BaseItem GetItem(Folder parent, string path)
|
private async Task<BaseItem> GetItemInternal(Folder parent, string path, FileAttributes attributes)
|
||||||
{
|
|
||||||
BaseItem item = GetItemInternal(parent, path, File.GetAttributes(path));
|
|
||||||
|
|
||||||
if (item != null)
|
|
||||||
{
|
|
||||||
var folder = item as Folder;
|
|
||||||
|
|
||||||
if (folder != null)
|
|
||||||
{
|
|
||||||
FireCreateEventsRecursive(folder, parent);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OnBaseItemCreated(item, parent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Resolves a path into a BaseItem
|
|
||||||
/// </summary>
|
|
||||||
private BaseItem GetItemInternal(Folder parent, string path, FileAttributes attributes)
|
|
||||||
{
|
{
|
||||||
if (!OnPreBeginResolvePath(parent, path, attributes))
|
if (!OnPreBeginResolvePath(parent, path, attributes))
|
||||||
{
|
{
|
||||||
|
@ -201,14 +120,14 @@ namespace MediaBrowser.Controller.Library
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseItem item = ResolveItem(args);
|
BaseItem item = await ResolveItem(args);
|
||||||
|
|
||||||
var folder = item as Folder;
|
var folder = item as Folder;
|
||||||
|
|
||||||
if (folder != null)
|
if (folder != null)
|
||||||
{
|
{
|
||||||
// If it's a folder look for child entities
|
// If it's a folder look for child entities
|
||||||
AttachChildren(folder, fileSystemChildren);
|
await AttachChildren(folder, fileSystemChildren);
|
||||||
}
|
}
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
|
@ -217,30 +136,25 @@ namespace MediaBrowser.Controller.Library
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds child BaseItems for a given Folder
|
/// Finds child BaseItems for a given Folder
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void AttachChildren(Folder folder, IEnumerable<KeyValuePair<string, FileAttributes>> fileSystemChildren)
|
private async Task AttachChildren(Folder folder, IEnumerable<KeyValuePair<string, FileAttributes>> fileSystemChildren)
|
||||||
{
|
{
|
||||||
List<BaseItem> baseItemChildren = new List<BaseItem>();
|
KeyValuePair<string, FileAttributes>[] fileSystemChildrenArray = fileSystemChildren.ToArray();
|
||||||
|
|
||||||
int count = fileSystemChildren.Count();
|
int count = fileSystemChildrenArray.Length;
|
||||||
|
|
||||||
// Resolve the child folder paths into entities
|
Task<BaseItem>[] tasks = new Task<BaseItem>[count];
|
||||||
Parallel.For(0, count, i =>
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
KeyValuePair<string, FileAttributes> child = fileSystemChildren.ElementAt(i);
|
var child = fileSystemChildrenArray[i];
|
||||||
|
|
||||||
BaseItem item = GetItemInternal(folder, child.Key, child.Value);
|
tasks[i] = GetItemInternal(folder, child.Key, child.Value);
|
||||||
|
|
||||||
if (item != null)
|
|
||||||
{
|
|
||||||
lock (baseItemChildren)
|
|
||||||
{
|
|
||||||
baseItemChildren.Add(item);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
BaseItem[] baseItemChildren = await Task<BaseItem>.WhenAll(tasks);
|
||||||
|
|
||||||
// Sort them
|
// Sort them
|
||||||
folder.Children = baseItemChildren.OrderBy(f =>
|
folder.Children = baseItemChildren.Where(i => i != null).OrderBy(f =>
|
||||||
{
|
{
|
||||||
return string.IsNullOrEmpty(f.SortName) ? f.Name : f.SortName;
|
return string.IsNullOrEmpty(f.SortName) ? f.Name : f.SortName;
|
||||||
|
|
||||||
|
@ -363,7 +277,7 @@ namespace MediaBrowser.Controller.Library
|
||||||
/// Creates an IBN item based on a given path
|
/// Creates an IBN item based on a given path
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private T CreateImagesByNameItem<T>(string path, string name)
|
private T CreateImagesByNameItem<T>(string path, string name)
|
||||||
where T : BaseEntity, new ()
|
where T : BaseEntity, new()
|
||||||
{
|
{
|
||||||
T item = new T();
|
T item = new T();
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,11 @@
|
||||||
<Compile Include="Library\ItemController.cs" />
|
<Compile Include="Library\ItemController.cs" />
|
||||||
<Compile Include="Kernel.cs" />
|
<Compile Include="Kernel.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="Providers\BaseMetadataProvider.cs" />
|
||||||
|
<Compile Include="Providers\AudioInfoProvider.cs" />
|
||||||
|
<Compile Include="Providers\FolderProviderFromXml.cs" />
|
||||||
|
<Compile Include="Providers\ImageFromMediaLocationProvider.cs" />
|
||||||
|
<Compile Include="Providers\LocalTrailerProvider.cs" />
|
||||||
<Compile Include="Resolvers\AudioResolver.cs" />
|
<Compile Include="Resolvers\AudioResolver.cs" />
|
||||||
<Compile Include="Resolvers\BaseItemResolver.cs" />
|
<Compile Include="Resolvers\BaseItemResolver.cs" />
|
||||||
<Compile Include="Resolvers\FolderResolver.cs" />
|
<Compile Include="Resolvers\FolderResolver.cs" />
|
||||||
|
|
86
MediaBrowser.Controller/Providers/AudioInfoProvider.cs
Normal file
86
MediaBrowser.Controller/Providers/AudioInfoProvider.cs
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
using System;
|
||||||
|
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 AudioInfoProvider : BaseMetadataProvider
|
||||||
|
{
|
||||||
|
public override bool Supports(BaseItem item)
|
||||||
|
{
|
||||||
|
return item is Audio;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async override Task Fetch(BaseItem item, ItemResolveEventArgs args)
|
||||||
|
{
|
||||||
|
Audio audio = item as Audio;
|
||||||
|
|
||||||
|
string outputDirectory = Path.Combine(Kernel.Instance.ApplicationPaths.FFProbeAudioCacheDirectory, item.Id.ToString().Substring(0, 1));
|
||||||
|
|
||||||
|
string outputPath = Path.Combine(outputDirectory, item.Id + "-" + item.DateModified.Ticks + ".js");
|
||||||
|
|
||||||
|
FFProbeResult data = await FFProbe.Run(audio, outputPath);
|
||||||
|
|
||||||
|
MediaStream stream = data.streams.FirstOrDefault(s => s.codec_type.Equals("audio", StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
audio.Channels = stream.channels;
|
||||||
|
|
||||||
|
string bitrate = null;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(stream.sample_rate))
|
||||||
|
{
|
||||||
|
audio.SampleRate = int.Parse(stream.sample_rate);
|
||||||
|
|
||||||
|
bitrate = stream.bit_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(bitrate))
|
||||||
|
{
|
||||||
|
bitrate = data.format.bit_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(bitrate))
|
||||||
|
{
|
||||||
|
audio.BitRate = int.Parse(bitrate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetOutputCachePath(BaseItem item)
|
||||||
|
{
|
||||||
|
string outputDirectory = Path.Combine(Kernel.Instance.ApplicationPaths.FFProbeAudioCacheDirectory, item.Id.ToString().Substring(0, 1));
|
||||||
|
|
||||||
|
return Path.Combine(outputDirectory, item.Id + "-" + item.DateModified.Ticks + ".js");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Init()
|
||||||
|
{
|
||||||
|
base.Init();
|
||||||
|
|
||||||
|
for (int i = 0; i <= 9; i++)
|
||||||
|
{
|
||||||
|
EnsureDirectory(Path.Combine(Kernel.Instance.ApplicationPaths.FFProbeAudioCacheDirectory, 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"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureDirectory(string path)
|
||||||
|
{
|
||||||
|
if (!Directory.Exists(path))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
MediaBrowser.Controller/Providers/BaseMetadataProvider.cs
Normal file
23
MediaBrowser.Controller/Providers/BaseMetadataProvider.cs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MediaBrowser.Controller.Events;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Controller.Providers
|
||||||
|
{
|
||||||
|
public abstract class BaseMetadataProvider
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// If the provider needs any startup routines, add them here
|
||||||
|
/// </summary>
|
||||||
|
public virtual void Init()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool Supports(BaseItem item)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Task Fetch(BaseItem item, ItemResolveEventArgs args);
|
||||||
|
}
|
||||||
|
}
|
30
MediaBrowser.Controller/Providers/FolderProviderFromXml.cs
Normal file
30
MediaBrowser.Controller/Providers/FolderProviderFromXml.cs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
using System.ComponentModel.Composition;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MediaBrowser.Controller.Events;
|
||||||
|
using MediaBrowser.Controller.Xml;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Controller.Providers
|
||||||
|
{
|
||||||
|
[Export(typeof(BaseMetadataProvider))]
|
||||||
|
public class FolderProviderFromXml : BaseMetadataProvider
|
||||||
|
{
|
||||||
|
public override bool Supports(BaseItem item)
|
||||||
|
{
|
||||||
|
return item is Folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task Fetch(BaseItem item, ItemResolveEventArgs args)
|
||||||
|
{
|
||||||
|
return Task.Run(() =>
|
||||||
|
{
|
||||||
|
var metadataFile = args.GetFileByName("folder.xml");
|
||||||
|
|
||||||
|
if (metadataFile.HasValue)
|
||||||
|
{
|
||||||
|
new FolderXmlParser().Fetch(item as Folder, metadataFile.Value.Key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.Composition;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MediaBrowser.Controller.Events;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Controller.Providers
|
||||||
|
{
|
||||||
|
[Export(typeof(BaseMetadataProvider))]
|
||||||
|
public class ImageFromMediaLocationProvider : BaseMetadataProvider
|
||||||
|
{
|
||||||
|
public override Task Fetch(BaseItem item, ItemResolveEventArgs args)
|
||||||
|
{
|
||||||
|
return Task.Run(() =>
|
||||||
|
{
|
||||||
|
if (args.IsFolder)
|
||||||
|
{
|
||||||
|
PopulateImages(item, args);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fills in image paths based on files win the folder
|
||||||
|
/// </summary>
|
||||||
|
private void PopulateImages(BaseItem item, ItemResolveEventArgs args)
|
||||||
|
{
|
||||||
|
List<string> backdropFiles = new List<string>();
|
||||||
|
|
||||||
|
foreach (KeyValuePair<string, FileAttributes> file in args.FileSystemChildren)
|
||||||
|
{
|
||||||
|
if (file.Value.HasFlag(FileAttributes.Directory))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string filePath = file.Key;
|
||||||
|
|
||||||
|
string ext = Path.GetExtension(filePath);
|
||||||
|
|
||||||
|
// Only support png and jpg files
|
||||||
|
if (!ext.EndsWith("png", StringComparison.OrdinalIgnoreCase) && !ext.EndsWith("jpg", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string name = Path.GetFileNameWithoutExtension(filePath);
|
||||||
|
|
||||||
|
if (name.Equals("folder", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
item.PrimaryImagePath = filePath;
|
||||||
|
}
|
||||||
|
else if (name.StartsWith("backdrop", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
backdropFiles.Add(filePath);
|
||||||
|
}
|
||||||
|
if (name.Equals("logo", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
item.LogoImagePath = filePath;
|
||||||
|
}
|
||||||
|
if (name.Equals("banner", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
item.BannerImagePath = filePath;
|
||||||
|
}
|
||||||
|
if (name.Equals("art", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
item.ArtImagePath = filePath;
|
||||||
|
}
|
||||||
|
if (name.Equals("thumb", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
item.ThumbnailImagePath = filePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backdropFiles.Any())
|
||||||
|
{
|
||||||
|
item.BackdropImagePaths = backdropFiles;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
39
MediaBrowser.Controller/Providers/LocalTrailerProvider.cs
Normal file
39
MediaBrowser.Controller/Providers/LocalTrailerProvider.cs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.Composition;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MediaBrowser.Controller.Events;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Controller.Providers
|
||||||
|
{
|
||||||
|
[Export(typeof(BaseMetadataProvider))]
|
||||||
|
public class LocalTrailerProvider : BaseMetadataProvider
|
||||||
|
{
|
||||||
|
public async override Task Fetch(BaseItem item, ItemResolveEventArgs args)
|
||||||
|
{
|
||||||
|
var trailerPath = args.GetFolderByName("trailers");
|
||||||
|
|
||||||
|
if (trailerPath.HasValue)
|
||||||
|
{
|
||||||
|
string[] allFiles = Directory.GetFileSystemEntries(trailerPath.Value.Key, "*", SearchOption.TopDirectoryOnly);
|
||||||
|
|
||||||
|
List<Video> localTrailers = new List<Video>();
|
||||||
|
|
||||||
|
foreach (string file in allFiles)
|
||||||
|
{
|
||||||
|
BaseItem child = await Kernel.Instance.ItemController.GetItem(null, file);
|
||||||
|
|
||||||
|
Video video = child as Video;
|
||||||
|
|
||||||
|
if (video != null)
|
||||||
|
{
|
||||||
|
localTrailers.Add(video);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
item.LocalTrailers = localTrailers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Controller.Events;
|
using MediaBrowser.Controller.Events;
|
||||||
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Resolvers
|
namespace MediaBrowser.Controller.Resolvers
|
||||||
|
@ -33,19 +33,15 @@ namespace MediaBrowser.Controller.Resolvers
|
||||||
}
|
}
|
||||||
|
|
||||||
item.Id = Kernel.GetMD5(item.Path);
|
item.Id = Kernel.GetMD5(item.Path);
|
||||||
|
|
||||||
PopulateImages(item, args);
|
|
||||||
PopulateLocalTrailers(item, args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BaseItem ResolvePath(ItemResolveEventArgs args)
|
public async Task<BaseItem> ResolvePath(ItemResolveEventArgs args)
|
||||||
{
|
{
|
||||||
T item = Resolve(args);
|
T item = Resolve(args);
|
||||||
|
|
||||||
if (item != null)
|
if (item != null)
|
||||||
{
|
{
|
||||||
// Set initial values on the newly resolved item
|
// Set initial values on the newly resolved item
|
||||||
|
|
||||||
SetItemValues(item, args);
|
SetItemValues(item, args);
|
||||||
|
|
||||||
// Make sure the item has a name
|
// Make sure the item has a name
|
||||||
|
@ -53,11 +49,24 @@ namespace MediaBrowser.Controller.Resolvers
|
||||||
|
|
||||||
// Make sure DateCreated and DateModified have values
|
// Make sure DateCreated and DateModified have values
|
||||||
EnsureDates(item);
|
EnsureDates(item);
|
||||||
|
|
||||||
|
await FetchMetadataFromProviders(item, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task FetchMetadataFromProviders(T item, ItemResolveEventArgs args)
|
||||||
|
{
|
||||||
|
foreach (BaseMetadataProvider provider in Kernel.Instance.MetadataProviders)
|
||||||
|
{
|
||||||
|
if (provider.Supports(item))
|
||||||
|
{
|
||||||
|
await provider.Fetch(item, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void EnsureName(T item)
|
private void EnsureName(T item)
|
||||||
{
|
{
|
||||||
// If the subclass didn't supply a name, add it here
|
// If the subclass didn't supply a name, add it here
|
||||||
|
@ -84,76 +93,6 @@ namespace MediaBrowser.Controller.Resolvers
|
||||||
item.DateModified = Path.IsPathRooted(item.Path) ? File.GetLastWriteTime(item.Path) : DateTime.Now;
|
item.DateModified = Path.IsPathRooted(item.Path) ? File.GetLastWriteTime(item.Path) : DateTime.Now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Fills in image paths based on files win the folder
|
|
||||||
/// </summary>
|
|
||||||
protected virtual void PopulateImages(T item, ItemResolveEventArgs args)
|
|
||||||
{
|
|
||||||
List<string> backdropFiles = new List<string>();
|
|
||||||
|
|
||||||
foreach (KeyValuePair<string,FileAttributes> file in args.FileSystemChildren)
|
|
||||||
{
|
|
||||||
if (file.Value.HasFlag(FileAttributes.Directory))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
string filePath = file.Key;
|
|
||||||
|
|
||||||
string ext = Path.GetExtension(filePath);
|
|
||||||
|
|
||||||
// Only support png and jpg files
|
|
||||||
if (!ext.EndsWith("png", StringComparison.OrdinalIgnoreCase) && !ext.EndsWith("jpg", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
string name = Path.GetFileNameWithoutExtension(filePath);
|
|
||||||
|
|
||||||
if (name.Equals("folder", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
item.PrimaryImagePath = filePath;
|
|
||||||
}
|
|
||||||
else if (name.StartsWith("backdrop", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
backdropFiles.Add(filePath);
|
|
||||||
}
|
|
||||||
if (name.Equals("logo", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
item.LogoImagePath = filePath;
|
|
||||||
}
|
|
||||||
if (name.Equals("banner", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
item.BannerImagePath = filePath;
|
|
||||||
}
|
|
||||||
if (name.Equals("art", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
item.ArtImagePath = filePath;
|
|
||||||
}
|
|
||||||
if (name.Equals("thumb", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
item.ThumbnailImagePath = filePath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (backdropFiles.Any())
|
|
||||||
{
|
|
||||||
item.BackdropImagePaths = backdropFiles;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void PopulateLocalTrailers(T item, ItemResolveEventArgs args)
|
|
||||||
{
|
|
||||||
var trailerPath = args.GetFolderByName("trailers");
|
|
||||||
|
|
||||||
if (trailerPath.HasValue)
|
|
||||||
{
|
|
||||||
string[] allFiles = Directory.GetFileSystemEntries(trailerPath.Value.Key, "*", SearchOption.TopDirectoryOnly);
|
|
||||||
|
|
||||||
item.LocalTrailers = allFiles.Select(f => Kernel.Instance.ItemController.GetItem(f)).OfType<Video>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -161,6 +100,6 @@ namespace MediaBrowser.Controller.Resolvers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IBaseItemResolver
|
public interface IBaseItemResolver
|
||||||
{
|
{
|
||||||
BaseItem ResolvePath(ItemResolveEventArgs args);
|
Task<BaseItem> ResolvePath(ItemResolveEventArgs args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using System.ComponentModel.Composition;
|
using System.ComponentModel.Composition;
|
||||||
using MediaBrowser.Controller.Events;
|
using MediaBrowser.Controller.Events;
|
||||||
using MediaBrowser.Controller.Xml;
|
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Resolvers
|
namespace MediaBrowser.Controller.Resolvers
|
||||||
|
@ -27,19 +26,6 @@ namespace MediaBrowser.Controller.Resolvers
|
||||||
base.SetItemValues(item, args);
|
base.SetItemValues(item, args);
|
||||||
|
|
||||||
item.IsRoot = args.Parent == null;
|
item.IsRoot = args.Parent == null;
|
||||||
|
|
||||||
// Read data from folder.xml, if it exists
|
|
||||||
PopulateFolderMetadata(item, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PopulateFolderMetadata(TItemType folder, ItemResolveEventArgs args)
|
|
||||||
{
|
|
||||||
var metadataFile = args.GetFileByName("folder.xml");
|
|
||||||
|
|
||||||
if (metadataFile.HasValue)
|
|
||||||
{
|
|
||||||
new FolderXmlParser().Fetch(folder, metadataFile.Value.Key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,7 @@ namespace MediaBrowser.Movies.Resolvers
|
||||||
{
|
{
|
||||||
string[] allFiles = Directory.GetFileSystemEntries(trailerPath.Value.Key, "*", SearchOption.TopDirectoryOnly);
|
string[] allFiles = Directory.GetFileSystemEntries(trailerPath.Value.Key, "*", SearchOption.TopDirectoryOnly);
|
||||||
|
|
||||||
item.SpecialFeatures = allFiles.Select(f => Kernel.Instance.ItemController.GetItem(f)).OfType<Video>();
|
item.SpecialFeatures = allFiles.Select(f => Kernel.Instance.ItemController.GetItem(null, f)).OfType<Video>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:tb="http://www.hardcodet.net/taskbar"
|
xmlns:tb="http://www.hardcodet.net/taskbar"
|
||||||
Title="MainWindow" Height="350" Width="525" AllowsTransparency="True" Background="Transparent" WindowStyle="None" ShowInTaskbar="False" Loaded="MainWindow_Loaded">
|
Title="MainWindow" Height="350" Width="525" AllowsTransparency="True" Background="Transparent" WindowStyle="None" ShowInTaskbar="False">
|
||||||
<Grid>
|
<Grid>
|
||||||
<tb:TaskbarIcon Name="MbTaskbarIcon" IconSource="/Icons/Icon.ico" ToolTipText="MediaBrowser Server" Visibility="Hidden">
|
<tb:TaskbarIcon Name="MbTaskbarIcon" IconSource="/Icons/Icon.ico" ToolTipText="MediaBrowser Server" Visibility="Hidden">
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@ using System.Windows;
|
||||||
using MediaBrowser.Common.Logging;
|
using MediaBrowser.Common.Logging;
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Model.Progress;
|
using MediaBrowser.Model.Progress;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MediaBrowser.Common.UI;
|
||||||
|
|
||||||
namespace MediaBrowser.ServerApplication
|
namespace MediaBrowser.ServerApplication
|
||||||
{
|
{
|
||||||
|
@ -18,10 +20,10 @@ namespace MediaBrowser.ServerApplication
|
||||||
LoadKernel();
|
LoadKernel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadKernel()
|
private async void LoadKernel()
|
||||||
{
|
{
|
||||||
Progress<TaskProgress> progress = new Progress<TaskProgress>();
|
Progress<TaskProgress> progress = new Progress<TaskProgress>();
|
||||||
Common.UI.Splash splash = new Common.UI.Splash(progress);
|
Splash splash = new Splash(progress);
|
||||||
|
|
||||||
splash.Show();
|
splash.Show();
|
||||||
|
|
||||||
|
@ -29,11 +31,14 @@ namespace MediaBrowser.ServerApplication
|
||||||
{
|
{
|
||||||
DateTime now = DateTime.Now;
|
DateTime now = DateTime.Now;
|
||||||
|
|
||||||
new Kernel().Init(progress);
|
await new Kernel().Init(progress);
|
||||||
|
|
||||||
double seconds = (DateTime.Now - now).TotalSeconds;
|
double seconds = (DateTime.Now - now).TotalSeconds;
|
||||||
|
|
||||||
Logger.LogInfo("Kernel.Init completed in {0} seconds.", seconds);
|
Logger.LogInfo("Kernel.Init completed in {0} seconds.", seconds);
|
||||||
|
|
||||||
|
// Don't show the system tray icon until the kernel finishes.
|
||||||
|
this.MbTaskbarIcon.Visibility = System.Windows.Visibility.Visible;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -46,16 +51,6 @@ namespace MediaBrowser.ServerApplication
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Main Window Events
|
|
||||||
|
|
||||||
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
// Don't show the system tray icon until the app has loaded.
|
|
||||||
this.MbTaskbarIcon.Visibility = System.Windows.Visibility.Visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Context Menu events
|
#region Context Menu events
|
||||||
|
|
||||||
private void cmOpenDashboard_click(object sender, RoutedEventArgs e)
|
private void cmOpenDashboard_click(object sender, RoutedEventArgs e)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user