mirror of https://github.com/jellyfin/jellyfin.git
add option to merge metadata and IBN paths
This commit is contained in:
parent
91416cb8a8
commit
63f3cf97da
|
@ -73,44 +73,44 @@ namespace MediaBrowser.Api.Music
|
||||||
|
|
||||||
public object Get(GetInstantMixFromArtistId request)
|
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 user = _userManager.GetUserById(request.UserId.Value);
|
||||||
|
|
||||||
var items = _musicManager.GetInstantMixFromArtist(item.Name, user);
|
var items = _musicManager.GetInstantMixFromItem(item, user);
|
||||||
|
|
||||||
return GetResult(items, user, request);
|
return GetResult(items, user, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Get(GetInstantMixFromMusicGenreId 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 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);
|
return GetResult(items, user, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Get(GetInstantMixFromSong 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 user = _userManager.GetUserById(request.UserId.Value);
|
||||||
|
|
||||||
var items = _musicManager.GetInstantMixFromSong(item, user);
|
var items = _musicManager.GetInstantMixFromItem(item, user);
|
||||||
|
|
||||||
return GetResult(items, user, request);
|
return GetResult(items, user, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Get(GetInstantMixFromAlbum 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 user = _userManager.GetUserById(request.UserId.Value);
|
||||||
|
|
||||||
var items = _musicManager.GetInstantMixFromAlbum(album, user);
|
var items = _musicManager.GetInstantMixFromItem(album, user);
|
||||||
|
|
||||||
return GetResult(items, user, request);
|
return GetResult(items, user, request);
|
||||||
}
|
}
|
||||||
|
@ -121,7 +121,7 @@ namespace MediaBrowser.Api.Music
|
||||||
|
|
||||||
var user = _userManager.GetUserById(request.UserId.Value);
|
var user = _userManager.GetUserById(request.UserId.Value);
|
||||||
|
|
||||||
var items = _musicManager.GetInstantMixFromPlaylist(playlist, user);
|
var items = _musicManager.GetInstantMixFromItem(playlist, user);
|
||||||
|
|
||||||
return GetResult(items, user, request);
|
return GetResult(items, user, request);
|
||||||
}
|
}
|
||||||
|
|
|
@ -824,7 +824,7 @@ namespace MediaBrowser.Api.Playback
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ namespace MediaBrowser.Api
|
||||||
{
|
{
|
||||||
_config.Configuration.IsStartupWizardCompleted = true;
|
_config.Configuration.IsStartupWizardCompleted = true;
|
||||||
_config.Configuration.EnableLocalizedGuids = true;
|
_config.Configuration.EnableLocalizedGuids = true;
|
||||||
_config.Configuration.StoreArtistsInMetadata = true;
|
_config.Configuration.MergeMetadataAndImagesByName = true;
|
||||||
_config.Configuration.EnableStandaloneMetadata = true;
|
_config.Configuration.EnableStandaloneMetadata = true;
|
||||||
_config.Configuration.EnableLibraryMetadataSubFolder = true;
|
_config.Configuration.EnableLibraryMetadataSubFolder = true;
|
||||||
_config.SaveConfiguration();
|
_config.SaveConfiguration();
|
||||||
|
|
|
@ -228,7 +228,7 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The user id.</value>
|
/// <value>The user id.</value>
|
||||||
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
[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")]
|
[ApiMember(Name = "Limit", Description = "Limit", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||||
public int Limit { get; set; }
|
public int Limit { get; set; }
|
||||||
|
@ -304,81 +304,15 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(request.UserId);
|
var user = _userManager.GetUserById(request.UserId);
|
||||||
|
|
||||||
var includeTypes = string.IsNullOrWhiteSpace(request.IncludeItemTypes)
|
var list = _userViewManager.GetLatestItems(new LatestItemsQuery
|
||||||
? new string[] { }
|
|
||||||
: request.IncludeItemTypes.Split(',');
|
|
||||||
|
|
||||||
var currentUser = user;
|
|
||||||
|
|
||||||
Func<BaseItem, bool> filter = i =>
|
|
||||||
{
|
{
|
||||||
if (includeTypes.Length > 0)
|
GroupItems = request.GroupItems,
|
||||||
{
|
IncludeItemTypes = (request.IncludeItemTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(),
|
||||||
if (!includeTypes.Contains(i.GetType().Name, StringComparer.OrdinalIgnoreCase))
|
IsPlayed = request.IsPlayed,
|
||||||
{
|
Limit = request.Limit,
|
||||||
return false;
|
ParentId = request.ParentId,
|
||||||
}
|
UserId = request.UserId
|
||||||
}
|
});
|
||||||
|
|
||||||
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;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var options = GetDtoOptions(request);
|
var options = GetDtoOptions(request);
|
||||||
|
|
||||||
|
@ -403,18 +337,6 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
return ToOptimizedResult(dtos.ToList());
|
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)
|
public async Task<object> Get(GetUserViews request)
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(request.UserId);
|
var user = _userManager.GetUserById(request.UserId);
|
||||||
|
|
|
@ -108,13 +108,9 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private TaskResult _lastExecutionResult;
|
private TaskResult _lastExecutionResult;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The _last execution resultinitialized
|
|
||||||
/// </summary>
|
|
||||||
private bool _lastExecutionResultinitialized;
|
|
||||||
/// <summary>
|
|
||||||
/// The _last execution result sync lock
|
/// The _last execution result sync lock
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private object _lastExecutionResultSyncLock = new object();
|
private readonly object _lastExecutionResultSyncLock = new object();
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the last execution result.
|
/// Gets the last execution result.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -123,38 +119,39 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
LazyInitializer.EnsureInitialized(ref _lastExecutionResult, ref _lastExecutionResultinitialized, ref _lastExecutionResultSyncLock, () =>
|
if (_lastExecutionResult == null)
|
||||||
{
|
{
|
||||||
var path = GetHistoryFilePath();
|
lock (_lastExecutionResultSyncLock)
|
||||||
|
{
|
||||||
|
if (_lastExecutionResult == null)
|
||||||
|
{
|
||||||
|
var path = GetHistoryFilePath();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return JsonSerializer.DeserializeFromFile<TaskResult>(path);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (DirectoryNotFoundException)
|
}
|
||||||
{
|
|
||||||
// File doesn't exist. No biggie
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
catch (FileNotFoundException)
|
|
||||||
{
|
|
||||||
// File doesn't exist. No biggie
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.ErrorException("Error deserializing {0}", ex, path);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return _lastExecutionResult;
|
return _lastExecutionResult;
|
||||||
}
|
}
|
||||||
private set
|
private set
|
||||||
{
|
{
|
||||||
_lastExecutionResult = value;
|
_lastExecutionResult = value;
|
||||||
|
|
||||||
_lastExecutionResultinitialized = value != null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,13 +224,9 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private IEnumerable<ITaskTrigger> _triggers;
|
private IEnumerable<ITaskTrigger> _triggers;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The _triggers initialized
|
|
||||||
/// </summary>
|
|
||||||
private bool _triggersInitialized;
|
|
||||||
/// <summary>
|
|
||||||
/// The _triggers sync lock
|
/// The _triggers sync lock
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private object _triggersSyncLock = new object();
|
private readonly object _triggersSyncLock = new object();
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the triggers that define when the task will run
|
/// Gets the triggers that define when the task will run
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -243,7 +236,16 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
LazyInitializer.EnsureInitialized(ref _triggers, ref _triggersInitialized, ref _triggersSyncLock, LoadTriggers);
|
if (_triggers == null)
|
||||||
|
{
|
||||||
|
lock (_triggersSyncLock)
|
||||||
|
{
|
||||||
|
if (_triggers == null)
|
||||||
|
{
|
||||||
|
_triggers = LoadTriggers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return _triggers;
|
return _triggers;
|
||||||
}
|
}
|
||||||
|
@ -262,8 +264,6 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||||
|
|
||||||
_triggers = value.ToList();
|
_triggers = value.ToList();
|
||||||
|
|
||||||
_triggersInitialized = true;
|
|
||||||
|
|
||||||
ReloadTriggerEvents(false);
|
ReloadTriggerEvents(false);
|
||||||
|
|
||||||
SaveTriggers(_triggers);
|
SaveTriggers(_triggers);
|
||||||
|
|
|
@ -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; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,6 @@ using MediaBrowser.Model.Events;
|
||||||
using MediaBrowser.Model.Querying;
|
using MediaBrowser.Model.Querying;
|
||||||
using MediaBrowser.Model.Session;
|
using MediaBrowser.Model.Session;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
@ -15,6 +14,10 @@ namespace MediaBrowser.Controller.Devices
|
||||||
/// Occurs when [device options updated].
|
/// Occurs when [device options updated].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event EventHandler<GenericEventArgs<DeviceInfo>> DeviceOptionsUpdated;
|
event EventHandler<GenericEventArgs<DeviceInfo>> DeviceOptionsUpdated;
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when [camera image uploaded].
|
||||||
|
/// </summary>
|
||||||
|
event EventHandler<GenericEventArgs<CameraImageUploadInfo>> CameraImageUploaded;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers the device.
|
/// Registers the device.
|
||||||
|
|
|
@ -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>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +1,11 @@
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Configuration;
|
using MediaBrowser.Model.Configuration;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Users;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
using MediaBrowser.Model.Users;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities.Audio
|
namespace MediaBrowser.Controller.Entities.Audio
|
||||||
{
|
{
|
||||||
|
@ -181,10 +181,4 @@ namespace MediaBrowser.Controller.Entities.Audio
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete]
|
|
||||||
public class MusicAlbumDisc : Folder
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,44 +129,21 @@ namespace MediaBrowser.Controller.Entities.Audio
|
||||||
var others = items.Except(songs).ToList();
|
var others = items.Except(songs).ToList();
|
||||||
|
|
||||||
var totalItems = songs.Count + others.Count;
|
var totalItems = songs.Count + others.Count;
|
||||||
var percentages = new Dictionary<Guid, double>(totalItems);
|
var numComplete = 0;
|
||||||
|
|
||||||
var tasks = new List<Task>();
|
|
||||||
|
|
||||||
// Refresh songs
|
// Refresh songs
|
||||||
foreach (var item in songs)
|
foreach (var item in songs)
|
||||||
{
|
{
|
||||||
if (tasks.Count >= 2)
|
|
||||||
{
|
|
||||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
|
||||||
tasks.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
var innerProgress = new ActionableProgress<double>();
|
|
||||||
|
|
||||||
// Avoid implicitly captured closure
|
await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
|
||||||
var currentChild = item;
|
|
||||||
innerProgress.RegisterAction(p =>
|
|
||||||
{
|
|
||||||
lock (percentages)
|
|
||||||
{
|
|
||||||
percentages[currentChild.Id] = p / 100;
|
|
||||||
|
|
||||||
var percent = percentages.Values.Sum();
|
numComplete++;
|
||||||
percent /= totalItems;
|
double percent = numComplete;
|
||||||
percent *= 100;
|
percent /= totalItems;
|
||||||
progress.Report(percent);
|
progress.Report(percent * 100);
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var taskChild = item;
|
|
||||||
tasks.Add(Task.Run(async () => await RefreshItem(taskChild, refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false), cancellationToken));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
|
||||||
tasks.Clear();
|
|
||||||
|
|
||||||
// Refresh current item
|
// Refresh current item
|
||||||
await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
|
await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
@ -175,31 +152,17 @@ namespace MediaBrowser.Controller.Entities.Audio
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
// Avoid implicitly captured closure
|
|
||||||
var currentChild = item;
|
|
||||||
|
|
||||||
await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
|
await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
|
||||||
lock (percentages)
|
|
||||||
{
|
|
||||||
percentages[currentChild.Id] = 1;
|
|
||||||
|
|
||||||
var percent = percentages.Values.Sum();
|
numComplete++;
|
||||||
percent /= totalItems;
|
double percent = numComplete;
|
||||||
percent *= 100;
|
percent /= totalItems;
|
||||||
progress.Report(percent);
|
progress.Report(percent * 100);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
progress.Report(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()
|
public ArtistInfo GetLookupInfo()
|
||||||
{
|
{
|
||||||
var info = GetItemLookupInfo<ArtistInfo>();
|
var info = GetItemLookupInfo<ArtistInfo>();
|
||||||
|
|
|
@ -283,7 +283,17 @@ namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
get
|
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;
|
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)
|
private BaseItem RetrieveChild(BaseItem child)
|
||||||
{
|
{
|
||||||
if (child.Id == Guid.Empty)
|
if (child.Id == Guid.Empty)
|
||||||
|
|
|
@ -143,31 +143,19 @@ namespace MediaBrowser.Controller.Entities.Movies
|
||||||
var items = GetRecursiveChildren().ToList();
|
var items = GetRecursiveChildren().ToList();
|
||||||
|
|
||||||
var totalItems = items.Count;
|
var totalItems = items.Count;
|
||||||
var percentages = new Dictionary<Guid, double>(totalItems);
|
var numComplete = 0;
|
||||||
|
|
||||||
// Refresh songs
|
// Refresh songs
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
var innerProgress = new ActionableProgress<double>();
|
|
||||||
|
|
||||||
// Avoid implicitly captured closure
|
await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
|
||||||
var currentChild = item;
|
|
||||||
innerProgress.RegisterAction(p =>
|
|
||||||
{
|
|
||||||
lock (percentages)
|
|
||||||
{
|
|
||||||
percentages[currentChild.Id] = p / 100;
|
|
||||||
|
|
||||||
var percent = percentages.Values.Sum();
|
numComplete++;
|
||||||
percent /= totalItems;
|
double percent = numComplete;
|
||||||
percent *= 100;
|
percent /= totalItems;
|
||||||
progress.Report(percent);
|
progress.Report(percent * 100);
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Avoid implicitly captured closure
|
|
||||||
await RefreshItem(item, refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh current item
|
// Refresh current item
|
||||||
|
@ -176,13 +164,6 @@ namespace MediaBrowser.Controller.Entities.Movies
|
||||||
progress.Report(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 override bool IsVisible(User user)
|
public override bool IsVisible(User user)
|
||||||
{
|
{
|
||||||
if (base.IsVisible(user))
|
if (base.IsVisible(user))
|
||||||
|
|
|
@ -64,8 +64,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
CollectionType.Books,
|
CollectionType.Books,
|
||||||
CollectionType.HomeVideos,
|
CollectionType.HomeVideos,
|
||||||
CollectionType.Photos,
|
CollectionType.Photos
|
||||||
string.Empty
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var collectionFolder = folder as ICollectionFolder;
|
var collectionFolder = folder as ICollectionFolder;
|
||||||
|
|
|
@ -409,12 +409,21 @@ namespace MediaBrowser.Controller.Entities
|
||||||
|
|
||||||
private QueryResult<BaseItem> GetMusicLatest(Folder parent, User user, InternalItemsQuery query)
|
private QueryResult<BaseItem> GetMusicLatest(Folder parent, User user, InternalItemsQuery query)
|
||||||
{
|
{
|
||||||
query.SortBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName };
|
var items = _userViewManager.GetLatestItems(new LatestItemsQuery
|
||||||
query.SortOrder = SortOrder.Descending;
|
{
|
||||||
|
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)
|
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)
|
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));
|
i => i is Game && i.Genres.Contains(displayParent.Name, StringComparer.OrdinalIgnoreCase));
|
||||||
|
|
||||||
return GetResult(items, queryParent, query);
|
return GetResult(items, queryParent, query);
|
||||||
|
@ -1686,7 +1695,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return parent.GetRecursiveChildren(user);
|
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)
|
if (parent == null || parent is UserView)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Audio;
|
using MediaBrowser.Controller.Entities.Audio;
|
||||||
using MediaBrowser.Controller.Playlists;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Library
|
namespace MediaBrowser.Controller.Library
|
||||||
|
@ -13,7 +12,7 @@ namespace MediaBrowser.Controller.Library
|
||||||
/// <param name="item">The item.</param>
|
/// <param name="item">The item.</param>
|
||||||
/// <param name="user">The user.</param>
|
/// <param name="user">The user.</param>
|
||||||
/// <returns>IEnumerable{Audio}.</returns>
|
/// <returns>IEnumerable{Audio}.</returns>
|
||||||
IEnumerable<Audio> GetInstantMixFromSong(Audio item, User user);
|
IEnumerable<Audio> GetInstantMixFromItem(BaseItem item, User user);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the instant mix from artist.
|
/// Gets the instant mix from artist.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -22,20 +21,6 @@ namespace MediaBrowser.Controller.Library
|
||||||
/// <returns>IEnumerable{Audio}.</returns>
|
/// <returns>IEnumerable{Audio}.</returns>
|
||||||
IEnumerable<Audio> GetInstantMixFromArtist(string name, User user);
|
IEnumerable<Audio> GetInstantMixFromArtist(string name, User user);
|
||||||
/// <summary>
|
/// <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.
|
/// Gets the instant mix from genre.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="genres">The genres.</param>
|
/// <param name="genres">The genres.</param>
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Model.Library;
|
using MediaBrowser.Model.Library;
|
||||||
|
using MediaBrowser.Model.Querying;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
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 type, string sortName, CancellationToken cancellationToken);
|
||||||
|
|
||||||
Task<UserView> GetUserView(string category, string type, User user, 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,6 +101,7 @@
|
||||||
<Compile Include="Collections\ICollectionManager.cs" />
|
<Compile Include="Collections\ICollectionManager.cs" />
|
||||||
<Compile Include="Connect\IConnectManager.cs" />
|
<Compile Include="Connect\IConnectManager.cs" />
|
||||||
<Compile Include="Connect\UserLinkResult.cs" />
|
<Compile Include="Connect\UserLinkResult.cs" />
|
||||||
|
<Compile Include="Devices\CameraImageUploadInfo.cs" />
|
||||||
<Compile Include="Devices\IDeviceManager.cs" />
|
<Compile Include="Devices\IDeviceManager.cs" />
|
||||||
<Compile Include="Devices\IDeviceRepository.cs" />
|
<Compile Include="Devices\IDeviceRepository.cs" />
|
||||||
<Compile Include="Dlna\ControlRequest.cs" />
|
<Compile Include="Dlna\ControlRequest.cs" />
|
||||||
|
@ -117,7 +118,6 @@
|
||||||
<Compile Include="Drawing\ImageStream.cs" />
|
<Compile Include="Drawing\ImageStream.cs" />
|
||||||
<Compile Include="Dto\DtoOptions.cs" />
|
<Compile Include="Dto\DtoOptions.cs" />
|
||||||
<Compile Include="Dto\IDtoService.cs" />
|
<Compile Include="Dto\IDtoService.cs" />
|
||||||
<Compile Include="Entities\AdultVideo.cs" />
|
|
||||||
<Compile Include="Entities\Audio\IHasAlbumArtist.cs" />
|
<Compile Include="Entities\Audio\IHasAlbumArtist.cs" />
|
||||||
<Compile Include="Entities\Audio\IHasMusicGenres.cs" />
|
<Compile Include="Entities\Audio\IHasMusicGenres.cs" />
|
||||||
<Compile Include="Entities\Book.cs" />
|
<Compile Include="Entities\Book.cs" />
|
||||||
|
|
|
@ -113,38 +113,17 @@ namespace MediaBrowser.Controller.Playlists
|
||||||
return LibraryManager.Sort(items, user, new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }, SortOrder.Ascending);
|
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;
|
var folder = item as Folder;
|
||||||
|
|
||||||
if (folder != null)
|
if (folder != null)
|
||||||
{
|
{
|
||||||
var items = user == null
|
var items = user == null
|
||||||
? folder.GetRecursiveChildren(m => !m.IsFolder)
|
? folder.GetRecursiveChildren(m => !m.IsFolder)
|
||||||
: folder.GetRecursiveChildren(user, m => !m.IsFolder);
|
: folder.GetRecursiveChildren(user, m => !m.IsFolder);
|
||||||
|
|
||||||
|
if (folder.IsPreSorted)
|
||||||
|
{
|
||||||
|
return items;
|
||||||
|
}
|
||||||
return LibraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending);
|
return LibraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -168,7 +168,7 @@ namespace MediaBrowser.Model.Configuration
|
||||||
/// <value>The dashboard source path.</value>
|
/// <value>The dashboard source path.</value>
|
||||||
public string DashboardSourcePath { get; set; }
|
public string DashboardSourcePath { get; set; }
|
||||||
|
|
||||||
public bool StoreArtistsInMetadata { get; set; }
|
public bool MergeMetadataAndImagesByName { get; set; }
|
||||||
public bool EnableStandaloneMetadata { get; set; }
|
public bool EnableStandaloneMetadata { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -18,6 +18,7 @@ namespace MediaBrowser.Model.Notifications
|
||||||
NewLibraryContent,
|
NewLibraryContent,
|
||||||
NewLibraryContentMultiple,
|
NewLibraryContentMultiple,
|
||||||
ServerRestartRequired,
|
ServerRestartRequired,
|
||||||
TaskFailed
|
TaskFailed,
|
||||||
|
CameraImageUploaded
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -88,9 +88,12 @@ namespace MediaBrowser.Server.Implementations.Configuration
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void UpdateItemsByNamePath()
|
private void UpdateItemsByNamePath()
|
||||||
{
|
{
|
||||||
((ServerApplicationPaths)ApplicationPaths).ItemsByNamePath = string.IsNullOrEmpty(Configuration.ItemsByNamePath) ?
|
if (!Configuration.MergeMetadataAndImagesByName)
|
||||||
null :
|
{
|
||||||
Configuration.ItemsByNamePath;
|
((ServerApplicationPaths)ApplicationPaths).ItemsByNamePath = string.IsNullOrEmpty(Configuration.ItemsByNamePath) ?
|
||||||
|
null :
|
||||||
|
Configuration.ItemsByNamePath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -101,6 +104,11 @@ namespace MediaBrowser.Server.Implementations.Configuration
|
||||||
((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = string.IsNullOrEmpty(Configuration.MetadataPath) ?
|
((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = string.IsNullOrEmpty(Configuration.MetadataPath) ?
|
||||||
GetInternalMetadataPath() :
|
GetInternalMetadataPath() :
|
||||||
Configuration.MetadataPath;
|
Configuration.MetadataPath;
|
||||||
|
|
||||||
|
if (Configuration.MergeMetadataAndImagesByName)
|
||||||
|
{
|
||||||
|
((ServerApplicationPaths)ApplicationPaths).ItemsByNamePath = ((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetInternalMetadataPath()
|
private string GetInternalMetadataPath()
|
||||||
|
|
|
@ -27,6 +27,8 @@ namespace MediaBrowser.Server.Implementations.Devices
|
||||||
private readonly IConfigurationManager _config;
|
private readonly IConfigurationManager _config;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
|
public event EventHandler<GenericEventArgs<CameraImageUploadInfo>> CameraImageUploaded;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when [device options updated].
|
/// Occurs when [device options updated].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -116,7 +118,7 @@ namespace MediaBrowser.Server.Implementations.Devices
|
||||||
{
|
{
|
||||||
devices = devices.Where(i => CanAccessDevice(query.UserId, i.Id));
|
devices = devices.Where(i => CanAccessDevice(query.UserId, i.Id));
|
||||||
}
|
}
|
||||||
|
|
||||||
var array = devices.ToArray();
|
var array = devices.ToArray();
|
||||||
return new QueryResult<DeviceInfo>
|
return new QueryResult<DeviceInfo>
|
||||||
{
|
{
|
||||||
|
@ -137,7 +139,8 @@ namespace MediaBrowser.Server.Implementations.Devices
|
||||||
|
|
||||||
public async Task AcceptCameraUpload(string deviceId, Stream stream, LocalFileInfo file)
|
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))
|
if (!string.IsNullOrWhiteSpace(file.Album))
|
||||||
{
|
{
|
||||||
|
@ -163,11 +166,27 @@ namespace MediaBrowser.Server.Implementations.Devices
|
||||||
{
|
{
|
||||||
_libraryMonitor.ReportFileSystemChangeComplete(path, true);
|
_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)
|
private string GetUploadPath(string deviceId)
|
||||||
{
|
{
|
||||||
var device = GetDevice(deviceId);
|
return GetUploadPath(GetDevice(deviceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetUploadPath(DeviceInfo device)
|
||||||
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(device.CameraUploadPath))
|
if (!string.IsNullOrWhiteSpace(device.CameraUploadPath))
|
||||||
{
|
{
|
||||||
return device.CameraUploadPath;
|
return device.CameraUploadPath;
|
||||||
|
|
|
@ -3,6 +3,7 @@ using MediaBrowser.Common.Plugins;
|
||||||
using MediaBrowser.Common.ScheduledTasks;
|
using MediaBrowser.Common.ScheduledTasks;
|
||||||
using MediaBrowser.Common.Updates;
|
using MediaBrowser.Common.Updates;
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
|
using MediaBrowser.Controller.Devices;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Audio;
|
using MediaBrowser.Controller.Entities.Audio;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
|
@ -44,8 +45,9 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
|
||||||
private readonly object _libraryChangedSyncLock = new object();
|
private readonly object _libraryChangedSyncLock = new object();
|
||||||
|
|
||||||
private readonly IConfigurationManager _config;
|
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;
|
_installationManager = installationManager;
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
|
@ -56,6 +58,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
|
||||||
_sessionManager = sessionManager;
|
_sessionManager = sessionManager;
|
||||||
_appHost = appHost;
|
_appHost = appHost;
|
||||||
_config = config;
|
_config = config;
|
||||||
|
_deviceManager = deviceManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Run()
|
public void Run()
|
||||||
|
@ -74,6 +77,21 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
|
||||||
_appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged;
|
_appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged;
|
||||||
_appHost.HasUpdateAvailableChanged += _appHost_HasUpdateAvailableChanged;
|
_appHost.HasUpdateAvailableChanged += _appHost_HasUpdateAvailableChanged;
|
||||||
_appHost.ApplicationUpdated += _appHost_ApplicationUpdated;
|
_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)
|
async void _appHost_ApplicationUpdated(object sender, GenericEventArgs<PackageVersionInfo> e)
|
||||||
|
@ -451,6 +469,8 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
|
||||||
_appHost.HasPendingRestartChanged -= _appHost_HasPendingRestartChanged;
|
_appHost.HasPendingRestartChanged -= _appHost_HasPendingRestartChanged;
|
||||||
_appHost.HasUpdateAvailableChanged -= _appHost_HasUpdateAvailableChanged;
|
_appHost.HasUpdateAvailableChanged -= _appHost_HasUpdateAvailableChanged;
|
||||||
_appHost.ApplicationUpdated -= _appHost_ApplicationUpdated;
|
_appHost.ApplicationUpdated -= _appHost_ApplicationUpdated;
|
||||||
|
|
||||||
|
_deviceManager.CameraImageUploaded -= _deviceManager_CameraImageUploaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DisposeLibraryUpdateTimer()
|
private void DisposeLibraryUpdateTimer()
|
||||||
|
|
|
@ -566,7 +566,10 @@ namespace MediaBrowser.Server.Implementations.IO
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.ToList();
|
.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 the root folder changed, run the library task so the user can see it
|
||||||
if (itemsToRefresh.Any(i => i is AggregateFolder))
|
if (itemsToRefresh.Any(i => i is AggregateFolder))
|
||||||
|
|
|
@ -219,11 +219,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The _root folder sync lock
|
/// The _root folder sync lock
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private object _rootFolderSyncLock = new object();
|
private readonly object _rootFolderSyncLock = new object();
|
||||||
/// <summary>
|
|
||||||
/// The _root folder initialized
|
|
||||||
/// </summary>
|
|
||||||
private bool _rootFolderInitialized;
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the root folder.
|
/// Gets the root folder.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -232,17 +228,17 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
LazyInitializer.EnsureInitialized(ref _rootFolder, ref _rootFolderInitialized, ref _rootFolderSyncLock, CreateRootFolder);
|
if (_rootFolder == null)
|
||||||
return _rootFolder;
|
|
||||||
}
|
|
||||||
private set
|
|
||||||
{
|
|
||||||
_rootFolder = value;
|
|
||||||
|
|
||||||
if (value == null)
|
|
||||||
{
|
{
|
||||||
_rootFolderInitialized = false;
|
lock (_rootFolderSyncLock)
|
||||||
|
{
|
||||||
|
if (_rootFolder == null)
|
||||||
|
{
|
||||||
|
_rootFolder = CreateRootFolder();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return _rootFolder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -849,11 +845,6 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (ConfigurationManager.Configuration.StoreArtistsInMetadata)
|
|
||||||
{
|
|
||||||
return Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "artists");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Path.Combine(ConfigurationManager.ApplicationPaths.ItemsByNamePath, "artists");
|
return Path.Combine(ConfigurationManager.ApplicationPaths.ItemsByNamePath, "artists");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,5 +83,40 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
.Take(100)
|
.Take(100)
|
||||||
.OrderBy(i => Guid.NewGuid());
|
.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[] { };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MoreLinq;
|
||||||
|
|
||||||
namespace MediaBrowser.Server.Implementations.Library
|
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 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
|
var foldersWithViewTypes = folders
|
||||||
.Except(standaloneFolders)
|
.Except(standaloneFolders)
|
||||||
|
@ -164,5 +167,141 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
|
|
||||||
return _libraryManager.GetNamedView(name, type, sortName, cancellationToken);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,7 @@
|
||||||
"HeaderAudio": "Audio",
|
"HeaderAudio": "Audio",
|
||||||
"HeaderVideo": "Video",
|
"HeaderVideo": "Video",
|
||||||
"HeaderPaths": "Paths",
|
"HeaderPaths": "Paths",
|
||||||
|
"CategorySync": "Sync",
|
||||||
"HeaderSyncRequiresSupporterMembership": "Sync Requires a Supporter Membership",
|
"HeaderSyncRequiresSupporterMembership": "Sync Requires a Supporter Membership",
|
||||||
"HeaderEnjoyDayTrial": "Enjoy a 14 Day Free Trial",
|
"HeaderEnjoyDayTrial": "Enjoy a 14 Day Free Trial",
|
||||||
"LabelSyncTempPath": "Temporary file path:",
|
"LabelSyncTempPath": "Temporary file path:",
|
||||||
|
@ -669,6 +670,7 @@
|
||||||
"NotificationOptionInstallationFailed": "Installation failure",
|
"NotificationOptionInstallationFailed": "Installation failure",
|
||||||
"NotificationOptionNewLibraryContent": "New content added",
|
"NotificationOptionNewLibraryContent": "New content added",
|
||||||
"NotificationOptionNewLibraryContentMultiple": "New content added (multiple)",
|
"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.",
|
"SendNotificationHelp": "By default, notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.",
|
||||||
"NotificationOptionServerRestartRequired": "Server restart required",
|
"NotificationOptionServerRestartRequired": "Server restart required",
|
||||||
"LabelNotificationEnabled": "Enable this notification",
|
"LabelNotificationEnabled": "Enable this notification",
|
||||||
|
@ -893,7 +895,7 @@
|
||||||
"OptionCommunityMostWatchedSort": "Most Watched",
|
"OptionCommunityMostWatchedSort": "Most Watched",
|
||||||
"TabNextUp": "Next Up",
|
"TabNextUp": "Next Up",
|
||||||
"HeaderBecomeMediaBrowserSupporter": "Become a Media Browser Supporter",
|
"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.",
|
"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.",
|
"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.",
|
"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",
|
"OptionLatestTvRecordings": "Latest recordings",
|
||||||
"LabelProtocolInfo": "Protocol info:",
|
"LabelProtocolInfo": "Protocol info:",
|
||||||
"LabelProtocolInfoHelp": "The value that will be used when responding to GetProtocolInfo requests from the device.",
|
"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.",
|
"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:",
|
"LabelKodiMetadataUser": "Sync user watch data to nfo's for:",
|
||||||
"LabelKodiMetadataUserHelp": "Enable this to keep watch data in sync between Media Browser and Kodi.",
|
"LabelKodiMetadataUserHelp": "Enable this to keep watch data in sync between Media Browser and Kodi.",
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Controller.Localization;
|
using MediaBrowser.Controller.Localization;
|
||||||
using MediaBrowser.Controller.Notifications;
|
using MediaBrowser.Controller.Notifications;
|
||||||
using MediaBrowser.Model.Configuration;
|
|
||||||
using MediaBrowser.Model.Notifications;
|
using MediaBrowser.Model.Notifications;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -137,6 +136,13 @@ namespace MediaBrowser.Server.Implementations.Notifications
|
||||||
Type = NotificationType.VideoPlaybackStopped.ToString(),
|
Type = NotificationType.VideoPlaybackStopped.ToString(),
|
||||||
DefaultTitle = "{UserName} has finished playing {ItemName} on {DeviceName}.",
|
DefaultTitle = "{UserName} has finished playing {ItemName} on {DeviceName}.",
|
||||||
Variables = new List<string>{"UserName", "ItemName", "DeviceName", "AppName"}
|
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");
|
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");
|
note.Category = _localization.GetLocalizedString("CategoryPlugin");
|
||||||
}
|
}
|
||||||
|
else if (note.Type.IndexOf("CameraImageUploaded", StringComparison.OrdinalIgnoreCase) != -1)
|
||||||
|
{
|
||||||
|
note.Category = _localization.GetLocalizedString("CategorySync");
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
note.Category = _localization.GetLocalizedString("CategorySystem");
|
note.Category = _localization.GetLocalizedString("CategorySystem");
|
||||||
|
|
|
@ -870,14 +870,14 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
{
|
{
|
||||||
if (items.Any(i => !session.QueueableMediaTypes.Contains(i.MediaType, StringComparer.OrdinalIgnoreCase)))
|
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
|
else
|
||||||
{
|
{
|
||||||
if (items.Any(i => !session.PlayableMediaTypes.Contains(i.MediaType, StringComparer.OrdinalIgnoreCase)))
|
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 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)
|
if (item.IsFolder)
|
||||||
{
|
{
|
||||||
var folder = (Folder)item;
|
var folder = (Folder)item;
|
||||||
|
@ -913,37 +926,9 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
|
|
||||||
private IEnumerable<BaseItem> TranslateItemForInstantMix(string id, User user)
|
private IEnumerable<BaseItem> TranslateItemForInstantMix(string id, User user)
|
||||||
{
|
{
|
||||||
var item = _libraryManager.GetItemById(new Guid(id));
|
var item = _libraryManager.GetItemById(id);
|
||||||
|
|
||||||
var audio = item as Audio;
|
return _musicManager.GetInstantMixFromItem(item, user);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new BaseItem[] { };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task SendBrowseCommand(string controllingSessionId, string sessionId, BrowseRequest command, CancellationToken cancellationToken)
|
public Task SendBrowseCommand(string controllingSessionId, string sessionId, BrowseRequest command, CancellationToken cancellationToken)
|
||||||
|
|
|
@ -93,7 +93,8 @@ namespace MediaBrowser.Server.Implementations.TV
|
||||||
return FilterSeries(request, series)
|
return FilterSeries(request, series)
|
||||||
.AsParallel()
|
.AsParallel()
|
||||||
.Select(i => GetNextUp(i, currentUser))
|
.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 =>
|
.OrderByDescending(i =>
|
||||||
{
|
{
|
||||||
var episode = i.Item1;
|
var episode = i.Item1;
|
||||||
|
@ -123,7 +124,7 @@ namespace MediaBrowser.Server.Implementations.TV
|
||||||
/// <param name="series">The series.</param>
|
/// <param name="series">The series.</param>
|
||||||
/// <param name="user">The user.</param>
|
/// <param name="user">The user.</param>
|
||||||
/// <returns>Task{Episode}.</returns>
|
/// <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
|
// Get them in display order, then reverse
|
||||||
var allEpisodes = series.GetSeasons(user, true, true)
|
var allEpisodes = series.GetSeasons(user, true, true)
|
||||||
|
@ -162,13 +163,13 @@ namespace MediaBrowser.Server.Implementations.TV
|
||||||
|
|
||||||
if (lastWatched != null)
|
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));
|
var firstEpisode = allEpisodes.LastOrDefault(i => i.LocationType != LocationType.Virtual && !i.IsPlayed(user));
|
||||||
|
|
||||||
// Return the first episode
|
// 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)
|
private IEnumerable<Series> FilterSeries(NextUpQuery request, IEnumerable<Series> items)
|
||||||
|
|
|
@ -441,7 +441,7 @@ namespace MediaBrowser.WebDashboard.Api
|
||||||
"metadataconfigurationpage.js",
|
"metadataconfigurationpage.js",
|
||||||
"metadataimagespage.js",
|
"metadataimagespage.js",
|
||||||
"metadatasubtitles.js",
|
"metadatasubtitles.js",
|
||||||
"metadatakodi.js",
|
"metadatanfo.js",
|
||||||
"moviegenres.js",
|
"moviegenres.js",
|
||||||
"moviecollections.js",
|
"moviecollections.js",
|
||||||
"movies.js",
|
"movies.js",
|
||||||
|
|
|
@ -480,7 +480,7 @@
|
||||||
<Content Include="dashboard-ui\librarypathmapping.html">
|
<Content Include="dashboard-ui\librarypathmapping.html">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="dashboard-ui\metadatakodi.html">
|
<Content Include="dashboard-ui\metadatanfo.html">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="dashboard-ui\mypreferencesdisplay.html">
|
<Content Include="dashboard-ui\mypreferencesdisplay.html">
|
||||||
|
@ -795,7 +795,7 @@
|
||||||
<Content Include="dashboard-ui\scripts\librarypathmapping.js">
|
<Content Include="dashboard-ui\scripts\librarypathmapping.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="dashboard-ui\scripts\metadatakodi.js">
|
<Content Include="dashboard-ui\scripts\metadatanfo.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="dashboard-ui\scripts\mypreferencesdisplay.js">
|
<Content Include="dashboard-ui\scripts\mypreferencesdisplay.js">
|
||||||
|
|
Loading…
Reference in New Issue