diff options
33 files changed, 413 insertions, 381 deletions
diff --git a/MediaBrowser.Api/Music/InstantMixService.cs b/MediaBrowser.Api/Music/InstantMixService.cs index cfb826a13..78c6a8bf4 100644 --- a/MediaBrowser.Api/Music/InstantMixService.cs +++ b/MediaBrowser.Api/Music/InstantMixService.cs @@ -73,44 +73,44 @@ namespace MediaBrowser.Api.Music public object Get(GetInstantMixFromArtistId request) { - var item = (MusicArtist)_libraryManager.GetItemById(request.Id); + var item = _libraryManager.GetItemById(request.Id); var user = _userManager.GetUserById(request.UserId.Value); - var items = _musicManager.GetInstantMixFromArtist(item.Name, user); + var items = _musicManager.GetInstantMixFromItem(item, user); return GetResult(items, user, request); } public object Get(GetInstantMixFromMusicGenreId request) { - var item = (MusicGenre)_libraryManager.GetItemById(request.Id); + var item = _libraryManager.GetItemById(request.Id); var user = _userManager.GetUserById(request.UserId.Value); - var items = _musicManager.GetInstantMixFromGenres(new[] { item.Name }, user); + var items = _musicManager.GetInstantMixFromItem(item, user); return GetResult(items, user, request); } public object Get(GetInstantMixFromSong request) { - var item = (Audio)_libraryManager.GetItemById(request.Id); + var item = _libraryManager.GetItemById(request.Id); var user = _userManager.GetUserById(request.UserId.Value); - var items = _musicManager.GetInstantMixFromSong(item, user); + var items = _musicManager.GetInstantMixFromItem(item, user); return GetResult(items, user, request); } public object Get(GetInstantMixFromAlbum request) { - var album = (MusicAlbum)_libraryManager.GetItemById(request.Id); + var album = _libraryManager.GetItemById(request.Id); var user = _userManager.GetUserById(request.UserId.Value); - var items = _musicManager.GetInstantMixFromAlbum(album, user); + var items = _musicManager.GetInstantMixFromItem(album, user); return GetResult(items, user, request); } @@ -121,7 +121,7 @@ namespace MediaBrowser.Api.Music var user = _userManager.GetUserById(request.UserId.Value); - var items = _musicManager.GetInstantMixFromPlaylist(playlist, user); + var items = _musicManager.GetInstantMixFromItem(playlist, user); return GetResult(items, user, request); } diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 77f6dc103..b3610bc38 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -824,7 +824,7 @@ namespace MediaBrowser.Api.Playback { get { - return true; + return false; } } diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs index cb0c54674..bf5c04540 100644 --- a/MediaBrowser.Api/StartupWizardService.cs +++ b/MediaBrowser.Api/StartupWizardService.cs @@ -62,7 +62,7 @@ namespace MediaBrowser.Api { _config.Configuration.IsStartupWizardCompleted = true; _config.Configuration.EnableLocalizedGuids = true; - _config.Configuration.StoreArtistsInMetadata = true; + _config.Configuration.MergeMetadataAndImagesByName = true; _config.Configuration.EnableStandaloneMetadata = true; _config.Configuration.EnableLibraryMetadataSubFolder = true; _config.SaveConfiguration(); diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs index c6ec53018..80ab72db7 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs @@ -228,7 +228,7 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public Guid UserId { get; set; } + public string UserId { get; set; } [ApiMember(Name = "Limit", Description = "Limit", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int Limit { get; set; } @@ -304,81 +304,15 @@ namespace MediaBrowser.Api.UserLibrary { var user = _userManager.GetUserById(request.UserId); - var includeTypes = string.IsNullOrWhiteSpace(request.IncludeItemTypes) - ? new string[] { } - : request.IncludeItemTypes.Split(','); - - var currentUser = user; - - Func<BaseItem, bool> filter = i => - { - if (includeTypes.Length > 0) - { - if (!includeTypes.Contains(i.GetType().Name, StringComparer.OrdinalIgnoreCase)) - { - return false; - } - } - - if (request.IsPlayed.HasValue) - { - var val = request.IsPlayed.Value; - if (i.IsPlayed(currentUser) != val) - { - return false; - } - } - - return i.LocationType != LocationType.Virtual && !i.IsFolder; - }; - - // Avoid implicitly captured closure - var libraryItems = string.IsNullOrEmpty(request.ParentId) && user != null ? - GetItemsConfiguredForLatest(user, filter) : - GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId, filter); - - libraryItems = libraryItems.OrderByDescending(i => i.DateCreated); - - if (request.IsPlayed.HasValue) + var list = _userViewManager.GetLatestItems(new LatestItemsQuery { - var takeLimit = request.Limit * 20; - libraryItems = libraryItems.Take(takeLimit); - } - - // Avoid implicitly captured closure - var items = libraryItems - .ToList(); - - var list = new List<Tuple<BaseItem, List<BaseItem>>>(); - - foreach (var item in items) - { - // Only grab the index container for media - var container = item.IsFolder || !request.GroupItems ? null : item.LatestItemsIndexContainer; - - if (container == null) - { - list.Add(new Tuple<BaseItem, List<BaseItem>>(null, new List<BaseItem> { item })); - } - else - { - var current = list.FirstOrDefault(i => i.Item1 != null && i.Item1.Id == container.Id); - - if (current != null) - { - current.Item2.Add(item); - } - else - { - list.Add(new Tuple<BaseItem, List<BaseItem>>(container, new List<BaseItem> { item })); - } - } - - if (list.Count >= request.Limit) - { - break; - } - } + GroupItems = request.GroupItems, + IncludeItemTypes = (request.IncludeItemTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(), + IsPlayed = request.IsPlayed, + Limit = request.Limit, + ParentId = request.ParentId, + UserId = request.UserId + }); var options = GetDtoOptions(request); @@ -403,18 +337,6 @@ namespace MediaBrowser.Api.UserLibrary return ToOptimizedResult(dtos.ToList()); } - private IEnumerable<BaseItem> GetItemsConfiguredForLatest(User user, Func<BaseItem,bool> filter) - { - // Avoid implicitly captured closure - var currentUser = user; - - return user.RootFolder.GetChildren(user, true) - .OfType<Folder>() - .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N"))) - .SelectMany(i => i.GetRecursiveChildren(currentUser, filter)) - .DistinctBy(i => i.Id); - } - public async Task<object> Get(GetUserViews request) { var user = _userManager.GetUserById(request.UserId); diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index 5e9038500..b09cb7105 100644 --- a/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -108,13 +108,9 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks /// </summary> private TaskResult _lastExecutionResult; /// <summary> - /// The _last execution resultinitialized - /// </summary> - private bool _lastExecutionResultinitialized; - /// <summary> /// The _last execution result sync lock /// </summary> - private object _lastExecutionResultSyncLock = new object(); + private readonly object _lastExecutionResultSyncLock = new object(); /// <summary> /// Gets the last execution result. /// </summary> @@ -123,38 +119,39 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks { get { - LazyInitializer.EnsureInitialized(ref _lastExecutionResult, ref _lastExecutionResultinitialized, ref _lastExecutionResultSyncLock, () => + if (_lastExecutionResult == null) { - var path = GetHistoryFilePath(); - - try - { - return JsonSerializer.DeserializeFromFile<TaskResult>(path); - } - catch (DirectoryNotFoundException) - { - // File doesn't exist. No biggie - return null; - } - catch (FileNotFoundException) - { - // File doesn't exist. No biggie - return null; - } - catch (Exception ex) + lock (_lastExecutionResultSyncLock) { - Logger.ErrorException("Error deserializing {0}", ex, path); - return null; + if (_lastExecutionResult == null) + { + var path = GetHistoryFilePath(); + + try + { + return JsonSerializer.DeserializeFromFile<TaskResult>(path); + } + catch (DirectoryNotFoundException) + { + // File doesn't exist. No biggie + } + catch (FileNotFoundException) + { + // File doesn't exist. No biggie + } + catch (Exception ex) + { + Logger.ErrorException("Error deserializing {0}", ex, path); + } + } } - }); + } return _lastExecutionResult; } private set { _lastExecutionResult = value; - - _lastExecutionResultinitialized = value != null; } } @@ -227,13 +224,9 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks /// </summary> private IEnumerable<ITaskTrigger> _triggers; /// <summary> - /// The _triggers initialized - /// </summary> - private bool _triggersInitialized; - /// <summary> /// The _triggers sync lock /// </summary> - private object _triggersSyncLock = new object(); + private readonly object _triggersSyncLock = new object(); /// <summary> /// Gets the triggers that define when the task will run /// </summary> @@ -243,7 +236,16 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks { get { - LazyInitializer.EnsureInitialized(ref _triggers, ref _triggersInitialized, ref _triggersSyncLock, LoadTriggers); + if (_triggers == null) + { + lock (_triggersSyncLock) + { + if (_triggers == null) + { + _triggers = LoadTriggers(); + } + } + } return _triggers; } @@ -262,8 +264,6 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks _triggers = value.ToList(); - _triggersInitialized = true; - ReloadTriggerEvents(false); SaveTriggers(_triggers); diff --git a/MediaBrowser.Controller/Devices/CameraImageUploadInfo.cs b/MediaBrowser.Controller/Devices/CameraImageUploadInfo.cs new file mode 100644 index 000000000..b3f3bb902 --- /dev/null +++ b/MediaBrowser.Controller/Devices/CameraImageUploadInfo.cs @@ -0,0 +1,10 @@ +using MediaBrowser.Model.Devices; + +namespace MediaBrowser.Controller.Devices +{ + public class CameraImageUploadInfo + { + public LocalFileInfo FileInfo { get; set; } + public DeviceInfo Device { get; set; } + } +} diff --git a/MediaBrowser.Controller/Devices/IDeviceManager.cs b/MediaBrowser.Controller/Devices/IDeviceManager.cs index f5010bb45..78eebd994 100644 --- a/MediaBrowser.Controller/Devices/IDeviceManager.cs +++ b/MediaBrowser.Controller/Devices/IDeviceManager.cs @@ -3,7 +3,6 @@ using MediaBrowser.Model.Events; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Session; using System; -using System.Collections.Generic; using System.IO; using System.Threading.Tasks; @@ -15,6 +14,10 @@ namespace MediaBrowser.Controller.Devices /// Occurs when [device options updated]. /// </summary> event EventHandler<GenericEventArgs<DeviceInfo>> DeviceOptionsUpdated; + /// <summary> + /// Occurs when [camera image uploaded]. + /// </summary> + event EventHandler<GenericEventArgs<CameraImageUploadInfo>> CameraImageUploaded; /// <summary> /// Registers the device. diff --git a/MediaBrowser.Controller/Entities/AdultVideo.cs b/MediaBrowser.Controller/Entities/AdultVideo.cs deleted file mode 100644 index 6c3f7851e..000000000 --- a/MediaBrowser.Controller/Entities/AdultVideo.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace MediaBrowser.Controller.Entities -{ - [Obsolete] - public class AdultVideo : Video, IHasProductionLocations, IHasTaglines - { - public List<string> ProductionLocations { get; set; } - - public List<string> Taglines { get; set; } - - public AdultVideo() - { - Taglines = new List<string>(); - ProductionLocations = new List<string>(); - } - } -} diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs index 00a7e8b67..b9a38d594 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -1,11 +1,11 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Users; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; -using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.Entities.Audio { @@ -181,10 +181,4 @@ namespace MediaBrowser.Controller.Entities.Audio return id; } } - - [Obsolete] - public class MusicAlbumDisc : Folder - { - - } } diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 5cca91202..4cc3f1892 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -129,43 +129,20 @@ namespace MediaBrowser.Controller.Entities.Audio var others = items.Except(songs).ToList(); var totalItems = songs.Count + others.Count; - var percentages = new Dictionary<Guid, double>(totalItems); - - var tasks = new List<Task>(); + var numComplete = 0; // Refresh songs foreach (var item in songs) { - if (tasks.Count >= 2) - { - await Task.WhenAll(tasks).ConfigureAwait(false); - tasks.Clear(); - } - cancellationToken.ThrowIfCancellationRequested(); - var innerProgress = new ActionableProgress<double>(); - // Avoid implicitly captured closure - var currentChild = item; - innerProgress.RegisterAction(p => - { - lock (percentages) - { - percentages[currentChild.Id] = p / 100; - - var percent = percentages.Values.Sum(); - percent /= totalItems; - percent *= 100; - progress.Report(percent); - } - }); - - var taskChild = item; - tasks.Add(Task.Run(async () => await RefreshItem(taskChild, refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false), cancellationToken)); - } + await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); - await Task.WhenAll(tasks).ConfigureAwait(false); - tasks.Clear(); + numComplete++; + double percent = numComplete; + percent /= totalItems; + progress.Report(percent * 100); + } // Refresh current item await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); @@ -175,31 +152,17 @@ namespace MediaBrowser.Controller.Entities.Audio { cancellationToken.ThrowIfCancellationRequested(); - // Avoid implicitly captured closure - var currentChild = item; - await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); - lock (percentages) - { - percentages[currentChild.Id] = 1; - var percent = percentages.Values.Sum(); - percent /= totalItems; - percent *= 100; - progress.Report(percent); - } + numComplete++; + double percent = numComplete; + percent /= totalItems; + progress.Report(percent * 100); } progress.Report(100); } - private async Task RefreshItem(BaseItem item, MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken) - { - await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); - - progress.Report(100); - } - public ArtistInfo GetLookupInfo() { var info = GetItemLookupInfo<ArtistInfo>(); diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 9cc320095..8c13c7e66 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -283,7 +283,17 @@ namespace MediaBrowser.Controller.Entities { get { - return _children ?? (_children = LoadChildrenInternal()); + if (_children == null) + { + lock (_childrenSyncLock) + { + if (_children == null) + { + _children = LoadChildrenInternal(); + } + } + } + return _children; } } @@ -749,28 +759,6 @@ namespace MediaBrowser.Controller.Entities return childrenItems; } - /// <summary> - /// Retrieves the child. - /// </summary> - /// <param name="child">The child.</param> - /// <returns>BaseItem.</returns> - private BaseItem RetrieveChild(Guid child) - { - var item = LibraryManager.GetItemById(child); - - if (item != null) - { - if (item is IByReferenceItem) - { - return LibraryManager.GetOrAddByReferenceItem(item); - } - - item.Parent = this; - } - - return item; - } - private BaseItem RetrieveChild(BaseItem child) { if (child.Id == Guid.Empty) diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index e08d88b02..dea97aed4 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -143,31 +143,19 @@ namespace MediaBrowser.Controller.Entities.Movies var items = GetRecursiveChildren().ToList(); var totalItems = items.Count; - var percentages = new Dictionary<Guid, double>(totalItems); + var numComplete = 0; // Refresh songs foreach (var item in items) { cancellationToken.ThrowIfCancellationRequested(); - var innerProgress = new ActionableProgress<double>(); - // Avoid implicitly captured closure - var currentChild = item; - innerProgress.RegisterAction(p => - { - lock (percentages) - { - percentages[currentChild.Id] = p / 100; - - var percent = percentages.Values.Sum(); - percent /= totalItems; - percent *= 100; - progress.Report(percent); - } - }); - - // Avoid implicitly captured closure - await RefreshItem(item, refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false); + await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); + + numComplete++; + double percent = numComplete; + percent /= totalItems; + progress.Report(percent * 100); } // Refresh current item @@ -176,13 +164,6 @@ namespace MediaBrowser.Controller.Entities.Movies progress.Report(100); } - private async Task RefreshItem(BaseItem item, MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken) - { - await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); - - progress.Report(100); - } - public override bool IsVisible(User user) { if (base.IsVisible(user)) diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index f1436d45e..cd179eb42 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -64,8 +64,7 @@ namespace MediaBrowser.Controller.Entities { CollectionType.Books, CollectionType.HomeVideos, - CollectionType.Photos, - string.Empty + CollectionType.Photos }; var collectionFolder = folder as ICollectionFolder; diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index 1572c1af0..a24a42535 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -409,12 +409,21 @@ namespace MediaBrowser.Controller.Entities private QueryResult<BaseItem> GetMusicLatest(Folder parent, User user, InternalItemsQuery query) { - query.SortBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName }; - query.SortOrder = SortOrder.Descending; + var items = _userViewManager.GetLatestItems(new LatestItemsQuery + { + UserId = user.Id.ToString("N"), + Limit = GetSpecialItemsLimit(), + IncludeItemTypes = new[] { typeof(Audio.Audio).Name }, + ParentId = (parent == null ? null : parent.Id.ToString("N")), + GroupItems = true - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }, i => i is MusicVideo || i is Audio.Audio && FilterItem(i, query)); + }).Select(i => i.Item1); - return PostFilterAndSort(items, parent, GetSpecialItemsLimit(), query); + query.SortBy = new string[] { }; + + //var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }, i => i is MusicVideo || i is Audio.Audio && FilterItem(i, query)); + + return PostFilterAndSort(items, parent, null, query); } private async Task<QueryResult<BaseItem>> GetMovieFolders(Folder parent, User user, InternalItemsQuery query) @@ -741,7 +750,7 @@ namespace MediaBrowser.Controller.Entities private async Task<QueryResult<BaseItem>> GetGameGenreItems(Folder queryParent, Folder displayParent, User user, InternalItemsQuery query) { - var items = GetRecursiveChildren(queryParent, user, new[] {CollectionType.Games}, + var items = GetRecursiveChildren(queryParent, user, new[] { CollectionType.Games }, i => i is Game && i.Genres.Contains(displayParent.Name, StringComparer.OrdinalIgnoreCase)); return GetResult(items, queryParent, query); @@ -1686,7 +1695,7 @@ namespace MediaBrowser.Controller.Entities return parent.GetRecursiveChildren(user); } - private IEnumerable<BaseItem> GetRecursiveChildren(Folder parent, User user, IEnumerable<string> viewTypes, Func<BaseItem,bool> filter) + private IEnumerable<BaseItem> GetRecursiveChildren(Folder parent, User user, IEnumerable<string> viewTypes, Func<BaseItem, bool> filter) { if (parent == null || parent is UserView) { diff --git a/MediaBrowser.Controller/Library/IMusicManager.cs b/MediaBrowser.Controller/Library/IMusicManager.cs index f66f18401..0ce0687cc 100644 --- a/MediaBrowser.Controller/Library/IMusicManager.cs +++ b/MediaBrowser.Controller/Library/IMusicManager.cs @@ -1,6 +1,5 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Playlists; using System.Collections.Generic; namespace MediaBrowser.Controller.Library @@ -13,7 +12,7 @@ namespace MediaBrowser.Controller.Library /// <param name="item">The item.</param> /// <param name="user">The user.</param> /// <returns>IEnumerable{Audio}.</returns> - IEnumerable<Audio> GetInstantMixFromSong(Audio item, User user); + IEnumerable<Audio> GetInstantMixFromItem(BaseItem item, User user); /// <summary> /// Gets the instant mix from artist. /// </summary> @@ -22,20 +21,6 @@ namespace MediaBrowser.Controller.Library /// <returns>IEnumerable{Audio}.</returns> IEnumerable<Audio> GetInstantMixFromArtist(string name, User user); /// <summary> - /// Gets the instant mix from album. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="user">The user.</param> - /// <returns>IEnumerable{Audio}.</returns> - IEnumerable<Audio> GetInstantMixFromAlbum(MusicAlbum item, User user); - /// <summary> - /// Gets the instant mix from playlist. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="user">The user.</param> - /// <returns>IEnumerable<Audio>.</returns> - IEnumerable<Audio> GetInstantMixFromPlaylist(Playlist item, User user); - /// <summary> /// Gets the instant mix from genre. /// </summary> /// <param name="genres">The genres.</param> diff --git a/MediaBrowser.Controller/Library/IUserViewManager.cs b/MediaBrowser.Controller/Library/IUserViewManager.cs index 727ccd00a..f55c17924 100644 --- a/MediaBrowser.Controller/Library/IUserViewManager.cs +++ b/MediaBrowser.Controller/Library/IUserViewManager.cs @@ -1,5 +1,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Library; +using MediaBrowser.Model.Querying; +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -16,5 +18,7 @@ namespace MediaBrowser.Controller.Library Task<UserView> GetUserView(string type, string sortName, CancellationToken cancellationToken); Task<UserView> GetUserView(string category, string type, User user, string sortName, CancellationToken cancellationToken); + + List<Tuple<BaseItem, List<BaseItem>>> GetLatestItems(LatestItemsQuery request); } } diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 0667730fd..867d9b0c5 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -101,6 +101,7 @@ <Compile Include="Collections\ICollectionManager.cs" /> <Compile Include="Connect\IConnectManager.cs" /> <Compile Include="Connect\UserLinkResult.cs" /> + <Compile Include="Devices\CameraImageUploadInfo.cs" /> <Compile Include="Devices\IDeviceManager.cs" /> <Compile Include="Devices\IDeviceRepository.cs" /> <Compile Include="Dlna\ControlRequest.cs" /> @@ -117,7 +118,6 @@ <Compile Include="Drawing\ImageStream.cs" /> <Compile Include="Dto\DtoOptions.cs" /> <Compile Include="Dto\IDtoService.cs" /> - <Compile Include="Entities\AdultVideo.cs" /> <Compile Include="Entities\Audio\IHasAlbumArtist.cs" /> <Compile Include="Entities\Audio\IHasMusicGenres.cs" /> <Compile Include="Entities\Book.cs" /> diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index 071a9bf93..0f0c6a97e 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -113,38 +113,17 @@ namespace MediaBrowser.Controller.Playlists return LibraryManager.Sort(items, user, new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }, SortOrder.Ascending); } - // Grab these explicitly to avoid the sorting that will happen below - var collection = item as BoxSet; - if (collection != null) - { - var items = user == null - ? collection.Children - : collection.GetChildren(user, true); - - return items - .Where(m => !m.IsFolder); - } - - // Grab these explicitly to avoid the sorting that will happen below - var season = item as Season; - if (season != null) - { - var items = user == null - ? season.Children - : season.GetChildren(user, true); - - return items - .Where(m => !m.IsFolder); - } - var folder = item as Folder; - if (folder != null) { var items = user == null ? folder.GetRecursiveChildren(m => !m.IsFolder) : folder.GetRecursiveChildren(user, m => !m.IsFolder); + if (folder.IsPreSorted) + { + return items; + } return LibraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending); } diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index f62cf88b7..c15c76004 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -168,7 +168,7 @@ namespace MediaBrowser.Model.Configuration /// <value>The dashboard source path.</value> public string DashboardSourcePath { get; set; } - public bool StoreArtistsInMetadata { get; set; } + public bool MergeMetadataAndImagesByName { get; set; } public bool EnableStandaloneMetadata { get; set; } /// <summary> diff --git a/MediaBrowser.Model/Notifications/NotificationType.cs b/MediaBrowser.Model/Notifications/NotificationType.cs index 34e4d32b5..269e27a4f 100644 --- a/MediaBrowser.Model/Notifications/NotificationType.cs +++ b/MediaBrowser.Model/Notifications/NotificationType.cs @@ -18,6 +18,7 @@ namespace MediaBrowser.Model.Notifications NewLibraryContent, NewLibraryContentMultiple, ServerRestartRequired, - TaskFailed + TaskFailed, + CameraImageUploaded } }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs b/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs index 5f7ccec4b..bb9e9057a 100644 --- a/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs +++ b/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs @@ -88,9 +88,12 @@ namespace MediaBrowser.Server.Implementations.Configuration /// </summary> private void UpdateItemsByNamePath() { - ((ServerApplicationPaths)ApplicationPaths).ItemsByNamePath = string.IsNullOrEmpty(Configuration.ItemsByNamePath) ? - null : - Configuration.ItemsByNamePath; + if (!Configuration.MergeMetadataAndImagesByName) + { + ((ServerApplicationPaths)ApplicationPaths).ItemsByNamePath = string.IsNullOrEmpty(Configuration.ItemsByNamePath) ? + null : + Configuration.ItemsByNamePath; + } } /// <summary> @@ -101,6 +104,11 @@ namespace MediaBrowser.Server.Implementations.Configuration ((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = string.IsNullOrEmpty(Configuration.MetadataPath) ? GetInternalMetadataPath() : Configuration.MetadataPath; + + if (Configuration.MergeMetadataAndImagesByName) + { + ((ServerApplicationPaths)ApplicationPaths).ItemsByNamePath = ((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath; + } } private string GetInternalMetadataPath() diff --git a/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs b/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs index e057ec5cd..3211f88d5 100644 --- a/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs +++ b/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs @@ -27,6 +27,8 @@ namespace MediaBrowser.Server.Implementations.Devices private readonly IConfigurationManager _config; private readonly ILogger _logger; + public event EventHandler<GenericEventArgs<CameraImageUploadInfo>> CameraImageUploaded; + /// <summary> /// Occurs when [device options updated]. /// </summary> @@ -116,7 +118,7 @@ namespace MediaBrowser.Server.Implementations.Devices { devices = devices.Where(i => CanAccessDevice(query.UserId, i.Id)); } - + var array = devices.ToArray(); return new QueryResult<DeviceInfo> { @@ -137,7 +139,8 @@ namespace MediaBrowser.Server.Implementations.Devices public async Task AcceptCameraUpload(string deviceId, Stream stream, LocalFileInfo file) { - var path = GetUploadPath(deviceId); + var device = GetDevice(deviceId); + var path = GetUploadPath(device); if (!string.IsNullOrWhiteSpace(file.Album)) { @@ -163,11 +166,27 @@ namespace MediaBrowser.Server.Implementations.Devices { _libraryMonitor.ReportFileSystemChangeComplete(path, true); } + + if (CameraImageUploaded != null) + { + EventHelper.FireEventIfNotNull(CameraImageUploaded, this, new GenericEventArgs<CameraImageUploadInfo> + { + Argument = new CameraImageUploadInfo + { + Device = device, + FileInfo = file + } + }, _logger); + } } private string GetUploadPath(string deviceId) { - var device = GetDevice(deviceId); + return GetUploadPath(GetDevice(deviceId)); + } + + private string GetUploadPath(DeviceInfo device) + { if (!string.IsNullOrWhiteSpace(device.CameraUploadPath)) { return device.CameraUploadPath; diff --git a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs index 2d824f36c..37bca4ddb 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs @@ -3,6 +3,7 @@ using MediaBrowser.Common.Plugins; using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Common.Updates; using MediaBrowser.Controller; +using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; @@ -44,8 +45,9 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications private readonly object _libraryChangedSyncLock = new object(); private readonly IConfigurationManager _config; + private readonly IDeviceManager _deviceManager; - public Notifications(IInstallationManager installationManager, IUserManager userManager, ILogger logger, ITaskManager taskManager, INotificationManager notificationManager, ILibraryManager libraryManager, ISessionManager sessionManager, IServerApplicationHost appHost, IConfigurationManager config) + public Notifications(IInstallationManager installationManager, IUserManager userManager, ILogger logger, ITaskManager taskManager, INotificationManager notificationManager, ILibraryManager libraryManager, ISessionManager sessionManager, IServerApplicationHost appHost, IConfigurationManager config, IDeviceManager deviceManager) { _installationManager = installationManager; _userManager = userManager; @@ -56,6 +58,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications _sessionManager = sessionManager; _appHost = appHost; _config = config; + _deviceManager = deviceManager; } public void Run() @@ -74,6 +77,21 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications _appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged; _appHost.HasUpdateAvailableChanged += _appHost_HasUpdateAvailableChanged; _appHost.ApplicationUpdated += _appHost_ApplicationUpdated; + _deviceManager.CameraImageUploaded +=_deviceManager_CameraImageUploaded; + } + + async void _deviceManager_CameraImageUploaded(object sender, GenericEventArgs<CameraImageUploadInfo> e) + { + var type = NotificationType.CameraImageUploaded.ToString(); + + var notification = new NotificationRequest + { + NotificationType = type + }; + + notification.Variables["DeviceName"] = e.Argument.Device.Name; + + await SendNotification(notification).ConfigureAwait(false); } async void _appHost_ApplicationUpdated(object sender, GenericEventArgs<PackageVersionInfo> e) @@ -451,6 +469,8 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications _appHost.HasPendingRestartChanged -= _appHost_HasPendingRestartChanged; _appHost.HasUpdateAvailableChanged -= _appHost_HasUpdateAvailableChanged; _appHost.ApplicationUpdated -= _appHost_ApplicationUpdated; + + _deviceManager.CameraImageUploaded -= _deviceManager_CameraImageUploaded; } private void DisposeLibraryUpdateTimer() diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs index ce6224295..d501d1210 100644 --- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs +++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs @@ -566,7 +566,10 @@ namespace MediaBrowser.Server.Implementations.IO .Distinct() .ToList(); - foreach (var p in paths) Logger.Info(p + " reports change."); + foreach (var p in paths) + { + Logger.Info(p + " reports change."); + } // If the root folder changed, run the library task so the user can see it if (itemsToRefresh.Any(i => i is AggregateFolder)) diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 0ebd1aace..124bb396e 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -219,11 +219,7 @@ namespace MediaBrowser.Server.Implementations.Library /// <summary> /// The _root folder sync lock /// </summary> - private object _rootFolderSyncLock = new object(); - /// <summary> - /// The _root folder initialized - /// </summary> - private bool _rootFolderInitialized; + private readonly object _rootFolderSyncLock = new object(); /// <summary> /// Gets the root folder. /// </summary> @@ -232,17 +228,17 @@ namespace MediaBrowser.Server.Implementations.Library { get { - LazyInitializer.EnsureInitialized(ref _rootFolder, ref _rootFolderInitialized, ref _rootFolderSyncLock, CreateRootFolder); - return _rootFolder; - } - private set - { - _rootFolder = value; - - if (value == null) + if (_rootFolder == null) { - _rootFolderInitialized = false; + lock (_rootFolderSyncLock) + { + if (_rootFolder == null) + { + _rootFolder = CreateRootFolder(); + } + } } + return _rootFolder; } } @@ -849,11 +845,6 @@ namespace MediaBrowser.Server.Implementations.Library { get { - if (ConfigurationManager.Configuration.StoreArtistsInMetadata) - { - return Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "artists"); - } - return Path.Combine(ConfigurationManager.ApplicationPaths.ItemsByNamePath, "artists"); } } diff --git a/MediaBrowser.Server.Implementations/Library/MusicManager.cs b/MediaBrowser.Server.Implementations/Library/MusicManager.cs index 5a7533ad2..7733e7d37 100644 --- a/MediaBrowser.Server.Implementations/Library/MusicManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MusicManager.cs @@ -83,5 +83,40 @@ namespace MediaBrowser.Server.Implementations.Library .Take(100) .OrderBy(i => Guid.NewGuid()); } + + public IEnumerable<Audio> GetInstantMixFromItem(BaseItem item, User user) + { + var genre = item as MusicGenre; + if (genre != null) + { + return GetInstantMixFromGenres(new[] { item.Name }, user); + } + + var playlist = item as Playlist; + if (playlist != null) + { + return GetInstantMixFromPlaylist(playlist, user); + } + + var album = item as MusicAlbum; + if (album != null) + { + return GetInstantMixFromAlbum(album, user); + } + + var artist = item as MusicArtist; + if (artist != null) + { + return GetInstantMixFromArtist(artist.Name, user); + } + + var song = item as Audio; + if (song != null) + { + return GetInstantMixFromSong(song, user); + } + + return new Audio[] { }; + } } } diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs index d8c5f85c0..2ec9e8a4f 100644 --- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs @@ -16,6 +16,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MoreLinq; namespace MediaBrowser.Server.Implementations.Library { @@ -56,7 +57,9 @@ namespace MediaBrowser.Server.Implementations.Library var excludeFolderIds = user.Configuration.ExcludeFoldersFromGrouping.Select(i => new Guid(i)).ToList(); - var standaloneFolders = folders.Where(i => UserView.IsExcludedFromGrouping(i) || excludeFolderIds.Contains(i.Id)).ToList(); + var standaloneFolders = folders + .Where(i => UserView.IsExcludedFromGrouping(i) || excludeFolderIds.Contains(i.Id)) + .ToList(); var foldersWithViewTypes = folders .Except(standaloneFolders) @@ -164,5 +167,141 @@ namespace MediaBrowser.Server.Implementations.Library return _libraryManager.GetNamedView(name, type, sortName, cancellationToken); } + + public List<Tuple<BaseItem, List<BaseItem>>> GetLatestItems(LatestItemsQuery request) + { + var user = _userManager.GetUserById(request.UserId); + + var includeTypes = request.IncludeItemTypes; + + var currentUser = user; + + Func<BaseItem, bool> filter = i => + { + if (includeTypes.Length > 0) + { + if (!includeTypes.Contains(i.GetType().Name, StringComparer.OrdinalIgnoreCase)) + { + return false; + } + } + + if (request.IsPlayed.HasValue) + { + var val = request.IsPlayed.Value; + if (i.IsPlayed(currentUser) != val) + { + return false; + } + } + + return i.LocationType != LocationType.Virtual && !i.IsFolder; + }; + + // Avoid implicitly captured closure + var libraryItems = string.IsNullOrEmpty(request.ParentId) && user != null ? + GetItemsConfiguredForLatest(user, filter) : + GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId, filter); + + libraryItems = libraryItems.OrderByDescending(i => i.DateCreated); + + if (request.IsPlayed.HasValue) + { + var takeLimit = (request.Limit ?? 20) * 20; + libraryItems = libraryItems.Take(takeLimit); + } + + // Avoid implicitly captured closure + var items = libraryItems + .ToList(); + + var list = new List<Tuple<BaseItem, List<BaseItem>>>(); + + foreach (var item in items) + { + // Only grab the index container for media + var container = item.IsFolder || !request.GroupItems ? null : item.LatestItemsIndexContainer; + + if (container == null) + { + list.Add(new Tuple<BaseItem, List<BaseItem>>(null, new List<BaseItem> { item })); + } + else + { + var current = list.FirstOrDefault(i => i.Item1 != null && i.Item1.Id == container.Id); + + if (current != null) + { + current.Item2.Add(item); + } + else + { + list.Add(new Tuple<BaseItem, List<BaseItem>>(container, new List<BaseItem> { item })); + } + } + + if (list.Count >= request.Limit) + { + break; + } + } + + return list; + } + + protected IList<BaseItem> GetAllLibraryItems(string userId, IUserManager userManager, ILibraryManager libraryManager, string parentId, Func<BaseItem, bool> filter) + { + if (!string.IsNullOrEmpty(parentId)) + { + var folder = (Folder)libraryManager.GetItemById(new Guid(parentId)); + + if (!string.IsNullOrWhiteSpace(userId)) + { + var user = userManager.GetUserById(userId); + + if (user == null) + { + throw new ArgumentException("User not found"); + } + + return folder + .GetRecursiveChildren(user, filter) + .ToList(); + } + + return folder + .GetRecursiveChildren(filter); + } + if (!string.IsNullOrWhiteSpace(userId)) + { + var user = userManager.GetUserById(userId); + + if (user == null) + { + throw new ArgumentException("User not found"); + } + + return user + .RootFolder + .GetRecursiveChildren(user, filter) + .ToList(); + } + + return libraryManager + .RootFolder + .GetRecursiveChildren(filter); + } + + private IEnumerable<BaseItem> GetItemsConfiguredForLatest(User user, Func<BaseItem, bool> filter) + { + // Avoid implicitly captured closure + var currentUser = user; + + return user.RootFolder.GetChildren(user, true) + .OfType<Folder>() + .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N"))) + .SelectMany(i => i.GetRecursiveChildren(currentUser, filter)) + .DistinctBy(i => i.Id); + } } } diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index 15bc62412..d75a1bc3f 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -55,6 +55,7 @@ "HeaderAudio": "Audio", "HeaderVideo": "Video", "HeaderPaths": "Paths", + "CategorySync": "Sync", "HeaderSyncRequiresSupporterMembership": "Sync Requires a Supporter Membership", "HeaderEnjoyDayTrial": "Enjoy a 14 Day Free Trial", "LabelSyncTempPath": "Temporary file path:", @@ -669,6 +670,7 @@ "NotificationOptionInstallationFailed": "Installation failure", "NotificationOptionNewLibraryContent": "New content added", "NotificationOptionNewLibraryContentMultiple": "New content added (multiple)", + "NotificationOptionCameraImageUploaded": "Camera image uploaded", "SendNotificationHelp": "By default, notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", "NotificationOptionServerRestartRequired": "Server restart required", "LabelNotificationEnabled": "Enable this notification", @@ -893,7 +895,7 @@ "OptionCommunityMostWatchedSort": "Most Watched", "TabNextUp": "Next Up", "HeaderBecomeMediaBrowserSupporter": "Become a Media Browser Supporter", - "TextAccessPremiumFeatures": "Enjoy Premium Features", + "TextEnjoyBonusFeatures": "Enjoy Bonus Features", "MessageNoMovieSuggestionsAvailable": "No movie suggestions are currently available. Start watching and rating your movies, and then come back to view your recommendations.", "MessageNoCollectionsAvailable": "Collections allow you to enjoy personalized groupings of Movies, Series, Albums, Books and Games. Click the + button to start creating Collections.", "MessageNoPlaylistsAvailable": "Playlists allow you to create lists of content to play consecutively at a time. To add items to playlists, right click or tap and hold, then select Add to Playlist.", @@ -957,7 +959,7 @@ "OptionLatestTvRecordings": "Latest recordings", "LabelProtocolInfo": "Protocol info:", "LabelProtocolInfoHelp": "The value that will be used when responding to GetProtocolInfo requests from the device.", - "TabKodiMetadata": "Kodi", + "TabNfo": "Nfo", "HeaderKodiMetadataHelp": "Media Browser includes native support for Kodi Nfo metadata and images. To enable or disable Kodi metadata, use the Advanced tab to configure options for your media types.", "LabelKodiMetadataUser": "Sync user watch data to nfo's for:", "LabelKodiMetadataUserHelp": "Enable this to keep watch data in sync between Media Browser and Kodi.", diff --git a/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs b/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs index 405bf407a..d8acbe06c 100644 --- a/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs +++ b/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs @@ -1,7 +1,6 @@ using MediaBrowser.Controller; using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Notifications; -using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Notifications; using System; using System.Collections.Generic; @@ -137,6 +136,13 @@ namespace MediaBrowser.Server.Implementations.Notifications Type = NotificationType.VideoPlaybackStopped.ToString(), DefaultTitle = "{UserName} has finished playing {ItemName} on {DeviceName}.", Variables = new List<string>{"UserName", "ItemName", "DeviceName", "AppName"} + }, + + new NotificationTypeInfo + { + Type = NotificationType.CameraImageUploaded.ToString(), + DefaultTitle = "A new camera image has been uploaded from {DeviceName}.", + Variables = new List<string>{"DeviceName"} } }; @@ -171,10 +177,14 @@ namespace MediaBrowser.Server.Implementations.Notifications { note.Category = _localization.GetLocalizedString("CategoryUser"); } - else if (note.Type.IndexOf("Plugin", StringComparison.OrdinalIgnoreCase) != -1) + else if (note.Type.IndexOf("Plugin", StringComparison.OrdinalIgnoreCase) != -1) { note.Category = _localization.GetLocalizedString("CategoryPlugin"); } + else if (note.Type.IndexOf("CameraImageUploaded", StringComparison.OrdinalIgnoreCase) != -1) + { + note.Category = _localization.GetLocalizedString("CategorySync"); + } else { note.Category = _localization.GetLocalizedString("CategorySystem"); diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index b4be3d788..0613cda30 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -870,14 +870,14 @@ namespace MediaBrowser.Server.Implementations.Session { if (items.Any(i => !session.QueueableMediaTypes.Contains(i.MediaType, StringComparer.OrdinalIgnoreCase))) { - throw new ArgumentException(string.Format("{0} is unable to queue the requested media type.", session.DeviceName ?? session.Id.ToString())); + throw new ArgumentException(string.Format("{0} is unable to queue the requested media type.", session.DeviceName ?? session.Id)); } } else { if (items.Any(i => !session.PlayableMediaTypes.Contains(i.MediaType, StringComparer.OrdinalIgnoreCase))) { - throw new ArgumentException(string.Format("{0} is unable to play the requested media type.", session.DeviceName ?? session.Id.ToString())); + throw new ArgumentException(string.Format("{0} is unable to play the requested media type.", session.DeviceName ?? session.Id)); } } @@ -895,6 +895,19 @@ namespace MediaBrowser.Server.Implementations.Session { var item = _libraryManager.GetItemById(new Guid(id)); + var byName = item as IItemByName; + + if (byName != null) + { + var items = user == null ? + _libraryManager.RootFolder.GetRecursiveChildren(i => !i.IsFolder && byName.ItemFilter(i)) : + user.RootFolder.GetRecursiveChildren(user, i => !i.IsFolder && byName.ItemFilter(i)); + + items = items.OrderBy(i => i.SortName); + + return items; + } + if (item.IsFolder) { var folder = (Folder)item; @@ -913,37 +926,9 @@ namespace MediaBrowser.Server.Implementations.Session private IEnumerable<BaseItem> TranslateItemForInstantMix(string id, User user) { - var item = _libraryManager.GetItemById(new Guid(id)); - - var audio = item as Audio; - - if (audio != null) - { - return _musicManager.GetInstantMixFromSong(audio, user); - } - - var artist = item as MusicArtist; - - if (artist != null) - { - return _musicManager.GetInstantMixFromArtist(artist.Name, user); - } - - var album = item as MusicAlbum; - - if (album != null) - { - return _musicManager.GetInstantMixFromAlbum(album, user); - } - - var genre = item as MusicGenre; - - if (genre != null) - { - return _musicManager.GetInstantMixFromGenres(new[] { genre.Name }, user); - } + var item = _libraryManager.GetItemById(id); - return new BaseItem[] { }; + return _musicManager.GetInstantMixFromItem(item, user); } public Task SendBrowseCommand(string controllingSessionId, string sessionId, BrowseRequest command, CancellationToken cancellationToken) diff --git a/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs b/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs index 11dfb4791..6700e9054 100644 --- a/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs +++ b/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs @@ -93,7 +93,8 @@ namespace MediaBrowser.Server.Implementations.TV return FilterSeries(request, series) .AsParallel() .Select(i => GetNextUp(i, currentUser)) - .Where(i => i.Item1 != null) + // Include if an episode was found, and either the series is not unwatched or the specific series was requested + .Where(i => i.Item1 != null && (!i.Item3 || !string.IsNullOrWhiteSpace(request.SeriesId))) .OrderByDescending(i => { var episode = i.Item1; @@ -123,7 +124,7 @@ namespace MediaBrowser.Server.Implementations.TV /// <param name="series">The series.</param> /// <param name="user">The user.</param> /// <returns>Task{Episode}.</returns> - private Tuple<Episode, DateTime> GetNextUp(Series series, User user) + private Tuple<Episode, DateTime, bool> GetNextUp(Series series, User user) { // Get them in display order, then reverse var allEpisodes = series.GetSeasons(user, true, true) @@ -162,13 +163,13 @@ namespace MediaBrowser.Server.Implementations.TV if (lastWatched != null) { - return new Tuple<Episode, DateTime>(nextUp, lastWatchedDate); + return new Tuple<Episode, DateTime, bool>(nextUp, lastWatchedDate, false); } var firstEpisode = allEpisodes.LastOrDefault(i => i.LocationType != LocationType.Virtual && !i.IsPlayed(user)); // Return the first episode - return new Tuple<Episode, DateTime>(firstEpisode, DateTime.MinValue); + return new Tuple<Episode, DateTime, bool>(firstEpisode, DateTime.MinValue, true); } private IEnumerable<Series> FilterSeries(NextUpQuery request, IEnumerable<Series> items) diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs index 340b02be7..3eb98e95d 100644 --- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs +++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs @@ -441,7 +441,7 @@ namespace MediaBrowser.WebDashboard.Api "metadataconfigurationpage.js", "metadataimagespage.js", "metadatasubtitles.js", - "metadatakodi.js", + "metadatanfo.js", "moviegenres.js", "moviecollections.js", "movies.js", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 7613195ea..72f3a5757 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -480,7 +480,7 @@ <Content Include="dashboard-ui\librarypathmapping.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\metadatakodi.html">
+ <Content Include="dashboard-ui\metadatanfo.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\mypreferencesdisplay.html">
@@ -795,7 +795,7 @@ <Content Include="dashboard-ui\scripts\librarypathmapping.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\scripts\metadatakodi.js">
+ <Content Include="dashboard-ui\scripts\metadatanfo.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\scripts\mypreferencesdisplay.js">
|
