diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs
index 96fe01ab32..abeaba9105 100644
--- a/MediaBrowser.Api/LiveTv/LiveTvService.cs
+++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs
@@ -39,6 +39,9 @@ namespace MediaBrowser.Api.LiveTv
/// The limit.
[ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? Limit { get; set; }
+
+ [ApiMember(Name = "IsFavorite", Description = "Filter by channels that are favorites, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ public bool? IsFavorite { get; set; }
}
[Route("/LiveTv/Channels/{Id}", "GET", Summary = "Gets a live tv channel")]
@@ -290,7 +293,8 @@ namespace MediaBrowser.Api.LiveTv
ChannelType = request.Type,
UserId = request.UserId,
StartIndex = request.StartIndex,
- Limit = request.Limit
+ Limit = request.Limit,
+ IsFavorite = request.IsFavorite
}, CancellationToken.None).Result;
diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/MediaBrowser.Dlna/DlnaManager.cs
index ec9ecb9ef2..958188f37a 100644
--- a/MediaBrowser.Dlna/DlnaManager.cs
+++ b/MediaBrowser.Dlna/DlnaManager.cs
@@ -20,13 +20,15 @@ namespace MediaBrowser.Dlna
private readonly IXmlSerializer _xmlSerializer;
private readonly IFileSystem _fileSystem;
private readonly ILogger _logger;
+ private readonly IJsonSerializer _jsonSerializer;
- public DlnaManager(IXmlSerializer xmlSerializer, IFileSystem fileSystem, IApplicationPaths appPaths, ILogger logger)
+ public DlnaManager(IXmlSerializer xmlSerializer, IFileSystem fileSystem, IApplicationPaths appPaths, ILogger logger, IJsonSerializer jsonSerializer)
{
_xmlSerializer = xmlSerializer;
_fileSystem = fileSystem;
_appPaths = appPaths;
_logger = logger;
+ _jsonSerializer = jsonSerializer;
//DumpProfiles();
}
@@ -381,10 +383,66 @@ namespace MediaBrowser.Dlna
public void CreateProfile(DeviceProfile profile)
{
+ profile = ReserializeProfile(profile);
+
+ if (string.IsNullOrWhiteSpace(profile.Name))
+ {
+ throw new ArgumentException("Profile is missing Name");
+ }
+
+ var newFilename = _fileSystem.GetValidFilename(profile.Name) + ".xml";
+ var path = Path.Combine(UserProfilesPath, newFilename);
+
+ _xmlSerializer.SerializeToFile(profile, path);
}
public void UpdateProfile(DeviceProfile profile)
{
+ profile = ReserializeProfile(profile);
+
+ if (string.IsNullOrWhiteSpace(profile.Id))
+ {
+ throw new ArgumentException("Profile is missing Id");
+ }
+ if (string.IsNullOrWhiteSpace(profile.Name))
+ {
+ throw new ArgumentException("Profile is missing Name");
+ }
+
+ var current = GetProfileInfosInternal().First(i => string.Equals(i.Info.Id, profile.Id, StringComparison.OrdinalIgnoreCase));
+
+ if (current.Info.Type == DeviceProfileType.System)
+ {
+ throw new ArgumentException("System profiles are readonly");
+ }
+
+ var newFilename = _fileSystem.GetValidFilename(profile.Name) + ".xml";
+ var path = Path.Combine(UserProfilesPath, newFilename);
+
+ if (!string.Equals(path, current.Path, StringComparison.Ordinal))
+ {
+ File.Delete(current.Path);
+ }
+
+ _xmlSerializer.SerializeToFile(profile, path);
+ }
+
+ ///
+ /// Recreates the object using serialization, to ensure it's not a subclass.
+ /// If it's a subclass it may not serlialize properly to xml (different root element tag name)
+ ///
+ ///
+ ///
+ private DeviceProfile ReserializeProfile(DeviceProfile profile)
+ {
+ if (profile.GetType() == typeof(DeviceProfile))
+ {
+ return profile;
+ }
+
+ var json = _jsonSerializer.SerializeToString(profile);
+
+ return _jsonSerializer.DeserializeFromString(json);
}
class InternalProfileInfo
diff --git a/MediaBrowser.Model/LiveTv/ChannelQuery.cs b/MediaBrowser.Model/LiveTv/ChannelQuery.cs
index eb3b20ce3b..9079ebacd9 100644
--- a/MediaBrowser.Model/LiveTv/ChannelQuery.cs
+++ b/MediaBrowser.Model/LiveTv/ChannelQuery.cs
@@ -12,6 +12,12 @@ namespace MediaBrowser.Model.LiveTv
/// The type of the channel.
public ChannelType? ChannelType { get; set; }
+ ///
+ /// Gets or sets a value indicating whether this instance is favorite.
+ ///
+ /// null if [is favorite] contains no value, true if [is favorite]; otherwise, false.
+ public bool? IsFavorite { get; set; }
+
///
/// Gets or sets the user identifier.
///
diff --git a/MediaBrowser.Model/Session/PlayRequest.cs b/MediaBrowser.Model/Session/PlayRequest.cs
index 949274a5d5..74d7a70a35 100644
--- a/MediaBrowser.Model/Session/PlayRequest.cs
+++ b/MediaBrowser.Model/Session/PlayRequest.cs
@@ -39,14 +39,22 @@ namespace MediaBrowser.Model.Session
///
/// The play now
///
- PlayNow,
+ PlayNow = 0,
///
/// The play next
///
- PlayNext,
+ PlayNext = 1,
///
/// The play last
///
- PlayLast
+ PlayLast = 2,
+ ///
+ /// The play instant mix
+ ///
+ PlayInstantMix = 3,
+ ///
+ /// The play shuffle
+ ///
+ PlayShuffle = 4
}
}
\ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
index 1833b708fc..2e0b3cb172 100644
--- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs
+++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
@@ -1297,7 +1297,7 @@ namespace MediaBrowser.Server.Implementations.Dto
{
var result = new List
{
- GetVersionInfo(item, true)
+ GetVersionInfo(item)
};
return result;
@@ -1321,7 +1321,7 @@ namespace MediaBrowser.Server.Implementations.Dto
};
}
- private MediaSourceInfo GetVersionInfo(Audio i, bool isPrimary)
+ private MediaSourceInfo GetVersionInfo(Audio i)
{
return new MediaSourceInfo
{
diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
index 9279fd8d73..08add952a7 100644
--- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
+++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
@@ -349,13 +349,13 @@ namespace MediaBrowser.Server.Implementations.IO
{
try
{
- Logger.Debug("Watcher sees change of type " + e.ChangeType + " to " + e.FullPath);
+ Logger.Debug("Changed detected of type " + e.ChangeType + " to " + e.FullPath);
ReportFileSystemChanged(e.FullPath);
}
catch (Exception ex)
{
- Logger.ErrorException("Exception in watcher changed. Path: {0}", ex, e.FullPath);
+ Logger.ErrorException("Exception in ReportFileSystemChanged. Path: {0}", ex, e.FullPath);
}
}
@@ -397,14 +397,6 @@ namespace MediaBrowser.Server.Implementations.IO
Logger.Debug("Ignoring change to {0}", path);
return true;
}
-
- // Go up another level
- parent = Path.GetDirectoryName(i);
- if (string.Equals(parent, path, StringComparison.OrdinalIgnoreCase))
- {
- Logger.Debug("Ignoring change to {0}", path);
- return true;
- }
}
return false;
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
index db8786d62b..b828dc0de5 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -134,6 +134,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return number;
});
+
+ if (query.IsFavorite.HasValue)
+ {
+ var val = query.IsFavorite.Value;
+
+ channels = channels
+ .Where(i => _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).IsFavorite == val);
+ }
}
channels = channels.OrderBy(i =>
diff --git a/MediaBrowser.Server.Implementations/Roku/RokuSessionController.cs b/MediaBrowser.Server.Implementations/Roku/RokuSessionController.cs
index d806db1e0b..7c8d71b4f0 100644
--- a/MediaBrowser.Server.Implementations/Roku/RokuSessionController.cs
+++ b/MediaBrowser.Server.Implementations/Roku/RokuSessionController.cs
@@ -30,7 +30,7 @@ namespace MediaBrowser.Server.Implementations.Roku
public bool SupportsMediaRemoteControl
{
- get { return true; }
+ get { return false; }
}
public bool IsSessionActive
diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs
index 9d405a1751..4f748a6a80 100644
--- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs
+++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs
@@ -3,6 +3,8 @@ using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Session;
@@ -564,7 +566,7 @@ namespace MediaBrowser.Server.Implementations.Session
return playedToCompletion;
}
-
+
///
/// Updates playstate position for an item but does not save
///
@@ -666,7 +668,7 @@ namespace MediaBrowser.Server.Implementations.Session
var controllingSession = GetSession(controllingSessionId);
AssertCanControl(session, controllingSession);
-
+
return session.SessionController.SendSystemCommand(command, cancellationToken);
}
@@ -676,7 +678,7 @@ namespace MediaBrowser.Server.Implementations.Session
var controllingSession = GetSession(controllingSessionId);
AssertCanControl(session, controllingSession);
-
+
return session.SessionController.SendMessageCommand(command, cancellationToken);
}
@@ -684,14 +686,22 @@ namespace MediaBrowser.Server.Implementations.Session
{
var session = GetSessionForRemoteControl(sessionId);
- var items = command.ItemIds.Select(i => _libraryManager.GetItemById(new Guid(i)))
+ var user = session.UserId.HasValue ? _userManager.GetUserById(session.UserId.Value) : null;
+
+ var items = command.ItemIds.SelectMany(i => TranslateItemForPlayback(i, user))
.Where(i => i.LocationType != LocationType.Virtual)
.ToList();
- if (session.UserId.HasValue)
+ if (command.PlayCommand == PlayCommand.PlayShuffle)
{
- var user = _userManager.GetUserById(session.UserId.Value);
+ items = items.OrderBy(i => Guid.NewGuid()).ToList();
+ command.PlayCommand = PlayCommand.PlayNow;
+ }
+ command.ItemIds = items.Select(i => i.Id.ToString("N")).ToArray();
+
+ if (user != null)
+ {
if (items.Any(i => i.GetPlayAccess(user) != PlayAccess.Full))
{
throw new ArgumentException(string.Format("{0} is not allowed to play media.", user.Name));
@@ -723,13 +733,34 @@ namespace MediaBrowser.Server.Implementations.Session
return session.SessionController.SendPlayCommand(command, cancellationToken);
}
+ private IEnumerable TranslateItemForPlayback(string id, User user)
+ {
+ var item = _libraryManager.GetItemById(new Guid(id));
+
+ if (item.IsFolder)
+ {
+ var folder = (Folder)item;
+
+ var items = user == null ? folder.RecursiveChildren:
+ folder.GetRecursiveChildren(user);
+
+ items = items.Where(i => !i.IsFolder);
+
+ items = items.OrderBy(i => i.SortName);
+
+ return items;
+ }
+
+ return new[] { item };
+ }
+
public Task SendBrowseCommand(Guid controllingSessionId, Guid sessionId, BrowseRequest command, CancellationToken cancellationToken)
{
var session = GetSessionForRemoteControl(sessionId);
var controllingSession = GetSession(controllingSessionId);
AssertCanControl(session, controllingSession);
-
+
return session.SessionController.SendBrowseCommand(command, cancellationToken);
}
diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs
index b7e9017d6e..d9d5e007e8 100644
--- a/MediaBrowser.ServerApplication/ApplicationHost.cs
+++ b/MediaBrowser.ServerApplication/ApplicationHost.cs
@@ -503,7 +503,7 @@ namespace MediaBrowser.ServerApplication
var appThemeManager = new AppThemeManager(ApplicationPaths, FileSystemManager, JsonSerializer, Logger);
RegisterSingleInstance(appThemeManager);
- var dlnaManager = new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LogManager.GetLogger("DLNA"));
+ var dlnaManager = new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LogManager.GetLogger("DLNA"), JsonSerializer);
RegisterSingleInstance(dlnaManager);
var collectionManager = new CollectionManager(LibraryManager, FileSystemManager, LibraryMonitor);
diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs
index 99afbbdd70..369fb2a07f 100644
--- a/MediaBrowser.WebDashboard/Api/DashboardService.cs
+++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs
@@ -401,9 +401,13 @@ namespace MediaBrowser.WebDashboard.Api
"librarylist.js",
"editorsidebar.js",
"librarymenu.js",
- //"chromecast.js",
+ "chromecast.js",
"contextmenu.js",
+ "mediacontroller.js",
+ "mediaplayer.js",
+ "mediaplayer-video.js",
+
"ratingdialog.js",
"aboutpage.js",
"allusersettings.js",
@@ -461,10 +465,6 @@ namespace MediaBrowser.WebDashboard.Api
"loginpage.js",
"logpage.js",
"medialibrarypage.js",
- "mediaplayer.js",
-
- "mediaplayer-video.js",
-
"metadataconfigurationpage.js",
"metadataimagespage.js",
"moviegenres.js",
diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
index 6a8cc49b1a..6c6487cc2f 100644
--- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
+++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
@@ -587,6 +587,9 @@
PreserveNewest
+
+ PreserveNewest
+
PreserveNewest