diff options
| author | LukePulverenti <luke.pulverenti@gmail.com> | 2013-02-28 14:32:41 -0500 |
|---|---|---|
| committer | LukePulverenti <luke.pulverenti@gmail.com> | 2013-02-28 14:32:41 -0500 |
| commit | af7aa597c35279e286ee88641854db69744e7b15 (patch) | |
| tree | 09cddf891558aed82f62bd5802fd9ff9ab21f116 /MediaBrowser.Server.Implementations | |
| parent | 4435e83e696c9172b2dcb8a5d48d866f767a421c (diff) | |
referenced core plugins, fixed some dashboard issues, extracted library manager
Diffstat (limited to 'MediaBrowser.Server.Implementations')
6 files changed, 837 insertions, 6 deletions
diff --git a/MediaBrowser.Server.Implementations/BdInfo/BdInfoExaminer.cs b/MediaBrowser.Server.Implementations/BdInfo/BdInfoExaminer.cs new file mode 100644 index 000000000..f33682f12 --- /dev/null +++ b/MediaBrowser.Server.Implementations/BdInfo/BdInfoExaminer.cs @@ -0,0 +1,170 @@ +using BDInfo; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.MediaInfo; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MediaBrowser.Server.Implementations.BdInfo +{ + /// <summary> + /// Class BdInfoExaminer + /// </summary> + public class BdInfoExaminer : IBlurayExaminer + { + /// <summary> + /// Gets the disc info. + /// </summary> + /// <param name="path">The path.</param> + /// <returns>BlurayDiscInfo.</returns> + public BlurayDiscInfo GetDiscInfo(string path) + { + var bdrom = new BDROM(path); + + bdrom.Scan(); + + // Get the longest playlist + var playlist = bdrom.PlaylistFiles.Values.OrderByDescending(p => p.TotalLength).FirstOrDefault(p => p.IsValid); + + var outputStream = new BlurayDiscInfo + { + MediaStreams = new List<MediaStream>() + }; + + if (playlist == null) + { + return outputStream; + } + + outputStream.Chapters = playlist.Chapters; + + outputStream.RunTimeTicks = TimeSpan.FromSeconds(playlist.TotalLength).Ticks; + + var mediaStreams = new List<MediaStream> { }; + + foreach (var stream in playlist.SortedStreams) + { + var videoStream = stream as TSVideoStream; + + if (videoStream != null) + { + AddVideoStream(mediaStreams, videoStream); + continue; + } + + var audioStream = stream as TSAudioStream; + + if (audioStream != null) + { + AddAudioStream(mediaStreams, audioStream); + continue; + } + + var textStream = stream as TSTextStream; + + if (textStream != null) + { + AddSubtitleStream(mediaStreams, textStream); + continue; + } + + var graphicsStream = stream as TSGraphicsStream; + + if (graphicsStream != null) + { + AddSubtitleStream(mediaStreams, graphicsStream); + } + } + + outputStream.MediaStreams = mediaStreams; + + if (playlist.StreamClips != null && playlist.StreamClips.Any()) + { + // Get the files in the playlist + outputStream.Files = playlist.StreamClips.Select(i => i.StreamFile.Name).ToList(); + } + + return outputStream; + } + + /// <summary> + /// Adds the video stream. + /// </summary> + /// <param name="streams">The streams.</param> + /// <param name="videoStream">The video stream.</param> + private void AddVideoStream(List<MediaStream> streams, TSVideoStream videoStream) + { + var mediaStream = new MediaStream + { + BitRate = Convert.ToInt32(videoStream.BitRate), + Width = videoStream.Width, + Height = videoStream.Height, + Codec = videoStream.CodecShortName, + ScanType = videoStream.IsInterlaced ? "interlaced" : "progressive", + Type = MediaStreamType.Video, + Index = streams.Count + }; + + if (videoStream.FrameRateDenominator > 0) + { + float frameRateEnumerator = videoStream.FrameRateEnumerator; + float frameRateDenominator = videoStream.FrameRateDenominator; + + mediaStream.AverageFrameRate = mediaStream.RealFrameRate = frameRateEnumerator / frameRateDenominator; + } + + streams.Add(mediaStream); + } + + /// <summary> + /// Adds the audio stream. + /// </summary> + /// <param name="streams">The streams.</param> + /// <param name="audioStream">The audio stream.</param> + private void AddAudioStream(List<MediaStream> streams, TSAudioStream audioStream) + { + streams.Add(new MediaStream + { + BitRate = Convert.ToInt32(audioStream.BitRate), + Codec = audioStream.CodecShortName, + Language = audioStream.LanguageCode, + Channels = audioStream.ChannelCount, + SampleRate = audioStream.SampleRate, + Type = MediaStreamType.Audio, + Index = streams.Count + }); + } + + /// <summary> + /// Adds the subtitle stream. + /// </summary> + /// <param name="streams">The streams.</param> + /// <param name="textStream">The text stream.</param> + private void AddSubtitleStream(List<MediaStream> streams, TSTextStream textStream) + { + streams.Add(new MediaStream + { + Language = textStream.LanguageCode, + Codec = textStream.CodecShortName, + Type = MediaStreamType.Subtitle, + Index = streams.Count + }); + } + + /// <summary> + /// Adds the subtitle stream. + /// </summary> + /// <param name="streams">The streams.</param> + /// <param name="textStream">The text stream.</param> + private void AddSubtitleStream(List<MediaStream> streams, TSGraphicsStream textStream) + { + streams.Add(new MediaStream + { + Language = textStream.LanguageCode, + Codec = textStream.CodecShortName, + Type = MediaStreamType.Subtitle, + Index = streams.Count + }); + } + } +} diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs new file mode 100644 index 000000000..73eed018d --- /dev/null +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -0,0 +1,644 @@ +using MediaBrowser.Common.Events; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.ScheduledTasks; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.IO; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Resolvers; +using MediaBrowser.Controller.ScheduledTasks; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using MoreLinq; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Implementations.Library +{ + /// <summary> + /// Class LibraryManager + /// </summary> + public class LibraryManager : ILibraryManager + { + #region LibraryChanged Event + /// <summary> + /// Fires whenever any validation routine adds or removes items. The added and removed items are properties of the args. + /// *** Will fire asynchronously. *** + /// </summary> + public event EventHandler<ChildrenChangedEventArgs> LibraryChanged; + + /// <summary> + /// Raises the <see cref="E:LibraryChanged" /> event. + /// </summary> + /// <param name="args">The <see cref="ChildrenChangedEventArgs" /> instance containing the event data.</param> + public void ReportLibraryChanged(ChildrenChangedEventArgs args) + { + EventHelper.QueueEventIfNotNull(LibraryChanged, this, args, _logger); + + // Had to put this in a separate method to avoid an implicitly captured closure + SendLibraryChangedWebSocketMessage(args); + } + + /// <summary> + /// Sends the library changed web socket message. + /// </summary> + /// <param name="args">The <see cref="ChildrenChangedEventArgs" /> instance containing the event data.</param> + private void SendLibraryChangedWebSocketMessage(ChildrenChangedEventArgs args) + { + // Notify connected ui's + Kernel.ServerManager.SendWebSocketMessage("LibraryChanged", () => DtoBuilder.GetLibraryUpdateInfo(args)); + } + #endregion + + /// <summary> + /// The _logger + /// </summary> + private readonly ILogger _logger; + + /// <summary> + /// The _task manager + /// </summary> + private readonly ITaskManager _taskManager; + + /// <summary> + /// The _user manager + /// </summary> + private readonly IUserManager _userManager; + + /// <summary> + /// Gets or sets the kernel. + /// </summary> + /// <value>The kernel.</value> + private Kernel Kernel { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="LibraryManager" /> class. + /// </summary> + /// <param name="kernel">The kernel.</param> + /// <param name="logger">The logger.</param> + /// <param name="taskManager">The task manager.</param> + /// <param name="userManager">The user manager.</param> + public LibraryManager(Kernel kernel, ILogger logger, ITaskManager taskManager, IUserManager userManager) + { + Kernel = kernel; + _logger = logger; + _taskManager = taskManager; + _userManager = userManager; + + kernel.ConfigurationUpdated += kernel_ConfigurationUpdated; + } + + /// <summary> + /// The _root folder + /// </summary> + private AggregateFolder _rootFolder; + /// <summary> + /// The _root folder sync lock + /// </summary> + private object _rootFolderSyncLock = new object(); + /// <summary> + /// The _root folder initialized + /// </summary> + private bool _rootFolderInitialized; + /// <summary> + /// Gets the root folder. + /// </summary> + /// <value>The root folder.</value> + public AggregateFolder RootFolder + { + get + { + LazyInitializer.EnsureInitialized(ref _rootFolder, ref _rootFolderInitialized, ref _rootFolderSyncLock, CreateRootFolder); + return _rootFolder; + } + private set + { + _rootFolder = value; + + if (value == null) + { + _rootFolderInitialized = false; + } + } + } + + /// <summary> + /// Handles the ConfigurationUpdated event of the kernel control. + /// </summary> + /// <param name="sender">The source of the event.</param> + /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param> + void kernel_ConfigurationUpdated(object sender, EventArgs e) + { + //// Figure out whether or not we should refresh people after the update is finished + //var refreshPeopleAfterUpdate = !oldConfiguration.EnableInternetProviders && config.EnableInternetProviders; + + //// This is true if internet providers has just been turned on, or if People have just been removed from InternetProviderExcludeTypes + //if (!refreshPeopleAfterUpdate) + //{ + // var oldConfigurationFetchesPeopleImages = oldConfiguration.InternetProviderExcludeTypes == null || !oldConfiguration.InternetProviderExcludeTypes.Contains(typeof(Person).Name, StringComparer.OrdinalIgnoreCase); + // var newConfigurationFetchesPeopleImages = config.InternetProviderExcludeTypes == null || !config.InternetProviderExcludeTypes.Contains(typeof(Person).Name, StringComparer.OrdinalIgnoreCase); + + // refreshPeopleAfterUpdate = newConfigurationFetchesPeopleImages && !oldConfigurationFetchesPeopleImages; + //} + + Task.Run(() => + { + // Any number of configuration settings could change the way the library is refreshed, so do that now + _taskManager.CancelIfRunningAndQueue<RefreshMediaLibraryTask>(); + _taskManager.CancelIfRunningAndQueue<PeopleValidationTask>(); + }); + } + + /// <summary> + /// Resolves the item. + /// </summary> + /// <param name="args">The args.</param> + /// <returns>BaseItem.</returns> + public BaseItem ResolveItem(ItemResolveArgs args) + { + return Kernel.EntityResolvers.Select(r => r.ResolvePath(args)).FirstOrDefault(i => i != null); + } + + /// <summary> + /// Resolves a path into a BaseItem + /// </summary> + /// <param name="path">The path.</param> + /// <param name="parent">The parent.</param> + /// <param name="fileInfo">The file info.</param> + /// <returns>BaseItem.</returns> + /// <exception cref="System.ArgumentNullException"></exception> + public BaseItem ResolvePath(string path, Folder parent = null, WIN32_FIND_DATA? fileInfo = null) + { + if (string.IsNullOrEmpty(path)) + { + throw new ArgumentNullException(); + } + + fileInfo = fileInfo ?? FileSystem.GetFileData(path); + + if (!fileInfo.HasValue) + { + return null; + } + + var args = new ItemResolveArgs + { + Parent = parent, + Path = path, + FileInfo = fileInfo.Value + }; + + // Return null if ignore rules deem that we should do so + if (Kernel.EntityResolutionIgnoreRules.Any(r => r.ShouldIgnore(args))) + { + return null; + } + + // Gather child folder and files + if (args.IsDirectory) + { + // When resolving the root, we need it's grandchildren (children of user views) + var flattenFolderDepth = args.IsPhysicalRoot ? 2 : 0; + + args.FileSystemDictionary = FileData.GetFilteredFileSystemEntries(args.Path, _logger, flattenFolderDepth: flattenFolderDepth, args: args); + } + + // Check to see if we should resolve based on our contents + if (args.IsDirectory && !EntityResolutionHelper.ShouldResolvePathContents(args)) + { + return null; + } + + return ResolveItem(args); + } + + /// <summary> + /// Resolves a set of files into a list of BaseItem + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="files">The files.</param> + /// <param name="parent">The parent.</param> + /// <returns>List{``0}.</returns> + public List<T> ResolvePaths<T>(IEnumerable<WIN32_FIND_DATA> files, Folder parent) + where T : BaseItem + { + var list = new List<T>(); + + Parallel.ForEach(files, f => + { + try + { + var item = ResolvePath(f.Path, parent, f) as T; + + if (item != null) + { + lock (list) + { + list.Add(item); + } + } + } + catch (Exception ex) + { + _logger.ErrorException("Error resolving path {0}", ex, f.Path); + } + }); + + return list; + } + + /// <summary> + /// Creates the root media folder + /// </summary> + /// <returns>AggregateFolder.</returns> + /// <exception cref="System.InvalidOperationException">Cannot create the root folder until plugins have loaded</exception> + public AggregateFolder CreateRootFolder() + { + if (Kernel.Plugins == null) + { + throw new InvalidOperationException("Cannot create the root folder until plugins have loaded"); + } + + var rootFolderPath = Kernel.ApplicationPaths.RootFolderPath; + var rootFolder = Kernel.ItemRepository.RetrieveItem(rootFolderPath.GetMBId(typeof(AggregateFolder))) as AggregateFolder ?? (AggregateFolder)ResolvePath(rootFolderPath); + + // Add in the plug-in folders + foreach (var child in Kernel.PluginFolderCreators) + { + rootFolder.AddVirtualChild(child.GetFolder()); + } + + return rootFolder; + } + + /// <summary> + /// Gets a Person + /// </summary> + /// <param name="name">The name.</param> + /// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param> + /// <returns>Task{Person}.</returns> + public Task<Person> GetPerson(string name, bool allowSlowProviders = false) + { + return GetPerson(name, CancellationToken.None, allowSlowProviders); + } + + /// <summary> + /// Gets a Person + /// </summary> + /// <param name="name">The name.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param> + /// <returns>Task{Person}.</returns> + private Task<Person> GetPerson(string name, CancellationToken cancellationToken, bool allowSlowProviders = false) + { + return GetImagesByNameItem<Person>(Kernel.ApplicationPaths.PeoplePath, name, cancellationToken, allowSlowProviders); + } + + /// <summary> + /// Gets a Studio + /// </summary> + /// <param name="name">The name.</param> + /// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param> + /// <returns>Task{Studio}.</returns> + public Task<Studio> GetStudio(string name, bool allowSlowProviders = false) + { + return GetImagesByNameItem<Studio>(Kernel.ApplicationPaths.StudioPath, name, CancellationToken.None, allowSlowProviders); + } + + /// <summary> + /// Gets a Genre + /// </summary> + /// <param name="name">The name.</param> + /// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param> + /// <returns>Task{Genre}.</returns> + public Task<Genre> GetGenre(string name, bool allowSlowProviders = false) + { + return GetImagesByNameItem<Genre>(Kernel.ApplicationPaths.GenrePath, name, CancellationToken.None, allowSlowProviders); + } + + /// <summary> + /// The us culture + /// </summary> + private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); + + /// <summary> + /// Gets a Year + /// </summary> + /// <param name="value">The value.</param> + /// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param> + /// <returns>Task{Year}.</returns> + /// <exception cref="System.ArgumentOutOfRangeException"></exception> + public Task<Year> GetYear(int value, bool allowSlowProviders = false) + { + if (value <= 0) + { + throw new ArgumentOutOfRangeException(); + } + + return GetImagesByNameItem<Year>(Kernel.ApplicationPaths.YearPath, value.ToString(UsCulture), CancellationToken.None, allowSlowProviders); + } + + /// <summary> + /// The images by name item cache + /// </summary> + private readonly ConcurrentDictionary<string, object> ImagesByNameItemCache = new ConcurrentDictionary<string, object>(StringComparer.OrdinalIgnoreCase); + + /// <summary> + /// Generically retrieves an IBN item + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="path">The path.</param> + /// <param name="name">The name.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param> + /// <returns>Task{``0}.</returns> + /// <exception cref="System.ArgumentNullException"></exception> + private Task<T> GetImagesByNameItem<T>(string path, string name, CancellationToken cancellationToken, bool allowSlowProviders = true) + where T : BaseItem, new() + { + if (string.IsNullOrEmpty(path)) + { + throw new ArgumentNullException(); + } + + if (string.IsNullOrEmpty(name)) + { + throw new ArgumentNullException(); + } + + var key = Path.Combine(path, FileSystem.GetValidFilename(name)); + + var obj = ImagesByNameItemCache.GetOrAdd(key, keyname => CreateImagesByNameItem<T>(path, name, cancellationToken, allowSlowProviders)); + + return obj as Task<T>; + } + + /// <summary> + /// Creates an IBN item based on a given path + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="path">The path.</param> + /// <param name="name">The name.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param> + /// <returns>Task{``0}.</returns> + /// <exception cref="System.IO.IOException">Path not created: + path</exception> + private async Task<T> CreateImagesByNameItem<T>(string path, string name, CancellationToken cancellationToken, bool allowSlowProviders = true) + where T : BaseItem, new() + { + cancellationToken.ThrowIfCancellationRequested(); + + _logger.Debug("Creating {0}: {1}", typeof(T).Name, name); + + path = Path.Combine(path, FileSystem.GetValidFilename(name)); + + var fileInfo = FileSystem.GetFileData(path); + + var isNew = false; + + if (!fileInfo.HasValue) + { + Directory.CreateDirectory(path); + fileInfo = FileSystem.GetFileData(path); + + if (!fileInfo.HasValue) + { + throw new IOException("Path not created: " + path); + } + + isNew = true; + } + + cancellationToken.ThrowIfCancellationRequested(); + + var id = path.GetMBId(typeof(T)); + + var item = Kernel.ItemRepository.RetrieveItem(id) as T; + if (item == null) + { + item = new T + { + Name = name, + Id = id, + DateCreated = fileInfo.Value.CreationTimeUtc, + DateModified = fileInfo.Value.LastWriteTimeUtc, + Path = path + }; + isNew = true; + } + + cancellationToken.ThrowIfCancellationRequested(); + + // Set this now so we don't cause additional file system access during provider executions + item.ResetResolveArgs(fileInfo); + + await item.RefreshMetadata(cancellationToken, isNew, allowSlowProviders: allowSlowProviders).ConfigureAwait(false); + + cancellationToken.ThrowIfCancellationRequested(); + + return item; + } + + /// <summary> + /// Validate and refresh the People sub-set of the IBN. + /// The items are stored in the db but not loaded into memory until actually requested by an operation. + /// </summary> + /// <param name="cancellationToken">The cancellation token.</param> + /// <param name="progress">The progress.</param> + /// <returns>Task.</returns> + public async Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress) + { + // Clear the IBN cache + ImagesByNameItemCache.Clear(); + + const int maxTasks = 250; + + var tasks = new List<Task>(); + + var includedPersonTypes = new[] { PersonType.Actor, PersonType.Director }; + + var people = RootFolder.RecursiveChildren + .Where(c => c.People != null) + .SelectMany(c => c.People.Where(p => includedPersonTypes.Contains(p.Type))) + .DistinctBy(p => p.Name, StringComparer.OrdinalIgnoreCase) + .ToList(); + + var numComplete = 0; + + foreach (var person in people) + { + if (tasks.Count > maxTasks) + { + await Task.WhenAll(tasks).ConfigureAwait(false); + tasks.Clear(); + + // Safe cancellation point, when there are no pending tasks + cancellationToken.ThrowIfCancellationRequested(); + } + + // Avoid accessing the foreach variable within the closure + var currentPerson = person; + + tasks.Add(Task.Run(async () => + { + cancellationToken.ThrowIfCancellationRequested(); + + try + { + await GetPerson(currentPerson.Name, cancellationToken, allowSlowProviders: true).ConfigureAwait(false); + } + catch (IOException ex) + { + _logger.ErrorException("Error validating IBN entry {0}", ex, currentPerson.Name); + } + + // Update progress + lock (progress) + { + numComplete++; + double percent = numComplete; + percent /= people.Count; + + progress.Report(100 * percent); + } + })); + } + + await Task.WhenAll(tasks).ConfigureAwait(false); + + progress.Report(100); + + _logger.Info("People validation complete"); + } + + /// <summary> + /// Reloads the root media folder + /// </summary> + /// <param name="progress">The progress.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + public async Task ValidateMediaLibrary(IProgress<double> progress, CancellationToken cancellationToken) + { + _logger.Info("Validating media library"); + + await RootFolder.RefreshMetadata(cancellationToken).ConfigureAwait(false); + + // Start by just validating the children of the root, but go no further + await RootFolder.ValidateChildren(new Progress<double> { }, cancellationToken, recursive: false); + + // Validate only the collection folders for each user, just to make them available as quickly as possible + var userCollectionFolderTasks = _userManager.Users.AsParallel().Select(user => user.ValidateCollectionFolders(new Progress<double> { }, cancellationToken)); + await Task.WhenAll(userCollectionFolderTasks).ConfigureAwait(false); + + // Now validate the entire media library + await RootFolder.ValidateChildren(progress, cancellationToken, recursive: true).ConfigureAwait(false); + + foreach (var user in _userManager.Users) + { + await user.ValidateMediaLibrary(new Progress<double> { }, cancellationToken).ConfigureAwait(false); + } + } + + /// <summary> + /// Saves display preferences for a Folder + /// </summary> + /// <param name="user">The user.</param> + /// <param name="folder">The folder.</param> + /// <param name="data">The data.</param> + /// <returns>Task.</returns> + public Task SaveDisplayPreferencesForFolder(User user, Folder folder, DisplayPreferences data) + { + // Need to update all items with the same DisplayPrefsId + foreach (var child in RootFolder.GetRecursiveChildren(user) + .OfType<Folder>() + .Where(i => i.DisplayPrefsId == folder.DisplayPrefsId)) + { + child.AddOrUpdateDisplayPrefs(user, data); + } + + return Kernel.DisplayPreferencesRepository.SaveDisplayPrefs(folder, CancellationToken.None); + } + + /// <summary> + /// Gets the default view. + /// </summary> + /// <returns>IEnumerable{VirtualFolderInfo}.</returns> + public IEnumerable<VirtualFolderInfo> GetDefaultVirtualFolders() + { + return GetView(Kernel.ApplicationPaths.DefaultUserViewsPath); + } + + /// <summary> + /// Gets the view. + /// </summary> + /// <param name="user">The user.</param> + /// <returns>IEnumerable{VirtualFolderInfo}.</returns> + public IEnumerable<VirtualFolderInfo> GetVirtualFolders(User user) + { + return GetView(user.RootFolderPath); + } + + /// <summary> + /// Gets the view. + /// </summary> + /// <param name="path">The path.</param> + /// <returns>IEnumerable{VirtualFolderInfo}.</returns> + private IEnumerable<VirtualFolderInfo> GetView(string path) + { + return Directory.EnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly) + .Select(dir => new VirtualFolderInfo + { + Name = Path.GetFileName(dir), + Locations = Directory.EnumerateFiles(dir, "*.lnk", SearchOption.TopDirectoryOnly).Select(FileSystem.ResolveShortcut).ToList() + }); + } + + /// <summary> + /// Finds a library item by Id and UserId. + /// </summary> + /// <param name="id">The id.</param> + /// <param name="userId">The user id.</param> + /// <param name="userManager">The user manager.</param> + /// <returns>BaseItem.</returns> + /// <exception cref="System.ArgumentNullException">id</exception> + public BaseItem GetItemById(Guid id, Guid userId) + { + if (id == Guid.Empty) + { + throw new ArgumentNullException("id"); + } + + if (userId == Guid.Empty) + { + throw new ArgumentNullException("userId"); + } + + var user = _userManager.GetUserById(userId); + var userRoot = user.RootFolder; + + return userRoot.FindItemById(id, user); + } + + /// <summary> + /// Gets the item by id. + /// </summary> + /// <param name="id">The id.</param> + /// <returns>BaseItem.</returns> + /// <exception cref="System.ArgumentNullException">id</exception> + public BaseItem GetItemById(Guid id) + { + if (id == Guid.Empty) + { + throw new ArgumentNullException("id"); + } + return null; + //return RootFolder.FindItemById(id, null); + } + } +} diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 4fba9da43..d59335837 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -32,6 +32,9 @@ <WarningLevel>4</WarningLevel> </PropertyGroup> <ItemGroup> + <Reference Include="MoreLinq"> + <HintPath>..\packages\morelinq.1.0.15631-beta\lib\net35\MoreLinq.dll</HintPath> + </Reference> <Reference Include="System" /> <Reference Include="System.Core" /> <Reference Include="System.Data.SQLite"> @@ -50,6 +53,8 @@ <Compile Include="..\SharedVersion.cs"> <Link>Properties\SharedVersion.cs</Link> </Compile> + <Compile Include="BdInfo\BdInfoExaminer.cs" /> + <Compile Include="Library\LibraryManager.cs" /> <Compile Include="Library\UserManager.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Reflection\TypeMapper.cs" /> @@ -66,6 +71,10 @@ <Compile Include="WorldWeatherOnline\WeatherProvider.cs" /> </ItemGroup> <ItemGroup> + <ProjectReference Include="..\BDInfo\BDInfo.csproj"> + <Project>{07b509c0-0c28-4f3f-8963-5263281f7e3d}</Project> + <Name>BDInfo</Name> + </ProjectReference> <ProjectReference Include="..\MediaBrowser.Common.Implementations\MediaBrowser.Common.Implementations.csproj"> <Project>{c4d2573a-3fd3-441f-81af-174ac4cd4e1d}</Project> <Name>MediaBrowser.Common.Implementations</Name> diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs index d3854f9d7..3ad1f1700 100644 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs +++ b/MediaBrowser.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs @@ -1,6 +1,7 @@ using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; @@ -23,16 +24,19 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks.Tasks /// The _logger /// </summary> private readonly ILogger _logger; + private readonly ILibraryManager _libraryManager; /// <summary> /// Initializes a new instance of the <see cref="ChapterImagesTask" /> class. /// </summary> /// <param name="kernel">The kernel.</param> /// <param name="logger">The logger.</param> - public ChapterImagesTask(Kernel kernel, ILogger logger) + /// <param name="libraryManager">The library manager.</param> + public ChapterImagesTask(Kernel kernel, ILogger logger, ILibraryManager libraryManager) { _kernel = kernel; _logger = logger; + _libraryManager = libraryManager; } /// <summary> @@ -55,7 +59,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks.Tasks /// <returns>Task.</returns> public Task Execute(CancellationToken cancellationToken, IProgress<double> progress) { - var videos = _kernel.RootFolder.RecursiveChildren.OfType<Video>().Where(v => v.Chapters != null).ToList(); + var videos = _libraryManager.RootFolder.RecursiveChildren.OfType<Video>().Where(v => v.Chapters != null).ToList(); var numComplete = 0; diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/Tasks/ImageCleanupTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/Tasks/ImageCleanupTask.cs index 57f69e2e3..939ba9317 100644 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/Tasks/ImageCleanupTask.cs +++ b/MediaBrowser.Server.Implementations/ScheduledTasks/Tasks/ImageCleanupTask.cs @@ -2,6 +2,7 @@ using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Library; using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; @@ -25,16 +26,18 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks.Tasks /// The _logger /// </summary> private readonly ILogger _logger; + private readonly ILibraryManager _libraryManager; /// <summary> /// Initializes a new instance of the <see cref="ImageCleanupTask" /> class. /// </summary> /// <param name="kernel">The kernel.</param> /// <param name="logger">The logger.</param> - public ImageCleanupTask(Kernel kernel, ILogger logger) + public ImageCleanupTask(Kernel kernel, ILogger logger, ILibraryManager libraryManager) { _kernel = kernel; _logger = logger; + _libraryManager = libraryManager; } /// <summary> @@ -66,8 +69,8 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks.Tasks .ToList(); // Now gather all items - var items = _kernel.RootFolder.RecursiveChildren.ToList(); - items.Add(_kernel.RootFolder); + var items = _libraryManager.RootFolder.RecursiveChildren.ToList(); + items.Add(_libraryManager.RootFolder); // Determine all possible image paths var pathsInUse = items.SelectMany(GetPathsInUse) @@ -115,7 +118,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks.Tasks /// <returns>Task.</returns> private Task EnsureChapterImages(CancellationToken cancellationToken) { - var videos = _kernel.RootFolder.RecursiveChildren.OfType<Video>().Where(v => v.Chapters != null).ToList(); + var videos = _libraryManager.RootFolder.RecursiveChildren.OfType<Video>().Where(v => v.Chapters != null).ToList(); var tasks = videos.Select(v => Task.Run(async () => { diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config index 106618814..270010817 100644 --- a/MediaBrowser.Server.Implementations/packages.config +++ b/MediaBrowser.Server.Implementations/packages.config @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <packages> + <package id="morelinq" version="1.0.15631-beta" targetFramework="net45" /> <package id="System.Data.SQLite" version="1.0.84.0" targetFramework="net45" /> </packages>
\ No newline at end of file |
