add option to merge metadata and IBN paths

This commit is contained in:
Luke Pulverenti
2015-01-26 11:47:15 -05:00
parent 91416cb8a8
commit 63f3cf97da
33 changed files with 411 additions and 379 deletions

View File

@@ -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()

View File

@@ -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;

View File

@@ -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()

View File

@@ -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))

View File

@@ -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");
}
}

View File

@@ -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[] { };
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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.",

View File

@@ -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");

View File

@@ -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 item = _libraryManager.GetItemById(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);
}
return new BaseItem[] { };
return _musicManager.GetInstantMixFromItem(item, user);
}
public Task SendBrowseCommand(string controllingSessionId, string sessionId, BrowseRequest command, CancellationToken cancellationToken)

View File

@@ -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)