aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MediaBrowser.Api/Music/InstantMixService.cs18
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs2
-rw-r--r--MediaBrowser.Api/StartupWizardService.cs2
-rw-r--r--MediaBrowser.Api/UserLibrary/UserLibraryService.cs96
-rw-r--r--MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs72
-rw-r--r--MediaBrowser.Controller/Devices/CameraImageUploadInfo.cs10
-rw-r--r--MediaBrowser.Controller/Devices/IDeviceManager.cs5
-rw-r--r--MediaBrowser.Controller/Entities/AdultVideo.cs19
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs8
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicArtist.cs59
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs34
-rw-r--r--MediaBrowser.Controller/Entities/Movies/BoxSet.cs33
-rw-r--r--MediaBrowser.Controller/Entities/UserView.cs3
-rw-r--r--MediaBrowser.Controller/Entities/UserViewBuilder.cs21
-rw-r--r--MediaBrowser.Controller/Library/IMusicManager.cs17
-rw-r--r--MediaBrowser.Controller/Library/IUserViewManager.cs4
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj2
-rw-r--r--MediaBrowser.Controller/Playlists/Playlist.cs29
-rw-r--r--MediaBrowser.Model/Configuration/ServerConfiguration.cs2
-rw-r--r--MediaBrowser.Model/Notifications/NotificationType.cs3
-rw-r--r--MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs14
-rw-r--r--MediaBrowser.Server.Implementations/Devices/DeviceManager.cs25
-rw-r--r--MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs22
-rw-r--r--MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs5
-rw-r--r--MediaBrowser.Server.Implementations/Library/LibraryManager.cs29
-rw-r--r--MediaBrowser.Server.Implementations/Library/MusicManager.cs35
-rw-r--r--MediaBrowser.Server.Implementations/Library/UserViewManager.cs141
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/server.json6
-rw-r--r--MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs14
-rw-r--r--MediaBrowser.Server.Implementations/Session/SessionManager.cs49
-rw-r--r--MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs9
-rw-r--r--MediaBrowser.WebDashboard/Api/PackageCreator.cs2
-rw-r--r--MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj4
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&lt;Audio&gt;.</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">